create-jnrs-template-vue 1.1.5 → 1.1.6

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 (34) hide show
  1. package/jnrs-template-vue/.prettierrc.json +1 -1
  2. package/jnrs-template-vue/auto-imports.d.ts +1 -0
  3. package/jnrs-template-vue/components.d.ts +10 -0
  4. package/jnrs-template-vue/package.json +1 -1
  5. package/jnrs-template-vue/public/system/menu.json +29 -9
  6. package/jnrs-template-vue/src/App.vue +2 -2
  7. package/jnrs-template-vue/src/api/{base → system}/index.ts +30 -6
  8. package/jnrs-template-vue/src/layout/RouterTabs.vue +1 -1
  9. package/jnrs-template-vue/src/layout/SideMenuItem.vue +1 -1
  10. package/jnrs-template-vue/src/layout/TopHeader.vue +11 -23
  11. package/jnrs-template-vue/src/layout/index.vue +35 -5
  12. package/jnrs-template-vue/src/main.ts +0 -2
  13. package/jnrs-template-vue/src/router/index.ts +10 -4
  14. package/jnrs-template-vue/src/types/index.d.ts +6 -0
  15. package/jnrs-template-vue/src/views/common/403.vue +2 -2
  16. package/jnrs-template-vue/src/views/common/404.vue +2 -2
  17. package/jnrs-template-vue/src/views/home/index.vue +2 -2
  18. package/jnrs-template-vue/src/views/login/index.vue +12 -17
  19. package/jnrs-template-vue/src/views/system/dict/index.vue +176 -0
  20. package/jnrs-template-vue/src/views/system/menu/index.vue +67 -0
  21. package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +38 -30
  22. package/jnrs-template-vue/src/views/system/mine/securitySettings.vue +11 -19
  23. package/jnrs-template-vue/src/views/system/role/editDialog.vue +94 -0
  24. package/jnrs-template-vue/src/views/system/role/index.vue +30 -3
  25. package/jnrs-template-vue/vite.config.ts +2 -1
  26. package/jnrs-template-vue/viteMockServe/dictItemRes.json +27 -0
  27. package/jnrs-template-vue/viteMockServe/dictRes.json +141 -0
  28. package/jnrs-template-vue/viteMockServe/fail.ts +26 -0
  29. package/jnrs-template-vue/viteMockServe/index.ts +18 -25
  30. package/jnrs-template-vue/viteMockServe/{login.json → loginRes.json} +1 -0
  31. package/jnrs-template-vue/viteMockServe/success.ts +31 -0
  32. package/package.json +1 -1
  33. package/jnrs-template-vue/src/types/index.ts +0 -2
  34. package/jnrs-template-vue/src/views/system/user/index.vue +0 -9
