minitest2.0 0.0.7 → 0.0.9
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/package.json +1 -1
- package/src/api/dashboard.ts +44 -0
- package/src/router/index.ts +18 -18
- package/src/stores/position-filter.ts +38 -0
- package/src/views/dashboard/index.vue +142 -17
- package/src/views/{foreign-position → foreign/position}/index.vue +9 -2
- package/src/views/{rmb-position → rmb/position}/index.vue +9 -2
- /package/src/views/{article-detail → announcement/detail}/index.vue +0 -0
- /package/src/views/{article-edit → announcement/edit}/index.vue +0 -0
- /package/src/views/{articles → announcement}/index.vue +0 -0
- /package/src/views/{foreign-position-create → foreign/position/create}/index.vue +0 -0
- /package/src/views/{foreign-position-detail → foreign/position/detail}/index.vue +0 -0
- /package/src/views/{settings → mine/settings}/index.vue +0 -0
- /package/src/views/{clearing-detail → rmb/clearing-detail}/index.vue +0 -0
- /package/src/views/{net-debit → rmb/net-debit}/index.vue +0 -0
- /package/src/views/{pbc-position → rmb/pbc-position}/index.vue +0 -0
- /package/src/views/{rmb-position-create → rmb/position/create}/index.vue +0 -0
- /package/src/views/{rmb-position-detail → rmb/position/detail}/index.vue +0 -0
- /package/src/views/{position-estimate → rmb/position-estimate}/index.vue +0 -0
- /package/src/views/{opening-reserve-estimate → rmb/position-estimate/opening-reserve}/index.vue +0 -0
- /package/src/views/{position-estimate-report → rmb/position-estimate/report}/index.vue +0 -0
- /package/src/views/{warning → rmb/warning}/index.vue +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { buildTamRequestPayload, tamRequest } from '@/api/tam'
|
|
2
|
+
|
|
3
|
+
export type RmbDashboardBody = {
|
|
4
|
+
/** 城银账户资金余额 */
|
|
5
|
+
availCredit?: string
|
|
6
|
+
/** 跨境人民币可用余额 */
|
|
7
|
+
cipsAvailBal?: string
|
|
8
|
+
/** 数字人民币可用余额 */
|
|
9
|
+
dcpTotalAmt?: string
|
|
10
|
+
/** 人行账户资金余额 */
|
|
11
|
+
settleAccAmt?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type ForeignDashboardBody = {
|
|
15
|
+
eurDomesticAmt?: string
|
|
16
|
+
eurOverseasAmt?: string
|
|
17
|
+
hkdDomesticAmt?: string
|
|
18
|
+
hkdOverseasAmt?: string
|
|
19
|
+
jpyDomesticAmt?: string
|
|
20
|
+
jpyOverseasAmt?: string
|
|
21
|
+
usdDomesticAmt?: string
|
|
22
|
+
usdOverseasAmt?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const RMB_DASHBOARD_TRAN_CODE = '/TAM/0001/140000007'
|
|
26
|
+
const FOREIGN_DASHBOARD_TRAN_CODE = '/TAM/0001/140000008'
|
|
27
|
+
|
|
28
|
+
export function queryRmbDashboard() {
|
|
29
|
+
return tamRequest<Record<string, never>, RmbDashboardBody>(
|
|
30
|
+
buildTamRequestPayload({
|
|
31
|
+
tranCode: RMB_DASHBOARD_TRAN_CODE,
|
|
32
|
+
body: {},
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function queryForeignDashboard() {
|
|
38
|
+
return tamRequest<Record<string, never>, ForeignDashboardBody>(
|
|
39
|
+
buildTamRequestPayload({
|
|
40
|
+
tranCode: FOREIGN_DASHBOARD_TRAN_CODE,
|
|
41
|
+
body: {},
|
|
42
|
+
}),
|
|
43
|
+
)
|
|
44
|
+
}
|
package/src/router/index.ts
CHANGED
|
@@ -26,79 +26,79 @@ const router = createRouter({
|
|
|
26
26
|
{
|
|
27
27
|
path: '/rmb-position',
|
|
28
28
|
name: 'RmbPosition',
|
|
29
|
-
component: () => import('@/views/rmb
|
|
29
|
+
component: () => import('@/views/rmb/position/index.vue'),
|
|
30
30
|
meta: { requiresAuth: true, title: '本币预报信息查询' },
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
path: '/rmb-position/new',
|
|
34
34
|
name: 'RmbPositionCreate',
|
|
35
|
-
component: () => import('@/views/rmb
|
|
35
|
+
component: () => import('@/views/rmb/position/create/index.vue'),
|
|
36
36
|
meta: { requiresAuth: true, title: '新增本币预报' },
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
path: '/rmb-position/:predRefNo',
|
|
40
40
|
name: 'RmbPositionDetail',
|
|
41
|
-
component: () => import('@/views/rmb
|
|
41
|
+
component: () => import('@/views/rmb/position/detail/index.vue'),
|
|
42
42
|
meta: { requiresAuth: true, title: '预报详情' },
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
path: '/foreign-position',
|
|
46
46
|
name: 'ForeignPosition',
|
|
47
|
-
component: () => import('@/views/foreign
|
|
47
|
+
component: () => import('@/views/foreign/position/index.vue'),
|
|
48
48
|
meta: { requiresAuth: true, title: '外币预报信息查询' },
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
path: '/foreign-position/new',
|
|
52
52
|
name: 'ForeignPositionCreate',
|
|
53
|
-
component: () => import('@/views/foreign
|
|
53
|
+
component: () => import('@/views/foreign/position/create/index.vue'),
|
|
54
54
|
meta: { requiresAuth: true, title: '新增外币预报' },
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
path: '/foreign-position/:predRefNo',
|
|
58
58
|
name: 'ForeignPositionDetail',
|
|
59
|
-
component: () => import('@/views/foreign
|
|
59
|
+
component: () => import('@/views/foreign/position/detail/index.vue'),
|
|
60
60
|
meta: { requiresAuth: true, title: '外币预报详情' },
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
path: '/pbc-position',
|
|
64
64
|
name: 'PbcPosition',
|
|
65
|
-
component: () => import('@/views/pbc-position/index.vue'),
|
|
65
|
+
component: () => import('@/views/rmb/pbc-position/index.vue'),
|
|
66
66
|
meta: { requiresAuth: true, title: '人行头寸余额查询' },
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
path: '/position-estimate',
|
|
70
70
|
name: 'PositionEstimate',
|
|
71
|
-
component: () => import('@/views/position-estimate/index.vue'),
|
|
71
|
+
component: () => import('@/views/rmb/position-estimate/index.vue'),
|
|
72
72
|
meta: { requiresAuth: true, title: '头寸匡算' },
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
path: '/position-estimate/report',
|
|
76
76
|
name: 'PositionEstimateReport',
|
|
77
|
-
component: () => import('@/views/position-estimate
|
|
77
|
+
component: () => import('@/views/rmb/position-estimate/report/index.vue'),
|
|
78
78
|
meta: { requiresAuth: true, title: '头寸匡算报表' },
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
path: '/position-estimate/opening-reserve',
|
|
82
82
|
name: 'OpeningReserveEstimate',
|
|
83
|
-
component: () => import('@/views/opening-reserve
|
|
83
|
+
component: () => import('@/views/rmb/position-estimate/opening-reserve/index.vue'),
|
|
84
84
|
meta: { requiresAuth: true, title: '日初备款数匡算' },
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
path: '/net-debit',
|
|
88
88
|
name: 'NetDebit',
|
|
89
|
-
component: () => import('@/views/net-debit/index.vue'),
|
|
89
|
+
component: () => import('@/views/rmb/net-debit/index.vue'),
|
|
90
90
|
meta: { requiresAuth: true, title: '人民币净借记可用额度管理' },
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
path: '/clearing-detail',
|
|
94
94
|
name: 'ClearingDetail',
|
|
95
|
-
component: () => import('@/views/clearing-detail/index.vue'),
|
|
95
|
+
component: () => import('@/views/rmb/clearing-detail/index.vue'),
|
|
96
96
|
meta: { requiresAuth: true, title: '人民币小额网银清算场次明细' },
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
path: '/warning',
|
|
100
100
|
name: 'Warning',
|
|
101
|
-
component: () => import('@/views/warning/index.vue'),
|
|
101
|
+
component: () => import('@/views/rmb/warning/index.vue'),
|
|
102
102
|
meta: { requiresAuth: true, title: '头寸管理平台' },
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -110,25 +110,25 @@ const router = createRouter({
|
|
|
110
110
|
{
|
|
111
111
|
path: '/articles',
|
|
112
112
|
name: 'Articles',
|
|
113
|
-
component: () => import('@/views/
|
|
113
|
+
component: () => import('@/views/announcement/index.vue'),
|
|
114
114
|
meta: { requiresAuth: true, showTabbar: true, title: '公告' },
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
117
|
path: '/articles/new',
|
|
118
118
|
name: 'ArticlePublish',
|
|
119
|
-
component: () => import('@/views/
|
|
119
|
+
component: () => import('@/views/announcement/edit/index.vue'),
|
|
120
120
|
meta: { requiresAuth: true, title: '发布公告' },
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
path: '/articles/:id',
|
|
124
124
|
name: 'ArticleDetail',
|
|
125
|
-
component: () => import('@/views/
|
|
125
|
+
component: () => import('@/views/announcement/detail/index.vue'),
|
|
126
126
|
meta: { requiresAuth: true, title: '公告详情' },
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
path: '/articles/:id/edit',
|
|
130
130
|
name: 'ArticleEdit',
|
|
131
|
-
component: () => import('@/views/
|
|
131
|
+
component: () => import('@/views/announcement/edit/index.vue'),
|
|
132
132
|
meta: { requiresAuth: true, title: '编辑公告' },
|
|
133
133
|
},
|
|
134
134
|
{
|
|
@@ -140,7 +140,7 @@ const router = createRouter({
|
|
|
140
140
|
{
|
|
141
141
|
path: '/settings',
|
|
142
142
|
name: 'Settings',
|
|
143
|
-
component: () => import('@/views/settings/index.vue'),
|
|
143
|
+
component: () => import('@/views/mine/settings/index.vue'),
|
|
144
144
|
meta: { requiresAuth: true, title: '设置' },
|
|
145
145
|
},
|
|
146
146
|
],
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 头寸预报查询筛选区状态 Store
|
|
5
|
+
*
|
|
6
|
+
* 只保存列表页筛选条件面板的展开/收起状态,避免和应用级配置混在一起。
|
|
7
|
+
*/
|
|
8
|
+
export const usePositionFilterStore = defineStore(
|
|
9
|
+
'positionFilter',
|
|
10
|
+
() => {
|
|
11
|
+
/** 人民币头寸预报及查询列表筛选条件是否展开,默认收起并持久化用户选择。 */
|
|
12
|
+
const rmbFilterExpanded = ref(false)
|
|
13
|
+
|
|
14
|
+
/** 外币头寸预报及查询列表筛选条件是否展开,默认收起并持久化用户选择。 */
|
|
15
|
+
const foreignFilterExpanded = ref(false)
|
|
16
|
+
|
|
17
|
+
function setRmbFilterExpanded(value: boolean) {
|
|
18
|
+
rmbFilterExpanded.value = value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function setForeignFilterExpanded(value: boolean) {
|
|
22
|
+
foreignFilterExpanded.value = value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
rmbFilterExpanded,
|
|
27
|
+
foreignFilterExpanded,
|
|
28
|
+
setRmbFilterExpanded,
|
|
29
|
+
setForeignFilterExpanded,
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
persist: {
|
|
34
|
+
key: 'mini2_position_filter_settings',
|
|
35
|
+
pick: ['rmbFilterExpanded', 'foreignFilterExpanded'],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { queryForeignDashboard, queryRmbDashboard } from '@/api/dashboard'
|
|
3
|
+
|
|
2
4
|
defineOptions({ name: 'DashboardPage' })
|
|
3
5
|
|
|
4
6
|
type DashboardMetric = {
|
|
@@ -37,12 +39,30 @@ const selectedUnitFactor = computed(
|
|
|
37
39
|
() => unitOptions.find((unit) => unit.label === selectedUnit.value)?.factor || 1,
|
|
38
40
|
)
|
|
39
41
|
|
|
40
|
-
const
|
|
42
|
+
const dashboardAmounts = reactive({
|
|
43
|
+
rmb: 0,
|
|
44
|
+
cityClearing: 0,
|
|
45
|
+
crossBorderRmb: 0,
|
|
46
|
+
digitalCurrency: 0,
|
|
47
|
+
usdDomestic: 0,
|
|
48
|
+
usdOverseas: 0,
|
|
49
|
+
eurDomestic: 0,
|
|
50
|
+
eurOverseas: 0,
|
|
51
|
+
hkdDomestic: 0,
|
|
52
|
+
hkdOverseas: 0,
|
|
53
|
+
jpyDomestic: 0,
|
|
54
|
+
jpyOverseas: 0,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const isDashboardLoading = ref(false)
|
|
58
|
+
const dashboardLoadError = ref('')
|
|
59
|
+
|
|
60
|
+
const cards = computed<DashboardCard[]>(() => [
|
|
41
61
|
{
|
|
42
62
|
name: '人民币',
|
|
43
63
|
code: 'CNY',
|
|
44
64
|
symbol: '¥',
|
|
45
|
-
amount:
|
|
65
|
+
amount: dashboardAmounts.rmb,
|
|
46
66
|
image: dashboardImage('01-rmb-great-wall-bg.jpg'),
|
|
47
67
|
accent: '#df2f38',
|
|
48
68
|
position: 'center center',
|
|
@@ -51,7 +71,7 @@ const cards: DashboardCard[] = [
|
|
|
51
71
|
name: '城银清',
|
|
52
72
|
code: 'CNY',
|
|
53
73
|
symbol: '¥',
|
|
54
|
-
amount:
|
|
74
|
+
amount: dashboardAmounts.cityClearing,
|
|
55
75
|
image: dashboardImage('02-city-clearing-skyline-bg.jpg'),
|
|
56
76
|
accent: '#0f4ca7',
|
|
57
77
|
position: 'center center',
|
|
@@ -60,7 +80,7 @@ const cards: DashboardCard[] = [
|
|
|
60
80
|
name: '跨境人民币',
|
|
61
81
|
code: 'CNY',
|
|
62
82
|
symbol: '¥',
|
|
63
|
-
amount:
|
|
83
|
+
amount: dashboardAmounts.crossBorderRmb,
|
|
64
84
|
image: dashboardImage('03-cross-border-rmb-world-map-bg.jpg'),
|
|
65
85
|
accent: '#078b95',
|
|
66
86
|
position: 'center center',
|
|
@@ -69,7 +89,7 @@ const cards: DashboardCard[] = [
|
|
|
69
89
|
name: '数字货币',
|
|
70
90
|
code: 'CNY',
|
|
71
91
|
symbol: '¥',
|
|
72
|
-
amount:
|
|
92
|
+
amount: dashboardAmounts.digitalCurrency,
|
|
73
93
|
image: dashboardImage('04-digital-currency-fintech-bg.jpg'),
|
|
74
94
|
accent: '#4423b7',
|
|
75
95
|
position: 'center center',
|
|
@@ -78,8 +98,8 @@ const cards: DashboardCard[] = [
|
|
|
78
98
|
name: '美元',
|
|
79
99
|
code: 'USD',
|
|
80
100
|
metrics: [
|
|
81
|
-
{ label: '境内', symbol: '$', amount:
|
|
82
|
-
{ label: '境外', symbol: '$', amount:
|
|
101
|
+
{ label: '境内', symbol: '$', amount: dashboardAmounts.usdDomestic },
|
|
102
|
+
{ label: '境外', symbol: '$', amount: dashboardAmounts.usdOverseas },
|
|
83
103
|
],
|
|
84
104
|
image: dashboardImage('05-usd-liberty-bg.jpg'),
|
|
85
105
|
accent: '#0d8b3f',
|
|
@@ -89,8 +109,8 @@ const cards: DashboardCard[] = [
|
|
|
89
109
|
name: '欧元',
|
|
90
110
|
code: 'EUR',
|
|
91
111
|
metrics: [
|
|
92
|
-
{ label: '境内', symbol: '€', amount:
|
|
93
|
-
{ label: '境外', symbol: '€', amount:
|
|
112
|
+
{ label: '境内', symbol: '€', amount: dashboardAmounts.eurDomestic },
|
|
113
|
+
{ label: '境外', symbol: '€', amount: dashboardAmounts.eurOverseas },
|
|
94
114
|
],
|
|
95
115
|
image: dashboardImage('06-eur-europe-architecture-bg.jpg'),
|
|
96
116
|
accent: '#1267d5',
|
|
@@ -100,8 +120,8 @@ const cards: DashboardCard[] = [
|
|
|
100
120
|
name: '港元',
|
|
101
121
|
code: 'HKD',
|
|
102
122
|
metrics: [
|
|
103
|
-
{ label: '境内', symbol: 'HK$', amount:
|
|
104
|
-
{ label: '境外', symbol: 'HK$', amount:
|
|
123
|
+
{ label: '境内', symbol: 'HK$', amount: dashboardAmounts.hkdDomestic },
|
|
124
|
+
{ label: '境外', symbol: 'HK$', amount: dashboardAmounts.hkdOverseas },
|
|
105
125
|
],
|
|
106
126
|
image: dashboardImage('07-hkd-hong-kong-skyline-bg.jpg'),
|
|
107
127
|
accent: '#e06b00',
|
|
@@ -111,14 +131,18 @@ const cards: DashboardCard[] = [
|
|
|
111
131
|
name: '日元',
|
|
112
132
|
code: 'JPY',
|
|
113
133
|
metrics: [
|
|
114
|
-
{ label: '境内', symbol: '¥', amount:
|
|
115
|
-
{ label: '境外', symbol: '¥', amount:
|
|
134
|
+
{ label: '境内', symbol: '¥', amount: dashboardAmounts.jpyDomestic },
|
|
135
|
+
{ label: '境外', symbol: '¥', amount: dashboardAmounts.jpyOverseas },
|
|
116
136
|
],
|
|
117
137
|
image: dashboardImage('08-jpy-mount-fuji-bg.jpg'),
|
|
118
138
|
accent: '#6125c8',
|
|
119
139
|
position: 'center center',
|
|
120
140
|
},
|
|
121
|
-
]
|
|
141
|
+
])
|
|
142
|
+
|
|
143
|
+
onMounted(() => {
|
|
144
|
+
void loadDashboard()
|
|
145
|
+
})
|
|
122
146
|
|
|
123
147
|
function getCardStyle(card: DashboardCard) {
|
|
124
148
|
return {
|
|
@@ -150,11 +174,93 @@ function formatMoney(amount: number, symbol: string) {
|
|
|
150
174
|
|
|
151
175
|
return `${symbol} ${formattedAmount}${unitSuffix}`
|
|
152
176
|
}
|
|
177
|
+
|
|
178
|
+
async function loadDashboard() {
|
|
179
|
+
if (isDashboardLoading.value) return
|
|
180
|
+
|
|
181
|
+
isDashboardLoading.value = true
|
|
182
|
+
dashboardLoadError.value = ''
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const [rmbResult, foreignResult] = await Promise.allSettled([
|
|
186
|
+
queryRmbDashboard(),
|
|
187
|
+
queryForeignDashboard(),
|
|
188
|
+
])
|
|
189
|
+
const errorMessages: string[] = []
|
|
190
|
+
|
|
191
|
+
if (rmbResult.status === 'fulfilled') {
|
|
192
|
+
applyRmbDashboardBody(rmbResult.value.body)
|
|
193
|
+
} else {
|
|
194
|
+
errorMessages.push(getErrorMessage(rmbResult.reason))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (foreignResult.status === 'fulfilled') {
|
|
198
|
+
applyForeignDashboardBody(foreignResult.value.body)
|
|
199
|
+
} else {
|
|
200
|
+
errorMessages.push(getErrorMessage(foreignResult.reason))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (errorMessages.length) {
|
|
204
|
+
dashboardLoadError.value = errorMessages.join(';')
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('[看板] 数据加载失败', error)
|
|
208
|
+
dashboardLoadError.value = getErrorMessage(error)
|
|
209
|
+
} finally {
|
|
210
|
+
isDashboardLoading.value = false
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function applyRmbDashboardBody(body: Awaited<ReturnType<typeof queryRmbDashboard>>['body']) {
|
|
215
|
+
const result = body || {}
|
|
216
|
+
|
|
217
|
+
dashboardAmounts.rmb = toAmount(result.settleAccAmt)
|
|
218
|
+
dashboardAmounts.cityClearing = toAmount(result.availCredit)
|
|
219
|
+
dashboardAmounts.crossBorderRmb = toAmount(result.cipsAvailBal)
|
|
220
|
+
dashboardAmounts.digitalCurrency = toAmount(result.dcpTotalAmt)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function applyForeignDashboardBody(
|
|
224
|
+
body: Awaited<ReturnType<typeof queryForeignDashboard>>['body'],
|
|
225
|
+
) {
|
|
226
|
+
const result = body || {}
|
|
227
|
+
|
|
228
|
+
dashboardAmounts.usdDomestic = toAmount(result.usdDomesticAmt)
|
|
229
|
+
dashboardAmounts.usdOverseas = toAmount(result.usdOverseasAmt)
|
|
230
|
+
dashboardAmounts.eurDomestic = toAmount(result.eurDomesticAmt)
|
|
231
|
+
dashboardAmounts.eurOverseas = toAmount(result.eurOverseasAmt)
|
|
232
|
+
dashboardAmounts.hkdDomestic = toAmount(result.hkdDomesticAmt)
|
|
233
|
+
dashboardAmounts.hkdOverseas = toAmount(result.hkdOverseasAmt)
|
|
234
|
+
dashboardAmounts.jpyDomestic = toAmount(result.jpyDomesticAmt)
|
|
235
|
+
dashboardAmounts.jpyOverseas = toAmount(result.jpyOverseasAmt)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function toAmount(value: unknown) {
|
|
239
|
+
const amount = Number(String(value ?? '').replaceAll(',', '').trim())
|
|
240
|
+
|
|
241
|
+
return Number.isFinite(amount) ? amount : 0
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getErrorMessage(error: unknown) {
|
|
245
|
+
if (error instanceof Error && error.message) return error.message
|
|
246
|
+
|
|
247
|
+
return '看板数据加载失败'
|
|
248
|
+
}
|
|
153
249
|
</script>
|
|
154
250
|
|
|
155
251
|
<template>
|
|
156
|
-
<main class="dashboard-page">
|
|
252
|
+
<main class="dashboard-page" :aria-busy="isDashboardLoading">
|
|
157
253
|
<div class="dashboard-toolbar" aria-label="看板设置">
|
|
254
|
+
<button
|
|
255
|
+
v-if="dashboardLoadError"
|
|
256
|
+
class="dashboard-retry"
|
|
257
|
+
type="button"
|
|
258
|
+
:disabled="isDashboardLoading"
|
|
259
|
+
@click="loadDashboard"
|
|
260
|
+
>
|
|
261
|
+
刷新
|
|
262
|
+
</button>
|
|
263
|
+
|
|
158
264
|
<label class="unit-switch">
|
|
159
265
|
<span class="unit-switch__label">单位</span>
|
|
160
266
|
<select v-model="selectedUnit" class="unit-switch__select" aria-label="切换金额单位">
|
|
@@ -180,8 +286,8 @@ function formatMoney(amount: number, symbol: string) {
|
|
|
180
286
|
<span class="dashboard-card__code">{{ card.code }}</span>
|
|
181
287
|
</header>
|
|
182
288
|
|
|
183
|
-
<p v-if="card.
|
|
184
|
-
{{ formatMoney(card.amount, card.symbol || '') }}
|
|
289
|
+
<p v-if="!card.metrics" class="dashboard-card__amount">
|
|
290
|
+
{{ formatMoney(card.amount || 0, card.symbol || '') }}
|
|
185
291
|
</p>
|
|
186
292
|
|
|
187
293
|
<div v-else class="dashboard-card__metrics">
|
|
@@ -211,10 +317,29 @@ function formatMoney(amount: number, symbol: string) {
|
|
|
211
317
|
|
|
212
318
|
.dashboard-toolbar {
|
|
213
319
|
display: flex;
|
|
320
|
+
align-items: center;
|
|
214
321
|
justify-content: flex-end;
|
|
215
322
|
margin-bottom: 6px;
|
|
216
323
|
}
|
|
217
324
|
|
|
325
|
+
.dashboard-retry {
|
|
326
|
+
height: 24px;
|
|
327
|
+
margin-right: auto;
|
|
328
|
+
border: 1px solid #ffd4d8;
|
|
329
|
+
border-radius: 5px;
|
|
330
|
+
padding: 0 10px;
|
|
331
|
+
color: #dc2735;
|
|
332
|
+
font: inherit;
|
|
333
|
+
font-size: 12px;
|
|
334
|
+
font-weight: 700;
|
|
335
|
+
line-height: 22px;
|
|
336
|
+
background: #fff5f6;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.dashboard-retry:disabled {
|
|
340
|
+
opacity: 0.62;
|
|
341
|
+
}
|
|
342
|
+
|
|
218
343
|
.unit-switch {
|
|
219
344
|
position: relative;
|
|
220
345
|
display: inline-flex;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ForeignForecastRecord,
|
|
7
7
|
} from '@/api/foreign-position'
|
|
8
8
|
import { useAppStore } from '@/stores/app'
|
|
9
|
+
import { usePositionFilterStore } from '@/stores/position-filter'
|
|
9
10
|
import {
|
|
10
11
|
allForeignBusinessOption,
|
|
11
12
|
allForeignStatusOption,
|
|
@@ -54,6 +55,7 @@ type SelectorOption = ForeignOption | ForeignStatusOption
|
|
|
54
55
|
|
|
55
56
|
const router = useRouter()
|
|
56
57
|
const appStore = useAppStore()
|
|
58
|
+
const positionFilterStore = usePositionFilterStore()
|
|
57
59
|
const defaultTranDate = appStore.systemWorkDate
|
|
58
60
|
const heroImage = `${import.meta.env.BASE_URL}images/12a73787-86a9-4891-a65f-66104746f6a8.png`
|
|
59
61
|
const pageSize = 10
|
|
@@ -74,7 +76,8 @@ const showDatePicker = ref(false)
|
|
|
74
76
|
const datePickerValue = ref(selectedDate.value.split('-'))
|
|
75
77
|
const minDate = new Date(2020, 0, 1)
|
|
76
78
|
const maxDate = new Date(2030, 11, 31)
|
|
77
|
-
|
|
79
|
+
// 筛选区展开状态由 Pinia 持久化;首次进入无缓存时默认收起。
|
|
80
|
+
const isFilterExpanded = computed(() => positionFilterStore.foreignFilterExpanded)
|
|
78
81
|
|
|
79
82
|
const forecastList = ref<Forecast[]>([])
|
|
80
83
|
const forecastTotal = ref(0)
|
|
@@ -183,6 +186,10 @@ function scrollToTop() {
|
|
|
183
186
|
})
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
function toggleFilterExpanded() {
|
|
190
|
+
positionFilterStore.setForeignFilterExpanded(!isFilterExpanded.value)
|
|
191
|
+
}
|
|
192
|
+
|
|
186
193
|
function openCreatePage() {
|
|
187
194
|
router.push({
|
|
188
195
|
name: 'ForeignPositionCreate',
|
|
@@ -417,7 +424,7 @@ function isPendingForecast(value: Partial<PendingForecast> | null): value is Pen
|
|
|
417
424
|
class="filter-toggle"
|
|
418
425
|
type="button"
|
|
419
426
|
:aria-expanded="isFilterExpanded"
|
|
420
|
-
@click="
|
|
427
|
+
@click="toggleFilterExpanded"
|
|
421
428
|
>
|
|
422
429
|
<van-icon :name="isFilterExpanded ? 'arrow-up' : 'arrow-down'" />
|
|
423
430
|
<span>{{ isFilterExpanded ? '收起' : '展开' }}</span>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type RmbForecastRecord,
|
|
7
7
|
} from '@/api/rmb-position'
|
|
8
8
|
import { useAppStore } from '@/stores/app'
|
|
9
|
+
import { usePositionFilterStore } from '@/stores/position-filter'
|
|
9
10
|
import {
|
|
10
11
|
forecastStatusMap,
|
|
11
12
|
formatForecastAmount,
|
|
@@ -41,6 +42,7 @@ type PendingForecast = {
|
|
|
41
42
|
|
|
42
43
|
const router = useRouter()
|
|
43
44
|
const appStore = useAppStore()
|
|
45
|
+
const positionFilterStore = usePositionFilterStore()
|
|
44
46
|
const defaultTranDate = appStore.systemWorkDate
|
|
45
47
|
const heroImage = `${import.meta.env.BASE_URL}images/5798d7aa-ba8b-4605-8079-58b35495ac55.png`
|
|
46
48
|
const pageSize = 10
|
|
@@ -58,7 +60,8 @@ const showDatePicker = ref(false)
|
|
|
58
60
|
const datePickerValue = ref(selectedDate.value.split('-'))
|
|
59
61
|
const minDate = new Date(2020, 0, 1)
|
|
60
62
|
const maxDate = new Date(2030, 11, 31)
|
|
61
|
-
|
|
63
|
+
// 筛选区展开状态由 Pinia 持久化;首次进入无缓存时默认收起。
|
|
64
|
+
const isFilterExpanded = computed(() => positionFilterStore.rmbFilterExpanded)
|
|
62
65
|
|
|
63
66
|
const forecastList = ref<Forecast[]>([])
|
|
64
67
|
const forecastTotal = ref(0)
|
|
@@ -135,6 +138,10 @@ function scrollToTop() {
|
|
|
135
138
|
})
|
|
136
139
|
}
|
|
137
140
|
|
|
141
|
+
function toggleFilterExpanded() {
|
|
142
|
+
positionFilterStore.setRmbFilterExpanded(!isFilterExpanded.value)
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
function openCreatePage() {
|
|
139
146
|
router.push({
|
|
140
147
|
name: 'RmbPositionCreate',
|
|
@@ -311,7 +318,7 @@ function isPendingForecast(value: Partial<PendingForecast> | null): value is Pen
|
|
|
311
318
|
class="filter-toggle"
|
|
312
319
|
type="button"
|
|
313
320
|
:aria-expanded="isFilterExpanded"
|
|
314
|
-
@click="
|
|
321
|
+
@click="toggleFilterExpanded"
|
|
315
322
|
>
|
|
316
323
|
<van-icon :name="isFilterExpanded ? 'arrow-up' : 'arrow-down'" />
|
|
317
324
|
<span>{{ isFilterExpanded ? '收起' : '展开' }}</span>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/views/{opening-reserve-estimate → rmb/position-estimate/opening-reserve}/index.vue
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|