create-jnrs-template-vue 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/jnrs-template-vue/.prettierrc.json +1 -1
  2. package/jnrs-template-vue/auto-imports.d.ts +1 -0
  3. package/jnrs-template-vue/components.d.ts +11 -2
  4. package/jnrs-template-vue/package.json +18 -17
  5. package/jnrs-template-vue/public/system/menu.json +29 -9
  6. package/jnrs-template-vue/src/App.vue +36 -5
  7. package/jnrs-template-vue/src/api/{base → system}/index.ts +30 -6
  8. package/jnrs-template-vue/src/assets/styles/init.scss +8 -3
  9. package/jnrs-template-vue/src/assets/styles/root.scss +7 -24
  10. package/jnrs-template-vue/src/layout/RouterTabs.vue +5 -4
  11. package/jnrs-template-vue/src/layout/SideMenu.vue +29 -20
  12. package/jnrs-template-vue/src/layout/SideMenuItem.vue +1 -1
  13. package/jnrs-template-vue/src/layout/TopHeader.vue +30 -19
  14. package/jnrs-template-vue/src/layout/index.vue +36 -6
  15. package/jnrs-template-vue/src/locales/en.ts +9 -0
  16. package/jnrs-template-vue/src/locales/index.ts +16 -0
  17. package/jnrs-template-vue/src/locales/zhCn.ts +9 -0
  18. package/jnrs-template-vue/src/main.ts +5 -1
  19. package/jnrs-template-vue/src/router/index.ts +10 -4
  20. package/jnrs-template-vue/src/types/index.d.ts +6 -0
  21. package/jnrs-template-vue/src/utils/common.ts +58 -0
  22. package/jnrs-template-vue/src/views/common/403.vue +2 -2
  23. package/jnrs-template-vue/src/views/common/404.vue +2 -2
  24. package/jnrs-template-vue/src/views/home/index.vue +6 -3
  25. package/jnrs-template-vue/src/views/login/index.vue +15 -26
  26. package/jnrs-template-vue/src/views/system/dict/index.vue +176 -0
  27. package/jnrs-template-vue/src/views/system/menu/index.vue +67 -0
  28. package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +38 -30
  29. package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +22 -31
  30. package/jnrs-template-vue/src/views/system/role/editDialog.vue +94 -0
  31. package/jnrs-template-vue/src/views/system/role/index.vue +30 -3
  32. package/jnrs-template-vue/src/views/visual/index.vue +6 -1
  33. package/jnrs-template-vue/vite.config.ts +6 -4
  34. package/jnrs-template-vue/viteMockServe/dictItemRes.json +27 -0
  35. package/jnrs-template-vue/viteMockServe/dictRes.json +141 -0
  36. package/jnrs-template-vue/viteMockServe/fail.ts +26 -0
  37. package/jnrs-template-vue/viteMockServe/index.ts +18 -25
  38. package/jnrs-template-vue/viteMockServe/{login.json → loginRes.json} +1 -0
  39. package/jnrs-template-vue/viteMockServe/success.ts +31 -0
  40. package/package.json +1 -1
  41. package/jnrs-template-vue/src/types/index.ts +0 -1
  42. package/jnrs-template-vue/src/views/system/user/index.vue +0 -9
@@ -1,7 +1,31 @@
1
1
  <script setup lang="ts">
2
+ import { ref, watch, toRefs } from 'vue'
2
3
  import SideMenu from './SideMenu.vue'
3
4
  import TopHeader from './TopHeader.vue'
4
5
  import RouterTabs from './RouterTabs.vue'
6
+ import { useAuthStore, useSystemStore } from '@/stores'
7
+
8
+ const watermarkFont = ref({
9
+ color: 'rgba(0, 0, 0, 0)',
10
+ fontSize: 12
11
+ })
12
+
13
+ const { userInfo } = useAuthStore()
14
+
15
+ const { computedThemeMode } = toRefs(useSystemStore())
16
+ watch(
17
+ computedThemeMode,
18
+ (newVal) => {
19
+ if (newVal === 'dark') {
20
+ watermarkFont.value.color = 'rgba(255, 255, 255, .06)'
21
+ } else {
22
+ watermarkFont.value.color = 'rgba(0, 0, 0, .06)'
23
+ }
24
+ },
25
+ {
26
+ immediate: true
27
+ }
28
+ )
5
29
  </script>
6
30
 
7
31
  <template>
@@ -13,11 +37,17 @@ import RouterTabs from './RouterTabs.vue'
13
37
  </el-header>
