vue3-element-plus-app-chu 0.0.13 → 0.0.15
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 +4 -4
- package/package.json +3 -2
- package/src/layout/index.vue +21 -36
- package/src/router/index.ts +107 -0
- package/src/router/menus.ts +16 -0
- package/src/utils/request.ts +3 -1
- package/src/view/dcps/README.md +52 -0
- package/src/view/dcps/auth/index.vue +151 -14
- package/src/view/dcps/balance/index.vue +4 -6
- package/src/view/dcps/common.css +7 -0
- package/src/view/dcps/estimation/api.ts +18 -148
- package/src/view/dcps/estimation/index.vue +312 -13
- package/src/view/dcps/forecast/api.ts +21 -218
- package/src/view/dcps/forecast/index.vue +996 -50
- package/src/view/dcps/injection/index.vue +154 -11
- package/src/view/dcps/write-off/api.ts +25 -180
- package/src/view/dcps/write-off/index.vue +776 -39
- package/src/view/dcps/auth/components/.gitkeep +0 -0
- package/src/view/dcps/auth/components/AuthorizationDetailDialog.vue +0 -171
- package/src/view/dcps/estimation/components/EstimationDetailDialog.vue +0 -222
- package/src/view/dcps/forecast/components/ForecastDetailView.vue +0 -145
- package/src/view/dcps/forecast/components/ForecastEditorView.vue +0 -320
- package/src/view/dcps/forecast/components/ForecastListView.vue +0 -322
- package/src/view/dcps/forecast/components/ForecastPageShell.vue +0 -56
- package/src/view/dcps/injection/components/InjectionDetailDialog.vue +0 -171
- package/src/view/dcps/runtime.ts +0 -38
- package/src/view/dcps/write-off/components/FlowWriteOffView.vue +0 -299
- package/src/view/dcps/write-off/components/ForecastWriteOffView.vue +0 -437
package/README.md
CHANGED
|
@@ -14,18 +14,18 @@ pnpm dev:backend # 只启动后端(backend/ 目录,tsx watch)
|
|
|
14
14
|
|
|
15
15
|
## 全局布局
|
|
16
16
|
|
|
17
|
-
应用外壳位于 `src/layout/index.vue`,由 `App.vue`
|
|
17
|
+
应用外壳位于 `src/layout/index.vue`,由 `App.vue` 通过 `RouterView` 挂载,统一承载侧边导航、折叠状态和业务页面内容。菜单配置在 `src/router/menus.ts`,路由表在 `src/router/index.ts`,新增业务页面时两处各追加一项即可。业务地址、浏览器前进后退和刷新都由 vue-router 驱动;头寸预报列表页由 `KeepAlive` 缓存,返回时保留筛选和分页状态。
|
|
18
18
|
|
|
19
19
|
## 数字人民币头寸预报
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
默认入口为数字人民币头寸预报(`/forecast`),侧边菜单可切换至其他数字人民币页面。
|
|
22
22
|
|
|
23
23
|
- 列表页只保留筛选卡片和表格卡片;筛选条件压在一行,窗口过窄时才换行。
|
|
24
|
-
-
|
|
24
|
+
- 新增、修改、详情不使用弹窗,分别对应 `/forecast/create`、`/forecast/:predRefNo/edit`、`/forecast/:predRefNo` 独立路由,页面顶部提供“返回列表”按钮;返回后列表的筛选和分页状态不变。
|
|
25
25
|
- 支持交易日期、客户、交易对手、机构、金额范围及业务代码筛选,分页、详情、新增、修改和批量删除。
|
|
26
26
|
- 金额、账号及流水号使用字符串处理。业务状态与类型代码保留接口原值,待服务端提供字典后再接入中文选项。
|
|
27
27
|
- 导入、导出按钮保留在列表工具栏,文件由后端接口生成和解析,页面不再在前端拼装 CSV。
|
|
28
|
-
- 模块目录与授权页面保持一致:`api.ts`
|
|
28
|
+
- 模块目录与授权页面保持一致:`api.ts` 只放接口定义,`utils.ts` 放日期、金额、表单校验等共用工具,`typs.ts` 类型,`runtime.ts` 默认交易日期,`index.vue` 为入口,`components/` 放页面外壳和列表、新增修改、详情页面。接口定义只维护在各自模块下,不再集中到 `src/api`。
|
|
29
29
|
- 新增、修改、详情页面每行四列展示,窗口变窄后依次降为三列、两列、一列。
|
|
30
30
|
|
|
31
31
|
本地后端的预报接口当前使用通用模拟响应,写入成功响应不代表真实持久化。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue3-element-plus-app-chu",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"axios": "^1.20.0",
|
|
30
30
|
"dayjs": "^1.11.23",
|
|
31
31
|
"element-plus": "^2.14.7",
|
|
32
|
-
"vue": "^3.5.42"
|
|
32
|
+
"vue": "^3.5.42",
|
|
33
|
+
"vue-router": "^4.6.4"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@tsconfig/node24": "^24.0.5",
|
package/src/layout/index.vue
CHANGED
|
@@ -1,38 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, onMounted, ref
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import DigitalRmbForecast from '@/view/dcps/forecast/index.vue'
|
|
6
|
-
import DigitalRmbInjection from '@/view/dcps/injection/index.vue'
|
|
7
|
-
import DigitalRmbWriteOff from '@/view/dcps/write-off/index.vue'
|
|
8
|
-
import DigitalRmbPositionEstimation from '@/view/dcps/estimation/index.vue'
|
|
9
|
-
|
|
10
|
-
interface LayoutMenu {
|
|
11
|
-
/** 菜单唯一标识,同时作为 KeepAlive 内的页面缓存键。 */
|
|
12
|
-
key: string
|
|
13
|
-
title: string
|
|
14
|
-
compactTitle: string
|
|
15
|
-
component: Component
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// 全局布局的菜单配置:新增业务页面时只需在这里追加一项。
|
|
19
|
-
const menus: LayoutMenu[] = [
|
|
20
|
-
{ key: 'forecast', title: '数字人民币头寸预报', compactTitle: '头寸预报', component: DigitalRmbForecast },
|
|
21
|
-
{ key: 'estimation', title: '数字人民币头寸匡算', compactTitle: '匡算', component: DigitalRmbPositionEstimation },
|
|
22
|
-
{ key: 'injection', title: '数字人民币注资明细查询', compactTitle: '注资明细', component: DigitalRmbInjection },
|
|
23
|
-
{ key: 'balance', title: '数字人民币账户行余额查询申请', compactTitle: '余额查询', component: DigitalRmbAccountBalance },
|
|
24
|
-
{ key: 'auth', title: '数字人民币授权', compactTitle: '授权', component: DigitalRmbAuthorization },
|
|
25
|
-
{ key: 'write-off', title: '数字人民币核销', compactTitle: '核销', component: DigitalRmbWriteOff },
|
|
26
|
-
]
|
|
2
|
+
import { computed, onMounted, ref } from 'vue'
|
|
3
|
+
import { RouterView, useRoute, useRouter } from 'vue-router'
|
|
4
|
+
import { layoutMenus as menus } from '@/router/menus'
|
|
27
5
|
|
|
6
|
+
const route = useRoute()
|
|
7
|
+
const router = useRouter()
|
|
28
8
|
const firstMenu = menus[0]!
|
|
29
9
|
const collapsed = ref(false)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
10
|
+
// 子页面通过 meta.menu 指向所属一级菜单,进入新增、详情时侧边栏仍保持高亮。
|
|
11
|
+
const activeMenu = computed(() => route.meta.menu ?? String(route.name ?? firstMenu.name))
|
|
12
|
+
const selectMenu = (name: string) => {
|
|
13
|
+
if (name !== route.name) void router.push({ name })
|
|
14
|
+
}
|
|
36
15
|
|
|
37
16
|
onMounted(() => {
|
|
38
17
|
collapsed.value = window.innerWidth <= 760
|
|
@@ -54,9 +33,9 @@ onMounted(() => {
|
|
|
54
33
|
<el-menu
|
|
55
34
|
class="dcps-menu"
|
|
56
35
|
:default-active="activeMenu"
|
|
57
|
-
@select="
|
|
36
|
+
@select="selectMenu"
|
|
58
37
|
>
|
|
59
|
-
<el-menu-item v-for="menu in menus" :key="menu.
|
|
38
|
+
<el-menu-item v-for="menu in menus" :key="menu.name" :index="menu.name">
|
|
60
39
|
<span v-if="collapsed" class="menu-label menu-label--compact">{{ menu.compactTitle }}</span>
|
|
61
40
|
<span v-else class="menu-label">{{ menu.title }}</span>
|
|
62
41
|
</el-menu-item>
|
|
@@ -82,9 +61,15 @@ onMounted(() => {
|
|
|
82
61
|
</aside>
|
|
83
62
|
|
|
84
63
|
<div class="layout-content">
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
64
|
+
<!-- 头寸预报列表进入新增、修改或详情后保持挂载,返回时筛选条件和分页不变。 -->
|
|
65
|
+
<RouterView v-slot="{ Component, route: currentRoute }">
|
|
66
|
+
<KeepAlive include="DcpsForecastPage">
|
|
67
|
+
<component
|
|
68
|
+
:is="Component"
|
|
69
|
+
:key="currentRoute.name === 'forecast' ? 'forecast' : currentRoute.fullPath"
|
|
70
|
+
/>
|
|
71
|
+
</KeepAlive>
|
|
72
|
+
</RouterView>
|
|
88
73
|
</div>
|
|
89
74
|
</div>
|
|
90
75
|
</template>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
|
2
|
+
import AppLayout from '@/layout/index.vue'
|
|
3
|
+
import DigitalRmbAuthorization from '@/view/dcps/auth/index.vue'
|
|
4
|
+
import DigitalRmbAccountBalance from '@/view/dcps/balance/index.vue'
|
|
5
|
+
import DigitalRmbForecast from '@/view/dcps/forecast/index.vue'
|
|
6
|
+
import DigitalRmbInjection from '@/view/dcps/injection/index.vue'
|
|
7
|
+
import DigitalRmbWriteOff from '@/view/dcps/write-off/index.vue'
|
|
8
|
+
import DigitalRmbPositionEstimation from '@/view/dcps/estimation/index.vue'
|
|
9
|
+
|
|
10
|
+
declare module 'vue-router' {
|
|
11
|
+
interface RouteMeta {
|
|
12
|
+
/** 一级菜单路由名;子页面用它保持侧边菜单高亮。 */
|
|
13
|
+
menu?: string
|
|
14
|
+
/** 浏览器标题使用的页面名称。 */
|
|
15
|
+
title?: string
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const routes: RouteRecordRaw[] = [
|
|
20
|
+
{
|
|
21
|
+
path: '/',
|
|
22
|
+
component: AppLayout,
|
|
23
|
+
redirect: { name: 'forecast' },
|
|
24
|
+
children: [
|
|
25
|
+
{
|
|
26
|
+
path: 'forecast',
|
|
27
|
+
name: 'forecast',
|
|
28
|
+
component: DigitalRmbForecast,
|
|
29
|
+
meta: { menu: 'forecast', title: '数字人民币头寸预报' },
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: 'forecast/create',
|
|
33
|
+
name: 'forecast-create',
|
|
34
|
+
component: DigitalRmbForecast,
|
|
35
|
+
meta: { menu: 'forecast', title: '新增数字人民币头寸预报' },
|
|
36
|
+
props: (route) => ({
|
|
37
|
+
mode: 'create',
|
|
38
|
+
tranDate: typeof route.query.tranDate === 'string' ? route.query.tranDate : undefined,
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
path: 'forecast/:predRefNo/edit',
|
|
43
|
+
name: 'forecast-edit',
|
|
44
|
+
component: DigitalRmbForecast,
|
|
45
|
+
meta: { menu: 'forecast', title: '修改数字人民币头寸预报' },
|
|
46
|
+
props: (route) => ({
|
|
47
|
+
mode: 'edit',
|
|
48
|
+
predRefNo: String(route.params.predRefNo ?? ''),
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
path: 'forecast/:predRefNo',
|
|
53
|
+
name: 'forecast-detail',
|
|
54
|
+
component: DigitalRmbForecast,
|
|
55
|
+
meta: { menu: 'forecast', title: '数字人民币头寸预报详情' },
|
|
56
|
+
props: (route) => ({
|
|
57
|
+
mode: 'detail',
|
|
58
|
+
predRefNo: String(route.params.predRefNo ?? ''),
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
path: 'estimation',
|
|
63
|
+
name: 'estimation',
|
|
64
|
+
component: DigitalRmbPositionEstimation,
|
|
65
|
+
meta: { menu: 'estimation', title: '数字人民币头寸匡算' },
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
path: 'injection',
|
|
69
|
+
name: 'injection',
|
|
70
|
+
component: DigitalRmbInjection,
|
|
71
|
+
meta: { menu: 'injection', title: '数字人民币注资明细查询' },
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
path: 'balance',
|
|
75
|
+
name: 'balance',
|
|
76
|
+
component: DigitalRmbAccountBalance,
|
|
77
|
+
meta: { menu: 'balance', title: '数字人民币账户行余额查询申请' },
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
path: 'auth',
|
|
81
|
+
name: 'auth',
|
|
82
|
+
component: DigitalRmbAuthorization,
|
|
83
|
+
meta: { menu: 'auth', title: '数字人民币授权' },
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
path: 'write-off',
|
|
87
|
+
name: 'write-off',
|
|
88
|
+
component: DigitalRmbWriteOff,
|
|
89
|
+
meta: { menu: 'write-off', title: '数字人民币核销' },
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
{ path: '/:pathMatch(.*)*', redirect: { name: 'forecast' } },
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
const router = createRouter({
|
|
97
|
+
history: createWebHistory(import.meta.env.BASE_URL),
|
|
98
|
+
routes,
|
|
99
|
+
scrollBehavior: () => ({ top: 0 }),
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
router.afterEach((to) => {
|
|
103
|
+
const title = to.meta.title
|
|
104
|
+
document.title = title ? `${title} · DCPS` : 'DCPS 资金头寸管理'
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
export default router
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** 侧边菜单项;name 与对应的一级路由名保持一致。 */
|
|
2
|
+
export interface LayoutMenu {
|
|
3
|
+
name: string
|
|
4
|
+
title: string
|
|
5
|
+
compactTitle: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// 侧边菜单配置:新增业务页面时在这里追加一项,并在 router 中添加对应路由。
|
|
9
|
+
export const layoutMenus: LayoutMenu[] = [
|
|
10
|
+
{ name: 'forecast', title: '数字人民币头寸预报', compactTitle: '头寸预报' },
|
|
11
|
+
{ name: 'estimation', title: '数字人民币头寸匡算', compactTitle: '匡算' },
|
|
12
|
+
{ name: 'injection', title: '数字人民币注资明细查询', compactTitle: '注资明细' },
|
|
13
|
+
{ name: 'balance', title: '数字人民币账户行余额查询申请', compactTitle: '余额查询' },
|
|
14
|
+
{ name: 'auth', title: '数字人民币授权', compactTitle: '授权' },
|
|
15
|
+
{ name: 'write-off', title: '数字人民币核销', compactTitle: '核销' },
|
|
16
|
+
]
|
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
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# DCPS 页面文件结构
|
|
2
|
+
|
|
3
|
+
`src/view/dcps` 下每个业务页面统一保持三个文件,不再拆分子组件、工具文件或运行时配置:
|
|
4
|
+
|
|
5
|
+
| 文件 | 用途 |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `api.ts` | 当前页面用到的接口定义。 |
|
|
8
|
+
| `typs.ts` | 当前页面用到的接口类型、页面类型。 |
|
|
9
|
+
| `index.vue` | 页面入口,包含模板、页面内子视图(列表 / 表单 / 详情 / 弹窗)和页面内工具函数。 |
|
|
10
|
+
|
|
11
|
+
公共样式变量仍放在 `common.css`,由 `src/main.ts` 统一引入。
|
|
12
|
+
|
|
13
|
+
## 命名约定
|
|
14
|
+
|
|
15
|
+
- 所有页面组件的 `defineOptions({ name })` 统一以 `Dcps` 开头(PascalCase)。
|
|
16
|
+
- 页面入口统一命名为 `Dcps<模块>Page`。
|
|
17
|
+
- 页面内部的弹窗、表格、表单不再单独拆文件,直接写在 `index.vue` 中,相关状态和方法使用页面前缀区分。
|
|
18
|
+
- 路由名使用 kebab-case(如 `forecast-create`)。
|
|
19
|
+
|
|
20
|
+
## 路由与页面文件
|
|
21
|
+
|
|
22
|
+
路由定义在 `src/router/index.ts`,所有业务页面都挂在 `AppLayout`(`src/layout/index.vue`)下。
|
|
23
|
+
|
|
24
|
+
| 路由路径 | 路由 name | 页面文件 | 页面形态 | 页面标题(meta.title) |
|
|
25
|
+
| --- | --- | --- | --- | --- |
|
|
26
|
+
| `/forecast` | `forecast` | `forecast/index.vue` | 列表 | 数字人民币头寸预报 |
|
|
27
|
+
| `/forecast/create` | `forecast-create` | `forecast/index.vue` | 新增(`mode: 'create'`) | 新增数字人民币头寸预报 |
|
|
28
|
+
| `/forecast/:predRefNo/edit` | `forecast-edit` | `forecast/index.vue` | 修改(`mode: 'edit'`) | 修改数字人民币头寸预报 |
|
|
29
|
+
| `/forecast/:predRefNo` | `forecast-detail` | `forecast/index.vue` | 详情(`mode: 'detail'`) | 数字人民币头寸预报详情 |
|
|
30
|
+
| `/estimation` | `estimation` | `estimation/index.vue` | 页面 | 数字人民币头寸匡算 |
|
|
31
|
+
| `/injection` | `injection` | `injection/index.vue` | 页面 | 数字人民币注资明细查询 |
|
|
32
|
+
| `/balance` | `balance` | `balance/index.vue` | 页面 | 数字人民币账户行余额查询申请 |
|
|
33
|
+
| `/auth` | `auth` | `auth/index.vue` | 页面 | 数字人民币授权 |
|
|
34
|
+
| `/write-off` | `write-off` | `write-off/index.vue` | 页面 | 数字人民币核销 |
|
|
35
|
+
| `/` | — | 重定向到 `forecast` | — | — |
|
|
36
|
+
| `/:pathMatch(.*)*` | — | 重定向到 `forecast`(404 兜底) | — | — |
|
|
37
|
+
|
|
38
|
+
补充说明:
|
|
39
|
+
|
|
40
|
+
- 头寸预报的列表、新增、修改、详情共用 `forecast/index.vue`,通过路由 `props.mode` 切换页面形态;修改和详情通过 `props.predRefNo` 接收路由参数。
|
|
41
|
+
- `forecast/index.vue` 顶部使用一个普通 `<script>` 块保存列表记录缓存,跳转修改或详情前暂存列表行,用于补齐详情接口未返回的字段。
|
|
42
|
+
- 侧边菜单配置在 `src/router/menus.ts`,其中 `name` 必须与对应的一级路由 `name` 保持一致。
|
|
43
|
+
- `src/layout/index.vue` 使用 `<KeepAlive include="DcpsForecastPage">` 缓存头寸预报页面;新增、修改、详情按路由 `key` 分别缓存,返回列表时自动重新查询。
|
|
44
|
+
|
|
45
|
+
## 新增页面检查清单
|
|
46
|
+
|
|
47
|
+
1. 在 `src/view/dcps/<模块>/` 下新建 `index.vue`,并添加 `defineOptions({ name: 'Dcps<模块>Page' })`。
|
|
48
|
+
2. 同目录补充 `api.ts`、`typs.ts`;不要再创建 `components/`、`utils.ts`、`runtime.ts` 等额外文件。
|
|
49
|
+
3. 弹窗、子表格、表单直接写在 `index.vue` 中;工具函数写在 `index.vue` 的 `<script setup>` 内。
|
|
50
|
+
4. 在 `src/router/index.ts` 的 `children` 中新增路由,配置 `path`、`name`、`component`、`meta.menu`、`meta.title`。
|
|
51
|
+
5. 如需出现在侧边菜单,在 `src/router/menus.ts` 中追加菜单项,`name` 与一级路由名一致。
|
|
52
|
+
6. 更新本文档的对应关系表。
|
|
@@ -2,12 +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
|
|
|
8
|
+
defineOptions({ name: 'DcpsAuthPage' })
|
|
9
|
+
|
|
10
10
|
type Decision = 'approve' | 'reject'
|
|
11
|
+
|
|
12
|
+
// 授权页面的审批人和默认创建日期;日期格式为 YYYYMMDD。
|
|
13
|
+
const authorizationRuntime = {
|
|
14
|
+
approverId: '10092', // 审批人工号。
|
|
15
|
+
approverName: '王干事', // 审批人姓名。
|
|
16
|
+
createTmBegin: '20261012', // 默认创建开始日期。
|
|
17
|
+
createTmEnd: '20261012', // 默认创建结束日期。
|
|
18
|
+
}
|
|
11
19
|
type AmountSortOrder = 'ascending' | 'descending' | null
|
|
12
20
|
|
|
13
21
|
// 初始查询条件;空字符串表示不限制该条件,重置时恢复此配置。
|
|
@@ -54,6 +62,52 @@ let disposed = false // 页面是否已卸载,避免未完成请求继续更
|
|
|
54
62
|
const detailDialogVisible = ref(false) // 详情弹窗是否显示。
|
|
55
63
|
const detailTask = ref<any>(null) // 当前查看详情的原始任务,未选择时为 null。
|
|
56
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
|
+
|
|
57
111
|
// 审批弹窗状态。
|
|
58
112
|
const dialogVisible = ref(false) // 审批弹窗是否显示。
|
|
59
113
|
const selectedTask = ref<any>(null) // 本次待处理的任务,提交时读取平台流水号。
|
|
@@ -439,13 +493,66 @@ onBeforeUnmount(() => {
|
|
|
439
493
|
</ArtDataTable>
|
|
440
494
|
</div>
|
|
441
495
|
|
|
442
|
-
|
|
496
|
+
<!-- 授权详情弹窗:按任务字段分组展示,底部可直接进入审批。 -->
|
|
497
|
+
<el-dialog
|
|
443
498
|
v-model="detailDialogVisible"
|
|
444
|
-
|
|
445
|
-
:
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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>
|
|
449
556
|
<!-- 审批提交期间禁止关闭弹窗,等待服务端返回结果。 -->
|
|
450
557
|
<el-dialog
|
|
451
558
|
v-model="dialogVisible"
|
|
@@ -513,16 +620,12 @@ onBeforeUnmount(() => {
|
|
|
513
620
|
<style scoped>
|
|
514
621
|
/* 页面先扣除宿主占用高度,再使用 flex 分配剩余高度,让列表内部滚动并保留分页。 */
|
|
515
622
|
.dcps-page {
|
|
516
|
-
/* 手动修改这里:顶部导航/页签 + 底部状态栏 + 宿主上下留白的合计高度。
|
|
517
|
-
独立页面保持 0px;例如顶部 90px、底部 24px、上下留白 6px,填写 120px。
|
|
518
|
-
不包含浏览器标签栏、地址栏,也不包含本页面自身的 padding。 */
|
|
519
|
-
--host-layout-height: 0px;
|
|
520
623
|
box-sizing: border-box;
|
|
521
624
|
display: flex;
|
|
522
625
|
flex-direction: column;
|
|
523
626
|
gap: 5px;
|
|
524
|
-
height: calc(100vh - var(--host-layout-height));
|
|
525
|
-
height: calc(100dvh - var(--host-layout-height));
|
|
627
|
+
height: calc(100vh - var(--dpcs-host-layout-height));
|
|
628
|
+
height: calc(100dvh - var(--dpcs-host-layout-height));
|
|
526
629
|
min-height: 0;
|
|
527
630
|
min-width: 0;
|
|
528
631
|
padding: 5px;
|
|
@@ -548,6 +651,40 @@ onBeforeUnmount(() => {
|
|
|
548
651
|
.filter-actions { flex-shrink: 0; margin-left: auto; }
|
|
549
652
|
.amount-cell { color: var(--el-color-primary-dark-2); font-variant-numeric: tabular-nums; font-weight: 600; }
|
|
550
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
|
+
|
|
551
688
|
@media (max-width: 540px) {
|
|
552
689
|
.filter-form :deep(.el-form-item) { flex-basis: 100%; }
|
|
553
690
|
.filter-actions { margin-left: 0; }
|
|
@@ -4,6 +4,8 @@ import { ElMessage } from 'element-plus'
|
|
|
4
4
|
import API from './api'
|
|
5
5
|
import type { AccountBalanceInfo, InjectionCreateParams } from './typs'
|
|
6
6
|
|
|
7
|
+
defineOptions({ name: 'DcpsBalancePage' })
|
|
8
|
+
|
|
7
9
|
// 余额信息、请求与加载状态。
|
|
8
10
|
const balance = ref<AccountBalanceInfo | null>(null) // 当前展示的账户行余额信息,未查询到时为 null。
|
|
9
11
|
const loading = ref(false) // 查询是否进行中,同时控制查询按钮状态。
|
|
@@ -335,16 +337,12 @@ onBeforeUnmount(() => {
|
|
|
335
337
|
<style scoped>
|
|
336
338
|
/* 页面先扣除宿主占用高度,再使用 flex 分配剩余高度,内容由滚动区承载。 */
|
|
337
339
|
.dcps-page {
|
|
338
|
-
/* 手动修改这里:顶部导航/页签 + 底部状态栏 + 宿主上下留白的合计高度。
|
|
339
|
-
独立页面保持 0px;例如顶部 90px、底部 24px、上下留白 6px,填写 120px。
|
|
340
|
-
不包含浏览器标签栏、地址栏,也不包含本页面自身的 padding。 */
|
|
341
|
-
--host-layout-height: 0px;
|
|
342
340
|
box-sizing: border-box;
|
|
343
341
|
display: flex;
|
|
344
342
|
flex-direction: column;
|
|
345
343
|
gap: 5px;
|
|
346
|
-
height: calc(100vh - var(--host-layout-height));
|
|
347
|
-
height: calc(100dvh - var(--host-layout-height));
|
|
344
|
+
height: calc(100vh - var(--dpcs-host-layout-height));
|
|
345
|
+
height: calc(100dvh - var(--dpcs-host-layout-height));
|
|
348
346
|
min-height: 0;
|
|
349
347
|
min-width: 0;
|
|
350
348
|
padding: 5px;
|