初始化
This commit is contained in:
402
saiadmin-artd/src/views/safeguard/dict/index.vue
Normal file
402
saiadmin-artd/src/views/safeguard/dict/index.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<!-- 左右页面 -->
|
||||
<template>
|
||||
<div class="art-full-height">
|
||||
<div class="box-border flex gap-4 h-full max-md:block max-md:gap-0 max-md:h-auto">
|
||||
<div class="flex-shrink-0 h-full max-md:w-full max-md:h-auto max-md:mb-5">
|
||||
<ElCard class="left-card art-card-xs flex flex-col h-full mt-0" shadow="never">
|
||||
<template #header>
|
||||
<b>数据字典</b>
|
||||
</template>
|
||||
<ElSpace wrap>
|
||||
<SaButton type="primary" icon="ri:refresh-line" @click="refreshTypeData" />
|
||||
<SaButton
|
||||
v-permission="'core:dict:edit'"
|
||||
type="primary"
|
||||
@click="typeShowDialog('add')"
|
||||
/>
|
||||
<SaButton v-permission="'core:dict:edit'" type="secondary" @click="updateTypeDialog" />
|
||||
<SaButton v-permission="'core:dict:edit'" type="error" @click="deleteTypeDialog" />
|
||||
</ElSpace>
|
||||
<ArtTable
|
||||
rowKey="id"
|
||||
:loading="loading"
|
||||
:data="typeData"
|
||||
:columns="typeColumns"
|
||||
:pagination="typePagination"
|
||||
highlight-current-row
|
||||
@pagination:size-change="handleSizeChange"
|
||||
@pagination:current-change="handleCurrentChange"
|
||||
>
|
||||
<!-- 基础列 -->
|
||||
<template #name-header="{ column }">
|
||||
<ElPopover placement="bottom" :width="200" trigger="hover">
|
||||
<template #reference>
|
||||
<div class="flex items-center gap-2 text-theme c-p custom-header">
|
||||
<span>{{ column.label }}</span>
|
||||
<ElIcon>
|
||||
<Search />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</template>
|
||||
<ElInput
|
||||
v-model="typeSearch.name"
|
||||
placeholder="搜索字典名称"
|
||||
size="small"
|
||||
clearable
|
||||
@input="handleTypeSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<ElIcon>
|
||||
<Search />
|
||||
</ElIcon>
|
||||
</template>
|
||||
</ElInput>
|
||||
</ElPopover>
|
||||
</template>
|
||||
<template #code-header="{ column }">
|
||||
<ElPopover placement="bottom" :width="200" trigger="hover">
|
||||
<template #reference>
|
||||
<div class="flex items-center gap-2 text-theme c-p custom-header">
|
||||
<span>{{ column.label }}</span>
|
||||
<ElIcon>
|
||||
<Search />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</template>
|
||||
<ElInput
|
||||
v-model="typeSearch.code"
|
||||
placeholder="搜索字典标识"
|
||||
size="small"
|
||||
clearable
|
||||
@input="handleTypeSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<ElIcon>
|
||||
<Search />
|
||||
</ElIcon>
|
||||
</template>
|
||||
</ElInput>
|
||||
</ElPopover>
|
||||
</template>
|
||||
<template #id="{ row }">
|
||||
<ElRadio
|
||||
v-model="selectedId"
|
||||
:value="row.id"
|
||||
@update:modelValue="handleTypeChange(row.id, row)"
|
||||
/>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col flex-1 min-w-0" v-if="selectedId === 0">
|
||||
<ElCard class="flex flex-col flex-5 min-h-0 !mt-0" shadow="never">
|
||||
<el-empty description="请先选择左侧字典类型配置" />
|
||||
</ElCard>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col flex-1 min-w-0" v-if="selectedId > 0">
|
||||
<DictSearch v-model="searchForm" @search="handleSearch" @reset="handleReset" />
|
||||
|
||||
<ElCard class="flex flex-col flex-5 min-h-0 art-table-card" shadow="never">
|
||||
<ElSpace wrap>
|
||||
<ElButton
|
||||
v-permission="'core:dict:edit'"
|
||||
@click="showDataDialog('add', { type_id: selectedId })"
|
||||
v-ripple
|
||||
>
|
||||
<template #icon>
|
||||
<ArtSvgIcon icon="ri:add-fill" />
|
||||
</template>
|
||||
新增
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-permission="'core:dict:edit'"
|
||||
@click="deleteSelectedRows(api.dataDelete, getDictData)"
|
||||
:disabled="selectedRows.length === 0"
|
||||
v-ripple
|
||||
>
|
||||
<template #icon>
|
||||
<ArtSvgIcon icon="ri:delete-bin-5-line" />
|
||||
</template>
|
||||
删除
|
||||
</ElButton>
|
||||
</ElSpace>
|
||||
<ArtTable
|
||||
rowKey="id"
|
||||
:loading="loading"
|
||||
:data="dictData"
|
||||
:columns="dictColumns"
|
||||
:pagination="dictPagination"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionChange"
|
||||
@pagination:size-change="handleSizeChange"
|
||||
@pagination:current-change="handleCurrentChange"
|
||||
>
|
||||
<!-- 基础列 -->
|
||||
<template #label="{ row }">
|
||||
<ElTag
|
||||
:style="{
|
||||
backgroundColor: getColor(row.color, 'bg'),
|
||||
borderColor: getColor(row.color, 'border'),
|
||||
color: getColor(row.color, 'text')
|
||||
}"
|
||||
>
|
||||
{{ row.label }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<!-- 操作列 -->
|
||||
<template #operation="{ row }">
|
||||
<div class="flex gap-2">
|
||||
<SaButton
|
||||
v-permission="'core:dict:edit'"
|
||||
type="secondary"
|
||||
@click="showDataDialog('edit', row)"
|
||||
/>
|
||||
<SaButton
|
||||
v-permission="'core:dict:edit'"
|
||||
type="error"
|
||||
@click="deleteRow(row, api.dataDelete, getDictData)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 字典编辑弹窗 -->
|
||||
<TypeEditDialog
|
||||
v-model="typeVisible"
|
||||
:dialog-type="typeDialogType"
|
||||
:data="currentTypeData"
|
||||
@success="getTypeData()"
|
||||
/>
|
||||
|
||||
<!-- 字典项编辑弹窗 -->
|
||||
<DictEditDialog
|
||||
v-model="dictVisible"
|
||||
:dialog-type="dictDialogType"
|
||||
:data="currentDictData"
|
||||
@success="getDictData()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTable } from '@/hooks/core/useTable'
|
||||
import { useSaiAdmin } from '@/composables/useSaiAdmin'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import api from '@/api/safeguard/dict'
|
||||
import DictSearch from '@/views/safeguard/dict/modules/dict-search.vue'
|
||||
import DictEditDialog from './modules/dict-edit-dialog.vue'
|
||||
import TypeEditDialog from './modules/type-edit-dialog.vue'
|
||||
|
||||
// 字典类型数据
|
||||
const {
|
||||
dialogType: typeDialogType,
|
||||
dialogVisible: typeVisible,
|
||||
dialogData: currentTypeData,
|
||||
showDialog: typeShowDialog,
|
||||
deleteRow: typeDeleteRow
|
||||
} = useSaiAdmin()
|
||||
|
||||
// 字典类型
|
||||
const selectedId = ref(0)
|
||||
const selectedRow = ref({})
|
||||
const typeSearch = ref({
|
||||
name: '',
|
||||
code: ''
|
||||
})
|
||||
|
||||
/** 修改字典类型 */
|
||||
const updateTypeDialog = () => {
|
||||
if (selectedId.value === 0) {
|
||||
ElMessage.error('请选择要修改的数据')
|
||||
return
|
||||
}
|
||||
typeShowDialog('edit', { ...selectedRow.value })
|
||||
}
|
||||
|
||||
/** 删除字典类型 */
|
||||
const deleteTypeDialog = () => {
|
||||
if (selectedId.value === 0) {
|
||||
ElMessage.error('请选择要删除的数据')
|
||||
return
|
||||
}
|
||||
typeDeleteRow({ ...selectedRow.value }, api.delete, refreshTypeData)
|
||||
}
|
||||
|
||||
/** 字典类型搜索 */
|
||||
const handleTypeSearch = () => {
|
||||
Object.assign(searchTypeParams, typeSearch.value)
|
||||
getTypeData()
|
||||
}
|
||||
|
||||
/** 字典类型切换 */
|
||||
const handleTypeChange = (val: any, row?: any) => {
|
||||
selectedId.value = val
|
||||
selectedRow.value = row
|
||||
searchForm.value.type_id = val
|
||||
Object.assign(searchParams, searchForm.value)
|
||||
getDictData()
|
||||
}
|
||||
|
||||
/** 刷新数据 */
|
||||
const refreshTypeData = () => {
|
||||
selectedId.value = 0
|
||||
selectedRow.value = {}
|
||||
getTypeData()
|
||||
getDictData()
|
||||
}
|
||||
|
||||
// 字典类型数据
|
||||
const {
|
||||
data: typeData,
|
||||
columns: typeColumns,
|
||||
getData: getTypeData,
|
||||
searchParams: searchTypeParams,
|
||||
loading,
|
||||
pagination: typePagination,
|
||||
handleSizeChange,
|
||||
handleCurrentChange
|
||||
} = useTable({
|
||||
core: {
|
||||
apiFn: api.typeList,
|
||||
apiParams: {
|
||||
...typeSearch.value
|
||||
},
|
||||
columnsFactory: () => [
|
||||
{ prop: 'id', label: '选中', width: 80, align: 'center', useSlot: true },
|
||||
{ prop: 'name', label: '字典名称', useHeaderSlot: true, width: 150 },
|
||||
{ prop: 'code', label: '字典标识', useHeaderSlot: true, width: 150 },
|
||||
{ prop: 'status', label: '状态', saiType: 'dict', saiDict: 'data_status', width: 100 }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 字典项数据
|
||||
const {
|
||||
dialogType: dictDialogType,
|
||||
dialogVisible: dictVisible,
|
||||
dialogData: currentDictData,
|
||||
showDialog: showDataDialog,
|
||||
deleteRow,
|
||||
handleSelectionChange,
|
||||
selectedRows,
|
||||
deleteSelectedRows
|
||||
} = useSaiAdmin()
|
||||
|
||||
/** 字典项搜索 */
|
||||
const searchForm = ref({
|
||||
label: '',
|
||||
value: '',
|
||||
status: '',
|
||||
type_id: null
|
||||
})
|
||||
|
||||
// 字典项数据
|
||||
const {
|
||||
data: dictData,
|
||||
columns: dictColumns,
|
||||
getData: getDictData,
|
||||
pagination: dictPagination,
|
||||
searchParams
|
||||
} = useTable({
|
||||
core: {
|
||||
apiFn: api.dataList,
|
||||
immediate: false,
|
||||
apiParams: {
|
||||
...searchForm.value
|
||||
},
|
||||
columnsFactory: () => [
|
||||
{ type: 'selection' },
|
||||
{ prop: 'label', label: '字典标签', useSlot: true },
|
||||
{ prop: 'value', label: '字典键值' },
|
||||
{ prop: 'color', label: '颜色' },
|
||||
{ prop: 'sort', label: '排序' },
|
||||
{ prop: 'status', label: '状态', saiType: 'dict', saiDict: 'data_status' },
|
||||
{ prop: 'operation', label: '操作', useSlot: true, width: 120 }
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 字典项搜索
|
||||
const handleSearch = (params: Record<string, any>) => {
|
||||
if (selectedId.value) {
|
||||
Object.assign(searchParams, params)
|
||||
getDictData()
|
||||
}
|
||||
}
|
||||
|
||||
// 字典项重置搜索
|
||||
const handleReset = () => {
|
||||
if (!selectedId.value) {
|
||||
ElMessage.warning('请选择字典类型')
|
||||
return
|
||||
}
|
||||
Object.assign(searchParams, {
|
||||
label: '',
|
||||
value: '',
|
||||
status: '',
|
||||
type_id: selectedId.value
|
||||
})
|
||||
getDictData()
|
||||
}
|
||||
|
||||
const getColor = (color: string | undefined, type: 'bg' | 'border' | 'text') => {
|
||||
// 如果没有指定颜色,使用默认主色调
|
||||
if (!color) {
|
||||
const colors = {
|
||||
bg: 'var(--el-color-primary-light-9)',
|
||||
border: 'var(--el-color-primary-light-8)',
|
||||
text: 'var(--el-color-primary)'
|
||||
}
|
||||
return colors[type]
|
||||
}
|
||||
|
||||
// 如果是 hex 颜色,转换为 RGB
|
||||
let r, g, b
|
||||
if (color.startsWith('#')) {
|
||||
const hex = color.slice(1)
|
||||
r = parseInt(hex.slice(0, 2), 16)
|
||||
g = parseInt(hex.slice(2, 4), 16)
|
||||
b = parseInt(hex.slice(4, 6), 16)
|
||||
} else if (color.startsWith('rgb')) {
|
||||
const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/)
|
||||
if (match) {
|
||||
r = parseInt(match[1])
|
||||
g = parseInt(match[2])
|
||||
b = parseInt(match[3])
|
||||
} else {
|
||||
return color
|
||||
}
|
||||
} else {
|
||||
return color
|
||||
}
|
||||
|
||||
// 根据类型返回不同的颜色变体
|
||||
switch (type) {
|
||||
case 'bg':
|
||||
// 背景色 - 更浅的版本
|
||||
return `rgba(${r}, ${g}, ${b}, 0.1)`
|
||||
case 'border':
|
||||
// 边框色 - 中等亮度
|
||||
return `rgba(${r}, ${g}, ${b}, 0.3)`
|
||||
case 'text':
|
||||
// 文字色 - 原始颜色
|
||||
return `rgb(${r}, ${g}, ${b})`
|
||||
default:
|
||||
return color
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.left-card :deep(.el-card__body) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 10px 2px 10px 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="dialogType === 'add' ? '新增字典项数据' : '编辑字典项数据'"
|
||||
width="600px"
|
||||
align-center
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="字典标签" prop="label">
|
||||
<el-input v-model="formData.label" placeholder="请输入字典标签" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典键值" prop="value">
|
||||
<el-input v-model="formData.value" placeholder="请输入字典键值" />
|
||||
</el-form-item>
|
||||
<el-form-item label="颜色选择" prop="color">
|
||||
<el-color-picker v-model="formData.color" color-format="hex" :predefine="predefineColors" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" placeholder="请输入排序" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<SaiRadio v-model="formData.status" dict="data_status" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.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">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '@/api/safeguard/dict'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useDictStore } from '@/store/modules/dict'
|
||||
import SaiRadio from '@/components/sai/sa-radio/index.vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
dialogType: string
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
dialogType: 'add',
|
||||
data: undefined
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
const dictStore = useDictStore()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
const predefineColors = ref([
|
||||
'#ff4500',
|
||||
'#ff8c00',
|
||||
'#ffd700',
|
||||
'#90ee90',
|
||||
'#00ced1',
|
||||
'#1e90ff',
|
||||
'#c71585',
|
||||
'#5d87ff',
|
||||
'#b48df3',
|
||||
'#1d84ff',
|
||||
'#60c041',
|
||||
'#38c0fc',
|
||||
'#f9901f',
|
||||
'#ff80c8',
|
||||
'#909399'
|
||||
])
|
||||
|
||||
/**
|
||||
* 弹窗显示状态双向绑定
|
||||
*/
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
/**
|
||||
* 表单验证规则
|
||||
*/
|
||||
const rules = reactive<FormRules>({
|
||||
label: [{ required: true, message: '请输入字典标签', trigger: 'blur' }],
|
||||
value: [{ required: true, message: '请输入字典键值', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
id: null,
|
||||
type_id: '',
|
||||
code: '',
|
||||
label: '',
|
||||
color: '#5d87ff',
|
||||
value: '',
|
||||
remark: '',
|
||||
sort: 100,
|
||||
status: 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const formData = reactive({ ...initialFormData })
|
||||
|
||||
/**
|
||||
* 监听弹窗打开,初始化表单数据
|
||||
*/
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
initPage()
|
||||
}
|
||||
}
|
||||
)
|
||||
// 初始化页面数据
|
||||
const initPage = async () => {
|
||||
// 先重置为初始值
|
||||
Object.assign(formData, initialFormData)
|
||||
// 如果有数据,则填充数据
|
||||
if (props.data) {
|
||||
await nextTick()
|
||||
initForm(props.data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单数据
|
||||
*/
|
||||
const initForm = (data: any) => {
|
||||
if (data) {
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
;(formData as any)[key] = data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭弹窗并重置表单
|
||||
*/
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
if (props.dialogType === 'add') {
|
||||
await api.dataSave(formData)
|
||||
ElMessage.success('新增成功')
|
||||
} else {
|
||||
await api.dataUpdate(formData)
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
dictStore.refresh()
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
console.log('表单验证失败:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<sa-search-bar
|
||||
ref="searchBarRef"
|
||||
v-model="formData"
|
||||
label-width="80px"
|
||||
:showExpand="false"
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
@expand="handleExpand"
|
||||
>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="字典标签" prop="label">
|
||||
<el-input v-model="formData.label" placeholder="请输入字典标签" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="字典键值" prop="value">
|
||||
<el-input v-model="formData.value" placeholder="请输入字典键值" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-bind="setSpan(6)">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<sa-select v-model="formData.status" dict="data_status" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</sa-search-bar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
modelValue: Record<string, any>
|
||||
}
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: Record<string, any>): void
|
||||
(e: 'search', params: Record<string, any>): void
|
||||
(e: 'reset'): void
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
const isExpanded = ref<boolean>(false)
|
||||
|
||||
// 表单数据双向绑定
|
||||
const searchBarRef = ref()
|
||||
const formData = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
// 重置
|
||||
function handleReset() {
|
||||
searchBarRef.value?.ref.resetFields()
|
||||
emit('reset')
|
||||
}
|
||||
|
||||
// 搜索
|
||||
async function handleSearch() {
|
||||
emit('search', formData.value)
|
||||
}
|
||||
|
||||
// 展开/收起
|
||||
function handleExpand(expanded: boolean) {
|
||||
isExpanded.value = expanded
|
||||
}
|
||||
|
||||
// 栅格占据的列数
|
||||
const setSpan = (span: number) => {
|
||||
return {
|
||||
span: span,
|
||||
xs: 24, // 手机:满宽显示
|
||||
sm: span >= 12 ? span : 12, // 平板:大于等于12保持,否则用半宽
|
||||
md: span >= 8 ? span : 8, // 中等屏幕:大于等于8保持,否则用三分之一宽
|
||||
lg: span,
|
||||
xl: span
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="dialogType === 'add' ? '新增字典数据' : '编辑字典数据'"
|
||||
width="600px"
|
||||
align-center
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入字典名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="字典标识" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入字典标识" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<SaiRadio v-model="formData.status" dict="data_status" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.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">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '@/api/safeguard/dict'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import SaiRadio from '@/components/sai/sa-radio/index.vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean
|
||||
dialogType: string
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
dialogType: 'add',
|
||||
data: undefined
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
|
||||
/**
|
||||
* 弹窗显示状态双向绑定
|
||||
*/
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
|
||||
/**
|
||||
* 表单验证规则
|
||||
*/
|
||||
const rules = reactive<FormRules>({
|
||||
name: [{ required: true, message: '请输入字典名称', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入字典标识', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始数据
|
||||
*/
|
||||
const initialFormData = {
|
||||
id: null,
|
||||
name: '',
|
||||
code: '',
|
||||
remark: '',
|
||||
status: 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单数据
|
||||
*/
|
||||
const formData = reactive({ ...initialFormData })
|
||||
|
||||
/**
|
||||
* 监听弹窗打开,初始化表单数据
|
||||
*/
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
initPage()
|
||||
}
|
||||
}
|
||||
)
|
||||
// 初始化页面数据
|
||||
const initPage = async () => {
|
||||
// 先重置为初始值
|
||||
Object.assign(formData, initialFormData)
|
||||
// 如果有数据,则填充数据
|
||||
if (props.data) {
|
||||
await nextTick()
|
||||
initForm(props.data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化表单数据
|
||||
*/
|
||||
const initForm = (data: any) => {
|
||||
if (data) {
|
||||
for (const key in formData) {
|
||||
if (data[key] != null && data[key] != undefined) {
|
||||
;(formData as any)[key] = data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭弹窗并重置表单
|
||||
*/
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交表单
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
if (props.dialogType === 'add') {
|
||||
await api.save(formData)
|
||||
ElMessage.success('新增成功')
|
||||
} else {
|
||||
await api.update(formData)
|
||||
ElMessage.success('修改成功')
|
||||
}
|
||||
emit('success')
|
||||
handleClose()
|
||||
} catch (error) {
|
||||
console.log('表单验证失败:', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user