create-jnrs-template-vue 1.1.4 → 1.1.5
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/components.d.ts +1 -2
- package/jnrs-template-vue/package.json +18 -17
- package/jnrs-template-vue/src/App.vue +36 -5
- package/jnrs-template-vue/src/assets/styles/init.scss +8 -3
- package/jnrs-template-vue/src/assets/styles/root.scss +7 -24
- package/jnrs-template-vue/src/layout/RouterTabs.vue +4 -3
- package/jnrs-template-vue/src/layout/SideMenu.vue +29 -20
- package/jnrs-template-vue/src/layout/TopHeader.vue +27 -4
- package/jnrs-template-vue/src/layout/index.vue +1 -1
- package/jnrs-template-vue/src/locales/en.ts +9 -0
- package/jnrs-template-vue/src/locales/index.ts +16 -0
- package/jnrs-template-vue/src/locales/zhCn.ts +9 -0
- package/jnrs-template-vue/src/main.ts +7 -1
- package/jnrs-template-vue/src/types/index.ts +1 -0
- package/jnrs-template-vue/src/utils/common.ts +58 -0
- package/jnrs-template-vue/src/views/home/index.vue +4 -1
- package/jnrs-template-vue/src/views/login/index.vue +4 -10
- package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +11 -12
- package/jnrs-template-vue/src/views/visual/index.vue +6 -1
- package/jnrs-template-vue/vite.config.ts +4 -3
- package/package.json +1 -1
|
@@ -17,8 +17,8 @@ declare module 'vue' {
|
|
|
17
17
|
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
|
|
18
18
|
ElCard: typeof import('element-plus/es')['ElCard']
|
|
19
19
|
ElCascader: typeof import('element-plus/es')['ElCascader']
|
|
20
|
-
ElCol: typeof import('element-plus/es')['ElCol']
|
|
21
20
|
ElContainer: typeof import('element-plus/es')['ElContainer']
|
|
21
|
+
ElDatePickerPanel: typeof import('element-plus/es')['ElDatePickerPanel']
|
|
22
22
|
ElForm: typeof import('element-plus/es')['ElForm']
|
|
23
23
|
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
|
24
24
|
ElHeader: typeof import('element-plus/es')['ElHeader']
|
|
@@ -28,7 +28,6 @@ declare module 'vue' {
|
|
|
28
28
|
ElMenu: typeof import('element-plus/es')['ElMenu']
|
|
29
29
|
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
|
30
30
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
|
31
|
-
ElRow: typeof import('element-plus/es')['ElRow']
|
|
32
31
|
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
|
33
32
|
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
|
34
33
|
ElTabs: typeof import('element-plus/es')['ElTabs']
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jnrs-template-vue",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "JNRS 信息化管理系统模板",
|
|
5
5
|
"author": "Talia-Tan",
|
|
6
6
|
"private": true,
|
|
@@ -22,32 +22,33 @@
|
|
|
22
22
|
"@jnrs/core": "*",
|
|
23
23
|
"@jnrs/shared": "*",
|
|
24
24
|
"@jnrs/vue-core": "*",
|
|
25
|
-
"element-plus": "^2.11.
|
|
26
|
-
"pinia": "^3.0.
|
|
27
|
-
"pinia-plugin-persistedstate": "^4.
|
|
28
|
-
"vue": "^3.5.
|
|
25
|
+
"element-plus": "^2.11.9",
|
|
26
|
+
"pinia": "^3.0.4",
|
|
27
|
+
"pinia-plugin-persistedstate": "^4.7.1",
|
|
28
|
+
"vue": "^3.5.25",
|
|
29
|
+
"vue-i18n": "^9.14.5"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"@tsconfig/node22": "^22.0.
|
|
32
|
-
"@types/node": "^22.
|
|
33
|
-
"@typescript-eslint/parser": "^8.
|
|
34
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
32
|
+
"@tsconfig/node22": "^22.0.5",
|
|
33
|
+
"@types/node": "^22.19.1",
|
|
34
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
35
|
+
"@vitejs/plugin-vue": "^6.0.2",
|
|
35
36
|
"@vue/eslint-config-prettier": "^10.2.0",
|
|
36
37
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
37
38
|
"@vue/tsconfig": "^0.8.1",
|
|
38
|
-
"eslint": "^9.
|
|
39
|
+
"eslint": "^9.39.1",
|
|
39
40
|
"eslint-plugin-vue": "~10.4.0",
|
|
40
|
-
"jiti": "^2.
|
|
41
|
+
"jiti": "^2.6.1",
|
|
41
42
|
"npm-run-all2": "^8.0.4",
|
|
42
43
|
"prettier": "3.6.2",
|
|
43
44
|
"sass": "^1.94.2",
|
|
44
|
-
"typescript": "~5.9.
|
|
45
|
-
"unplugin-auto-import": "^20.
|
|
46
|
-
"unplugin-vue-components": "^29.
|
|
47
|
-
"vite": "^7.
|
|
45
|
+
"typescript": "~5.9.3",
|
|
46
|
+
"unplugin-auto-import": "^20.3.0",
|
|
47
|
+
"unplugin-vue-components": "^29.2.0",
|
|
48
|
+
"vite": "^7.2.2",
|
|
48
49
|
"vite-plugin-compression": "^0.5.1",
|
|
49
50
|
"vite-plugin-mock": "^3.0.2",
|
|
50
|
-
"vite-plugin-vue-devtools": "^8.0.
|
|
51
|
-
"vue-tsc": "^3.1.
|
|
51
|
+
"vite-plugin-vue-devtools": "^8.0.5",
|
|
52
|
+
"vue-tsc": "^3.1.5"
|
|
52
53
|
}
|
|
53
54
|
}
|
|
@@ -1,14 +1,45 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { onMounted, watch } from 'vue'
|
|
3
|
+
import { useSystemStore } from '@/stores'
|
|
4
|
+
import { ElConfigProvider } from 'element-plus'
|
|
5
|
+
import zhCn from 'element-plus/es/locale/lang/zh-CN'
|
|
6
|
+
import en from 'element-plus/es/locale/lang/en'
|
|
7
|
+
import { useI18n } from 'vue-i18n'
|
|
8
|
+
import { changeLocales as changeLocalesForShared } from '@jnrs/shared'
|
|
9
|
+
import { changeLocales as changeLocalesForCore } from '@jnrs/core'
|
|
10
|
+
|
|
11
|
+
const { locale } = useI18n()
|
|
12
|
+
const { theme } = useSystemStore()
|
|
13
|
+
watch(
|
|
14
|
+
() => theme.locale,
|
|
15
|
+
(newValue) => {
|
|
16
|
+
locale.value = newValue
|
|
17
|
+
changeLocalesForShared(newValue)
|
|
18
|
+
changeLocalesForCore(newValue)
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
immediate: true
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
const localeMap = {
|
|
25
|
+
zhCn: zhCn,
|
|
26
|
+
en: en
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
onMounted(() => {
|
|
30
|
+
console.log(
|
|
31
|
+
'%cPowered by 🅹🅽🆁🆂 TECH',
|
|
32
|
+
`background: #f2f2c1;
|
|
5
33
|
color: #d15f2c;
|
|
6
34
|
font-weight: bold;
|
|
7
35
|
padding: 0 8px;
|
|
8
36
|
font-family: 'Helvetica Neue', sans-serif;`
|
|
9
|
-
)
|
|
37
|
+
)
|
|
38
|
+
})
|
|
10
39
|
</script>
|
|
11
40
|
|
|
12
41
|
<template>
|
|
13
|
-
<
|
|
42
|
+
<el-config-provider :locale="localeMap[theme.locale]">
|
|
43
|
+
<router-view></router-view>
|
|
44
|
+
</el-config-provider>
|
|
14
45
|
</template>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
*,
|
|
2
2
|
*::before,
|
|
3
3
|
*::after {
|
|
4
|
-
box-sizing: border-box;
|
|
5
4
|
margin: 0;
|
|
5
|
+
box-sizing: border-box;
|
|
6
6
|
font-family: Alibaba-PuHuiTi-Regular;
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -14,8 +14,13 @@ a {
|
|
|
14
14
|
input:-webkit-autofill,
|
|
15
15
|
input:-webkit-autofill:hover,
|
|
16
16
|
input:-webkit-autofill:focus,
|
|
17
|
-
input:-webkit-autofill:active
|
|
18
|
-
|
|
17
|
+
input:-webkit-autofill:active,
|
|
18
|
+
input:autofill,
|
|
19
|
+
input:-webkit-autofill-strong-password,
|
|
20
|
+
input:-webkit-autofill-strong-password-viewable,
|
|
21
|
+
input:-webkit-autofill-and-obscured {
|
|
22
|
+
-webkit-box-shadow: 0 0 0 1000px rgba(255, 0, 0, 0) inset !important;
|
|
23
|
+
background-color: rgb(255, 0, 0) !important;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
/*
|
|
@@ -1,29 +1,12 @@
|
|
|
1
|
-
/* 默认样式(浅色模式) */
|
|
2
1
|
:root {
|
|
3
|
-
//
|
|
4
|
-
// --jnrs-color-primary: oklch(0.6 0.19 41); // jnrs 橙
|
|
5
|
-
--jnrs-color-primary: oklch(0.51 0.21 264); // 蔚蓝
|
|
6
|
-
--jnrs-color-primary-06: oklch(from var(--jnrs-color-primary) l c h / 0.6);
|
|
7
|
-
--jnrs-color-primary-light: oklch(from var(--jnrs-color-primary) calc(l + 0.1) c h);
|
|
8
|
-
|
|
9
|
-
// 状态相关
|
|
10
|
-
--jnrs-color-success: oklch(0.8 0.17 147);
|
|
11
|
-
--jnrs-color-error: oklch(0.65 0.24 33);
|
|
12
|
-
|
|
13
|
-
// 框架相关
|
|
14
|
-
--jnrs-font-primary: oklch(0.3 0.01 264);
|
|
15
|
-
--jnrs-font-primary-06: oklch(from var(--jnrs-font-primary) l c h / 0.6);
|
|
16
|
-
--jnrs-card-primary: oklch(1 0 0);
|
|
17
|
-
--jnrs-background-primary: oklch(0.96 0 0);
|
|
2
|
+
// Layout 头部高度
|
|
18
3
|
--jnrs-head-height: 50px;
|
|
4
|
+
--jnrs-routerTabs-height: 30px;
|
|
19
5
|
|
|
20
|
-
// element-ui
|
|
21
|
-
--el-menu-base-level-padding: 8px !important;
|
|
6
|
+
// // element-ui 样式
|
|
7
|
+
// --el-menu-base-level-padding: 8px !important;
|
|
22
8
|
}
|
|
23
9
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// --jnrs-background-primary: oklch(0 0 0);
|
|
28
|
-
// }
|
|
29
|
-
// }
|
|
10
|
+
.el-button--primary {
|
|
11
|
+
--el-button-bg-color: var(--jnrs-color-primary);
|
|
12
|
+
}
|
|
@@ -126,20 +126,21 @@ const handleTabClick = (tab: TabsPaneContext) => {
|
|
|
126
126
|
z-index: 5;
|
|
127
127
|
:deep(.el-tabs__header) {
|
|
128
128
|
margin-bottom: 0;
|
|
129
|
-
height:
|
|
129
|
+
height: var(--jnrs-routerTabs-height);
|
|
130
130
|
}
|
|
131
131
|
:deep(.el-tabs__item) {
|
|
132
132
|
color: var(--jnrs-font-primary-06);
|
|
133
133
|
font-size: 12px;
|
|
134
|
-
height:
|
|
134
|
+
height: var(--jnrs-routerTabs-height);
|
|
135
135
|
}
|
|
136
136
|
:deep(.el-tabs__item.is-active) {
|
|
137
137
|
color: var(--jnrs-color-primary);
|
|
138
|
-
border-bottom-color: var(--jnrs-
|
|
138
|
+
border-bottom-color: var(--jnrs-color-primary);
|
|
139
139
|
}
|
|
140
140
|
:deep(.el-tabs__nav) {
|
|
141
141
|
border-radius: 0;
|
|
142
142
|
border-left: none;
|
|
143
|
+
border-top: none;
|
|
143
144
|
}
|
|
144
145
|
:deep(.el-tabs__nav-prev) {
|
|
145
146
|
color: var(--jnrs-color-primary);
|
|
@@ -3,7 +3,9 @@ import SideMenuItem from './SideMenuItem.vue'
|
|
|
3
3
|
import { storeToRefs } from 'pinia'
|
|
4
4
|
import { useRoute } from '@jnrs/vue-core/router'
|
|
5
5
|
import { useSystemStore, useMenuStore } from '@/stores'
|
|
6
|
+
import { useI18n } from 'vue-i18n'
|
|
6
7
|
|
|
8
|
+
const { t } = useI18n()
|
|
7
9
|
const systemStore = useSystemStore()
|
|
8
10
|
const { menuCollapse } = storeToRefs(systemStore)
|
|
9
11
|
const { toggleCollapse } = systemStore
|
|
@@ -15,8 +17,8 @@ const route = useRoute()
|
|
|
15
17
|
<template>
|
|
16
18
|
<el-aside class="sideMenu">
|
|
17
19
|
<div class="logo" :class="{ logo_collapse: menuCollapse }">
|
|
18
|
-
<span class="logo_text">信息化管理系统模板</span>
|
|
19
20
|
<img class="logo_img" src="@/assets/images/common/jnrs-white.svg" alt="jnrs" />
|
|
21
|
+
<span class="logo_text">{{ t('main.title') }}</span>
|
|
20
22
|
</div>
|
|
21
23
|
<el-icon
|
|
22
24
|
class="collapseBtn"
|
|
@@ -40,26 +42,30 @@ const route = useRoute()
|
|
|
40
42
|
</template>
|
|
41
43
|
|
|
42
44
|
<style lang="scss" scoped>
|
|
45
|
+
$mainFontColor: rgba(255, 255, 255, 0.8);
|
|
46
|
+
|
|
43
47
|
.sideMenu {
|
|
44
48
|
position: relative;
|
|
45
|
-
z-index:
|
|
49
|
+
z-index: 20;
|
|
46
50
|
width: auto;
|
|
47
51
|
height: 100%;
|
|
48
52
|
padding: 0 8px;
|
|
49
|
-
background:
|
|
50
|
-
|
|
53
|
+
background: oklch(0.24 0 0);
|
|
54
|
+
// background: var(--jnrs-card-primary);
|
|
55
|
+
box-shadow: 1px 0 1px var(--jnrs-font-primary-03);
|
|
51
56
|
overflow-x: hidden;
|
|
57
|
+
color: $mainFontColor;
|
|
52
58
|
|
|
53
59
|
.logo {
|
|
54
60
|
position: relative;
|
|
55
|
-
height: var(--jnrs-head-height);
|
|
61
|
+
height: calc(var(--jnrs-head-height) + var(--jnrs-routerTabs-height));
|
|
62
|
+
border-bottom: 1px solid rgb(248 248 248 / 15%);
|
|
56
63
|
|
|
57
64
|
.logo_text {
|
|
58
65
|
position: absolute;
|
|
59
|
-
|
|
66
|
+
bottom: 0;
|
|
60
67
|
left: 50%;
|
|
61
68
|
color: rgba(255, 255, 255, 1);
|
|
62
|
-
font-size: 20px;
|
|
63
69
|
font-weight: normal;
|
|
64
70
|
font-family: AlimamaShuHeiTi-Bold;
|
|
65
71
|
white-space: nowrap;
|
|
@@ -70,22 +76,23 @@ const route = useRoute()
|
|
|
70
76
|
|
|
71
77
|
.logo_img {
|
|
72
78
|
position: absolute;
|
|
73
|
-
top:
|
|
79
|
+
top: 0;
|
|
74
80
|
left: 50%;
|
|
75
|
-
width:
|
|
76
|
-
transform: translate(-50%,
|
|
81
|
+
width: 80%;
|
|
82
|
+
transform: translate(-50%, 50%);
|
|
77
83
|
transition: all 0.3s ease;
|
|
78
|
-
filter: opacity(0);
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
.logo_collapse {
|
|
83
88
|
.logo_text {
|
|
84
89
|
transform: translate(-50%, -50%) scale(0);
|
|
85
|
-
|
|
90
|
+
opacity: 0;
|
|
86
91
|
}
|
|
87
92
|
.logo_img {
|
|
88
|
-
|
|
93
|
+
width: 100%;
|
|
94
|
+
top: 50%;
|
|
95
|
+
transform: translate(-50%, -50%);
|
|
89
96
|
}
|
|
90
97
|
}
|
|
91
98
|
|
|
@@ -93,7 +100,7 @@ const route = useRoute()
|
|
|
93
100
|
position: absolute;
|
|
94
101
|
bottom: 10px;
|
|
95
102
|
font-size: 24px;
|
|
96
|
-
color:
|
|
103
|
+
color: $mainFontColor;
|
|
97
104
|
padding: 4px;
|
|
98
105
|
border-radius: 50%;
|
|
99
106
|
background-color: #424242;
|
|
@@ -130,8 +137,8 @@ const route = useRoute()
|
|
|
130
137
|
.leftSide_menu {
|
|
131
138
|
border: none;
|
|
132
139
|
min-width: 180px;
|
|
133
|
-
--el-menu-text-color:
|
|
134
|
-
--el-menu-active-color:
|
|
140
|
+
--el-menu-text-color: $mainFontColor;
|
|
141
|
+
--el-menu-active-color: $mainFontColor;
|
|
135
142
|
--el-menu-bg-color: none;
|
|
136
143
|
|
|
137
144
|
:deep(.el-menu-item) {
|
|
@@ -150,7 +157,7 @@ const route = useRoute()
|
|
|
150
157
|
|
|
151
158
|
:deep(.el-menu-item.is-active) {
|
|
152
159
|
background: var(--jnrs-color-primary) !important;
|
|
153
|
-
color:
|
|
160
|
+
color: $mainFontColor !important;
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
.el-menu-item.is-active {
|
|
@@ -166,11 +173,13 @@ const route = useRoute()
|
|
|
166
173
|
</style>
|
|
167
174
|
|
|
168
175
|
<style lang="scss">
|
|
176
|
+
$mainFontColor: rgba(255, 255, 255, 0.8);
|
|
177
|
+
|
|
169
178
|
// 弹出层样式
|
|
170
179
|
.layoutPage_leftSide_menu_popper {
|
|
171
180
|
background: #051524;
|
|
172
181
|
border: none !important;
|
|
173
|
-
left:
|
|
182
|
+
left: 74px !important;
|
|
174
183
|
border-radius: 15px;
|
|
175
184
|
padding: 0 10px;
|
|
176
185
|
.el-menu {
|
|
@@ -179,7 +188,7 @@ const route = useRoute()
|
|
|
179
188
|
.el-menu-item {
|
|
180
189
|
border-radius: 10px;
|
|
181
190
|
background-color: none;
|
|
182
|
-
color:
|
|
191
|
+
color: $mainFontColor;
|
|
183
192
|
&:hover {
|
|
184
193
|
color: var(--jnrs-color-primary);
|
|
185
194
|
background: none;
|
|
@@ -193,7 +202,7 @@ const route = useRoute()
|
|
|
193
202
|
.el-menu-item.is-active {
|
|
194
203
|
border-radius: 10px;
|
|
195
204
|
background: var(--jnrs-color-primary);
|
|
196
|
-
color:
|
|
205
|
+
color: $mainFontColor;
|
|
197
206
|
}
|
|
198
207
|
}
|
|
199
208
|
</style>
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { GlobalSetting } from '@jnrs/vue-core/components'
|
|
3
|
+
import { ref, computed } from 'vue'
|
|
2
4
|
import { storeToRefs } from 'pinia'
|
|
3
5
|
import { ElMessageBox } from 'element-plus'
|
|
4
6
|
import { handleRouter } from '@jnrs/vue-core/router'
|
|
5
7
|
import { useSystemStore, useAuthStore } from '@/stores'
|
|
6
8
|
import { useAvatar } from '@/composables/common/useAvatar'
|
|
7
9
|
import { LogoutApi } from '@/api/base/index'
|
|
10
|
+
import { getDictLabel, getDictColor } from '@/utils/common'
|
|
8
11
|
|
|
9
12
|
const { avatar } = useAvatar()
|
|
10
13
|
const { userInfo, asyncClearAuth } = useAuthStore()
|
|
11
14
|
|
|
15
|
+
const roleLabel = computed(() => getDictLabel('role', userInfo.role))
|
|
16
|
+
const roleColor = computed(() => getDictColor('role', userInfo.role))
|
|
17
|
+
|
|
12
18
|
const systemStore = useSystemStore()
|
|
13
19
|
const { documentFullscreen } = storeToRefs(systemStore)
|
|
14
20
|
const { toggleFullScreen } = systemStore
|
|
21
|
+
const globalSettingRef = ref()
|
|
15
22
|
|
|
16
23
|
const handleLogout = async () => {
|
|
17
24
|
try {
|
|
@@ -25,6 +32,10 @@ const handleLogout = async () => {
|
|
|
25
32
|
handleRouter({ name: 'Login' }, 'replace')
|
|
26
33
|
} catch {}
|
|
27
34
|
}
|
|
35
|
+
|
|
36
|
+
const showGlobalSetting = () => {
|
|
37
|
+
globalSettingRef.value.handleShow()
|
|
38
|
+
}
|
|
28
39
|
</script>
|
|
29
40
|
|
|
30
41
|
<template>
|
|
@@ -39,10 +50,13 @@ const handleLogout = async () => {
|
|
|
39
50
|
})
|
|
40
51
|
"
|
|
41
52
|
>
|
|
42
|
-
<
|
|
53
|
+
<el-icon><Platform /></el-icon>
|
|
43
54
|
</el-icon>
|
|
44
55
|
</div>
|
|
45
56
|
<div class="right">
|
|
57
|
+
<el-icon class="btn" title="全屏切换" @click="showGlobalSetting()">
|
|
58
|
+
<el-icon><Setting /></el-icon>
|
|
59
|
+
</el-icon>
|
|
46
60
|
<el-icon class="btn" title="全屏切换" @click="toggleFullScreen()">
|
|
47
61
|
<component :is="!documentFullscreen ? 'FullScreen' : 'Rank'" />
|
|
48
62
|
</el-icon>
|
|
@@ -58,7 +72,9 @@ const handleLogout = async () => {
|
|
|
58
72
|
<span class="userMenu_reference">
|
|
59
73
|
<img class="userMenu_avatar" :src="avatar" alt="avatar" />
|
|
60
74
|
<span>{{ userInfo.name }}</span>
|
|
61
|
-
<span class="userMenu_roleName" v-if="userInfo.role">
|
|
75
|
+
<span class="userMenu_roleName" :style="{ color: roleColor }" v-if="userInfo.role">
|
|
76
|
+
[{{ roleLabel }}]
|
|
77
|
+
</span>
|
|
62
78
|
<el-icon class="userMenu_icon"><arrow-down /></el-icon>
|
|
63
79
|
</span>
|
|
64
80
|
</template>
|
|
@@ -66,7 +82,9 @@ const handleLogout = async () => {
|
|
|
66
82
|
<img class="userMenu_dropdown_avatar" :src="avatar" alt="avatar" />
|
|
67
83
|
<b>
|
|
68
84
|
{{ userInfo.name }}
|
|
69
|
-
<span class="userMenu_roleName" v-if="userInfo.role">
|
|
85
|
+
<span class="userMenu_roleName" :style="{ color: roleColor }" v-if="userInfo.role">
|
|
86
|
+
[{{ roleLabel }}]
|
|
87
|
+
</span>
|
|
70
88
|
</b>
|
|
71
89
|
<div class="loginDateTime">
|
|
72
90
|
<span>登录时间</span>
|
|
@@ -92,6 +110,7 @@ const handleLogout = async () => {
|
|
|
92
110
|
</el-popover>
|
|
93
111
|
</div>
|
|
94
112
|
</div>
|
|
113
|
+
<GlobalSetting ref="globalSettingRef" />
|
|
95
114
|
</template>
|
|
96
115
|
|
|
97
116
|
<style lang="scss" scoped>
|
|
@@ -107,7 +126,7 @@ $topHoverSize: 35px;
|
|
|
107
126
|
justify-content: space-between;
|
|
108
127
|
background: var(--jnrs-card-primary);
|
|
109
128
|
padding: 0 8px;
|
|
110
|
-
box-shadow: 0 2px
|
|
129
|
+
box-shadow: 0 1px 2px var(--jnrs-font-primary-03);
|
|
111
130
|
|
|
112
131
|
.btn {
|
|
113
132
|
position: relative;
|
|
@@ -142,9 +161,13 @@ $topHoverSize: 35px;
|
|
|
142
161
|
display: flex;
|
|
143
162
|
align-items: center;
|
|
144
163
|
margin-left: 16px;
|
|
164
|
+
transition: all 0.25s ease;
|
|
145
165
|
cursor: pointer;
|
|
146
166
|
&:hover {
|
|
147
167
|
color: var(--jnrs-color-primary);
|
|
168
|
+
.userMenu_icon {
|
|
169
|
+
color: var(--jnrs-color-primary);
|
|
170
|
+
}
|
|
148
171
|
}
|
|
149
172
|
.userMenu_avatar {
|
|
150
173
|
width: $topHoverSize;
|
|
@@ -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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import 'element-plus/dist/index.css'
|
|
2
|
-
import '
|
|
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,9 @@ 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'
|
|
13
|
+
import { changeLocales as changeLocalesForShared } from '@jnrs/shared'
|
|
14
|
+
changeLocalesForShared('en')
|
|
10
15
|
|
|
11
16
|
const app = createApp(App)
|
|
12
17
|
|
|
@@ -21,4 +26,5 @@ pinia.use(piniaPluginPersistedstate)
|
|
|
21
26
|
|
|
22
27
|
app.use(pinia)
|
|
23
28
|
app.use(router)
|
|
29
|
+
app.use(locales)
|
|
24
30
|
app.mount('#app')
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
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>
|
|
@@ -5,6 +5,7 @@ import { LoginApi } from '@/api/base/index'
|
|
|
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)
|
|
@@ -34,6 +35,7 @@ const submitForm = () => {
|
|
|
34
35
|
try {
|
|
35
36
|
const res = await LoginApi(ruleForm.value)
|
|
36
37
|
const { token, dict, ...userInfo } = res
|
|
38
|
+
userInfo.loginDateTime = formatDateTime() + ' ' + formatWeekday()
|
|
37
39
|
const { asyncSetAuth } = useAuthStore()
|
|
38
40
|
await asyncSetAuth({
|
|
39
41
|
token: token,
|
|
@@ -102,14 +104,6 @@ const submitForm = () => {
|
|
|
102
104
|
</template>
|
|
103
105
|
|
|
104
106
|
<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
107
|
.main {
|
|
114
108
|
position: relative;
|
|
115
109
|
width: 100%;
|
|
@@ -147,8 +141,8 @@ $design-width: 1920; // 设计稿宽度
|
|
|
147
141
|
width: 50%;
|
|
148
142
|
height: 100%;
|
|
149
143
|
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%;
|
|
144
|
+
// background: url('@/assets/img/common/card_bg.png') no-repeat;
|
|
145
|
+
// background-size: 100% 100%;
|
|
152
146
|
text-align: center;
|
|
153
147
|
font-size: 22px;
|
|
154
148
|
|
|
@@ -3,9 +3,8 @@ import { reactive, ref } from 'vue'
|
|
|
3
3
|
import { LoginApi, PasswordChangeApi } from '@/api/base'
|
|
4
4
|
import { useAuthStore } from '@/stores'
|
|
5
5
|
import { handleRouter } from '@jnrs/vue-core/router'
|
|
6
|
-
import { isWeakPwd } from '
|
|
7
|
-
import type { FormInstance } from 'element-plus'
|
|
8
|
-
import type { RuleItem } from 'async-validator'
|
|
6
|
+
import { isWeakPwd } from '@jnrs/shared/validator'
|
|
7
|
+
import type { FormInstance, FormItemRule } from 'element-plus'
|
|
9
8
|
import { ElMessage } from 'element-plus'
|
|
10
9
|
import { LogoutApi } from '@/api/base/index'
|
|
11
10
|
|
|
@@ -21,25 +20,25 @@ const rules = reactive({
|
|
|
21
20
|
originPassword: [{ required: true, message: '请输入', trigger: 'change' }],
|
|
22
21
|
password: [
|
|
23
22
|
{ required: true, message: '请输入', trigger: 'change' },
|
|
24
|
-
{ min: 6, max: 36, message: '密码长度为 6 - 36 个字符', trigger: 'change' },
|
|
25
23
|
{
|
|
26
|
-
validator:
|
|
27
|
-
|
|
24
|
+
validator: isWeakPwd,
|
|
25
|
+
trigger: 'change'
|
|
28
26
|
}
|
|
29
|
-
]
|
|
27
|
+
],
|
|
30
28
|
repassword: [
|
|
31
29
|
{ required: true, message: '请输入', trigger: 'change' },
|
|
32
30
|
{
|
|
33
|
-
validator: (rule:
|
|
34
|
-
if (value
|
|
35
|
-
callback(
|
|
31
|
+
validator: (rule: FormItemRule, value: string, callback: (error?: string) => void) => {
|
|
32
|
+
if (value && value !== ruleForm.value.password) {
|
|
33
|
+
callback('两次密码不一致!')
|
|
36
34
|
} else {
|
|
37
35
|
callback()
|
|
38
36
|
}
|
|
39
37
|
},
|
|
40
|
-
required: false
|
|
38
|
+
required: false,
|
|
39
|
+
trigger: 'change'
|
|
41
40
|
}
|
|
42
|
-
]
|
|
41
|
+
]
|
|
43
42
|
})
|
|
44
43
|
|
|
45
44
|
const submitForm = async (formEl: FormInstance | undefined) => {
|
|
@@ -6,7 +6,7 @@ import Components from 'unplugin-vue-components/vite'
|
|
|
6
6
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
|
7
7
|
import compression from 'vite-plugin-compression'
|
|
8
8
|
import { viteMockServe } from 'vite-plugin-mock'
|
|
9
|
-
|
|
9
|
+
import vueDevTools from 'vite-plugin-vue-devtools'
|
|
10
10
|
|
|
11
11
|
const path = (url: string) => fileURLToPath(new URL(url, import.meta.url))
|
|
12
12
|
const config = loadEnv('development', './')
|
|
@@ -15,11 +15,12 @@ const isMock = config.VITE_USE_MOCK === 'true'
|
|
|
15
15
|
export default defineConfig({
|
|
16
16
|
plugins: [
|
|
17
17
|
vue(),
|
|
18
|
-
|
|
18
|
+
vueDevTools(),
|
|
19
19
|
AutoImport({
|
|
20
20
|
resolvers: [ElementPlusResolver()]
|
|
21
21
|
}),
|
|
22
22
|
Components({
|
|
23
|
+
dirs: [], // 禁用本地组件自动导入
|
|
23
24
|
resolvers: [ElementPlusResolver()]
|
|
24
25
|
}),
|
|
25
26
|
compression({
|
|
@@ -49,7 +50,7 @@ export default defineConfig({
|
|
|
49
50
|
'/api': {
|
|
50
51
|
target: 'http://' + config.VITE_BASE_URL,
|
|
51
52
|
changeOrigin: true,
|
|
52
|
-
rewrite: (path) => path.replace(/^\/api/, '')
|
|
53
|
+
rewrite: (path: string) => path.replace(/^\/api/, '')
|
|
53
54
|
},
|
|
54
55
|
'/ws': {
|
|
55
56
|
target: 'ws://' + config.VITE_BASE_URL,
|