mirror of
https://github.com/chansee97/nova-admin.git
synced 2026-07-07 18:11:09 +08:00
Compare commits
4 Commits
1738bed2ca
...
cf41abc5a5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf41abc5a5 | ||
|
|
4b7946867e | ||
|
|
41d4e9a0d5 | ||
|
|
c168e92135 |
@ -1,31 +0,0 @@
|
||||
import { request } from '../utils/alova'
|
||||
|
||||
/* 示例列表接口 */
|
||||
export function fetchUserList() {
|
||||
return request.Get('/userList')
|
||||
}
|
||||
|
||||
// 获取所有路由信息
|
||||
export function fetchAllRoutes() {
|
||||
return request.Get<Api.Response<AppRoute.RowRoute[]>>('/getUserRoutes')
|
||||
}
|
||||
|
||||
// 获取所有用户信息
|
||||
export function fetchUserPage() {
|
||||
return request.Get<Api.Response<Entity.User[]>>('/userPage')
|
||||
}
|
||||
// 获取所有角色列表
|
||||
export function fetchRoleList() {
|
||||
return request.Get<Api.Response<Entity.Role[]>>('/role/list')
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求获取字典列表
|
||||
*
|
||||
* @param code - 字典编码,用于筛选特定的字典列表
|
||||
* @returns 返回的字典列表数据
|
||||
*/
|
||||
export function fetchDictList(code?: string) {
|
||||
const params = { code }
|
||||
return request.Get<Api.Response<Entity.DictType[]>>('/dict/list', { params })
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
export * from './login'
|
||||
export * from './demo'
|
||||
export * from './test'
|
||||
export * from './system'
|
||||
|
||||
@ -39,7 +39,7 @@ export function getDeptById(id: number) {
|
||||
* PUT /dept/{id}
|
||||
*/
|
||||
export function updateDept(id: number, data: Partial<Entity.Dept>) {
|
||||
return request.Put<Api.Response<Entity.Dept>>(`/dept/${id}`, data)
|
||||
return request.Patch<Api.Response<Entity.Dept>>(`/dept/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -51,7 +51,7 @@ export function getDictTypeById(id: number) {
|
||||
* PUT /dict/types/{id}
|
||||
*/
|
||||
export function updateDictType(id: number, data: Partial<Entity.DictType>) {
|
||||
return request.Put<Api.Response<Entity.DictType>>(`/dict/types/${id}`, data)
|
||||
return request.Patch<Api.Response<Entity.DictType>>(`/dict/types/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,7 +95,7 @@ export function getDictDataById(id: number) {
|
||||
* PUT /dict/data/{id}
|
||||
*/
|
||||
export function updateDictData(id: number, data: Partial<Entity.DictData>) {
|
||||
return request.Put<Api.Response<Entity.DictData>>(`/dict/data/${id}`, data)
|
||||
return request.Patch<Api.Response<Entity.DictData>>(`/dict/data/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -30,7 +30,7 @@ export function getMenuById(id: number) {
|
||||
* PUT /menu/{id}
|
||||
*/
|
||||
export function updateMenu(id: number, data: Partial<Entity.Menu>) {
|
||||
return request.Put<Api.Response<Entity.Menu>>(`/menu/${id}`, data)
|
||||
return request.Patch<Api.Response<Entity.Menu>>(`/menu/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -46,7 +46,7 @@ export function getRoleById(id: number) {
|
||||
* PUT /role/{id}
|
||||
*/
|
||||
export function updateRole(id: number, data: Partial<Entity.Role>) {
|
||||
return request.Put<Api.Response<Entity.Role>>(`/role/${id}`, data)
|
||||
return request.Patch<Api.Response<Entity.Role>>(`/role/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -38,7 +38,7 @@ export function getUserById(id: number) {
|
||||
* PUT /user/{id}
|
||||
*/
|
||||
export function updateUser(id: number, data: Partial<Entity.User>) {
|
||||
return request.Put<Api.Response<Entity.User>>(`/user/${id}`, data)
|
||||
return request.Patch<Api.Response<Entity.User>>(`/user/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,3 +48,14 @@ export function updateUser(id: number, data: Partial<Entity.User>) {
|
||||
export function deleteUser(id: number) {
|
||||
return request.Delete<Api.Response<boolean>>(`/user/${id}`)
|
||||
}
|
||||
/**
|
||||
* 更新用户密码
|
||||
* Patch /user/password
|
||||
*/
|
||||
export interface UserPasswordParams {
|
||||
oldPassword: string
|
||||
newPassword: string
|
||||
}
|
||||
export function updateUserPassword(data: UserPasswordParams) {
|
||||
return request.Patch<Api.Response<boolean>>(`/user/password`, data)
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ export function fetchDelete() {
|
||||
}
|
||||
/* put方法测试 */
|
||||
export function fetchPut(data: any) {
|
||||
return request.Put('/putAPI', data)
|
||||
return request.Patch('/putAPI', data)
|
||||
}
|
||||
/* 不携带token的接口 */
|
||||
export function withoutToken() {
|
||||
|
||||
@ -114,7 +114,17 @@ useDraggable(el, tabs, {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-scrollbar ref="scrollbar" class="relative flex h-full tab-bar-scroller-wrapper" content-class="h-full pr-34 tab-bar-scroller-content" :x-scrollable="true" @wheel="onWheel">
|
||||
<n-scrollbar
|
||||
ref="scrollbar"
|
||||
class="relative flex h-full tab-bar-scroller-wrapper"
|
||||
content-class="h-full pr-34 tab-bar-scroller-content"
|
||||
x-scrollable
|
||||
:theme-overrides="{
|
||||
color: 'transrparent',
|
||||
colorHover: 'transrparent',
|
||||
}"
|
||||
@wheel="onWheel"
|
||||
>
|
||||
<div class="p-l-2 flex wh-full relative">
|
||||
<div class="flex items-end">
|
||||
<TabBarItem
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
// 通知设置
|
||||
const settings = ref({
|
||||
systemUpdateEmail: true,
|
||||
securityAlertEmail: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- 邮件通知 -->
|
||||
<n-card title="邮件通知" size="small">
|
||||
<n-list>
|
||||
<n-list-item>
|
||||
<n-thing>
|
||||
<template #header>
|
||||
系统更新通知
|
||||
</template>
|
||||
<template #description>
|
||||
系统有重要更新时发送邮件通知
|
||||
</template>
|
||||
</n-thing>
|
||||
<template #suffix>
|
||||
<n-switch v-model:value="settings.systemUpdateEmail" />
|
||||
</template>
|
||||
</n-list-item>
|
||||
<n-list-item>
|
||||
<n-thing>
|
||||
<template #header>
|
||||
安全警报
|
||||
</template>
|
||||
<template #description>
|
||||
检测到安全威胁时立即发送邮件
|
||||
</template>
|
||||
</n-thing>
|
||||
<template #suffix>
|
||||
<n-switch v-model:value="settings.securityAlertEmail" />
|
||||
</template>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
@ -61,7 +61,6 @@ async function updateUserInfo() {
|
||||
:model="formData"
|
||||
label-placement="left"
|
||||
label-width="80px"
|
||||
class="max-w-400px"
|
||||
>
|
||||
<n-grid :cols="1">
|
||||
<n-grid-item>
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
// 偏好设置
|
||||
const preferences = ref({
|
||||
aiAssistant: false,
|
||||
predictiveLoading: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- 实验性功能 -->
|
||||
<n-card title="实验性功能" size="small">
|
||||
<n-alert type="warning" style="margin-bottom: 16px;">
|
||||
以下功能仍在测试阶段,可能会影响系统稳定性
|
||||
</n-alert>
|
||||
|
||||
<n-list>
|
||||
<n-list-item>
|
||||
<n-thing>
|
||||
<template #header>
|
||||
AI 助手
|
||||
</template>
|
||||
<template #description>
|
||||
启用智能助手功能,提供操作建议
|
||||
</template>
|
||||
</n-thing>
|
||||
<template #suffix>
|
||||
<n-switch v-model:value="preferences.aiAssistant" />
|
||||
</template>
|
||||
</n-list-item>
|
||||
<n-list-item>
|
||||
<n-thing>
|
||||
<template #header>
|
||||
预测性加载
|
||||
</template>
|
||||
<template #description>
|
||||
根据使用习惯预加载可能需要的内容
|
||||
</template>
|
||||
</n-thing>
|
||||
<template #suffix>
|
||||
<n-switch v-model:value="preferences.predictiveLoading" />
|
||||
</template>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
162
src/views/build-in/user-center/components/SecuritySettings.vue
Normal file
162
src/views/build-in/user-center/components/SecuritySettings.vue
Normal file
@ -0,0 +1,162 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import type { FormInst, FormRules } from 'naive-ui'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { updateUserPassword } from '@/api/system/user'
|
||||
|
||||
const message = useMessage()
|
||||
const formRef = ref<FormInst | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
// 密码表单
|
||||
const passwordForm = ref({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
})
|
||||
|
||||
// 安全设置
|
||||
const loginNotificationEnabled = ref(true)
|
||||
|
||||
// 表单验证规则
|
||||
const passwordRules: FormRules = {
|
||||
oldPassword: [
|
||||
{ required: true, message: '请输入当前密码', trigger: 'blur' },
|
||||
],
|
||||
newPassword: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
|
||||
],
|
||||
confirmPassword: [
|
||||
{ required: true, message: '请确认新密码', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value) => {
|
||||
return value === passwordForm.value.newPassword
|
||||
},
|
||||
message: '两次输入的密码不一致',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
async function handleUpdatePassword() {
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
loading.value = true
|
||||
|
||||
await updateUserPassword({
|
||||
oldPassword: passwordForm.value.oldPassword,
|
||||
newPassword: passwordForm.value.newPassword,
|
||||
})
|
||||
|
||||
message.success('密码更新成功')
|
||||
resetForm()
|
||||
}
|
||||
finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
passwordForm.value = {
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
}
|
||||
formRef.value?.restoreValidation()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- 密码更新 -->
|
||||
<n-card title="密码更新" size="small">
|
||||
<n-form
|
||||
ref="formRef"
|
||||
:model="passwordForm"
|
||||
:rules="passwordRules"
|
||||
label-placement="left"
|
||||
label-width="120px"
|
||||
require-mark-placement="right-hanging"
|
||||
>
|
||||
<n-form-item label="当前密码" path="oldPassword">
|
||||
<n-input
|
||||
v-model:value="passwordForm.oldPassword"
|
||||
type="password"
|
||||
placeholder="请输入当前密码"
|
||||
show-password-on="click"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="新密码" path="newPassword">
|
||||
<n-input
|
||||
v-model:value="passwordForm.newPassword"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
show-password-on="click"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="确认新密码" path="confirmPassword">
|
||||
<n-input
|
||||
v-model:value="passwordForm.confirmPassword"
|
||||
type="password"
|
||||
placeholder="请再次输入新密码"
|
||||
show-password-on="click"
|
||||
/>
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label=" ">
|
||||
<n-space>
|
||||
<n-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="handleUpdatePassword"
|
||||
>
|
||||
更新密码
|
||||
</n-button>
|
||||
<n-button @click="resetForm">
|
||||
重置
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-card>
|
||||
|
||||
<!-- 账户安全 -->
|
||||
<n-card title="账户安全" size="small">
|
||||
<n-list>
|
||||
<n-list-item>
|
||||
<n-thing>
|
||||
<template #header>
|
||||
登录设备管理
|
||||
</template>
|
||||
<template #description>
|
||||
管理已登录的设备,可以远程注销可疑设备
|
||||
</template>
|
||||
</n-thing>
|
||||
<template #suffix>
|
||||
<n-button size="small" secondary>
|
||||
查看设备
|
||||
</n-button>
|
||||
</template>
|
||||
</n-list-item>
|
||||
<n-list-item>
|
||||
<n-thing>
|
||||
<template #header>
|
||||
登录通知
|
||||
</template>
|
||||
<template #description>
|
||||
新设备登录时发送邮件通知
|
||||
</template>
|
||||
</n-thing>
|
||||
<template #suffix>
|
||||
<n-switch v-model:value="loginNotificationEnabled" />
|
||||
</template>
|
||||
</n-list-item>
|
||||
</n-list>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,13 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { computed, h, onMounted, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useAuthStore } from '@/store'
|
||||
import PersonalInfo from './components/PersonalInfo.vue'
|
||||
import SecuritySettings from './components/SecuritySettings.vue'
|
||||
import NotificationSettings from './components/NotificationSettings.vue'
|
||||
import PreferenceSettings from './components/PreferenceSettings.vue'
|
||||
import IconUser from '~icons/icon-park-outline/user'
|
||||
import IconLock from '~icons/icon-park-outline/lock'
|
||||
import IconRemind from '~icons/icon-park-outline/remind'
|
||||
import IconSetting from '~icons/icon-park-outline/setting-one'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const { userInfo } = storeToRefs(authStore)
|
||||
|
||||
// 当前选中的标签页
|
||||
const activeTab = ref('profile')
|
||||
// 当前选中的菜单项
|
||||
const activeMenu = ref('profile')
|
||||
|
||||
// 页面配置 - 统一管理菜单项、标题和组件
|
||||
const pageConfig = [
|
||||
{
|
||||
key: 'profile',
|
||||
label: '个人信息',
|
||||
title: '个人信息',
|
||||
icon: IconUser,
|
||||
component: PersonalInfo,
|
||||
},
|
||||
{
|
||||
key: 'security',
|
||||
label: '安全设置',
|
||||
title: '安全设置',
|
||||
icon: IconLock,
|
||||
component: SecuritySettings,
|
||||
},
|
||||
{
|
||||
key: 'notification',
|
||||
label: '通知设置',
|
||||
title: '通知设置',
|
||||
icon: IconRemind,
|
||||
component: NotificationSettings,
|
||||
},
|
||||
{
|
||||
key: 'preference',
|
||||
label: '偏好设置',
|
||||
title: '偏好设置',
|
||||
icon: IconSetting,
|
||||
component: PreferenceSettings,
|
||||
},
|
||||
]
|
||||
|
||||
// 菜单选项 - 从配置中提取
|
||||
const menuOptions = pageConfig.map(({ key, label, icon }) => ({
|
||||
key,
|
||||
label,
|
||||
icon: () => h(icon),
|
||||
}))
|
||||
|
||||
// 当前页面配置 - 根据激活的菜单项获取
|
||||
const currentPageConfig = computed(() => {
|
||||
return pageConfig.find(config => config.key === activeMenu.value)
|
||||
})
|
||||
|
||||
// 获取问候语
|
||||
function getGreeting() {
|
||||
@ -34,83 +86,46 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<!-- 欢迎标题 -->
|
||||
<n-card>
|
||||
<n-flex align="center" justify="space-between">
|
||||
<div>
|
||||
<n-h2>
|
||||
<n-card>
|
||||
<n-flex :wrap="false" style="height: 100%;">
|
||||
<!-- 左侧区域 -->
|
||||
<div class="w-[220px] border-r border-[var(--n-border-color)] flex flex-col">
|
||||
<!-- 左上:头像和招呼 -->
|
||||
<n-flex
|
||||
align="center" justify="center" vertical
|
||||
class="p-3 border-b border-[var(--n-border-color)]"
|
||||
:size="8"
|
||||
>
|
||||
<n-avatar
|
||||
round
|
||||
:size="80"
|
||||
:src="userInfo?.avatar || `https://api.dicebear.com/9.x/adventurer-neutral/svg?seed=${userInfo!.username}`"
|
||||
/>
|
||||
<n-h3 class="m-0">
|
||||
{{ getGreeting() }},{{ userInfo?.nickName || userInfo.username }}
|
||||
</n-h2>
|
||||
<n-p class="text-sm opacity-90 m-0">
|
||||
</n-h3>
|
||||
<n-text>
|
||||
欢迎使用个人中心
|
||||
</n-p>
|
||||
</div>
|
||||
<n-avatar
|
||||
round
|
||||
:size="64"
|
||||
:src="userInfo?.avatar || `https://api.dicebear.com/9.x/adventurer-neutral/svg?seed=${userInfo!.username}`"
|
||||
</n-text>
|
||||
</n-flex>
|
||||
|
||||
<!-- 左下:菜单 -->
|
||||
<n-menu
|
||||
v-model:value="activeMenu"
|
||||
class="flex-1"
|
||||
:options="menuOptions"
|
||||
:icon-size="16"
|
||||
/>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<n-card>
|
||||
<n-tabs
|
||||
v-model:value="activeTab"
|
||||
type="line"
|
||||
placement="left"
|
||||
tab-style="min-height: 30px;"
|
||||
pane-style="padding-left: 1rem;"
|
||||
>
|
||||
<n-tab-pane name="profile">
|
||||
<template #tab>
|
||||
<icon-park-outline-user class="mr-2" />
|
||||
个人信息
|
||||
</template>
|
||||
<n-h5>
|
||||
个人信息
|
||||
</n-h5>
|
||||
<n-divider />
|
||||
<PersonalInfo />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="security">
|
||||
<template #tab>
|
||||
<icon-park-outline-lock class="mr-2" />
|
||||
安全设置
|
||||
</template>
|
||||
<n-h5>
|
||||
安全设置
|
||||
</n-h5>
|
||||
<n-divider />
|
||||
<n-empty description="功能开发中..." />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="notification">
|
||||
<template #tab>
|
||||
<icon-park-outline-remind class="mr-2" />
|
||||
通知设置
|
||||
</template>
|
||||
<n-h5>
|
||||
通知设置
|
||||
</n-h5>
|
||||
<n-divider />
|
||||
<n-empty description="功能开发中..." />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane name="preference">
|
||||
<template #tab>
|
||||
<icon-park-outline-setting-one class="mr-2" />
|
||||
偏好设置
|
||||
</template>
|
||||
<n-h5>
|
||||
偏好设置
|
||||
</n-h5>
|
||||
<n-divider />
|
||||
<n-empty description="功能开发中..." />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
</n-card>
|
||||
</n-space>
|
||||
<!-- 右侧功能区域 -->
|
||||
<div class="flex-1 p-2">
|
||||
<n-h4 class="flex items-center gap-2">
|
||||
<component :is="currentPageConfig!.icon" /> {{ currentPageConfig!.title }}
|
||||
</n-h4>
|
||||
<n-divider />
|
||||
<component :is="currentPageConfig!.component" />
|
||||
</div>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
@ -1,109 +1,172 @@
|
||||
<script setup lang="ts">
|
||||
import { fetchDictList } from '@/api'
|
||||
import { getDictTypeList } from '@/api'
|
||||
import { useDict } from '@/hooks/useDict'
|
||||
import { useDictStore } from '@/store'
|
||||
|
||||
const { dict } = useDictStore()
|
||||
const dictStore = useDictStore()
|
||||
|
||||
const dictKey = ref('')
|
||||
const options = ref()
|
||||
const subOptions = ref()
|
||||
const currentDict = ref()
|
||||
const selectedDictType = ref('')
|
||||
const selectedDictValue = ref('')
|
||||
const dictTypeOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const displayData = ref<Entity.DictData[] | Record<string, any>>([])
|
||||
|
||||
async function getAlldict() {
|
||||
const { data } = await fetchDictList()
|
||||
options.value = data.map((item) => {
|
||||
// 使用 useDict hook 获取字典数据
|
||||
const dictUtils = computed(() => {
|
||||
if (!selectedDictType.value) {
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.code,
|
||||
rawData: ref([]),
|
||||
enumMap: ref({}),
|
||||
valueMap: ref({}),
|
||||
labelMap: ref({}),
|
||||
options: ref([])
|
||||
}
|
||||
})
|
||||
}
|
||||
return useDict(selectedDictType.value)
|
||||
})
|
||||
|
||||
// 获取所有字典类型
|
||||
async function getAllDictTypes() {
|
||||
try {
|
||||
const { data } = await getDictTypeList()
|
||||
dictTypeOptions.value = data.list.map((item: Entity.DictType) => ({
|
||||
label: item.name,
|
||||
value: item.type,
|
||||
}))
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取字典类型失败:', error)
|
||||
}
|
||||
}
|
||||
function changeSelect(v: string) {
|
||||
dict(v).then((data) => {
|
||||
currentDict.value = data
|
||||
subOptions.value = data.data()
|
||||
|
||||
// 选择字典类型时的处理
|
||||
function changeSelect(dictType: string) {
|
||||
selectedDictType.value = dictType
|
||||
selectedDictValue.value = ''
|
||||
// 默认显示原始数据
|
||||
nextTick(() => {
|
||||
if (dictUtils.value.rawData.value.length > 0) {
|
||||
displayData.value = dictUtils.value.rawData.value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAlldict()
|
||||
getAllDictTypes()
|
||||
})
|
||||
|
||||
const data = ref()
|
||||
async function getDict() {
|
||||
data.value = currentDict.value.data()
|
||||
// 显示原始字典数据
|
||||
function showRawData() {
|
||||
displayData.value = dictUtils.value.rawData.value
|
||||
}
|
||||
|
||||
async function getEnum() {
|
||||
data.value = currentDict.value.enum()
|
||||
// 显示枚举映射(value -> name)
|
||||
function showEnumMap() {
|
||||
displayData.value = dictUtils.value.enumMap.value
|
||||
}
|
||||
|
||||
async function getValueMap() {
|
||||
data.value = currentDict.value.valueMap()
|
||||
// 显示值映射(value -> dictData)
|
||||
function showValueMap() {
|
||||
displayData.value = dictUtils.value.valueMap.value
|
||||
}
|
||||
|
||||
async function getLabelMap() {
|
||||
data.value = currentDict.value.labelMap()
|
||||
// 显示标签映射(name -> dictData)
|
||||
function showLabelMap() {
|
||||
displayData.value = dictUtils.value.labelMap.value
|
||||
}
|
||||
|
||||
const dictValue = ref()
|
||||
// 显示选项格式
|
||||
function showOptions() {
|
||||
displayData.value = dictUtils.value.options.value
|
||||
}
|
||||
|
||||
// 根据值获取标签(使用 enumMap)
|
||||
const dictLabel = computed(() => {
|
||||
if (data.value && data.value[dictValue.value]) {
|
||||
return data.value[dictValue.value].label
|
||||
}
|
||||
return '--'
|
||||
if (!selectedDictValue.value || !dictUtils.value.enumMap.value) return '--'
|
||||
return dictUtils.value.enumMap.value[selectedDictValue.value] || '--'
|
||||
})
|
||||
|
||||
const enumValue = ref()
|
||||
// 清理缓存
|
||||
function clearCache() {
|
||||
dictStore.cleanDict()
|
||||
window.$message?.success('字典缓存已清理')
|
||||
}
|
||||
|
||||
const enumLabel = computed(() => {
|
||||
if (data.value && data.value[enumValue.value]) {
|
||||
return data.value[enumValue.value]
|
||||
// 移除指定字典缓存
|
||||
function removeCache() {
|
||||
if (selectedDictType.value) {
|
||||
dictStore.removeDict(selectedDictType.value)
|
||||
window.$message?.success(`已移除 ${selectedDictType.value} 的缓存`)
|
||||
}
|
||||
return '--'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card title="字典示例">
|
||||
<n-card title="字典演示">
|
||||
<n-flex vertical>
|
||||
<n-flex>
|
||||
<n-select v-model:value="dictKey" class="w-1/3" :options="options" @update:value="changeSelect" />
|
||||
子字典下拉框
|
||||
<n-select class="w-1/3" :options="subOptions" />
|
||||
<n-flex align="center">
|
||||
<n-select
|
||||
v-model:value="selectedDictType"
|
||||
:options="dictTypeOptions"
|
||||
placeholder="请选择字典类型"
|
||||
@update:value="changeSelect"
|
||||
/>
|
||||
<n-select
|
||||
v-model:value="selectedDictValue"
|
||||
:options="dictUtils.options.value"
|
||||
placeholder="请选择字典项"
|
||||
/>
|
||||
</n-flex>
|
||||
|
||||
<n-flex>
|
||||
<n-button @click="getDict">
|
||||
获取当前字典数据
|
||||
<n-button @click="showRawData">
|
||||
显示原始数据 (rawData)
|
||||
</n-button>
|
||||
<n-button @click="getEnum">
|
||||
获取当前字典枚举
|
||||
<n-button @click="showEnumMap">
|
||||
显示枚举映射 (enumMap)
|
||||
</n-button>
|
||||
<n-button @click="getValueMap">
|
||||
获取当前字典ValueMap
|
||||
<n-button @click="showValueMap">
|
||||
显示值映射 (valueMap)
|
||||
</n-button>
|
||||
<n-button @click="getLabelMap">
|
||||
获取当前字典LabelMap
|
||||
<n-button @click="showLabelMap">
|
||||
显示标签映射 (labelMap)
|
||||
</n-button>
|
||||
<n-button @click="showOptions">
|
||||
显示选项格式 (options)
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
<n-flex>
|
||||
<n-button @click="clearCache" type="warning">
|
||||
清理所有缓存
|
||||
</n-button>
|
||||
<n-button @click="removeCache" type="error" :disabled="!selectedDictType">
|
||||
移除当前字典缓存
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
<pre class="bg-#eee:30">
|
||||
{{ data }}
|
||||
</pre>
|
||||
<pre class="bg-gray-100 p-4 rounded text-sm overflow-auto max-h-96">
|
||||
{{ JSON.stringify(displayData, null, 2) }}</pre>
|
||||
|
||||
<n-flex align="center">
|
||||
<n-input-number v-model:value="dictValue" :min="0" />
|
||||
<n-flex align="center" v-if="selectedDictValue">
|
||||
<n-text>选中值: {{ selectedDictValue }}</n-text>
|
||||
<n-text type="info">
|
||||
Map回显结果 {{ dictLabel }}
|
||||
</n-text>
|
||||
</n-flex>
|
||||
<n-flex align="center">
|
||||
<n-input-number v-model:value="enumValue" :min="0" />
|
||||
<n-text type="info">
|
||||
Enum回显结果 {{ enumLabel }}
|
||||
对应标签: {{ dictLabel }}
|
||||
</n-text>
|
||||
</n-flex>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<n-text depth="3">useDict Hook 使用说明:</n-text>
|
||||
<n-ul>
|
||||
<n-li><n-text code>useDict(dictType)</n-text> - 使用字典类型获取字典工具对象</n-li>
|
||||
<n-li><n-text code>rawData</n-text> - 原始字典数据数组 Entity.DictData[]</n-li>
|
||||
<n-li><n-text code>enumMap</n-text> - 枚举映射 { value: name }</n-li>
|
||||
<n-li><n-text code>valueMap</n-text> - 值映射 { value: dictData }</n-li>
|
||||
<n-li><n-text code>labelMap</n-text> - 标签映射 { name: dictData }</n-li>
|
||||
<n-li><n-text code>options</n-text> - 选项数组 [{ label: name, value }]</n-li>
|
||||
<n-li>字典数据会自动缓存60分钟,支持手动清理缓存</n-li>
|
||||
<n-li>推荐使用 useDict hook 而不是直接调用 API 或 store</n-li>
|
||||
</n-ul>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
@ -21,7 +21,7 @@ const modalForm = createProModalForm<Partial<Entity.DictData>>({
|
||||
omitEmptyString: false,
|
||||
initialValues: {
|
||||
sort: 0,
|
||||
status: 1,
|
||||
status: 0,
|
||||
},
|
||||
onSubmit: submitModal,
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user