Files
laravel_swoole/resources/admin/src/pages/system/config/components/ConfigDialog.vue
2026-02-18 17:15:33 +08:00

311 lines
8.2 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="700px">
<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 />
</a-form-item>
<!-- 配置键名 -->
<a-form-item label="配置键名" name="key" required>
<a-input v-model:value="form.key" placeholder="如site_name" allow-clear :disabled="isEdit" />
<div class="form-tip">系统唯一标识只能包含字母数字下划线</div>
</a-form-item>
<!-- 配置分组 -->
<a-form-item label="配置分组" name="group" required>
<a-select v-model:value="form.group" placeholder="请选择配置分组" :options="groupOptions" />
</a-form-item>
<!-- 配置类型 -->
<a-form-item label="配置类型" name="type" required>
<a-select v-model:value="form.type" placeholder="请选择配置类型">
<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="text">文本</a-select-option>
<a-select-option value="file">文件</a-select-option>
<a-select-option value="image">图片</a-select-option>
<a-select-option value="select">选择框</a-select-option>
</a-select>
</a-form-item>
<!-- 配置值 -->
<a-form-item label="配置值" name="value">
<!-- 字符串/文本 -->
<template v-if="['string', 'text'].includes(form.type)">
<a-input v-if="form.type === 'string'" v-model:value="form.value" placeholder="请输入配置值" allow-clear />
<a-textarea v-else v-model:value="form.value" placeholder="请输入配置值" :rows="4" />
</template>
<!-- 数字 -->
<a-input-number v-else-if="form.type === 'number'" v-model:value="form.value" :min="0" style="width: 100%" />
<!-- 布尔值 -->
<a-switch v-else-if="form.type === 'boolean'" v-model:checked="valueChecked" checked-children="启用" un-checked-children="禁用" />
<!-- 文件/图片 -->
<a-input v-else-if="['file', 'image'].includes(form.type)" v-model:value="form.value" placeholder="请输入文件地址" />
<!-- 选择框 -->
<a-input v-else v-model:value="form.optionsText" placeholder="选项值用逗号分隔选项1,选项2,选项3" />
</a-form-item>
<!-- 默认值 -->
<a-form-item label="默认值" name="default_value">
<a-input v-model:value="form.default_value" placeholder="默认值(可选)" allow-clear />
</a-form-item>
<!-- 排序 -->
<a-form-item label="排序" name="sort">
<a-input-number v-model:value="form.sort" :min="0" :max="10000" style="width: 100%" />
</a-form-item>
<!-- 状态 -->
<a-form-item label="状态" name="status">
<a-switch v-model:checked="statusChecked" checked-children="启用" un-checked-children="禁用" />
</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 systemApi from '@/api/system'
import { useDictionaryStore } from '@/stores/modules/dictionary'
const props = defineProps({
visible: {
type: Boolean,
default: false
},
record: {
type: Object,
default: null
}
})
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 groupOptions = ref([])
// 表单数据
const form = ref({
id: '',
name: '',
key: '',
group: '',
type: 'string',
value: '',
default_value: '',
description: '',
status: true,
sort: 0,
options: []
})
// 选项文本(用于选择框类型)
const optionsText = ref('')
// 计算属性:状态开关
const statusChecked = computed({
get: () => form.value.status === true,
set: (val) => {
form.value.status = val ? true : false
}
})
// 计算属性:值开关(布尔类型)
const valueChecked = computed({
get: () => form.value.value === '1' || form.value.value === true,
set: (val) => {
form.value.value = val ? '1' : '0'
}
})
// 初始化字典 store
const dictionaryStore = useDictionaryStore()
// 加载配置分组
const loadGroups = async () => {
const groups = await dictionaryStore.getDictionary('config_group')
groupOptions.value = groups.map(item => ({
label: item.label,
value: item.value
}))
}
// 验证规则
const rules = {
name: [
{ required: true, message: '请输入配置名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
key: [
{ required: true, message: '请输入配置键名', trigger: 'blur' },
{ pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '格式不正确', trigger: 'blur' }
],
group: [
{ required: true, message: '请选择配置分组', trigger: 'change' }
],
type: [
{ required: true, message: '请选择配置类型', trigger: 'change' }
]
}
// 重置表单
const resetForm = () => {
form.value = {
id: '',
name: '',
key: '',
group: '',
type: 'string',
value: '',
default_value: '',
description: '',
status: true,
sort: 0,
options: []
}
optionsText.value = ''
formRef.value?.clearValidate()
}
// 设置数据
const setData = (data) => {
if (data) {
form.value = {
id: data.id || '',
name: data.name || '',
key: data.key || '',
group: data.group || '',
type: data.type || 'string',
value: data.value || '',
default_value: data.default_value || '',
description: data.description || '',
status: data.status !== undefined ? data.status : true,
sort: data.sort !== undefined ? data.sort : 0,
options: data.options || []
}
// 如果是选择框类型,设置选项文本
if (data.type === 'select' && data.options) {
optionsText.value = Array.isArray(data.options) ? data.options.join(',') : data.options
}
}
}
// 提交表单
const handleSubmit = async () => {
try {
await formRef.value.validate()
isSaving.value = true
// 处理选项
if (form.value.type === 'select') {
form.value.options = optionsText.value ? optionsText.value.split(',').map(s => s.trim()) : []
}
// 处理值
let submitValue = form.value.value
if (form.value.type === 'boolean') {
submitValue = valueChecked.value ? '1' : '0'
}
const submitData = {
...form.value,
value: submitValue
}
let res = {}
if (isEdit.value) {
res = await systemApi.config.update.put(form.value.id, submitData)
} else {
res = await systemApi.config.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 {
console.error('提交失败:', error)
message.error('操作失败')
}
} finally {
isSaving.value = false
}
}
// 取消
const handleCancel = () => {
resetForm()
emit('update:visible', false)
}
// 监听 visible 变化
watch(() => props.visible, (newVal) => {
if (newVal) {
loadGroups()
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>