Files
nanxiisletAdmin/src/components/system/dict/DictItemDrawer.vue
2026-01-15 13:17:41 +08:00

275 lines
7.3 KiB
Vue

<template>
<a-drawer
:open="visible"
:title="`字典项管理 - ${dictData?.name || ''}`"
width="800"
:destroyOnClose="true"
@close="handleClose"
>
<div class="drawer-content">
<!-- 字典项操作栏 -->
<div class="item-toolbar">
<a-button type="primary" @click="handleAddItem">
<template #icon><PlusOutlined /></template>
新增字典项
</a-button>
</div>
<!-- 字典项表格 -->
<a-table
:columns="columns"
:data-source="tableData"
:loading="loading"
:pagination="false"
row-key="id"
size="small"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'status'">
<a-tag :color="record.status === 1 ? 'green' : 'red'" size="small">
{{ record.status === 1 ? '正常' : '禁用' }}
</a-tag>
</template>
<template v-if="column.dataIndex === 'isDefault'">
<a-tag v-if="record.isDefault === 1" color="blue"></a-tag>
<span v-else class="text-muted"></span>
</template>
<template v-if="column.dataIndex === 'action'">
<a-space>
<a-button type="link" size="small" @click="handleEditItem(record as DictItemRecord)">编辑</a-button>
<a-popconfirm
title="确定删除此字典项吗?"
@confirm="handleDeleteItem((record as DictItemRecord).id!)"
>
<a-button type="link" size="small" danger>删除</a-button>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
</div>
<!-- 新增/编辑字典项弹窗 -->
<a-modal
v-model:open="itemModalVisible"
:title="isEditItem ? '编辑字典项' : '新增字典项'"
@ok="handleSubmitItem"
:confirm-loading="submitLoading"
>
<a-form
ref="formRef"
:model="formData"
:rules="formRules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
>
<a-form-item label="字典项标签" name="label">
<a-input v-model:value="formData.label" placeholder="请输入显示的文本" />
</a-form-item>
<a-form-item label="字典项值" name="value">
<a-input v-model:value="formData.value" placeholder="请输入存储的值" />
</a-form-item>
<a-form-item label="排序" name="sort">
<a-input-number v-model:value="formData.sort" :min="0" :max="999" style="width: 100%" />
</a-form-item>
<a-form-item label="是否默认" name="isDefault">
<a-switch v-model:checked="formData.isDefault" :checked-value="1" :un-checked-value="0" />
</a-form-item>
<a-form-item label="状态" name="status">
<a-radio-group v-model:value="formData.status">
<a-radio :value="1">正常</a-radio>
<a-radio :value="0">禁用</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="备注" name="remark">
<a-textarea v-model:value="formData.remark" placeholder="请输入备注" :rows="2" />
</a-form-item>
</a-form>
</a-modal>
</a-drawer>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from 'vue'
import { message } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import type { FormInstance } from 'ant-design-vue'
import { getDictItems, createDictItem, updateDictItem, deleteDictItem } from '@/api/system/dict'
// 类型定义
interface DictRecord {
id: number
name: string
code: string
}
interface DictItemRecord {
id?: number
dictId?: number
label: string
value: string
sort: number
isDefault: number
status: number
remark?: string
}
const props = defineProps<{
visible: boolean
dictData?: DictRecord | null
}>()
const emit = defineEmits(['update:visible'])
// 表格
const loading = ref(false)
const tableData = ref<DictItemRecord[]>([])
const columns = [
{ title: '标签', dataIndex: 'label', width: 120 },
{ title: '值', dataIndex: 'value', width: 100 },
{ title: '排序', dataIndex: 'sort', width: 70 },
{ title: '默认', dataIndex: 'isDefault', width: 70 },
{ title: '状态', dataIndex: 'status', width: 70 },
{ title: '备注', dataIndex: 'remark', ellipsis: true },
{ title: '操作', dataIndex: 'action', width: 120, fixed: 'right' as const }
]
// 字典项表单
const itemModalVisible = ref(false)
const isEditItem = ref(false)
const submitLoading = ref(false)
const formRef = ref<FormInstance>()
const formData = reactive<DictItemRecord>({
id: undefined,
dictId: undefined,
label: '',
value: '',
sort: 0,
isDefault: 0,
status: 1,
remark: ''
})
const formRules = {
label: [{ required: true, message: '请输入字典项标签' }],
value: [{ required: true, message: '请输入字典项值' }]
}
// 监听 visible 变化,加载数据
watch(
() => props.visible,
(val) => {
if (val && props.dictData) {
loadData(props.dictData.id)
}
}
)
function handleClose() {
emit('update:visible', false)
}
// 加载字典项数据
async function loadData(dictId: number) {
loading.value = true
try {
const res = await getDictItems(dictId)
tableData.value = res.data.data || []
} catch (error) {
console.error('加载字典项失败:', error)
tableData.value = []
} finally {
loading.value = false
}
}
function handleAddItem() {
isEditItem.value = false
formData.id = undefined
formData.dictId = props.dictData?.id
formData.label = ''
formData.value = ''
formData.sort = 0
formData.isDefault = 0
formData.status = 1
formData.remark = ''
itemModalVisible.value = true
}
function handleEditItem(record: DictItemRecord) {
isEditItem.value = true
formData.id = record.id
formData.dictId = record.dictId
formData.label = record.label
formData.value = record.value
formData.sort = record.sort
formData.isDefault = record.isDefault
formData.status = record.status
formData.remark = record.remark || ''
itemModalVisible.value = true
}
async function handleDeleteItem(id: number) {
try {
await deleteDictItem(id)
message.success('删除成功')
if (props.dictData) {
loadData(props.dictData.id)
}
} catch (error: any) {
if (error?.response?.data?.message) {
message.error(error.response.data.message)
}
}
}
async function handleSubmitItem() {
try {
await formRef.value?.validate()
submitLoading.value = true
const data = {
dictId: formData.dictId!,
label: formData.label,
value: formData.value,
sort: formData.sort,
isDefault: formData.isDefault,
status: formData.status,
remark: formData.remark
}
if (isEditItem.value && formData.id) {
await updateDictItem(formData.id, data)
} else {
await createDictItem(data)
}
message.success(isEditItem.value ? '编辑成功' : '新增成功')
itemModalVisible.value = false
if (props.dictData) {
loadData(props.dictData.id)
}
} catch (error: any) {
if (error?.response?.data?.message) {
message.error(error.response.data.message)
}
} finally {
submitLoading.value = false
}
}
</script>
<style scoped>
.drawer-content {
height: 100%;
}
.item-toolbar {
margin-bottom: 16px;
}
.text-muted {
color: #999;
}
</style>