imeik-bizui 2.7.9 → 2.8.1
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/dist/bizui/api/applycenter.js +48 -0
- package/dist/bizui/api/meeting.js +55 -3
- package/dist/bizui/api/mms.js +36 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/README.md +148 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/core/PersonBudgetDashboard.vue +300 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/core/PersonBudgetDetailDialog.vue +84 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/core/PersonBudgetDetailPanel.vue +897 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/core/PersonBudgetOverview.vue +479 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/core/format.js +75 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/core/mock.js +32 -0
- package/dist/bizui/src/FieldPersonBudgetBoard/index.vue +147 -0
- package/dist/imeik-bizui.common.js +2788 -527
- package/dist/imeik-bizui.common.js.gz +0 -0
- package/dist/imeik-bizui.css +1 -1
- package/dist/imeik-bizui.css.gz +0 -0
- package/dist/imeik-bizui.umd.js +2788 -527
- package/dist/imeik-bizui.umd.js.gz +0 -0
- package/dist/imeik-bizui.umd.min.js +8 -8
- package/dist/imeik-bizui.umd.min.js.gz +0 -0
- package/package.json +1 -1
|
@@ -279,3 +279,51 @@ export function getLizhenBasePriceDetail(data) {
|
|
|
279
279
|
data
|
|
280
280
|
})
|
|
281
281
|
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* 个人预算概述
|
|
285
|
+
* POST /application-center-admin/openApi/personBudget/overview
|
|
286
|
+
*/
|
|
287
|
+
export function getPersonBudgetOverview(data) {
|
|
288
|
+
return request({
|
|
289
|
+
url: '/application-center-admin/openApi/personBudget/overview',
|
|
290
|
+
method: 'post',
|
|
291
|
+
data
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 个人预算详情(概述 + 使用/业绩明细分页快照)
|
|
297
|
+
* POST /application-center-admin/openApi/personBudget/detail
|
|
298
|
+
*/
|
|
299
|
+
export function getPersonBudgetDetail(data) {
|
|
300
|
+
return request({
|
|
301
|
+
url: '/application-center-admin/openApi/personBudget/detail',
|
|
302
|
+
method: 'post',
|
|
303
|
+
data
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* 个人预算使用明细分页
|
|
309
|
+
* POST /application-center-admin/openApi/personBudget/usagePage
|
|
310
|
+
*/
|
|
311
|
+
export function getPersonBudgetUsagePage(data) {
|
|
312
|
+
return request({
|
|
313
|
+
url: '/application-center-admin/openApi/personBudget/usagePage',
|
|
314
|
+
method: 'post',
|
|
315
|
+
data
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 个人预算业绩明细分页
|
|
321
|
+
* POST /application-center-admin/openApi/personBudget/performancePage
|
|
322
|
+
*/
|
|
323
|
+
export function getPersonBudgetPerformancePage(data) {
|
|
324
|
+
return request({
|
|
325
|
+
url: '/application-center-admin/openApi/personBudget/performancePage',
|
|
326
|
+
method: 'post',
|
|
327
|
+
data
|
|
328
|
+
})
|
|
329
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 会议系统接口(FieldFloorPriceBoard / FieldPersonBudgetBoard scene=meeting)
|
|
3
|
+
* 底价:总览 / 明细均为 POST,入参同形
|
|
4
|
+
* 个人预算:overview / usagePage / performancePage,入参对齐底价(forceRefresh + meetFormJson)
|
|
5
5
|
*/
|
|
6
6
|
import request from '../utils/userRequest.js'
|
|
7
7
|
|
|
@@ -38,3 +38,55 @@ export function fetchMeetingBasePriceOverview(data) {
|
|
|
38
38
|
export function fetchMeetingBasePriceDetail(payload) {
|
|
39
39
|
return getBasePriceDetail(payload)
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 个人预算概述
|
|
44
|
+
* POST /qxcollege/meeting/personBudget/overview
|
|
45
|
+
* 入参对齐底价:forceRefresh + meetFormJson + meetTypeCode;meetCode / insFlowCode 有值才传
|
|
46
|
+
*/
|
|
47
|
+
export function getPersonBudgetOverview(data) {
|
|
48
|
+
return request({
|
|
49
|
+
url: '/qxcollege/meeting/personBudget/overview',
|
|
50
|
+
method: 'post',
|
|
51
|
+
data
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 个人预算使用明细分页
|
|
57
|
+
* POST /qxcollege/meeting/personBudget/usagePage
|
|
58
|
+
*/
|
|
59
|
+
export function getPersonBudgetUsagePage(data) {
|
|
60
|
+
return request({
|
|
61
|
+
url: '/qxcollege/meeting/personBudget/usagePage',
|
|
62
|
+
method: 'post',
|
|
63
|
+
data
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 个人预算业绩明细分页
|
|
69
|
+
* POST /qxcollege/meeting/personBudget/performancePage
|
|
70
|
+
*/
|
|
71
|
+
export function getPersonBudgetPerformancePage(data) {
|
|
72
|
+
return request({
|
|
73
|
+
url: '/qxcollege/meeting/personBudget/performancePage',
|
|
74
|
+
method: 'post',
|
|
75
|
+
data
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 个人预算概述:业务侧已拼好 requestPayload */
|
|
80
|
+
export function fetchMeetingPersonBudgetOverview(data) {
|
|
81
|
+
return getPersonBudgetOverview(data)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 个人预算使用明细分页 */
|
|
85
|
+
export function fetchMeetingPersonBudgetUsagePage(data) {
|
|
86
|
+
return getPersonBudgetUsagePage(data)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** 个人预算业绩明细分页 */
|
|
90
|
+
export function fetchMeetingPersonBudgetPerformancePage(data) {
|
|
91
|
+
return getPersonBudgetPerformancePage(data)
|
|
92
|
+
}
|
package/dist/bizui/api/mms.js
CHANGED
|
@@ -81,3 +81,39 @@ export function getProductPriceDetail(data) {
|
|
|
81
81
|
data
|
|
82
82
|
})
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 个人预算概述(实时不传 applyNumber;查看只传 applyNumber)
|
|
87
|
+
* POST /mms/admin/custom/getPersonBudgetOverview
|
|
88
|
+
*/
|
|
89
|
+
export function getPersonBudgetOverview(data) {
|
|
90
|
+
return request({
|
|
91
|
+
url: '/mms/admin/custom/getPersonBudgetOverview',
|
|
92
|
+
method: 'post',
|
|
93
|
+
data
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 个人预算使用明细独立分页
|
|
99
|
+
* POST /mms/admin/custom/getPersonBudgetUsagePage
|
|
100
|
+
*/
|
|
101
|
+
export function getPersonBudgetUsagePage(data) {
|
|
102
|
+
return request({
|
|
103
|
+
url: '/mms/admin/custom/getPersonBudgetUsagePage',
|
|
104
|
+
method: 'post',
|
|
105
|
+
data
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 个人预算业绩明细独立分页
|
|
111
|
+
* POST /mms/admin/custom/getPersonBudgetPerformancePage
|
|
112
|
+
*/
|
|
113
|
+
export function getPersonBudgetPerformancePage(data) {
|
|
114
|
+
return request({
|
|
115
|
+
url: '/mms/admin/custom/getPersonBudgetPerformancePage',
|
|
116
|
+
method: 'post',
|
|
117
|
+
data
|
|
118
|
+
})
|
|
119
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# 个人预算看板(FieldPersonBudgetBoard)
|
|
2
|
+
|
|
3
|
+
> 范围:物资系统 · 会议系统 · 申请中心 · **imeik-bizui**
|
|
4
|
+
> 目标:bizui 只负责「选接口 + 拉数 + 看板 UI」;**请求体组装在业务系统**
|
|
5
|
+
> 对齐:`FieldFloorPriceBoard`(底价看板)同一套分层
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、目录结构
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
imeik-bizui/src/bizui/
|
|
13
|
+
├── api/mms.js # 物资 getPersonBudgetOverview / UsagePage / PerformancePage
|
|
14
|
+
├── api/meeting.js # 会议 personBudget/overview|usagePage|performancePage
|
|
15
|
+
├── api/applycenter.js # 申请中心 openApi/personBudget/*
|
|
16
|
+
└── src/
|
|
17
|
+
└── FieldPersonBudgetBoard/
|
|
18
|
+
├── index.vue # scene → 接口映射 + Dashboard
|
|
19
|
+
├── README.md
|
|
20
|
+
└── core/ # Overview / Dashboard / Detail / format / mock
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`Vue.use(bizui)` 后:
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<FieldPersonBudgetBoard
|
|
27
|
+
scene="meeting"
|
|
28
|
+
:request-payload="payload"
|
|
29
|
+
:show-detail-link="hasPerm"
|
|
30
|
+
@submit-blocked="onBlocked"
|
|
31
|
+
/>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
移动端想保留弹窗:加 `narrow-detail-mode="dialog"`(默认 `inline`,在看板下方展开)。
|
|
35
|
+
|
|
36
|
+
`scene`:`supplies` | `meeting` | `apply` / `applyCenter`。
|
|
37
|
+
|
|
38
|
+
请求体组装在业务系统(如会议 `buildPersonBudgetPayload.js` / 物资 / 申请中心 `buildRequestPayload.js`)。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 二、分层原则
|
|
43
|
+
|
|
44
|
+
1. **接口**在 `api/mms.js`(物资)、`api/meeting.js`(会议)、`api/applycenter.js`(申请中心)。
|
|
45
|
+
2. **对外只注册** `FieldPersonBudgetBoard`;`index.vue` 按 `scene` 选 fetch。
|
|
46
|
+
3. **宿主组装** `requestPayload` + 权限;不写 debounce / 看板 UI。
|
|
47
|
+
4. **bizui 不解析**各域表单字段(费用承担人、snapshotList、整表 meetFormJson / formDataJson 等)。
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
业务系统:表单字段 → requestPayload
|
|
51
|
+
↓
|
|
52
|
+
bizui: scene 选接口 → Dashboard 拉数 → Overview / Detail
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 三、标准契约
|
|
58
|
+
|
|
59
|
+
| 类型 | 内容 |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Props | `scene`、`requestPayload`、`showDetailLink`、`narrowDetailMode`、`useMock`、`emptyText`、`debounceMs` |
|
|
62
|
+
| Events | `submit-blocked`:`{ blocked, warn }`(禁止提交 / 预警) |
|
|
63
|
+
| 明细形态 | 宽屏始终弹窗;窄屏(≤768px)由 `narrowDetailMode`:`inline`(默认)或 `dialog` |
|
|
64
|
+
| 物资 · 查看 | `{ applyNumber }`(真实申请单号,不是 objectCode) |
|
|
65
|
+
| 物资 · 实时 | `{ costBearEmployeeNo, costType, materialList[] }`;`materialList` **允许 `[]`**;可选 `budgetDate` |
|
|
66
|
+
| 会议 | `{ forceRefresh, meetTypeCode, meetFormJson, meetCode?, insFlowCode? }`(**对齐底价**;费用承担人/金额由后端从 `meetFormJson` 解析) |
|
|
67
|
+
| 申请中心 | `{ forceRefresh, formDataJson?, formDataNumber? }`(`PersonBudgetQueryDTO`);明细另带 `usageCurrPage` / `performanceCurrPage` 等 |
|
|
68
|
+
| 编辑态(物资) | **不传** `applyNumber`(即使草稿已有单号) |
|
|
69
|
+
| 编辑态(会议) | `forceRefresh=true`;`meetCode` **有值才传**(不作为读快照依据,分流看 `forceRefresh`) |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 四、scene → 接口
|
|
74
|
+
|
|
75
|
+
| 域 | scene | 接口 |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| 物资 | `supplies` | `mms.getPersonBudgetOverview` / `UsagePage` / `PerformancePage` |
|
|
78
|
+
| 会议 | `meeting` | `meeting` `personBudget/overview\|usagePage\|performancePage` |
|
|
79
|
+
| 申请中心 | `apply` / `applyCenter` | `applycenter` `openApi/personBudget/overview\|usagePage\|performancePage` |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 五、业务侧薄封装示例
|
|
84
|
+
|
|
85
|
+
### 物资
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
export function buildSuppliesPersonBudgetPayload(ctx) {
|
|
89
|
+
if (ctx.isView) {
|
|
90
|
+
const no = String(ctx.applyNumber || '').trim()
|
|
91
|
+
return no ? { applyNumber: no } : null
|
|
92
|
+
}
|
|
93
|
+
const employeeNo = String(ctx.costBearEmployeeNo || '').trim()
|
|
94
|
+
const type = ctx.costType != null && ctx.costType !== '' ? String(ctx.costType) : ''
|
|
95
|
+
if (!employeeNo || !type) return null
|
|
96
|
+
return {
|
|
97
|
+
costBearEmployeeNo: employeeNo,
|
|
98
|
+
costType: type,
|
|
99
|
+
materialList: mapMaterialList(ctx.snapshotList) // 允许 []
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 会议(对齐底价 buildFloorPricePayload)
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
export function buildPersonBudgetPayload(options = {}) {
|
|
108
|
+
const meetTypeCode = String(options.meetTypeCode || '').trim()
|
|
109
|
+
if (!meetTypeCode) return null
|
|
110
|
+
const node = options.nodeFormJsonData || {}
|
|
111
|
+
const budget = options.budgetValue || node.meetingBudgetDTO
|
|
112
|
+
const meetFormSource = options.isAddBudget
|
|
113
|
+
? { ...node, meetingBudgetDTO: budget }
|
|
114
|
+
: node
|
|
115
|
+
const payload = {
|
|
116
|
+
meetTypeCode,
|
|
117
|
+
meetFormJson: JSON.stringify(meetFormSource),
|
|
118
|
+
forceRefresh: !!options.forceRefresh
|
|
119
|
+
}
|
|
120
|
+
const meetCode = String(options.meetCode || '').trim()
|
|
121
|
+
if (meetCode) payload.meetCode = meetCode
|
|
122
|
+
const insFlowCode = String(options.insFlowCode || '').trim()
|
|
123
|
+
if (insFlowCode) payload.insFlowCode = insFlowCode
|
|
124
|
+
return payload
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 申请中心
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
export function buildApplyPersonBudgetPayload(ctx) {
|
|
132
|
+
const forceRefresh = !!ctx.forceRefresh
|
|
133
|
+
const formDataNumber = String(ctx.formDataNumber || '').trim()
|
|
134
|
+
const formDataJson = typeof ctx.formData === 'string'
|
|
135
|
+
? ctx.formData
|
|
136
|
+
: JSON.stringify(ctx.formData || {})
|
|
137
|
+
if (forceRefresh) {
|
|
138
|
+
return hasMeaningfulFormData(ctx.formData) ? { forceRefresh: true, formDataJson } : null
|
|
139
|
+
}
|
|
140
|
+
return formDataNumber ? { forceRefresh: false, formDataNumber, formDataJson } : null
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 六、一句话
|
|
147
|
+
|
|
148
|
+
**业务侧拼好 `requestPayload`;bizui 按 `scene` 调对应个人预算接口并展示。**
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
个人预算看板编排:防抖拉概述、宽屏弹窗 / 窄屏就地展开、禁止态事件
|
|
3
|
+
对齐底价 FieldFloorPriceBoard:≤768 默认 inline
|
|
4
|
+
|
|
5
|
+
不负责:各业务域字段映射、拼请求体 —— 由业务系统组装后经 FieldPersonBudgetBoard 传入。
|
|
6
|
+
-->
|
|
7
|
+
<template>
|
|
8
|
+
<div class="person-budget-dashboard">
|
|
9
|
+
<PersonBudgetOverview
|
|
10
|
+
:overview="overview"
|
|
11
|
+
:loading="loading"
|
|
12
|
+
:error="error"
|
|
13
|
+
:empty-text="emptyText"
|
|
14
|
+
:show-detail-link="showDetailLink && !!overview && !useDemo"
|
|
15
|
+
:detail-link-text="detailLinkText"
|
|
16
|
+
@view-detail="onViewDetail"
|
|
17
|
+
/>
|
|
18
|
+
<PersonBudgetDetailPanel
|
|
19
|
+
v-if="useInlineDetail && inlineOpen"
|
|
20
|
+
variant="inline"
|
|
21
|
+
:active="inlineOpen"
|
|
22
|
+
:overview="overview"
|
|
23
|
+
:request-payload="requestPayload"
|
|
24
|
+
:fetch-usage-page="fetchUsagePage"
|
|
25
|
+
:fetch-performance-page="fetchPerformancePage"
|
|
26
|
+
@collapse="collapseInline"
|
|
27
|
+
/>
|
|
28
|
+
<PersonBudgetDetailDialog
|
|
29
|
+
:visible.sync="detailVisible"
|
|
30
|
+
:overview="overview"
|
|
31
|
+
:request-payload="requestPayload"
|
|
32
|
+
:fetch-usage-page="fetchUsagePage"
|
|
33
|
+
:fetch-performance-page="fetchPerformancePage"
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script>
|
|
39
|
+
import { BUDGET_STATUS, normalizeBudgetStatus } from './format'
|
|
40
|
+
import { createDemoOverview } from './mock'
|
|
41
|
+
import PersonBudgetOverview from './PersonBudgetOverview.vue'
|
|
42
|
+
import PersonBudgetDetailDialog from './PersonBudgetDetailDialog.vue'
|
|
43
|
+
import PersonBudgetDetailPanel from './PersonBudgetDetailPanel.vue'
|
|
44
|
+
|
|
45
|
+
const NARROW_BREAKPOINT = 768
|
|
46
|
+
|
|
47
|
+
function debounce(fn, wait) {
|
|
48
|
+
let timer = null
|
|
49
|
+
const wrapped = function() {
|
|
50
|
+
const ctx = this
|
|
51
|
+
const args = arguments
|
|
52
|
+
if (timer) clearTimeout(timer)
|
|
53
|
+
timer = setTimeout(() => {
|
|
54
|
+
timer = null
|
|
55
|
+
fn.apply(ctx, args)
|
|
56
|
+
}, wait)
|
|
57
|
+
}
|
|
58
|
+
wrapped.cancel = function() {
|
|
59
|
+
if (timer) clearTimeout(timer)
|
|
60
|
+
timer = null
|
|
61
|
+
}
|
|
62
|
+
return wrapped
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function payloadSignature(payload) {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.stringify(payload || null)
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return String(payload)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default {
|
|
74
|
+
name: 'PersonBudgetDashboard',
|
|
75
|
+
components: {
|
|
76
|
+
PersonBudgetOverview,
|
|
77
|
+
PersonBudgetDetailDialog,
|
|
78
|
+
PersonBudgetDetailPanel
|
|
79
|
+
},
|
|
80
|
+
props: {
|
|
81
|
+
requestPayload: {
|
|
82
|
+
type: Object,
|
|
83
|
+
default: null
|
|
84
|
+
},
|
|
85
|
+
showDetailLink: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
default: true
|
|
88
|
+
},
|
|
89
|
+
emptyText: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: '请完善费用承担人和费用类型后查看个人预算'
|
|
92
|
+
},
|
|
93
|
+
debounceMs: {
|
|
94
|
+
type: Number,
|
|
95
|
+
default: 320
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* 窄屏明细形态:inline 看板下方展开(默认)| dialog 仍用弹窗
|
|
99
|
+
*/
|
|
100
|
+
narrowDetailMode: {
|
|
101
|
+
type: String,
|
|
102
|
+
default: 'inline',
|
|
103
|
+
validator(v) {
|
|
104
|
+
return ['inline', 'dialog'].includes(v)
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
/** (payload) => Promise<{ code, data, message }>;useDemo 时可为空 */
|
|
108
|
+
fetchOverview: {
|
|
109
|
+
type: Function,
|
|
110
|
+
default: null
|
|
111
|
+
},
|
|
112
|
+
fetchUsagePage: {
|
|
113
|
+
type: Function,
|
|
114
|
+
default: null
|
|
115
|
+
},
|
|
116
|
+
fetchPerformancePage: {
|
|
117
|
+
type: Function,
|
|
118
|
+
default: null
|
|
119
|
+
},
|
|
120
|
+
/** true 时不调接口,展示演示概述(配置态) */
|
|
121
|
+
useDemo: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: false
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
data() {
|
|
127
|
+
return {
|
|
128
|
+
loading: false,
|
|
129
|
+
error: '',
|
|
130
|
+
overview: null,
|
|
131
|
+
detailVisible: false,
|
|
132
|
+
inlineOpen: false,
|
|
133
|
+
isNarrow: false,
|
|
134
|
+
reqSeq: 0,
|
|
135
|
+
debouncedFetch: null
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
computed: {
|
|
139
|
+
useInlineDetail() {
|
|
140
|
+
return this.isNarrow && this.narrowDetailMode === 'inline'
|
|
141
|
+
},
|
|
142
|
+
detailLinkText() {
|
|
143
|
+
if (!this.useInlineDetail) return '查看详情 →'
|
|
144
|
+
return this.inlineOpen ? '收起详情 ↑' : '查看详情 ↓'
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
watch: {
|
|
148
|
+
requestPayload: {
|
|
149
|
+
deep: true,
|
|
150
|
+
handler() {
|
|
151
|
+
if (this.debouncedFetch) this.debouncedFetch()
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
useDemo: {
|
|
155
|
+
immediate: true,
|
|
156
|
+
handler(val) {
|
|
157
|
+
if (val) {
|
|
158
|
+
this.overview = createDemoOverview()
|
|
159
|
+
this.error = ''
|
|
160
|
+
this.loading = false
|
|
161
|
+
this.emitSubmitBlocked(false, false)
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
if (this.debouncedFetch) this.debouncedFetch()
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
useInlineDetail(inline) {
|
|
168
|
+
if (inline && this.detailVisible) {
|
|
169
|
+
this.detailVisible = false
|
|
170
|
+
this.inlineOpen = true
|
|
171
|
+
} else if (!inline) {
|
|
172
|
+
this.inlineOpen = false
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
overview(val) {
|
|
176
|
+
if (!val && this.inlineOpen) this.inlineOpen = false
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
created() {
|
|
180
|
+
this.debouncedFetch = debounce(() => {
|
|
181
|
+
this.fetchOverviewNow()
|
|
182
|
+
}, this.debounceMs)
|
|
183
|
+
if (!this.useDemo) this.debouncedFetch()
|
|
184
|
+
},
|
|
185
|
+
mounted() {
|
|
186
|
+
this.updateNarrow()
|
|
187
|
+
if (typeof window !== 'undefined') {
|
|
188
|
+
window.addEventListener('resize', this.updateNarrow)
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
beforeDestroy() {
|
|
192
|
+
if (this.debouncedFetch && this.debouncedFetch.cancel) {
|
|
193
|
+
this.debouncedFetch.cancel()
|
|
194
|
+
}
|
|
195
|
+
if (typeof window !== 'undefined') {
|
|
196
|
+
window.removeEventListener('resize', this.updateNarrow)
|
|
197
|
+
}
|
|
198
|
+
this.emitSubmitBlocked(false, false)
|
|
199
|
+
},
|
|
200
|
+
methods: {
|
|
201
|
+
updateNarrow() {
|
|
202
|
+
if (typeof window === 'undefined') return
|
|
203
|
+
this.isNarrow = window.innerWidth <= NARROW_BREAKPOINT
|
|
204
|
+
},
|
|
205
|
+
emitSubmitBlocked(blocked, warn) {
|
|
206
|
+
this.$emit('submit-blocked', { blocked: !!blocked, warn: !!warn })
|
|
207
|
+
},
|
|
208
|
+
clearBoard() {
|
|
209
|
+
this.overview = null
|
|
210
|
+
this.error = ''
|
|
211
|
+
this.loading = false
|
|
212
|
+
this.inlineOpen = false
|
|
213
|
+
this.emitSubmitBlocked(false, false)
|
|
214
|
+
},
|
|
215
|
+
async fetchOverviewNow() {
|
|
216
|
+
if (this.useDemo) {
|
|
217
|
+
this.overview = createDemoOverview()
|
|
218
|
+
this.error = ''
|
|
219
|
+
this.loading = false
|
|
220
|
+
this.emitSubmitBlocked(false, false)
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const payload = this.requestPayload
|
|
225
|
+
const sig = payloadSignature(payload)
|
|
226
|
+
const seq = ++this.reqSeq
|
|
227
|
+
|
|
228
|
+
if (!payload || typeof this.fetchOverview !== 'function') {
|
|
229
|
+
this.clearBoard()
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.loading = true
|
|
234
|
+
this.error = ''
|
|
235
|
+
try {
|
|
236
|
+
const res = await this.fetchOverview(payload)
|
|
237
|
+
if (seq !== this.reqSeq || sig !== payloadSignature(this.requestPayload)) return
|
|
238
|
+
|
|
239
|
+
if (!res || res.code !== 200) {
|
|
240
|
+
this.overview = null
|
|
241
|
+
this.error = (res && res.message) || '个人预算概述获取失败'
|
|
242
|
+
this.emitSubmitBlocked(false, false)
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
this.overview = res.data || null
|
|
247
|
+
const status = normalizeBudgetStatus(this.overview && this.overview.status)
|
|
248
|
+
this.emitSubmitBlocked(
|
|
249
|
+
status === BUDGET_STATUS.FORBID,
|
|
250
|
+
status === BUDGET_STATUS.WARN
|
|
251
|
+
)
|
|
252
|
+
} catch (e) {
|
|
253
|
+
if (seq !== this.reqSeq) return
|
|
254
|
+
this.overview = null
|
|
255
|
+
this.error = (e && e.message) || '个人预算概述获取失败'
|
|
256
|
+
this.emitSubmitBlocked(false, false)
|
|
257
|
+
} finally {
|
|
258
|
+
if (seq === this.reqSeq) this.loading = false
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
onViewDetail() {
|
|
262
|
+
if (!this.overview) return
|
|
263
|
+
// 配置态演示:无真实 requestPayload / fetch,不允许进明细
|
|
264
|
+
if (this.useDemo || !this.requestPayload) return
|
|
265
|
+
if (typeof this.fetchUsagePage !== 'function' || typeof this.fetchPerformancePage !== 'function') {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
if (this.useInlineDetail) {
|
|
269
|
+
this.toggleInline()
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
this.detailVisible = true
|
|
273
|
+
},
|
|
274
|
+
toggleInline() {
|
|
275
|
+
this.inlineOpen = !this.inlineOpen
|
|
276
|
+
},
|
|
277
|
+
collapseInline() {
|
|
278
|
+
this.inlineOpen = false
|
|
279
|
+
this.$nextTick(() => {
|
|
280
|
+
const el = this.$el
|
|
281
|
+
if (!el || typeof el.getBoundingClientRect !== 'function') return
|
|
282
|
+
if (el.getBoundingClientRect().top < 0) el.scrollIntoView({ block: 'start' })
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
</script>
|
|
288
|
+
|
|
289
|
+
<style lang="scss" scoped>
|
|
290
|
+
.person-budget-dashboard {
|
|
291
|
+
width: 100%;
|
|
292
|
+
max-width: 100%;
|
|
293
|
+
margin: 0 0 12px;
|
|
294
|
+
padding: 0;
|
|
295
|
+
box-sizing: border-box;
|
|
296
|
+
min-width: 0;
|
|
297
|
+
/* 收起明细回滚时给宿主固定顶栏留位(对齐底价看板) */
|
|
298
|
+
scroll-margin-top: 80px;
|
|
299
|
+
}
|
|
300
|
+
</style>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
个人预算详情弹窗(宽屏)
|
|
3
|
+
-->
|
|
4
|
+
<template>
|
|
5
|
+
<ImDialog
|
|
6
|
+
:visible.sync="dialogVisible"
|
|
7
|
+
custom-class="person-budget-detail-dialog-wrap"
|
|
8
|
+
title="个人预算详情"
|
|
9
|
+
:width="dialogWidth"
|
|
10
|
+
height="auto"
|
|
11
|
+
top="3vh"
|
|
12
|
+
container-style="maxHeight: 90vh;overflow-y: auto;"
|
|
13
|
+
append-to-body
|
|
14
|
+
destroy-on-close
|
|
15
|
+
hide-footer
|
|
16
|
+
@closeDialog="onClosed"
|
|
17
|
+
>
|
|
18
|
+
<PersonBudgetDetailPanel
|
|
19
|
+
variant="dialog"
|
|
20
|
+
:active="visible"
|
|
21
|
+
:overview="overview"
|
|
22
|
+
:request-payload="requestPayload"
|
|
23
|
+
:fetch-usage-page="fetchUsagePage"
|
|
24
|
+
:fetch-performance-page="fetchPerformancePage"
|
|
25
|
+
/>
|
|
26
|
+
</ImDialog>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script>
|
|
30
|
+
import PersonBudgetDetailPanel from './PersonBudgetDetailPanel.vue'
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
name: 'PersonBudgetDetailDialog',
|
|
34
|
+
components: {
|
|
35
|
+
PersonBudgetDetailPanel
|
|
36
|
+
},
|
|
37
|
+
props: {
|
|
38
|
+
visible: { type: Boolean, default: false },
|
|
39
|
+
overview: { type: Object, default: null },
|
|
40
|
+
requestPayload: { type: Object, default: null },
|
|
41
|
+
fetchUsagePage: { type: Function, required: true },
|
|
42
|
+
fetchPerformancePage: { type: Function, required: true }
|
|
43
|
+
},
|
|
44
|
+
data() {
|
|
45
|
+
return {
|
|
46
|
+
viewportWidth: typeof window !== 'undefined' ? window.innerWidth : 1360
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
computed: {
|
|
50
|
+
dialogVisible: {
|
|
51
|
+
get() {
|
|
52
|
+
return this.visible
|
|
53
|
+
},
|
|
54
|
+
set(v) {
|
|
55
|
+
this.$emit('update:visible', v)
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
dialogWidth() {
|
|
59
|
+
const w = this.viewportWidth || 1360
|
|
60
|
+
return `${Math.min(1360, Math.max(w - 48, 720))}px`
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
mounted() {
|
|
64
|
+
this.onResize = () => {
|
|
65
|
+
this.viewportWidth = window.innerWidth
|
|
66
|
+
}
|
|
67
|
+
window.addEventListener('resize', this.onResize)
|
|
68
|
+
},
|
|
69
|
+
beforeDestroy() {
|
|
70
|
+
if (this.onResize) window.removeEventListener('resize', this.onResize)
|
|
71
|
+
},
|
|
72
|
+
methods: {
|
|
73
|
+
onClosed() {
|
|
74
|
+
this.$emit('update:visible', false)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<style lang="scss">
|
|
81
|
+
.el-dialog.person-budget-detail-dialog-wrap {
|
|
82
|
+
max-width: calc(100vw - 48px);
|
|
83
|
+
}
|
|
84
|
+
</style>
|