|
@@ -0,0 +1,379 @@
|
|
|
+<template>
|
|
|
+ <div class="customize-container">
|
|
|
+ <el-card body-style="padding: 20px 80px 0 80px;">
|
|
|
+ <div style="font-weight: 700; padding-bottom: 18px">
|
|
|
+ <span style="color: #306cd7; font-size: 26px">|</span>
|
|
|
+ <span style="font-size: 18px; padding-left: 5px">创意</span>
|
|
|
+ </div>
|
|
|
+ <el-form
|
|
|
+ ref="ruleFormRef"
|
|
|
+ :model="ruleForm"
|
|
|
+ :rules="rules"
|
|
|
+ label-width="120px"
|
|
|
+ class="demo-ruleForm"
|
|
|
+ size="default"
|
|
|
+ label-position="top"
|
|
|
+ status-icon>
|
|
|
+ <el-form-item label="广告名称" prop="name">
|
|
|
+ <el-input v-model="ruleForm.name" style="width: 50%" />
|
|
|
+ </el-form-item>
|
|
|
+ <div style="border: 1px solid #dddfe6; padding: 0 0 0 5px; margin-bottom: 20px">
|
|
|
+ <div style="width: 50%; padding-left: 5px; border-right: 1px solid #dddfe6">
|
|
|
+ <el-scrollbar height="700px">
|
|
|
+ <el-collapse v-model="activeNames" @change="handleChange">
|
|
|
+ <el-collapse-item name="1">
|
|
|
+ <template #title> <span style="color: #e47470; margin-right: 4px">*</span>品牌名称和徽标</template>
|
|
|
+ <el-form-item prop="brandName">
|
|
|
+ <el-input v-model="ruleForm.brandName" placeholder="请输入品牌名称" style="padding: 0 10px 5px 0"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-upload
|
|
|
+ class="upload-demo"
|
|
|
+ drag
|
|
|
+ action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
|
|
|
+ multiple
|
|
|
+ style="padding-right: 10px">
|
|
|
+ <el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
|
|
+ <div class="el-upload__text">Drop file here or <em>click to upload</em></div>
|
|
|
+ <template #tip>
|
|
|
+ <div style="margin-top: 10px">
|
|
|
+ <div style="display: flex; align-items: center; justify-content: space-between">
|
|
|
+ <span style="line-height: 17px; font-weight: 600; color: #1e2128">徽标规格</span>
|
|
|
+ <el-button type="primary" :icon="Picture" @click="openDialog">从素材库中选择</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="introduce-item">1、图片大小: 400x400 像素或更大</div>
|
|
|
+ <div class="introduce-item">2、文件大小: 1MB 或更小</div>
|
|
|
+ <div class="introduce-item">3、文件格式: PNG 或 JPG</div>
|
|
|
+ <div class="introduce-item">
|
|
|
+ 4、内容: 徽标必须填满图片或置于白色或透明背景上详细了解我们的徽标要求
|
|
|
+ <span style="margin-left: 25px; position: relative">
|
|
|
+ <el-icon size="14" style="position: absolute; left: -14px; top: 1px"><Link /></el-icon>
|
|
|
+ <el-link
|
|
|
+ type="primary"
|
|
|
+ :underline="false"
|
|
|
+ href="https://advertising.amazon.com/resources/ad-policy/sponsored-ads-policies#brandlogo"
|
|
|
+ target="_blank"
|
|
|
+ >查看要求</el-link
|
|
|
+ >
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ </el-collapse-item>
|
|
|
+ <el-collapse-item name="2">
|
|
|
+ <template #title>自定义图片(可选)</template>
|
|
|
+ <el-upload
|
|
|
+ class="avatar-uploader"
|
|
|
+ style="padding-right: 10px"
|
|
|
+ action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
|
|
|
+ :show-file-list="false"
|
|
|
+ :on-success="handleAvatarSuccess"
|
|
|
+ :before-upload="beforeAvatarUpload">
|
|
|
+ <img v-if="imageUrl" :src="imageUrl" class="avatar" />
|
|
|
+ <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
|
|
+ <template #tip>
|
|
|
+ <div style="margin-top: 10px">
|
|
|
+ <div style="display: flex; align-items: center; justify-content: space-between">
|
|
|
+ <span style="line-height: 17px; font-weight: 600; color: #1e2128">图片规格</span>
|
|
|
+ <el-button type="primary" :icon="Picture">从素材库中选择</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="introduce-item">1、图片大小: 1200 x 628 像素或更大</div>
|
|
|
+ <div class="introduce-item">2、文件大小: 5MB 或更小</div>
|
|
|
+ <div class="introduce-item">3、文件格式: PNG 或 JPG</div>
|
|
|
+ <div class="introduce-item">4、内容: 图片中未添加文本、图形或徽标</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ </el-collapse-item>
|
|
|
+
|
|
|
+ <el-collapse-item name="3">
|
|
|
+ <template #title> <span style="color: #e47470; margin-right: 4px">*</span>商品</template>
|
|
|
+ 拖动带编号的选项卡,可以重新排列商品。商品可更换成你落地页上的任何商品。
|
|
|
+ </el-collapse-item>
|
|
|
+ <el-collapse-item name="4">
|
|
|
+ <template #title> <span style="color: #e47470; margin-right: 4px">*</span>标题</template>
|
|
|
+ <el-form-item prop="title">
|
|
|
+ <el-input v-model="ruleForm.title" maxlength="50" placeholder="请输入标题" show-word-limit style="padding: 0 10px 0 0"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+ </el-scrollbar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ <el-dialog v-model="centerDialogVisible" title="从素材库中选择" width="65%">
|
|
|
+ <el-input :prefix-icon="Search"></el-input>
|
|
|
+ <div class="grid-container">
|
|
|
+ <div
|
|
|
+ class="grid-item"
|
|
|
+ v-for="item in cards"
|
|
|
+ :key="item.id"
|
|
|
+ @click="selectCard(item)"
|
|
|
+ :class="{ selected: isSelected(item.id), hover: hoverId === item.id }"
|
|
|
+ @mouseover="hoverId = item.id"
|
|
|
+ @mouseleave="hoverId = null">
|
|
|
+ <el-card :body-style="{ padding: '0px' }">
|
|
|
+ <el-image class="image" :src="item.imageUrl" fit="cover" />
|
|
|
+ <div style="padding: 10px">
|
|
|
+ <span>
|
|
|
+ <el-tooltip placement="top" :content="item.title">
|
|
|
+ {{ item.title }}
|
|
|
+ </el-tooltip>
|
|
|
+ </span>
|
|
|
+ <div class="bottom">
|
|
|
+ <div class="bottom-item">{{ item.size }}KB</div>
|
|
|
+ <div class="bottom-item">{{ item.width }} * {{ item.height }}</div>
|
|
|
+ <div class="bottom-item">徽标</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <span class="dialog-footer">
|
|
|
+ <el-button @click="centerDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="centerDialogVisible = false">确定</el-button>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { reactive, ref } from 'vue'
|
|
|
+import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus'
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
+import { Plus, Picture, Search } from '@element-plus/icons-vue'
|
|
|
+import { getAssets } from '../api/index'
|
|
|
+import { storeToRefs } from 'pinia'
|
|
|
+import { useShopInfo } from '/@/stores/shopInfo'
|
|
|
+
|
|
|
+const shopInfo = useShopInfo()
|
|
|
+const { profile } = storeToRefs(shopInfo)
|
|
|
+
|
|
|
+interface RuleForm {
|
|
|
+ name: string
|
|
|
+ brandName: string
|
|
|
+ title: string
|
|
|
+}
|
|
|
+
|
|
|
+const ruleFormRef = ref<FormInstance>()
|
|
|
+const ruleForm = reactive<RuleForm>({
|
|
|
+ name: '视频 广告 - 1/15/2024 17:51:10.236',
|
|
|
+ brandName: '',
|
|
|
+ title: '',
|
|
|
+})
|
|
|
+
|
|
|
+const rules = reactive<FormRules<RuleForm>>({
|
|
|
+ name: [{ required: true, message: '请输入广告名称', trigger: 'blur' }],
|
|
|
+ brandName: [{ required: true, message: '请输入品牌名称', trigger: 'blur' }],
|
|
|
+ title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
|
|
+})
|
|
|
+
|
|
|
+const submitForm = async (formEl: FormInstance | undefined) => {
|
|
|
+ if (!formEl) return
|
|
|
+ await formEl.validate((valid, fields) => {
|
|
|
+ if (valid) {
|
|
|
+ console.log('submit!')
|
|
|
+ } else {
|
|
|
+ console.log('error submit!', fields)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const activeNames = ref(['1'])
|
|
|
+const handleChange = (val: string[]) => {
|
|
|
+ console.log(val)
|
|
|
+}
|
|
|
+
|
|
|
+// const fileList = ref<UploadUserFile[]>([
|
|
|
+// {
|
|
|
+// name: 'element-plus-logo.svg',
|
|
|
+// url: 'https://element-plus.org/images/element-plus-logo.svg',
|
|
|
+// },
|
|
|
+// {
|
|
|
+// name: 'element-plus-logo2.svg',
|
|
|
+// url: 'https://element-plus.org/images/element-plus-logo.svg',
|
|
|
+// },
|
|
|
+// ])
|
|
|
+
|
|
|
+// const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
|
|
|
+// console.log(file, uploadFiles)
|
|
|
+// }
|
|
|
+
|
|
|
+// const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
|
|
+// console.log(uploadFile)
|
|
|
+// }
|
|
|
+
|
|
|
+// const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
|
|
|
+// ElMessage.warning(`The limit is 3, you selected ${files.length} files this time, add up to ${files.length + uploadFiles.length} totally`)
|
|
|
+// }
|
|
|
+
|
|
|
+// const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
|
|
|
+// return ElMessageBox.confirm(`Cancel the transfer of ${uploadFile.name} ?`).then(
|
|
|
+// () => true,
|
|
|
+// () => false
|
|
|
+// )
|
|
|
+// }
|
|
|
+
|
|
|
+// function handleSelect() {
|
|
|
+// // console.log()
|
|
|
+// }
|
|
|
+
|
|
|
+const imageUrl = ref('')
|
|
|
+
|
|
|
+const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
|
|
|
+ imageUrl.value = URL.createObjectURL(uploadFile.raw!)
|
|
|
+}
|
|
|
+
|
|
|
+const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
|
|
+ if (rawFile.type !== 'image/jpeg') {
|
|
|
+ ElMessage.error('Avatar picture must be JPG format!')
|
|
|
+ return false
|
|
|
+ } else if (rawFile.size / 1024 / 1024 > 2) {
|
|
|
+ ElMessage.error('Avatar picture size can not exceed 2MB!')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const selectedId = ref(null)
|
|
|
+const hoverId = ref(null)
|
|
|
+
|
|
|
+const selectCard = (item) => {
|
|
|
+ selectedId.value = item.id
|
|
|
+}
|
|
|
+
|
|
|
+const isSelected = (id) => {
|
|
|
+ return selectedId.value === id
|
|
|
+}
|
|
|
+
|
|
|
+const cards = reactive([])
|
|
|
+async function getAssetsData() {
|
|
|
+ const query = {
|
|
|
+ profile_id: profile.value.profile_id,
|
|
|
+ assetType: 'IMAGE',
|
|
|
+ assetSubType: 'LOGO',
|
|
|
+ }
|
|
|
+ const response = await getAssets(query)
|
|
|
+ console.log('🚀 ~ getAssetsData ~ response-->>', response)
|
|
|
+
|
|
|
+ // 清空现有的 cards 数组
|
|
|
+ cards.splice(0, cards.length)
|
|
|
+
|
|
|
+ // 添加新的数据到 cards 数组
|
|
|
+ response.data.forEach((asset) => {
|
|
|
+ cards.push({
|
|
|
+ id: asset.assetId,
|
|
|
+ title: asset.name,
|
|
|
+ imageUrl: asset.storageLocationUrls.defaultUrl,
|
|
|
+ width: asset.fileMetadata.width,
|
|
|
+ height: asset.fileMetadata.height,
|
|
|
+ size: bytesToKB(asset.fileMetadata.sizeInBytes),
|
|
|
+ })
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function bytesToKB(bytes) {
|
|
|
+ return (bytes / 1024).toFixed(2); // 保留两位小数
|
|
|
+}
|
|
|
+
|
|
|
+const centerDialogVisible = ref(false)
|
|
|
+function openDialog() {
|
|
|
+ centerDialogVisible.value = true
|
|
|
+ getAssetsData()
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.customize-container {
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+.upload-button-group {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.introduce-item {
|
|
|
+ line-height: 17px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #88909b;
|
|
|
+}
|
|
|
+.avatar-uploader .avatar {
|
|
|
+ width: 178px;
|
|
|
+ height: 178px;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+::v-deep(.avatar-uploader .el-upload) {
|
|
|
+ border: 1px dashed var(--el-border-color);
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ transition: var(--el-transition-duration-fast);
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep(.avatar-uploader .el-upload:hover) {
|
|
|
+ border-color: var(--el-color-primary);
|
|
|
+}
|
|
|
+::v-deep(.el-icon.avatar-uploader-icon) {
|
|
|
+ font-size: 28px;
|
|
|
+ color: #8c939d;
|
|
|
+ width: 178px;
|
|
|
+ height: 178px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+::v-deep(.avatar-uploader .el-upload.el-upload--text) {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.grid-container {
|
|
|
+ flex-wrap: wrap;
|
|
|
+ display: flex;
|
|
|
+ width: 100%;
|
|
|
+ justify-content: left;
|
|
|
+}
|
|
|
+.grid-item {
|
|
|
+ transition: outline, background-color 0.3s;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 1px solid #ffffff00;
|
|
|
+ cursor: pointer;
|
|
|
+ width: calc(25% - 10px);
|
|
|
+ margin: 10px 5px;
|
|
|
+}
|
|
|
+.grid-item span {
|
|
|
+ display: block; /* 或者 inline-block */
|
|
|
+ white-space: nowrap; /* 保持文本在一行 */
|
|
|
+ overflow: hidden; /* 隐藏超出部分 */
|
|
|
+ text-overflow: ellipsis; /* 超出部分显示省略号 */
|
|
|
+ max-width: 100%; /* 限制最大宽度 */
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 22px;
|
|
|
+}
|
|
|
+.grid-item.hover,
|
|
|
+.grid-item.selected {
|
|
|
+ border: 1px solid #306cd8;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+.grid-item.selected > :first-child {
|
|
|
+ background-color: #f5f7fe;
|
|
|
+}
|
|
|
+.image {
|
|
|
+ width: 100%;
|
|
|
+ height: 146.49px;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+.image > :first-child {
|
|
|
+ border-radius: 10px;
|
|
|
+}
|
|
|
+.bottom {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 5px;
|
|
|
+}
|
|
|
+.bottom-item {
|
|
|
+ background-color: #f5f7fe;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 0 3px;
|
|
|
+}
|
|
|
+</style>
|