14
38
  <RouterTabs />
15
39
  <el-main>
16
- <router-view v-slot="{ Component }">
17
- <keep-alive>
18
- <component :is="Component" />
19
- </keep-alive>
20
- </router-view>
40
+ <el-watermark
41
+ :font="watermarkFont"
42
+ :content="[userInfo?.name, userInfo?.workNo]"
43
+ style="width: 100%; height: 100%"
44
+ >
45
+ <router-view v-slot="{ Component }">
46
+ <keep-alive>
47
+ <component :is="Component" />
48
+ </keep-alive>
49
+ </router-view>
50
+ </el-watermark>
21
51
  </el-main>
22
52
  </el-container>
23
53
  </el-container>
@@ -35,7 +65,7 @@ import RouterTabs from './RouterTabs.vue'
35
65
  height: 100%;
36
66
 
37
67
  .el-main {
38
- padding: 10px;
68
+ padding: 16px;
39
69
  }
40
70
  }
41
71
  </style>
@@ -0,0 +1,9 @@
1
+ export default {
2
+ main: {
3
+ title: 'Management System'
4
+ },
5
+ login: {
6
+ title: 'Login'
7
+ },
8
+ layout: {}
9
+ }
@@ -0,0 +1,16 @@
1
+ import { createI18n } from 'vue-i18n'
2
+ import zhCN from './zhCn'
3
+ import en from './en'
4
+
5
+ const i18n = createI18n({
6
+ legacy: false, // 启用 Composition API 模式
7
+ globalInjection: true, // 允许在模板中使用 $t
8
+ locale: 'zhCn',
9
+ fallbackLocale: 'en',
10
+ messages: {
11
+ zhCn: zhCN,
12
+ en: en
13
+ }
14
+ })
15
+
16
+ export default i18n
@@ -0,0 +1,9 @@
1
+ export default {
2
+ main: {
3
+ title: '信息化管理系统模板'
4
+ },
5
+ login: {
6
+ title: '登录'
7
+ },
8
+ layout: {}
9
+ }
@@ -1,5 +1,7 @@
1
1
  import 'element-plus/dist/index.css'
2
- import './assets/styles/main.scss'
2
+ import 'element-plus/theme-chalk/dark/css-vars.css'
3
+ import '@jnrs/shared/styles/theme.scss'
4
+ import '@/assets/styles/main.scss'
3
5
 
4
6
  import { createApp } from 'vue'
5
7
  import { createPinia } from 'pinia'
@@ -7,6 +9,7 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
7
9
  import * as ElementPlusIconsVue from '@element-plus/icons-vue'
8
10
  import App from './App.vue'
9
11
  import { router } from './router'
12
+ import locales from './locales/index'
10
13
 
11
14
  const app = createApp(App)
12
15
 
@@ -21,4 +24,5 @@ pinia.use(piniaPluginPersistedstate)
21
24
 
22
25
  app.use(pinia)
23
26
  app.use(router)
27
+ app.use(locales)
24
28
  app.mount('#app')
@@ -2,7 +2,8 @@ import { LAYOUT_NAME, GLOBAL_COMPONENT, routes } from './routes'
2
2
  import { createVueRouter } from '@jnrs/vue-core/router'
3
3
  import type { FileModules, RouteLocationNormalizedGeneric } from '@jnrs/vue-core/router'
4
4
  import { useAuthStore, useMenuStore } from '@/stores'
5
- import { MenuApi } from '@/api/base/index'
5
+ import { MenuApi } from '@/api/system'
6
+ import { hasPermission } from '@jnrs/shared'
6
7
 
7
8
  const fileModules = import.meta.glob('/src/views/**/*.vue') as FileModules
8
9
 
