vue3-element-plus-app-chu 0.0.14 → 0.0.16
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/README.md +2 -0
- package/package.json +1 -1
- package/src/layout/index.vue +38 -9
- package/src/router/index.ts +34 -27
- package/src/router/menus.ts +17 -5
- package/src/utils/request.ts +3 -1
- package/src/view/dcps/README.md +38 -41
- package/src/view/dcps/auth/index.vue +147 -8
- package/src/view/dcps/estimation/api.ts +25 -30
- package/src/view/dcps/estimation/index.vue +307 -7
- package/src/view/dcps/forecast/api.ts +31 -36
- package/src/view/dcps/forecast/index.vue +987 -13
- package/src/view/dcps/{injection → injection-management/injection}/index.vue +150 -5
- package/src/view/dcps/write-off/api.ts +34 -34
- package/src/view/dcps/write-off/index.vue +771 -33
- package/src/view/dcps/auth/components/.gitkeep +0 -0
- package/src/view/dcps/auth/components/AuthorizationDetailDialog.vue +0 -173
- package/src/view/dcps/estimation/components/EstimationDetailDialog.vue +0 -225
- package/src/view/dcps/estimation/utils.ts +0 -113
- package/src/view/dcps/forecast/components/ForecastDetailView.vue +0 -155
- package/src/view/dcps/forecast/components/ForecastEditorView.vue +0 -331
- package/src/view/dcps/forecast/components/ForecastListView.vue +0 -319
- package/src/view/dcps/forecast/components/ForecastPageShell.vue +0 -52
- package/src/view/dcps/forecast/list-refresh.ts +0 -11
- package/src/view/dcps/forecast/row-cache.ts +0 -18
- package/src/view/dcps/forecast/utils.ts +0 -169
- package/src/view/dcps/injection/components/InjectionDetailDialog.vue +0 -173
- package/src/view/dcps/runtime.ts +0 -38
- package/src/view/dcps/write-off/components/FlowWriteOffView.vue +0 -300
- package/src/view/dcps/write-off/components/ForecastWriteOffView.vue +0 -438
- package/src/view/dcps/write-off/utils.ts +0 -131
- /package/src/view/dcps/{balance → injection-management/balance}/api.ts +0 -0
- /package/src/view/dcps/{balance → injection-management/balance}/index.vue +0 -0
- /package/src/view/dcps/{balance → injection-management/balance}/typs.ts +0 -0
- /package/src/view/dcps/{injection → injection-management/injection}/api.ts +0 -0
- /package/src/view/dcps/{injection → injection-management/injection}/typs.ts +0 -0
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ pnpm dev:backend # 只启动后端(backend/ 目录,tsx watch)
|
|
|
16
16
|
|
|
17
17
|
应用外壳位于 `src/layout/index.vue`,由 `App.vue` 通过 `RouterView` 挂载,统一承载侧边导航、折叠状态和业务页面内容。菜单配置在 `src/router/menus.ts`,路由表在 `src/router/index.ts`,新增业务页面时两处各追加一项即可。业务地址、浏览器前进后退和刷新都由 vue-router 驱动;头寸预报列表页由 `KeepAlive` 缓存,返回时保留筛选和分页状态。
|
|
18
18
|
|
|
19
|
+
“注资/预注资管理”为一级分组菜单,下设“数字人民币注资明细查询”和“数字人民币账户行余额查询申请”。页面目录统一放在 `src/view/dcps/injection-management/` 下,路由分别为 `/injection-management/injection` 和 `/injection-management/balance`;旧地址 `/injection`、`/balance` 继续可用。
|
|
20
|
+
|
|
19
21
|
## 数字人民币头寸预报
|
|
20
22
|
|
|
21
23
|
默认入口为数字人民币头寸预报(`/forecast`),侧边菜单可切换至其他数字人民币页面。
|
package/package.json
CHANGED
package/src/layout/index.vue
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, onMounted, ref } from 'vue'
|
|
2
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
|
3
|
+
import type { MenuInstance } from 'element-plus'
|
|
3
4
|
import { RouterView, useRoute, useRouter } from 'vue-router'
|
|
4
5
|
import { layoutMenus as menus } from '@/router/menus'
|
|
5
6
|
|
|
@@ -7,12 +8,19 @@ const route = useRoute()
|
|
|
7
8
|
const router = useRouter()
|
|
8
9
|
const firstMenu = menus[0]!
|
|
9
10
|
const collapsed = ref(false)
|
|
10
|
-
|
|
11
|
+
const menuRef = ref<MenuInstance>()
|
|
12
|
+
// 子页面通过 meta.menu 指向所属页面菜单,进入新增、详情时侧边栏仍保持高亮。
|
|
11
13
|
const activeMenu = computed(() => route.meta.menu ?? String(route.name ?? firstMenu.name))
|
|
12
14
|
const selectMenu = (name: string) => {
|
|
13
15
|
if (name !== route.name) void router.push({ name })
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
// 直接访问子页面或通过浏览器前进、后退切换时,自动展开所属一级菜单。
|
|
19
|
+
watch([activeMenu, menuRef], ([name, menu]) => {
|
|
20
|
+
const parentMenu = menus.find((item) => item.children?.some((child) => child.name === name))
|
|
21
|
+
if (parentMenu) menu?.open(parentMenu.name)
|
|
22
|
+
}, { flush: 'post' })
|
|
23
|
+
|
|
16
24
|
onMounted(() => {
|
|
17
25
|
collapsed.value = window.innerWidth <= 760
|
|
18
26
|
})
|
|
@@ -31,14 +39,27 @@ onMounted(() => {
|
|
|
31
39
|
|
|
32
40
|
<div v-if="!collapsed" class="rail-caption">OPERATIONS</div>
|
|
33
41
|
<el-menu
|
|
42
|
+
ref="menuRef"
|
|
34
43
|
class="dcps-menu"
|
|
35
44
|
:default-active="activeMenu"
|
|
36
45
|
@select="selectMenu"
|
|
37
46
|
>
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
<template v-for="menu in menus" :key="menu.name">
|
|
48
|
+
<el-sub-menu v-if="menu.children?.length" :index="menu.name">
|
|
49
|
+
<template #title>
|
|
50
|
+
<span v-if="collapsed" class="menu-label menu-label--compact">{{ menu.compactTitle }}</span>
|
|
51
|
+
<span v-else class="menu-label">{{ menu.title }}</span>
|
|
52
|
+
</template>
|
|
53
|
+
<el-menu-item v-for="child in menu.children" :key="child.name" :index="child.name">
|
|
54
|
+
<span v-if="collapsed" class="menu-label menu-label--compact">{{ child.compactTitle }}</span>
|
|
55
|
+
<span v-else class="menu-label menu-label--child">{{ child.title }}</span>
|
|
56
|
+
</el-menu-item>
|
|
57
|
+
</el-sub-menu>
|
|
58
|
+
<el-menu-item v-else :index="menu.name">
|
|
59
|
+
<span v-if="collapsed" class="menu-label menu-label--compact">{{ menu.compactTitle }}</span>
|
|
60
|
+
<span v-else class="menu-label">{{ menu.title }}</span>
|
|
61
|
+
</el-menu-item>
|
|
62
|
+
</template>
|
|
42
63
|
</el-menu>
|
|
43
64
|
|
|
44
65
|
<div class="rail-bottom">
|
|
@@ -99,7 +120,9 @@ onMounted(() => {
|
|
|
99
120
|
|
|
100
121
|
.is-collapsed .side-rail { width: 76px; }
|
|
101
122
|
|
|
102
|
-
.is-collapsed .dcps-menu :deep(.el-menu-item)
|
|
123
|
+
.is-collapsed .dcps-menu :deep(.el-menu-item),
|
|
124
|
+
.is-collapsed .dcps-menu :deep(.el-sub-menu__title) { padding: 0 6px !important; }
|
|
125
|
+
.is-collapsed .dcps-menu :deep(.el-sub-menu__icon-arrow) { display: none; }
|
|
103
126
|
|
|
104
127
|
.rail-brand {
|
|
105
128
|
display: flex;
|
|
@@ -141,7 +164,8 @@ onMounted(() => {
|
|
|
141
164
|
border-right: 0;
|
|
142
165
|
}
|
|
143
166
|
|
|
144
|
-
.dcps-menu :deep(.el-menu-item)
|
|
167
|
+
.dcps-menu :deep(.el-menu-item),
|
|
168
|
+
.dcps-menu :deep(.el-sub-menu__title) {
|
|
145
169
|
height: 46px;
|
|
146
170
|
margin: 3px 0;
|
|
147
171
|
padding: 0 12px !important;
|
|
@@ -150,10 +174,14 @@ onMounted(() => {
|
|
|
150
174
|
font-size: 12px;
|
|
151
175
|
}
|
|
152
176
|
|
|
177
|
+
.dcps-menu :deep(.el-sub-menu .el-menu-item) { height: auto; min-height: 46px; padding: 9px 12px 9px 24px !important; }
|
|
178
|
+
.dcps-menu :deep(.el-sub-menu__title) { padding-right: 32px !important; }
|
|
179
|
+
.dcps-menu :deep(.el-sub-menu.is-active > .el-sub-menu__title) { color: var(--el-color-primary); }
|
|
153
180
|
.dcps-menu :deep(.el-menu-item:hover) { color: var(--el-color-primary); }
|
|
154
181
|
.dcps-menu :deep(.el-menu-item.is-active) { border-color: var(--el-color-primary-light-8); color: var(--el-color-primary); background: var(--el-color-primary-light-9); }
|
|
155
182
|
.dcps-menu :deep(.el-menu-item.is-active::before) { position: absolute; left: 0; width: 3px; height: 19px; border-radius: 0 4px 4px 0; background: var(--el-color-primary); content: ''; }
|
|
156
183
|
.menu-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.4; }
|
|
184
|
+
.menu-label--child { white-space: normal; }
|
|
157
185
|
|
|
158
186
|
.menu-label--compact {
|
|
159
187
|
width: 26px;
|
|
@@ -212,6 +240,7 @@ onMounted(() => {
|
|
|
212
240
|
.rail-bottom { justify-content: center; margin-right: 0; margin-left: 0; padding-right: 0; padding-left: 0; }
|
|
213
241
|
.collapse-button { justify-content: center; padding: 0; }
|
|
214
242
|
.is-collapsed .side-rail { width: 64px; }
|
|
215
|
-
.dcps-menu :deep(.el-menu-item)
|
|
243
|
+
.dcps-menu :deep(.el-menu-item),
|
|
244
|
+
.dcps-menu :deep(.el-sub-menu__title) { justify-content: center; padding: 0 !important; }
|
|
216
245
|
}
|
|
217
246
|
</style>
|
package/src/router/index.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
|
2
2
|
import AppLayout from '@/layout/index.vue'
|
|
3
3
|
import DigitalRmbAuthorization from '@/view/dcps/auth/index.vue'
|
|
4
|
-
import DigitalRmbAccountBalance from '@/view/dcps/balance/index.vue'
|
|
4
|
+
import DigitalRmbAccountBalance from '@/view/dcps/injection-management/balance/index.vue'
|
|
5
5
|
import DigitalRmbForecast from '@/view/dcps/forecast/index.vue'
|
|
6
|
-
import
|
|
7
|
-
import ForecastDetailView from '@/view/dcps/forecast/components/ForecastDetailView.vue'
|
|
8
|
-
import DigitalRmbInjection from '@/view/dcps/injection/index.vue'
|
|
6
|
+
import DigitalRmbInjection from '@/view/dcps/injection-management/injection/index.vue'
|
|
9
7
|
import DigitalRmbWriteOff from '@/view/dcps/write-off/index.vue'
|
|
10
8
|
import DigitalRmbPositionEstimation from '@/view/dcps/estimation/index.vue'
|
|
11
|
-
import { recallForecastRow } from '@/view/dcps/forecast/row-cache'
|
|
12
9
|
|
|
13
10
|
declare module 'vue-router' {
|
|
14
11
|
interface RouteMeta {
|
|
15
|
-
/**
|
|
12
|
+
/** 所属页面菜单的路由名;新增、详情等子页面用它保持侧边菜单高亮。 */
|
|
16
13
|
menu?: string
|
|
17
14
|
/** 浏览器标题使用的页面名称。 */
|
|
18
15
|
title?: string
|
|
@@ -34,7 +31,7 @@ const routes: RouteRecordRaw[] = [
|
|
|
34
31
|
{
|
|
35
32
|
path: 'forecast/create',
|
|
36
33
|
name: 'forecast-create',
|
|
37
|
-
component:
|
|
34
|
+
component: DigitalRmbForecast,
|
|
38
35
|
meta: { menu: 'forecast', title: '新增数字人民币头寸预报' },
|
|
39
36
|
props: (route) => ({
|
|
40
37
|
mode: 'create',
|
|
@@ -44,22 +41,22 @@ const routes: RouteRecordRaw[] = [
|
|
|
44
41
|
{
|
|
45
42
|
path: 'forecast/:predRefNo/edit',
|
|
46
43
|
name: 'forecast-edit',
|
|
47
|
-
component:
|
|
44
|
+
component: DigitalRmbForecast,
|
|
48
45
|
meta: { menu: 'forecast', title: '修改数字人民币头寸预报' },
|
|
49
|
-
props: (route) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
46
|
+
props: (route) => ({
|
|
47
|
+
mode: 'edit',
|
|
48
|
+
predRefNo: String(route.params.predRefNo ?? ''),
|
|
49
|
+
}),
|
|
53
50
|
},
|
|
54
51
|
{
|
|
55
52
|
path: 'forecast/:predRefNo',
|
|
56
53
|
name: 'forecast-detail',
|
|
57
|
-
component:
|
|
54
|
+
component: DigitalRmbForecast,
|
|
58
55
|
meta: { menu: 'forecast', title: '数字人民币头寸预报详情' },
|
|
59
|
-
props: (route) => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
},
|
|
56
|
+
props: (route) => ({
|
|
57
|
+
mode: 'detail',
|
|
58
|
+
predRefNo: String(route.params.predRefNo ?? ''),
|
|
59
|
+
}),
|
|
63
60
|
},
|
|
64
61
|
{
|
|
65
62
|
path: 'estimation',
|
|
@@ -68,16 +65,26 @@ const routes: RouteRecordRaw[] = [
|
|
|
68
65
|
meta: { menu: 'estimation', title: '数字人民币头寸匡算' },
|
|
69
66
|
},
|
|
70
67
|
{
|
|
71
|
-
path: 'injection',
|
|
72
|
-
name: 'injection',
|
|
73
|
-
|
|
74
|
-
meta: {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
path: 'injection-management',
|
|
69
|
+
name: 'injection-management',
|
|
70
|
+
redirect: { name: 'injection' },
|
|
71
|
+
meta: { title: '注资/预注资管理' },
|
|
72
|
+
children: [
|
|
73
|
+
{
|
|
74
|
+
path: 'injection',
|
|
75
|
+
alias: '/injection',
|
|
76
|
+
name: 'injection',
|
|
77
|
+
component: DigitalRmbInjection,
|
|
78
|
+
meta: { menu: 'injection', title: '数字人民币注资明细查询' },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
path: 'balance',
|
|
82
|
+
alias: '/balance',
|
|
83
|
+
name: 'balance',
|
|
84
|
+
component: DigitalRmbAccountBalance,
|
|
85
|
+
meta: { menu: 'balance', title: '数字人民币账户行余额查询申请' },
|
|
86
|
+
},
|
|
87
|
+
],
|
|
81
88
|
},
|
|
82
89
|
{
|
|
83
90
|
path: 'auth',
|
package/src/router/menus.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
/** 侧边菜单项;name
|
|
2
|
-
|
|
1
|
+
/** 侧边菜单项;name 与对应的路由名保持一致。 */
|
|
2
|
+
interface LayoutMenuItem {
|
|
3
3
|
name: string
|
|
4
4
|
title: string
|
|
5
5
|
compactTitle: string
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/** 一级菜单可包含二级页面菜单。 */
|
|
9
|
+
export interface LayoutMenu extends LayoutMenuItem {
|
|
10
|
+
children?: LayoutMenuItem[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// 侧边菜单配置:业务页面可直接添加,或放入所属一级菜单的 children 中。
|
|
9
14
|
export const layoutMenus: LayoutMenu[] = [
|
|
10
15
|
{ name: 'forecast', title: '数字人民币头寸预报', compactTitle: '头寸预报' },
|
|
11
16
|
{ name: 'estimation', title: '数字人民币头寸匡算', compactTitle: '匡算' },
|
|
12
|
-
{
|
|
13
|
-
|
|
17
|
+
{
|
|
18
|
+
name: 'injection-management',
|
|
19
|
+
title: '注资/预注资管理',
|
|
20
|
+
compactTitle: '注资管理',
|
|
21
|
+
children: [
|
|
22
|
+
{ name: 'injection', title: '数字人民币注资明细查询', compactTitle: '注资明细' },
|
|
23
|
+
{ name: 'balance', title: '数字人民币账户行余额查询申请', compactTitle: '余额查询' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
14
26
|
{ name: 'auth', title: '数字人民币授权', compactTitle: '授权' },
|
|
15
27
|
{ name: 'write-off', title: '数字人民币核销', compactTitle: '核销' },
|
|
16
28
|
]
|
package/src/utils/request.ts
CHANGED
|
@@ -46,12 +46,14 @@ export interface StandardApiResponse<T = unknown> {
|
|
|
46
46
|
export type HttpRequestConfig = AxiosRequestConfig
|
|
47
47
|
|
|
48
48
|
// 默认将操作类型分配到 appHead,公共字段分配到 head,其余字段分配到 body。
|
|
49
|
+
// tranDate 在数币业务接口中是业务字段(如 140501001、140503001),平铺入参统一保留在 body;
|
|
50
|
+
// 需要作为公共交易日期时,请显式传入 { head: { tranDate } }。
|
|
49
51
|
const appHeadFields = new Set(['ip', 'opType'])
|
|
50
52
|
const headFields = new Set([
|
|
51
53
|
'adminUserId', 'agentBranchId', 'apprUserId', 'areaCode', 'batch',
|
|
52
54
|
'branchId', 'consumerId', 'consumerSeqNo', 'ctrlBits', 'digitalSign',
|
|
53
55
|
'extra', 'fileFlag', 'filePath', 'isBatch', 'msgId', 'origSender',
|
|
54
|
-
'pageIndex', 'pageSize', 'spanId', 'traceId', 'tranCode',
|
|
56
|
+
'pageIndex', 'pageSize', 'spanId', 'traceId', 'tranCode',
|
|
55
57
|
'tranTimestamp', 'userId', 'verifyUserId', 'versionId',
|
|
56
58
|
])
|
|
57
59
|
|
package/src/view/dcps/README.md
CHANGED
|
@@ -1,59 +1,56 @@
|
|
|
1
|
-
# DCPS
|
|
1
|
+
# DCPS 页面文件结构
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
`src/view/dcps` 下每个业务页面统一保持三个文件,不再拆分子组件、工具文件或运行时配置:
|
|
4
|
+
|
|
5
|
+
一级菜单为分组时,页面放在该分组目录下。例如“注资/预注资管理”的两个页面分别位于 `injection-management/injection/` 和 `injection-management/balance/`,各自保留下面三个文件。
|
|
6
|
+
|
|
7
|
+
| 文件 | 用途 |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `api.ts` | 当前页面用到的接口定义。 |
|
|
10
|
+
| `typs.ts` | 当前页面用到的接口类型、页面类型。 |
|
|
11
|
+
| `index.vue` | 页面入口,包含模板、页面内子视图(列表 / 表单 / 详情 / 弹窗)和页面内工具函数。 |
|
|
12
|
+
|
|
13
|
+
公共样式变量仍放在 `common.css`,由 `src/main.ts` 统一引入。
|
|
5
14
|
|
|
6
15
|
## 命名约定
|
|
7
16
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
17
|
+
- 所有页面组件的 `defineOptions({ name })` 统一以 `Dcps` 开头(PascalCase)。
|
|
18
|
+
- 页面入口统一命名为 `Dcps<模块>Page`。
|
|
19
|
+
- 页面内部的弹窗、表格、表单不再单独拆文件,直接写在 `index.vue` 中,相关状态和方法使用页面前缀区分。
|
|
20
|
+
- 路由名使用 kebab-case(如 `forecast-create`)。
|
|
12
21
|
|
|
13
|
-
##
|
|
22
|
+
## 路由与页面文件
|
|
14
23
|
|
|
15
24
|
路由定义在 `src/router/index.ts`,所有业务页面都挂在 `AppLayout`(`src/layout/index.vue`)下。
|
|
16
25
|
|
|
17
|
-
| 路由路径 | 路由 name | 页面文件 |
|
|
26
|
+
| 路由路径 | 路由 name | 页面文件 | 页面形态 | 页面标题(meta.title) |
|
|
18
27
|
| --- | --- | --- | --- | --- |
|
|
19
|
-
| `/forecast` | `forecast` | `forecast/index.vue` |
|
|
20
|
-
| `/forecast/create` | `forecast-create` | `forecast/
|
|
21
|
-
| `/forecast/:predRefNo/edit` | `forecast-edit` | `forecast/
|
|
22
|
-
| `/forecast/:predRefNo` | `forecast-detail` | `forecast/
|
|
23
|
-
| `/estimation` | `estimation` | `estimation/index.vue` |
|
|
24
|
-
| `/injection` | `injection` |
|
|
25
|
-
| `/
|
|
26
|
-
| `/
|
|
27
|
-
| `/
|
|
28
|
+
| `/forecast` | `forecast` | `forecast/index.vue` | 列表 | 数字人民币头寸预报 |
|
|
29
|
+
| `/forecast/create` | `forecast-create` | `forecast/index.vue` | 新增(`mode: 'create'`) | 新增数字人民币头寸预报 |
|
|
30
|
+
| `/forecast/:predRefNo/edit` | `forecast-edit` | `forecast/index.vue` | 修改(`mode: 'edit'`) | 修改数字人民币头寸预报 |
|
|
31
|
+
| `/forecast/:predRefNo` | `forecast-detail` | `forecast/index.vue` | 详情(`mode: 'detail'`) | 数字人民币头寸预报详情 |
|
|
32
|
+
| `/estimation` | `estimation` | `estimation/index.vue` | 页面 | 数字人民币头寸匡算 |
|
|
33
|
+
| `/injection-management` | `injection-management` | — | 重定向到 `injection` | 注资/预注资管理 |
|
|
34
|
+
| `/injection-management/injection` | `injection` | `injection-management/injection/index.vue` | 页面 | 数字人民币注资明细查询 |
|
|
35
|
+
| `/injection-management/balance` | `balance` | `injection-management/balance/index.vue` | 页面 | 数字人民币账户行余额查询申请 |
|
|
36
|
+
| `/auth` | `auth` | `auth/index.vue` | 页面 | 数字人民币授权 |
|
|
37
|
+
| `/write-off` | `write-off` | `write-off/index.vue` | 页面 | 数字人民币核销 |
|
|
28
38
|
| `/` | — | 重定向到 `forecast` | — | — |
|
|
29
39
|
| `/:pathMatch(.*)*` | — | 重定向到 `forecast`(404 兜底) | — | — |
|
|
30
40
|
|
|
31
41
|
补充说明:
|
|
32
42
|
|
|
33
|
-
-
|
|
34
|
-
- `forecast
|
|
35
|
-
- 侧边菜单配置在 `src/router/menus.ts`,其中 `name`
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
## 非路由子组件
|
|
39
|
-
|
|
40
|
-
以下组件不直接对应路由,由页面组件在内部引用:
|
|
41
|
-
|
|
42
|
-
| 组件文件 | 组件 name | 使用位置 | 说明 |
|
|
43
|
-
| --- | --- | --- | --- |
|
|
44
|
-
| `forecast/components/ForecastPageShell.vue` | `DcpsForecastPageShell` | `DcpsForecastEditor`、`DcpsForecastDetail` | 新增 / 修改 / 详情页共用外壳,提供返回按钮和标题 |
|
|
45
|
-
| `forecast/components/ForecastListView.vue` | `DcpsForecastList` | `DcpsForecastPage` | 头寸预报列表,负责查询与展示,通过事件向外抛出新增 / 修改 / 详情 |
|
|
46
|
-
| `estimation/components/EstimationDetailDialog.vue` | `DcpsEstimationDetailDialog` | `DcpsEstimationPage` | 匡算单元格详情弹窗 |
|
|
47
|
-
| `injection/components/InjectionDetailDialog.vue` | `DcpsInjectionDetailDialog` | `DcpsInjectionPage` | 注资明细详情弹窗 |
|
|
48
|
-
| `auth/components/AuthorizationDetailDialog.vue` | `DcpsAuthDetailDialog` | `DcpsAuthPage` | 授权任务详情弹窗 |
|
|
49
|
-
| `write-off/components/ForecastWriteOffView.vue` | `DcpsForecastWriteOff` | `DcpsWriteOffPage` | 核销页上方预报信息表格 |
|
|
50
|
-
| `write-off/components/FlowWriteOffView.vue` | `DcpsFlowWriteOff` | `DcpsWriteOffPage` | 核销页下方流水信息表格 |
|
|
43
|
+
- 头寸预报的列表、新增、修改、详情共用 `forecast/index.vue`,通过路由 `props.mode` 切换页面形态;修改和详情通过 `props.predRefNo` 接收路由参数。
|
|
44
|
+
- `forecast/index.vue` 顶部使用一个普通 `<script>` 块保存列表记录缓存,跳转修改或详情前暂存列表行,用于补齐详情接口未返回的字段。
|
|
45
|
+
- 侧边菜单配置在 `src/router/menus.ts`,其中 `name` 必须与对应路由的 `name` 保持一致;分组菜单通过 `children` 配置二级页面,页面的 `meta.menu` 指向自己的菜单项。
|
|
46
|
+
- “注资/预注资管理”对应 `injection-management` 分组,包含注资明细查询和账户行余额查询申请两个页面;访问子页面时自动展开父菜单。旧地址 `/injection`、`/balance` 保留为对应页面的别名。
|
|
47
|
+
- `src/layout/index.vue` 使用 `<KeepAlive include="DcpsForecastPage">` 缓存头寸预报页面;新增、修改、详情按路由 `key` 分别缓存,返回列表时自动重新查询。
|
|
51
48
|
|
|
52
49
|
## 新增页面检查清单
|
|
53
50
|
|
|
54
|
-
1. 在 `src/view/dcps/<模块>/` 下新建 `index.vue
|
|
55
|
-
2.
|
|
56
|
-
3.
|
|
57
|
-
4.
|
|
58
|
-
5.
|
|
51
|
+
1. 在 `src/view/dcps/<模块>/` 下新建 `index.vue`;如归属某个分组,放在 `src/view/dcps/<分组>/<模块>/` 下,并添加 `defineOptions({ name: 'Dcps<模块>Page' })`。
|
|
52
|
+
2. 同目录补充 `api.ts`、`typs.ts`;不要再创建 `components/`、`utils.ts`、`runtime.ts` 等额外文件。
|
|
53
|
+
3. 弹窗、子表格、表单直接写在 `index.vue` 中;工具函数写在 `index.vue` 的 `<script setup>` 内。
|
|
54
|
+
4. 在 `src/router/index.ts` 中新增路由;分组页面添加到对应分组路由的 `children` 下,配置 `path`、`name`、`component`、`meta.menu`、`meta.title`。
|
|
55
|
+
5. 如需出现在侧边菜单,在 `src/router/menus.ts` 中追加菜单项或添加到分组的 `children` 中,`name` 与对应路由名一致。
|
|
59
56
|
6. 更新本文档的对应关系表。
|
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
|
3
3
|
import { ElMessage } from 'element-plus'
|
|
4
4
|
import { ArtDataTable, type ColumnOption, type PaginationOptions, type TableSettings } from '@/components/table'
|
|
5
|
-
import AuthorizationDetailDialog from './components/AuthorizationDetailDialog.vue'
|
|
6
5
|
import API from './api'
|
|
7
|
-
import { authorizationRuntime } from '../runtime'
|
|
8
6
|
import type { AuthorizationFilters, AuthorizationQueryParams, AuthorizationTask } from './typs'
|
|
9
7
|
|
|
10
8
|
defineOptions({ name: 'DcpsAuthPage' })
|
|
11
9
|
|
|
12
10
|
type Decision = 'approve' | 'reject'
|
|
11
|
+
|
|
12
|
+
// 授权页面的审批人和默认创建日期;日期格式为 YYYYMMDD。
|
|
13
|
+
const authorizationRuntime = {
|
|
14
|
+
approverId: '10092', // 审批人工号。
|
|
15
|
+
approverName: '王干事', // 审批人姓名。
|
|
16
|
+
createTmBegin: '20261012', // 默认创建开始日期。
|
|
17
|
+
createTmEnd: '20261012', // 默认创建结束日期。
|
|
18
|
+
}
|
|
13
19
|
type AmountSortOrder = 'ascending' | 'descending' | null
|
|
14
20
|
|
|
15
21
|
// 初始查询条件;空字符串表示不限制该条件,重置时恢复此配置。
|
|
@@ -56,6 +62,52 @@ let disposed = false // 页面是否已卸载,避免未完成请求继续更
|
|
|
56
62
|
const detailDialogVisible = ref(false) // 详情弹窗是否显示。
|
|
57
63
|
const detailTask = ref<any>(null) // 当前查看详情的原始任务,未选择时为 null。
|
|
58
64
|
|
|
65
|
+
// 详情分组配置;title 为分组标题,fields 每项为 [任务字段名, 展示名称]。
|
|
66
|
+
const detailGroups = [
|
|
67
|
+
{
|
|
68
|
+
title: '任务信息',
|
|
69
|
+
fields: [
|
|
70
|
+
['id', '任务 ID'],
|
|
71
|
+
['bizType', '业务类型'],
|
|
72
|
+
['createTm', '创建时间'],
|
|
73
|
+
['txnTm', '交易发起时间'],
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
title: '交易信息',
|
|
78
|
+
fields: [
|
|
79
|
+
['collePayerName', '付款人名称'],
|
|
80
|
+
['payeeName', '收款人名称'],
|
|
81
|
+
['payWltId', '付款钱包 ID'],
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
title: '机构信息',
|
|
86
|
+
fields: [
|
|
87
|
+
['authoOrgName', '授权机构名称'],
|
|
88
|
+
['authoOrgNo', '授权机构编号'],
|
|
89
|
+
['paySideAcctBeloOrgName', '付款方账户所属机构名称'],
|
|
90
|
+
['paySideAcctBeloOrgNo', '付款方账户所属机构编号'],
|
|
91
|
+
['collSideAcctBeloOrgName', '收款方账户所属机构名称'],
|
|
92
|
+
['collSideAcctBeloOrgNo', '收款方账户所属机构编号'],
|
|
93
|
+
['subBrchName', '落地支行名称'],
|
|
94
|
+
['subBrchBankNo', '落地支行行号'],
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
title: '审批信息',
|
|
99
|
+
fields: [
|
|
100
|
+
['apprvrId', '审批人 ID'],
|
|
101
|
+
['apprvrName', '审批人名称'],
|
|
102
|
+
['approveTm', '审批时间'],
|
|
103
|
+
['rmk', '审批备注'],
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
] as const
|
|
107
|
+
|
|
108
|
+
/** 读取详情展示值,空值显示占位符;field 为任务字段名。 */
|
|
109
|
+
const detailValue = (field: string) => detailValues.value?.[field]?.trim() || '—'
|
|
110
|
+
|
|
59
111
|
// 审批弹窗状态。
|
|
60
112
|
const dialogVisible = ref(false) // 审批弹窗是否显示。
|
|
61
113
|
const selectedTask = ref<any>(null) // 本次待处理的任务,提交时读取平台流水号。
|
|
@@ -441,13 +493,66 @@ onBeforeUnmount(() => {
|
|
|
441
493
|
</ArtDataTable>
|
|
442
494
|
</div>
|
|
443
495
|
|
|
444
|
-
|
|
496
|
+
<!-- 授权详情弹窗:按任务字段分组展示,底部可直接进入审批。 -->
|
|
497
|
+
<el-dialog
|
|
445
498
|
v-model="detailDialogVisible"
|
|
446
|
-
|
|
447
|
-
:
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
499
|
+
title="数字人民币授权详情"
|
|
500
|
+
style="--el-dialog-padding-primary: 10px; padding: 10px 16px; margin-bottom: 16px"
|
|
501
|
+
width="min(1280px, calc(100vw - 32px))"
|
|
502
|
+
top="16px"
|
|
503
|
+
append-to-body
|
|
504
|
+
destroy-on-close
|
|
505
|
+
>
|
|
506
|
+
<el-scrollbar class="detail-scrollbar">
|
|
507
|
+
<div v-if="detailTask" class="detail-content">
|
|
508
|
+
<!-- 交易摘要 -->
|
|
509
|
+
<header class="detail-summary">
|
|
510
|
+
<div>
|
|
511
|
+
<span class="detail-label">交易金额(元)</span>
|
|
512
|
+
<strong class="detail-amount">{{ detailValue('txnAmt') }}</strong>
|
|
513
|
+
</div>
|
|
514
|
+
<div>
|
|
515
|
+
<span class="detail-label">任务状态</span>
|
|
516
|
+
<strong class="detail-status">{{ detailValue('taskSts') }}</strong>
|
|
517
|
+
</div>
|
|
518
|
+
<div class="detail-reference">
|
|
519
|
+
<span class="detail-label">平台流水号</span>
|
|
520
|
+
<strong>{{ detailValue('platfSeqNo') }}</strong>
|
|
521
|
+
</div>
|
|
522
|
+
</header>
|
|
523
|
+
|
|
524
|
+
<!-- group 为当前详情分组,包含标题和字段配置。 -->
|
|
525
|
+
<section v-for="group in detailGroups" :key="group.title" class="detail-group" :aria-label="group.title">
|
|
526
|
+
<h3>{{ group.title }}</h3>
|
|
527
|
+
<dl class="detail-fields">
|
|
528
|
+
<!-- field 为任务字段名,label 为字段的中文展示名称。 -->
|
|
529
|
+
<div
|
|
530
|
+
v-for="[field, label] in group.fields"
|
|
531
|
+
:key="field"
|
|
532
|
+
class="detail-field"
|
|
533
|
+
:class="{ 'detail-field--remark': field === 'rmk' }"
|
|
534
|
+
>
|
|
535
|
+
<dt>{{ label }}</dt>
|
|
536
|
+
<dd>{{ detailValue(field) }}</dd>
|
|
537
|
+
</div>
|
|
538
|
+
</dl>
|
|
539
|
+
</section>
|
|
540
|
+
</div>
|
|
541
|
+
<el-empty v-else description="暂无任务详情" :image-size="64" />
|
|
542
|
+
</el-scrollbar>
|
|
543
|
+
|
|
544
|
+
<template #footer>
|
|
545
|
+
<el-button size="small" @click="detailDialogVisible = false">关闭</el-button>
|
|
546
|
+
<el-button
|
|
547
|
+
size="small"
|
|
548
|
+
type="primary"
|
|
549
|
+
:disabled="loading || !!pendingDecision || !canApprove(detailTask)"
|
|
550
|
+
@click="openDecisionDialog(detailTask)"
|
|
551
|
+
>
|
|
552
|
+
审批
|
|
553
|
+
</el-button>
|
|
554
|
+
</template>
|
|
555
|
+
</el-dialog>
|
|
451
556
|
<!-- 审批提交期间禁止关闭弹窗,等待服务端返回结果。 -->
|
|
452
557
|
<el-dialog
|
|
453
558
|
v-model="dialogVisible"
|
|
@@ -546,6 +651,40 @@ onBeforeUnmount(() => {
|
|
|
546
651
|
.filter-actions { flex-shrink: 0; margin-left: auto; }
|
|
547
652
|
.amount-cell { color: var(--el-color-primary-dark-2); font-variant-numeric: tabular-nums; font-weight: 600; }
|
|
548
653
|
.decision-summary { margin-bottom: 20px; overflow-wrap: anywhere; }
|
|
654
|
+
|
|
655
|
+
/* ---------- 授权详情弹窗 ---------- */
|
|
656
|
+
/* 为弹窗边距、标题和底部按钮预留空间,其余高度用于完整展示详情。 */
|
|
657
|
+
.detail-scrollbar :deep(.el-scrollbar__wrap) {
|
|
658
|
+
max-height: calc(100vh - 120px);
|
|
659
|
+
max-height: calc(100dvh - 120px);
|
|
660
|
+
}
|
|
661
|
+
.detail-content { display: grid; gap: 8px; font-size: 13px; line-height: 1.4; }
|
|
662
|
+
.detail-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; padding: 14px; border-radius: 8px; background: var(--el-color-primary-light-9); overflow-wrap: anywhere; }
|
|
663
|
+
.detail-label,
|
|
664
|
+
.detail-field dt { color: var(--el-text-color-secondary); font-size: 12px; }
|
|
665
|
+
.detail-amount { display: block; margin-top: 4px; color: var(--el-color-primary-dark-2); font-size: 24px; font-variant-numeric: tabular-nums; line-height: 1.3; }
|
|
666
|
+
.detail-status { display: block; margin-top: 6px; color: var(--el-text-color-primary); font-size: 15px; }
|
|
667
|
+
.detail-reference { display: grid; grid-column: span 2; align-content: start; gap: 4px; }
|
|
668
|
+
.detail-group { display: grid; grid-template-columns: 72px minmax(0, 1fr); align-items: start; gap: 8px; padding: 12px; border: 1px solid var(--el-border-color-lighter); border-radius: 8px; }
|
|
669
|
+
.detail-group h3 { margin: 4px 0 0; padding-left: 8px; border-left: 3px solid var(--el-color-primary); color: var(--el-text-color-primary); font-size: 14px; line-height: 20px; }
|
|
670
|
+
.detail-fields { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 0; }
|
|
671
|
+
.detail-field { min-width: 0; padding: 8px 12px; border-radius: 6px; background: var(--el-fill-color-lighter); }
|
|
672
|
+
.detail-field--remark { grid-column: 1 / -1; }
|
|
673
|
+
.detail-field dd { margin: 2px 0 0; color: var(--el-text-color-primary); font-weight: 600; overflow-wrap: anywhere; white-space: pre-wrap; }
|
|
674
|
+
|
|
675
|
+
@media (max-width: 1024px) {
|
|
676
|
+
.detail-group { grid-template-columns: minmax(0, 1fr); }
|
|
677
|
+
.detail-group h3 { margin-top: 0; }
|
|
678
|
+
.detail-summary,
|
|
679
|
+
.detail-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
@media (max-width: 600px) {
|
|
683
|
+
.detail-summary,
|
|
684
|
+
.detail-fields { grid-template-columns: minmax(0, 1fr); }
|
|
685
|
+
.detail-reference { grid-column: auto; }
|
|
686
|
+
}
|
|
687
|
+
|
|
549
688
|
@media (max-width: 540px) {
|
|
550
689
|
.filter-form :deep(.el-form-item) { flex-basis: 100%; }
|
|
551
690
|
.filter-actions { margin-left: 0; }
|
|
@@ -1,34 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
EstimationQueryParams,
|
|
4
|
-
Tam140503001InBody,
|
|
5
|
-
Tam140503001OutBody,
|
|
6
|
-
Tam140503002InBody,
|
|
7
|
-
Tam140503002OutBody,
|
|
8
|
-
Tam140503003InBody,
|
|
9
|
-
Tam140503003OutBody,
|
|
10
|
-
Tam140503004InBody,
|
|
11
|
-
Tam140503004OutBody,
|
|
12
|
-
} from './typs'
|
|
1
|
+
import baseHttpClient from "@chic/http"
|
|
13
2
|
|
|
14
|
-
/**
|
|
15
|
-
* 数字人民币头寸匡算接口。
|
|
16
|
-
* 查询 140503001、导出 140503002、详情 140503003、历史预计调拨金额 140503004。
|
|
17
|
-
*/
|
|
18
3
|
export default {
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 查询数字人民币头寸匡算结果。
|
|
6
|
+
* @param params 查询参数:tranDate 为交易日期(YYYYMMDD),
|
|
7
|
+
* predictAllotInAmt、predictAllotOutAmt 为今日预计调拨收入、支出金额。
|
|
8
|
+
* 返回 B15、C3、C6、C7、C8、C11、D6、D7、D8、D11 十个单元格金额。
|
|
9
|
+
*/
|
|
10
|
+
queryListApi: (params: unknown) => baseHttpClient.post<any>("/TAM/0001/140503001", params),
|
|
11
|
+
/**
|
|
12
|
+
* 导出数字人民币头寸匡算结果。
|
|
13
|
+
* @param params 导出参数与查询一致:tranDate、predictAllotInAmt、predictAllotOutAmt;
|
|
14
|
+
* 响应只返回导出文件名 fileName。
|
|
15
|
+
*/
|
|
16
|
+
exportApi: (params: unknown) => baseHttpClient.post<any>("/TAM/0001/140503002", params),
|
|
17
|
+
/**
|
|
18
|
+
* 查询匡算单元格明细。
|
|
19
|
+
* @param params 查询参数:position 为单元格位置编码,tranDate 为交易日期(YYYYMMDD)。
|
|
20
|
+
* 响应包含预报明细 predList、流水明细 tranList 和总条数 totalCount。
|
|
21
|
+
*/
|
|
22
|
+
getDetailApi: (params: unknown) => baseHttpClient.post<any>("/TAM/0001/140503003", params),
|
|
23
|
+
/**
|
|
24
|
+
* 查询指定日期的历史预计调拨金额,用于预填查询条件。
|
|
25
|
+
* @param tranDate 交易日期,格式为 YYYYMMDD。
|
|
26
|
+
*/
|
|
32
27
|
queryHistoryApi: (tranDate: string) =>
|
|
33
|
-
|
|
28
|
+
baseHttpClient.post<any>("/TAM/0001/140503004", { tranDate })
|
|
34
29
|
}
|