前言
setRangeText
: setRangeText
在线预览
:wordPackage
内容
<template> <t-tabs :default-value="1"> <t-tab-panel :value="1" label="广告创意"> <template #panel> <t-form ref="form" :rules="FORM_RULES" :data="formData" :required-mark="false" @submit="onSubmit"> <!-- 标题 --> <t-card :title="`标题(${formData.dynamic_creative_elements.title_options.length}/10)`" header-bordered :style="{ width: '800px', margin: '20px 0 0 20px' }" > <t-form-item v-for="(titles, index) in formData.titles" :key="index" label="标题(1-30字)" :name="`titles[${index}].title`" > <t-input :id="`title_${index}`" v-model="formData.titles[index].title" placeholder="请输入标题" :maxlength="30" show-limit-number allow-input-over-max @change="handleWordPackageDelete($event, 1, index)" /> <t-dropdown :options="wordPackageOptions" :min-column-width="100" @click="handleWordsPackage($event, 1, index)" > <span> <t-button variant="text"> <template #icon><wallet-icon /></template> 插入词包 </t-button> </span> </t-dropdown> <span v-if="formData.titles.length > 1" @click="handleRemoveElement(1, index)"> <t-button shape="square"> <template #icon><remove-icon size="2em" /></template> </t-button> </span> </t-form-item> <div> <t-button block :disabled="formData.titles.length === 10" @click="handleAddElement(1)"> 还可以添加{{ 10 - formData.titles.length }}个 </t-button> </div> </t-card> <!-- 描述 --> <t-card :title="`描述(${formData.descriptions.length}/10)`" header-bordered :style="{ width: '800px', margin: '20px 0 0 20px' }" > <template #subtitle> <t-tooltip theme="light"> <template #content> <div style="width: 280px"> 为使展示效果最佳,建议搜一搜描述文案字数 30 字以内 <t-image src="https://qzonestyle.gdtimg.com/gdt_ui_proj/imghub/dist/search-desc-tip.png?max_age=31536000" /> </div> </template> <help-circle-icon size="18px" /> </t-tooltip> </template> <t-form-item v-for="(descriptions, index) in formData.descriptions" :key="index" label="描述(4-80字)" :name="`descriptions[${index}].description`" > <t-input :id="`desc_${index}`" v-model="formData.descriptions[index].description" placeholder="请输入描述" :maxlength="80" show-limit-number allow-input-over-max @change="handleWordPackageDelete($event, 2, index)" /> <t-dropdown :options="wordPackageOptions" :min-column-width="100" @click="handleWordsPackage($event, 2, index)" > <span> <t-button variant="text"> <template #icon><wallet-icon /></template> 插入词包 </t-button> </span> </t-dropdown> <span v-if="formData.descriptions.length > 1" @click="handleRemoveElement(2, index)"> <t-button shape="square"> <template #icon><remove-icon size="2em" /></template> </t-button> </span> </t-form-item> <div> <t-button block :disabled="formData.descriptions.length === 10" @click="handleAddElement(2)"> 还可以添加{{ 10 - formData.descriptions.length }}个 </t-button> </div> </t-card> </t-form> </template> </t-tab-panel> </t-tabs> <!-- 插入词包 | 添加关键词弹窗 --> <t-dialog v-model:visible="addDefaultKeywordVisible" @close="handleAddDefaultKeywordCancel" @confirm="handleAddDefaultKeywordConfirm" > <template #header> <div> <span>添加默认关键词</span> <t-tooltip theme="light"> <template #content> <div style="width: 280px"> 插入关键词通配符的创意得到展现时,系统会根据匹配策略,将默认关键词替换为触发的关键词, 提高创意与用户搜索词之间的相关性,同时创意中和用户搜索词一致的词汇,可能得到飘红展示。 例如:如果广告主填写了“北京同城{鲜花}配送”,且购买了“玫瑰花”这个关键词,当用户搜索“北京哪里可以送玫瑰花”时, 广告创意可能以“北京同城玫瑰花配送”展示给用户。即默认关键词“鲜花”被替换成用户搜索词中包含的关键词“玫瑰花”。 <br /> 请为关键词词包设置默认关键词。触发的关键词替换时,如果用户搜索词过长, 直接替换可能超过字数限制,或替换后命中审核黑词,此时系统将以广告主设置的默认关键词来替换并展现。 </div> </template> <help-circle-icon size="18px" /> </t-tooltip> </div> </template> <t-input v-model="defaultKeyword" placeholder="请输入关键词" :maxlength="30" show-limit-number /> </t-dialog> </template> <script setup lang="ts"> import {ref} from 'vue' import { HelpCircleIcon, InfoCircleFilledIcon, RemoveIcon, WalletIcon } from 'tdesign-icons-vue-next'; import { MessagePlugin } from 'tdesign-vue-next'; const props = defineProps({ promote: Object, adcreativeId: String, }); const formData: any = ref({ dynamic_creative_elements: { title_options: [], description_options: [], image_options: [], video_options: [], brand: { brand_img: '', brand_name: '', }, }, page_type: 'PAGE_TYPE_CANVAS_WECHAT', page_spec: {}, dynamic_creative_name: '', // 数据中转 titles: [{ title: '' }], descriptions: [{ description: '' }], images: [], videos: [], brand: {}, }); const wordPackageOptions = [ { content: '城市', value: 1, extends: '{{city}}' }, { content: '日期', value: 2, extends: '{{day}}' }, { content: '星期', value: 3, extends: '{{week}}' }, { content: '关键词', value: 4 }, ]; const wordPackagesArray = ref(['{{city}}', '{{day}}', '{{week}}']); const addDefaultKeywordVisible = ref(false); const defaultKeyword = ref(''); // 处理词包 const handleWordPackageDelete = (val: string, type: number, index: number) => { let inputValue = val; inputValue = (inputValue += '') .replace(/([^{]+|^)({[^{}]*?}+)/g, (e, t) => t) .replace(/{{[^{}]*?}([^}]|$)/g, (e, t) => t) .replace(/{{([^{}]*?)}}/g, (e) => (wordPackagesArray.value.includes(e) ? e : '')); if (type === 1) formData.value.titles[index].title = inputValue; if (type === 2) formData.value.descriptions[index].description = inputValue; }; // 处理取消添加默认关键词 const handleAddDefaultKeywordCancel = () => { addDefaultKeywordVisible.value = false; defaultKeyword.value = ''; }; // 处理确定添加默认关键词 const handleAddDefaultKeywordConfirm = () => { const wordPackages = JSON.parse(localStorage.getItem('wordPackages')); const keyword = `{${defaultKeyword.value}}`; if (!wordPackagesArray.value.includes(keyword)) wordPackagesArray.value.push(keyword); handleWordsPackage({ extends: keyword }, wordPackages.type, wordPackages.index); addDefaultKeywordVisible.value = false; defaultKeyword.value = ''; }; // 处理词包插入 | NOTE 这里不考虑IE浏览器 const handleWordsPackage = (val: any, type: number, index: number) => { localStorage.setItem('wordPackages', JSON.stringify({ type, index })); const id = `#${['title_', 'desc_'][type - 1]}${index} input`; const input = document.querySelector(id) as HTMLInputElement; if (val.value === 4) addDefaultKeywordVisible.value = true; else input.setRangeText(val.extends); if (type === 1) formData.value.titles[index].title = input.value; if (type === 2) formData.value.descriptions[index].description = input.value; }; // TODO 标题和描述元素抽离成组件 // 处理标题和描述的元素的移除 const handleRemoveElement = (option: number, index: number) => { const { titles, descriptions } = formData.value; const options = { 1: () => { titles.splice(index, 1); }, 2: () => { descriptions.splice(index, 1); }, }; options[option](); }; // 处理标题和描述的元素添加 const handleAddElement = (option: number) => { const { titles, descriptions } = formData.value; const options = { 1: () => { titles.push({ title: '', }); }, 2: () => { descriptions.push({ description: '', }); }, }; options[option](); }; // 表单校验规则 const FORM_RULES = { title: [ { required: true, message: '请输入标题', type: 'error', trigger: 'blur' }, { validator: (val: any) => val.length <= 30, message: '标题字数不能超过30字', type: 'error', trigger: 'change', }, ], description: [ { required: true, message: '请输入描述', type: 'error', trigger: 'blur' }, { validator: (val: any) => val.length <= 80, message: '描述字数不能超过80字', type: 'error', trigger: 'change', }, ], }; </script> <style lang="less" scoped> :deep(.t-tab-panel) { margin-left: var(--td-comp-margin-xxxl); } .promote-label { margin-right: var(--td-comp-margin-xxl); } :deep(.t-input) { border: transparent; border-bottom: 1px solid var(--td-border-level-2-color) !important; } :deep(.t-card__title) { margin-right: var(--td-comp-margin-xs); } :deep(.t-icon) { vertical-align: sub; } :deep(.t-button--variant-text) { border-bottom: 1px solid var(--td-border-level-2-color); color: var(--td-text-color-secondary); } .sub-text { margin-top: 5px; font: var(--td-font-body-small); color: var(--td-text-color-placeholder); } :deep(.t-collapse-panel__icon--left) { position: relative; top: -26px; } .select__overlay-option .t-select-option { height: 100%; padding: 8px; } .user-option-info { margin-left: 16px; } .user-option { display: flex; } .select-panel-footer { border-top: 1px solid var(--td-component-stroke); padding: 6px; } </style>
学无止境,谦卑而行.