mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-09-18 03:39:58 +08:00
feat: 增加字典管理角色管理
This commit is contained in:
parent
3124d0b923
commit
53282f9453
@ -45,7 +45,7 @@ const propOverrides = {
|
|||||||
cols: 4,
|
cols: 4,
|
||||||
},
|
},
|
||||||
ProModalForm: {
|
ProModalForm: {
|
||||||
labelWidth: 120,
|
labelWidth: 100,
|
||||||
labelPlacement: 'left',
|
labelPlacement: 'left',
|
||||||
preset: 'card',
|
preset: 'card',
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,7 @@ interface UserQueryParams {
|
|||||||
pageSize?: number
|
pageSize?: number
|
||||||
username?: string
|
username?: string
|
||||||
gender?: 'male' | 'female' | 'unknown'
|
gender?: 'male' | 'female' | 'unknown'
|
||||||
userStatus?: number
|
status?: number
|
||||||
deptId?: number
|
deptId?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,5 +8,5 @@ export enum Regex {
|
|||||||
|
|
||||||
Email = '^(([^<>()[\\]\\\\.,;:\\s@"]+(\\.[^<>()[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$',
|
Email = '^(([^<>()[\\]\\\\.,;:\\s@"]+(\\.[^<>()[\\]\\\\.,;:\\s@"]+)*)|(".+"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$',
|
||||||
|
|
||||||
RouteName = '^[\\w_!@#$%^&*~-]+$',
|
Phone = '^1[3-9]\d{9}$',
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
/** Gender */
|
|
||||||
export enum Gender {
|
|
||||||
male,
|
|
||||||
female,
|
|
||||||
}
|
|
@ -1,2 +1 @@
|
|||||||
export * from './Regex'
|
export * from './Regex'
|
||||||
export * from './User'
|
|
||||||
|
44
src/typings/entities/dict.d.ts
vendored
44
src/typings/entities/dict.d.ts
vendored
@ -3,14 +3,18 @@
|
|||||||
/** 数据库表字段 */
|
/** 数据库表字段 */
|
||||||
namespace Entity {
|
namespace Entity {
|
||||||
interface DictType {
|
interface DictType {
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
id?: number
|
||||||
/**
|
/**
|
||||||
* 字典名称
|
* 字典名称
|
||||||
*/
|
*/
|
||||||
dictName: string
|
name: string
|
||||||
/**
|
/**
|
||||||
* 字典类型
|
* 字典类型
|
||||||
*/
|
*/
|
||||||
dictType: string
|
type: string
|
||||||
/**
|
/**
|
||||||
* 备注信息
|
* 备注信息
|
||||||
*/
|
*/
|
||||||
@ -19,21 +23,29 @@ namespace Entity {
|
|||||||
* 状态
|
* 状态
|
||||||
*/
|
*/
|
||||||
status?: number
|
status?: number
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
createTime?: string
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
updateTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DictData {
|
interface DictData {
|
||||||
/**
|
/**
|
||||||
* 样式属性
|
* 主键ID
|
||||||
*/
|
*/
|
||||||
cssClass?: string
|
id?: number
|
||||||
/**
|
/**
|
||||||
* 字典标签
|
* 字典名称
|
||||||
*/
|
*/
|
||||||
dictLabel: string
|
name: string
|
||||||
/**
|
/**
|
||||||
* 字典排序
|
* 字典排序
|
||||||
*/
|
*/
|
||||||
dictSort?: number
|
sort?: number
|
||||||
/**
|
/**
|
||||||
* 字典类型
|
* 字典类型
|
||||||
*/
|
*/
|
||||||
@ -41,15 +53,7 @@ namespace Entity {
|
|||||||
/**
|
/**
|
||||||
* 字典键值
|
* 字典键值
|
||||||
*/
|
*/
|
||||||
dictValue: string
|
value: string
|
||||||
/**
|
|
||||||
* 是否默认
|
|
||||||
*/
|
|
||||||
isDefault?: number
|
|
||||||
/**
|
|
||||||
* 表格回显样式
|
|
||||||
*/
|
|
||||||
listClass?: string
|
|
||||||
/**
|
/**
|
||||||
* 备注信息
|
* 备注信息
|
||||||
*/
|
*/
|
||||||
@ -58,5 +62,13 @@ namespace Entity {
|
|||||||
* 状态
|
* 状态
|
||||||
*/
|
*/
|
||||||
status?: number
|
status?: number
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
createTime?: string
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
updateTime?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
src/typings/entities/role.d.ts
vendored
4
src/typings/entities/role.d.ts
vendored
@ -8,7 +8,7 @@ namespace Entity {
|
|||||||
/**
|
/**
|
||||||
* 角色ID
|
* 角色ID
|
||||||
*/
|
*/
|
||||||
roleId: number
|
id: number
|
||||||
/**
|
/**
|
||||||
* 菜单ID数组
|
* 菜单ID数组
|
||||||
*/
|
*/
|
||||||
@ -28,7 +28,7 @@ namespace Entity {
|
|||||||
/**
|
/**
|
||||||
* 角色状态
|
* 角色状态
|
||||||
*/
|
*/
|
||||||
roleStatus?: number
|
status?: number
|
||||||
/**
|
/**
|
||||||
* 显示顺序
|
* 显示顺序
|
||||||
*/
|
*/
|
||||||
|
8
src/typings/entities/user.d.ts
vendored
8
src/typings/entities/user.d.ts
vendored
@ -4,7 +4,7 @@
|
|||||||
namespace Entity {
|
namespace Entity {
|
||||||
interface User {
|
interface User {
|
||||||
/** 用户id */
|
/** 用户id */
|
||||||
userId: number
|
id: number
|
||||||
/** 部门id */
|
/** 部门id */
|
||||||
deptId?: any
|
deptId?: any
|
||||||
/** 用户名 */
|
/** 用户名 */
|
||||||
@ -22,13 +22,9 @@ namespace Entity {
|
|||||||
/** 头像 */
|
/** 头像 */
|
||||||
avatar?: string
|
avatar?: string
|
||||||
/** 用户状态 */
|
/** 用户状态 */
|
||||||
userStatus: number
|
status: number
|
||||||
/** 创建人 */
|
|
||||||
createBy?: string
|
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
createTime: string
|
createTime: string
|
||||||
/** 更新人 */
|
|
||||||
updateBy?: string
|
|
||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
updateTime: string
|
updateTime: string
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
|
@ -72,24 +72,23 @@ export function createAlovaInstance(
|
|||||||
const { status } = response
|
const { status } = response
|
||||||
let errorMessage = ''
|
let errorMessage = ''
|
||||||
|
|
||||||
|
const res = await response.clone().json()
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
// 返回blob数据
|
// 返回blob数据
|
||||||
if (method.meta?.isBlob)
|
if (method.meta?.isBlob)
|
||||||
return response.blob()
|
return response.blob()
|
||||||
|
|
||||||
// 返回json数据
|
|
||||||
const apiData = await response.json()
|
|
||||||
// 请求成功
|
// 请求成功
|
||||||
if (apiData[_backendConfig.codeKey] === _backendConfig.successCode)
|
if (res[_backendConfig.codeKey] === _backendConfig.successCode)
|
||||||
return apiData
|
return res
|
||||||
|
|
||||||
// 业务请求失败
|
// 业务请求失败
|
||||||
errorMessage = apiData[_backendConfig.msgKey]
|
errorMessage = res[_backendConfig.msgKey]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// 接口请求失败
|
// 接口请求失败
|
||||||
const errorCode = response.status as ErrorStatus
|
const errorCode = response.status as ErrorStatus
|
||||||
errorMessage = ERROR_STATUS[errorCode] || ERROR_STATUS.default
|
errorMessage = res[_backendConfig.msgKey] || ERROR_STATUS[errorCode] || ERROR_STATUS.default
|
||||||
}
|
}
|
||||||
window.$message?.error(errorMessage)
|
window.$message?.error(errorMessage)
|
||||||
throw new Error(errorMessage)
|
throw new Error(errorMessage)
|
||||||
|
@ -49,7 +49,7 @@ const rules = computed(() => {
|
|||||||
})
|
})
|
||||||
const formValue = ref({
|
const formValue = ref({
|
||||||
account: 'admin',
|
account: 'admin',
|
||||||
pwd: '12345',
|
pwd: '123456',
|
||||||
captcha: '',
|
captcha: '',
|
||||||
})
|
})
|
||||||
const isRemember = ref(false)
|
const isRemember = ref(false)
|
||||||
|
@ -185,7 +185,7 @@ function handleAddTable() {
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-park-outline-add-one />
|
<icon-park-outline-add-one />
|
||||||
</template>
|
</template>
|
||||||
新建
|
新增
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton strong secondary>
|
<NButton strong secondary>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
@ -1,144 +1,98 @@
|
|||||||
import type { DataTableColumns } from 'naive-ui'
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
import { NButton, NFlex, NPopconfirm } from 'naive-ui'
|
import { NButton, NPopconfirm, NSpace } from 'naive-ui'
|
||||||
import CopyText from '@/components/custom/CopyText.vue'
|
import type { ProSearchFormColumns } from 'pro-naive-ui'
|
||||||
|
import { renderProCopyableText, renderProTags } from 'pro-naive-ui'
|
||||||
|
|
||||||
// 字典类型columns配置函数
|
|
||||||
interface DictTypeColumnActions {
|
|
||||||
onView: (code: string) => void
|
|
||||||
onEdit: (row: Entity.DictType) => void
|
|
||||||
onDelete: (id: number) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDictTypeColumns(actions: DictTypeColumnActions): DataTableColumns<Entity.DictType> {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
title: '字典项',
|
|
||||||
key: 'dictName',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '字典码',
|
|
||||||
key: 'dictType',
|
|
||||||
render: (row) => {
|
|
||||||
return (
|
|
||||||
<CopyText value={row.dictType} />
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
key: 'status',
|
|
||||||
align: 'center',
|
|
||||||
render: (row) => {
|
|
||||||
return (
|
|
||||||
<span>{row.status === 1 ? '正常' : '停用'}</span>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
align: 'center',
|
|
||||||
render: (row) => {
|
|
||||||
return (
|
|
||||||
<NFlex justify="center">
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
onClick={() => actions.onView(row.dictType)}
|
|
||||||
>
|
|
||||||
查看字典
|
|
||||||
</NButton>
|
|
||||||
<NButton
|
|
||||||
size="small"
|
|
||||||
onClick={() => actions.onEdit(row)}
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</NButton>
|
|
||||||
<NPopconfirm onPositiveClick={() => actions.onDelete(row.id!)}>
|
|
||||||
{{
|
|
||||||
default: () => (
|
|
||||||
<span>
|
|
||||||
确认删除字典类型
|
|
||||||
<b>{row.dictName}</b>
|
|
||||||
{' '}
|
|
||||||
?
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
trigger: () => <NButton size="small" type="error">删除</NButton>,
|
|
||||||
}}
|
|
||||||
</NPopconfirm>
|
|
||||||
</NFlex>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字典数据columns配置函数
|
|
||||||
interface DictDataColumnActions {
|
interface DictDataColumnActions {
|
||||||
onEdit: (row: Entity.DictData) => void
|
onEdit: (row: Entity.DictData) => void
|
||||||
onDelete: (id: number) => void
|
onDelete: (id: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const dictDataSearchColumns: ProSearchFormColumns<Entity.DictData> = [
|
||||||
|
{
|
||||||
|
title: '数据名称',
|
||||||
|
path: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '数据键值',
|
||||||
|
path: 'value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
path: 'status',
|
||||||
|
field: 'select',
|
||||||
|
fieldProps: {
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 1 },
|
||||||
|
{ label: '停用', value: 0 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export function createDictDataColumns(actions: DictDataColumnActions): DataTableColumns<Entity.DictData> {
|
export function createDictDataColumns(actions: DictDataColumnActions): DataTableColumns<Entity.DictData> {
|
||||||
return [
|
return [
|
||||||
|
|
||||||
{
|
{
|
||||||
title: '字典名称',
|
key: 'name',
|
||||||
key: 'dictLabel',
|
title: '数据名称',
|
||||||
|
align: 'left',
|
||||||
|
minWidth: 120,
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'value',
|
||||||
|
title: '数据键值',
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
render: row => renderProCopyableText(row.value),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '字典码',
|
|
||||||
key: 'dictType',
|
key: 'dictType',
|
||||||
|
title: '字典类型',
|
||||||
|
align: 'center',
|
||||||
|
render: row => renderProTags(row.dictType),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '字典值',
|
key: 'sort',
|
||||||
key: 'dictValue',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '排序',
|
title: '排序',
|
||||||
key: 'dictSort',
|
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: '80px',
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
key: 'remark',
|
||||||
key: 'status',
|
title: '备注',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
render: (row) => {
|
|
||||||
return (
|
|
||||||
<span>{row.status === 1 ? '正常' : '停用'}</span>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
|
title: '操作',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: '15em',
|
width: 150,
|
||||||
render: (row) => {
|
render: (row: Entity.DictData) => (
|
||||||
return (
|
<NSpace justify="center">
|
||||||
<NFlex justify="center">
|
<NButton
|
||||||
<NButton
|
type="primary"
|
||||||
size="small"
|
text
|
||||||
onClick={() => actions.onEdit(row)}
|
onClick={() => actions.onEdit(row)}
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</NButton>
|
</NButton>
|
||||||
<NPopconfirm onPositiveClick={() => actions.onDelete(row.id!)}>
|
<NPopconfirm
|
||||||
{{
|
onPositiveClick={() => actions.onDelete(row.id!)}
|
||||||
default: () => (
|
v-slots={{
|
||||||
<span>
|
trigger: () => (
|
||||||
确认删除字典数据
|
<NButton type="error" text>
|
||||||
<b>{row.dictLabel}</b>
|
删除
|
||||||
{' '}
|
</NButton>
|
||||||
?
|
),
|
||||||
</span>
|
default: () => `确定删除字典数据"${row.name}"吗?`,
|
||||||
),
|
}}
|
||||||
trigger: () => <NButton size="small" type="error">删除</NButton>,
|
/>
|
||||||
}}
|
</NSpace>
|
||||||
</NPopconfirm>
|
),
|
||||||
</NFlex>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
143
src/views/setting/dict/components/DictDataModal.vue
Normal file
143
src/views/setting/dict/components/DictDataModal.vue
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useBoolean } from '@/hooks'
|
||||||
|
import { createDictData, getDictDataById, updateDictData } from '@/api'
|
||||||
|
import { createProModalForm } from 'pro-naive-ui'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modalName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalName = '',
|
||||||
|
} = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
success: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
||||||
|
|
||||||
|
const modalForm = createProModalForm<Partial<Entity.DictData>>({
|
||||||
|
omitEmptyString: false,
|
||||||
|
initialValues: {
|
||||||
|
sort: 0,
|
||||||
|
status: 1,
|
||||||
|
},
|
||||||
|
onSubmit: submitModal,
|
||||||
|
})
|
||||||
|
|
||||||
|
type ModalType = 'add' | 'edit'
|
||||||
|
const modalType = shallowRef<ModalType>('add')
|
||||||
|
const modalTitle = computed(() => {
|
||||||
|
const titleMap: Record<ModalType, string> = {
|
||||||
|
add: '添加',
|
||||||
|
edit: '编辑',
|
||||||
|
}
|
||||||
|
return `${titleMap[modalType.value]}${modalName}`
|
||||||
|
})
|
||||||
|
|
||||||
|
async function openModal(type: ModalType = 'add', data?: Partial<Entity.DictData>) {
|
||||||
|
modalType.value = type
|
||||||
|
modalForm.open()
|
||||||
|
const handlers = {
|
||||||
|
async add() {
|
||||||
|
if (data?.dictType) {
|
||||||
|
modalForm.values.value.dictType = data.dictType
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async edit() {
|
||||||
|
if (!data)
|
||||||
|
return
|
||||||
|
|
||||||
|
const { data: dictData } = await getDictDataById(data.id!)
|
||||||
|
modalForm.values.value = dictData
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await handlers[type]()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitModal(filedValues: Partial<Entity.DictData>) {
|
||||||
|
const handlers = {
|
||||||
|
async add() {
|
||||||
|
try {
|
||||||
|
await createDictData(filedValues)
|
||||||
|
window.$message.success('字典数据创建成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('创建字典数据失败', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async edit() {
|
||||||
|
try {
|
||||||
|
await updateDictData(modalForm.values.value.id!, filedValues)
|
||||||
|
window.$message.success('字典数据更新成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('更新字典数据失败', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
startLoading()
|
||||||
|
const success = await handlers[modalType.value]()
|
||||||
|
endLoading()
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
emit('success')
|
||||||
|
modalForm.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openModal,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<pro-modal-form
|
||||||
|
:title="modalTitle"
|
||||||
|
:form="modalForm"
|
||||||
|
:loading="submitLoading"
|
||||||
|
width="700px"
|
||||||
|
>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<pro-input
|
||||||
|
required
|
||||||
|
title="字典名称"
|
||||||
|
path="name"
|
||||||
|
placeholder="请输入字典名称"
|
||||||
|
/>
|
||||||
|
<pro-input
|
||||||
|
required
|
||||||
|
title="字典键值"
|
||||||
|
path="value"
|
||||||
|
placeholder="请输入字典键值"
|
||||||
|
/>
|
||||||
|
<pro-input
|
||||||
|
title="字典类型"
|
||||||
|
path="dictType"
|
||||||
|
placeholder="字典类型"
|
||||||
|
:readonly="true"
|
||||||
|
/>
|
||||||
|
<pro-digit
|
||||||
|
title="排序"
|
||||||
|
path="sort"
|
||||||
|
:field-props="{ min: 0, max: 999 }"
|
||||||
|
/>
|
||||||
|
<pro-switch
|
||||||
|
title="状态"
|
||||||
|
path="status"
|
||||||
|
:field-props="{ checkedValue: 1, uncheckedValue: 0 }"
|
||||||
|
/>
|
||||||
|
<pro-textarea
|
||||||
|
class="col-span-2"
|
||||||
|
title="备注"
|
||||||
|
path="remark"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</pro-modal-form>
|
||||||
|
</template>
|
@ -1,202 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { FormRules } from 'naive-ui'
|
|
||||||
import { useBoolean } from '@/hooks'
|
|
||||||
import { createDictData, createDictType, updateDictData, updateDictType } from '@/api'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
modalName?: string
|
|
||||||
dictCode?: string
|
|
||||||
isRoot?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
modalName = '',
|
|
||||||
dictCode,
|
|
||||||
isRoot = false,
|
|
||||||
} = defineProps<Props>()
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
open: []
|
|
||||||
close: []
|
|
||||||
success: []
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const { bool: modalVisible, setTrue: showModal, setFalse: hiddenModal } = useBoolean(false)
|
|
||||||
|
|
||||||
const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
|
||||||
|
|
||||||
const formDefault: any = {
|
|
||||||
label: '',
|
|
||||||
code: '',
|
|
||||||
}
|
|
||||||
const formModel = ref<any>({ ...formDefault })
|
|
||||||
|
|
||||||
type ModalType = 'add' | 'view' | 'edit'
|
|
||||||
const modalType = shallowRef<ModalType>('add')
|
|
||||||
const modalTitle = computed(() => {
|
|
||||||
const titleMap: Record<ModalType, string> = {
|
|
||||||
add: '添加',
|
|
||||||
view: '查看',
|
|
||||||
edit: '编辑',
|
|
||||||
}
|
|
||||||
return `${titleMap[modalType.value]}${modalName}`
|
|
||||||
})
|
|
||||||
|
|
||||||
async function openModal(type: ModalType = 'add', data?: any) {
|
|
||||||
emit('open')
|
|
||||||
modalType.value = type
|
|
||||||
showModal()
|
|
||||||
const handlers = {
|
|
||||||
async add() {
|
|
||||||
formModel.value = { ...formDefault }
|
|
||||||
|
|
||||||
formModel.value.isRoot = isRoot ? 1 : 0
|
|
||||||
if (dictCode) {
|
|
||||||
formModel.value.code = dictCode
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async view() {
|
|
||||||
if (!data)
|
|
||||||
return
|
|
||||||
formModel.value = { ...data }
|
|
||||||
},
|
|
||||||
async edit() {
|
|
||||||
if (!data)
|
|
||||||
return
|
|
||||||
formModel.value = { ...data }
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await handlers[type]()
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
hiddenModal()
|
|
||||||
endLoading()
|
|
||||||
emit('close')
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
openModal,
|
|
||||||
})
|
|
||||||
|
|
||||||
const formRef = ref()
|
|
||||||
async function submitModal() {
|
|
||||||
const handlers = {
|
|
||||||
async add() {
|
|
||||||
try {
|
|
||||||
if (isRoot) {
|
|
||||||
// 创建字典类型
|
|
||||||
await createDictType({
|
|
||||||
dictType: formModel.value.label,
|
|
||||||
dictName: formModel.value.code,
|
|
||||||
})
|
|
||||||
window.$message.success('字典类型创建成功')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 创建字典数据
|
|
||||||
await createDictData({
|
|
||||||
dictType: formModel.value.label,
|
|
||||||
dictValue: formModel.value.value,
|
|
||||||
})
|
|
||||||
window.$message.success('字典数据创建成功')
|
|
||||||
}
|
|
||||||
emit('success')
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async edit() {
|
|
||||||
try {
|
|
||||||
if (isRoot) {
|
|
||||||
// 更新字典类型
|
|
||||||
await updateDictType(formModel.value.id!, {
|
|
||||||
dictType: formModel.value.label as any,
|
|
||||||
dictName: formModel.value.code,
|
|
||||||
})
|
|
||||||
window.$message.success('字典类型更新成功')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 更新字典数据
|
|
||||||
await updateDictData(formModel.value.id!, {
|
|
||||||
dictType: formModel.value.label,
|
|
||||||
dictValue: formModel.value.value,
|
|
||||||
})
|
|
||||||
window.$message.success('字典数据更新成功')
|
|
||||||
}
|
|
||||||
emit('success')
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async view() {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await formRef.value?.validate()
|
|
||||||
startLoading()
|
|
||||||
const success = await handlers[modalType.value]()
|
|
||||||
endLoading()
|
|
||||||
if (success) {
|
|
||||||
closeModal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules: FormRules = {
|
|
||||||
label: {
|
|
||||||
required: true,
|
|
||||||
message: '请输入字典名称',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
required: true,
|
|
||||||
message: '请输入字典码',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
required: true,
|
|
||||||
message: '请输入字典值',
|
|
||||||
type: 'number',
|
|
||||||
trigger: ['input', 'blur'],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<n-modal
|
|
||||||
v-model:show="modalVisible"
|
|
||||||
:mask-closable="false"
|
|
||||||
preset="card"
|
|
||||||
:title="modalTitle"
|
|
||||||
class="w-700px"
|
|
||||||
:segmented="{
|
|
||||||
content: true,
|
|
||||||
action: true,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<n-form ref="formRef" :rules="rules" label-placement="left" :model="formModel" :label-width="100" :disabled="modalType === 'view'">
|
|
||||||
<n-form-item label="字典名称" path="label">
|
|
||||||
<n-input v-model:value="formModel.label" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item label="字典码" path="code">
|
|
||||||
<n-input v-model:value="formModel.code" :disabled="!isRoot" />
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item v-if="!isRoot" label="字典值" path="value">
|
|
||||||
<n-input-number v-model:value="formModel.value" :min="0" />
|
|
||||||
</n-form-item>
|
|
||||||
</n-form>
|
|
||||||
<template #action>
|
|
||||||
<n-space justify="center">
|
|
||||||
<n-button @click="closeModal">
|
|
||||||
取消
|
|
||||||
</n-button>
|
|
||||||
<n-button type="primary" :loading="submitLoading" @click="submitModal">
|
|
||||||
提交
|
|
||||||
</n-button>
|
|
||||||
</n-space>
|
|
||||||
</template>
|
|
||||||
</n-modal>
|
|
||||||
</template>
|
|
171
src/views/setting/dict/components/DictTypeList.vue
Normal file
171
src/views/setting/dict/components/DictTypeList.vue
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
|
import { NButton, NPopconfirm, NSpace } from 'naive-ui'
|
||||||
|
import type { ProSearchFormColumns } from 'pro-naive-ui'
|
||||||
|
import { createProSearchForm, renderProCopyableText, useNDataTable } from 'pro-naive-ui'
|
||||||
|
import { deleteDictType, getDictTypeList } from '@/api'
|
||||||
|
import DictTypeModal from './DictTypeModal.vue'
|
||||||
|
|
||||||
|
defineEmits(['select'])
|
||||||
|
|
||||||
|
// 字典类型相关
|
||||||
|
const dictTypeModalRef = ref<InstanceType<typeof DictTypeModal>>()
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const dictTypeSearchForm = createProSearchForm<Partial<Entity.DictType>>({
|
||||||
|
initialValues: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 搜索表单列配置
|
||||||
|
const dictTypeSearchColumns: ProSearchFormColumns<Entity.DictType> = [
|
||||||
|
{
|
||||||
|
title: '字典名称',
|
||||||
|
path: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '字典类型',
|
||||||
|
path: 'type',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 使用 useNDataTable
|
||||||
|
const {
|
||||||
|
table: {
|
||||||
|
tableProps: dictTypeTableProps,
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
proSearchFormProps: dictTypeSearchProps,
|
||||||
|
},
|
||||||
|
refresh: refreshDictTypes,
|
||||||
|
} = useNDataTable(getDictTypePage, {
|
||||||
|
form: dictTypeSearchForm,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取字典类型分页数据
|
||||||
|
async function getDictTypePage({ current, pageSize }: any, formData: Partial<Entity.DictType>) {
|
||||||
|
try {
|
||||||
|
const { data } = await getDictTypeList({
|
||||||
|
...formData,
|
||||||
|
pageNum: current || 1,
|
||||||
|
pageSize: pageSize || 10,
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return {
|
||||||
|
list: [],
|
||||||
|
total: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除字典类型
|
||||||
|
async function deleteDictTypeData(id: number) {
|
||||||
|
try {
|
||||||
|
await deleteDictType(id)
|
||||||
|
window.$message.success('字典类型删除成功')
|
||||||
|
refreshDictTypes()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
window.$message.error('字典类型删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const columns: DataTableColumns<Entity.DictType> = [
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '字典名称',
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'type',
|
||||||
|
title: '字典类型',
|
||||||
|
render: row => renderProCopyableText(row.type),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'actions',
|
||||||
|
title: '操作',
|
||||||
|
align: 'center',
|
||||||
|
render: (row: Entity.DictType) => (
|
||||||
|
<NSpace size="small" justify="center">
|
||||||
|
<NButton
|
||||||
|
text
|
||||||
|
type="primary"
|
||||||
|
onClick={(e: Event) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
dictTypeModalRef.value?.openModal('edit', row)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm
|
||||||
|
onPositiveClick={() => deleteDictTypeData(row.id!)}
|
||||||
|
v-slots={{
|
||||||
|
trigger: () => (
|
||||||
|
<NButton
|
||||||
|
text
|
||||||
|
type="error"
|
||||||
|
onClick={(e: Event) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</NButton>
|
||||||
|
),
|
||||||
|
default: () => `确定删除字典类型"${row.name}"吗?`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</NSpace>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace vertical>
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<n-card>
|
||||||
|
<pro-search-form
|
||||||
|
:form="dictTypeSearchForm"
|
||||||
|
:columns="dictTypeSearchColumns"
|
||||||
|
:collapse-button-props="false"
|
||||||
|
v-bind="dictTypeSearchProps"
|
||||||
|
:cols="3"
|
||||||
|
/>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<pro-data-table
|
||||||
|
:columns="columns"
|
||||||
|
v-bind="dictTypeTableProps"
|
||||||
|
:row-props="(row:Entity.DictType) => {
|
||||||
|
return {
|
||||||
|
onClick: () => $emit('select', row),
|
||||||
|
style: {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<NButton
|
||||||
|
type="primary"
|
||||||
|
@click="dictTypeModalRef?.openModal('add')"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-park-outline-plus />
|
||||||
|
</template>
|
||||||
|
新增字典类型
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
</pro-data-table>
|
||||||
|
|
||||||
|
<!-- 字典类型弹窗 -->
|
||||||
|
<DictTypeModal
|
||||||
|
ref="dictTypeModalRef"
|
||||||
|
modal-name="字典类型"
|
||||||
|
@success="refreshDictTypes"
|
||||||
|
/>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
133
src/views/setting/dict/components/DictTypeModal.vue
Normal file
133
src/views/setting/dict/components/DictTypeModal.vue
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useBoolean } from '@/hooks'
|
||||||
|
import { createDictType, getDictTypeById, updateDictType } from '@/api'
|
||||||
|
import { createProModalForm } from 'pro-naive-ui'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modalName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
modalName = '',
|
||||||
|
} = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
success: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
||||||
|
|
||||||
|
const modalForm = createProModalForm<Partial<Entity.DictType>>({
|
||||||
|
omitEmptyString: false,
|
||||||
|
initialValues: {
|
||||||
|
name: '',
|
||||||
|
type: '',
|
||||||
|
remark: '',
|
||||||
|
status: 1,
|
||||||
|
},
|
||||||
|
onSubmit: submitModal,
|
||||||
|
})
|
||||||
|
|
||||||
|
type ModalType = 'add' | 'edit'
|
||||||
|
const modalType = shallowRef<ModalType>('add')
|
||||||
|
const modalTitle = computed(() => {
|
||||||
|
const titleMap: Record<ModalType, string> = {
|
||||||
|
add: '添加',
|
||||||
|
edit: '编辑',
|
||||||
|
}
|
||||||
|
return `${titleMap[modalType.value]}${modalName}`
|
||||||
|
})
|
||||||
|
|
||||||
|
async function openModal(type: ModalType = 'add', data?: Partial<Entity.DictType>) {
|
||||||
|
modalType.value = type
|
||||||
|
modalForm.open()
|
||||||
|
const handlers = {
|
||||||
|
async add() {
|
||||||
|
// 使用默认值
|
||||||
|
},
|
||||||
|
async edit() {
|
||||||
|
if (!data)
|
||||||
|
return
|
||||||
|
|
||||||
|
const { data: dictType } = await getDictTypeById(data.id!)
|
||||||
|
modalForm.values.value = dictType
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await handlers[type]()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitModal(filedValues: Partial<Entity.DictType>) {
|
||||||
|
const handlers = {
|
||||||
|
async add() {
|
||||||
|
try {
|
||||||
|
await createDictType(filedValues)
|
||||||
|
window.$message.success('字典类型创建成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('创建字典类型失败', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async edit() {
|
||||||
|
try {
|
||||||
|
await updateDictType(modalForm.values.value.id!, filedValues)
|
||||||
|
window.$message.success('字典类型更新成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('更新字典类型失败', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
startLoading()
|
||||||
|
const success = await handlers[modalType.value]()
|
||||||
|
endLoading()
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
emit('success')
|
||||||
|
modalForm.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openModal,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<pro-modal-form
|
||||||
|
:title="modalTitle"
|
||||||
|
:form="modalForm"
|
||||||
|
:loading="submitLoading"
|
||||||
|
width="600px"
|
||||||
|
>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<pro-input
|
||||||
|
required
|
||||||
|
title="字典名称"
|
||||||
|
path="name"
|
||||||
|
placeholder="请输入字典名称"
|
||||||
|
/>
|
||||||
|
<pro-input
|
||||||
|
required
|
||||||
|
title="字典类型"
|
||||||
|
path="type"
|
||||||
|
placeholder="请输入字典类型"
|
||||||
|
:readonly="modalType === 'edit'"
|
||||||
|
/>
|
||||||
|
<pro-textarea
|
||||||
|
class="col-span-2"
|
||||||
|
title="备注"
|
||||||
|
path="remark"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
/>
|
||||||
|
<pro-switch
|
||||||
|
title="状态"
|
||||||
|
path="status"
|
||||||
|
:field-props="{ checkedValue: 1, uncheckedValue: 0 }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</pro-modal-form>
|
||||||
|
</template>
|
@ -1,144 +1,124 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="ts">
|
||||||
import { useBoolean } from '@/hooks'
|
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
||||||
import { deleteDictData, deleteDictType, getDictDataByType, getDictTypeList } from '@/api'
|
import { deleteDictData, getDictDataList } from '@/api'
|
||||||
import { createDictDataColumns, createDictTypeColumns } from './columns'
|
import { createDictDataColumns, dictDataSearchColumns } from './columns'
|
||||||
import DictModal from './components/DictModal.vue'
|
import DictTypeList from './components/DictTypeList.vue'
|
||||||
|
import DictDataModal from './components/DictDataModal.vue'
|
||||||
|
|
||||||
const { bool: dictLoading, setTrue: startDictLoading, setFalse: endDictLoading } = useBoolean(false)
|
const dictTypeListRef = ref<InstanceType<typeof DictTypeList>>()
|
||||||
const { bool: contentLoading, setTrue: startContentLoading, setFalse: endContentLoading } = useBoolean(false)
|
|
||||||
|
|
||||||
const dictRef = ref<InstanceType<typeof DictModal>>()
|
// 字典数据相关
|
||||||
const dictContentRef = ref<InstanceType<typeof DictModal>>()
|
const dictDataSearchForm = createProSearchForm<Partial<Entity.DictData>>({
|
||||||
|
initialValues: {},
|
||||||
|
})
|
||||||
|
const { values } = dictDataSearchForm
|
||||||
|
|
||||||
onMounted(() => {
|
const {
|
||||||
getDictList()
|
table: {
|
||||||
|
tableProps: dictDataTableProps,
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
proSearchFormProps: dictDataSearchProps,
|
||||||
|
},
|
||||||
|
refresh: refreshDictData,
|
||||||
|
} = useNDataTable(getDictDataPage, {
|
||||||
|
form: dictDataSearchForm,
|
||||||
})
|
})
|
||||||
|
|
||||||
const dictData = ref<Entity.DictType[]>([])
|
const dictDataModalRef = ref<InstanceType<typeof DictDataModal>>()
|
||||||
const dictContentData = ref<Entity.DictData[]>([])
|
|
||||||
|
|
||||||
async function getDictList() {
|
// 选择字典类型
|
||||||
startDictLoading()
|
const currentDictType = ref<Entity.DictType | null>(null)
|
||||||
|
function handleDictTypeSelect(dictType: Entity.DictType) {
|
||||||
|
currentDictType.value = dictType
|
||||||
|
refreshDictData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除字典数据
|
||||||
|
async function deleteDictDataItem(id: number) {
|
||||||
try {
|
try {
|
||||||
const { data } = await getDictTypeList()
|
await deleteDictData(id)
|
||||||
dictData.value = data.list
|
window.$message.success('字典数据删除成功')
|
||||||
|
refreshDictData()
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch {
|
||||||
console.error('获取字典类型列表失败', error)
|
window.$message.error('字典数据删除失败')
|
||||||
}
|
|
||||||
finally {
|
|
||||||
endDictLoading()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastDictCode = ref('')
|
// 字典数据表格列配置
|
||||||
async function getDictContent(code: string) {
|
const dictDataColumns = createDictDataColumns({
|
||||||
startContentLoading()
|
onEdit: (row: Entity.DictData) => dictDataModalRef.value?.openModal('edit', row),
|
||||||
try {
|
onDelete: deleteDictDataItem,
|
||||||
const { data } = await getDictDataByType(code)
|
|
||||||
dictContentData.value = data
|
|
||||||
lastDictCode.value = code
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error('获取字典数据失败', error)
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
endContentLoading()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字典类型columns配置
|
|
||||||
const dictColumns = createDictTypeColumns({
|
|
||||||
onView: getDictContent,
|
|
||||||
onEdit: row => dictRef.value!.openModal('edit', row),
|
|
||||||
onDelete: id => deleteDict(id, true),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 字典数据columns配置
|
// 获取字典数据分页
|
||||||
const contentColumns = createDictDataColumns({
|
async function getDictDataPage({ current, pageSize }: any, formData: Partial<Entity.DictData>) {
|
||||||
onEdit: row => dictContentRef.value!.openModal('edit', row),
|
if (!currentDictType.value) {
|
||||||
onDelete: id => deleteDict(id, false),
|
return { list: [], total: 0 }
|
||||||
})
|
|
||||||
|
|
||||||
async function deleteDict(id: number, isType: boolean = false) {
|
|
||||||
try {
|
|
||||||
if (isType) {
|
|
||||||
await deleteDictType(id)
|
|
||||||
window.$message.success('删除字典类型成功')
|
|
||||||
getDictList() // 重新加载字典类型列表
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await deleteDictData(id)
|
|
||||||
window.$message.success('删除字典数据成功')
|
|
||||||
if (lastDictCode.value) {
|
|
||||||
getDictContent(lastDictCode.value) // 重新加载字典数据
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
|
||||||
console.error(`删除${isType ? '字典类型' : '字典数据'}失败`, error)
|
try {
|
||||||
|
const { data } = await getDictDataList({
|
||||||
|
...formData,
|
||||||
|
dictType: currentDictType.value.type,
|
||||||
|
pageNum: current || 1,
|
||||||
|
pageSize,
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return {
|
||||||
|
list: [],
|
||||||
|
total: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NFlex>
|
<div class="flex h-full gap-2">
|
||||||
<div class="basis-2/5">
|
<!-- 左侧字典类型列表 -->
|
||||||
<n-card>
|
<div class="w-1/3">
|
||||||
<template #header>
|
<DictTypeList
|
||||||
<NButton type="primary" @click="dictRef!.openModal('add')">
|
ref="dictTypeListRef"
|
||||||
<template #icon>
|
@select="handleDictTypeSelect"
|
||||||
<icon-park-outline-add-one />
|
/>
|
||||||
</template>
|
|
||||||
新建
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #header-extra>
|
|
||||||
<NFlex>
|
|
||||||
<NButton type="primary" secondary @click="getDictList">
|
|
||||||
<template #icon>
|
|
||||||
<icon-park-outline-refresh />
|
|
||||||
</template>
|
|
||||||
刷新
|
|
||||||
</NButton>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
<n-data-table
|
|
||||||
:columns="dictColumns" :data="dictData" :loading="dictLoading" :pagination="false"
|
|
||||||
:bordered="false"
|
|
||||||
/>
|
|
||||||
</n-card>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1">
|
|
||||||
<n-card>
|
|
||||||
<template #header>
|
|
||||||
<NButton type="primary" :disabled="!lastDictCode" @click="dictContentRef!.openModal('add')">
|
|
||||||
<template #icon>
|
|
||||||
<icon-park-outline-add-one />
|
|
||||||
</template>
|
|
||||||
新建
|
|
||||||
</NButton>
|
|
||||||
</template>
|
|
||||||
<template #header-extra>
|
|
||||||
<NFlex>
|
|
||||||
<NButton type="primary" :disabled="!lastDictCode" secondary @click="getDictContent(lastDictCode)">
|
|
||||||
<template #icon>
|
|
||||||
<icon-park-outline-refresh />
|
|
||||||
</template>
|
|
||||||
刷新
|
|
||||||
</NButton>
|
|
||||||
</NFlex>
|
|
||||||
</template>
|
|
||||||
<n-data-table
|
|
||||||
:columns="contentColumns" :data="dictContentData" :loading="contentLoading" :pagination="false"
|
|
||||||
:bordered="false"
|
|
||||||
/>
|
|
||||||
</n-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DictModal ref="dictRef" modal-name="字典项" is-root @success="getDictList" />
|
<!-- 右侧字典数据表格 -->
|
||||||
<DictModal ref="dictContentRef" modal-name="字典值" :dict-code="lastDictCode" @success="() => getDictContent(lastDictCode)" />
|
<n-space class="flex-1" vertical>
|
||||||
</NFlex>
|
<n-card>
|
||||||
|
<pro-search-form
|
||||||
|
:form="dictDataSearchForm"
|
||||||
|
:columns="dictDataSearchColumns"
|
||||||
|
v-bind="dictDataSearchProps"
|
||||||
|
:collapse-button-props="false"
|
||||||
|
/>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<pro-data-table
|
||||||
|
:columns="dictDataColumns"
|
||||||
|
v-bind="dictDataTableProps"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<n-button
|
||||||
|
v-if="values.dictType"
|
||||||
|
type="primary"
|
||||||
|
@click="dictDataModalRef?.openModal('add', { dictType: values.dictType })"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-park-outline-plus />
|
||||||
|
</template>
|
||||||
|
新增字典数据
|
||||||
|
</n-button>
|
||||||
|
</template>
|
||||||
|
</pro-data-table>
|
||||||
|
</n-space>
|
||||||
|
<!-- 字典数据弹窗 -->
|
||||||
|
<DictDataModal
|
||||||
|
ref="dictDataModalRef"
|
||||||
|
@success="refreshDictData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
@ -88,7 +88,7 @@ export function createMenuColumns(actions: MenuColumnActions): DataTableColumns<
|
|||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => onAdd(row)}
|
onClick={() => onAdd(row)}
|
||||||
>
|
>
|
||||||
新建
|
新增
|
||||||
</NButton>
|
</NButton>
|
||||||
)}
|
)}
|
||||||
<NButton
|
<NButton
|
||||||
|
@ -71,7 +71,7 @@ async function openModal(type: ModalType = 'add', data?: Partial<Entity.Menu>) {
|
|||||||
modalForm.open()
|
modalForm.open()
|
||||||
const handlers = {
|
const handlers = {
|
||||||
async add() {
|
async add() {
|
||||||
// 如果新建传入了menuId,设置为父级菜单
|
// 如果新增传入了menuId,设置为父级菜单
|
||||||
if (data?.id) {
|
if (data?.id) {
|
||||||
modalForm.values.value.parentId = data.id
|
modalForm.values.value.parentId = data.id
|
||||||
modalForm.values.value.path = `${data.path}/`
|
modalForm.values.value.path = `${data.path}/`
|
||||||
@ -93,27 +93,34 @@ async function submitModal(filedValues: Partial<Entity.Menu>) {
|
|||||||
try {
|
try {
|
||||||
await createMenu(filedValues)
|
await createMenu(filedValues)
|
||||||
window.$message.success('菜单创建成功')
|
window.$message.success('菜单创建成功')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('创建菜单失败', error)
|
console.error('创建菜单失败', error)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async edit() {
|
async edit() {
|
||||||
try {
|
try {
|
||||||
await updateMenu(modalForm.values.value.id!, filedValues)
|
await updateMenu(modalForm.values.value.id!, filedValues)
|
||||||
window.$message.success('菜单更新成功')
|
window.$message.success('菜单更新成功')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('更新菜单失败', error)
|
console.error('更新菜单失败', error)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
startLoading()
|
startLoading()
|
||||||
await handlers[modalType.value]()
|
const success = await handlers[modalType.value]()
|
||||||
emit('success')
|
|
||||||
modalForm.close()
|
|
||||||
endLoading()
|
endLoading()
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
emit('success')
|
||||||
|
modalForm.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
@ -127,6 +134,7 @@ defineExpose({
|
|||||||
:form="modalForm"
|
:form="modalForm"
|
||||||
:loading="submitLoading"
|
:loading="submitLoading"
|
||||||
width="700px"
|
width="700px"
|
||||||
|
label-width="120px"
|
||||||
>
|
>
|
||||||
<pro-field
|
<pro-field
|
||||||
path="menuType"
|
path="menuType"
|
||||||
|
@ -79,9 +79,9 @@ onMounted(() => {
|
|||||||
<template #title>
|
<template #title>
|
||||||
<n-button type="primary" @click="menuModalRef.openModal('add')">
|
<n-button type="primary" @click="menuModalRef.openModal('add')">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-park-outline-add-one />
|
<icon-park-outline-plus />
|
||||||
</template>
|
</template>
|
||||||
新建
|
新增
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ export const searchColumns: ProSearchFormColumns<Entity.Role> = [
|
|||||||
path: 'roleName',
|
path: 'roleName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '角色权限',
|
title: '权限标识',
|
||||||
path: 'roleKey',
|
path: 'roleKey',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
path: 'roleStatus',
|
path: 'status',
|
||||||
field: 'select',
|
field: 'select',
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
options: [
|
options: [
|
||||||
@ -35,24 +35,18 @@ export const searchColumns: ProSearchFormColumns<Entity.Role> = [
|
|||||||
interface RoleColumnActions {
|
interface RoleColumnActions {
|
||||||
onEdit: (row: Entity.Role) => void
|
onEdit: (row: Entity.Role) => void
|
||||||
onDelete: (id: number) => void
|
onDelete: (id: number) => void
|
||||||
onStatusChange: (value: 0 | 1, id: number) => void
|
onStatusChange: (id: number, value: 0 | 1) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createRoleColumns(actions: RoleColumnActions): DataTableColumns<Entity.Role> {
|
export function createRoleColumns(actions: RoleColumnActions): DataTableColumns<Entity.Role> {
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
title: '角色ID',
|
|
||||||
align: 'center',
|
|
||||||
key: 'roleId',
|
|
||||||
width: 80,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '角色名称',
|
title: '角色名称',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
key: 'roleName',
|
key: 'roleName',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '角色权限',
|
title: '权限标识',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
key: 'roleKey',
|
key: 'roleKey',
|
||||||
render: row => renderProCopyableText(row.roleKey),
|
render: row => renderProCopyableText(row.roleKey),
|
||||||
@ -63,25 +57,18 @@ export function createRoleColumns(actions: RoleColumnActions): DataTableColumns<
|
|||||||
key: 'remark',
|
key: 'remark',
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '排序',
|
|
||||||
align: 'center',
|
|
||||||
key: 'sort',
|
|
||||||
width: 80,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
key: 'roleStatus',
|
key: 'status',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
return (
|
return (
|
||||||
<NSwitch
|
<NSwitch
|
||||||
value={row.roleStatus || 1}
|
value={row.status}
|
||||||
checked-value={1}
|
checked-value={0}
|
||||||
unchecked-value={0}
|
unchecked-value={1}
|
||||||
onUpdateValue={(value: 0 | 1) =>
|
onUpdateValue={(value: 0 | 1) => actions.onStatusChange(row.id, value)}
|
||||||
actions.onStatusChange(value, row.roleId)}
|
|
||||||
>
|
>
|
||||||
{{ checked: () => '启用', unchecked: () => '禁用' }}
|
{{ checked: () => '启用', unchecked: () => '禁用' }}
|
||||||
</NSwitch>
|
</NSwitch>
|
||||||
@ -106,19 +93,19 @@ export function createRoleColumns(actions: RoleColumnActions): DataTableColumns<
|
|||||||
return (
|
return (
|
||||||
<NSpace justify="center">
|
<NSpace justify="center">
|
||||||
<NButton
|
<NButton
|
||||||
size="small"
|
text
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => actions.onEdit(row)}
|
onClick={() => actions.onEdit(row)}
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</NButton>
|
</NButton>
|
||||||
<NPopconfirm
|
<NPopconfirm
|
||||||
onPositiveClick={() => actions.onDelete(row.roleId)}
|
onPositiveClick={() => actions.onDelete(row.id)}
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
default: () => '确认删除该角色?',
|
default: () => '确认删除该角色?',
|
||||||
trigger: () => (
|
trigger: () => (
|
||||||
<NButton size="small" type="error">
|
<NButton text type="error">
|
||||||
删除
|
删除
|
||||||
</NButton>
|
</NButton>
|
||||||
),
|
),
|
||||||
|
@ -20,11 +20,7 @@ const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = use
|
|||||||
const modalForm = createProModalForm<Partial<Entity.Role>>({
|
const modalForm = createProModalForm<Partial<Entity.Role>>({
|
||||||
omitEmptyString: false,
|
omitEmptyString: false,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
roleName: '',
|
status: 0,
|
||||||
roleKey: '',
|
|
||||||
remark: '',
|
|
||||||
sort: 0,
|
|
||||||
roleStatus: 1,
|
|
||||||
},
|
},
|
||||||
onSubmit: submitModal,
|
onSubmit: submitModal,
|
||||||
})
|
})
|
||||||
@ -51,7 +47,7 @@ async function openModal(type: ModalType = 'add', data?: Partial<Entity.Role>) {
|
|||||||
if (!data)
|
if (!data)
|
||||||
return
|
return
|
||||||
|
|
||||||
const { data: role } = await getRoleById(data.roleId!)
|
const { data: role } = await getRoleById(data.id!)
|
||||||
modalForm.values.value = role
|
modalForm.values.value = role
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -64,28 +60,33 @@ async function submitModal(filedValues: Partial<Entity.Role>) {
|
|||||||
try {
|
try {
|
||||||
await createRole(filedValues)
|
await createRole(filedValues)
|
||||||
window.$message.success('角色创建成功')
|
window.$message.success('角色创建成功')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('创建角色失败', error)
|
console.error('创建角色失败', error)
|
||||||
window.$message.error('创建角色失败')
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async edit() {
|
async edit() {
|
||||||
try {
|
try {
|
||||||
await updateRole(modalForm.values.value.roleId!, filedValues)
|
await updateRole(modalForm.values.value.id!, filedValues)
|
||||||
window.$message.success('角色更新成功')
|
window.$message.success('角色更新成功')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('更新角色失败', error)
|
console.error('更新角色失败', error)
|
||||||
window.$message.error('更新角色失败')
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
startLoading()
|
startLoading()
|
||||||
await handlers[modalType.value]()
|
const success = await handlers[modalType.value]()
|
||||||
endLoading()
|
endLoading()
|
||||||
emit('success')
|
|
||||||
modalForm.close()
|
if (success) {
|
||||||
|
emit('success')
|
||||||
|
modalForm.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
@ -105,29 +106,21 @@ defineExpose({
|
|||||||
required
|
required
|
||||||
title="角色名称"
|
title="角色名称"
|
||||||
path="roleName"
|
path="roleName"
|
||||||
placeholder="请输入角色名称"
|
|
||||||
/>
|
/>
|
||||||
<pro-input
|
<pro-input
|
||||||
required
|
required
|
||||||
title="角色权限"
|
title="权限标识"
|
||||||
path="roleKey"
|
path="roleKey"
|
||||||
placeholder="请输入角色权限"
|
/>
|
||||||
|
<pro-switch
|
||||||
|
title="状态"
|
||||||
|
path="status"
|
||||||
|
:field-props="{ checkedValue: 0, uncheckedValue: 1 }"
|
||||||
/>
|
/>
|
||||||
<pro-textarea
|
<pro-textarea
|
||||||
class="col-span-2"
|
class="col-span-2"
|
||||||
title="备注"
|
title="备注"
|
||||||
path="remark"
|
path="remark"
|
||||||
placeholder="请输入备注信息"
|
|
||||||
/>
|
|
||||||
<pro-digit
|
|
||||||
title="排序"
|
|
||||||
path="sort"
|
|
||||||
:field-props="{ min: 0, max: 999 }"
|
|
||||||
/>
|
|
||||||
<pro-switch
|
|
||||||
title="状态"
|
|
||||||
path="roleStatus"
|
|
||||||
:field-props="{ checkedValue: 1, uncheckedValue: 0 }"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</pro-modal-form>
|
</pro-modal-form>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
||||||
import { deleteRole, getRoleList } from '@/api'
|
import { deleteRole, getRoleList, updateRole } from '@/api'
|
||||||
import { createRoleColumns, searchColumns } from './columns'
|
import { createRoleColumns, searchColumns } from './columns'
|
||||||
import RoleModal from './components/RoleModal.vue'
|
import RoleModal from './components/RoleModal.vue'
|
||||||
|
|
||||||
@ -34,10 +34,21 @@ async function deleteRoleData(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateRoleStatus(id: number, value: 0 | 1) {
|
||||||
|
try {
|
||||||
|
await updateRole(id, { status: value })
|
||||||
|
window.$message.success('角色状态更新成功')
|
||||||
|
refresh() // 重新加载列表
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
window.$message.error('角色状态更新失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tablecolumns = createRoleColumns({
|
const tablecolumns = createRoleColumns({
|
||||||
onEdit: (row: Entity.Role) => modalRef.value?.openModal('edit', row),
|
onEdit: (row: Entity.Role) => modalRef.value?.openModal('edit', row),
|
||||||
onDelete: deleteRoleData,
|
onDelete: deleteRoleData,
|
||||||
onStatusChange: () => {},
|
onStatusChange: updateRoleStatus,
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getRolePage({ current, pageSize }: any, formData: Entity.Role[]) {
|
async function getRolePage({ current, pageSize }: any, formData: Entity.Role[]) {
|
||||||
@ -47,10 +58,7 @@ async function getRolePage({ current, pageSize }: any, formData: Entity.Role[])
|
|||||||
pageNum: current,
|
pageNum: current,
|
||||||
pageSize,
|
pageSize,
|
||||||
})
|
})
|
||||||
return {
|
return data
|
||||||
list: data.list,
|
|
||||||
total: data.total,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
return {
|
return {
|
||||||
@ -81,7 +89,7 @@ async function getRolePage({ current, pageSize }: any, formData: Entity.Role[])
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-park-outline-plus />
|
<icon-park-outline-plus />
|
||||||
</template>
|
</template>
|
||||||
新建角色
|
新增角色
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
</pro-data-table>
|
</pro-data-table>
|
||||||
|
@ -14,17 +14,17 @@ export const searchColumns: ProSearchFormColumns<Entity.User> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
path: 'userStatus',
|
path: 'status',
|
||||||
field: 'select',
|
field: 'select',
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '启用',
|
label: '启用',
|
||||||
value: 1,
|
value: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '禁用',
|
label: '禁用',
|
||||||
value: 0,
|
value: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -36,7 +36,7 @@ export const searchColumns: ProSearchFormColumns<Entity.User> = [
|
|||||||
interface UserColumnActions {
|
interface UserColumnActions {
|
||||||
onEdit: (row: Entity.User) => void
|
onEdit: (row: Entity.User) => void
|
||||||
onDelete: (id: number) => void
|
onDelete: (id: number) => void
|
||||||
onStatusChange: (value: 0 | 1, id: number) => void
|
onStatusChange: (id: number, value: 0 | 1) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createUserColumns(actions: UserColumnActions): DataTableColumns<Entity.User> {
|
export function createUserColumns(actions: UserColumnActions): DataTableColumns<Entity.User> {
|
||||||
@ -83,16 +83,15 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
|
|||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
key: 'userStatus',
|
key: 'status',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (row) => {
|
render: (row) => {
|
||||||
return (
|
return (
|
||||||
<NSwitch
|
<NSwitch
|
||||||
value={row.userStatus}
|
value={row.status}
|
||||||
checked-value={0}
|
checked-value={0}
|
||||||
unchecked-value={1}
|
unchecked-value={1}
|
||||||
onUpdateValue={(value: 0 | 1) =>
|
onUpdateValue={(value: 0 | 1) => actions.onStatusChange(row.id, value)}
|
||||||
actions.onStatusChange(value, row.userId)}
|
|
||||||
>
|
>
|
||||||
{{ checked: () => '启用', unchecked: () => '禁用' }}
|
{{ checked: () => '启用', unchecked: () => '禁用' }}
|
||||||
</NSwitch>
|
</NSwitch>
|
||||||
@ -122,7 +121,7 @@ export function createUserColumns(actions: UserColumnActions): DataTableColumns<
|
|||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</NButton>
|
</NButton>
|
||||||
<NPopconfirm onPositiveClick={() => actions.onDelete(row.userId)}>
|
<NPopconfirm onPositiveClick={() => actions.onDelete(row.id)}>
|
||||||
{{
|
{{
|
||||||
default: () => '确认删除',
|
default: () => '确认删除',
|
||||||
trigger: () => <NButton text type="error">删除</NButton>,
|
trigger: () => <NButton text type="error">删除</NButton>,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { useBoolean } from '@/hooks'
|
import { useBoolean } from '@/hooks'
|
||||||
import { createUser, getRoleOptions, getUserById, updateUser } from '@/api'
|
import { createUser, getRoleOptions, getUserById, updateUser } from '@/api'
|
||||||
import { createProModalForm } from 'pro-naive-ui'
|
import { createProModalForm } from 'pro-naive-ui'
|
||||||
|
import { Regex } from '@/constants'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modalName?: string
|
modalName?: string
|
||||||
@ -17,12 +18,11 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
||||||
|
|
||||||
const modalForm = createProModalForm<Partial<Entity.User>>({
|
const modalForm = createProModalForm<Partial<Entity.User> & { roleIds?: number[] }>({
|
||||||
omitEmptyString: false,
|
omitEmptyString: false,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
gender: 'unknown',
|
gender: 'unknown',
|
||||||
userStatus: 1,
|
status: 0,
|
||||||
roles: [],
|
|
||||||
},
|
},
|
||||||
onSubmit: submitModal,
|
onSubmit: submitModal,
|
||||||
})
|
})
|
||||||
@ -51,8 +51,9 @@ async function openModal(type: ModalType = 'add', data?: Partial<Entity.User>) {
|
|||||||
if (!data)
|
if (!data)
|
||||||
return
|
return
|
||||||
|
|
||||||
const { data: user } = await getUserById(data.userId!)
|
const { data: user } = await getUserById(data.id!)
|
||||||
modalForm.values.value = user
|
modalForm.values.value = user
|
||||||
|
modalForm.values.value.roleIds = user.roles.map(role => role.id)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
await handlers[type]()
|
await handlers[type]()
|
||||||
@ -64,26 +65,33 @@ async function submitModal(filedValues: Partial<Entity.User>) {
|
|||||||
try {
|
try {
|
||||||
await createUser(filedValues)
|
await createUser(filedValues)
|
||||||
window.$message.success('用户创建成功')
|
window.$message.success('用户创建成功')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('创建用户失败', error)
|
console.error('创建用户失败', error)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async edit() {
|
async edit() {
|
||||||
try {
|
try {
|
||||||
await updateUser(modalForm.values.value.userId!, filedValues)
|
await updateUser(modalForm.values.value.id!, filedValues)
|
||||||
window.$message.success('用户更新成功')
|
window.$message.success('用户更新成功')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('更新用户失败', error)
|
console.error('更新用户失败', error)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
startLoading()
|
startLoading()
|
||||||
await handlers[modalType.value]()
|
const success = await handlers[modalType.value]()
|
||||||
endLoading()
|
endLoading()
|
||||||
emit('success')
|
|
||||||
modalForm.close()
|
if (success) {
|
||||||
|
emit('success')
|
||||||
|
modalForm.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openModal,
|
openModal,
|
||||||
@ -104,6 +112,12 @@ defineExpose({
|
|||||||
path="username"
|
path="username"
|
||||||
:readonly="modalType === 'edit'"
|
:readonly="modalType === 'edit'"
|
||||||
/>
|
/>
|
||||||
|
<pro-input
|
||||||
|
v-if="modalType === 'add'"
|
||||||
|
required
|
||||||
|
title="密码"
|
||||||
|
path="password"
|
||||||
|
/>
|
||||||
<pro-input
|
<pro-input
|
||||||
title="昵称"
|
title="昵称"
|
||||||
path="nickName"
|
path="nickName"
|
||||||
@ -112,7 +126,6 @@ defineExpose({
|
|||||||
title="性别"
|
title="性别"
|
||||||
path="gender"
|
path="gender"
|
||||||
:field-props="{
|
:field-props="{
|
||||||
type: 'button',
|
|
||||||
options: [
|
options: [
|
||||||
{ label: '男', value: 'male' },
|
{ label: '男', value: 'male' },
|
||||||
{ label: '女', value: 'female' },
|
{ label: '女', value: 'female' },
|
||||||
@ -127,28 +140,41 @@ defineExpose({
|
|||||||
title="邮箱"
|
title="邮箱"
|
||||||
path="email"
|
path="email"
|
||||||
placeholder="example@domain.com"
|
placeholder="example@domain.com"
|
||||||
|
:rule="[
|
||||||
|
{
|
||||||
|
pattern: new RegExp(Regex.Email),
|
||||||
|
message: '请输入正确的邮箱格式',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
/>
|
/>
|
||||||
<pro-input
|
<pro-input
|
||||||
title="手机号"
|
title="手机号"
|
||||||
path="phone"
|
path="phone"
|
||||||
placeholder="11位手机号"
|
placeholder="11位手机号"
|
||||||
|
:rule="[
|
||||||
|
{
|
||||||
|
pattern: new RegExp(Regex.Phone),
|
||||||
|
message: '请输入正确的手机号格式',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<pro-select
|
||||||
|
title="角色"
|
||||||
|
path="roleIds"
|
||||||
|
:field-props="{ multiple: true, options: roleOptions }"
|
||||||
|
/>
|
||||||
|
<pro-switch
|
||||||
|
title="用户状态"
|
||||||
|
path="status"
|
||||||
|
:field-props="{ checkedValue: 0, uncheckedValue: 1 }"
|
||||||
/>
|
/>
|
||||||
<pro-textarea
|
<pro-textarea
|
||||||
class="col-span-2"
|
class="col-span-2"
|
||||||
title="备注"
|
title="备注"
|
||||||
path="remark"
|
path="remark"
|
||||||
/>
|
/>
|
||||||
<pro-switch
|
|
||||||
title="用户状态"
|
|
||||||
path="userStatus"
|
|
||||||
:field-props="{ checkedValue: 1, uncheckedValue: 0 }"
|
|
||||||
/>
|
|
||||||
<pro-select
|
|
||||||
class="col-span-2"
|
|
||||||
title="角色"
|
|
||||||
path="roles"
|
|
||||||
:field-props="{ multiple: true, options: roleOptions }"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</pro-modal-form>
|
</pro-modal-form>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
||||||
import { deleteUser, getUserList } from '@/api'
|
import { deleteUser, getUserList, updateUser } from '@/api'
|
||||||
import { createUserColumns, searchColumns } from './columns'
|
import { createUserColumns, searchColumns } from './columns'
|
||||||
import UserModal from './components/UserModal.vue'
|
import UserModal from './components/UserModal.vue'
|
||||||
|
|
||||||
@ -33,10 +33,21 @@ async function delteteUser(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateUserStatus(id: number, value: 0 | 1) {
|
||||||
|
try {
|
||||||
|
await updateUser(id, { status: value })
|
||||||
|
window.$message.success('用户状态更新成功')
|
||||||
|
refresh() // 重新加载列表
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
window.$message.error('角色状态更新失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tablecolumns = createUserColumns({
|
const tablecolumns = createUserColumns({
|
||||||
onEdit: row => modalRef.value?.openModal('edit', row),
|
onEdit: row => modalRef.value?.openModal('edit', row),
|
||||||
onDelete: delteteUser,
|
onDelete: delteteUser,
|
||||||
onStatusChange: () => {},
|
onStatusChange: updateUserStatus,
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getUserPage({ current, pageSize }: any, formData: Entity.User[]) {
|
async function getUserPage({ current, pageSize }: any, formData: Entity.User[]) {
|
||||||
@ -46,10 +57,7 @@ async function getUserPage({ current, pageSize }: any, formData: Entity.User[])
|
|||||||
pageNum: current,
|
pageNum: current,
|
||||||
pageSize,
|
pageSize,
|
||||||
})
|
})
|
||||||
return {
|
return data
|
||||||
list: data.list,
|
|
||||||
total: data.total,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
return {
|
return {
|
||||||
@ -116,7 +124,7 @@ const treeData = ref([
|
|||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-park-outline-plus />
|
<icon-park-outline-plus />
|
||||||
</template>
|
</template>
|
||||||
新建用户
|
新增用户
|
||||||
</n-button>
|
</n-button>
|
||||||
</template>
|
</template>
|
||||||
</pro-data-table>
|
</pro-data-table>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user