335 lines
9.6 KiB
Vue
335 lines
9.6 KiB
Vue
<template>
|
||
<el-dialog
|
||
:title="isEditing ? '编辑字典项' : '添加字典项'"
|
||
v-model="visible"
|
||
width="600px"
|
||
:close-on-click-modal="false"
|
||
@close="handleClose"
|
||
>
|
||
<el-form
|
||
:model="dictItemForm"
|
||
:rules="formRules"
|
||
ref="dictItemFormRef"
|
||
label-width="100px"
|
||
>
|
||
<el-form-item label="字典类型" prop="dict_type_id">
|
||
<el-input
|
||
:value="dictTypeDisplayName"
|
||
disabled
|
||
style="width: 100%"
|
||
/>
|
||
<div class="form-tip">当前字典类型:{{ dictTypeDisplayName }}</div>
|
||
</el-form-item>
|
||
<el-form-item label="字典标签" prop="dict_label">
|
||
<el-input
|
||
v-model="dictItemForm.dict_label"
|
||
placeholder="请输入字典标签(显示值)"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="字典值" prop="dict_value">
|
||
<el-input
|
||
v-model="dictItemForm.dict_value"
|
||
placeholder="请输入字典值(存储值)"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="父级字典项" prop="parent_id">
|
||
<el-select
|
||
v-model="dictItemForm.parent_id"
|
||
placeholder="选择父级字典项(可选)"
|
||
clearable
|
||
filterable
|
||
style="width: 100%"
|
||
>
|
||
<el-option
|
||
v-for="item in parentDictItems"
|
||
:key="item.id"
|
||
:label="item.dict_label"
|
||
:value="item.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="状态" prop="status">
|
||
<el-radio-group v-model="dictItemForm.status">
|
||
<el-radio :value="1">启用</el-radio>
|
||
<el-radio :value="0">禁用</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="排序" prop="sort">
|
||
<el-input-number
|
||
v-model="dictItemForm.sort"
|
||
:min="0"
|
||
style="width: 100%"
|
||
placeholder="数字越小越靠前"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="颜色" prop="color">
|
||
<el-input
|
||
v-model="dictItemForm.color"
|
||
placeholder="请输入颜色值(如:#FF0000 或 red)"
|
||
>
|
||
<template #append>
|
||
<el-color-picker
|
||
v-model="dictItemForm.color"
|
||
:predefine="predefineColors"
|
||
/>
|
||
</template>
|
||
</el-input>
|
||
<div class="form-tip">支持十六进制颜色值(如:#FF0000)或颜色名称(如:red)</div>
|
||
</el-form-item>
|
||
<el-form-item label="图标" prop="icon">
|
||
<el-input
|
||
v-model="dictItemForm.icon"
|
||
placeholder="请输入图标(如:✓、✗、⭐)"
|
||
/>
|
||
<div class="form-tip">支持emoji或图标字符</div>
|
||
</el-form-item>
|
||
<el-form-item label="备注" prop="remark">
|
||
<el-input
|
||
v-model="dictItemForm.remark"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入备注"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="handleClose">取消</el-button>
|
||
<el-button type="primary" @click="handleSubmit" :loading="submitting">
|
||
保存
|
||
</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, watch, computed } from 'vue';
|
||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||
import { addDictItem, updateDictItem, getDictItems } from '@/api/dict';
|
||
|
||
interface Props {
|
||
modelValue: boolean;
|
||
dictItem?: any;
|
||
dictTypeId?: number;
|
||
dictTypeName?: string;
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
modelValue: false,
|
||
dictItem: null,
|
||
dictTypeId: undefined,
|
||
dictTypeName: '',
|
||
});
|
||
|
||
const emit = defineEmits<{
|
||
'update:modelValue': [value: boolean];
|
||
success: [];
|
||
}>();
|
||
|
||
const visible = computed({
|
||
get: () => props.modelValue,
|
||
set: (val) => emit('update:modelValue', val),
|
||
});
|
||
|
||
const submitting = ref(false);
|
||
const dictItemFormRef = ref<FormInstance>();
|
||
const parentDictItems = ref<any[]>([]);
|
||
|
||
// 预定义颜色
|
||
const predefineColors = [
|
||
'#ff4500',
|
||
'#ff8c00',
|
||
'#ffd700',
|
||
'#90ee90',
|
||
'#00ced1',
|
||
'#1e90ff',
|
||
'#c71585',
|
||
'#ff0000',
|
||
'#00ff00',
|
||
'#0000ff',
|
||
];
|
||
|
||
// 判断是否为编辑模式
|
||
const isEditing = computed(() => {
|
||
return !!(props.dictItem && props.dictItem.id);
|
||
});
|
||
|
||
// 表单数据
|
||
const dictItemForm = reactive({
|
||
id: null as number | null,
|
||
dict_type_id: 0,
|
||
dict_label: '',
|
||
dict_value: '',
|
||
parent_id: 0,
|
||
status: 1,
|
||
sort: 0,
|
||
color: '',
|
||
icon: '',
|
||
remark: '',
|
||
});
|
||
|
||
// 表单验证规则
|
||
const formRules: FormRules = {
|
||
dict_label: [
|
||
{ required: true, message: '请输入字典标签', trigger: 'blur' },
|
||
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' },
|
||
],
|
||
dict_value: [
|
||
{ required: true, message: '请输入字典值', trigger: 'blur' },
|
||
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' },
|
||
],
|
||
sort: [
|
||
{ required: true, message: '请输入排序', trigger: 'blur' },
|
||
{ type: 'number', min: 0, message: '排序必须大于等于 0', trigger: 'blur' },
|
||
],
|
||
color: [
|
||
{ max: 20, message: '颜色值长度不能超过 20 个字符', trigger: 'blur' },
|
||
],
|
||
icon: [
|
||
{ max: 50, message: '图标长度不能超过 50 个字符', trigger: 'blur' },
|
||
],
|
||
remark: [
|
||
{ max: 500, message: '备注长度不能超过 500 个字符', trigger: 'blur' },
|
||
],
|
||
};
|
||
|
||
// 获取父级字典项列表(排除自己)
|
||
async function fetchParentDictItems() {
|
||
if (!props.dictTypeId) return;
|
||
try {
|
||
const res = await getDictItems({ dict_type_id: props.dictTypeId });
|
||
if (res.success && res.data) {
|
||
// 如果是编辑模式,排除当前字典项
|
||
if (isEditing.value && props.dictItem) {
|
||
parentDictItems.value = res.data.filter(
|
||
(item: any) => item.id !== props.dictItem.id
|
||
);
|
||
} else {
|
||
parentDictItems.value = res.data;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取父级字典项失败:', error);
|
||
}
|
||
}
|
||
|
||
// 计算字典类型显示名称
|
||
const dictTypeDisplayName = computed(() => {
|
||
if (props.dictTypeName) {
|
||
return props.dictTypeName;
|
||
}
|
||
if (props.dictTypeId) {
|
||
return `字典类型 ID: ${props.dictTypeId}`;
|
||
}
|
||
return '未知字典类型';
|
||
});
|
||
|
||
// 监听 dictItem 变化,填充表单数据
|
||
watch(
|
||
() => props.dictItem,
|
||
(newDictItem) => {
|
||
if (newDictItem) {
|
||
dictItemForm.id = newDictItem.id || null;
|
||
dictItemForm.dict_type_id = newDictItem.dict_type_id || props.dictTypeId || 0;
|
||
dictItemForm.dict_label = newDictItem.dict_label || '';
|
||
dictItemForm.dict_value = newDictItem.dict_value || '';
|
||
dictItemForm.parent_id = newDictItem.parent_id || 0;
|
||
dictItemForm.status = newDictItem.status !== undefined ? newDictItem.status : 1;
|
||
dictItemForm.sort = newDictItem.sort || 0;
|
||
dictItemForm.color = newDictItem.color || '';
|
||
dictItemForm.icon = newDictItem.icon || '';
|
||
dictItemForm.remark = newDictItem.remark || '';
|
||
} else {
|
||
resetForm();
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
// 监听弹窗打开,获取父级字典项
|
||
watch(visible, (val) => {
|
||
if (val) {
|
||
if (props.dictTypeId) {
|
||
dictItemForm.dict_type_id = props.dictTypeId;
|
||
}
|
||
fetchParentDictItems();
|
||
}
|
||
});
|
||
|
||
// 重置表单
|
||
function resetForm() {
|
||
dictItemForm.id = null;
|
||
dictItemForm.dict_type_id = props.dictTypeId || 0;
|
||
dictItemForm.dict_label = '';
|
||
dictItemForm.dict_value = '';
|
||
dictItemForm.parent_id = 0;
|
||
dictItemForm.status = 1;
|
||
dictItemForm.sort = 0;
|
||
dictItemForm.color = '';
|
||
dictItemForm.icon = '';
|
||
dictItemForm.remark = '';
|
||
dictItemFormRef.value?.clearValidate();
|
||
}
|
||
|
||
// 关闭对话框
|
||
function handleClose() {
|
||
visible.value = false;
|
||
resetForm();
|
||
}
|
||
|
||
// 提交表单
|
||
async function handleSubmit() {
|
||
if (!dictItemFormRef.value) return;
|
||
|
||
try {
|
||
await dictItemFormRef.value.validate();
|
||
} catch (error) {
|
||
return;
|
||
}
|
||
|
||
if (!dictItemForm.dict_type_id) {
|
||
ElMessage.error('字典类型ID不能为空');
|
||
return;
|
||
}
|
||
|
||
submitting.value = true;
|
||
try {
|
||
const submitData: any = {
|
||
dict_type_id: dictItemForm.dict_type_id,
|
||
dict_label: dictItemForm.dict_label,
|
||
dict_value: dictItemForm.dict_value,
|
||
parent_id: dictItemForm.parent_id || 0,
|
||
status: dictItemForm.status,
|
||
sort: dictItemForm.sort,
|
||
color: dictItemForm.color || '',
|
||
icon: dictItemForm.icon || '',
|
||
remark: dictItemForm.remark || '',
|
||
};
|
||
|
||
if (isEditing.value) {
|
||
await updateDictItem(dictItemForm.id!, submitData);
|
||
ElMessage.success('字典项更新成功');
|
||
} else {
|
||
await addDictItem(submitData);
|
||
ElMessage.success('字典项添加成功');
|
||
}
|
||
|
||
emit('success');
|
||
handleClose();
|
||
} catch (error: any) {
|
||
ElMessage.error(error.message || '操作失败,请重试');
|
||
} finally {
|
||
submitting.value = false;
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.form-tip {
|
||
font-size: 12px;
|
||
color: var(--el-text-color-secondary);
|
||
margin-top: 4px;
|
||
line-height: 1.4;
|
||
}
|
||
</style>
|
||
|
||
|