create-jnrs-vue 1.2.11

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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/bin/create.mjs +221 -0
  4. package/bin/upgrade.mjs +40 -0
  5. package/jnrs-template-vue/.env.development +13 -0
  6. package/jnrs-template-vue/.env.production +4 -0
  7. package/jnrs-template-vue/.prettierrc.json +12 -0
  8. package/jnrs-template-vue/README.md +48 -0
  9. package/jnrs-template-vue/auto-imports.d.ts +17 -0
  10. package/jnrs-template-vue/components.d.ts +51 -0
  11. package/jnrs-template-vue/eslint.config.ts +40 -0
  12. package/jnrs-template-vue/index.html +13 -0
  13. package/jnrs-template-vue/package.json +55 -0
  14. package/jnrs-template-vue/public/favicon.ico +0 -0
  15. package/jnrs-template-vue/public/system/menu.json +137 -0
  16. package/jnrs-template-vue/src/App.vue +45 -0
  17. package/jnrs-template-vue/src/api/common/index.ts +28 -0
  18. package/jnrs-template-vue/src/api/demos/index.ts +155 -0
  19. package/jnrs-template-vue/src/api/request.ts +53 -0
  20. package/jnrs-template-vue/src/api/system/index.ts +107 -0
  21. package/jnrs-template-vue/src/api/user/index.ts +12 -0
  22. package/jnrs-template-vue/src/assets/fonts/.keep +0 -0
  23. package/jnrs-template-vue/src/assets/fonts/AlibabaPuHuiTi-Regular.woff2 +0 -0
  24. package/jnrs-template-vue/src/assets/fonts/AlimamaShuHeiTi-Bold.woff2 +0 -0
  25. package/jnrs-template-vue/src/assets/images/common/403.png +0 -0
  26. package/jnrs-template-vue/src/assets/images/common/404.png +0 -0
  27. package/jnrs-template-vue/src/assets/images/common/avatar.png +0 -0
  28. package/jnrs-template-vue/src/assets/images/common/jnrs-white.svg +1 -0
  29. package/jnrs-template-vue/src/assets/styles/animation.scss +0 -0
  30. package/jnrs-template-vue/src/assets/styles/common.scss +39 -0
  31. package/jnrs-template-vue/src/assets/styles/fonts.scss +27 -0
  32. package/jnrs-template-vue/src/assets/styles/index.scss +5 -0
  33. package/jnrs-template-vue/src/assets/styles/init.scss +41 -0
  34. package/jnrs-template-vue/src/assets/styles/root.scss +13 -0
  35. package/jnrs-template-vue/src/components/common/CardTable.vue +90 -0
  36. package/jnrs-template-vue/src/components/common/DictTag.vue +74 -0
  37. package/jnrs-template-vue/src/components/common/ImageView.vue +144 -0
  38. package/jnrs-template-vue/src/components/common/PdfView.vue +115 -0
  39. package/jnrs-template-vue/src/components/select/SelectManager.vue +26 -0
  40. package/jnrs-template-vue/src/directives/permissions.ts +28 -0
  41. package/jnrs-template-vue/src/layout/BlankLayout.vue +15 -0
  42. package/jnrs-template-vue/src/layout/RouterTabs /344/277/256/345/244/215/350/267/257/347/224/261/350/267/263/350/275/254/346/220/272/345/270/246/345/217/202/346/225/260/351/227/256/351/242/230.vue" +150 -0
  43. package/jnrs-template-vue/src/layout/RouterTabs.vue +142 -0
  44. package/jnrs-template-vue/src/layout/SideMenu.vue +208 -0
  45. package/jnrs-template-vue/src/layout/SideMenuItem.vue +38 -0
  46. package/jnrs-template-vue/src/layout/TopHeader.vue +184 -0
  47. package/jnrs-template-vue/src/layout/index.vue +71 -0
  48. package/jnrs-template-vue/src/locales/en.ts +14 -0
  49. package/jnrs-template-vue/src/locales/index.ts +23 -0
  50. package/jnrs-template-vue/src/locales/zhCn.ts +14 -0
  51. package/jnrs-template-vue/src/main.ts +31 -0
  52. package/jnrs-template-vue/src/router/index.ts +77 -0
  53. package/jnrs-template-vue/src/router/routes.ts +48 -0
  54. package/jnrs-template-vue/src/types/env.d.ts +12 -0
  55. package/jnrs-template-vue/src/types/index.ts +81 -0
  56. package/jnrs-template-vue/src/types/webSocket.ts +19 -0
  57. package/jnrs-template-vue/src/utils/file.ts +56 -0
  58. package/jnrs-template-vue/src/utils/packages.ts +116 -0
  59. package/jnrs-template-vue/src/utils/permissions.ts +16 -0
  60. package/jnrs-template-vue/src/views/common/403.vue +52 -0
  61. package/jnrs-template-vue/src/views/common/404.vue +52 -0
  62. package/jnrs-template-vue/src/views/demos/crud/index.vue +355 -0
  63. package/jnrs-template-vue/src/views/demos/simpleTable/index.vue +41 -0
  64. package/jnrs-template-vue/src/views/demos/unitTest/RequestPage.vue +137 -0
  65. package/jnrs-template-vue/src/views/demos/unitTest/index.vue +131 -0
  66. package/jnrs-template-vue/src/views/home/index.vue +9 -0
  67. package/jnrs-template-vue/src/views/lingshuSmart/editorPage.vue +9 -0
  68. package/jnrs-template-vue/src/views/login/index.vue +314 -0
  69. package/jnrs-template-vue/src/views/system/dict/index.vue +161 -0
  70. package/jnrs-template-vue/src/views/system/menu/index.vue +43 -0
  71. package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +108 -0
  72. package/jnrs-template-vue/src/views/system/mine/index.vue +83 -0
  73. package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +105 -0
  74. package/jnrs-template-vue/src/views/system/role/editDialog.vue +94 -0
  75. package/jnrs-template-vue/src/views/system/role/index.vue +41 -0
  76. package/jnrs-template-vue/src/views/visual/index.vue +143 -0
  77. package/jnrs-template-vue/tsconfig.json +25 -0
  78. package/jnrs-template-vue/vite.config.ts +71 -0
  79. package/jnrs-template-vue/viteMockServe/fail.ts +38 -0
  80. package/jnrs-template-vue/viteMockServe/file/mock-pdf.pdf +0 -0
  81. package/jnrs-template-vue/viteMockServe/file/mock-png-0.png +0 -0
  82. package/jnrs-template-vue/viteMockServe/file/mock-png-1.png +0 -0
  83. package/jnrs-template-vue/viteMockServe/file.ts +67 -0
  84. package/jnrs-template-vue/viteMockServe/index.ts +87 -0
  85. package/jnrs-template-vue/viteMockServe/json/detailsRes.json +56 -0
  86. package/jnrs-template-vue/viteMockServe/json/dictItemRes.json +27 -0
  87. package/jnrs-template-vue/viteMockServe/json/dictRes.json +21 -0
  88. package/jnrs-template-vue/viteMockServe/json/loginRes_admin.json +157 -0
  89. package/jnrs-template-vue/viteMockServe/json/loginRes_user.json +713 -0
  90. package/jnrs-template-vue/viteMockServe/json/roleRes.json +37 -0
  91. package/jnrs-template-vue/viteMockServe/json/tableRes.json +390 -0
  92. package/jnrs-template-vue/viteMockServe/success.ts +39 -0
  93. package/package.json +41 -0
