create-jnrs-template-vue 1.1.5 → 1.1.7
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.
- package/jnrs-template-vue/.env.development +4 -5
- package/jnrs-template-vue/.env.production +1 -1
- package/jnrs-template-vue/.prettierrc.json +1 -1
- package/jnrs-template-vue/auto-imports.d.ts +1 -0
- package/jnrs-template-vue/components.d.ts +14 -0
- package/jnrs-template-vue/package.json +1 -1
- package/jnrs-template-vue/public/system/menu.json +27 -8
- package/jnrs-template-vue/src/App.vue +2 -2
- package/jnrs-template-vue/src/api/request.ts +4 -1
- package/jnrs-template-vue/src/api/{base → system}/index.ts +48 -20
- package/jnrs-template-vue/src/directives/permissions.ts +28 -0
- package/jnrs-template-vue/src/layout/RouterTabs.vue +1 -1
- package/jnrs-template-vue/src/layout/SideMenu.vue +2 -4
- package/jnrs-template-vue/src/layout/SideMenuItem.vue +1 -1
- package/jnrs-template-vue/src/layout/TopHeader.vue +20 -28
- package/jnrs-template-vue/src/layout/index.vue +35 -5
- package/jnrs-template-vue/src/locales/index.ts +10 -3
- package/jnrs-template-vue/src/main.ts +3 -2
- package/jnrs-template-vue/src/router/index.ts +41 -6
- package/jnrs-template-vue/src/router/routes.ts +28 -28
- package/jnrs-template-vue/src/stores/mock.ts +4 -2
- package/jnrs-template-vue/src/utils/permissions.ts +16 -0
- package/jnrs-template-vue/src/views/common/403.vue +5 -9
- package/jnrs-template-vue/src/views/common/404.vue +5 -9
- package/jnrs-template-vue/src/views/home/index.vue +54 -20
- package/jnrs-template-vue/src/views/login/index.vue +36 -20
- package/jnrs-template-vue/src/views/system/dict/index.vue +174 -0
- package/jnrs-template-vue/src/views/system/menu/index.vue +65 -0
- package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +38 -30
- package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +12 -20
- package/jnrs-template-vue/src/views/system/role/editDialog.vue +94 -0
- package/jnrs-template-vue/src/views/system/role/index.vue +45 -4
- package/jnrs-template-vue/vite.config.ts +2 -1
- package/jnrs-template-vue/viteMockServe/dictItemRes.json +27 -0
- package/jnrs-template-vue/viteMockServe/dictRes.json +141 -0
- package/jnrs-template-vue/viteMockServe/fail.ts +26 -0
- package/jnrs-template-vue/viteMockServe/index.ts +36 -27
- package/jnrs-template-vue/viteMockServe/{login.json → loginRes_admin.json} +4 -3
- package/jnrs-template-vue/viteMockServe/loginRes_user.json +713 -0
- package/jnrs-template-vue/viteMockServe/roleRes.json +37 -0
- package/jnrs-template-vue/viteMockServe/success.ts +31 -0
- package/package.json +1 -1
- package/jnrs-template-vue/.env.example +0 -14
- package/jnrs-template-vue/src/types/index.ts +0 -2
- package/jnrs-template-vue/src/views/system/user/index.vue +0 -9
|
@@ -10,25 +10,37 @@ export const LAYOUT_NAME = 'Layout'
|
|
|
10
10
|
export const GLOBAL_COMPONENT = import('@/layout/BlankLayout.vue')
|
|
11
11
|
|
|
12
12
|
export const routes = [
|
|
13
|
-
{
|
|
14
|
-
path: '/login',
|
|
15
|
-
component: () => import('@/layout/BlankLayout.vue'),
|
|
16
|
-
meta: { title: '登录' },
|
|
17
|
-
children: [
|
|
18
|
-
{
|
|
19
|
-
path: '',
|
|
20
|
-
name: 'Login',
|
|
21
|
-
meta: { title: '登录', noAuth: true },
|
|
22
|
-
component: () => import('@/views/login/index.vue')
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
},
|
|
26
13
|
// {
|
|
27
|
-
// name: 'Login',
|
|
28
14
|
// path: '/login',
|
|
29
|
-
// component: () => import('@/
|
|
30
|
-
// meta: { title: '登录'
|
|
15
|
+
// component: () => import('@/layout/BlankLayout.vue'),
|
|
16
|
+
// meta: { title: '登录' },
|
|
17
|
+
// children: [
|
|
18
|
+
// {
|
|
19
|
+
// path: '',
|
|
20
|
+
// name: 'Login',
|
|
21
|
+
// meta: { title: '登录', noAuth: true },
|
|
22
|
+
// component: () => import('@/views/login/index.vue')
|
|
23
|
+
// }
|
|
24
|
+
// ]
|
|
31
25
|
// },
|
|
26
|
+
{
|
|
27
|
+
name: '403',
|
|
28
|
+
path: '/403',
|
|
29
|
+
component: () => import('@/views/common/403.vue'),
|
|
30
|
+
meta: { title: '403', noAuth: true, global: true }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: '404',
|
|
34
|
+
path: '/:pathMatch(.*)*',
|
|
35
|
+
component: () => import('@/views/common/404.vue'),
|
|
36
|
+
meta: { title: '404', global: true }
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Login',
|
|
40
|
+
path: '/login',
|
|
41
|
+
component: () => import('@/views/login/index.vue'),
|
|
42
|
+
meta: { title: '登录', noAuth: true, global: true }
|
|
43
|
+
},
|
|
32
44
|
{
|
|
33
45
|
name: LAYOUT_NAME,
|
|
34
46
|
path: '/',
|
|
@@ -45,17 +57,5 @@ export const routes = [
|
|
|
45
57
|
component: () => import('@/views/home/index.vue')
|
|
46
58
|
}
|
|
47
59
|
]
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: '403',
|
|
51
|
-
path: '/403',
|
|
52
|
-
component: () => import('@/views/common/403.vue'),
|
|
53
|
-
meta: { title: '403', noAuth: true, global: true }
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: '404',
|
|
57
|
-
path: '/:pathMatch(.*)*',
|
|
58
|
-
component: () => import('@/views/common/404.vue'),
|
|
59
|
-
meta: { title: '404', global: true }
|
|
60
60
|
}
|
|
61
61
|
]
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Mock仓库
|
|
5
6
|
* @returns isMock 是否使用mock数据
|
|
6
7
|
*/
|
|
7
8
|
export const useMockStore = defineStore(
|
|
8
|
-
'mock',
|
|
9
|
+
'jnrs-app-mock',
|
|
9
10
|
() => {
|
|
10
|
-
|
|
11
|
+
// 是否使用mock数据
|
|
12
|
+
const isMock = ref(import.meta.env.VITE_USE_MOCK === 'true')
|
|
11
13
|
|
|
12
14
|
return { isMock }
|
|
13
15
|
},
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { hasPermissionWithSuffix } from '@jnrs/shared'
|
|
2
|
+
import { useAuthStore } from '@/stores'
|
|
3
|
+
|
|
4
|
+
// 菜单查看权限
|
|
5
|
+
export function hasMenuViewPermission(requiredPerms: string[] | undefined) {
|
|
6
|
+
const { userInfo } = useAuthStore()
|
|
7
|
+
const userPerms = userInfo?.permissions || []
|
|
8
|
+
return hasPermissionWithSuffix(requiredPerms, userPerms, ':view', 'some')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 按钮操作权限
|
|
12
|
+
export function hasPermission(requiredPerms: string[] | undefined) {
|
|
13
|
+
const { userInfo } = useAuthStore()
|
|
14
|
+
const userPerms = userInfo?.permissions || []
|
|
15
|
+
return hasPermissionWithSuffix(requiredPerms, userPerms)
|
|
16
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { router } from '@/router'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const route = useRoute()
|
|
3
|
+
import { handleRouter } from '@jnrs/vue-core/router'
|
|
6
4
|
|
|
7
5
|
const goLogin = () => {
|
|
8
|
-
handleRouter({ name: 'Login'
|
|
6
|
+
handleRouter({ name: 'Login' }, 'replace')
|
|
9
7
|
}
|
|
10
8
|
const goHome = () => {
|
|
11
9
|
handleRouter({ path: '/' }, 'replace')
|
|
@@ -22,9 +20,7 @@ const goBack = () => {
|
|
|
22
20
|
<h3>抱歉,您访问的资源权限受限</h3>
|
|
23
21
|
<div class="main_mid_btn">
|
|
24
22
|
<el-button @click="goLogin" style="margin-right: 20px">重新登录</el-button>
|
|
25
|
-
<el-button type="primary" plain @click="goHome" style="margin-right: 20px">
|
|
26
|
-
返回首页
|
|
27
|
-
</el-button>
|
|
23
|
+
<el-button type="primary" plain @click="goHome" style="margin-right: 20px">返回首页</el-button>
|
|
28
24
|
<el-button type="primary" @click="goBack">返回上一页</el-button>
|
|
29
25
|
</div>
|
|
30
26
|
</div>
|
|
@@ -38,15 +34,15 @@ const goBack = () => {
|
|
|
38
34
|
justify-content: center;
|
|
39
35
|
width: 100%;
|
|
40
36
|
height: 100vh;
|
|
41
|
-
background: #f9fafe;
|
|
42
37
|
overflow: hidden;
|
|
38
|
+
background: var(--jnrs-background-primary);
|
|
43
39
|
.main_mid {
|
|
44
40
|
text-align: center;
|
|
45
41
|
img {
|
|
46
42
|
width: 300px;
|
|
47
43
|
}
|
|
48
44
|
h3 {
|
|
49
|
-
color:
|
|
45
|
+
color: var(--jnrs-font-primary);
|
|
50
46
|
}
|
|
51
47
|
.main_mid_btn {
|
|
52
48
|
margin-top: 30px;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { router } from '@/router'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const route = useRoute()
|
|
3
|
+
import { handleRouter } from '@jnrs/vue-core/router'
|
|
6
4
|
|
|
7
5
|
const goLogin = () => {
|
|
8
|
-
handleRouter({ name: 'Login'
|
|
6
|
+
handleRouter({ name: 'Login' }, 'replace')
|
|
9
7
|
}
|
|
10
8
|
const goHome = () => {
|
|
11
9
|
handleRouter({ path: '/' }, 'replace')
|
|
@@ -22,9 +20,7 @@ const goBack = () => {
|
|
|
22
20
|
<h3>抱歉,您访问的页面不存在</h3>
|
|
23
21
|
<div class="main_mid_btn">
|
|
24
22
|
<el-button @click="goLogin" style="margin-right: 20px">重新登录</el-button>
|
|
25
|
-
<el-button type="primary" plain @click="goHome" style="margin-right: 20px">
|
|
26
|
-
返回首页
|
|
27
|
-
</el-button>
|
|
23
|
+
<el-button type="primary" plain @click="goHome" style="margin-right: 20px">返回首页</el-button>
|
|
28
24
|
<el-button type="primary" @click="goBack">返回上一页</el-button>
|
|
29
25
|
</div>
|
|
30
26
|
</div>
|
|
@@ -38,15 +34,15 @@ const goBack = () => {
|
|
|
38
34
|
justify-content: center;
|
|
39
35
|
width: 100%;
|
|
40
36
|
height: 100vh;
|
|
41
|
-
background: #f9fafe;
|
|
42
37
|
overflow: hidden;
|
|
38
|
+
background: var(--jnrs-background-primary);
|
|
43
39
|
.main_mid {
|
|
44
40
|
text-align: center;
|
|
45
41
|
img {
|
|
46
42
|
width: 300px;
|
|
47
43
|
}
|
|
48
44
|
h3 {
|
|
49
|
-
color:
|
|
45
|
+
color: var(--jnrs-font-primary);
|
|
50
46
|
}
|
|
51
47
|
.main_mid_btn {
|
|
52
48
|
margin-top: 30px;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, onMounted } from 'vue'
|
|
3
|
+
import { storeToRefs } from 'pinia'
|
|
3
4
|
import { ElMessage } from 'element-plus'
|
|
4
|
-
import { MenuApi, LoginApi, UserInfoApi } from '@/api/
|
|
5
|
+
import { MenuApi, LoginApi, UserInfoApi } from '@/api/system'
|
|
5
6
|
import { NotFoundApi, NoAuth } from '@/api/mock'
|
|
6
7
|
import { handleRouter } from '@jnrs/vue-core/router'
|
|
7
|
-
import type { MenuItem } from '
|
|
8
|
+
import type { MenuItem } from '@jnrs/vue-core'
|
|
9
|
+
import { hasPermission } from '@/utils/permissions'
|
|
10
|
+
import { useMockStore } from '@/stores'
|
|
11
|
+
|
|
12
|
+
const { isMock } = storeToRefs(useMockStore())
|
|
8
13
|
|
|
9
14
|
const loginParams = ref({
|
|
10
|
-
account: '
|
|
15
|
+
account: 'user',
|
|
11
16
|
password: '123456'
|
|
12
17
|
})
|
|
13
18
|
|
|
@@ -15,9 +20,7 @@ const routeOptions = ref<MenuItem[]>([])
|
|
|
15
20
|
const currentRoute = ref('')
|
|
16
21
|
const datePicker = ref(new Date())
|
|
17
22
|
|
|
18
|
-
onMounted(() => {
|
|
19
|
-
handleInfoApi()
|
|
20
|
-
})
|
|
23
|
+
onMounted(() => {})
|
|
21
24
|
|
|
22
25
|
const handleInfoApi = async () => {
|
|
23
26
|
try {
|
|
@@ -32,7 +35,6 @@ const handleMenuApi = async () => {
|
|
|
32
35
|
try {
|
|
33
36
|
const res = await MenuApi()
|
|
34
37
|
routeOptions.value = res
|
|
35
|
-
console.log(res)
|
|
36
38
|
} catch (error) {
|
|
37
39
|
console.log(error)
|
|
38
40
|
}
|
|
@@ -71,29 +73,52 @@ const handleRouteChange = () => {
|
|
|
71
73
|
name: currentRoute.value
|
|
72
74
|
})
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
// import { MyButton, MyModal } from '../src' // 直接导入本地源码
|
|
76
|
-
// import { useUserStore } from '../src'
|
|
77
76
|
</script>
|
|
78
77
|
|
|
79
78
|
<template>
|
|
80
79
|
<div>
|
|
81
|
-
<
|
|
80
|
+
<h3>Playground - 功能测试</h3>
|
|
81
|
+
<p>权限测试(admin 账号拥有全部权限,请用非 admin 账号测试该功能)</p>
|
|
82
|
+
<ul>
|
|
83
|
+
<li>
|
|
84
|
+
<span>有权限:(期望:正常显示按钮)</span>
|
|
85
|
+
<el-button type="primary" size="small" v-permissions="['mine:view']">添加</el-button>
|
|
86
|
+
</li>
|
|
87
|
+
<li>
|
|
88
|
+
<span>无权限:(期望:按钮禁用 -> UI 组件:使用 UI 框架提供的禁用属性)</span>
|
|
89
|
+
<el-button type="primary" size="small" :disabled="!hasPermission(['test:del'])">删除</el-button>
|
|
90
|
+
</li>
|
|
91
|
+
<li>
|
|
92
|
+
<span>无权限:(期望:按钮禁用 -> 原生元素:添加 disabled 属性和类名)</span>
|
|
93
|
+
<button size="small" v-permissions.disabled="['test:del']">删除</button>
|
|
94
|
+
</li>
|
|
95
|
+
<li>
|
|
96
|
+
<span>无权限:(期望:按钮不可见 -> 添加样式 display:none)</span>
|
|
97
|
+
<button size="small" v-permissions.display="['test:del']">删除</button>
|
|
98
|
+
</li>
|
|
99
|
+
<li>
|
|
100
|
+
<span>无权限:(期望:按钮不可见 -> remove HTMLElement)</span>
|
|
101
|
+
<button size="small" v-permissions="['test:del']">删除</button>
|
|
102
|
+
</li>
|
|
103
|
+
</ul>
|
|
82
104
|
<p>网络请求测试</p>
|
|
105
|
+
<div>
|
|
106
|
+
<span>服务器类型:</span>
|
|
107
|
+
<el-switch v-model="isMock" active-text="Mock 服务器" inactive-text="后端服务器" />
|
|
108
|
+
</div>
|
|
83
109
|
<el-button-group>
|
|
84
|
-
<el-button type="primary" size="small" @click="handleMenuApi"
|
|
110
|
+
<el-button type="primary" size="small" @click="handleMenuApi">获取完整菜单数据</el-button>
|
|
111
|
+
<el-button type="primary" size="small" @click="handleInfoApi">获取用户数据</el-button>
|
|
85
112
|
<el-button type="primary" size="small" @click="handleNotFoundApi">404</el-button>
|
|
86
113
|
<el-button type="primary" size="small" @click="handleNoAuth()">暂无权限</el-button>
|
|
87
|
-
<el-button type="primary" size="small" @click="handleNoAuth(false)">
|
|
88
|
-
暂无权限(不显示Msg)
|
|
89
|
-
</el-button>
|
|
114
|
+
<el-button type="primary" size="small" @click="handleNoAuth(false)">暂无权限(不显示 Message)</el-button>
|
|
90
115
|
</el-button-group>
|
|
91
|
-
<el-input v-model="loginParams.account" size="small" style="width:
|
|
116
|
+
<el-input v-model="loginParams.account" size="small" style="width: 200px">
|
|
92
117
|
<template #append>
|
|
93
|
-
<el-button size="small" @click="handleLogin"
|
|
118
|
+
<el-button size="small" @click="handleLogin">验证登录接口</el-button>
|
|
94
119
|
</template>
|
|
95
120
|
</el-input>
|
|
96
|
-
<p
|
|
121
|
+
<p>完整路由测试(先点击按钮,获取完整菜单数据)</p>
|
|
97
122
|
<el-cascader
|
|
98
123
|
v-model="currentRoute"
|
|
99
124
|
:options="routeOptions"
|
|
@@ -111,8 +136,17 @@ const handleRouteChange = () => {
|
|
|
111
136
|
</el-cascader>
|
|
112
137
|
<p>Element 组件测试</p>
|
|
113
138
|
<el-date-picker-panel v-model="datePicker" />
|
|
114
|
-
<!-- <MyButton type="primary">测试按钮</MyButton>
|
|
115
|
-
<MyModal v-model="show">内容</MyModal> -->
|
|
116
139
|
</div>
|
|
117
140
|
<router-view></router-view>
|
|
118
141
|
</template>
|
|
142
|
+
|
|
143
|
+
<style scoped lang="scss">
|
|
144
|
+
h3 {
|
|
145
|
+
color: var(--jnrs-color-primary);
|
|
146
|
+
}
|
|
147
|
+
p {
|
|
148
|
+
margin: 24px 0 8px;
|
|
149
|
+
color: var(--jnrs-background-primary);
|
|
150
|
+
background: var(--jnrs-font-primary);
|
|
151
|
+
}
|
|
152
|
+
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
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/
|
|
4
|
+
import { RoleApi, 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'
|
|
@@ -9,6 +9,12 @@ import { formatDateTime, formatWeekday } from '@jnrs/shared'
|
|
|
9
9
|
|
|
10
10
|
const route = useRoute()
|
|
11
11
|
const loading = ref(false)
|
|
12
|
+
const {
|
|
13
|
+
setUserInfo,
|
|
14
|
+
setToken,
|
|
15
|
+
setDict
|
|
16
|
+
// setRole
|
|
17
|
+
} = useAuthStore()
|
|
12
18
|
|
|
13
19
|
// 表单 ref
|
|
14
20
|
const ruleFormRef = ref<FormInstance>()
|
|
@@ -27,23 +33,40 @@ const rules = ref<FormRules>({
|
|
|
27
33
|
})
|
|
28
34
|
|
|
29
35
|
// 提交函数
|
|
30
|
-
const submitForm = () => {
|
|
36
|
+
const submitForm = async () => {
|
|
31
37
|
if (!ruleFormRef.value) return
|
|
32
38
|
ruleFormRef.value.validate(async (valid) => {
|
|
33
39
|
if (!valid) return
|
|
34
40
|
loading.value = true
|
|
35
41
|
try {
|
|
36
|
-
const
|
|
37
|
-
const { token, dict, ...
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const loginRes = await LoginApi(ruleForm.value)
|
|
43
|
+
const { token, dict, ...restParameter } = loginRes
|
|
44
|
+
const loginDateTime = formatDateTime() + ' ' + formatWeekday()
|
|
45
|
+
|
|
46
|
+
// 获取角色列表,将登录人角色对应的权限叠加给当前登录人(一次性获取叠加)
|
|
47
|
+
const roleRes = await RoleApi()
|
|
48
|
+
let permissionsMerger = restParameter.permissions
|
|
49
|
+
if (Array.isArray(restParameter.permissions)) {
|
|
50
|
+
permissionsMerger = [
|
|
51
|
+
...new Set([
|
|
52
|
+
...restParameter.permissions,
|
|
53
|
+
...(roleRes.find((r) => r.value === restParameter.role)?.permissions ?? []).flat()
|
|
54
|
+
])
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
// setRole(roleRes) // 由于是一次性获取叠加,此处不需要保存角色列表到全局状态
|
|
58
|
+
|
|
59
|
+
setUserInfo({
|
|
60
|
+
...restParameter,
|
|
61
|
+
permissions: permissionsMerger,
|
|
62
|
+
account: ruleForm.value.account,
|
|
63
|
+
loginDateTime
|
|
44
64
|
})
|
|
65
|
+
setToken(token)
|
|
66
|
+
setDict(dict)
|
|
45
67
|
handleRouter({ path: route.query.redirect?.toString() || '/' }, 'replace')
|
|
46
|
-
} catch {
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.log(e)
|
|
47
70
|
} finally {
|
|
48
71
|
loading.value = false
|
|
49
72
|
}
|
|
@@ -72,12 +95,7 @@ const submitForm = () => {
|
|
|
72
95
|
@keyup.enter="submitForm()"
|
|
73
96
|
>
|
|
74
97
|
<el-form-item prop="account">
|
|
75
|
-
<el-input
|
|
76
|
-
v-model="ruleForm.account"
|
|
77
|
-
placeholder="请输入账号"
|
|
78
|
-
prefix-icon="User"
|
|
79
|
-
clearable
|
|
80
|
-
/>
|
|
98
|
+
<el-input v-model="ruleForm.account" placeholder="请输入账号" prefix-icon="User" clearable />
|
|
81
99
|
</el-form-item>
|
|
82
100
|
<el-form-item prop="password">
|
|
83
101
|
<el-input
|
|
@@ -90,9 +108,7 @@ const submitForm = () => {
|
|
|
90
108
|
></el-input>
|
|
91
109
|
</el-form-item>
|
|
92
110
|
<el-form-item>
|
|
93
|
-
<el-button class="btn" type="primary" :loading="loading" @click="submitForm()">
|
|
94
|
-
登 录
|
|
95
|
-
</el-button>
|
|
111
|
+
<el-button class="btn" type="primary" :loading="loading" @click="submitForm()">登 录</el-button>
|
|
96
112
|
</el-form-item>
|
|
97
113
|
</el-form>
|
|
98
114
|
<div class="greeting">欢迎使用</div>
|
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
import { hasPermission } from '@/utils/permissions'
|
|
8
|
+
|
|
9
|
+
const { maxHeight } = useAsyncTableHeight('el_table_dict', 0)
|
|
10
|
+
const PERMISSION_EDIT = ['dict:edit']
|
|
11
|
+
const loading = ref(false)
|
|
12
|
+
const tableData = ref<DictItem[]>()
|
|
13
|
+
const predefineColors = ref([
|
|
14
|
+
'#000000',
|
|
15
|
+
'#999999',
|
|
16
|
+
'#f2f2f2',
|
|
17
|
+
'#ffffff',
|
|
18
|
+
'#65DC79',
|
|
19
|
+
'#5887F7',
|
|
20
|
+
'#E43634',
|
|
21
|
+
'#FEA822',
|
|
22
|
+
'#C9512C'
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
onActivated(() => {
|
|
26
|
+
getList()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const getList = () => {
|
|
30
|
+
loading.value = true
|
|
31
|
+
DictApi()
|
|
32
|
+
.then((res) => {
|
|
33
|
+
tableData.value = res
|
|
34
|
+
})
|
|
35
|
+
.finally(() => {
|
|
36
|
+
loading.value = false
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const handleExpandChange = (row: DictItem) => {
|
|
41
|
+
loading.value = true
|
|
42
|
+
DictDetailApi(String(row.id))
|
|
43
|
+
.then((res) => {
|
|
44
|
+
row.options = res
|
|
45
|
+
})
|
|
46
|
+
.finally(() => {
|
|
47
|
+
loading.value = false
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 保存键值对
|
|
52
|
+
const editKeyValue = (row: DictItem) => {
|
|
53
|
+
if (!row.label) {
|
|
54
|
+
ElMessage({
|
|
55
|
+
type: 'warning',
|
|
56
|
+
message: 'label 不能为空'
|
|
57
|
+
})
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
loading.value = true
|
|
61
|
+
DictChangeApi(row)
|
|
62
|
+
.then(() => {
|
|
63
|
+
ElMessage({
|
|
64
|
+
type: 'success',
|
|
65
|
+
message: '修改成功',
|
|
66
|
+
grouping: true
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
.finally(() => {
|
|
70
|
+
loading.value = false
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<template>
|
|
76
|
+
<div class="main">
|
|
77
|
+
<el-card shadow="never">
|
|
78
|
+
<template #header>
|
|
79
|
+
<div class="card_header">
|
|
80
|
+
<h3>字典管理</h3>
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
<el-table
|
|
84
|
+
class="el_table_dict"
|
|
85
|
+
:data="tableData"
|
|
86
|
+
:height="maxHeight"
|
|
87
|
+
scrollbar-always-on
|
|
88
|
+
border
|
|
89
|
+
v-loading="loading"
|
|
90
|
+
row-key="id"
|
|
91
|
+
@expand-change="handleExpandChange"
|
|
92
|
+
>
|
|
93
|
+
<el-table-column prop="dictName" label="字典名" />
|
|
94
|
+
<el-table-column prop="describe" label="说明" />
|
|
95
|
+
<el-table-column type="expand" fixed="right" label="字典值" width="100">
|
|
96
|
+
<template #default="scope">
|
|
97
|
+
<div class="table_expand animation15" style="animation-duration: 0.5s; animation-delay: 0s">
|
|
98
|
+
<div class="table_expand_top">
|
|
99
|
+
<h5>字典值列表</h5>
|
|
100
|
+
</div>
|
|
101
|
+
<el-table :data="scope.row.options" :border="true" size="small" row-key="value">
|
|
102
|
+
<el-table-column label="value" prop="value" width="100" align="center"></el-table-column>
|
|
103
|
+
<el-table-column label="label" prop="label">
|
|
104
|
+
<template #default="optionScope">
|
|
105
|
+
<el-input
|
|
106
|
+
v-model="optionScope.row.label"
|
|
107
|
+
maxlength="20"
|
|
108
|
+
:disabled="!hasPermission(PERMISSION_EDIT)"
|
|
109
|
+
/>
|
|
110
|
+
</template>
|
|
111
|
+
</el-table-column>
|
|
112
|
+
<el-table-column label="颜色" prop="displayColor">
|
|
113
|
+
<template #default="optionScope">
|
|
114
|
+
<el-color-picker
|
|
115
|
+
v-model="optionScope.row.displayColor"
|
|
116
|
+
:predefine="predefineColors"
|
|
117
|
+
:disabled="!hasPermission(PERMISSION_EDIT)"
|
|
118
|
+
/>
|
|
119
|
+
<span style="margin-left: 10px">
|
|
120
|
+
{{ optionScope.row.displayColor || '无' }}
|
|
121
|
+
</span>
|
|
122
|
+
</template>
|
|
123
|
+
</el-table-column>
|
|
124
|
+
<el-table-column prop="sequence" label="排序" width="180" align="center">
|
|
125
|
+
<template #default="optionScope">
|
|
126
|
+
<el-input-number
|
|
127
|
+
v-model="optionScope.row.sequence"
|
|
128
|
+
:min="1"
|
|
129
|
+
:max="999"
|
|
130
|
+
:step="1"
|
|
131
|
+
controls-position="right"
|
|
132
|
+
step-strictly
|
|
133
|
+
:disabled="!hasPermission(PERMISSION_EDIT)"
|
|
134
|
+
style="width: 140px"
|
|
135
|
+
/>
|
|
136
|
+
</template>
|
|
137
|
+
</el-table-column>
|
|
138
|
+
<el-table-column label="操作" width="100" align="center" fixed="right" v-permissions="PERMISSION_EDIT">
|
|
139
|
+
<template #default="optionScope">
|
|
140
|
+
<el-button type="success" size="small" @click="editKeyValue(optionScope.row)">确认修改</el-button>
|
|
141
|
+
</template>
|
|
142
|
+
</el-table-column>
|
|
143
|
+
</el-table>
|
|
144
|
+
</div>
|
|
145
|
+
</template>
|
|
146
|
+
</el-table-column>
|
|
147
|
+
</el-table>
|
|
148
|
+
</el-card>
|
|
149
|
+
</div>
|
|
150
|
+
</template>
|
|
151
|
+
|
|
152
|
+
<style scoped lang="scss">
|
|
153
|
+
.table_expand {
|
|
154
|
+
padding: 10px;
|
|
155
|
+
color: var(--jnrs-font-primary);
|
|
156
|
+
background: var(--jnrs-background-primary);
|
|
157
|
+
|
|
158
|
+
.table_expand_top {
|
|
159
|
+
position: relative;
|
|
160
|
+
|
|
161
|
+
h5 {
|
|
162
|
+
font-size: 14px;
|
|
163
|
+
margin-bottom: 10px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.table_expand_top_btn {
|
|
167
|
+
position: absolute;
|
|
168
|
+
right: 0;
|
|
169
|
+
top: 0;
|
|
170
|
+
z-index: 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
</style>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { useMenuStore } from '@/stores'
|
|
4
|
+
import { useAsyncTableHeight } from '@jnrs/vue-core/composables'
|
|
5
|
+
|
|
6
|
+
const { maxHeight } = useAsyncTableHeight('el_table_menu', 0)
|
|
7
|
+
const loading = ref(false)
|
|
8
|
+
|
|
9
|
+
const { menus } = useMenuStore()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div>
|
|
14
|
+
<el-card shadow="never">
|
|
15
|
+
<template #header>
|
|
16
|
+
<div class="card_header">
|
|
17
|
+
<h3>菜单管理</h3>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
<el-table
|
|
21
|
+
class="el_table_menu"
|
|
22
|
+
:data="menus"
|
|
23
|
+
:height="maxHeight"
|
|
24
|
+
scrollbar-always-on
|
|
25
|
+
row-key="meta.uuid"
|
|
26
|
+
border
|
|
27
|
+
v-loading="loading"
|
|
28
|
+
>
|
|
29
|
+
<el-table-column prop="meta.title" label="标题" min-width="120" header-align="center" />
|
|
30
|
+
<el-table-column prop="icon" label="图标" width="60" align="center">
|
|
31
|
+
<template #default="{ row }">
|
|
32
|
+
<el-icon v-if="row.meta.icon"><component :is="row.meta.icon" /></el-icon>
|
|
33
|
+
</template>
|
|
34
|
+
</el-table-column>
|
|
35
|
+
<el-table-column prop="path" label="路由地址" min-width="120" header-align="center" />
|
|
36
|
+
<el-table-column prop="component" label="component path" min-width="120" header-align="center" />
|
|
37
|
+
<el-table-column prop="redirect" label="redirect" min-width="120" header-align="center" />
|
|
38
|
+
<el-table-column prop="editRole" label="权限标识" min-width="120" header-align="center">
|
|
39
|
+
<template #default="{ row }">
|
|
40
|
+
<span v-if="!row.meta.permissions" style="color: #999">auto</span>
|
|
41
|
+
<span v-else>{{ row.meta.permissions }}</span>
|
|
42
|
+
</template>
|
|
43
|
+
</el-table-column>
|
|
44
|
+
<el-table-column prop="type" label="类型" width="80" align="center">
|
|
45
|
+
<template #default="{ row }">
|
|
46
|
+
<el-tag type="primary" v-if="row.path">菜单</el-tag>
|
|
47
|
+
<el-tag type="success" v-else>目录</el-tag>
|
|
48
|
+
</template>
|
|
49
|
+
</el-table-column>
|
|
50
|
+
<el-table-column prop="type" label="NoAuth" width="80" align="center">
|
|
51
|
+
<template #default="{ row }">
|
|
52
|
+
<el-tag type="danger" v-if="row.meta.noAuth">否</el-tag>
|
|
53
|
+
<el-tag type="success" v-else>是</el-tag>
|
|
54
|
+
</template>
|
|
55
|
+
</el-table-column>
|
|
56
|
+
<el-table-column prop="type" label="Global" width="80" align="center">
|
|
57
|
+
<template #default="{ row }">
|
|
58
|
+
<el-tag type="success" v-if="row.meta.global">是</el-tag>
|
|
59
|
+
<el-tag type="primary" v-else>否</el-tag>
|
|
60
|
+
</template>
|
|
61
|
+
</el-table-column>
|
|
62
|
+
</el-table>
|
|
63
|
+
</el-card>
|
|
64
|
+
</div>
|
|
65
|
+
</template>
|