前言
setRangeText
: setRangeText
在线预览
:wordPackage
内容
代码语言:javascript复制<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>