208 lines
6.1 KiB
Vue
208 lines
6.1 KiB
Vue
<!-- Author: fruit.com.cn -->
|
|
|
|
<script setup>
|
|
import { onMounted, reactive, ref } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { AppIcons } from '@/constants/icons'
|
|
import {
|
|
languageCodes,
|
|
languageLabel,
|
|
localesFromColumns,
|
|
payloadLocales
|
|
} from '@/constants/contentLanguages'
|
|
import {
|
|
createDramaCategoryApi,
|
|
deleteDramaCategoryApi,
|
|
listDramaCategoriesApi,
|
|
updateDramaCategoryApi
|
|
} from '@/api/admin/drama'
|
|
import { useRbac } from '@/stores/rbac'
|
|
import LocaleTextFields from '@/views/components/LocaleTextFields.vue'
|
|
import PageHeading from '@/views/components/PageHeading.vue'
|
|
|
|
const { can } = useRbac()
|
|
|
|
const loading = ref(false)
|
|
const list = ref([])
|
|
const dialogVisible = ref(false)
|
|
const saving = ref(false)
|
|
const editingId = ref(null)
|
|
const languages = ref(['zh'])
|
|
const form = reactive({ locales: { zh: '' }, sort: 1, status: 1 })
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
try {
|
|
const res = await listDramaCategoriesApi()
|
|
list.value = res.data || []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function nextSort() {
|
|
return list.value.reduce((max, item) => Math.max(max, item.sort || 0), 0) + 1
|
|
}
|
|
|
|
function openCreate() {
|
|
editingId.value = null
|
|
languages.value = ['zh']
|
|
Object.assign(form, { locales: { zh: '' }, sort: nextSort(), status: 1 })
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
function openEdit(row) {
|
|
editingId.value = row.id
|
|
const locales = localesFromColumns(row.locales, row.name, row.nameEn, row.nameId)
|
|
languages.value = languageCodes(locales)
|
|
Object.assign(form, { locales, sort: row.sort ?? 0, status: row.status ?? 1 })
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
async function saveCategory() {
|
|
const locales = payloadLocales(form.locales, languages.value)
|
|
const missing = languages.value.find((code) => !locales[code])
|
|
if (!locales.zh || missing) {
|
|
ElMessage.warning(missing ? `请填写${languageLabel(missing)}名称` : '请填写中文名称')
|
|
return
|
|
}
|
|
saving.value = true
|
|
try {
|
|
const payload = { name: locales.zh, locales, sort: form.sort, status: form.status }
|
|
if (editingId.value) {
|
|
await updateDramaCategoryApi(editingId.value, payload)
|
|
ElMessage.success('类型已更新')
|
|
} else {
|
|
await createDramaCategoryApi(payload)
|
|
ElMessage.success('类型已创建')
|
|
}
|
|
dialogVisible.value = false
|
|
await load()
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
async function confirmDelete(row) {
|
|
const count = row.dramaCount || 0
|
|
try {
|
|
await ElMessageBox.confirm(
|
|
count > 0
|
|
? `「${row.name}」下还有 ${count} 部短剧,删除会被拒绝。确定继续吗?`
|
|
: `确定删除类型「${row.name}」吗?`,
|
|
'删除确认',
|
|
{
|
|
confirmButtonText: '确认删除',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
confirmButtonClass: 'el-button--danger'
|
|
}
|
|
)
|
|
} catch {
|
|
return
|
|
}
|
|
await deleteDramaCategoryApi(row.id)
|
|
ElMessage.success('类型已删除')
|
|
await load()
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<template>
|
|
<PageHeading
|
|
eyebrow="内容管理"
|
|
title="类型管理"
|
|
description="维护短剧类型,供新建短剧选择,也作为客户端分类筛选的依据。"
|
|
>
|
|
<el-button v-if="can('drama:create')" type="primary" class="primary-button" @click="openCreate">
|
|
<AppIcon :icon="AppIcons.add" />
|
|
新建类型
|
|
</el-button>
|
|
</PageHeading>
|
|
|
|
<section class="panel">
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="list"
|
|
row-key="id"
|
|
border
|
|
class="drama-el-table"
|
|
empty-text="还没有短剧类型"
|
|
>
|
|
<el-table-column type="index" label="序号" width="80" />
|
|
<el-table-column prop="name" label="类型名称" min-width="160" />
|
|
<el-table-column label="语言" min-width="180">
|
|
<template #default="{ row }">
|
|
<el-tag
|
|
v-for="code in languageCodes(
|
|
row.locales || localesFromColumns(null, row.name, row.nameEn, row.nameId)
|
|
)"
|
|
:key="code"
|
|
effect="light"
|
|
class="locale-tag"
|
|
>{{ languageLabel(code) }}</el-tag
|
|
>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="短剧数" width="110">
|
|
<template #default="{ row }">{{ row.dramaCount || 0 }} 部</template>
|
|
</el-table-column>
|
|
<el-table-column prop="sort" label="排序" width="100" />
|
|
<el-table-column label="状态" width="110">
|
|
<template #default="{ row }">
|
|
<el-tag :type="row.status === 1 ? 'success' : 'info'" effect="light">{{
|
|
row.status === 1 ? '启用' : '停用'
|
|
}}</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" width="200" fixed="right">
|
|
<template #default="{ row }">
|
|
<div class="table-actions">
|
|
<el-button v-if="can('drama:update')" type="primary" size="small" @click="openEdit(row)"
|
|
>编辑</el-button
|
|
>
|
|
<el-button
|
|
v-if="can('drama:delete')"
|
|
type="danger"
|
|
size="small"
|
|
@click="confirmDelete(row)"
|
|
>删除</el-button
|
|
>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</section>
|
|
|
|
<el-dialog v-model="dialogVisible" :title="editingId ? '编辑类型' : '新建类型'" width="560px">
|
|
<el-form label-position="top">
|
|
<LocaleTextFields
|
|
v-model:languages="languages"
|
|
:locales="form.locales"
|
|
@update:locales="form.locales = $event"
|
|
label="名称"
|
|
:maxlength="64"
|
|
/>
|
|
<div class="form-row">
|
|
<el-form-item label="排序">
|
|
<el-input-number v-model="form.sort" :min="0" :max="999" />
|
|
</el-form-item>
|
|
<el-form-item label="状态">
|
|
<el-switch v-model="form.status" :active-value="1" :inactive-value="0" />
|
|
</el-form-item>
|
|
</div>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button type="primary" :loading="saving" @click="saveCategory">保存</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.locale-tag + .locale-tag {
|
|
margin-left: 6px;
|
|
}
|
|
</style>
|