fix(工单): 提交时校验

This commit is contained in:
Boen_Shi 2026-06-18 23:28:38 +08:00
parent c6be29d756
commit acea3b32f6

View File

@ -75,7 +75,17 @@
<el-cascader v-model='ticketForm.ticket_category_id' class='w-full' placeholder='请选择分类' :options='activeCategoryOptions' :props='categoryCascaderProps' /> <el-cascader v-model='ticketForm.ticket_category_id' class='w-full' placeholder='请选择分类' :options='activeCategoryOptions' :props='categoryCascaderProps' />
</el-form-item> </el-form-item>
<el-form-item label='标题'><el-input v-model='ticketForm.title' /></el-form-item> <el-form-item label='标题'><el-input v-model='ticketForm.title' /></el-form-item>
<el-form-item label='内容'><el-input v-model='ticketForm.content' type='textarea' :rows='6' /></el-form-item> <el-form-item label='内容'>
<div class='ticket-content-field'>
<el-input v-model='ticketForm.content' type='textarea' :rows='6' />
<div v-if='selectedCategoryDescriptions.length > 0' class='category-descriptions'>
<div v-for='item in selectedCategoryDescriptions' :key='item.id' class='category-description-line'>
<span class='category-description-name'>{{ item.name }}</span>
<span>{{ item.description }}</span>
</div>
</div>
</div>
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click='ticketDialogVisible = false'>取消</el-button> <el-button @click='ticketDialogVisible = false'>取消</el-button>
@ -193,7 +203,6 @@ const perPage = ref(20)
const filters = reactive<any>({ status: '', category_id: null, mine: false }) const filters = reactive<any>({ status: '', category_id: null, mine: false })
const categories = ref<any[]>([]) const categories = ref<any[]>([])
const categoryTree = ref<any[]>([]) const categoryTree = ref<any[]>([])
const activeCategories = computed(() => categories.value.filter((item) => item.is_active))
const categoryCascaderProps = { value: 'id', label: 'name', checkStrictly: true, emitPath: false } const categoryCascaderProps = { value: 'id', label: 'name', checkStrictly: true, emitPath: false }
const categoryOptions = computed(() => buildCategoryOptions(categoryTree.value)) const categoryOptions = computed(() => buildCategoryOptions(categoryTree.value))
const activeCategoryOptions = computed(() => buildCategoryOptions(categoryTree.value, true)) const activeCategoryOptions = computed(() => buildCategoryOptions(categoryTree.value, true))
@ -262,14 +271,22 @@ function handleSizeChange(nextSize: number): void {
} }
function openCreateTicket(): void { function openCreateTicket(): void {
Object.assign(ticketForm, { ticket_category_id: activeCategories.value[0]?.id || null, title: '', content: '' }) Object.assign(ticketForm, { ticket_category_id: null, title: '', content: '' })
ticketDialogVisible.value = true ticketDialogVisible.value = true
} }
async function submitTicket(): Promise<void> { async function submitTicket(): Promise<void> {
const ticketCategoryId = selectedCategoryId(ticketForm.ticket_category_id) const ticketCategoryId = selectedCategoryId(ticketForm.ticket_category_id)
if (!ticketCategoryId || !ticketForm.title?.trim() || !ticketForm.content?.trim()) { if (!ticketCategoryId) {
ElMessage.warning('请完整填写工单信息') ElMessage.warning('请选择工单分类')
return
}
if (!ticketForm.title?.trim()) {
ElMessage.warning('请输入工单标题')
return
}
if (!ticketForm.content?.trim()) {
ElMessage.warning('请输入工单内容')
return return
} }
savingTicket.value = true savingTicket.value = true
@ -415,6 +432,21 @@ function categoryLabel(categoryId: number | string | null | undefined): string {
return path.length > 0 ? path.join(' / ') : '-' return path.length > 0 ? path.join(' / ') : '-'
} }
const selectedCategoryDescriptions = computed(() => {
const categoryId = selectedCategoryId(ticketForm.ticket_category_id)
if (!categoryId) {
return []
}
return findCategoryNodes(categoryTree.value, categoryId)
.filter((item) => String(item.description || '').trim() !== '')
.map((item) => ({
id: item.id,
name: item.name,
description: String(item.description || '').trim(),
}))
})
function findCategoryPath(items: any[], categoryId: number, parents: string[] = []): string[] { function findCategoryPath(items: any[], categoryId: number, parents: string[] = []): string[] {
for (const item of items) { for (const item of items) {
const nextPath = [...parents, item.name] const nextPath = [...parents, item.name]
@ -430,6 +462,21 @@ function findCategoryPath(items: any[], categoryId: number, parents: string[] =
return [] return []
} }
function findCategoryNodes(items: any[], categoryId: number, parents: any[] = []): any[] {
for (const item of items) {
const nextPath = [...parents, item]
if (Number(item.id) === categoryId) {
return nextPath
}
const childPath = findCategoryNodes(item.children || [], categoryId, nextPath)
if (childPath.length > 0) {
return childPath
}
}
return []
}
async function removeCategory(row: any): Promise<void> { async function removeCategory(row: any): Promise<void> {
await ElMessageBox.confirm(`确认删除分类「${row.name}」吗?历史工单会保留但分类置空。`, '提示', { type: 'warning' }) await ElMessageBox.confirm(`确认删除分类「${row.name}」吗?历史工单会保留但分类置空。`, '提示', { type: 'warning' })
await ticketsApi.removeCategory(row.id) await ticketsApi.removeCategory(row.id)
@ -474,4 +521,8 @@ onMounted(async () => {
.message-box { border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; background: #f8fafc; } .message-box { border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; background: #f8fafc; }
.message-sender { font-size: 12px; color: #64748b; margin-bottom: 6px; } .message-sender { font-size: 12px; color: #64748b; margin-bottom: 6px; }
.message-content { white-space: pre-wrap; color: #0f172a; } .message-content { white-space: pre-wrap; color: #0f172a; }
.ticket-content-field { width: 100%; display: flex; flex-direction: column; gap: 10px; }
.category-descriptions { border: 1px solid #e5e7eb; border-radius: 6px; background: #f8fafc; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
.category-description-line { color: #334155; line-height: 1.5; word-break: break-word; }
.category-description-name { color: #111827; font-weight: 600; margin-right: 8px; }
</style> </style>