Commit ca28f62e by lijiabin

【需求 20331】 feat: 优化选择标签组件、增加上传excel的组件

parent 0f477de8
<template> // 选中的可能是 可能是 多选: [id1,id2] 或者 'id1,id2' 单选 'id1' 单选 number这种不考虑处理了
<div class="tag-selector">
<!-- 已选标签区域 -->
<div class="selected-tags">
<!-- 添加标签按钮 -->
<el-popover placement="top" :width="300" trigger="click">
<template #reference>
<el-button class="button-new-tag" size="small"> + 添加标签 </el-button>
</template>
<!-- 暂时不加搜索输入框 -->
<!-- <div class="search-section mb-4">
<el-input
v-model="searchKeyword"
placeholder="Enter键添加标签"
class="search-input"
clearable
@keyup.enter="addTagFromSearch"
>
<template #suffix>
<span class="text-gray-400 text-sm"
>{{ searchKeyword.length }} / {{ maxTagLength }}</span
>
</template>
</el-input>
</div> -->
<!-- 推荐标签区域 -->
<div class="recommended-section">
<div class="section-title mb-3 text-gray-600 text-sm">官方标签列表</div>
<div class="tags-grid">
<el-tag
v-for="tag in filteredRecommendedTags"
:key="tag.id"
:type="arryrOfModelValue.includes(tag.id) ? 'primary' : 'info'"
:effect="arryrOfModelValue.includes(tag.id) ? 'dark' : 'plain'"
class="tag-item cursor-pointer mr-2 mb-2"
@click="toggleTag(tag.id)"
>
{{ tag.title }}
</el-tag>
</div>
</div>
</el-popover>
<div class="flex flex-wrap gap-2 mt-2">
<el-tag
v-for="tag in selectedTags"
:key="tag.id"
closable
type="primary"
@close="removeTag(tag.id)"
>
{{ tag.title }}
</el-tag>
</div>
</div>
</div>
</template>
// 选中的可能是 可能是 多选 [id1,id2] 或者 'id1,id2' 或者 (单选 number这种不考虑处理了
可以在父组件自己处理)
<script setup lang="ts" generic="T extends string | number[] | number"> <script setup lang="ts" generic="T extends string | number[] | number">
import { useTagsStore } from '@/stores/tags' import { useTagsStore } from '@/stores/tags'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import type { TagItemDto } from '@/api/tag/types' import type { TagItemDto } from '@/api/tag/types'
import type { SelectTagProps } from './types' import type { SelectTagProps } from './types'
const { maxSelectedTags = 1, filterTagsFn, tagType = 'culture' } = defineProps<SelectTagProps>() import { TagTypeEnum } from '@/constants'
const {
maxSelectedTags = 1,
filterTagsFn,
tagType = TagTypeEnum.CULTURE_TAG,
} = defineProps<SelectTagProps>()
const emit = defineEmits<{ const emit = defineEmits<{
selected: [tag?: TagItemDto] selected: [tag?: TagItemDto]
}>() }>()
const tagsStore = useTagsStore() const tagsStore = useTagsStore()
const { tagList: cultureTagList, relatedScenariosTagList } = storeToRefs(tagsStore) const {
tagList: cultureTagList,
const tagList = computed<TagItemDto[]>(() => relatedScenariosTagList,
tagType === 'culture' ? cultureTagList.value : relatedScenariosTagList.value, yearRecommendTagList,
) } = storeToRefs(tagsStore)
const tagList = computed<TagItemDto[]>(() => {
if (tagType === TagTypeEnum.CULTURE_TAG) {
return cultureTagList.value
} else if (tagType === TagTypeEnum.SCENE_TAG) {
return relatedScenariosTagList.value
} else if (tagType === TagTypeEnum.YEAR_TAG) {
return yearRecommendTagList.value
}
return []
})
const filterTags = computed(() => { const filterTags = computed(() => {
if (filterTagsFn) { if (filterTagsFn) {
...@@ -136,10 +92,10 @@ const filteredRecommendedTags = computed(() => { ...@@ -136,10 +92,10 @@ const filteredRecommendedTags = computed(() => {
const addTag = (tagId: number) => { const addTag = (tagId: number) => {
if (arryrOfModelValue.value.length >= maxSelectedTags) if (arryrOfModelValue.value.length >= maxSelectedTags)
return ElMessage.warning(`最多只能选择 ${maxSelectedTags} 个标签`) return ElMessage.warning({ message: `最多只能选择 ${maxSelectedTags} 个标签`, plain: true })
// 不能直接push 触发不了computed 的 set // 不能直接push 触发不了computed 的 set
arryrOfModelValue.value = [...arryrOfModelValue.value, tagId] arryrOfModelValue.value = [...arryrOfModelValue.value, tagId]
ElMessage.success('标签添加成功') ElMessage.success({ message: '标签添加成功', plain: true })
emit('selected', filterTags.value.find((tag) => tag.id === tagId)!) emit('selected', filterTags.value.find((tag) => tag.id === tagId)!)
} }
...@@ -160,3 +116,63 @@ const toggleTag = (tagId: number) => { ...@@ -160,3 +116,63 @@ const toggleTag = (tagId: number) => {
} }
} }
</script> </script>
<template>
<div class="tag-selector">
<!-- 已选标签区域 -->
<div class="selected-tags">
<!-- 添加标签按钮 -->
<el-popover placement="top" :width="300" trigger="click">
<template #reference>
<el-button class="button-new-tag" size="small"> + 添加标签 </el-button>
</template>
<!-- 暂时不加搜索输入框 -->
<!-- <div class="search-section mb-4">
<el-input
v-model="searchKeyword"
placeholder="Enter键添加标签"
class="search-input"
clearable
@keyup.enter="addTagFromSearch"
>
<template #suffix>
<span class="text-gray-400 text-sm"
>{{ searchKeyword.length }} / {{ maxTagLength }}</span
>
</template>
</el-input>
</div> -->
<!-- 推荐标签区域 -->
<div class="recommended-section">
<div class="section-title mb-3 text-gray-600 text-sm">官方标签列表</div>
<div class="tags-grid">
<el-tag
v-for="tag in filteredRecommendedTags"
:key="tag.id"
:type="arryrOfModelValue.includes(tag.id) ? 'primary' : 'info'"
:effect="arryrOfModelValue.includes(tag.id) ? 'dark' : 'plain'"
class="tag-item cursor-pointer mr-2 mb-2"
@click="toggleTag(tag.id)"
>
{{ tag.title }}
</el-tag>
</div>
</div>
</el-popover>
<div class="flex flex-wrap gap-2 mt-2">
<el-tag
v-for="tag in selectedTags"
:key="tag.id"
closable
type="primary"
@close="removeTag(tag.id)"
>
{{ tag.title }}
</el-tag>
</div>
</div>
</div>
</template>
import type { TagItemDto } from '@/api/tag/types' import type { TagItemDto } from '@/api/tag/types'
import { TagTypeEnum } from '@/constants'
export type SelectTagProps = { export type SelectTagProps = {
maxSelectedTags?: number maxSelectedTags?: number
filterTagsFn?: (tags: TagItemDto[]) => TagItemDto[] filterTagsFn?: (tags: TagItemDto[]) => TagItemDto[]
tagType?: 'culture' | 'related_scenarios' tagType?: TagTypeEnum
} }
// 导入excel相关
<script setup lang="tsx" generic="T">
import type { BackendServiceResult } from '@/utils/request/types'
const props = defineProps<{
api: (file: File, onProgress?: (progress: number) => void) => Promise<BackendServiceResult<T[]>>
}>()
const emit = defineEmits<{
success: []
error: [errorList: T[]]
}>()
const fileInput = useTemplateRef<HTMLInputElement>('fileInput')
const uploadProgress = ref(0)
const handleChange = async (e: Event) => {
uploadProgress.value = 0
const file = (e.target as HTMLInputElement).files?.[0]
if (!file) return
const notificationInstance = ElNotification({
title: '上传文件进度',
message: () => (
<div>
<el-progress
indeterminate
percentage={uploadProgress.value}
duration={0}
status={uploadProgress.value === 100 ? 'success' : ''}
/>
</div>
),
duration: 0,
})
try {
await props.api(file, (progress) => {
uploadProgress.value = progress
})
setTimeout(() => {
ElMessage.success('上传成功')
}, 1000)
emit('success')
} catch (error: any) {
// 解析失败信息
console.error(error)
if (error.code === 501) {
// 部分上传的数据不对 有错误
emit('error', error.data as T[])
} else {
console.error(error)
emit('error', [])
}
} finally {
;(e.target as HTMLInputElement).value = ''
setTimeout(() => {
notificationInstance.close()
setTimeout(() => {
uploadProgress.value = 0
}, 300)
}, 1500)
}
}
</script>
<template>
<el-button :loading="uploadProgress > 0" type="primary" @click="fileInput?.click()">
<input type="file" accept=".xlsx" ref="fileInput" @change="handleChange" class="hidden" />
<el-icon v-show="uploadProgress === 0"><IEpUpload /></el-icon>
导入
</el-button>
</template>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment