create-jnrs-template-vue 1.1.3 → 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.
Files changed (41) hide show
  1. package/README.md +5 -5
  2. package/bin/create.mjs +1 -1
  3. package/jnrs-template-vue/README.md +2 -2
  4. package/jnrs-template-vue/components.d.ts +1 -0
  5. package/jnrs-template-vue/package.json +23 -23
  6. package/jnrs-template-vue/public/system/menu.json +8 -4
  7. package/jnrs-template-vue/src/App.vue +36 -5
  8. package/jnrs-template-vue/src/assets/fonts/.keep +0 -0
  9. package/jnrs-template-vue/src/assets/fonts/AlibabaPuHuiTi-Regular.woff2 +0 -0
  10. package/jnrs-template-vue/src/assets/fonts/AlimamaShuHeiTi-Bold.woff2 +0 -0
  11. package/jnrs-template-vue/src/assets/images/common/jnrs-white.svg +1 -0
  12. package/jnrs-template-vue/src/assets/styles/fonts.scss +24 -0
  13. package/jnrs-template-vue/src/assets/styles/init.scss +38 -0
  14. package/jnrs-template-vue/src/assets/styles/main.scss +27 -0
  15. package/jnrs-template-vue/src/assets/styles/root.scss +12 -0
  16. package/jnrs-template-vue/src/layout/BlankLayout.vue +2 -1
  17. package/jnrs-template-vue/src/layout/RouterTabs.vue +14 -9
  18. package/jnrs-template-vue/src/layout/SideMenu.vue +95 -27
  19. package/jnrs-template-vue/src/layout/SideMenuItem.vue +4 -4
  20. package/jnrs-template-vue/src/layout/TopHeader.vue +85 -86
  21. package/jnrs-template-vue/src/layout/index.vue +12 -8
  22. package/jnrs-template-vue/src/locales/en.ts +9 -0
  23. package/jnrs-template-vue/src/locales/index.ts +16 -0
  24. package/jnrs-template-vue/src/locales/zhCn.ts +9 -0
  25. package/jnrs-template-vue/src/main.ts +8 -2
  26. package/jnrs-template-vue/src/router/routes.ts +16 -16
  27. package/jnrs-template-vue/src/types/index.ts +1 -0
  28. package/jnrs-template-vue/src/utils/common.ts +58 -0
  29. package/jnrs-template-vue/src/views/home/index.vue +4 -1
  30. package/jnrs-template-vue/src/views/login/index.vue +222 -38
  31. package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +11 -12
  32. package/jnrs-template-vue/src/views/visual/index.vue +6 -1
  33. package/jnrs-template-vue/tsconfig.json +6 -1
  34. package/jnrs-template-vue/vite.config.ts +2 -2
  35. package/jnrs-template-vue/viteMockServe/index.ts +5 -0
  36. package/package.json +1 -1
  37. package/jnrs-template-vue/src/assets/styles/base.css +0 -28
  38. package/jnrs-template-vue/src/assets/styles/main.css +0 -1
  39. package/jnrs-template-vue/src/utils/storage.ts +0 -7
  40. package/jnrs-template-vue/src/utils/validate.ts +0 -321
  41. package/jnrs-template-vue/src/utils/validator.ts +0 -153
@@ -1,61 +1,245 @@
1
1
  <script setup lang="ts">
2
- import { reactive, ref } from 'vue'
3
2
  import type { FormInstance, FormRules } from 'element-plus'
3
+ import { ref } from 'vue'
4
4
  import { LoginApi } from '@/api/base/index'
5
5
  import { useAuthStore } from '@/stores'
6
6
  import { handleRouter, useRoute } from '@jnrs/vue-core/router'
7
+ import { isWeakPwd } from '@jnrs/shared/validator'
8
+ import { formatDateTime, formatWeekday } from '@jnrs/shared'
7
9
 
8
10
  const route = useRoute()
11
+ const loading = ref(false)
9
12
 
13
+ // 表单 ref
14
+ const ruleFormRef = ref<FormInstance>()
10
15
  // 表单数据
