Files
laravel_swoole/resources/admin/src/pages/system/dictionaries/components/DictionaryDialog.vue
2026-02-18 17:54:07 +08:00

251 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<a-modal :title="title" :open="visible" :confirm-loading="isSaving" :footer="null" @cancel="handleCancel" width="600px">
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
<!-- 字典名称 -->
<a-form-item label="字典名称" name="name" required>
<a-input v-model:value="form.name" placeholder="如:用户状态" allow-clear maxlength="50" show-count />
</a-form-item>
<!-- 字典编码 -->
<a-form-item label="字典编码" name="code" required>
<a-input v-model:value="form.code" placeholder="如user_status" allow-clear :disabled="isEdit" />
<div class="form-tip">系统唯一标识只能包含字母数字下划线且必须以字母开头</div>
</a-form-item>
<!-- 值类型 -->
<a-form-item label="值类型" name="value_type" required>
<a-select v-model:value="form.value_type" placeholder="请选择值类型" allow-clear>
<a-select-option value="string">字符串</a-select-option>
<a-select-option value="number">数字</a-select-option>
<a-select-option value="boolean">布尔值</a-select-option>
<a-select-option value="json">JSON</a-select-option>
</a-select>
<div class="form-tip">指定字典项值的类型系统会根据类型自动格式化返回数据</div>
</a-form-item>
<!-- 排序 -->
<a-form-item label="排序" name="sort">
<a-input-number v-model:value="form.sort" :min="0" :max="10000" style="width: 100%" />
<div class="form-tip">数值越小越靠前</div>
</a-form-item>
<!-- 状态 -->
<a-form-item label="状态" name="status">
<sc-select v-model:value="form.status" source-type="dictionary" dictionary-code="dictionary_status" placeholder="请选择状态" allow-clear />
</a-form-item>
<!-- 描述 -->
<a-form-item label="描述" name="description">
<a-textarea v-model:value="form.description" placeholder="请输入字典描述" :rows="3" maxlength="200"
show-count />
</a-form-item>
</a-form>
<!-- 底部按钮 -->
<div class="dialog-footer">
<a-space>
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" :loading="isSaving" @click="handleSubmit">保存</a-button>
</a-space>
</div>
</a-modal>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import { message } from 'ant-design-vue'
import scSelect from '@/components/scSelect/index.vue'
import systemApi from '@/api/system'
// ===== Props =====
const props = defineProps({
visible: {
type: Boolean,
default: false
},
record: {
type: Object,
default: null
},
dictionaryList: {
type: Array,
default: () => []
}
})
// ===== Emits =====
const emit = defineEmits(['update:visible', 'success'])
// ===== 状态 =====
const formRef = ref(null)
const isSaving = ref(false)
const isEdit = computed(() => !!props.record?.id)
const title = computed(() => {
return isEdit.value ? '编辑字典类型' : '新增字典类型'
})
// ===== 表单数据 =====
const form = ref({
id: '',
name: '',
code: '',
value_type: 'string',
description: '',
status: null,
sort: 0
})
// ===== 验证规则 =====
// 编码唯一性验证
const validateCodeUnique = async (rule, value) => {
if (!value) return Promise.resolve()
// 检查编码是否已存在(编辑时排除自己)
const exists = props.dictionaryList.some(
item => item.code === value && item.id !== props.record?.id
)
if (exists) {
return Promise.reject('该编码已存在,请使用其他编码')
}
return Promise.resolve()
}
const rules = {
name: [
{ required: true, message: '请输入字典名称', trigger: 'blur' },
{ min: 2, max: 50, message: '字典名称长度在 2 到 50 个字符', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入字典编码', trigger: 'blur' },
{
pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/,
message: '编码格式不正确,只能包含字母、数字、下划线,且必须以字母开头',
trigger: 'blur'
},
{ validator: validateCodeUnique, trigger: 'blur' }
],
value_type: [
{ required: true, message: '请选择值类型', trigger: 'change' }
]
}
// ===== 方法:重置表单 =====
const resetForm = () => {
form.value = {
id: '',
name: '',
code: '',
value_type: 'string',
description: '',
status: null,
sort: 0
}
formRef.value?.clearValidate()
}
// ===== 方法:设置数据(编辑时) =====
const setData = (data) => {
if (data) {
form.value = {
id: data.id || '',
name: data.name || '',
code: data.code || '',
value_type: data.value_type || 'string',
description: data.description || '',
status: data.status !== undefined ? data.status : null,
sort: data.sort !== undefined ? data.sort : 0
}
}
}
// ===== 方法:提交表单 =====
const handleSubmit = async () => {
try {
// 验证表单
await formRef.value.validate()
isSaving.value = true
const submitData = {
name: form.value.name,
code: form.value.code,
value_type: form.value.value_type,
description: form.value.description,
status: form.value.status,
sort: form.value.sort
}
let res = {}
if (isEdit.value) {
// 编辑
res = await systemApi.dictionaries.edit.put(form.value.id, submitData)
} else {
// 新增
res = await systemApi.dictionaries.add.post(submitData)
}
if (res.code === 200) {
message.success(isEdit.value ? '编辑成功' : '新增成功')
emit('success')
handleCancel()
} else {
message.error(res.message || '操作失败')
}
} catch (error) {
if (error.errorFields) {
// 表单验证失败
console.log('表单验证失败:', error)
} else {
// API 调用失败
console.error('提交失败:', error)
message.error('操作失败')
}
} finally {
isSaving.value = false
}
}
// ===== 方法:取消 =====
const handleCancel = () => {
resetForm()
emit('update:visible', false)
}
// ===== 监听 visible 变化 =====
watch(() => props.visible, (newVal) => {
if (newVal) {
// 打开弹窗时,如果有 record 则设置数据
if (props.record) {
setData(props.record)
} else {
resetForm()
}
}
}, { immediate: true })
</script>
<style scoped lang="scss">
.form-tip {
font-size: 12px;
color: #8c8c8c;
margin-top: 4px;
line-height: 1.5;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
padding-top: 16px;
border-top: 1px solid #f0f0f0;
margin-top: 16px;
}
:deep(.ant-modal-body) {
max-height: 60vh;
overflow-y: auto;
}
</style>