create-jnrs-template-vue 1.1.6 → 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.
Files changed (32) hide show
  1. package/jnrs-template-vue/.env.development +4 -5
  2. package/jnrs-template-vue/.env.production +1 -1
  3. package/jnrs-template-vue/components.d.ts +4 -0
  4. package/jnrs-template-vue/package.json +1 -1
  5. package/jnrs-template-vue/public/system/menu.json +1 -2
  6. package/jnrs-template-vue/src/api/request.ts +4 -1
  7. package/jnrs-template-vue/src/api/system/index.ts +19 -15
  8. package/jnrs-template-vue/src/directives/permissions.ts +28 -0
  9. package/jnrs-template-vue/src/layout/SideMenu.vue +2 -4
  10. package/jnrs-template-vue/src/layout/TopHeader.vue +10 -6
  11. package/jnrs-template-vue/src/locales/index.ts +10 -3
  12. package/jnrs-template-vue/src/main.ts +3 -0
  13. package/jnrs-template-vue/src/router/index.ts +35 -6
  14. package/jnrs-template-vue/src/router/routes.ts +28 -28
  15. package/jnrs-template-vue/src/stores/mock.ts +4 -2
  16. package/jnrs-template-vue/src/utils/permissions.ts +16 -0
  17. package/jnrs-template-vue/src/views/common/403.vue +3 -7
  18. package/jnrs-template-vue/src/views/common/404.vue +3 -7
  19. package/jnrs-template-vue/src/views/home/index.vue +52 -18
  20. package/jnrs-template-vue/src/views/login/index.vue +27 -6
  21. package/jnrs-template-vue/src/views/system/dict/index.vue +13 -15
  22. package/jnrs-template-vue/src/views/system/menu/index.vue +22 -24
  23. package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +2 -2
  24. package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +2 -2
  25. package/jnrs-template-vue/src/views/system/role/index.vue +23 -9
  26. package/jnrs-template-vue/viteMockServe/index.ts +22 -6
  27. package/jnrs-template-vue/viteMockServe/{loginRes.json → loginRes_admin.json} +4 -4
  28. package/jnrs-template-vue/viteMockServe/loginRes_user.json +713 -0
  29. package/jnrs-template-vue/viteMockServe/roleRes.json +37 -0
  30. package/package.json +1 -1
  31. package/jnrs-template-vue/.env.example +0 -14
  32. package/jnrs-template-vue/src/types/index.d.ts +0 -6
@@ -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/system'
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,7 +9,12 @@ import { formatDateTime, formatWeekday } from '@jnrs/shared'
9
9
 
10
10
  const route = useRoute()
11
11
  const loading = ref(false)
12
- const { setUserInfo, setToken, setDict } = useAuthStore()
12
+ const {
13
+ setUserInfo,
14
+ setToken,
15
+ setDict
16
+ // setRole
17
+ } = useAuthStore()
13
18
 
14
19
  // 表单 ref
15
20
  const ruleFormRef = ref<FormInstance>()
@@ -28,24 +33,40 @@ const rules = ref<FormRules>({
28
33
  })
29
34
 
30
35
  // 提交函数
