create-jnrs-template-vue 1.2.2 → 1.2.4
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/.env.development +1 -1
- package/jnrs-template-vue/index.html +1 -1
- package/jnrs-template-vue/package.json +3 -4
- package/jnrs-template-vue/public/system/menu.json +11 -2
- package/jnrs-template-vue/src/App.vue +1 -3
- package/jnrs-template-vue/src/api/common/index.ts +2 -2
- package/jnrs-template-vue/src/api/demos/index.ts +85 -43
- package/jnrs-template-vue/src/api/system/index.ts +20 -10
- package/jnrs-template-vue/src/api/user/index.ts +2 -2
- package/jnrs-template-vue/src/assets/styles/index.scss +0 -24
- package/jnrs-template-vue/src/assets/styles/init.scss +24 -0
- package/jnrs-template-vue/src/assets/styles/root.scss +4 -0
- package/jnrs-template-vue/src/components/common/CardTable.vue +89 -0
- package/jnrs-template-vue/src/components/common/DictTag.vue +8 -4
- package/jnrs-template-vue/src/components/common/ImageView.vue +16 -7
- package/jnrs-template-vue/src/components/common/PdfView.vue +14 -5
- package/jnrs-template-vue/src/components/select/SelectManager.vue +2 -2
- package/jnrs-template-vue/src/layout/SideMenu.vue +0 -1
- package/jnrs-template-vue/src/layout/TopHeader.vue +7 -14
- package/jnrs-template-vue/src/locales/index.ts +1 -1
- package/jnrs-template-vue/src/types/webSocket.ts +19 -0
- package/jnrs-template-vue/src/utils/file.ts +36 -1
- package/jnrs-template-vue/src/views/demos/crud/index.vue +57 -32
- package/jnrs-template-vue/src/views/demos/simpleTable/index.vue +41 -0
- package/jnrs-template-vue/src/views/demos/unitTest/RequestPage.vue +2 -2
- package/jnrs-template-vue/src/views/demos/unitTest/index.vue +26 -2
- package/jnrs-template-vue/src/views/login/index.vue +18 -15
- package/jnrs-template-vue/src/views/system/dict/index.vue +63 -76
- package/jnrs-template-vue/src/views/system/menu/index.vue +42 -54
- package/jnrs-template-vue/src/views/system/mine/baseInfo.vue +26 -59
- package/jnrs-template-vue/src/views/system/role/index.vue +20 -29
- package/jnrs-template-vue/src/views/visual/index.vue +130 -15
- package/jnrs-template-vue/vite.config.ts +0 -1
- package/jnrs-template-vue/viteMockServe/fail.ts +12 -0
- package/package.json +1 -1
- package/jnrs-template-vue/src/composables/base/useAvatar.ts +0 -36
- package/jnrs-template-vue/src/composables/tools/useMouseSelection.ts +0 -150
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref
|
|
2
|
+
import { ref } from 'vue'
|
|
3
3
|
import { storeToRefs } from 'pinia'
|
|
4
4
|
import { ElMessageBox } from 'element-plus'
|
|
5
5
|
import { handleRouter } from '@jnrs/vue-core/router'
|
|
6
6
|
import { GlobalSetting } from '@jnrs/vue-core/components'
|
|
7
7
|
import { useSystemStore, useAuthStore, useMenuStore } from '@jnrs/vue-core/pinia'
|
|
8
8
|
import { LogoutApi } from '@/api/system'
|
|
9
|
-
import { getDictLabel, getDictColor } from '@/utils/packages'
|
|
10
|
-
|
|
11
9
|
import ImageView from '@/components/common/ImageView.vue'
|
|
10
|
+
import DictTag from '@/components/common/DictTag.vue'
|
|
12
11
|
|
|
13
12
|
const { userInfo, clearAuth } = useAuthStore()
|
|
14
13
|
const { clearMenu } = useMenuStore()
|
|
15
14
|
|
|
16
|
-
const roleLabel = computed(() => getDictLabel('role', userInfo?.role || ''))
|
|
17
|
-
const roleColor = computed(() => getDictColor('role', userInfo?.role || ''))
|
|
18
|
-
|
|
19
15
|
const systemStore = useSystemStore()
|
|
20
16
|
const { documentFullscreen } = storeToRefs(systemStore)
|
|
21
17
|
const { toggleFullScreen } = systemStore
|
|
@@ -29,8 +25,8 @@ const handleLogout = async () => {
|
|
|
29
25
|
type: 'warning'
|
|
30
26
|
})
|
|
31
27
|
await LogoutApi()
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
clearAuth()
|
|
29
|
+
clearMenu()
|
|
34
30
|
handleRouter({ name: 'Login' }, 'replace')
|
|
35
31
|
} catch {}
|
|
36
32
|
}
|
|
@@ -68,16 +64,12 @@ const showGlobalSetting = () => {
|
|
|
68
64
|
<span class="userMenu_reference">
|
|
69
65
|
<ImageView class="userMenu_avatar" :loadKeys="userInfo?.avatarFileName" />
|
|
70
66
|
<span>{{ userInfo?.name }}</span>
|
|
71
|
-
<
|
|
67
|
+
<DictTag dictName="role" :value="userInfo.role" v-if="userInfo?.role" />
|
|
72
68
|
<el-icon class="userMenu_icon"><arrow-down /></el-icon>
|
|
73
69
|
</span>
|
|
74
70
|
</template>
|
|
75
71
|
<div class="userMenu_dropdown">
|
|
76
72
|
<ImageView class="userMenu_dropdown_avatar" :loadKeys="userInfo?.avatarFileName" />
|
|
77
|
-
<b>
|
|
78
|
-
<span>{{ userInfo?.name }}</span>
|
|
79
|
-
<span class="userMenu_roleName" :style="{ color: roleColor }" v-if="userInfo?.role">[{{ roleLabel }}]</span>
|
|
80
|
-
</b>
|
|
81
73
|
<div class="loginDateTime" v-if="userInfo?.loginDateTime">
|
|
82
74
|
<span>登录时间</span>
|
|
83
75
|
<p>{{ userInfo?.loginDateTime }}</p>
|
|
@@ -132,13 +124,14 @@ $topHoverSize: 35px;
|
|
|
132
124
|
display: flex;
|
|
133
125
|
align-items: center;
|
|
134
126
|
margin-left: 16px;
|
|
135
|
-
transition: all 0.25s ease;
|
|
136
127
|
cursor: pointer;
|
|
137
128
|
&:hover {
|
|
138
129
|
span {
|
|
130
|
+
transition: all 0.25s ease;
|
|
139
131
|
color: var(--jnrs-color-primary) !important;
|
|
140
132
|
}
|
|
141
133
|
.userMenu_icon {
|
|
134
|
+
transition: all 0.25s ease;
|
|
142
135
|
color: var(--jnrs-color-primary);
|
|
143
136
|
}
|
|
144
137
|
}
|
|
@@ -4,7 +4,7 @@ import en from './en'
|
|
|
4
4
|
import { zhCn as vueCore_zhCn, en as vueCore_en } from '@jnrs/vue-core/locales'
|
|
5
5
|
|
|
6
6
|
const i18n = createI18n({
|
|
7
|
-
legacy: false, //
|
|
7
|
+
legacy: false, // legacy 为 false 则启用 Composition API 模式
|
|
8
8
|
globalInjection: true, // 允许在模板中使用 $t
|
|
9
9
|
locale: 'zhCn',
|
|
10
10
|
fallbackLocale: 'en',
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface MsgIdMessage {
|
|
2
|
+
msgId: string
|
|
3
|
+
d: unknown
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface TypeDataMessage {
|
|
7
|
+
type: string | number
|
|
8
|
+
data: unknown
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// 类型守卫函数 MsgId 结构
|
|
12
|
+
export function isMsgIdMessage(msg: unknown): msg is MsgIdMessage {
|
|
13
|
+
return typeof msg === 'object' && msg !== null && 'msgId' in msg && 'd' in msg
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 类型守卫函数 Type 结构
|
|
17
|
+
export function isTypeDataMessage(msg: unknown): msg is TypeDataMessage {
|
|
18
|
+
return typeof msg === 'object' && msg !== null && 'type' in msg && 'data' in msg
|
|
19
|
+
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* @Desc. : 文件处理相关函数
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
// import { blobToUrl } from '@jnrs/shared'
|
|
10
9
|
import { useObjectUrl } from '@vueuse/core'
|
|
11
10
|
import { FileApi } from '@/api/common'
|
|
12
11
|
|
|
@@ -19,3 +18,39 @@ export const getFileUrl = async (uniqueFileName: string) => {
|
|
|
19
18
|
const blob = await FileApi(uniqueFileName)
|
|
20
19
|
return useObjectUrl(blob)
|
|
21
20
|
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 从可能为原始值或对象的输入中提取指定字段的值
|
|
24
|
+
* @param value - 输入值(可能是 string/number/对象)
|
|
25
|
+
* @param fieldName - 要提取的对象字段名(默认 'id')
|
|
26
|
+
* @returns 提取出的 string | number 值,或 undefined
|
|
27
|
+
*/
|
|
28
|
+
export function extractFieldId(value: unknown, fieldName: string = 'id'): string | number | undefined {
|
|
29
|
+
// 1. 排除 null 和 undefined
|
|
30
|
+
if (value == null) return undefined
|
|
31
|
+
|
|
32
|
+
// 2. 如果是原始 ID 类型,直接返回
|
|
33
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
34
|
+
return value
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 3. 必须是普通对象(非数组、非 Date 等)
|
|
38
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 4. 检查对象是否包含目标字段
|
|
43
|
+
if (!(fieldName in value)) {
|
|
44
|
+
return undefined
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 5. 安全获取字段值(使用 keyof 断言确保类型安全)
|
|
48
|
+
const fieldValue = (value as Record<string, unknown>)[fieldName]
|
|
49
|
+
|
|
50
|
+
// 6. 只接受 string 或 number 类型
|
|
51
|
+
if (typeof fieldValue === 'string' || typeof fieldValue === 'number') {
|
|
52
|
+
return fieldValue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
@@ -5,13 +5,13 @@ import type { ProjectItem, AddProjectItem } from '@/api/demos/index'
|
|
|
5
5
|
import { ref, onActivated } from 'vue'
|
|
6
6
|
import { ElMessage } from 'element-plus'
|
|
7
7
|
import { Plus } from '@element-plus/icons-vue'
|
|
8
|
-
import { objectMatchAssign } from '@jnrs/shared'
|
|
8
|
+
import { objectMatchAssign, dateFormatsToObject } from '@jnrs/shared'
|
|
9
9
|
import { debounce } from '@jnrs/shared/lodash'
|
|
10
10
|
import { isNumberGtZero } from '@jnrs/shared/validator'
|
|
11
|
-
import { getDictList,
|
|
12
|
-
import {
|
|
11
|
+
import { getDictList, downloadFile } from '@/utils/packages'
|
|
12
|
+
import { TableApi, EditApi, ImportTemplateApi, ImportDataApi, ExportApi } from '@/api/demos/index'
|
|
13
13
|
|
|
14
|
-
import { JnDialog, JnDatetime, JnPagination, JnFileUpload, JnTable } from '@jnrs/vue-core/components'
|
|
14
|
+
import { JnDialog, JnDatetime, JnPagination, JnFileUpload, JnTable, JnImportAndExport } from '@jnrs/vue-core/components'
|
|
15
15
|
import ImageView from '@/components/common/ImageView.vue'
|
|
16
16
|
import PdfView from '@/components/common/PdfView.vue'
|
|
17
17
|
import DictTag from '@/components/common/DictTag.vue'
|
|
@@ -28,12 +28,11 @@ const total = ref(0)
|
|
|
28
28
|
const pagination = ref<Pagination>({ pageNo: 1, pageSize: 10 })
|
|
29
29
|
|
|
30
30
|
// 编辑
|
|
31
|
-
const
|
|
31
|
+
const editDialogRef = ref()
|
|
32
32
|
const ruleFormRef = ref<FormInstance>()
|
|
33
33
|
const ruleForm = ref<AddProjectItem>({
|
|
34
34
|
id: '',
|
|
35
35
|
name: '',
|
|
36
|
-
program: '',
|
|
37
36
|
projectType: undefined,
|
|
38
37
|
managerId: undefined,
|
|
39
38
|
budget: 0,
|
|
@@ -44,7 +43,6 @@ const ruleForm = ref<AddProjectItem>({
|
|
|
44
43
|
})
|
|
45
44
|
const rules = ref<FormRules>({
|
|
46
45
|
name: [{ required: true, message: '请输入', trigger: 'change' }],
|
|
47
|
-
program: [{ required: true, message: '请选择', trigger: 'change' }],
|
|
48
46
|
projectType: [{ required: true, message: '请选择', trigger: 'change' }],
|
|
49
47
|
plannedStartDate: [{ required: true, message: '请选择', trigger: 'change' }],
|
|
50
48
|
newImageFiles: [{ required: true, message: '请上传', trigger: 'change' }],
|
|
@@ -62,16 +60,18 @@ const rules = ref<FormRules>({
|
|
|
62
60
|
const queryForm = ref({
|
|
63
61
|
code: '',
|
|
64
62
|
projectType: undefined,
|
|
65
|
-
manager: ''
|
|
63
|
+
manager: '',
|
|
64
|
+
plannedStartDate: ''
|
|
66
65
|
})
|
|
67
66
|
|
|
68
67
|
const handleSelectionChange = (row: ProjectItem[]) => {
|
|
69
68
|
console.log(row)
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
const
|
|
71
|
+
const getTable = debounce(async () => {
|
|
72
|
+
loading.value = true
|
|
73
73
|
try {
|
|
74
|
-
const res = await
|
|
74
|
+
const res = await TableApi({
|
|
75
75
|
...pagination.value,
|
|
76
76
|
...queryForm.value
|
|
77
77
|
})
|
|
@@ -83,12 +83,14 @@ const getDemosTable = debounce(async () => {
|
|
|
83
83
|
total.value = res.count
|
|
84
84
|
} catch (error) {
|
|
85
85
|
console.error(error)
|
|
86
|
+
} finally {
|
|
87
|
+
loading.value = false
|
|
86
88
|
}
|
|
87
89
|
}, 300)
|
|
88
90
|
|
|
89
91
|
// 新增和修改
|
|
90
92
|
const handleEdit = (row?: ProjectItem) => {
|
|
91
|
-
|
|
93
|
+
editDialogRef.value.open()
|
|
92
94
|
queueMicrotask(() => {
|
|
93
95
|
ruleFormRef.value?.resetFields()
|
|
94
96
|
const matched = objectMatchAssign(ruleForm.value, row)
|
|
@@ -108,15 +110,12 @@ const submitForm = () => {
|
|
|
108
110
|
ruleFormRef.value?.validate(async (valid) => {
|
|
109
111
|
if (valid) {
|
|
110
112
|
loading.value = true
|
|
111
|
-
const formData = objectToFormData({
|
|
112
|
-
...ruleForm.value,
|
|
113
|
-
managerId: ruleForm.value.managerId?.managerId
|
|
114
|
-
})
|
|
115
113
|
try {
|
|
116
|
-
await
|
|
114
|
+
await EditApi(ruleForm.value)
|
|
117
115
|
ElMessage({
|
|
118
116
|
message: '数据已保存',
|
|
119
117
|
grouping: true,
|
|
118
|
+
showClose: true,
|
|
120
119
|
type: 'success'
|
|
121
120
|
})
|
|
122
121
|
} catch (err) {
|
|
@@ -129,13 +128,13 @@ const submitForm = () => {
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
onActivated(() => {
|
|
132
|
-
|
|
131
|
+
getTable()
|
|
133
132
|
})
|
|
134
133
|
</script>
|
|
135
134
|
|
|
136
135
|
<template>
|
|
137
136
|
<!-- 编辑弹窗 -->
|
|
138
|
-
<JnDialog ref="
|
|
137
|
+
<JnDialog ref="editDialogRef" title="项目管理" width="600px">
|
|
139
138
|
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="auto" v-loading="loading">
|
|
140
139
|
<el-form-item prop="id"></el-form-item>
|
|
141
140
|
<el-form-item label="项目名称" prop="name">
|
|
@@ -209,20 +208,41 @@ onActivated(() => {
|
|
|
209
208
|
</template>
|
|
210
209
|
</JnDialog>
|
|
211
210
|
|
|
212
|
-
<el-card>
|
|
211
|
+
<el-card v-loading="loading">
|
|
213
212
|
<template #header>
|
|
214
|
-
<div style="display: flex; justify-content: space-between">
|
|
213
|
+
<div style="display: flex; justify-content: space-between; align-items: center">
|
|
215
214
|
<span>项目管理</span>
|
|
216
|
-
<
|
|
215
|
+
<div style="display: flex; justify-content: space-between; align-items: center">
|
|
216
|
+
<JnImportAndExport
|
|
217
|
+
:importTemplateApi="ImportTemplateApi"
|
|
218
|
+
importBtnName="导入项目"
|
|
219
|
+
:importApi="ImportDataApi"
|
|
220
|
+
exportBtnName="导出项目"
|
|
221
|
+
:exportApi="ExportApi"
|
|
222
|
+
:exportParams="{
|
|
223
|
+
...dateFormatsToObject(queryForm.plannedStartDate)
|
|
224
|
+
}"
|
|
225
|
+
:exportDynamicParamsConfig="{
|
|
226
|
+
label: '项目编号',
|
|
227
|
+
prop: 'code'
|
|
228
|
+
}"
|
|
229
|
+
:exportDisabled="tableData.length === 0"
|
|
230
|
+
size="small"
|
|
231
|
+
@change="getTable()"
|
|
232
|
+
/>
|
|
233
|
+
<el-button type="primary" size="small" :icon="Plus" @click="handleEdit()" style="margin-left: 12px">
|
|
234
|
+
新增
|
|
235
|
+
</el-button>
|
|
236
|
+
</div>
|
|
217
237
|
</div>
|
|
218
238
|
</template>
|
|
219
239
|
|
|
220
240
|
<!-- 查询条件 -->
|
|
221
241
|
<el-form :model="queryForm" size="small" inline v-loading="loading">
|
|
222
242
|
<el-form-item label="项目编号" prop="code">
|
|
223
|
-
<el-input v-model="queryForm.code" clearable style="width:
|
|
243
|
+
<el-input v-model="queryForm.code" clearable style="width: 200px">
|
|
224
244
|
<template #append>
|
|
225
|
-
<el-button icon="Search" @click="
|
|
245
|
+
<el-button icon="Search" @click="getTable()" />
|
|
226
246
|
</template>
|
|
227
247
|
</el-input>
|
|
228
248
|
</el-form-item>
|
|
@@ -232,8 +252,8 @@ onActivated(() => {
|
|
|
232
252
|
filterable
|
|
233
253
|
placeholder=""
|
|
234
254
|
clearable
|
|
235
|
-
style="width:
|
|
236
|
-
@change="
|
|
255
|
+
style="width: 200px"
|
|
256
|
+
@change="getTable()"
|
|
237
257
|
>
|
|
238
258
|
<el-option
|
|
239
259
|
:label="item.label"
|
|
@@ -249,9 +269,18 @@ onActivated(() => {
|
|
|
249
269
|
:initialParams="{ role: 1 }"
|
|
250
270
|
size="small"
|
|
251
271
|
width="200px"
|
|
252
|
-
@change="
|
|
272
|
+
@change="getTable()"
|
|
253
273
|
></SelectManager>
|
|
254
274
|
</el-form-item>
|
|
275
|
+
<el-form-item label="计划开始时间" prop="plannedStartDate">
|
|
276
|
+
<el-date-picker
|
|
277
|
+
v-model="queryForm.plannedStartDate"
|
|
278
|
+
value-format="YYYY-MM-DD"
|
|
279
|
+
size="small"
|
|
280
|
+
style="width: 200px"
|
|
281
|
+
@change="getTable()"
|
|
282
|
+
/>
|
|
283
|
+
</el-form-item>
|
|
255
284
|
</el-form>
|
|
256
285
|
|
|
257
286
|
<!-- 数据列表 -->
|
|
@@ -266,11 +295,7 @@ onActivated(() => {
|
|
|
266
295
|
@selection-change="handleSelectionChange"
|
|
267
296
|
>
|
|
268
297
|
<el-table-column prop="code" label="项目编号" min-width="200" sortable show-overflow-tooltip />
|
|
269
|
-
<el-table-column prop="name" label="项目名称" min-width="200" sortable show-overflow-tooltip
|
|
270
|
-
<template #default="{ row }">
|
|
271
|
-
{{ row.name }}
|
|
272
|
-
</template>
|
|
273
|
-
</el-table-column>
|
|
298
|
+
<el-table-column prop="name" label="项目名称" min-width="200" sortable show-overflow-tooltip />
|
|
274
299
|
<el-table-column prop="program" label="项目集名称" min-width="200" sortable show-overflow-tooltip />
|
|
275
300
|
<el-table-column prop="projectType" label="项目类型" width="100" align="center" sortable>
|
|
276
301
|
<template #default="{ row }">
|
|
@@ -322,6 +347,6 @@ onActivated(() => {
|
|
|
322
347
|
</JnTable>
|
|
323
348
|
|
|
324
349
|
<!-- 表格分页 -->
|
|
325
|
-
<JnPagination :total="total" v-model="pagination" @change="
|
|
350
|
+
<JnPagination :total="total" v-model="pagination" @change="getTable" />
|
|
326
351
|
</el-card>
|
|
327
352
|
</template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { TableApi } from '@/api/demos/index'
|
|
3
|
+
import { JnDatetime } from '@jnrs/vue-core/components'
|
|
4
|
+
import CardTable from '@/components/common/CardTable.vue'
|
|
5
|
+
import ImageView from '@/components/common/ImageView.vue'
|
|
6
|
+
import PdfView from '@/components/common/PdfView.vue'
|
|
7
|
+
import DictTag from '@/components/common/DictTag.vue'
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<CardTable :getTableDataApi="TableApi">
|
|
12
|
+
<template #header>项目列表</template>
|
|
13
|
+
<template #table>
|
|
14
|
+
<el-table-column prop="name" label="项目名称" min-width="200" sortable show-overflow-tooltip />
|
|
15
|
+
<el-table-column prop="projectType" label="项目类型" width="100" align="center" sortable>
|
|
16
|
+
<template #default="{ row }">
|
|
17
|
+
<DictTag dictName="projectType" :value="row.projectType" />
|
|
18
|
+
</template>
|
|
19
|
+
</el-table-column>
|
|
20
|
+
<el-table-column prop="manager" label="项目经理" width="100" align="center" sortable />
|
|
21
|
+
<el-table-column prop="plannedStartDate" label="计划开始时间" width="130" align="center" sortable>
|
|
22
|
+
<template #default="{ row }">
|
|
23
|
+
<JnDatetime :value="row.plannedStartDate" />
|
|
24
|
+
</template>
|
|
25
|
+
</el-table-column>
|
|
26
|
+
<el-table-column prop="description" label="描述" min-width="200" show-overflow-tooltip />
|
|
27
|
+
<el-table-column prop="imageDocument" label="图片" width="100" align="center" sortable>
|
|
28
|
+
<template #default="{ row }">
|
|
29
|
+
<ImageView :loadKeys="row.newImageFiles" preview height="50px" />
|
|
30
|
+
</template>
|
|
31
|
+
</el-table-column>
|
|
32
|
+
<el-table-column prop="attachmentDocument" label="附件" width="100" align="center" sortable>
|
|
33
|
+
<template #default="{ row }">
|
|
34
|
+
<PdfView :loadKeys="row.newAttachmentFile" isPdf />
|
|
35
|
+
</template>
|
|
36
|
+
</el-table-column>
|
|
37
|
+
</template>
|
|
38
|
+
</CardTable>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<style lang="scss" scoped></style>
|
|
@@ -3,7 +3,7 @@ import { ref } from 'vue'
|
|
|
3
3
|
import { ElMessage } from 'element-plus'
|
|
4
4
|
import { objectToFormData } from '@/utils/packages'
|
|
5
5
|
import { LoginApi, UserInfoApi } from '@/api/system'
|
|
6
|
-
import { NotFoundApi,
|
|
6
|
+
import { NotFoundApi, NoAuthApi, DetailsApi } from '@/api/demos'
|
|
7
7
|
import { getFileUrl } from '@/utils/file'
|
|
8
8
|
import { downloadFile } from '@/utils/packages'
|
|
9
9
|
|
|
@@ -36,7 +36,7 @@ const handleNotFoundApi = async () => {
|
|
|
36
36
|
|
|
37
37
|
const handleNoAuth = async (showErrorMsg = true) => {
|
|
38
38
|
try {
|
|
39
|
-
const res = await
|
|
39
|
+
const res = await NoAuthApi(showErrorMsg)
|
|
40
40
|
console.log(res)
|
|
41
41
|
} catch (error) {
|
|
42
42
|
console.log(error)
|
|
@@ -5,12 +5,16 @@ import { handleRouter } from '@jnrs/vue-core/router'
|
|
|
5
5
|
import type { MenuItem } from '@jnrs/vue-core'
|
|
6
6
|
import { hasPermission } from '@/utils/permissions'
|
|
7
7
|
import { MenuApi } from '@/api/system'
|
|
8
|
+
import { formatDateTime, formatWeekday } from '@jnrs/shared'
|
|
9
|
+
import { isFloatGtZero } from '@jnrs/shared/validator'
|
|
10
|
+
import { testI18n } from '@jnrs/shared/request'
|
|
8
11
|
|
|
9
12
|
const routeOptions = ref<MenuItem[]>([])
|
|
10
13
|
const currentRoute = ref('')
|
|
11
14
|
const datePicker = ref(new Date())
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const datetime = ref(formatDateTime() + ' ' + formatWeekday())
|
|
16
|
+
const validator = ref()
|
|
17
|
+
const testI18nInCore = ref('')
|
|
14
18
|
|
|
15
19
|
const handleMenuApi = async () => {
|
|
16
20
|
try {
|
|
@@ -26,11 +30,31 @@ const handleRouteChange = () => {
|
|
|
26
30
|
name: currentRoute.value
|
|
27
31
|
})
|
|
28
32
|
}
|
|
33
|
+
|
|
34
|
+
const changeI18n = () => {
|
|
35
|
+
datetime.value = formatDateTime() + ' ' + formatWeekday()
|
|
36
|
+
isFloatGtZero({}, '0', (msg) => {
|
|
37
|
+
validator.value = msg
|
|
38
|
+
})
|
|
39
|
+
testI18nInCore.value = testI18n()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onMounted(() => {
|
|
43
|
+
isFloatGtZero({}, '0', (msg) => {
|
|
44
|
+
validator.value = msg
|
|
45
|
+
})
|
|
46
|
+
testI18nInCore.value = testI18n()
|
|
47
|
+
})
|
|
29
48
|
</script>
|
|
30
49
|
|
|
31
50
|
<template>
|
|
32
51
|
<div>
|
|
33
52
|
<h3>Playground - 功能测试</h3>
|
|
53
|
+
<p>国际化测试</p>
|
|
54
|
+
<div>@jnrs/shared: {{ datetime }}</div>
|
|
55
|
+
<div>@jnrs/shared/validator: {{ validator }}</div>
|
|
56
|
+
<div>@jnrs/shared/request: {{ testI18nInCore }}</div>
|
|
57
|
+
<el-button type="primary" size="small" @click="changeI18n">切换语言后点击按钮更新国际化显示</el-button>
|
|
34
58
|
<p>权限测试(admin 账号拥有全部权限,请用非 admin 账号测试该功能)</p>
|
|
35
59
|
<ul>
|
|
36
60
|
<li>
|
|
@@ -45,18 +45,22 @@ const submitForm = async () => {
|
|
|
45
45
|
const { token, dict, ...restParameter } = loginRes
|
|
46
46
|
const loginDateTime = formatDateTime() + ' ' + formatWeekday()
|
|
47
47
|
|
|
48
|
-
// 获取角色列表,将登录人角色对应的权限叠加给当前登录人(一次性获取叠加)
|
|
49
|
-
const roleRes = await RoleApi()
|
|
50
48
|
let permissionsMerger = restParameter.permissions
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
try {
|
|
50
|
+
// 获取角色列表,将登录人角色对应的权限叠加给当前登录人(一次性获取叠加)
|
|
51
|
+
const roleRes = await RoleApi()
|
|
52
|
+
if (Array.isArray(restParameter.permissions)) {
|
|
53
|
+
permissionsMerger = [
|
|
54
|
+
...new Set([
|
|
55
|
+
...restParameter.permissions,
|
|
56
|
+
...(roleRes.find((r) => r.value === restParameter.role)?.permissions ?? []).flat()
|
|
57
|
+
])
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
setRole(roleRes) // 由于是一次性获取叠加,此处不需要保存角色列表到全局状态
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.log(e)
|
|
58
63
|
}
|
|
59
|
-
setRole(roleRes) // 由于是一次性获取叠加,此处不需要保存角色列表到全局状态
|
|
60
64
|
|
|
61
65
|
setUserInfo({
|
|
62
66
|
...restParameter,
|
|
@@ -153,6 +157,7 @@ const submitForm = async () => {
|
|
|
153
157
|
|
|
154
158
|
.topBarBtn {
|
|
155
159
|
font-size: 22px;
|
|
160
|
+
color: #c2c2c2;
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
.topFixed_right {
|
|
@@ -163,9 +168,9 @@ const submitForm = async () => {
|
|
|
163
168
|
display: flex;
|
|
164
169
|
align-items: center;
|
|
165
170
|
justify-content: space-around;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
border-radius: 10px;
|
|
171
|
+
padding: 8px 16px;
|
|
172
|
+
// background: var(--jnrs-card-primary);
|
|
173
|
+
// border-radius: 10px;
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
.card {
|
|
@@ -204,7 +209,6 @@ const submitForm = async () => {
|
|
|
204
209
|
.title {
|
|
205
210
|
text-align: center;
|
|
206
211
|
font-size: 24px;
|
|
207
|
-
letter-spacing: 4px;
|
|
208
212
|
white-space: nowrap;
|
|
209
213
|
font-weight: normal;
|
|
210
214
|
font-family: AlimamaShuHeiTi-Bold;
|
|
@@ -289,7 +293,6 @@ const submitForm = async () => {
|
|
|
289
293
|
position: absolute;
|
|
290
294
|
bottom: 5px;
|
|
291
295
|
font-size: 12px;
|
|
292
|
-
letter-spacing: 2px;
|
|
293
296
|
text-transform: uppercase;
|
|
294
297
|
color: rgba(0, 0, 0, 0.1);
|
|
295
298
|
}
|