| <template> | 
|   <ProFieldCustom> | 
|     <div class="select-tags-wrapper"> | 
|       <div class="select-tags-top-view"> | 
|         <el-radio-group v-model="state.tagMode"> | 
|           <el-radio-button class="select-tags-top-view-radio" :value="TagMode.System" | 
|             >系统标签</el-radio-button | 
|           > | 
|           <el-radio-button class="select-tags-top-view-radio" :value="TagMode.Custom" | 
|             >自定义</el-radio-button | 
|           > | 
|         </el-radio-group> | 
|         <template v-if="state.tagMode === TagMode.Custom"> | 
|           <el-input | 
|             class="select-tags-top-view-input" | 
|             v-model.trim="state.customInputValue" | 
|             placeholder="请输入新标签" | 
|             :maxlength="8" | 
|             :validate-event="false" | 
|           /> | 
|           <el-button class="add-tags-button" link type="primary" @click="handleAddTag"> | 
|             添加标签 <el-icon> <circle-plus /> </el-icon | 
|           ></el-button> | 
|         </template> | 
|       </div> | 
|       <div class="select-tags-content-view"> | 
|         <div class="selected-tags-wrapper"> | 
|           <div class="selected-tags-title"> | 
|             已选择标签({{ props.modelValue.length }}/{{ maxSelectedTagLength }}) | 
|           </div> | 
|           <TagGroup> | 
|             <TagGroupItem | 
|               v-for="tag in props.modelValue" | 
|               :key="tag.value" | 
|               showCloseBtn | 
|               @close="hanldeRemoveSelectedTag(tag)" | 
|               >{{ tag.text }}</TagGroupItem | 
|             > | 
|           </TagGroup> | 
|         </div> | 
|         <div class="select-tags-list-wrapper"> | 
|           <div class="select-tags-list-tab"> | 
|             <div | 
|               v-for="group in groupTagList" | 
|               :key="group.value" | 
|               :class="['select-tags-list-tab-item', { active: group.value === state.activeTab }]" | 
|               @click="handleChangeTab(group.value)" | 
|             > | 
|               {{ group.label }} | 
|             </div> | 
|           </div> | 
|           <TagGroup> | 
|             <TagGroupItem | 
|               v-for="tag in groupTagOptions" | 
|               :key="tag.value" | 
|               can-select | 
|               :active="props.modelValue.some((item) => item.value === tag.value)" | 
|               @click="handleSelectTag(tag)" | 
|               :show-close-btn="tag.canDelete" | 
|               @close="handleDeleteOption(tag)" | 
|               >{{ tag.text }}</TagGroupItem | 
|             > | 
|           </TagGroup> | 
|         </div> | 
|       </div> | 
|     </div> | 
|     <template #readContent>{{ props.modelValue.map((x) => x.text).join(',') }}</template> | 
|   </ProFieldCustom> | 
| </template> | 
|   | 
| <script setup lang="ts"> | 
| import { Message } from '@bole-core/core'; | 
| import TagGroup from './TagGroup.vue'; | 
| import TagGroupItem from './TagGroupItem.vue'; | 
| import { | 
|   selectTagsProps, | 
|   GroupTagListItemOption, | 
|   SelectedTag, | 
|   GroupTagListItem, | 
| } from './selectTags'; | 
| import { useFormItem } from 'element-plus'; | 
| import { ProFieldCustom } from '@bole-core/components'; | 
|   | 
| defineOptions({ | 
|   name: 'SelectTags', | 
| }); | 
|   | 
| const { formItem } = useFormItem(); | 
|   | 
| const props = defineProps(selectTagsProps); | 
|   | 
| const emit = defineEmits<{ | 
|   (e: 'update:modelValue', value: SelectedTag[]): void; | 
|   (e: 'addTag', value: string): void; | 
| }>(); | 
|   | 
| enum TagMode { | 
|   System = 1, | 
|   Custom, | 
| } | 
|   | 
| const state = reactive({ | 
|   tagMode: TagMode.System, | 
|   customInputValue: '', | 
|   activeTab: props.groupTagList?.[0]?.value ?? '', | 
| }); | 
|   | 
| const flatTagList = (groupList: GroupTagListItem[]) => { | 
|   let list: GroupTagListItemOption[] = []; | 
|   groupList.forEach((item) => { | 
|     list.push(...item.options); | 
|   }); | 
|   return list; | 
| }; | 
|   | 
| watch( | 
|   () => props.groupTagList, | 
|   (newGroupTagList, oldGroupTagList) => { | 
|     if (!props.groupTagList.some((x) => x.value === state.activeTab)) { | 
|       state.activeTab = props.groupTagList?.[0]?.value ?? ''; | 
|     } | 
|   | 
|     if (!oldGroupTagList?.length && newGroupTagList?.length) { | 
|       const tagList = flatTagList(props.groupTagList); | 
|   | 
|       emit( | 
|         'update:modelValue', | 
|         props.modelValue.filter((item) => tagList.some((x) => x.value === item.value)) | 
|       ); | 
|     } | 
|   }, | 
|   { | 
|     immediate: true, | 
|   } | 
| ); | 
|   | 
| function updateModelValue(val: SelectedTag[]) { | 
|   emit('update:modelValue', val); | 
|   formItem?.validate?.('select'); | 
| } | 
|   | 
| const groupTagOptions = computed(() => { | 
|   const group = props.groupTagList.find((item) => item.value === state.activeTab); | 
|   if (!group) { | 
|     return []; | 
|   } | 
|   return group.options; | 
| }); | 
|   | 
| function handleChangeTab(value: string) { | 
|   state.activeTab = value; | 
| } | 
|   | 
| function handleSelectTag(tag: GroupTagListItemOption) { | 
|   if (props.modelValue.some((item) => item.value === tag.value)) { | 
|     hanldeRemoveSelectedTag(tag); | 
|   } else { | 
|     if (props.modelValue.length >= props.maxSelectedTagLength) { | 
|       Message.warnMessage(`最多选择${props.maxSelectedTagLength}个标签`); | 
|       return; | 
|     } | 
|     updateModelValue([...props.modelValue, { text: tag.text, value: tag.value }]); | 
|   } | 
| } | 
|   | 
| function hanldeRemoveSelectedTag(tag: SelectedTag) { | 
|   updateModelValue(props.modelValue.filter((item) => item.value !== tag.value)); | 
| } | 
|   | 
| function handleAddTag() { | 
|   if (!state.customInputValue) { | 
|     Message.warnMessage('请输入新标签'); | 
|     return; | 
|   } | 
|   emit('addTag', state.customInputValue); | 
|   state.customInputValue = ''; | 
| } | 
|   | 
| async function handleDeleteOption(option: GroupTagListItemOption) { | 
|   try { | 
|     if (props.onDeleteCustomOption) { | 
|       await props.onDeleteCustomOption(option); | 
|     } | 
|     updateModelValue(props.modelValue.filter((item) => item.value !== option.value)); | 
|   } catch (error) {} | 
| } | 
|   | 
| // function handleAddTagChange(event) { | 
| //   console.log('event: ', event); | 
| // } | 
| </script> | 
|   | 
| <style lang="scss" scoped> | 
| @use '@/style/common.scss' as *; | 
|   | 
| .select-tags-wrapper { | 
|   width: 100%; | 
|   | 
|   .select-tags-top-view { | 
|     display: flex; | 
|     align-items: center; | 
|   | 
|     .select-tags-top-view-radio { | 
|       :deep(.el-radio-button__inner) { | 
|         width: 120px; | 
|       } | 
|     } | 
|   | 
|     .select-tags-top-view-input { | 
|       margin-left: 16px; | 
|       width: 200px; | 
|   | 
|       :deep(.el-input__wrapper) { | 
|         box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset; | 
|       } | 
|     } | 
|   | 
|     .add-tags-button { | 
|       margin-left: 16px; | 
|   | 
|       .el-icon { | 
|         margin-left: 6px; | 
|       } | 
|     } | 
|   } | 
|   | 
|   .select-tags-content-view { | 
|     margin-top: 20px; | 
|     padding: 28px 24px 30px 34px; | 
|     border: 1px solid getCssVar('border', 'color'); | 
|     border-radius: 4px; | 
|   | 
|     .selected-tags-wrapper { | 
|       margin-bottom: 20px; | 
|       padding-bottom: 20px; | 
|       border-bottom: 1px solid getCssVar('border', 'color'); | 
|   | 
|       .selected-tags-title { | 
|         margin-bottom: 22px; | 
|         font-size: 16px; | 
|         color: getCssVar('text-color', 'primary'); | 
|         line-height: 1; | 
|       } | 
|     } | 
|   | 
|     .select-tags-list-wrapper { | 
|       .select-tags-list-tab { | 
|         display: flex; | 
|         margin-bottom: 20px; | 
|   | 
|         .select-tags-list-tab-item { | 
|           margin-right: 34px; | 
|           height: 27px; | 
|           font-size: 16px; | 
|           color: getCssVar('text-color', 'primary'); | 
|           box-sizing: border-box; | 
|           line-height: 1; | 
|           cursor: pointer; | 
|   | 
|           &:last-child { | 
|             margin-right: 0; | 
|           } | 
|   | 
|           &.active { | 
|             border-bottom: 2px solid getCssVar('color', 'primary'); | 
|             color: getCssVar('color', 'primary'); | 
|           } | 
|         } | 
|       } | 
|     } | 
|   } | 
| } | 
| </style> |