31
- const submitForm = () => {
36
+ const submitForm = async () => {
32
37
  if (!ruleFormRef.value) return
33
38
  ruleFormRef.value.validate(async (valid) => {
34
39
  if (!valid) return
35
40
  loading.value = true
36
41
  try {
37
- const res = await LoginApi(ruleForm.value)
38
- const { token, dict, ...restParameter } = res
42
+ const loginRes = await LoginApi(ruleForm.value)
43
+ const { token, dict, ...restParameter } = loginRes
39
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
+
40
59
  setUserInfo({
41
60
  ...restParameter,
61
+ permissions: permissionsMerger,
42
62
  account: ruleForm.value.account,
43
63
  loginDateTime
44
64
  })
45
65
  setToken(token)
46
66
  setDict(dict)
47
67
  handleRouter({ path: route.query.redirect?.toString() || '/' }, 'replace')
48
- } catch {
68
+ } catch (e) {
69
+ console.log(e)
49
70
  } finally {
50
71
  loading.value = false
51
72
  }
@@ -4,8 +4,10 @@ import { ref, onActivated } from 'vue'
4
4
  import { DictApi, DictDetailApi, DictChangeApi } from '@/api/system'
5
5
  import { useAsyncTableHeight } from '@jnrs/vue-core/composables'
6
6
  import type { DictItem } from '@jnrs/shared'
7
+ import { hasPermission } from '@/utils/permissions'
7
8
 
8
9
  const { maxHeight } = useAsyncTableHeight('el_table_dict', 0)
10
+ const PERMISSION_EDIT = ['dict:edit']
9
11
  const loading = ref(false)
10
12
  const tableData = ref<DictItem[]>()
11
13
  const predefineColors = ref([
@@ -92,23 +94,19 @@ const editKeyValue = (row: DictItem) => {
92
94
  <el-table-column prop="describe" label="说明" />
93
95
  <el-table-column type="expand" fixed="right" label="字典值" width="100">
94
96
  <template #default="scope">
95
- <div
96
- class="table_expand animation15"
97
- style="animation-duration: 0.5s; animation-delay: 0s"
98
- >
97
+ <div class="table_expand animation15" style="animation-duration: 0.5s; animation-delay: 0s">
99
98
  <div class="table_expand_top">
100
99
  <h5>字典值列表</h5>
101
100
  </div>
102
101
  <el-table :data="scope.row.options" :border="true" size="small" row-key="value">
103
- <el-table-column
104
- label="value"
105
- prop="value"
106
- width="100"
107
- align="center"
108
- ></el-table-column>
102
+ <el-table-column label="value" prop="value" width="100" align="center"></el-table-column>
109
103
  <el-table-column label="label" prop="label">
110
104
  <template #default="optionScope">
111
- <el-input v-model="optionScope.row.label" maxlength="20" />
105
+ <el-input
106
+ v-model="optionScope.row.label"
107
+ maxlength="20"
108
+ :disabled="!hasPermission(PERMISSION_EDIT)"
109
+ />
112
110
  </template>
113
111
  </el-table-column>
114
112
  <el-table-column label="颜色" prop="displayColor">
@@ -116,6 +114,7 @@ const editKeyValue = (row: DictItem) => {
116
114
  <el-color-picker
117
115
  v-model="optionScope.row.displayColor"
118
116
  :predefine="predefineColors"
117
+ :disabled="!hasPermission(PERMISSION_EDIT)"
119
118
  />
120
119
  <span style="margin-left: 10px">
121
120
  {{ optionScope.row.displayColor || '无' }}
@@ -131,15 +130,14 @@ const editKeyValue = (row: DictItem) => {
131
130
  :step="1"
132
131
  controls-position="right"
133
132
  step-strictly
133
+ :disabled="!hasPermission(PERMISSION_EDIT)"
134
134
  style="width: 140px"
135
135
  />
136
136
  </template>
137
137
  </el-table-column>
138
- <el-table-column label="操作" width="100" align="center" fixed="right">
138
+ <el-table-column label="操作" width="100" align="center" fixed="right" v-permissions="PERMISSION_EDIT">
139
139
  <template #default="optionScope">
140
- <el-button type="success" size="small" @click="editKeyValue(optionScope.row)">
141
- 确认修改
142
- </el-button>
140
+ <el-button type="success" size="small" @click="editKeyValue(optionScope.row)">确认修改</el-button>
143
141
  </template>
144
142
  </el-table-column>
145
143
  </el-table>
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
3
  import { useMenuStore } from '@/stores'
4
- import { getDictLabel } from '@/utils/common'
5
4
  import { useAsyncTableHeight } from '@jnrs/vue-core/composables'
6
5
 
7
6
  const { maxHeight } = useAsyncTableHeight('el_table_menu', 0)
@@ -27,38 +26,37 @@ const { menus } = useMenuStore()
27
26
  border
28
27
  v-loading="loading"
29
28
  >
30
- <el-table-column prop="meta.title" label="名称" min-width="120" />
31
- <el-table-column prop="path" label="路由地址" min-width="120" />
32
- <el-table-column prop="role" label="权限" min-width="120">
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">
33
31
  <template #default="{ row }">
34
- <span v-if="!row.meta.role" style="color: #999">auto</span>
35
- <template v-else>
36
- {{ row.meta.role.map((d: string | number) => getDictLabel('role', d)) }}
37
- </template>
32
+ <el-icon v-if="row.meta.icon"><component :is="row.meta.icon" /></el-icon>
38
33
  </template>
39
34
  </el-table-column>
40
- <el-table-column prop="editRole" label="操作权限" min-width="120">
41
- <template #default="scope">
42
- <template v-if="!scope.row.editRole">
43
- <span style="color: #999" v-if="!scope.row.role">auto</span>
44
- <!-- <template v-else>
45
- {{ scope.row.role.map((d) => getDictLabel('role', d.split('Role')[1])) }}
46
- </template> -->
47
- </template>
48
- <!-- <template v-else>
49
- {{ scope.row.editRole.map((d) => getDictLabel('role', d.split('Role')[1])) }}
50
- </template> -->
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>
51
42
  </template>
52
43
  </el-table-column>
53
44
  <el-table-column prop="type" label="类型" width="80" align="center">
54
- <template #default="scope">
55
- <el-tag type="primary" v-if="scope.row.path">菜单</el-tag>
45
+ <template #default="{ row }">
46
+ <el-tag type="primary" v-if="row.path">菜单</el-tag>
56
47
  <el-tag type="success" v-else>目录</el-tag>
57
48
  </template>
58
49
  </el-table-column>
59
- <el-table-column prop="activate" label="图标" width="80" align="center">
60
- <template #default="scope">
61
- <el-icon v-if="scope.row.icon"><component :is="scope.row.icon" /></el-icon>
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>
62
60
  </template>
63
61
  </el-table-column>
64
62
  </el-table>
@@ -77,7 +77,7 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
77
77
  <el-input v-model.trim="ruleForm.workNo" style="width: 200px" />
78
78
  </el-form-item>
79
79
  <el-form-item label="职称" prop="jobTitle">
80
- <el-select v-model="ruleForm.jobTitle" style="width: 200px">
80
+ <el-select v-model="ruleForm.jobTitle" placeholder="" style="width: 200px">
81
81
  <el-option
82
82
  :label="item.label"
83
83
  :value="item.value"
@@ -90,7 +90,7 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
90
90
  <el-input v-model.trim="ruleForm.workgroup" style="width: 200px" />
91
91
  </el-form-item>
92
92
  <el-form-item label="职位/账号权限" prop="role">
93
- <el-select v-model="ruleForm.role" style="width: 200px">
93
+ <el-select v-model="ruleForm.role" placeholder="" style="width: 200px">
94
94
  <el-option
95
95
  :label="item.label"
96
96
  :value="item.value"
@@ -8,7 +8,7 @@ import type { FormInstance, FormItemRule } from 'element-plus'
8
8
  import { ElMessage } from 'element-plus'
9
9
  import { LogoutApi } from '@/api/system'
10
10
 
11
- const { userInfo, asyncClearAuth } = useAuthStore()
11
+ const { userInfo, clearAuth } = useAuthStore()
12
12
  const loading = ref(false)
13
13
  const formRef = ref<FormInstance>()
14
14
  const ruleForm = ref({
@@ -53,7 +53,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
53
53
  })
54
54
  ElMessage.success('密码修改成功,请重新登录系统')
55
55
  await LogoutApi()
56
- await asyncClearAuth()
56
+ await clearAuth()
57
57
  handleRouter({ name: 'Login' }, 'replace')
58
58
  }
59
59
  } catch {
@@ -1,10 +1,20 @@
1
1
  <script setup lang="ts">
2
- import { ref } from 'vue'
3
- import { useAuthStore } from '@/stores'
2
+ import { ref, onMounted } from 'vue'
3
+ import { RoleApi } from '@/api/system'
4
4
 
5
- const { dict } = useAuthStore()
6
5
  const loading = ref(false)
7
- const tableData = ref(dict?.role)
6
+ const tableData = ref()
7
+
8
+ const handleRoleApi = async () => {
9
+ try {
10
+ const res = await RoleApi()
11
+ tableData.value = res
12
+ } catch {}
13
+ }
14
+
15
+ onMounted(() => {
16
+ handleRoleApi()
17
+ })
8
18
  </script>
9
19
 
10
20
  <template>
@@ -23,11 +33,15 @@ const tableData = ref(dict?.role)
23
33
  border
24
34
  v-loading="loading"
25
35
  >
26
- <el-table-column prop="label" label="角色名称" align="center" />
27
- <!-- <el-table-column prop="codeValue" label="系统权限" align="center" /> -->
28
- <el-table-column prop="value" label="值" align="center">
29
- <template #default="scope">
30
- {{ scope.row.value + '[' + typeof scope.row.value + ']' }}
36
+ <el-table-column prop="label" label="角色名称" width="200" align="center" />
37
+ <el-table-column prop="value" label="" width="200" align="center">
38
+ <template #default="{ row }">
39
+ {{ row.value + ' [' + typeof row.value + ']' }}
40
+ </template>
41
+ </el-table-column>
42
+ <el-table-column prop="permissions" label="可用权限" header-align="center">
43
+ <template #default="{ row }">
44
+ {{ row.permissions }}
31
45
  </template>
32
46
  </el-table-column>
33
47
  </el-table>
@@ -1,9 +1,11 @@
1
+ import menuRes from '../public/system/menu.json'
1
2
  import successMock from './success'
2
3
  import failMock from './fail'
3
- import loginRes from './loginRes.json'
4
+ import loginRes_admin from './loginRes_admin.json'
5
+ import loginRes_user from './loginRes_user.json'
4
6
  import dictRes from './dictRes.json'
5
7
  import dictItemRes from './dictItemRes.json'
6
- import menuRes from '../public/system/menu.json'
8
+ import roleRes from './roleRes.json'
7
9
 
8
10
  export default [
9
11
  ...successMock,
@@ -20,16 +22,22 @@ export default [
20
22
  {
21
23
  url: '/mock/api/auth/login',
22
24
  method: 'post',
23
- response: () => {
24
- return loginRes
25
+ response: ({ body }) => {
26
+ if (body.account === 'admin') {
27
+ return loginRes_admin
28
+ }
29
+ return loginRes_user
25
30
  }
26
31
  },
27
32
  // 获取个人信息
28
33
  {
29
34
  url: '/mock/api/auth/user-info',
30
35
  method: 'get',
31
- response: () => {
32
- return loginRes
36
+ response: ({ headers }) => {
37
+ if (headers.authorization.includes('admin')) {
38
+ return loginRes_admin
39
+ }
40
+ return loginRes_user
33
41
  }
34
42
  },
35
43
  // 获取字典
@@ -47,5 +55,13 @@ export default [
47
55
  response: () => {
48
56
  return dictItemRes
49
57
  }
58
+ },
59
+ // 获取角色
60
+ {
61
+ url: '/mock/api/role-manager',
62
+ method: 'get',
63
+ response: () => {
64
+ return roleRes
65
+ }
50
66
  }
51
67
  ]
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "data": {
3
- "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
3
+ "token": "***admin***token***",
4
4
  "id": 1,
5
5
  "name": "管理员",
6
6
  "workNo": "000000",
7
- "avatarFilenName": "20251014181230905-b2bd246f-4174-4804-8122-37c826c56a7d.png",
7
+ "avatarFileName": "20251014181230905-b2bd246f-4174-4804-8122-37c826c56a7d.png",
8
8
  "workgroup": null,
9
- "jobTitle": 0,
9
+ "jobTitle": 1,
10
10
  "role": 6,
11
- "permissions": ["system:view", "mine:view", "mine:edit"],
11
+ "permissions": [],
12
12
  "abilityCoefficient": 0.0,
13
13
  "dict": {
14
14
  "defectiveWorkHourStatus": [