@@ -0,0 +1,176 @@
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
+
8
+ const { maxHeight } = useAsyncTableHeight('el_table_dict', 0)
9
+ const loading = ref(false)
10
+ const tableData = ref<DictItem[]>()
11
+ const predefineColors = ref([
12
+ '#000000',
13
+ '#999999',
14
+ '#f2f2f2',
15
+ '#ffffff',
16
+ '#65DC79',
17
+ '#5887F7',
18
+ '#E43634',
19
+ '#FEA822',
20
+ '#C9512C'
21
+ ])
22
+
23
+ onActivated(() => {
24
+ getList()
25
+ })
26
+
27
+ const getList = () => {
28
+ loading.value = true
29
+ DictApi()
30
+ .then((res) => {
31
+ tableData.value = res
32
+ })
33
+ .finally(() => {
34
+ loading.value = false
35
+ })
36
+ }
37
+
38
+ const handleExpandChange = (row: DictItem) => {
39
+ loading.value = true
40
+ DictDetailApi(String(row.id))
41
+ .then((res) => {
42
+ row.options = res
43
+ })
44
+ .finally(() => {
45
+ loading.value = false
46
+ })
47
+ }
48
+
49
+ // 保存键值对
50
+ const editKeyValue = (row: DictItem) => {
51
+ if (!row.label) {
52
+ ElMessage({
53
+ type: 'warning',
54
+ message: 'label 不能为空'
55
+ })
56
+ return false
57
+ }
58
+ loading.value = true
59
+ DictChangeApi(row)
60
+ .then(() => {
61
+ ElMessage({
62
+ type: 'success',
63
+ message: '修改成功',
64
+ grouping: true
65
+ })
66
+ })
67
+ .finally(() => {
68
+ loading.value = false
69
+ })
70
+ }
71
+ </script>
72
+
73
+ <template>
74
+ <div class="main">
75
+ <el-card shadow="never">
76
+ <template #header>
77
+ <div class="card_header">
78
+ <h3>字典管理</h3>
79
+ </div>
80
+ </template>
81
+ <el-table
82
+ class="el_table_dict"
83
+ :data="tableData"
84
+ :height="maxHeight"
85
+ scrollbar-always-on
86
+ border
87
+ v-loading="loading"
88
+ row-key="id"
89
+ @expand-change="handleExpandChange"
90
+ >
91
+ <el-table-column prop="dictName" label="字典名" />
92
+ <el-table-column prop="describe" label="说明" />
93
+ <el-table-column type="expand" fixed="right" label="字典值" width="100">
94
+ <template #default="scope">
95
+ <div
96
+ class="table_expand animation15"
97
+ style="animation-duration: 0.5s; animation-delay: 0s"
98
+ >
99
+ <div class="table_expand_top">
100
+ <h5>字典值列表</h5>
101
+ </div>
102
+ <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>
109
+ <el-table-column label="label" prop="label">
110
+ <template #default="optionScope">
111
+ <el-input v-model="optionScope.row.label" maxlength="20" />
112
+ </template>
113
+ </el-table-column>
114
+ <el-table-column label="颜色" prop="displayColor">
115
+ <template #default="optionScope">
116
+ <el-color-picker
117
+ v-model="optionScope.row.displayColor"
118
+ :predefine="predefineColors"
119
+ />
120
+ <span style="margin-left: 10px">
121
+ {{ optionScope.row.displayColor || '无' }}
122
+ </span>
123
+ </template>
124
+ </el-table-column>
125
+ <el-table-column prop="sequence" label="排序" width="180" align="center">
126
+ <template #default="optionScope">
127
+ <el-input-number
128
+ v-model="optionScope.row.sequence"
129
+ :min="1"
130
+ :max="999"
131
+ :step="1"
132
+ controls-position="right"
133
+ step-strictly
134
+ style="width: 140px"
135
+ />
136
+ </template>
137
+ </el-table-column>
138
+ <el-table-column label="操作" width="100" align="center" fixed="right">
139
+ <template #default="optionScope">
140
+ <el-button type="success" size="small" @click="editKeyValue(optionScope.row)">
141
+ 确认修改
142
+ </el-button>
143
+ </template>
144
+ </el-table-column>
145
+ </el-table>
146
+ </div>
147
+ </template>
148
+ </el-table-column>
149
+ </el-table>
150
+ </el-card>
151
+ </div>
152
+ </template>
153
+
154
+ <style scoped lang="scss">
155
+ .table_expand {
156
+ padding: 10px;
157
+ color: var(--jnrs-font-primary);
158
+ background: var(--jnrs-background-primary);
159
+
160
+ .table_expand_top {
161
+ position: relative;
162
+
163
+ h5 {
164
+ font-size: 14px;
165
+ margin-bottom: 10px;
166
+ }
167
+
168
+ .table_expand_top_btn {
169
+ position: absolute;
170
+ right: 0;
171
+ top: 0;
172
+ z-index: 1;
173
+ }
174
+ }
175
+ }
176
+ </style>
@@ -0,0 +1,67 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { useMenuStore } from '@/stores'
4
+ import { getDictLabel } from '@/utils/common'
5
+ import { useAsyncTableHeight } from '@jnrs/vue-core/composables'
6
+
7
+ const { maxHeight } = useAsyncTableHeight('el_table_menu', 0)
8
+ const loading = ref(false)
9
+
10
+ const { menus } = useMenuStore()
11
+ </script>
12
+
13
+ <template>
14
+ <div>
15
+ <el-card shadow="never">
16
+ <template #header>
17
+ <div class="card_header">
18
+ <h3>菜单管理</h3>
19
+ </div>
20
+ </template>
21
+ <el-table
22
+ class="el_table_menu"
23
+ :data="menus"
24
+ :height="maxHeight"
25
+ scrollbar-always-on
26
+ row-key="meta.uuid"
27
+ border
28
+ v-loading="loading"
29
+ >
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">
33
+ <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>
38
+ </template>
39
+ </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> -->
51
+ </template>
52
+ </el-table-column>
53
+ <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>
56
+ <el-tag type="success" v-else>目录</el-tag>
57
+ </template>
58
+ </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>
62
+ </template>
63
+ </el-table-column>
64
+ </el-table>
65
+ </el-card>
66
+ </div>
67
+ </template>
@@ -1,41 +1,32 @@
1
1
  <script setup lang="ts">
2
- import { ref } from 'vue'
2
+ import { ref, onMounted } from 'vue'
3
+ import { storeToRefs } from 'pinia'
3
4
  import { ElMessage } from 'element-plus'
4
5
  import type { UploadProps } from 'element-plus'
5
6
  import { useAuthStore } from '@/stores'
6
7
  import { useAvatar } from '@/composables/common/useAvatar'
8
+ import { getOneDictList } from '@/utils/common'
9
+ import type { User } from '@jnrs/shared'
7
10
 
8
- const { userInfo, token } = useAuthStore()
11
+ const { userInfo, token } = storeToRefs(useAuthStore())
9
12
  const { avatar } = useAvatar()
10
13
  const loading = ref(false)
11
14
  const ruleFormRef = ref()
12
- const ruleForm = ref({
15
+ const ruleForm = ref<User>({
16
+ id: 0,
13
17
  account: '',
14
18
  name: '',
15
19
  workNo: '',
16
- jobTitle: '',
20
+ jobTitle: 0,
17
21
  workgroup: '',
18
- role: ''
22
+ role: 0,
23
+ avatarFileName: ''
19
24
  })
20
25
  const rules = ref({
21
26
  // account: { required: true, message: '请输入', trigger: 'change' },
22
27
  // role: { required: true, message: '请输入', trigger: 'change' },
23
28
  // name: { required: true, message: '请输入', trigger: 'change' },
24
29
  // workNo: { required: true, message: '请输入', trigger: 'change' },
25
- // email: [
26
- // { required: false, message: '请输入', trigger: 'change' },
27
- // {
28
- // validator: (rule, value, callback) => {
29
- // if (value && !isEmail(value)) {
30
- // callback(new Error('请输入有效的邮箱!'))
31
- // } else {
32
- // callback()
33
- // }
34
- // },
35
- // required: false,
36
- // trigger: 'blur'
37
- // }
38
- // ]
39
30
  })
40
31
 
41
32
  interface ApiResponse {
@@ -44,9 +35,15 @@ interface ApiResponse {
44
35
  data: string
45
36
  }
46
37
 
38
+ onMounted(() => {
39
+ if (userInfo.value) {
40
+ ruleForm.value = userInfo.value
41
+ }
42
+ })
43
+
47
44
  const handleAvatarSuccess: UploadProps['onSuccess'] = (response: ApiResponse) => {
48
45
  loading.value = false
49
- if (response.code === 0) {
46
+ if (userInfo.value && response.code === 0) {
50
47
  userInfo.value.avatarFileName = response.data
51
48
  ElMessage.success({ message: '头像上传成功' })
52
49
  }
@@ -69,28 +66,39 @@ const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
69
66
  </script>
70
67
 
71
68
  <template>
72
- <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto">
69
+ <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto" disabled>
73
70
  <el-form-item label="登录账号" prop="account">
74
- <el-input v-model.trim="ruleForm.account" style="width: 200px" disabled />
71
+ <el-input v-model.trim="ruleForm.account" style="width: 200px" />
75
72
  </el-form-item>
76
73
  <el-form-item label="名称" prop="name">
77
- <el-input v-model.trim="ruleForm.name" style="width: 200px" disabled />
74
+ <el-input v-model.trim="ruleForm.name" style="width: 200px" />
78
75
  </el-form-item>
79
76
  <el-form-item label="工号" prop="workNo">
80
- <el-input v-model.trim="ruleForm.workNo" style="width: 200px" disabled />
77
+ <el-input v-model.trim="ruleForm.workNo" style="width: 200px" />
81
78
  </el-form-item>
82
79
  <el-form-item label="职称" prop="jobTitle">
83
- <el-input v-model.trim="ruleForm.jobTitle" style="width: 200px" disabled />
80
+ <el-select v-model="ruleForm.jobTitle" style="width: 200px">
81
+ <el-option
82
+ :label="item.label"
83
+ :value="item.value"
84
+ v-for="(item, index) in getOneDictList('jobTitle')"
85
+ :key="index"
86
+ />
87
+ </el-select>
84
88
  </el-form-item>
85
89
  <el-form-item label="部门/班组" prop="workgroup">
86
- <el-input v-model.trim="ruleForm.workgroup" style="width: 200px" disabled />
90
+ <el-input v-model.trim="ruleForm.workgroup" style="width: 200px" />
87
91
  </el-form-item>
88
92
  <el-form-item label="职位/账号权限" prop="role">
89
- <el-input v-model.trim="ruleForm.role" style="width: 200px" disabled />
93
+ <el-select v-model="ruleForm.role" style="width: 200px">
94
+ <el-option
95
+ :label="item.label"
96
+ :value="item.value"
97
+ v-for="(item, index) in getOneDictList('role')"
98
+ :key="index"
99
+ />
100
+ </el-select>
90
101
  </el-form-item>
91
- <!-- <el-form-item label="邮箱" prop="email">
92
- <el-input v-model.trim="ruleForm.email" style="width: 200px" />
93
- </el-form-item> -->
94
102
  <el-form-item label="头像" prop="avatarFileName" class="uploader_item">
95
103
  <el-upload
96
104
  action="/api/user/avatar"
@@ -1,12 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  import { reactive, ref } from 'vue'
3
- import { LoginApi, PasswordChangeApi } from '@/api/base'
3
+ import { PasswordChangeApi } from '@/api/system'
4
4
  import { useAuthStore } from '@/stores'
5
5
  import { handleRouter } from '@jnrs/vue-core/router'
6
6
  import { isWeakPwd } from '@jnrs/shared/validator'
7
7
  import type { FormInstance, FormItemRule } from 'element-plus'
8
8
  import { ElMessage } from 'element-plus'
9
- import { LogoutApi } from '@/api/base/index'
9
+ import { LogoutApi } from '@/api/system'
10
10
 
11
11
  const { userInfo, asyncClearAuth } = useAuthStore()
12
12
  const loading = ref(false)
@@ -45,22 +45,16 @@ const submitForm = async (formEl: FormInstance | undefined) => {
45
45
  if (!formEl) return
46
46
  try {
47
47
  const valid = await formEl.validate()
48
- if (valid) {
48
+ if (valid && userInfo?.id) {
49
49
  loading.value = true
50
- const loginRes = await LoginApi({
51
- account: userInfo.account,
52
- password: ruleForm.value.originPassword
50
+ await PasswordChangeApi({
51
+ userId: userInfo.id,
52
+ password: ruleForm.value.password
53
53
  })
54
- if (loginRes.token) {
55
- await PasswordChangeApi({
56
- userId: userInfo.id,
57
- password: ruleForm.value.password
58
- })
59
- ElMessage.success('密码修改成功,请重新登录系统')
60
- await LogoutApi()
61
- await asyncClearAuth()
62
- handleRouter({ name: 'Login' }, 'replace')
63
- }
54
+ ElMessage.success('密码修改成功,请重新登录系统')
55
+ await LogoutApi()
56
+ await asyncClearAuth()
57
+ handleRouter({ name: 'Login' }, 'replace')
64
58
  }
65
59
  } catch {
66
60
  } finally {
@@ -99,9 +93,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
99
93
  />
100
94
  </el-form-item>
101
95
  <el-form-item>
102
- <el-button class="form_submit" type="primary" @click="submitForm(formRef)">
103
- 保存修改
104
- </el-button>
96
+ <el-button class="form_submit" type="primary" @click="submitForm(formRef)">保存修改</el-button>
105
97
  </el-form-item>
106
98
  </el-form>
107
99
  </template>
@@ -0,0 +1,94 @@
1
+ <script setup lang="ts">
2
+ import type { FormInstance } from 'element-plus'
3
+ import { ref, nextTick } from 'vue'
4
+ defineOptions({
5
+ name: 'editDialog'
6
+ })
7
+
8
+ const loading = ref(false)
9
+ const dialogVisible = ref(false)
10
+ const ruleFormRef = ref<FormInstance>()
11
+ const ruleForm = ref({
12
+ id: '',
13
+ name: '',
14
+ enname: '',
15
+ remark: '',
16
+ usable: '1'
17
+ })
18
+ const rules = ref({
19
+ name: { required: true, message: '请输入角色名称', trigger: 'change' },
20
+ enname: { required: true, message: '请输入英文名称', trigger: 'change' },
21
+ usable: { required: true, message: '请选择是否可用', trigger: 'change' }
22
+ })
23
+
24
+ const toggleVisible = () => {
25
+ dialogVisible.value = !dialogVisible.value
26
+ nextTick(() => {
27
+ ruleFormRef.value?.resetFields()
28
+ })
29
+ }
30
+
31
+ const submitForm = () => {
32
+ if (!ruleFormRef.value) return
33
+ ruleFormRef.value.validate(async (valid) => {
34
+ if (!valid) return
35
+ })
36
+ }
37
+
38
+ defineExpose({
39
+ toggleVisible
40
+ })
41
+ </script>
42
+
43
+ <template>
44
+ <el-dialog
45
+ v-model="dialogVisible"
46
+ width="600"
47
+ :append-to-body="true"
48
+ :close-on-click-modal="true"
49
+ :close-on-press-escape="true"
50
+ >
51
+ <template #header>
52
+ <h3>{{ ruleForm.id ? '修改' : '添加' }}角色</h3>
53
+ </template>
54
+ <el-form
55
+ ref="ruleFormRef"
56
+ :model="ruleForm"
57
+ :rules="rules"
58
+ label-width="auto"
59
+ v-loading="loading"
60
+ style="width: 80%; margin: 0 auto"
61
+ >
62
+ <el-form-item prop="id" v-show="false">
63
+ <el-input v-model="ruleForm.id" />
64
+ </el-form-item>
65
+ <el-form-item label="角色名称" prop="name">
66
+ <el-input v-model="ruleForm.name" maxlength="20" />
67
+ </el-form-item>
68
+ <el-form-item label="英文名称" prop="enname">
69
+ <el-input v-model="ruleForm.enname" maxlength="20" />
70
+ </el-form-item>
71
+ <el-form-item label="说明" prop="remark">
72
+ <el-input v-model="ruleForm.remark" maxlength="200" type="textarea" :rows="5" />
73
+ </el-form-item>
74
+ <el-form-item label="是否可用" prop="usable">
75
+ <el-switch
76
+ v-model="ruleForm.usable"
77
+ inline-prompt
78
+ active-text="是"
79
+ inactive-text="否"
80
+ :active-value="true"
81
+ :inactive-value="false"
82
+ width="50"
83
+ style="--el-switch-on-color: #67c23a; --el-switch-off-color: #f56c6c"
84
+ />
85
+ </el-form-item>
86
+ </el-form>
87
+ <template #footer>
88
+ <div class="dialog-footer">
89
+ <el-button @click="toggleVisible()" :loading="loading">取消</el-button>
90
+ <el-button type="primary" @click="submitForm()" :loading="loading">提交</el-button>
91
+ </div>
92
+ </template>
93
+ </el-dialog>
94
+ </template>
@@ -1,9 +1,36 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
+ import { useAuthStore } from '@/stores'
4
+
5
+ const { dict } = useAuthStore()
6
+ const loading = ref(false)
7
+ const tableData = ref(dict?.role)
3
8
  </script>
4
9
 
5
10
  <template>
6
- <div>role</div>
11
+ <div class="main">
12
+ <el-card shadow="never">
13
+ <template #header>
14
+ <div class="card_header">
15
+ <h3>角色管理</h3>
16
+ </div>
17
+ </template>
18
+ <el-table
19
+ class="el_table_role"
20
+ header-cell-class-name="tableData_header_cell"
21
+ :data="tableData"
22
+ scrollbar-always-on
23
+ border
24
+ v-loading="loading"
25
+ >
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 + ']' }}
31
+ </template>
32
+ </el-table-column>
33
+ </el-table>
34
+ </el-card>
35
+ </div>
7
36
  </template>
8
-
9
- <style lang="scss" scoped></style>
@@ -33,8 +33,9 @@ export default defineConfig({
33
33
  }),
34
34
  viteMockServe({
35
35
  mockPath: 'viteMockServe', // mock 文件目录
36
+ watchFiles: true, // mock 文件热更新
36
37
  enable: isMock, // 是否开启
37
- logger: isMock // 打印 mock 日志
38
+ logger: isMock // 是否在终端显示请求日志
38
39
  })
39
40
  ],
40
41
  resolve: {
@@ -0,0 +1,27 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "id": 7,
5
+ "value": 0,
6
+ "label": "未审批",
7
+ "displayColor": "#FEA822",
8
+ "sequence": 1
9
+ },
10
+ {
11
+ "id": 8,
12
+ "value": 1,
13
+ "label": "未通过",
14
+ "displayColor": "#E43634",
15
+ "sequence": 2
16
+ },
17
+ {
18
+ "id": 9,
19
+ "value": 2,
20
+ "label": "已通过",
21
+ "displayColor": "#65DC79",
22
+ "sequence": 3
23
+ }
24
+ ],
25
+ "code": 0,
26
+ "msg": "操作成功"
27
+ }