@@ -0,0 +1,142 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, computed, onMounted } from 'vue'
3
+ import type { TabsPaneContext } from 'element-plus'
4
+ import type { MenuItem } from '@jnrs/vue-core'
5
+ import { handleRouter, getRoutes, useRoute } from '@jnrs/vue-core/router'
6
+
7
+ // 初始化时添加当前路由
8
+ const menuTabs = ref<MenuItem[]>([])
9
+ const activeRouterName = ref('')
10
+ const tabLabel = computed(() => {
11
+ return function (item: MenuItem) {
12
+ let label = item.meta.title
13
+ if (item.meta.fullPathTitle) {
14
+ label = item.meta.fullPathTitle.replace(',', '/')
15
+ }
16
+ return label
17
+ }
18
+ })
19
+ const route = useRoute()
20
+
21
+ // 监听路由变化,更新标签页
22
+ watch(
23
+ () => route.name,
24
+ (newName) => {
25
+ if (typeof newName !== 'string') {
26
+ return
27
+ }
28
+ if (!menuTabs.value.some((tab) => tab.name === newName)) {
29
+ addTab(newName)
30
+ }
31
+ activeRouterName.value = newName
32
+ }
33
+ )
34
+
35
+ onMounted(() => {
36
+ const routeName = route.name
37
+ if (typeof routeName === 'string') {
38
+ activeRouterName.value = routeName
39
+ }
40
+ const allRouters = getRoutes()
41
+ const baseMenu = allRouters.find((r) => isHome(r))
42
+ if (baseMenu) {
43
+ menuTabs.value.unshift(baseMenu)
44
+ }
45
+ const currentMenu = allRouters.find((r) => !isHome(r) && r.name === routeName)
46
+ if (currentMenu) {
47
+ menuTabs.value.push(currentMenu)
48
+ }
49
+ })
50
+
51
+ // 首页判断
52
+ const isHome = (d: MenuItem) => {
53
+ return d.path === '/' || d.path === ''
54
+ }
55
+
56
+ // 添加标签页
57
+ const addTab = (routerName: string) => {
58
+ if (!routerName) return
59
+ // 获取路由对象,如果路由对象不存在,则返回
60
+ const routeItem = getRoutes().find((r) => r.name === routerName)
61
+ if (!routeItem) return
62
+ // 如果标签页已存在,则直接激活标签页
63
+ if (menuTabs.value.some((tab) => tab.name === routerName)) {
64
+ activeRouterName.value = routerName
65
+ return
66
+ }
67
+ // 如果标签页不存在,则添加新标签页
68
+ menuTabs.value.push(routeItem)
69
+ activeRouterName.value = routerName
70
+ }
71
+
72
+ // 移除标签页
73
+ const removeTab = (routerName: string) => {
74
+ const currentIndex = menuTabs.value.findIndex((tab) => tab.name === routerName)
75
+ if (currentIndex === -1) return
76
+ // 删除当前标签页
77
+ menuTabs.value.splice(currentIndex, 1)
78
+ // 如果删除的是当前激活的标签页,就跳转到前一个标签页
79
+ if (activeRouterName.value === routerName) {
80
+ const nextTab = menuTabs.value[currentIndex] || menuTabs.value[currentIndex - 1]
81
+ if (nextTab.name) {
82
+ activeRouterName.value = nextTab.name
83
+ handleRouter({
84
+ name: nextTab.name
85
+ })
86
+ }
87
+ }
88
+ }
89
+
90
+ // 处理标签页点击事件
91
+ const handleTabClick = (tab: TabsPaneContext) => {
92
+ if (typeof tab.props.name !== 'string' || tab.props.name === route.name) {
93
+ return
94
+ }
95
+ handleRouter({
96
+ name: tab.props.name
97
+ })
98
+ }
99
+ </script>
100
+
101
+ <template>
102
+ <div class="routerTabs no_select">
103
+ <el-tabs v-model="activeRouterName" type="card" @tab-remove="removeTab" @tab-click="handleTabClick">
104
+ <el-tab-pane v-for="item in menuTabs" :key="item.name" :name="item.name" :closable="!isHome(item)">
105
+ <template #label>
106
+ <span>{{ tabLabel(item) }}</span>
107
+ </template>
108
+ </el-tab-pane>
109
+ </el-tabs>
110
+ </div>
111
+ </template>
112
+
113
+ <style lang="scss" scoped>
114
+ .routerTabs {
115
+ position: relative;
116
+ z-index: 5;
117
+ :deep(.el-tabs__header) {
118
+ margin-bottom: 0;
119
+ height: var(--jnrs-routerTabs-height);
120
+ }
121
+ :deep(.el-tabs__item) {
122
+ color: var(--jnrs-font-primary-06);
123
+ font-size: 12px;
124
+ height: var(--jnrs-routerTabs-height);
125
+ }
126
+ :deep(.el-tabs__item.is-active) {
127
+ color: var(--jnrs-color-primary);
128
+ border-bottom-color: var(--jnrs-color-primary);
129
+ }
130
+ :deep(.el-tabs__nav) {
131
+ border-radius: 0;
132
+ border-left: none;
133
+ border-top: none;
134
+ }
135
+ :deep(.el-tabs__nav-prev) {
136
+ color: var(--jnrs-color-primary);
137
+ }
138
+ :deep(.el-tabs__nav-next) {
139
+ color: var(--jnrs-color-primary);
140
+ }
141
+ }
142
+ </style>
@@ -0,0 +1,208 @@
1
+ <script setup lang="ts">
2
+ import SideMenuItem from './SideMenuItem.vue'
3
+ import { storeToRefs } from 'pinia'
4
+ import { useRoute } from '@jnrs/vue-core/router'
5
+ import { useSystemStore, useMenuStore } from '@jnrs/vue-core/pinia'
6
+
7
+ const systemStore = useSystemStore()
8
+ const { menuCollapse } = storeToRefs(systemStore)
9
+ const { toggleCollapse } = systemStore
10
+ const { menus } = useMenuStore()
11
+
12
+ const route = useRoute()
13
+ </script>
14
+
15
+ <template>
16
+ <el-aside class="sideMenu no_select">
17
+ <div class="logo" :class="{ logo_collapse: menuCollapse }">
18
+ <img class="logo_img" src="@/assets/images/common/jnrs-white.svg" alt="jnrs" />
19
+ <span class="logo_text">{{ $t('main.title') }}</span>
20
+ </div>
21
+ <el-icon
22
+ class="collapseBtn"
23
+ :class="{ collapseBtn_active: menuCollapse }"
24
+ title="菜单折叠"
25
+ @click="toggleCollapse()"
26
+ >
27
+ <Fold />
28
+ </el-icon>
29
+ <el-menu
30
+ class="leftSide_menu"
31
+ popper-class="layoutPage_leftSide_menu_popper"
32
+ :router="true"
33
+ :default-active="route.name"
34
+ :collapse="menuCollapse"
35
+ :unique-opened="true"
36
+ >
37
+ <SideMenuItem v-for="item in menus" :listItem="item" :key="item.meta.uuid" />
38
+ </el-menu>
39
+ </el-aside>
40
+ </template>
41
+
42
+ <style lang="scss" scoped>
43
+ $mainFontColor: rgba(255, 255, 255, 0.85);
44
+
45
+ .sideMenu {
46
+ position: relative;
47
+ z-index: 20;
48
+ width: auto;
49
+ height: 100%;
50
+ padding: 0 8px;
51
+ background: oklch(0.24 0 0);
52
+ // background: var(--jnrs-card-primary);
53
+ box-shadow: 1px 0 1px var(--jnrs-font-primary-03);
54
+ overflow-x: hidden;
55
+ color: $mainFontColor;
56
+
57
+ .logo {
58
+ position: relative;
59
+ height: calc(var(--jnrs-head-height) + var(--jnrs-routerTabs-height));
60
+ border-bottom: 1px solid rgb(248 248 248 / 15%);
61
+ filter: brightness(0.8);
62
+
63
+ .logo_text {
64
+ position: absolute;
65
+ bottom: 0;
66
+ left: 50%;
67
+ color: rgba(255, 255, 255, 1);
68
+ font-size: 18px;
69
+ font-weight: normal;
70
+ font-family: AlimamaShuHeiTi-Bold;
71
+ white-space: nowrap;
72
+ transform: translate(-50%, -50%);
73
+ transition: all 0.3s ease;
74
+ filter: opacity(1);
75
+ }
76
+
77
+ .logo_img {
78
+ position: absolute;
79
+ top: 0;
80
+ left: 50%;
81
+ width: 70%;
82
+ transform: translate(-50%, 50%);
83
+ transition: all 0.3s ease;
84
+ }
85
+ }
86
+
87
+ .logo_collapse {
88
+ .logo_text {
89
+ transform: translate(-50%, -50%) scale(0);
90
+ opacity: 0;
91
+ }
92
+ .logo_img {
93
+ width: 100%;
94
+ top: 50%;
95
+ transform: translate(-50%, -50%);
96
+ }
97
+ }
98
+
99
+ .collapseBtn {
100
+ position: absolute;
101
+ bottom: 10px;
102
+ font-size: 24px;
103
+ color: $mainFontColor;
104
+ padding: 4px;
105
+ border-radius: 50%;
106
+ background-color: #424242;
107
+ transition: all 0.25s ease;
108
+ cursor: pointer;
109
+ &:hover {
110
+ color: var(--jnrs-color-primary);
111
+ }
112
+ }
113
+
114
+ .collapseBtn_active {
115
+ transform: rotate(-180deg);
116
+ }
117
+
118
+ :deep(.el-sub-menu__title) {
119
+ &:hover {
120
+ background-color: unset;
121
+ }
122
+ }
123
+
124
+ .el-menu--collapse {
125
+ min-width: auto !important;
126
+
127
+ .el-sub-menu.is-active {
128
+ border-radius: 10px;
129
+ background: var(--jnrs-color-primary);
130
+ }
131
+
132
+ :deep(.el-menu-item span) {
133
+ display: none;
134
+ }
135
+ }
136
+
137
+ .leftSide_menu {
138
+ border: none;
139
+ min-width: 200px;
140
+ --el-menu-text-color: $mainFontColor;
141
+ --el-menu-active-color: $mainFontColor;
142
+ --el-menu-bg-color: none;
143
+
144
+ :deep(.el-menu-item) {
145
+ border-radius: 10px;
146
+ span {
147
+ overflow: hidden;
148
+ white-space: nowrap;
149
+ text-overflow: ellipsis;
150
+ }
151
+
152
+ &:hover {
153
+ background: none !important;
154
+ color: var(--jnrs-color-primary);
155
+ }
156
+ }
157
+
158
+ :deep(.el-menu-item.is-active) {
159
+ background: var(--jnrs-color-primary) !important;
160
+ color: $mainFontColor !important;
161
+ }
162
+
163
+ .el-menu-item.is-active {
164
+ background: var(--jnrs-color-primary);
165
+ color: #fff !important;
166
+
167
+ span {
168
+ color: #fff !important;
169
+ }
170
+ }
171
+ }
172
+ }
173
+ </style>
174
+
175
+ <style lang="scss">
176
+ $mainFontColor: rgba(255, 255, 255, 0.8);
177
+
178
+ // 弹出层样式
179
+ .layoutPage_leftSide_menu_popper {
180
+ background: #051524;
181
+ border: none !important;
182
+ left: 74px !important;
183
+ border-radius: 15px;
184
+ padding: 0 10px;
185
+ .el-menu {
186
+ background: none;
187
+ }
188
+ .el-menu-item {
189
+ border-radius: 10px;
190
+ background-color: none;
191
+ color: $mainFontColor;
192
+ &:hover {
193
+ color: var(--jnrs-color-primary);
194
+ background: none;
195
+ }
196
+ span {
197
+ overflow: hidden;
198
+ white-space: nowrap;
199
+ text-overflow: ellipsis;
200
+ }
201
+ }
202
+ .el-menu-item.is-active {
203
+ border-radius: 10px;
204
+ background: var(--jnrs-color-primary);
205
+ color: $mainFontColor;
206
+ }
207
+ }
208
+ </style>
@@ -0,0 +1,38 @@
1
+ <script setup lang="ts">
2
+ import { useSystemStore } from '@jnrs/vue-core/pinia'
3
+ import type { MenuItem } from '@jnrs/vue-core'
4
+
5
+ defineProps<{
6
+ listItem: MenuItem
7
+ }>()
8
+
9
+ const { menuCollapse } = useSystemStore()
10
+ </script>
11
+
12
+ <template>
13
+ <!-- 一级路由 -->
14
+ <el-menu-item :index="listItem.name" :route="listItem.path" v-if="!listItem.children">
15
+ <el-icon v-if="listItem.meta.icon">
16
+ <component :is="listItem.meta.icon" />
17
+ </el-icon>
18
+ <el-badge :show-zero="false" :value="listItem.meta.todoCount" :offset="[7, 17]" color="#EC5545">
19
+ <span>{{ listItem.meta.title }}</span>
20
+ </el-badge>
21
+ </el-menu-item>
22
+ <el-sub-menu :index="listItem.meta.uuid" v-else>
23
+ <!-- 一级目录 -->
24
+ <template #title>
25
+ <el-badge
26
+ :show-zero="false"
27
+ :is-dot="listItem.meta.todoCount && listItem.meta.todoCount > 0"
28
+ :offset="[menuCollapse ? -5 : -10, 20]"
29
+ color="#EC5545"
30
+ >
31
+ <el-icon v-if="listItem.meta.icon"><component :is="listItem.meta.icon" /></el-icon>
32
+ </el-badge>
33
+ <span>{{ listItem.meta.title }}</span>
34
+ </template>
35
+ <!-- 二级 -->
36
+ <SideMenuItem v-for="item in listItem.children" :listItem="item" :key="item.meta.uuid" />
37
+ </el-sub-menu>
38
+ </template>
@@ -0,0 +1,184 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { storeToRefs } from 'pinia'
4
+ import { ElMessageBox } from 'element-plus'
5
+ import { handleRouter } from '@jnrs/vue-core/router'
6
+ import { GlobalSetting } from '@jnrs/vue-core/components'
7
+ import { useSystemStore, useAuthStore, useMenuStore } from '@jnrs/vue-core/pinia'
8
+ import { LogoutApi } from '@/api/system'
9
+ import ImageView from '@/components/common/ImageView.vue'
10
+ import DictTag from '@/components/common/DictTag.vue'
11
+
12
+ const { userInfo, clearAuth } = useAuthStore()
13
+ const { clearMenu } = useMenuStore()
14
+
15
+ const systemStore = useSystemStore()
16
+ const { documentFullscreen } = storeToRefs(systemStore)
17
+ const { toggleFullScreen } = systemStore
18
+ const globalSettingRef = ref()
19
+
20
+ const handleLogout = async () => {
21
+ try {
22
+ await ElMessageBox.confirm('确定要退出系统吗?', '操作确认', {
23
+ confirmButtonText: '退出',
24
+ cancelButtonText: '取消',
25
+ type: 'warning'
26
+ })
27
+ await LogoutApi()
28
+ clearAuth()
29
+ clearMenu()
30
+ handleRouter({ name: 'Login' }, 'replace')
31
+ } catch {}
32
+ }
33
+
34
+ const showGlobalSetting = () => {
35
+ globalSettingRef.value.handleShow()
36
+ }
37
+ </script>
38
+
39
+ <template>
40
+ <div class="topHeader no_select">
41
+ <div class="left">
42
+ <el-icon
43
+ class="topBarBtn"
44
+ title="可视化看板"
45
+ @click="
46
+ handleRouter({
47
+ name: 'Visual'
48
+ })
49
+ "
50
+ >
51
+ <el-icon><Platform /></el-icon>
52
+ </el-icon>
53
+ </div>
54
+ <div class="right">
55
+ <el-icon class="topBarBtn" title="全局偏好设置" @click="showGlobalSetting()">
56
+ <el-icon><Setting /></el-icon>
57
+ </el-icon>
58
+ <el-icon class="topBarBtn" title="全屏切换" @click="toggleFullScreen()">
59
+ <component :is="!documentFullscreen ? 'FullScreen' : 'Rank'" />
60
+ </el-icon>
61
+ <!-- 头像和用户名 -->
62
+ <el-popover placement="bottom" trigger="click" :teleported="false" :width="260" :hide-after="0">
63
+ <template #reference>
64
+ <span class="userMenu_reference">
65
+ <ImageView class="userMenu_avatar" :loadKeys="userInfo.avatarFileName" v-if="userInfo?.avatarFileName" />
66
+ <img class="userMenu_avatar" src="@/assets/images/common/avatar.png" v-else />
67
+ <span>{{ userInfo?.name }}</span>
68
+ <DictTag dictName="role" :value="userInfo.role" v-if="userInfo?.role" />
69
+ <el-icon class="userMenu_icon"><arrow-down /></el-icon>
70
+ </span>
71
+ </template>
72
+ <div class="userMenu_dropdown">
73
+ <ImageView class="userMenu_dropdown_avatar" :loadKeys="userInfo?.avatarFileName" />
74
+ <div class="loginDateTime" v-if="userInfo?.loginDateTime">
75
+ <span>登录时间</span>
76
+ <p>{{ userInfo?.loginDateTime }}</p>
77
+ </div>
78
+ <div class="userMenu_dropdown_btn">
79
+ <el-button
80
+ type="primary"
81
+ icon="UserFilled"
82
+ @click="
83
+ handleRouter({
84
+ name: 'SystemMine'
85
+ })
86
+ "
87
+ >
88
+ 个人中心
89
+ </el-button>
90
+ <el-button type="danger" icon="SwitchButton" @click="handleLogout()">退出系统</el-button>
91
+ </div>
92
+ </div>
93
+ </el-popover>
94
+ </div>
95
+ </div>
96
+ <GlobalSetting ref="globalSettingRef" />
97
+ </template>
98
+
99
+ <style lang="scss" scoped>
100
+ $topHoverSize: 35px;
101
+
102
+ .topHeader {
103
+ position: relative;
104
+ z-index: 15;
105
+ width: 100%;
106
+ height: 100%;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: space-between;
110
+ background: var(--jnrs-card-primary);
111
+ padding: 0 8px;
112
+ box-shadow: 0 1px 2px var(--jnrs-font-primary-03);
113
+
114
+ .topBarBtn {
115
+ font-size: 22px;
116
+ }
117
+
118
+ .left,
119
+ .right {
120
+ display: flex;
121
+ align-items: center;
122
+ }
123
+
124
+ .userMenu_reference {
125
+ display: flex;
126
+ align-items: center;
127
+ margin-left: 16px;
128
+ cursor: pointer;
129
+ &:hover {
130
+ span {
131
+ transition: all 0.25s ease;
132
+ color: var(--jnrs-color-primary) !important;
133
+ }
134
+ .userMenu_icon {
135
+ transition: all 0.25s ease;
136
+ color: var(--jnrs-color-primary);
137
+ }
138
+ }
139
+ .userMenu_avatar {
140
+ width: $topHoverSize !important;
141
+ height: $topHoverSize !important;
142
+ border-radius: 50%;
143
+ margin-right: 4px;
144
+ overflow: hidden;
145
+ background: var(--jnrs-background-head);
146
+ }
147
+ .userMenu_icon {
148
+ color: var(--jnrs-font-primary-06);
149
+ }
150
+ }
151
+ .userMenu_dropdown {
152
+ text-align: center;
153
+
154
+ .userMenu_dropdown_avatar {
155
+ width: 120px !important;
156
+ height: 120px !important;
157
+ margin: 0 auto 8px;
158
+ background: var(--jnrs-background-head);
159
+ border-radius: 5px;
160
+ }
161
+ img {
162
+ width: 80px;
163
+ height: 80px;
164
+ border-radius: 50%;
165
+ object-fit: none;
166
+ object-position: center;
167
+ }
168
+ b {
169
+ display: block;
170
+ font-size: 16px;
171
+ color: var(--jnrs-font-primary);
172
+ }
173
+ .loginDateTime {
174
+ color: var(--jnrs-font-primary-06);
175
+ }
176
+ .userMenu_dropdown_btn {
177
+ margin-top: 8px;
178
+ }
179
+ }
180
+ .userMenu_roleName {
181
+ color: var(--jnrs-color-primary);
182
+ }
183
+ }
184
+ </style>
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, toRefs } from 'vue'
3
+ import SideMenu from './SideMenu.vue'
4
+ import TopHeader from './TopHeader.vue'
5
+ import RouterTabs from './RouterTabs.vue'
6
+ import { useAuthStore, useSystemStore } from '@jnrs/vue-core/pinia'
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
+ )
29
+ </script>
30
+
31
+ <template>
32
+ <el-container class="layout_container">
33
+ <SideMenu />
34
+ <el-container>
35
+ <el-header class="layout_header">
36
+ <TopHeader />
37
+ </el-header>
38
+ <RouterTabs />
39
+ <el-main>
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>
51
+ </el-main>
52
+ </el-container>
53
+ </el-container>
54
+ </template>
55
+
56
+ <style lang="scss" scoped>
57
+ .layout_header {
58
+ width: 100%;
59
+ height: var(--jnrs-head-height);
60
+ padding: 0;
61
+ }
62
+
63
+ .layout_container {
64
+ width: 100%;
65
+ height: 100%;
66
+
67
+ .el-main {
68
+ padding: 16px;
69
+ }
70
+ }
71
+ </style>
@@ -0,0 +1,14 @@
1
+ export default {
2
+ main: {
3
+ title: 'Management System'
4
+ },
5
+ login: {
6
+ title: 'Management System',
7
+ formTitle: 'Login',
8
+ formAccount: 'Please enter your account',
9
+ formPassword: 'Please enter your password',
10
+ formBtn: 'Log In',
11
+ greeting: 'Welcome'
12
+ },
13
+ layout: {}
14
+ }
@@ -0,0 +1,23 @@
1
+ import { createI18n } from 'vue-i18n'
2
+ import zhCn from './zhCn'
3
+ import en from './en'
4
+ import { zhCn as vueCore_zhCn, en as vueCore_en } from '@jnrs/vue-core/locales'
5
+
6
+ const i18n = createI18n({
7
+ legacy: false, // legacy 为 false 则启用 Composition API 模式
8
+ globalInjection: true, // 允许在模板中使用 $t
9
+ locale: 'zhCn',
10
+ fallbackLocale: 'en',
11
+ messages: {
12
+ zhCn: {
13
+ ...zhCn,
14
+ ...vueCore_zhCn
15
+ },
16
+ en: {
17
+ ...en,
18
+ ...vueCore_en
19
+ }
20
+ }
21
+ })
22
+
23
+ export default i18n
@@ -0,0 +1,14 @@
1
+ export default {
2
+ main: {
3
+ title: '信息化管理系统'
4
+ },
5
+ login: {
6
+ title: '信息化管理系统',
7
+ formTitle: '登录',
8
+ formAccount: '请输入账号',
9
+ formPassword: '请输入密码',
10
+ formBtn: '登 录',
11
+ greeting: '欢迎使用'
12
+ },
13
+ layout: {}
14
+ }