@@ -14,15 +15,20 @@ const router = createVueRouter({
14
15
  layoutName: LAYOUT_NAME,
15
16
  globalComponent: GLOBAL_COMPONENT,
16
17
  handleBeforeEach: async (to: RouteLocationNormalizedGeneric) => {
17
- // 如果是不需要授权的页面,直接放行
18
+ // 如果是不需要[身份验证]的页面,直接放行
18
19
  if (to.meta.noAuth) return true
19
20
 
20
- // 页面刷新处理:未授权时,重定向到授权页
21
- const { hasAuthenticated } = useAuthStore()
21
+ // 页面刷新处理:未[身份验证]时,重定向到[身份验证]页
22
+ const { hasAuthenticated, userInfo } = useAuthStore()
22
23
  if (!hasAuthenticated) {
23
24
  return { name: 'Login', query: { redirect: to.path }, replace: true }
24
25
  }
25
26
 
27
+ // 校验访问权限
28
+ if (to.meta.permissions && !hasPermission(to.meta.permissions, userInfo?.permissions || [])) {
29
+ return { name: '403' }
30
+ }
31
+
26
32
  // 页面刷新处理:菜单未获取时,先获取菜单,再重定向当前路由,即重新开始进入handleBeforeEach
27
33
  const { hasFetchedAsyncMenus, asyncSetMenus } = useMenuStore()
28
34
  if (!hasFetchedAsyncMenus) {
@@ -0,0 +1,6 @@
1
+ // declare module '@jnrs/shared' {
2
+ // interface User {
3
+ // account: string
4
+ // loginDateTime: string
5
+ // }
6
+ // }
@@ -0,0 +1,58 @@
1
+ import {
2
+ getOneDictList as _getOneDictList,
3
+ getDictLabel as _getDictLabel,
4
+ getDictValue as _getDictValue,
5
+ getDictColor as _getDictColor
6
+ } from '@jnrs/shared'
7
+ import { useAuthStore } from '@jnrs/vue-core/pinia'
8
+
9
+ /**
10
+ * 根据字典名称获取字典数据
11
+ * @param name 字典名称
12
+ * @returns 某字典名称所对应的字典列表数据
13
+ */
14
+ export const getOneDictList = (name: string) => {
15
+ const { dict } = useAuthStore()
16
+ if (dict) {
17
+ return _getOneDictList(name, dict)
18
+ }
19
+ }
20
+
21
+ /**
22
+ * 根据字典名称和字典值获取字典标签
23
+ * @param name 字典名称
24
+ * @param value 字典值
25
+ * @returns 某字典名称所对应的字典标签
26
+ */
27
+ export const getDictLabel = (name: string, value: string | number) => {
28
+ const { dict } = useAuthStore()
29
+ if (dict) {
30
+ return _getDictLabel(name, value, dict)
31
+ }
32
+ }
33
+
34
+ /**
35
+ * 根据字典名称和字典标签获取字典值
36
+ * @param name 字典名称
37
+ * @param label 字典标签
38
+ * @returns 某字典名称所对应的字典值
39
+ */
40
+ export const getDictValue = (name: string, label: string) => {
41
+ const { dict } = useAuthStore()
42
+ if (dict) {
43
+ return _getDictValue(name, label, dict)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * 根据字典名称和字典值获取字典颜色
49
+ * @param name 字典名称
50
+ * @param value 字典值
51
+ * @returns 某字典名称所对应的字典颜色
52
+ */
53
+ export const getDictColor = (name: string, value: string | number) => {
54
+ const { dict } = useAuthStore()
55
+ if (dict) {
56
+ return _getDictColor(name, value, dict)
57
+ }
58
+ }
@@ -38,15 +38,15 @@ const goBack = () => {
38
38
  justify-content: center;
39
39
  width: 100%;
40
40
  height: 100vh;
41
- background: #f9fafe;
42
41
  overflow: hidden;
42
+ background: var(--jnrs-background-primary);
43
43
  .main_mid {
44
44
  text-align: center;
45
45
  img {
46
46
  width: 300px;
47
47
  }
48
48
  h3 {
49
- color: #2a63a4;
49
+ color: var(--jnrs-font-primary);
50
50
  }
51
51
  .main_mid_btn {
52
52
  margin-top: 30px;
@@ -38,15 +38,15 @@ const goBack = () => {
38
38
  justify-content: center;
39
39
  width: 100%;
40
40
  height: 100vh;
41
- background: #f9fafe;
42
41
  overflow: hidden;
42
+ background: var(--jnrs-background-primary);
43
43
  .main_mid {
44
44
  text-align: center;
45
45
  img {
46
46
  width: 300px;
47
47
  }
48
48
  h3 {
49
- color: #2a63a4;
49
+ color: var(--jnrs-font-primary);
50
50
  }
51
51
  .main_mid_btn {
52
52
  margin-top: 30px;
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, onMounted } from 'vue'
3
3
  import { ElMessage } from 'element-plus'
4
- import { MenuApi, LoginApi, UserInfoApi } from '@/api/base'
4
+ import { MenuApi, LoginApi, UserInfoApi } from '@/api/system'
5
5
  import { NotFoundApi, NoAuth } from '@/api/mock'
6
6
  import { handleRouter } from '@jnrs/vue-core/router'
7
- import type { MenuItem } from '@/types'
7
+ import type { MenuItem } from '@jnrs/vue-core'
8
8
 
9
9
  const loginParams = ref({
10
10
  account: 'admin',
@@ -13,6 +13,7 @@ const loginParams = ref({
13
13
 
14
14
  const routeOptions = ref<MenuItem[]>([])
15
15
  const currentRoute = ref('')
16
+ const datePicker = ref(new Date())
16
17
 
17
18
  onMounted(() => {
18
19
  handleInfoApi()
@@ -76,7 +77,7 @@ const handleRouteChange = () => {
76
77
  </script>
77
78
 
78
79
  <template>
79
- <div style="background-color: #fff">
80
+ <div>
80
81
  <h1>Playground - 测试组件</h1>
81
82
  <p>网络请求测试</p>
82
83
  <el-button-group>
@@ -108,6 +109,8 @@ const handleRouteChange = () => {
108
109
  <span>{{ data.meta.title }}</span>
109
110
  </template>
110
111
  </el-cascader>
112
+ <p>Element 组件测试</p>
113
+ <el-date-picker-panel v-model="datePicker" />
111
114
  <!-- <MyButton type="primary">测试按钮</MyButton>
112
115
  <MyModal v-model="show">内容</MyModal> -->
113
116
  </div>
@@ -1,13 +1,15 @@
1
1
  <script setup lang="ts">
2
2
  import type { FormInstance, FormRules } from 'element-plus'
3
3
  import { ref } from 'vue'
4
- import { LoginApi } from '@/api/base/index'
4
+ import { LoginApi } from '@/api/system'
5
5
  import { useAuthStore } from '@/stores'
6
6
  import { handleRouter, useRoute } from '@jnrs/vue-core/router'
7
7
  import { isWeakPwd } from '@jnrs/shared/validator'
8
+ import { formatDateTime, formatWeekday } from '@jnrs/shared'
8
9
 
9
10
  const route = useRoute()
10
11
  const loading = ref(false)
12
+ const { setUserInfo, setToken, setDict } = useAuthStore()
11
13
 
12
14
  // 表单 ref
13
15
  const ruleFormRef = ref<FormInstance>()
@@ -33,13 +35,15 @@ const submitForm = () => {
33
35
  loading.value = true
34
36
  try {
35
37
  const res = await LoginApi(ruleForm.value)
36
- const { token, dict, ...userInfo } = res
37
- const { asyncSetAuth } = useAuthStore()
38
- await asyncSetAuth({
39
- token: token,
40
- dict: dict,
41
- userInfo: userInfo
38
+ const { token, dict, ...restParameter } = res
39
+ const loginDateTime = formatDateTime() + ' ' + formatWeekday()
40
+ setUserInfo({
41
+ ...restParameter,
42
+ account: ruleForm.value.account,
43
+ loginDateTime
42
44
  })
45
+ setToken(token)
46
+ setDict(dict)
43
47
  handleRouter({ path: route.query.redirect?.toString() || '/' }, 'replace')
44
48
  } catch {
45
49
  } finally {
@@ -70,12 +74,7 @@ const submitForm = () => {
70
74
  @keyup.enter="submitForm()"
71
75
  >
72
76
  <el-form-item prop="account">
73
- <el-input
74
- v-model="ruleForm.account"
75
- placeholder="请输入账号"
76
- prefix-icon="User"
77
- clearable
78
- />
77
+ <el-input v-model="ruleForm.account" placeholder="请输入账号" prefix-icon="User" clearable />
79
78
  </el-form-item>
80
79
  <el-form-item prop="password">
81
80
  <el-input
@@ -88,9 +87,7 @@ const submitForm = () => {
88
87
  ></el-input>
89
88
  </el-form-item>
90
89
  <el-form-item>
91
- <el-button class="btn" type="primary" :loading="loading" @click="submitForm()">
92
- 登 录
93
- </el-button>
90
+ <el-button class="btn" type="primary" :loading="loading" @click="submitForm()">登 录</el-button>
94
91
  </el-form-item>
95
92
  </el-form>
96
93
  <div class="greeting">欢迎使用</div>
@@ -102,14 +99,6 @@ const submitForm = () => {
102
99
  </template>
103
100
 
104
101
  <style scoped lang="scss">
105
- @use 'sass:math';
106
-
107
- $design-width: 1920; // 设计稿宽度
108
-
109
- @function px2vw($px) {
110
- @return math.div($px, $design-width) * 100vw;
111
- }
112
-
113
102
  .main {
114
103
  position: relative;
115
104
  width: 100%;
@@ -147,8 +136,8 @@ $design-width: 1920; // 设计稿宽度
147
136
  width: 50%;
148
137
  height: 100%;
149
138
  box-shadow: 5px 0 10px rgba(0, 0, 0, 0.5);
150
- background: url('@/assets/img/common/card_bg.png') no-repeat;
151
- background-size: 100% 100%;
139
+ // background: url('@/assets/img/common/card_bg.png') no-repeat;
140
+ // background-size: 100% 100%;
152
141
  text-align: center;
153
142
  font-size: 22px;
154
143
 
@@ -0,0 +1,176 @@
1
+ <script setup lang="ts">
2
+ import { ElMessage } from 'element-plus'
3
+ import { ref, onActivated } from 'vue'
4
+ import { DictApi, DictDetailApi, DictChangeApi } from '@/api/system'
5
+ import { useAsyncTableHeight } from '@jnrs/vue-core/composables'
6
+ import type { DictItem } from '@jnrs/shared'
7
+
8
+ const { maxHeight } = useAsyncTableHeight('el_table_dict', 0)
9
+ const loading = ref(false)
10
+ const tableData = ref<DictItem[]>()
11
+ const predefineColors = ref([
12
+ '#000000',
13
+ '#999999',
14
+ '#f2f2f2',
15
+ '#ffffff',
16
+ '#65DC79',
17
+ '#5887F7',
18
+ '#E43634',
19
+ '#FEA822',
20
+ '#C9512C'
21
+ ])
22
+
23
+ onActivated(() => {
24
+ getList()
25
+ })
26
+
27
+ const getList = () => {
28
+ loading.value = true
29
+ DictApi()
30
+ .then((res) => {
31
+ tableData.value = res
32
+ })
33
+ .finally(() => {
34
+ loading.value = false
35
+ })
36
+ }
37
+
38
+ const handleExpandChange = (row: DictItem) => {
39
+ loading.value = true
40
+ DictDetailApi(String(row.id))
41
+ .then((res) => {
42
+ row.options = res
43
+ })
44
+ .finally(() => {
45
+ loading.value = false
46
+ })
47
+ }
48
+
49
+ // 保存键值对
50
+ const editKeyValue = (row: DictItem) => {
51
+ if (!row.label) {
52
+ ElMessage({
53
+ type: 'warning',
54
+ message: 'label 不能为空'
55
+ })
56
+ return false
57
+ }
58
+ loading.value = true
59
+ DictChangeApi(row)
60
+ .then(() => {
61
+ ElMessage({
62
+ type: 'success',
63
+ message: '修改成功',
64
+ grouping: true
65
+ })
66
+ })
67
+ .finally(() => {
68
+ loading.value = false
69
+ })
70
+ }
71
+ </script>
72
+
73
+ <template>
74
+ <div class="main">
75
+ <el-card shadow="never">
76
+ <template #header>
77
+ <div class="card_header">
78
+ <h3>字典管理</h3>
79
+ </div>
80
+ </template>
81
+ <el-table
82
+ class="el_table_dict"
83
+ :data="tableData"
84
+ :height="maxHeight"
85
+ scrollbar-always-on
86
+ border
87
+ v-loading="loading"
88
+ row-key="id"
89
+ @expand-change="handleExpandChange"
90
+ >
91
+ <el-table-column prop="dictName" label="字典名" />
92
+ <el-table-column prop="describe" label="说明" />
93
+ <el-table-column type="expand" fixed="right" label="字典值" width="100">
94
+ <template #default="scope">
95
+ <div
96
+ class="table_expand animation15"
97
+ style="animation-duration: 0.5s; animation-delay: 0s"
98
+ >
99
+ <div class="table_expand_top">
100
+ <h5>字典值列表</h5>
101
+ </div>
102
+ <el-table :data="scope.row.options" :border="true" size="small" row-key="value">
103
+ <el-table-column
104
+ label="value"
105
+ prop="value"
106
+ width="100"
107
+ align="center"
108
+ ></el-table-column>
109
+ <el-table-column label="label" prop="label">
110
+ <template #default="optionScope">
111
+ <el-input v-model="optionScope.row.label" maxlength="20" />
112
+ </template>
113
+ </el-table-column>
114
+ <el-table-column label="颜色" prop="displayColor">
115
+ <template #default="optionScope">
116
+ <el-color-picker
117
+ v-model="optionScope.row.displayColor"
118
+ :predefine="predefineColors"
119
+ />
120
+ <span style="margin-left: 10px">
121
+ {{ optionScope.row.displayColor || '无' }}
122
+ </span>
123
+ </template>
124
+ </el-table-column>
125
+ <el-table-column prop="sequence" label="排序" width="180" align="center">
126
+ <template #default="optionScope">
127
+ <el-input-number
128
+ v-model="optionScope.row.sequence"
129
+ :min="1"
130
+ :max="999"
131
+ :step="1"
132
+ controls-position="right"
133
+ step-strictly
134
+ style="width: 140px"
135
+ />
136
+ </template>
137
+ </el-table-column>
138
+ <el-table-column label="操作" width="100" align="center" fixed="right">
139
+ <template #default="optionScope">
140
+ <el-button type="success" size="small" @click="editKeyValue(optionScope.row)">
141
+ 确认修改
142
+ </el-button>
143
+ </template>
144
+ </el-table-column>
145
+ </el-table>
146
+ </div>
147
+ </template>
148
+ </el-table-column>
149
+ </el-table>
150
+ </el-card>
151
+ </div>
152
+ </template>
153
+
154
+ <style scoped lang="scss">
155
+ .table_expand {
156
+ padding: 10px;
157
+ color: var(--jnrs-font-primary);
158
+ background: var(--jnrs-background-primary);
159
+
160
+ .table_expand_top {
161
+ position: relative;
162
+
163
+ h5 {
164
+ font-size: 14px;
165
+ margin-bottom: 10px;
166
+ }
167
+
168
+ .table_expand_top_btn {
169
+ position: absolute;
170
+ right: 0;
171
+ top: 0;
172
+ z-index: 1;
173
+ }
174
+ }
175
+ }
176
+ </style>
@@ -0,0 +1,67 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { useMenuStore } from '@/stores'
4
+ import { getDictLabel } from '@/utils/common'
5
+ import { useAsyncTableHeight } from '@jnrs/vue-core/composables'
6
+
7
+ const { maxHeight } = useAsyncTableHeight('el_table_menu', 0)
8
+ const loading = ref(false)
9
+
10
+ const { menus } = useMenuStore()
11
+ </script>
12
+
13
+ <template>
14
+ <div>
15
+ <el-card shadow="never">
16
+ <template #header>
17
+ <div class="card_header">
18
+ <h3>菜单管理</h3>
19
+ </div>
20
+ </template>
21
+ <el-table
22
+ class="el_table_menu"
23
+ :data="menus"
24
+ :height="maxHeight"
25
+ scrollbar-always-on
26
+ row-key="meta.uuid"
27
+ border
28
+ v-loading="loading"
29
+ >
30
+ <el-table-column prop="meta.title" label="名称" min-width="120" />
31
+ <el-table-column prop="path" label="路由地址" min-width="120" />
32
+ <el-table-column prop="role" label="权限" min-width="120">
33
+ <template #default="{ row }">
34
+ <span v-if="!row.meta.role" style="color: #999">auto</span>
35
+ <template v-else>
36
+ {{ row.meta.role.map((d: string | number) => getDictLabel('role', d)) }}
37
+ </template>
38
+ </template>
39
+ </el-table-column>
40
+ <el-table-column prop="editRole" label="操作权限" min-width="120">
41
+ <template #default="scope">
42
+ <template v-if="!scope.row.editRole">
43
+ <span style="color: #999" v-if="!scope.row.role">auto</span>
44
+ <!-- <template v-else>
45
+ {{ scope.row.role.map((d) => getDictLabel('role', d.split('Role')[1])) }}
46
+ </template> -->
47
+ </template>
48
+ <!-- <template v-else>
49
+ {{ scope.row.editRole.map((d) => getDictLabel('role', d.split('Role')[1])) }}
50
+ </template> -->
51
+ </template>
52
+ </el-table-column>
53
+ <el-table-column prop="type" label="类型" width="80" align="center">
54
+ <template #default="scope">
55
+ <el-tag type="primary" v-if="scope.row.path">菜单</el-tag>
56
+ <el-tag type="success" v-else>目录</el-tag>
57
+ </template>
58
+ </el-table-column>
59
+ <el-table-column prop="activate" label="图标" width="80" align="center">
60
+ <template #default="scope">
61
+ <el-icon v-if="scope.row.icon"><component :is="scope.row.icon" /></el-icon>
62
+ </template>
63
+ </el-table-column>
64
+ </el-table>
65
+ </el-card>
66
+ </div>
67
+ </template>