Commit f3e1fc4f9b9fb13db52ba8ebd4458ab17356ae67

Authored by xingyu
Committed by 芋道源码
1 parent 35413b52

!238 新增 todo done 页面,优化redis页面

* chore: update deps
* feat: add todo done pages
* chore: update deps
* docs: update
* feat: redis dialog
README.md
@@ -193,7 +193,7 @@ ps:核心功能已经实现,正在对接微信小程序中... @@ -193,7 +193,7 @@ ps:核心功能已经实现,正在对接微信小程序中...
193 |----------------------------------------------------------------------|------------------|--------| 193 |----------------------------------------------------------------------|------------------|--------|
194 | [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.37 | 194 | [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.2.37 |
195 | [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.0.3 | 195 | [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.0.3 |
196 -| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.9 | 196 +| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.11 |
197 | [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.7.4 | 197 | [TypeScript](https://www.typescriptlang.org/docs/) | TypeScript | 4.7.4 |
198 | [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.17 | 198 | [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.17 |
199 | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.1.10 | 199 | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.1.10 |
yudao-ui-admin-vue3/README.md
1 <h1>🌈 yudao-ui-admin-vue3</h1> 1 <h1>🌈 yudao-ui-admin-vue3</h1>
2 - 2 +<p align="center">
  3 + <img src="https://img.shields.io/badge/-Vue3.2-34495e?logo=vue.j" />
  4 + <img src="https://img.shields.io/badge/-Vite3-646cff?logo=vite&logoColor=white" />
  5 + <img src="https://img.shields.io/badge/-TypeScript4.7-blue?logo=typescript&logoColor=white" />
  6 + <img src="https://img.shields.io/badge/-Pinia2-yellow?logo=picpay&logoColor=white" />
  7 + <img src="https://img.shields.io/badge/-ESLint-4b32c3?logo=eslint&logoColor=white" />
  8 + <img src="https://img.shields.io/badge/-pnpm7-F69220?logo=pnpm&logoColor=white" />
  9 + <img src="https://img.shields.io/badge/-Axios-008fc7?logo=axios.js&logoColor=white" />
  10 + <img src="https://img.shields.io/badge/-Prettier-ef9421?logo=Prettier&logoColor=white" alt="Prettier">
  11 + <img src="https://img.shields.io/badge/-Less-1D365D?logo=less&logoColor=white" alt="Less">
  12 + <img src="https://img.shields.io/badge/-Wind%20CSS-06B6D4?logo=Tailwind%20CSS&logoColor=white" alt="Taiwind">
  13 + <img src="" alt="">
  14 +</p>
3 ## 介绍 15 ## 介绍
4 16
5 - 基于 vue3.2+ ,TypeScript ,Element Plus 2.2.0+ ,Vite3 ,Pinia ,Windicss 等开发的后台管理系统 17 - 基于 vue3.2+ ,TypeScript ,Element Plus 2.2.0+ ,Vite3 ,Pinia ,Windicss 等开发的后台管理系统
@@ -12,19 +24,19 @@ @@ -12,19 +24,19 @@
12 24
13 ### 前端依赖 25 ### 前端依赖
14 26
15 -| 框架 | 说明 | 版本 |  
16 -| --- | --- | --- | 27 +| 框架 | 说明 | 版本 |
  28 +| --- | --- |--------|
17 | [Vue](https://staging-cn.vuejs.org/) | vue 框架 | 3.2.37 | 29 | [Vue](https://staging-cn.vuejs.org/) | vue 框架 | 3.2.37 |
18 -| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.0.2 |  
19 -| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.9 |  
20 -| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 4.7.4 |  
21 -| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.16 |  
22 -| [vueuse](https://vueuse.org//) | 常用工具集 | 8.9.4 | 30 +| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 3.0.3 |
  31 +| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.2.11 |
  32 +| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 4.7.4 |
  33 +| [pinia](https://pinia.vuejs.org/) | Vue 存储库 替代 vuex5 | 2.0.17 |
  34 +| [vueuse](https://vueuse.org//) | 常用工具集 | 9.0.0 |
23 | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.1.10 | 35 | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 9.1.10 |
24 -| [vue-router](https://router.vuejs.org/) | vue 路由 | 4.1.2 |  
25 -| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 |  
26 -| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.1 |  
27 -| [wangeditor](https://www.wangeditor.com/) | 富文本编辑器 | 5.1.10 | 36 +| [vue-router](https://router.vuejs.org/) | vue 路由 | 4.1.3 |
  37 +| [windicss](https://cn.windicss.org/) | 下一代工具优先的 CSS 框架 | 3.5.6 |
  38 +| [iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.1 |
  39 +| [wangeditor](https://www.wangeditor.com/) | 富文本编辑器 | 5.1.14 |
28 40
29 ### 推荐 VScode 开发,插件如下 41 ### 推荐 VScode 开发,插件如下
30 42
yudao-ui-admin-vue3/package.json
@@ -56,12 +56,12 @@ @@ -56,12 +56,12 @@
56 "devDependencies": { 56 "devDependencies": {
57 "@commitlint/cli": "^17.0.3", 57 "@commitlint/cli": "^17.0.3",
58 "@commitlint/config-conventional": "^17.0.3", 58 "@commitlint/config-conventional": "^17.0.3",
59 - "@iconify/json": "^2.1.84", 59 + "@iconify/json": "^2.1.85",
60 "@intlify/vite-plugin-vue-i18n": "^5.0.1", 60 "@intlify/vite-plugin-vue-i18n": "^5.0.1",
61 "@purge-icons/generated": "^0.8.1", 61 "@purge-icons/generated": "^0.8.1",
62 "@types/intro.js": "^5.1.0", 62 "@types/intro.js": "^5.1.0",
63 "@types/lodash-es": "^4.17.6", 63 "@types/lodash-es": "^4.17.6",
64 - "@types/node": "^18.6.1", 64 + "@types/node": "^18.6.2",
65 "@types/nprogress": "^0.2.0", 65 "@types/nprogress": "^0.2.0",
66 "@types/qrcode": "^1.4.2", 66 "@types/qrcode": "^1.4.2",
67 "@types/qs": "^6.9.7", 67 "@types/qs": "^6.9.7",
@@ -101,8 +101,7 @@ @@ -101,8 +101,7 @@
101 "vite-plugin-svg-icons": "^2.0.1", 101 "vite-plugin-svg-icons": "^2.0.1",
102 "vite-plugin-windicss": "^1.8.7", 102 "vite-plugin-windicss": "^1.8.7",
103 "vue-tsc": "^0.39.2", 103 "vue-tsc": "^0.39.2",
104 - "windicss": "^3.5.6",  
105 - "windicss-analysis": "^0.3.5" 104 + "windicss": "^3.5.6"
106 }, 105 },
107 "engines": { 106 "engines": {
108 "node": ">= 14.18.0" 107 "node": ">= 14.18.0"
yudao-ui-admin-vue3/src/api/bpm/task/types.ts
@@ -7,3 +7,33 @@ export type FormVO = { @@ -7,3 +7,33 @@ export type FormVO = {
7 remark: string 7 remark: string
8 createTime: string 8 createTime: string
9 } 9 }
  10 +
  11 +export type TaskProcessVO = {
  12 + id: string
  13 + name: string
  14 + startUserId: number
  15 + startUserNickname: string
  16 + processDefinitionId: string
  17 +}
  18 +
  19 +export type TaskTodoVO = {
  20 + id: string
  21 + name: string
  22 + claimTime: string
  23 + createTime: string
  24 + suspensionState: number
  25 + processInstance: TaskProcessVO
  26 +}
  27 +
  28 +export type TaskDoneVO = {
  29 + id: string
  30 + name: string
  31 + claimTime: string
  32 + createTime: string
  33 + endTime: string
  34 + durationInMillis: number
  35 + suspensionState: number
  36 + result: number
  37 + reason: string
  38 + processInstance: TaskProcessVO
  39 +}
yudao-ui-admin-vue3/src/api/infra/redis/index.ts
@@ -24,16 +24,16 @@ export const getKeyListApi = (keyTemplate: string) =&gt; { @@ -24,16 +24,16 @@ export const getKeyListApi = (keyTemplate: string) =&gt; {
24 }) 24 })
25 } 25 }
26 // 获取缓存内容 26 // 获取缓存内容
27 -export const getKeyValue = (key: string) => { 27 +export const getKeyValueApi = (key: string) => {
28 return request.get({ url: '/infra/redis/get-key-value?key=' + key }) 28 return request.get({ url: '/infra/redis/get-key-value?key=' + key })
29 } 29 }
30 30
31 // 根据键名删除缓存 31 // 根据键名删除缓存
32 -export const deleteKey = (key: string) => { 32 +export const deleteKeyApi = (key: string) => {
33 return request.delete({ url: '/infra/redis/delete-key?key=' + key }) 33 return request.delete({ url: '/infra/redis/delete-key?key=' + key })
34 } 34 }
35 35
36 -export const deleteKeys = (keyTemplate: string) => { 36 +export const deleteKeysApi = (keyTemplate: string) => {
37 return request.delete({ 37 return request.delete({
38 url: '/infra/redis/delete-keys?', 38 url: '/infra/redis/delete-keys?',
39 params: { 39 params: {
yudao-ui-admin-vue3/src/views/bpm/task/done/done.data.ts 0 → 100644
  1 +import { reactive } from 'vue'
  2 +import { useI18n } from '@/hooks/web/useI18n'
  3 +import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
  4 +import { DICT_TYPE } from '@/utils/dict'
  5 +const { t } = useI18n() // 国际化
  6 +
  7 +// CrudSchema
  8 +const crudSchemas = reactive<CrudSchema[]>([
  9 + {
  10 + label: t('common.index'),
  11 + field: 'id',
  12 + type: 'index'
  13 + },
  14 + {
  15 + label: '任务名称',
  16 + field: 'name',
  17 + search: {
  18 + show: true
  19 + }
  20 + },
  21 + {
  22 + label: '所属流程',
  23 + field: 'processInstance.name'
  24 + },
  25 + {
  26 + label: '流程发起人',
  27 + field: 'processInstance.startUserNickname'
  28 + },
  29 + {
  30 + label: '结果',
  31 + field: 'result',
  32 + dictType: DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT
  33 + },
  34 + {
  35 + label: '审批意见',
  36 + field: 'reason'
  37 + },
  38 + {
  39 + label: t('common.createTime'),
  40 + field: 'createTime',
  41 + search: {
  42 + show: true,
  43 + component: 'DatePicker',
  44 + componentProps: {
  45 + type: 'datetimerange',
  46 + valueFormat: 'YYYY-MM-DD HH:mm:ss',
  47 + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)],
  48 + shortcuts: [
  49 + {
  50 + text: '近一周',
  51 + value: () => {
  52 + const end = new Date()
  53 + const start = new Date()
  54 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
  55 + return [start, end]
  56 + }
  57 + },
  58 + {
  59 + text: '近一个月',
  60 + value: () => {
  61 + const end = new Date()
  62 + const start = new Date()
  63 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
  64 + return [start, end]
  65 + }
  66 + },
  67 + {
  68 + text: '近三个月',
  69 + value: () => {
  70 + const end = new Date()
  71 + const start = new Date()
  72 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
  73 + return [start, end]
  74 + }
  75 + }
  76 + ]
  77 + }
  78 + }
  79 + },
  80 + {
  81 + label: '审批时间',
  82 + field: 'endTime'
  83 + },
  84 + {
  85 + label: '耗时',
  86 + field: 'durationInMillis'
  87 + },
  88 + {
  89 + label: t('table.action'),
  90 + field: 'action',
  91 + width: '100px'
  92 + }
  93 +])
  94 +export const { allSchemas } = useCrudSchemas(crudSchemas)
yudao-ui-admin-vue3/src/views/bpm/task/done/index.vue
1 -<script setup lang="ts"></script> 1 +<script setup lang="ts">
  2 +import dayjs from 'dayjs'
  3 +import duration from 'dayjs/plugin/duration'
  4 +import { DICT_TYPE } from '@/utils/dict'
  5 +import { useTable } from '@/hooks/web/useTable'
  6 +import { useI18n } from '@/hooks/web/useI18n'
  7 +import type { TaskDoneVO } from '@/api/bpm/task/types'
  8 +import { allSchemas } from './done.data'
  9 +import * as TaskDoneApi from '@/api/bpm/task'
  10 +import { useRouter } from 'vue-router'
  11 +dayjs.extend(duration)
  12 +const { t } = useI18n() // 国际化
  13 +const { push } = useRouter()
  14 +// ========== 列表相关 ==========
  15 +const { register, tableObject, methods } = useTable<TaskDoneVO>({
  16 + getListApi: TaskDoneApi.getDoneTaskPage
  17 +})
  18 +const { getList, setSearchParams } = methods
  19 +
  20 +// 审批操作
  21 +const handleAudit = async (row: TaskDoneVO) => {
  22 + push('/bpm/process-instance/detail?id=' + row.processInstance.id)
  23 +}
  24 +
  25 +// ========== 初始化 ==========
  26 +getList()
  27 +</script>
2 28
3 <template> 29 <template>
4 - <div>index</div> 30 + <!-- 搜索工作区 -->
  31 + <ContentWrap>
  32 + <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  33 + </ContentWrap>
  34 + <ContentWrap>
  35 + <!-- 列表 -->
  36 + <Table
  37 + :columns="allSchemas.tableColumns"
  38 + :selection="false"
  39 + :data="tableObject.tableList"
  40 + :loading="tableObject.loading"
  41 + :pagination="{
  42 + total: tableObject.total
  43 + }"
  44 + v-model:pageSize="tableObject.pageSize"
  45 + v-model:currentPage="tableObject.currentPage"
  46 + @register="register"
  47 + >
  48 + <template #status="{ row }">
  49 + <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
  50 + </template>
  51 + <template #createTime="{ row }">
  52 + <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
  53 + </template>
  54 + <template #endTime="{ row }">
  55 + <span>{{ dayjs(row.endTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
  56 + </template>
  57 + <template #durationInMillis="{ row }">
  58 + <span>{{ dayjs.duration(row.durationInMillis).asMilliseconds() }}</span>
  59 + </template>
  60 + <template #action="{ row }">
  61 + <el-button link type="primary" v-hasPermi="['bpm:task:query']" @click="handleAudit(row)">
  62 + <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
  63 + </el-button>
  64 + </template>
  65 + </Table>
  66 + </ContentWrap>
5 </template> 67 </template>
6 -  
7 -<style scoped></style>  
yudao-ui-admin-vue3/src/views/bpm/task/todo/done.data.ts 0 → 100644
  1 +import { reactive } from 'vue'
  2 +import { useI18n } from '@/hooks/web/useI18n'
  3 +import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
  4 +const { t } = useI18n() // 国际化
  5 +
  6 +// CrudSchema
  7 +const crudSchemas = reactive<CrudSchema[]>([
  8 + {
  9 + label: t('common.index'),
  10 + field: 'id',
  11 + type: 'index'
  12 + },
  13 + {
  14 + label: '任务名称',
  15 + field: 'name',
  16 + search: {
  17 + show: true
  18 + }
  19 + },
  20 + {
  21 + label: '所属流程',
  22 + field: 'processInstance.name'
  23 + },
  24 + {
  25 + label: '流程发起人',
  26 + field: 'processInstance.startUserNickname'
  27 + },
  28 + {
  29 + label: t('common.createTime'),
  30 + field: 'createTime',
  31 + search: {
  32 + show: true,
  33 + component: 'DatePicker',
  34 + componentProps: {
  35 + type: 'datetimerange',
  36 + valueFormat: 'YYYY-MM-DD HH:mm:ss',
  37 + defaultTime: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)],
  38 + shortcuts: [
  39 + {
  40 + text: '近一周',
  41 + value: () => {
  42 + const end = new Date()
  43 + const start = new Date()
  44 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
  45 + return [start, end]
  46 + }
  47 + },
  48 + {
  49 + text: '近一个月',
  50 + value: () => {
  51 + const end = new Date()
  52 + const start = new Date()
  53 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
  54 + return [start, end]
  55 + }
  56 + },
  57 + {
  58 + text: '近三个月',
  59 + value: () => {
  60 + const end = new Date()
  61 + const start = new Date()
  62 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
  63 + return [start, end]
  64 + }
  65 + }
  66 + ]
  67 + }
  68 + }
  69 + },
  70 + {
  71 + label: t('table.action'),
  72 + field: 'action',
  73 + width: '100px'
  74 + }
  75 +])
  76 +export const { allSchemas } = useCrudSchemas(crudSchemas)
yudao-ui-admin-vue3/src/views/bpm/task/todo/index.vue
1 -<script setup lang="ts"></script> 1 +<script setup lang="ts">
  2 +import dayjs from 'dayjs'
  3 +import { DICT_TYPE } from '@/utils/dict'
  4 +import { useTable } from '@/hooks/web/useTable'
  5 +import type { TaskTodoVO } from '@/api/bpm/task/types'
  6 +import { allSchemas } from './done.data'
  7 +import * as TaskTodoApi from '@/api/bpm/task'
  8 +import { useRouter } from 'vue-router'
  9 +const { push } = useRouter()
  10 +// ========== 列表相关 ==========
  11 +const { register, tableObject, methods } = useTable<TaskTodoVO>({
  12 + getListApi: TaskTodoApi.getTodoTaskPage
  13 +})
  14 +const { getList, setSearchParams } = methods
  15 +
  16 +// 审批操作
  17 +const handleAudit = async (row: TaskTodoVO) => {
  18 + push('/bpm/process-instance/detail?id=' + row.processInstance.id)
  19 +}
  20 +
  21 +// ========== 初始化 ==========
  22 +getList()
  23 +</script>
2 24
3 <template> 25 <template>
4 - <div>index</div> 26 + <!-- 搜索工作区 -->
  27 + <ContentWrap>
  28 + <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
  29 + </ContentWrap>
  30 + <ContentWrap>
  31 + <!-- 列表 -->
  32 + <Table
  33 + :columns="allSchemas.tableColumns"
  34 + :selection="false"
  35 + :data="tableObject.tableList"
  36 + :loading="tableObject.loading"
  37 + :pagination="{
  38 + total: tableObject.total
  39 + }"
  40 + v-model:pageSize="tableObject.pageSize"
  41 + v-model:currentPage="tableObject.currentPage"
  42 + @register="register"
  43 + >
  44 + <template #status="{ row }">
  45 + <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
  46 + </template>
  47 + <template #createTime="{ row }">
  48 + <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
  49 + </template>
  50 + <template #action="{ row }">
  51 + <el-button link type="primary" v-hasPermi="['bpm:task:update']" @click="handleAudit(row)">
  52 + <Icon icon="ep:edit" class="mr-1px" /> 审批
  53 + </el-button>
  54 + </template>
  55 + </Table>
  56 + </ContentWrap>
5 </template> 57 </template>
6 -  
7 -<style scoped></style>  
yudao-ui-admin-vue3/src/views/infra/redis/index.vue
@@ -13,7 +13,8 @@ import { @@ -13,7 +13,8 @@ import {
13 ElTableColumn, 13 ElTableColumn,
14 ElScrollbar, 14 ElScrollbar,
15 ElDescriptions, 15 ElDescriptions,
16 - ElDescriptionsItem 16 + ElDescriptionsItem,
  17 + ElMessage
17 } from 'element-plus' 18 } from 'element-plus'
18 const cache = ref<RedisMonitorInfoVO>() 19 const cache = ref<RedisMonitorInfoVO>()
19 const keyListLoad = ref(true) 20 const keyListLoad = ref(true)
@@ -121,7 +122,33 @@ const loadEchartOptions = (stats) =&gt; { @@ -121,7 +122,33 @@ const loadEchartOptions = (stats) =&gt; {
121 ] 122 ]
122 }) 123 })
123 } 124 }
124 - 125 +const dialogVisible = ref(false)
  126 +const keyTemplate = ref('')
  127 +const cacheKeys = ref()
  128 +const cacheForm = ref<{
  129 + key: string
  130 + value: string
  131 +}>({
  132 + key: '',
  133 + value: ''
  134 +})
  135 +const openKeyTemplate = async (row: RedisKeyInfo) => {
  136 + keyTemplate.value = row.keyTemplate
  137 + cacheKeys.value = await RedisApi.getKeyListApi(row.keyTemplate)
  138 + dialogVisible.value = true
  139 +}
  140 +const handleDeleteKey = async (row) => {
  141 + RedisApi.deleteKeyApi(row)
  142 + ElMessage.success('删除成功')
  143 +}
  144 +const handleDeleteKeys = async (row) => {
  145 + RedisApi.deleteKeysApi(row)
  146 + ElMessage.success('删除成功')
  147 +}
  148 +const handleKeyValue = async (row) => {
  149 + const res = await RedisApi.getKeyValueApi(row)
  150 + cacheForm.value = res
  151 +}
125 onBeforeMount(() => { 152 onBeforeMount(() => {
126 readRedisInfo() 153 readRedisInfo()
127 }) 154 })
@@ -186,7 +213,12 @@ onBeforeMount(() =&gt; { @@ -186,7 +213,12 @@ onBeforeMount(() =&gt; {
186 <el-row style="margin-top: 10px"> 213 <el-row style="margin-top: 10px">
187 <el-col :span="24" class="card-box" shadow="hover"> 214 <el-col :span="24" class="card-box" shadow="hover">
188 <el-card> 215 <el-card>
189 - <el-table v-loading="keyListLoad" :data="keyList" row-key="id"> 216 + <el-table
  217 + v-loading="keyListLoad"
  218 + :data="keyList"
  219 + row-key="id"
  220 + @row-click="openKeyTemplate"
  221 + >
190 <el-table-column prop="keyTemplate" label="Key 模板" width="200" /> 222 <el-table-column prop="keyTemplate" label="Key 模板" width="200" />
191 <el-table-column prop="keyType" label="Key 类型" width="100" /> 223 <el-table-column prop="keyType" label="Key 类型" width="100" />
192 <el-table-column prop="valueType" label="Value 类型" /> 224 <el-table-column prop="valueType" label="Value 类型" />
@@ -202,4 +234,58 @@ onBeforeMount(() =&gt; { @@ -202,4 +234,58 @@ onBeforeMount(() =&gt; {
202 </el-col> 234 </el-col>
203 </el-row> 235 </el-row>
204 </el-scrollbar> 236 </el-scrollbar>
  237 + <Dialog v-model="dialogVisible" :title="keyTemplate + ' 模板'" width="60%" maxHeight="800px">
  238 + <el-row>
  239 + <el-col :span="14" style="margin-top: 10px">
  240 + <el-card shadow="always">
  241 + <template #header>
  242 + <div class="card-header">
  243 + <span>键名列表</span>
  244 + </div>
  245 + </template>
  246 + <el-table :data="cacheKeys" style="width: 100%" @row-click="handleKeyValue">
  247 + <el-table-column label="缓存键名" align="center" :show-overflow-tooltip="true">
  248 + <template #default="{ row }">
  249 + {{ row }}
  250 + </template>
  251 + </el-table-column>
  252 + <el-table-column label="操作" align="right" width="60">
  253 + <template #default="{ row }">
  254 + <el-button link type="primary" @click="handleDeleteKey(row)">
  255 + <Icon icon="ep:delete" />
  256 + </el-button>
  257 + </template>
  258 + </el-table-column>
  259 + </el-table>
  260 + </el-card>
  261 + </el-col>
  262 + <el-col :span="10" style="margin-top: 10px">
  263 + <el-card shadow="always">
  264 + <template #header>
  265 + <div class="card-header">
  266 + <span>缓存内容</span>
  267 + <el-button
  268 + link
  269 + type="primary"
  270 + @click="handleDeleteKeys(keyTemplate)"
  271 + style="float: right; padding: 3px 0"
  272 + >
  273 + <Icon icon="ep:refresh" />清理全部
  274 + </el-button>
  275 + </div>
  276 + </template>
  277 + <el-descriptions :column="1">
  278 + <el-descriptions-item label="缓存键名:">{{ cacheForm.key }}</el-descriptions-item>
  279 + <el-descriptions-item label="缓存内容:">{{ cacheForm.value }}</el-descriptions-item>
  280 + </el-descriptions>
  281 + </el-card>
  282 + </el-col>
  283 + </el-row>
  284 + </Dialog>
205 </template> 285 </template>
  286 +<style scoped>
  287 +.redis {
  288 + height: 600px;
  289 + max-height: 860px;
  290 +}
  291 +</style>
yudao-ui-admin-vue3/src/views/system/role/index.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 -import { reactive, ref, unref } from 'vue' 2 +import { onMounted, reactive, ref, unref } from 'vue'
3 import dayjs from 'dayjs' 3 import dayjs from 'dayjs'
4 import { DICT_TYPE, getDictOptions } from '@/utils/dict' 4 import { DICT_TYPE, getDictOptions } from '@/utils/dict'
5 import { useTable } from '@/hooks/web/useTable' 5 import { useTable } from '@/hooks/web/useTable'
@@ -18,7 +18,7 @@ import { @@ -18,7 +18,7 @@ import {
18 ElMessage, 18 ElMessage,
19 ElTree, 19 ElTree,
20 ElCard, 20 ElCard,
21 - ElCheckbox 21 + ElSwitch
22 } from 'element-plus' 22 } from 'element-plus'
23 import { listSimpleMenusApi } from '@/api/system/menu' 23 import { listSimpleMenusApi } from '@/api/system/menu'
24 import { listSimpleDeptApi } from '@/api/system/dept' 24 import { listSimpleDeptApi } from '@/api/system/dept'
@@ -98,7 +98,6 @@ const dataScopeForm = reactive({ @@ -98,7 +98,6 @@ const dataScopeForm = reactive({
98 name: '', 98 name: '',
99 code: '', 99 code: '',
100 dataScope: 0, 100 dataScope: 0,
101 - checkStrictly: true,  
102 checkList: [] 101 checkList: []
103 }) 102 })
104 const defaultProps = { 103 const defaultProps = {
@@ -113,15 +112,18 @@ const dialogScopeTitle = ref(&#39;数据权限&#39;) @@ -113,15 +112,18 @@ const dialogScopeTitle = ref(&#39;数据权限&#39;)
113 const actionScopeType = ref('') 112 const actionScopeType = ref('')
114 const dataScopeDictDatas = ref() 113 const dataScopeDictDatas = ref()
115 // 选项 114 // 选项
  115 +const checkStrictly = ref(true)
116 const treeNodeAll = ref(false) 116 const treeNodeAll = ref(false)
117 // 权限操作 117 // 权限操作
118 const handleScope = async (type: string, row: RoleVO) => { 118 const handleScope = async (type: string, row: RoleVO) => {
  119 + checkStrictly.value = true
  120 + treeNodeAll.value = false
  121 + dataScopeForm.dataScope = 0
119 dataScopeForm.name = row.name 122 dataScopeForm.name = row.name
120 dataScopeForm.code = row.code 123 dataScopeForm.code = row.code
121 if (type === 'menu') { 124 if (type === 'menu') {
122 const menuRes = await listSimpleMenusApi() 125 const menuRes = await listSimpleMenusApi()
123 treeOptions.value = handleTree(menuRes) 126 treeOptions.value = handleTree(menuRes)
124 - dataScopeDictDatas.value = getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)  
125 } else if (type === 'dept') { 127 } else if (type === 'dept') {
126 const deptRes = await listSimpleDeptApi() 128 const deptRes = await listSimpleDeptApi()
127 treeOptions.value = handleTree(deptRes) 129 treeOptions.value = handleTree(deptRes)
@@ -129,20 +131,22 @@ const handleScope = async (type: string, row: RoleVO) =&gt; { @@ -129,20 +131,22 @@ const handleScope = async (type: string, row: RoleVO) =&gt; {
129 actionScopeType.value = type 131 actionScopeType.value = type
130 dialogScopeVisible.value = true 132 dialogScopeVisible.value = true
131 } 133 }
132 -// 树权限(父子联动)  
133 -const handleCheckedTreeConnect = (value) => {  
134 - dataScopeForm.checkStrictly = value ? true : false  
135 -}  
136 // 全选/全不选 134 // 全选/全不选
137 -const handleCheckedTreeNodeAll = (value) => {  
138 - treeRef.value?.setCheckedNodes(value ? dataScopeForm.checkList : []) 135 +const handleCheckedTreeNodeAll = () => {
  136 + treeRef.value?.setCheckedNodes(treeNodeAll.value ? treeOptions.value : [])
139 } 137 }
140 // TODO:保存 138 // TODO:保存
141 const submitScope = () => { 139 const submitScope = () => {
142 console.info() 140 console.info()
143 } 141 }
  142 +const init = () => {
  143 + dataScopeDictDatas.value = getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
  144 +}
144 // ========== 初始化 ========== 145 // ========== 初始化 ==========
145 -getList() 146 +onMounted(() => {
  147 + getList()
  148 + init()
  149 +})
146 </script> 150 </script>
147 151
148 <template> 152 <template>
@@ -261,7 +265,7 @@ getList() @@ -261,7 +265,7 @@ getList()
261 <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button> 265 <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
262 </template> 266 </template>
263 </Dialog> 267 </Dialog>
264 - <Dialog v-model="dialogScopeVisible" :title="dialogScopeTitle"> 268 + <Dialog v-model="dialogScopeVisible" :title="dialogScopeTitle" max-height="600px">
265 <el-form :model="dataScopeForm"> 269 <el-form :model="dataScopeForm">
266 <el-form-item label="角色名称"> 270 <el-form-item label="角色名称">
267 <el-input v-model="dataScopeForm.name" :disabled="true" /> 271 <el-input v-model="dataScopeForm.name" :disabled="true" />
@@ -289,21 +293,23 @@ getList() @@ -289,21 +293,23 @@ getList()
289 > 293 >
290 <el-card class="box-card"> 294 <el-card class="box-card">
291 <template #header> 295 <template #header>
292 - <el-checkbox  
293 - :checked="!dataScopeForm.checkStrictly"  
294 - @change="handleCheckedTreeConnect($event)"  
295 - >父子联动(选中父节点,自动选择子节点)  
296 - </el-checkbox>  
297 - <el-checkbox v-model="treeNodeAll" @change="handleCheckedTreeNodeAll($event)">  
298 - 全选/全不选  
299 - </el-checkbox> 296 + 父子联动(选中父节点,自动选择子节点):
  297 + <el-switch v-model="checkStrictly" inline-prompt active-text="是" inactive-text="否" />
  298 + 全选/全不选:
  299 + <el-switch
  300 + v-model="treeNodeAll"
  301 + inline-prompt
  302 + active-text="是"
  303 + inactive-text="否"
  304 + @change="handleCheckedTreeNodeAll()"
  305 + />
300 </template> 306 </template>
301 <el-tree 307 <el-tree
302 ref="treeRef" 308 ref="treeRef"
303 node-key="id" 309 node-key="id"
304 show-checkbox 310 show-checkbox
305 default-expand-all 311 default-expand-all
306 - :check-strictly="dataScopeForm.checkStrictly" 312 + :check-strictly="!checkStrictly"
307 :props="defaultProps" 313 :props="defaultProps"
308 :data="treeOptions" 314 :data="treeOptions"
309 empty-text="加载中,请稍后" 315 empty-text="加载中,请稍后"
yudao-ui-admin-vue3/windi.config.ts
1 -import { defineConfig } from 'windicss/helpers' 1 +import { defineConfig } from 'vite-plugin-windicss'
2 import plugin from 'windicss/plugin' 2 import plugin from 'windicss/plugin'
3 3
4 function range(size, startAt = 1) { 4 function range(size, startAt = 1) {
@@ -18,22 +18,7 @@ export default defineConfig({ @@ -18,22 +18,7 @@ export default defineConfig({
18 // 暗黑背景色 18 // 暗黑背景色
19 'v-dark': 'var(--dark-bg-color)' 19 'v-dark': 'var(--dark-bg-color)'
20 } 20 }
21 - // screens: {  
22 - // sm: '768px',  
23 - // md: '992px',  
24 - // lg: '1200px',  
25 - // xl: '1920px'  
26 - // }  
27 } 21 }
28 - // height: {  
29 - // ...range(50).map((i) => `h-${i}px`)  
30 - // },  
31 - // margin: {  
32 - // // ...range(50).map((i) => `mt-${i}px`),  
33 - // // ...range(50).map((i) => `mr-${i}px`),  
34 - // // ...range(50).map((i) => `mb-${i}px`),  
35 - // // ...range(50).map((i) => `ml-${i}px`)  
36 - // }  
37 }, 22 },
38 plugins: [ 23 plugins: [
39 plugin(({ addComponents }) => { 24 plugin(({ addComponents }) => {