feat(admin): 短剧类型与内容类型多选

This commit is contained in:
Zhou Weidong committed 2026-10-21 17:01:22 +08:00
1 parent 58380170b0
commit 9e427d485f
3 files changed
+95 -27

No files matched your search

+1
View File
@@ -15,6 +15,7 @@ export interface DramaPageParams {
keyword?: string keyword?: string
status?: string status?: string
categoryId?: number categoryId?: number
categoryIds?: number[]
isHot?: boolean isHot?: boolean
dramaTypes?: Array<'ANIME' | 'LIVE'> dramaTypes?: Array<'ANIME' | 'LIVE'>
} }
+5
View File
@@ -20,12 +20,15 @@ export interface DramaItem {
title: string title: string
subtitle?: string subtitle?: string
categoryId?: number categoryId?: number
categoryIds?: number[]
categoryNames?: string[]
category?: string category?: string
genre?: string genre?: string
description?: string description?: string
cover?: string cover?: string
language?: string language?: string
dramaType?: 'ANIME' | 'LIVE' dramaType?: 'ANIME' | 'LIVE'
dramaTypes?: Array<'ANIME' | 'LIVE'>
episodes?: number episodes?: number
freeEpisodes?: number freeEpisodes?: number
unlockCoin?: number unlockCoin?: number
@@ -47,11 +50,13 @@ export interface DramaSaveData {
title: string title: string
subtitle?: string subtitle?: string
categoryId?: number categoryId?: number
categoryIds?: number[]
genre?: string genre?: string
description?: string description?: string
cover?: string cover?: string
language?: string language?: string
dramaType?: 'ANIME' | 'LIVE' dramaType?: 'ANIME' | 'LIVE'
dramaTypes?: Array<'ANIME' | 'LIVE'>
freeEpisodes?: number freeEpisodes?: number
unlockCoin?: number unlockCoin?: number
favoriteCount?: number favoriteCount?: number
@@ -29,8 +29,8 @@ const categories = ref([])
const keyword = ref('') const keyword = ref('')
const statusFilter = ref(undefined) const statusFilter = ref(undefined)
const categoryId = ref(undefined) const categoryIds = ref([])
const dramaTypeFilter = ref(undefined) const dramaTypes = ref([])
const hotFilter = ref(undefined) const hotFilter = ref(undefined)
const pageIndex = ref(1) const pageIndex = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
@@ -47,8 +47,8 @@ const editingLikeCount = ref(0)
const formRef = ref() const formRef = ref()
const form = reactive({ const form = reactive({
title: '', title: '',
categoryId: undefined, categoryIds: [],
dramaType: 'LIVE', dramaTypes: ['LIVE'],
description: '', description: '',
freeEpisodes: 3, freeEpisodes: 3,
unlockCoin: 0, unlockCoin: 0,
@@ -63,8 +63,12 @@ const COVER_TYPES = ['image/jpeg', 'image/png', 'image/webp']
const formRules = { const formRules = {
title: [{ required: true, message: '请输入短剧名称', trigger: 'blur' }], title: [{ required: true, message: '请输入短剧名称', trigger: 'blur' }],
categoryId: [{ required: true, message: '请选择短剧类型', trigger: 'change' }], categoryIds: [
dramaType: [{ required: true, message: '请选择内容类型', trigger: 'change' }] { type: 'array', required: true, min: 1, message: '请至少选择一个类型', trigger: 'change' }
],
dramaTypes: [
{ type: 'array', required: true, min: 1, message: '请至少选择一个内容类型', trigger: 'change' }
]
} }
const coverDisplayName = computed( const coverDisplayName = computed(
@@ -106,8 +110,8 @@ async function load() {
pageSize: pageSize.value, pageSize: pageSize.value,
keyword: keyword.value.trim() || undefined, keyword: keyword.value.trim() || undefined,
status: statusFilter.value || undefined, status: statusFilter.value || undefined,
categoryId: categoryId.value, categoryIds: categoryIds.value.length ? categoryIds.value : undefined,
dramaTypes: dramaTypeFilter.value ? [dramaTypeFilter.value] : undefined, dramaTypes: dramaTypes.value.length ? dramaTypes.value : undefined,
isHot: hotFilter.value isHot: hotFilter.value
}) })
list.value = res.data?.list || [] list.value = res.data?.list || []
@@ -130,8 +134,8 @@ function search() {
function resetFilters() { function resetFilters() {
keyword.value = '' keyword.value = ''
statusFilter.value = undefined statusFilter.value = undefined
categoryId.value = undefined categoryIds.value = []
dramaTypeFilter.value = undefined dramaTypes.value = []
hotFilter.value = undefined hotFilter.value = undefined
search() search()
} }
@@ -156,8 +160,8 @@ function openCreate() {
editingId.value = null editingId.value = null
Object.assign(form, { Object.assign(form, {
title: '', title: '',
categoryId: undefined, categoryIds: [],
dramaType: 'LIVE', dramaTypes: ['LIVE'],
description: '', description: '',
freeEpisodes: 3, freeEpisodes: 3,
unlockCoin: 0, unlockCoin: 0,
@@ -178,8 +182,18 @@ function openEdit(row) {
editingId.value = row.id editingId.value = row.id
Object.assign(form, { Object.assign(form, {
title: row.title, title: row.title,
categoryId: row.categoryId || undefined, categoryIds:
dramaType: row.dramaType || 'LIVE', Array.isArray(row.categoryIds) && row.categoryIds.length
? row.categoryIds
: row.categoryId
? [row.categoryId]
: [],
dramaTypes:
Array.isArray(row.dramaTypes) && row.dramaTypes.length
? row.dramaTypes
: row.dramaType
? [row.dramaType]
: ['LIVE'],
description: row.description || '', description: row.description || '',
freeEpisodes: row.freeEpisodes ?? 3, freeEpisodes: row.freeEpisodes ?? 3,
unlockCoin: row.unlockCoin ?? 0, unlockCoin: row.unlockCoin ?? 0,
@@ -200,6 +214,16 @@ function dramaTypeLabel(value) {
return dramaTypeOptions.find((item) => item.value === value)?.label || '真人剧' return dramaTypeOptions.find((item) => item.value === value)?.label || '真人剧'
} }
function rowCategoryNames(row) {
const names = Array.isArray(row.categoryNames) ? row.categoryNames.filter(Boolean) : []
return names.length ? names.join(' / ') : row.genre || '—'
}
function rowDramaTypes(row) {
if (Array.isArray(row.dramaTypes) && row.dramaTypes.length) return row.dramaTypes
return row.dramaType ? [row.dramaType] : []
}
async function saveDrama() { async function saveDrama() {
if (saving.value || uploadingCover.value) return if (saving.value || uploadingCover.value) return
try { try {
@@ -207,11 +231,13 @@ async function saveDrama() {
} catch { } catch {
return return
} }
const category = categories.value.find((item) => item.id === form.categoryId) const categoryNames = form.categoryIds
.map((id) => categories.value.find((item) => item.id === id)?.name)
.filter(Boolean)
const payload = { const payload = {
...form, ...form,
title: form.title.trim(), title: form.title.trim(),
genre: category?.name || '' genre: categoryNames.join(' / ')
} }
saving.value = true saving.value = true
try { try {
@@ -366,13 +392,27 @@ onMounted(async () => {
:key="item.code" :key="item.code"
:label="item.text" :label="item.text"
:value="item.code" /></el-select :value="item.code" /></el-select
><el-select v-model="categoryId" clearable placeholder="全部类型" @change="search" ><el-select
v-model="categoryIds"
multiple
collapse-tags
collapse-tags-tooltip
clearable
placeholder="全部类型"
@change="search"
><el-option ><el-option
v-for="item in categories" v-for="item in categories"
:key="item.id" :key="item.id"
:label="item.name" :label="item.name"
:value="item.id" /></el-select :value="item.id" /></el-select
><el-select v-model="dramaTypeFilter" clearable placeholder="全部内容类型" @change="search" ><el-select
v-model="dramaTypes"
multiple
collapse-tags
collapse-tags-tooltip
clearable
placeholder="全部内容类型"
@change="search"
><el-option ><el-option
v-for="item in dramaTypeOptions" v-for="item in dramaTypeOptions"
:key="item.value" :key="item.value"
@@ -416,12 +456,20 @@ onMounted(async () => {
><span v-else class="table-empty-text">—</span></template ><span v-else class="table-empty-text">—</span></template
></el-table-column ></el-table-column
> >
<el-table-column prop="genre" label="类型" width="110" /> <el-table-column label="类型" min-width="150" show-overflow-tooltip
<el-table-column label="内容类型" width="110" ><template #default="{ row }">{{ rowCategoryNames(row) }}</template>
</el-table-column>
<el-table-column label="内容类型" min-width="150"
><template #default="{ row }" ><template #default="{ row }"
><el-tag :type="row.dramaType === 'ANIME' ? 'warning' : 'info'" effect="light">{{ ><div class="table-actions"
dramaTypeLabel(row.dramaType) ><el-tag
}}</el-tag></template v-for="type in rowDramaTypes(row)"
:key="type"
:type="type === 'ANIME' ? 'warning' : 'info'"
effect="light"
>{{ dramaTypeLabel(type) }}</el-tag
></div
></template
></el-table-column ></el-table-column
> >
<el-table-column label="集数" width="90" <el-table-column label="集数" width="90"
@@ -533,8 +581,15 @@ onMounted(async () => {
<el-form-item label="短剧名称" prop="title"> <el-form-item label="短剧名称" prop="title">
<el-input v-model="form.title" maxlength="80" placeholder="请输入短剧名称" /> <el-input v-model="form.title" maxlength="80" placeholder="请输入短剧名称" />
</el-form-item> </el-form-item>
<el-form-item label="类型" prop="categoryId"> <el-form-item label="类型" prop="categoryIds">
<el-select v-model="form.categoryId" placeholder="请选择类型"> <el-select
v-model="form.categoryIds"
multiple
collapse-tags
collapse-tags-tooltip
filterable
placeholder="请选择类型"
>
<el-option <el-option
v-for="item in categories" v-for="item in categories"
:key="item.id" :key="item.id"
@@ -543,8 +598,15 @@ onMounted(async () => {
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="内容类型" prop="dramaType"> <el-form-item label="内容类型" prop="dramaTypes">
<el-select v-model="form.dramaType" placeholder="请选择内容类型"> <el-select
v-model="form.dramaTypes"
multiple
collapse-tags
collapse-tags-tooltip
filterable
placeholder="请选择内容类型"
>
<el-option <el-option
v-for="item in dramaTypeOptions" v-for="item in dramaTypeOptions"
:key="item.value" :key="item.value"