11
- const form = reactive({
16
+ const ruleForm = ref({
12
17
  account: '',
13
18
  password: ''
14
19
  })
15
-
16
20
  // 校验规则
17
- const rules = reactive<FormRules>({
21
+ const rules = ref<FormRules>({
18
22
  account: [{ required: true, message: '请输入用户名', trigger: 'change' }],
19
- password: [{ required: true, message: '请输入密码', trigger: 'change' }]
23
+ password: [
24
+ { required: true, message: '请输入密码', trigger: 'change' },
25
+ { validator: isWeakPwd, trigger: 'change' }
26
+ ]
20
27
  })
21
28
 
22
- // 表单 ref
23
- const formRef = ref<FormInstance>()
24
-
25
29
  // 提交函数
26
- const loading = ref(false)
27
- const submitForm = async (formEl: FormInstance | undefined) => {
28
- if (!formEl) return
29
- try {
30
+ const submitForm = () => {
31
+ if (!ruleFormRef.value) return
32
+ ruleFormRef.value.validate(async (valid) => {
33
+ if (!valid) return
30
34
  loading.value = true
31
- await formEl.validate()
32
-
33
- const res = await LoginApi(form)
34
- const { token, dict, ...userInfo } = res
35
-
36
- const { asyncSetAuth } = useAuthStore()
37
- await asyncSetAuth({
38
- token: token,
39
- dict: dict,
40
- userInfo: userInfo
41
- })
42
-
43
- handleRouter({ path: route.query.redirect?.toString() || '/' }, 'replace')
44
- } catch {
45
- } finally {
46
- loading.value = false
47
- }
35
+ try {
36
+ const res = await LoginApi(ruleForm.value)
37
+ const { token, dict, ...userInfo } = res
38
+ userInfo.loginDateTime = formatDateTime() + ' ' + formatWeekday()
39
+ const { asyncSetAuth } = useAuthStore()
40
+ await asyncSetAuth({
41
+ token: token,
42
+ dict: dict,
43
+ userInfo: userInfo
44
+ })
45
+ handleRouter({ path: route.query.redirect?.toString() || '/' }, 'replace')
46
+ } catch {
47
+ } finally {
48
+ loading.value = false
49
+ }
50
+ })
48
51
  }
49
52
  </script>
50
53
 
51
54
  <template>
52
- <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
53
- <el-form-item label="用户名" prop="account">
54
- <el-input v-model="form.account" />
55
- </el-form-item>
56
- <el-form-item label="密码" prop="password">
57
- <el-input v-model="form.password" type="password" />
58
- </el-form-item>
59
- </el-form>
60
- <el-button type="primary" :loading="loading" @click="submitForm(formRef)">提交</el-button>
55
+ <div class="main">
56
+ <div class="card">
57
+ <div class="card_left">
58
+ <div class="card_left_mid">
59
+ <img class="logo" src="@/assets/images/common/jnrs-white.svg" alt="JNRS" />
60
+ <h5 class="title">信息化管理系统模板</h5>
61
+ </div>
62
+ </div>
63
+ <div class="card_right">
64
+ <div class="card_right_mid">
65
+ <h1 class="title">登录</h1>
66
+ <el-form
67
+ class="form"
68
+ ref="ruleFormRef"
69
+ :model="ruleForm"
70
+ :rules="rules"
71
+ size="large"
72
+ @keyup.enter="submitForm()"
73
+ >
74
+ <el-form-item prop="account">
75
+ <el-input
76
+ v-model="ruleForm.account"
77
+ placeholder="请输入账号"
78
+ prefix-icon="User"
79
+ clearable
80
+ />
81
+ </el-form-item>
82
+ <el-form-item prop="password">
83
+ <el-input
84
+ v-model="ruleForm.password"
85
+ type="password"
86
+ autocomplete="off"
87
+ placeholder="请输入密码"
88
+ prefix-icon="Lock"
89
+ show-password
90
+ ></el-input>
91
+ </el-form-item>
92
+ <el-form-item>
93
+ <el-button class="btn" type="primary" :loading="loading" @click="submitForm()">
94
+ 登 录
95
+ </el-button>
96
+ </el-form-item>
97
+ </el-form>
98
+ <div class="greeting">欢迎使用</div>
99
+ </div>
100
+ <div class="card_right_copyright">Powered by JNRS TECH 2026</div>
101
+ </div>
102
+ </div>
103
+ </div>
61
104
  </template>
105
+
106
+ <style scoped lang="scss">
107
+ .main {
108
+ position: relative;
109
+ width: 100%;
110
+ height: 100%;
111
+ background: radial-gradient(circle at center, #232a4c, #000);
112
+
113
+ &::before {
114
+ content: '';
115
+ position: absolute;
116
+ width: 100%;
117
+ height: 100%;
118
+ background-image: radial-gradient(transparent 50%, rgba(255, 255, 255, 0.05) 50%);
119
+ background-size: 10px 10px;
120
+ }
121
+ }
122
+
123
+ .card {
124
+ display: flex;
125
+ align-items: center;
126
+ position: absolute;
127
+ left: 50%;
128
+ top: 50%;
129
+ transform: translate(-50%, -50%);
130
+ width: 1000px;
131
+ height: 500px;
132
+ border-radius: 15px;
133
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
134
+ background: radial-gradient(circle at center, #232a4c, #000);
135
+ overflow: hidden;
136
+
137
+ .card_left {
138
+ display: flex;
139
+ justify-content: center;
140
+ align-items: center;
141
+ width: 50%;
142
+ height: 100%;
143
+ box-shadow: 5px 0 10px rgba(0, 0, 0, 0.5);
144
+ // background: url('@/assets/img/common/card_bg.png') no-repeat;
145
+ // background-size: 100% 100%;
146
+ text-align: center;
147
+ font-size: 22px;
148
+
149
+ .card_left_mid {
150
+ width: 50%;
151
+
152
+ .logo {
153
+ width: 100%;
154
+ margin-bottom: 20px;
155
+ filter: opacity(0.5);
156
+ }
157
+
158
+ .title {
159
+ font-size: 24px;
160
+ font-weight: normal;
161
+ font-family: AlimamaShuHeiTi-Bold;
162
+ color: rgba(255, 255, 255, 0.5);
163
+ }
164
+ }
165
+ }
166
+
167
+ .card_right {
168
+ width: 50%;
169
+ height: 100%;
170
+ display: flex;
171
+ align-items: center;
172
+ justify-content: center;
173
+ position: relative;
174
+
175
+ &::after {
176
+ content: '';
177
+ position: absolute;
178
+ z-index: -1;
179
+ left: 0;
180
+ top: 0;
181
+ width: 100%;
182
+ height: 100%;
183
+ background: rgba(255, 255, 255, 0.7);
184
+ backdrop-filter: blur(4px);
185
+ -webkit-backdrop-filter: blur(4px);
186
+ }
187
+
188
+ .card_right_mid {
189
+ width: 50%;
190
+
191
+ .title {
192
+ text-align: center;
193
+ color: var(--jnrs-color-primary);
194
+ }
195
+
196
+ .form {
197
+ margin-top: 50px;
198
+ }
199
+
200
+ .btn {
201
+ width: 100%;
202
+ background: var(--jnrs-color-primary);
203
+ border: none;
204
+ transition: all 0.25s ease;
205
+
206
+ &:hover {
207
+ background: var(--jnrs-color-primary-light);
208
+ }
209
+ }
210
+
211
+ .greeting {
212
+ position: relative;
213
+ color: var(--jnrs-color-primary-06);
214
+ font-size: 14px;
215
+ text-align: center;
216
+
217
+ &::before,
218
+ &::after {
219
+ content: '';
220
+ position: absolute;
221
+ right: 0;
222
+ top: 50%;
223
+ width: 35%;
224
+ height: 1px;
225
+ background: var(--jnrs-color-primary-06);
226
+ transform: translateY(-50%);
227
+ filter: opacity(0.25);
228
+ }
229
+
230
+ &::after {
231
+ left: 0;
232
+ top: 50%;
233
+ }
234
+ }
235
+ }
236
+
237
+ .card_right_copyright {
238
+ position: absolute;
239
+ bottom: 5px;
240
+ font-size: 12px;
241
+ color: rgba(0, 0, 0, 0.1);
242
+ }
243
+ }
244
+ }
245
+ </style>
@@ -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 '@/utils/validator'
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: (rule: RuleItem, value: string | undefined, callback: (error?: Error) => void) =>
27
- isWeakPwd(rule, value, callback)
24
+ validator: isWeakPwd,
25
+ trigger: 'change'
28
26
  }
29
- ] as RuleItem[],
27
+ ],
30
28
  repassword: [
31
29
  { required: true, message: '请输入', trigger: 'change' },
32
30
  {
33
- validator: (rule: RuleItem, value: string | undefined, callback: (error?: Error) => void) => {
34
- if (value != '' && value !== ruleForm.value.password) {
35
- callback(new Error('两次密码不一致!'))
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
- ] as RuleItem[]
41
+ ]
43
42
  })
44
43
 
45
44
  const submitForm = async (formEl: FormInstance | undefined) => {
@@ -20,4 +20,9 @@ const goBack = () => {
20
20
  </div>
21
21
  </template>
22
22
 
23
- <style lang="scss" scoped></style>
23
+ <style lang="scss" scoped>
24
+ @use 'sass:math';
25
+ @function px2vw($px) {
26
+ @return math.div($px, 1920) * 100vw;
27
+ }
28
+ </style>
@@ -19,6 +19,11 @@
19
19
  "@/*": ["src/*"]
20
20
  }
21
21
  },
22
- "include": ["src/**/*", "vite.config.ts"],
22
+ "include": [
23
+ "src/**/*",
24
+ "vite.config.ts",
25
+ "../../../packages/shared/src/validate.ts",
26
+ "../../../packages/shared/src/validator.ts"
27
+ ],
23
28
  "exclude": ["node_modules", "dist"]
24
29
  }
@@ -1,5 +1,4 @@
1
1
  import { fileURLToPath, URL } from 'node:url'
2
-
3
2
  import { defineConfig, loadEnv } from 'vite'
4
3
  import vue from '@vitejs/plugin-vue'
5
4
  import AutoImport from 'unplugin-auto-import/vite'
@@ -21,6 +20,7 @@ export default defineConfig({
21
20
  resolvers: [ElementPlusResolver()]
22
21
  }),
23
22
  Components({
23
+ dirs: [], // 禁用本地组件自动导入
24
24
  resolvers: [ElementPlusResolver()]
25
25
  }),
26
26
  compression({
@@ -50,7 +50,7 @@ export default defineConfig({
50
50
  '/api': {
51
51
  target: 'http://' + config.VITE_BASE_URL,
52
52
  changeOrigin: true,
53
- rewrite: (path) => path.replace(/^\/api/, '')
53
+ rewrite: (path: string) => path.replace(/^\/api/, '')
54
54
  },
55
55
  '/ws': {
56
56
  target: 'ws://' + config.VITE_BASE_URL,
@@ -6,6 +6,11 @@ const res_success = {
6
6
  msg: '操作成功'
7
7
  }
8
8
 
9
+ const res_fail = {
10
+ code: 1,
11
+ msg: '操作失败'
12
+ }
13
+
9
14
  export default [
10
15
  // 获取菜单
11
16
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-jnrs-template-vue",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "巨能前端工程化开发,Vue 项目模板脚手架",
5
5
  "keywords": [
6
6
  "vue",
@@ -1,28 +0,0 @@
1
- *,
2
- *::before,
3
- *::after {
4
- box-sizing: border-box;
5
- margin: 0;
6
- font-weight: normal;
7
- }
8
-
9
- body {
10
- width: 100vw;
11
- color: #14161a;
12
- height: 100vh;
13
- background: #f2f2f2;
14
- transition:
15
- color 1.5s,
16
- background-color 0.5s;
17
- line-height: 1.6;
18
- }
19
-
20
- #app {
21
- min-width: 1280px;
22
- height: 100%;
23
- font-weight: normal;
24
- }
25
-
26
- a {
27
- text-decoration: none;
28
- }
@@ -1 +0,0 @@
1
- @import './base.css';
@@ -1,7 +0,0 @@
1
- /**
2
- * @Author : TanRui
3
- * @WeChat : Tan578853789
4
- * @File : storage.ts
5
- * @Date : 2025/09/30
6
- * @Desc. : 本地存储
7
- */