feat(dict): 新增字典管理和字典详情页面
- 实现字典类型管理页面,支持增删改查 - 实现字典数据详情页面,支持数据管理 - 添加字典类型可点击链接跳转到详情页面 - 支持状态切换开关和确认操作 - 实现标签类型动态颜色显示功能 - 采用紧凑表单布局和统一按钮样式
This commit is contained in:
parent
bf94dd07be
commit
4bd079c4c2
1088
src/views/system/dict/data.vue
Normal file
1088
src/views/system/dict/data.vue
Normal file
File diff suppressed because it is too large
Load Diff
899
src/views/system/dict/index.vue
Normal file
899
src/views/system/dict/index.vue
Normal file
@ -0,0 +1,899 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dict-management p-1 bg-gray-50 h-screen flex flex-col">
|
||||||
|
<!-- 搜索表单和字典表格 -->
|
||||||
|
<div class="bg-white rounded-lg shadow-sm border border-gray-100 flex-1 flex flex-col overflow-hidden">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<div class="px-4 py-2 border-b border-gray-100">
|
||||||
|
<n-form
|
||||||
|
ref="searchFormRef"
|
||||||
|
:model="searchForm"
|
||||||
|
label-placement="left"
|
||||||
|
label-width="auto"
|
||||||
|
class="search-form"
|
||||||
|
>
|
||||||
|
<n-grid :cols="4" :x-gap="8" :y-gap="4">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="字典名称" path="dictName">
|
||||||
|
<n-input
|
||||||
|
v-model:value="searchForm.dictName"
|
||||||
|
placeholder="请输入字典名称"
|
||||||
|
clearable
|
||||||
|
@keydown.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="字典类型" path="dictType">
|
||||||
|
<n-input
|
||||||
|
v-model:value="searchForm.dictType"
|
||||||
|
placeholder="请输入字典类型"
|
||||||
|
clearable
|
||||||
|
@keydown.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="状态" path="dictStatus">
|
||||||
|
<n-select
|
||||||
|
v-model:value="searchForm.dictStatus"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
clearable
|
||||||
|
:options="statusOptions"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="" path="">
|
||||||
|
<NSpace>
|
||||||
|
<NButton type="primary" @click="handleSearch">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon><icon-park-outline:search /></NIcon>
|
||||||
|
</template>
|
||||||
|
搜索
|
||||||
|
</NButton>
|
||||||
|
<NButton @click="handleReset">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon><icon-park-outline:refresh /></NIcon>
|
||||||
|
</template>
|
||||||
|
重置
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
</n-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格头部操作栏 -->
|
||||||
|
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-100">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<NButton
|
||||||
|
v-permission="'system:dict:add'"
|
||||||
|
type="primary"
|
||||||
|
class="px-3 flex items-center"
|
||||||
|
@click="handleAdd"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||||
|
<icon-park-outline:plus />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
新增
|
||||||
|
</NButton>
|
||||||
|
|
||||||
|
<NButton
|
||||||
|
v-permission="'system:dict:delete'"
|
||||||
|
type="error"
|
||||||
|
:disabled="selectedRows.length === 0"
|
||||||
|
class="px-3 flex items-center"
|
||||||
|
@click="handleBatchDelete"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||||
|
<icon-park-outline:delete />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
批量删除
|
||||||
|
</NButton>
|
||||||
|
|
||||||
|
<NButton
|
||||||
|
v-permission="'system:dict:update'"
|
||||||
|
type="info"
|
||||||
|
class="px-3 flex items-center"
|
||||||
|
@click="handleSyncCache"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon class="mr-1" style="transform: translateY(-1px)">
|
||||||
|
<icon-park-outline:refresh />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
同步缓存
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格内容 -->
|
||||||
|
<div class="table-wrapper flex-1 overflow-auto pt-4">
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<n-data-table
|
||||||
|
v-if="tableData.length > 0 || loading"
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:row-key="rowKey"
|
||||||
|
:checked-row-keys="checkedRowKeys"
|
||||||
|
:bordered="false"
|
||||||
|
:single-line="false"
|
||||||
|
:scroll-x="1450"
|
||||||
|
size="medium"
|
||||||
|
class="custom-table"
|
||||||
|
@update:checked-row-keys="handleRowSelectionChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<CoiEmpty
|
||||||
|
v-else
|
||||||
|
type="nodata"
|
||||||
|
title="暂无数据"
|
||||||
|
description="当前没有字典类型数据"
|
||||||
|
:show-action="true"
|
||||||
|
>
|
||||||
|
<template #action>
|
||||||
|
<NButton
|
||||||
|
type="primary"
|
||||||
|
size="medium"
|
||||||
|
round
|
||||||
|
@click="handleAdd"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<NIcon>
|
||||||
|
<icon-park-outline:plus />
|
||||||
|
</NIcon>
|
||||||
|
</template>
|
||||||
|
新增字典类型
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
</CoiEmpty>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页器 -->
|
||||||
|
<div v-if="tableData.length > 0" class="border-t border-gray-100">
|
||||||
|
<CoiPagination
|
||||||
|
v-model:page="pagination.page"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:item-count="pagination.itemCount"
|
||||||
|
:show-size-picker="true"
|
||||||
|
@update:page="handlePageChange"
|
||||||
|
@update:page-size="handlePageSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹框 -->
|
||||||
|
<CoiDialog
|
||||||
|
ref="formDialogRef"
|
||||||
|
:title="modalTitle"
|
||||||
|
:width="800"
|
||||||
|
height="auto"
|
||||||
|
confirm-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
@coi-confirm="handleSubmit"
|
||||||
|
@coi-cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="px-3 py-2">
|
||||||
|
<n-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-placement="left"
|
||||||
|
label-width="90px"
|
||||||
|
require-mark-placement="right-hanging"
|
||||||
|
class="compact-form"
|
||||||
|
>
|
||||||
|
<n-grid :cols="2" :x-gap="10">
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="字典名称" path="dictName" class="mb-2">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formData.dictName"
|
||||||
|
placeholder="请输入字典名称"
|
||||||
|
maxlength="50"
|
||||||
|
show-count
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="字典类型" path="dictType" class="mb-2">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formData.dictType"
|
||||||
|
placeholder="请输入字典类型"
|
||||||
|
:disabled="isEdit"
|
||||||
|
maxlength="50"
|
||||||
|
show-count
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item>
|
||||||
|
<n-form-item label="状态" path="dictStatus" class="mb-2">
|
||||||
|
<n-select
|
||||||
|
v-model:value="formData.dictStatus"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
:options="statusOptions"
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
|
<n-form-item label="备注" path="remark" class="mb-2">
|
||||||
|
<n-input
|
||||||
|
v-model:value="formData.remark"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
:rows="3"
|
||||||
|
maxlength="500"
|
||||||
|
show-count
|
||||||
|
/>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</CoiDialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import type { DataTableColumns, FormInst, FormRules } from 'naive-ui'
|
||||||
|
import { NButton, NIcon, NPopconfirm, NSpace, NSwitch } from 'naive-ui'
|
||||||
|
import CoiDialog from '@/components/common/CoiDialog.vue'
|
||||||
|
import CoiEmpty from '@/components/common/CoiEmpty.vue'
|
||||||
|
import CoiPagination from '@/components/common/CoiPagination.vue'
|
||||||
|
import { usePermission } from '@/hooks/usePermission'
|
||||||
|
import { coiMsgBox, coiMsgError, coiMsgSuccess, coiMsgWarning } from '@/utils/coi'
|
||||||
|
import {
|
||||||
|
addDictType,
|
||||||
|
batchDeleteDictType,
|
||||||
|
deleteDictType,
|
||||||
|
DictStatus,
|
||||||
|
getDictTypeList,
|
||||||
|
syncDictCache,
|
||||||
|
updateDictType,
|
||||||
|
updateDictTypeStatus,
|
||||||
|
} from '@/service/api/system/dict'
|
||||||
|
import type {
|
||||||
|
DictTypeForm,
|
||||||
|
DictTypeSearchForm,
|
||||||
|
DictTypeVo,
|
||||||
|
} from '@/service/api/system/dict'
|
||||||
|
|
||||||
|
// 图标导入
|
||||||
|
import IconParkOutlineDelete from '~icons/icon-park-outline/delete'
|
||||||
|
import IconParkOutlineEdit from '~icons/icon-park-outline/edit'
|
||||||
|
|
||||||
|
// 路由和权限验证
|
||||||
|
const router = useRouter()
|
||||||
|
const { hasPermission } = usePermission()
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableData = ref<DictTypeVo[]>([])
|
||||||
|
const selectedRows = ref<DictTypeVo[]>([])
|
||||||
|
const checkedRowKeys = ref<string[]>([])
|
||||||
|
const searchFormRef = ref<FormInst>()
|
||||||
|
const formRef = ref<FormInst>()
|
||||||
|
const formDialogRef = ref()
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const modalTitle = ref('')
|
||||||
|
|
||||||
|
// 搜索表单数据
|
||||||
|
const searchForm = reactive<DictTypeSearchForm>({
|
||||||
|
dictName: '',
|
||||||
|
dictType: '',
|
||||||
|
dictStatus: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = reactive<DictTypeForm>({
|
||||||
|
dictId: '',
|
||||||
|
dictType: '',
|
||||||
|
dictName: '',
|
||||||
|
dictStatus: DictStatus.NORMAL,
|
||||||
|
remark: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分页配置
|
||||||
|
const pagination = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
itemCount: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分页处理函数
|
||||||
|
function handlePageChange(page: number) {
|
||||||
|
pagination.page = page
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageSizeChange(pageSize: number) {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.page = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态选项
|
||||||
|
const statusOptions = [
|
||||||
|
{ label: '正常', value: DictStatus.NORMAL },
|
||||||
|
{ label: '停用', value: DictStatus.DISABLED },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules: FormRules = {
|
||||||
|
dictName: [
|
||||||
|
{ required: true, message: '请输入字典名称', trigger: ['blur', 'input'] },
|
||||||
|
{ min: 1, max: 50, message: '字典名称长度在1到50个字符', trigger: ['blur', 'input'] },
|
||||||
|
],
|
||||||
|
dictType: [
|
||||||
|
{ required: true, message: '请输入字典类型', trigger: ['blur', 'input'] },
|
||||||
|
{ min: 1, max: 50, message: '字典类型长度在1到50个字符', trigger: ['blur', 'input'] },
|
||||||
|
{ pattern: /^[a-z_]\w*$/i, message: '字典类型只能包含字母、数字、下划线,且不能以数字开头', trigger: ['blur', 'input'] },
|
||||||
|
],
|
||||||
|
dictStatus: [
|
||||||
|
{ required: true, message: '请选择状态', trigger: ['blur', 'change'] },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格行键
|
||||||
|
const rowKey = (row: DictTypeVo) => row.dictId
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns: DataTableColumns<DictTypeVo> = [
|
||||||
|
{ type: 'selection' },
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
key: 'index',
|
||||||
|
width: 70,
|
||||||
|
align: 'center',
|
||||||
|
render: (_, index) => {
|
||||||
|
return (pagination.page - 1) * pagination.pageSize + index + 1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '字典名称',
|
||||||
|
key: 'dictName',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '字典类型',
|
||||||
|
key: 'dictType',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
render: (row) => {
|
||||||
|
return h('a', {
|
||||||
|
class: 'dict-type-clickable-link',
|
||||||
|
style: {
|
||||||
|
color: '#326C72',
|
||||||
|
cursor: 'pointer',
|
||||||
|
textDecoration: 'none',
|
||||||
|
fontWeight: '500',
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
display: 'inline-block',
|
||||||
|
},
|
||||||
|
onClick: (e: MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
handleViewDictData(row)
|
||||||
|
},
|
||||||
|
onMouseenter: (e: MouseEvent) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
target.style.color = '#4A9BA1'
|
||||||
|
target.style.textDecoration = 'underline'
|
||||||
|
},
|
||||||
|
onMouseleave: (e: MouseEvent) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
target.style.color = '#326C72'
|
||||||
|
target.style.textDecoration = 'none'
|
||||||
|
},
|
||||||
|
}, row.dictType)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '字典状态',
|
||||||
|
key: 'dictStatus',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
render: (row) => {
|
||||||
|
return h('div', { class: 'flex items-center justify-center' }, [
|
||||||
|
h(NPopconfirm, {
|
||||||
|
onPositiveClick: () => handleStatusChange(row),
|
||||||
|
negativeText: '取消',
|
||||||
|
positiveText: '确定',
|
||||||
|
}, {
|
||||||
|
default: () => `确定要${row.dictStatus === DictStatus.NORMAL ? '停用' : '启用'}字典类型「${row.dictName}」吗?`,
|
||||||
|
trigger: () => h(NSwitch, {
|
||||||
|
value: row.dictStatus === DictStatus.NORMAL,
|
||||||
|
size: 'small',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'remark',
|
||||||
|
width: 150,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
key: 'createTime',
|
||||||
|
width: 170,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '修改时间',
|
||||||
|
key: 'updateTime',
|
||||||
|
width: 170,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
render: (row) => {
|
||||||
|
return h('span', { class: 'text-gray-500 text-sm' }, row.updateTime || '-')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '修改人',
|
||||||
|
key: 'updateBy',
|
||||||
|
width: 120,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
render: (row) => {
|
||||||
|
return h('span', { class: 'text-gray-600' }, row.updateBy || '-')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'actions',
|
||||||
|
width: 180,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
render: (row) => {
|
||||||
|
const buttons = []
|
||||||
|
|
||||||
|
// 编辑按钮
|
||||||
|
if (hasPermission('system:dict:update')) {
|
||||||
|
buttons.push(h(NButton, {
|
||||||
|
type: 'primary',
|
||||||
|
size: 'small',
|
||||||
|
class: 'action-btn-primary',
|
||||||
|
onClick: () => handleEdit(row),
|
||||||
|
}, {
|
||||||
|
icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, {
|
||||||
|
default: () => h(IconParkOutlineEdit),
|
||||||
|
}),
|
||||||
|
default: () => '编辑',
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除按钮
|
||||||
|
if (hasPermission('system:dict:delete')) {
|
||||||
|
buttons.push(h(NPopconfirm, {
|
||||||
|
onPositiveClick: () => handleDelete(row),
|
||||||
|
negativeText: '取消',
|
||||||
|
positiveText: '确定',
|
||||||
|
}, {
|
||||||
|
default: () => '确定删除此字典类型吗?删除后将同时删除相关的字典数据!',
|
||||||
|
trigger: () => h(NButton, {
|
||||||
|
type: 'error',
|
||||||
|
secondary: true,
|
||||||
|
size: 'small',
|
||||||
|
class: 'action-btn-secondary action-btn-danger',
|
||||||
|
}, {
|
||||||
|
icon: () => h(NIcon, { size: 14, style: 'transform: translateY(-1px)' }, {
|
||||||
|
default: () => h(IconParkOutlineDelete),
|
||||||
|
}),
|
||||||
|
default: () => '删除',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
return h('div', { class: 'flex items-center justify-center gap-2' }, buttons)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const _hasSearchConditions = computed(() => {
|
||||||
|
return !!(searchForm.dictName || searchForm.dictType || searchForm.dictStatus)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取表格数据
|
||||||
|
async function getTableData() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const params = {
|
||||||
|
pageNo: pagination.page,
|
||||||
|
pageSize: pagination.pageSize,
|
||||||
|
dictName: searchForm.dictName || undefined,
|
||||||
|
dictType: searchForm.dictType || undefined,
|
||||||
|
dictStatus: searchForm.dictStatus || undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, isSuccess } = await getDictTypeList(params)
|
||||||
|
|
||||||
|
if (isSuccess && data) {
|
||||||
|
tableData.value = data.records
|
||||||
|
pagination.itemCount = data.total
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tableData.value = []
|
||||||
|
pagination.itemCount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
coiMsgError('获取数据失败')
|
||||||
|
tableData.value = []
|
||||||
|
pagination.itemCount = 0
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
function handleSearch() {
|
||||||
|
pagination.page = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置搜索
|
||||||
|
function handleReset() {
|
||||||
|
searchForm.dictName = ''
|
||||||
|
searchForm.dictType = ''
|
||||||
|
searchForm.dictStatus = ''
|
||||||
|
pagination.page = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
function handleAdd() {
|
||||||
|
isEdit.value = false
|
||||||
|
modalTitle.value = '新增字典类型'
|
||||||
|
|
||||||
|
// 重置表单数据
|
||||||
|
Object.assign(formData, {
|
||||||
|
dictId: '',
|
||||||
|
dictType: '',
|
||||||
|
dictName: '',
|
||||||
|
dictStatus: DictStatus.NORMAL,
|
||||||
|
remark: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
formDialogRef.value?.coiOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
function handleEdit(row: DictTypeVo) {
|
||||||
|
isEdit.value = true
|
||||||
|
modalTitle.value = '编辑字典类型'
|
||||||
|
|
||||||
|
// 填充表单数据
|
||||||
|
Object.assign(formData, {
|
||||||
|
dictId: row.dictId,
|
||||||
|
dictType: row.dictType,
|
||||||
|
dictName: row.dictName,
|
||||||
|
dictStatus: row.dictStatus,
|
||||||
|
remark: row.remark || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
formDialogRef.value?.coiOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
async function handleDelete(row: DictTypeVo) {
|
||||||
|
try {
|
||||||
|
const { isSuccess } = await deleteDictType(row.dictId)
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
coiMsgSuccess('删除成功')
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
coiMsgError('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
coiMsgError('删除失败,请检查网络连接')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除
|
||||||
|
async function handleBatchDelete() {
|
||||||
|
if (checkedRowKeys.value.length === 0) {
|
||||||
|
coiMsgWarning('请选择要删除的数据')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await coiMsgBox(`确定删除选中的${checkedRowKeys.value.length}条记录吗?删除后将同时删除相关的字典数据!`, '批量删除确认')
|
||||||
|
|
||||||
|
const { isSuccess } = await batchDeleteDictType(checkedRowKeys.value)
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
coiMsgSuccess('批量删除成功')
|
||||||
|
checkedRowKeys.value = []
|
||||||
|
selectedRows.value = []
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
coiMsgError('批量删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// 用户取消删除
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态切换
|
||||||
|
async function handleStatusChange(row: DictTypeVo) {
|
||||||
|
const newStatus = row.dictStatus === DictStatus.NORMAL ? DictStatus.DISABLED : DictStatus.NORMAL
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { isSuccess } = await updateDictTypeStatus(row.dictId, newStatus)
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
coiMsgSuccess('状态修改成功')
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
coiMsgError('状态修改失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
coiMsgError('状态修改失败,请检查网络连接')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步缓存
|
||||||
|
async function handleSyncCache() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const { isSuccess } = await syncDictCache()
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
coiMsgSuccess('缓存同步成功')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
coiMsgError('缓存同步失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
coiMsgError('缓存同步失败,请检查网络连接')
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 行选择变化
|
||||||
|
function handleRowSelectionChange(rowKeys: string[]) {
|
||||||
|
checkedRowKeys.value = rowKeys
|
||||||
|
selectedRows.value = tableData.value.filter(row => rowKeys.includes(row.dictId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单提交
|
||||||
|
async function handleSubmit() {
|
||||||
|
if (!formRef.value)
|
||||||
|
return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 先进行表单验证
|
||||||
|
await formRef.value.validate()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// 表单验证失败,提示用户检查填写内容
|
||||||
|
coiMsgWarning('验证失败,请检查填写内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证通过,执行API调用
|
||||||
|
try {
|
||||||
|
const submitData = { ...formData }
|
||||||
|
|
||||||
|
const { isSuccess } = isEdit.value ? await updateDictType(submitData) : await addDictType(submitData)
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
coiMsgSuccess(isEdit.value ? '修改成功' : '新增成功')
|
||||||
|
formDialogRef.value?.coiClose()
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
coiMsgError(isEdit.value ? '修改失败,请稍后重试' : '新增失败,请稍后重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
coiMsgError(error.message || (isEdit.value ? '修改失败,请检查网络连接' : '新增失败,请检查网络连接'))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
coiMsgError(isEdit.value ? '修改失败,请检查网络连接' : '新增失败,请检查网络连接')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看字典数据
|
||||||
|
function handleViewDictData(row: DictTypeVo) {
|
||||||
|
router.push({
|
||||||
|
path: '/system/dict/data',
|
||||||
|
query: {
|
||||||
|
dictType: row.dictType,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消操作
|
||||||
|
function handleCancel() {
|
||||||
|
formDialogRef.value?.coiClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dict-management {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格样式 */
|
||||||
|
.custom-table :deep(.n-data-table-td) {
|
||||||
|
padding: 8px 16px !important;
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.n-data-table-th) {
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.n-data-table-tr:hover .n-data-table-td) {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮样式 */
|
||||||
|
.action-btn-primary {
|
||||||
|
background-color: #1890ff;
|
||||||
|
border-color: #1890ff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn-secondary {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn-secondary.action-btn-danger {
|
||||||
|
color: #ff4d4f;
|
||||||
|
border-color: #ff4d4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn-secondary.action-btn-danger:hover {
|
||||||
|
background-color: #ff4d4f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条样式 */
|
||||||
|
.custom-table :deep(.n-data-table-base-table) {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.n-data-table-base-table)::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.n-data-table-base-table)::-webkit-scrollbar-track {
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.n-data-table-base-table)::-webkit-scrollbar-thumb {
|
||||||
|
background: #c1c1c1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.n-data-table-base-table)::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #a8a8a8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 紧凑表单样式 */
|
||||||
|
.compact-form :deep(.n-form-item) {
|
||||||
|
margin-bottom: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-form-item .n-form-item-feedback-wrapper) {
|
||||||
|
min-height: 0 !important;
|
||||||
|
padding-top: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-form-item .n-form-item-label) {
|
||||||
|
padding-bottom: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-input),
|
||||||
|
.compact-form :deep(.n-input-number),
|
||||||
|
.compact-form :deep(.n-select),
|
||||||
|
.compact-form :deep(.n-cascader),
|
||||||
|
.compact-form :deep(.n-radio-group) {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-input .n-input__input-el),
|
||||||
|
.compact-form :deep(.n-input-number .n-input__input-el) {
|
||||||
|
padding: 2px 1px !important;
|
||||||
|
min-height: 32px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-select .n-base-selection) {
|
||||||
|
min-height: 32px !important;
|
||||||
|
padding: 2px 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-select .n-base-selection .n-base-selection-label) {
|
||||||
|
padding: 0 6px !important;
|
||||||
|
min-height: 30px !important;
|
||||||
|
line-height: 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.compact-form :deep(.n-cascader .n-cascader-trigger) {
|
||||||
|
min-height: 32px !important;
|
||||||
|
padding: 2px 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字典类型可点击链接样式 - 使用青绿色系配色 */
|
||||||
|
.custom-table :deep(.dict-type-clickable-link) {
|
||||||
|
color: #326C72 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.dict-type-clickable-link:hover) {
|
||||||
|
color: #4A9BA1 !important;
|
||||||
|
text-decoration: underline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table :deep(.dict-type-clickable-link:active) {
|
||||||
|
color: #275A5F !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 针对表格单元格内容的强制样式 */
|
||||||
|
.custom-table :deep(.n-data-table-td .dict-type-clickable-link) {
|
||||||
|
color: #326C72 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 更加具体的选择器以确保样式生效 */
|
||||||
|
.dict-management .custom-table :deep(.n-data-table-tbody .n-data-table-tr .n-data-table-td .dict-type-clickable-link) {
|
||||||
|
color: #326C72 !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user