TDesign——Input指定光标插入内容

2024-08-15 09:48:49 浏览数 (1)

前言

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>

0 人点赞