verce-vue-test 0.0.8 → 0.0.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verce-vue-test",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -24,6 +24,7 @@
24
24
  "author": "",
25
25
  "license": "ISC",
26
26
  "dependencies": {
27
+ "echarts": "^6.1.0",
27
28
  "element-plus": "^2.14.0",
28
29
  "pinia": "^3.0.4",
29
30
  "vue": "^3.5.32",
package/src/App.vue CHANGED
@@ -1,51 +1,99 @@
1
1
  <script setup lang="ts">
2
+ import { ref } from 'vue'
2
3
  import { RouterView } from 'vue-router'
4
+ import {
5
+ HomeFilled,
6
+ Odometer,
7
+ User,
8
+ Document,
9
+ Grid,
10
+ Menu,
11
+ UserFilled,
12
+ EditPen,
13
+ DataLine,
14
+ InfoFilled,
15
+ } from '@element-plus/icons-vue'
16
+
17
+ const isCollapse = ref(false)
18
+
19
+ function toggleCollapse() {
20
+ isCollapse.value = !isCollapse.value
21
+ }
3
22
  </script>
4
23
 
5
24
  <template>
6
- <el-container style="min-height: 100vh">
7
- <el-aside width="200px">
8
- <el-menu
9
- :default-active="$route.path"
10
- router
11
- background-color="#304156"
12
- text-color="#bfcbd9"
13
- active-text-color="#409eff"
14
- >
15
- <el-menu-item index="/">
16
- <span>首页</span>
17
- </el-menu-item>
18
- <el-menu-item index="/dashboard">
19
- <span>仪表盘</span>
20
- </el-menu-item>
21
- <el-menu-item index="/users">
22
- <span>用户管理</span>
23
- </el-menu-item>
24
- <el-menu-item index="/form">
25
- <span>表单页面</span>
26
- </el-menu-item>
27
- <el-menu-item index="/table-pro">
28
- <span>高级表格</span>
29
- </el-menu-item>
30
- <el-menu-item index="/table-pro-for">
31
- <span>循环表格</span>
32
- </el-menu-item>
33
- <el-menu-item index="/employee">
34
- <span>员工管理</span>
35
- </el-menu-item>
36
- <el-menu-item index="/thousandth">
37
- <span>千分位输入</span>
38
- </el-menu-item>
39
- <el-menu-item index="/about">
40
- <span>关于</span>
41
- </el-menu-item>
42
- </el-menu>
25
+ <el-container style="height: 100vh">
26
+ <el-aside :width="isCollapse ? '64px' : '200px'" style="background-color: #304156; transition: width 0.3s; overflow: hidden; height: 100vh">
27
+ <div style="height: 50px; display: flex; align-items: center; padding-left: 20px; color: #fff; font-size: 16px; font-weight: bold; white-space: nowrap; overflow: hidden">
28
+ <span v-if="!isCollapse">管理系统</span>
29
+ <span v-else>系</span>
30
+ </div>
31
+ <el-scrollbar style="height: calc(100vh - 50px)">
32
+ <el-menu
33
+ :default-active="$route.path"
34
+ :collapse="isCollapse"
35
+ router
36
+ background-color="#304156"
37
+ text-color="#bfcbd9"
38
+ active-text-color="#409eff"
39
+ style="border-right: none; padding: 0; margin: 0"
40
+ >
41
+ <el-menu-item index="/">
42
+ <el-icon><HomeFilled /></el-icon>
43
+ <span>首页</span>
44
+ </el-menu-item>
45
+ <el-menu-item index="/dashboard">
46
+ <el-icon><Odometer /></el-icon>
47
+ <span>仪表盘</span>
48
+ </el-menu-item>
49
+ <el-menu-item index="/users">
50
+ <el-icon><User /></el-icon>
51
+ <span>用户管理</span>
52
+ </el-menu-item>
53
+ <el-menu-item index="/form">
54
+ <el-icon><Document /></el-icon>
55
+ <span>表单页面</span>
56
+ </el-menu-item>
57
+ <el-menu-item index="/table-pro">
58
+ <el-icon><Grid /></el-icon>
59
+ <span>高级表格</span>
60
+ </el-menu-item>
61
+ <el-menu-item index="/table-pro-for">
62
+ <el-icon><Menu /></el-icon>
63
+ <span>循环表格</span>
64
+ </el-menu-item>
65
+ <el-menu-item index="/employee">
66
+ <el-icon><UserFilled /></el-icon>
67
+ <span>员工管理</span>
68
+ </el-menu-item>
69
+ <el-menu-item index="/thousandth">
70
+ <el-icon><EditPen /></el-icon>
71
+ <span>千分位输入</span>
72
+ </el-menu-item>
73
+ <el-menu-item index="/position-forecast">
74
+ <el-icon><DataLine /></el-icon>
75
+ <span>头寸预报</span>
76
+ </el-menu-item>
77
+ <el-menu-item index="/about">
78
+ <el-icon><InfoFilled /></el-icon>
79
+ <span>关于</span>
80
+ </el-menu-item>
81
+ </el-menu>
82
+ </el-scrollbar>
43
83
  </el-aside>
44
84
 
45
- <el-container>
46
- <el-header>
85
+ <el-container style="height: 100vh; overflow: hidden">
86
+ <el-header style="display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e6e6e6">
87
+ <el-icon style="cursor: pointer; font-size: 20px" @click="toggleCollapse">
88
+ <svg v-if="isCollapse" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
89
+ <path fill="currentColor" d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 0 0 0 50.3l450.8 352.1c5.3 4.1 12.9 0.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"/>
90
+ </svg>
91
+ <svg v-else viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
92
+ <path fill="currentColor" d="M300 276.5v512c0 7.1 7.8 11.5 13 6.4l400-319.9c4.5-3.6 4.5-10.7 0-14.3l-400-319.9c-5.2-5.1-13-0.7-13 6.4z"/>
93
+ </svg>
94
+ </el-icon>
47
95
  <el-dropdown>
48
- <span>管理员</span>
96
+ <span style="cursor: pointer">管理员</span>
49
97
  <template #dropdown>
50
98
  <el-dropdown-menu>
51
99
  <el-dropdown-item>个人中心</el-dropdown-item>
@@ -55,7 +103,7 @@ import { RouterView } from 'vue-router'
55
103
  </el-dropdown>
56
104
  </el-header>
57
105
 
58
- <el-main>
106
+ <el-main style="height: calc(100vh - 60px); overflow-y: auto">
59
107
  <RouterView />
60
108
  </el-main>
61
109
  </el-container>
@@ -0,0 +1,19 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ html,
8
+ body {
9
+ margin: 0;
10
+ padding: 0;
11
+ height: 100%;
12
+ overflow: hidden;
13
+ }
14
+
15
+ #app {
16
+ margin: 0;
17
+ padding: 0;
18
+ height: 100%;
19
+ }
package/src/main.ts CHANGED
@@ -2,6 +2,7 @@ import { createApp } from 'vue'
2
2
  import { createPinia } from 'pinia'
3
3
  import ElementPlus from 'element-plus'
4
4
  import 'element-plus/dist/index.css'
5
+ import './assets/reset.css'
5
6
 
6
7
  import App from './App.vue'
7
8
  import router from './router'
@@ -49,6 +49,11 @@ const router = createRouter({
49
49
  name: 'thousandth',
50
50
  component: () => import('../views/ThousandthView.vue'),
51
51
  },
52
+ {
53
+ path: '/position-forecast',
54
+ name: 'positionForecast',
55
+ component: () => import('../views/PositionForecastView.vue'),
56
+ },
52
57
  ],
53
58
  })
54
59
 
@@ -0,0 +1,26 @@
1
+ import type { TableColumnCtx } from 'element-plus'
2
+
3
+ type FormatterFn = (
4
+ row: any,
5
+ column: TableColumnCtx<any>,
6
+ cellValue: any,
7
+ index: number,
8
+ ) => string
9
+
10
+ export const priceFormatter: FormatterFn = (
11
+ _row,
12
+ _column,
13
+ cellValue,
14
+ ) => `¥${Number(cellValue).toLocaleString()}`
15
+
16
+ export const booleanFormatter = (
17
+ truthyText = '启用',
18
+ falsyText = '禁用',
19
+ ): FormatterFn => (_row, _column, cellValue) =>
20
+ cellValue ? truthyText : falsyText
21
+
22
+ export const mapFormatter = (
23
+ mapping: Record<string, string>,
24
+ fallback = '',
25
+ ): FormatterFn => (_row, _column, cellValue) =>
26
+ mapping[String(cellValue)] ?? fallback
@@ -0,0 +1,437 @@
1
+ <!--
2
+ PositionForecastView - 分行头寸预报页面
3
+ ================================
4
+ 本页面用于展示分行头寸预报的总览数据、待处理任务以及偏离度计算结果。
5
+ 主要包含三个模块:
6
+ 1. 分行头寸预报总览(饼图 + 指标表格)
7
+ 2. 待处理任务列表
8
+ 3. 分行头寸预报偏离度计算结果表(含筛选、分页)
9
+ -->
10
+ <template>
11
+ <section class="position-forecast-page">
12
+ <!-- ======== 上半部分:左侧总览 + 右侧待处理任务,两列布局 ======== -->
13
+ <el-row :gutter="18" class="top-grid">
14
+ <!-- 左列:分行头寸预报总览 -->
15
+ <el-col :xs="24" :lg="12">
16
+ <el-card class="dashboard-card overview-card" shadow="never">
17
+ <template #header>
18
+ <h2 class="card-title"><span aria-hidden="true"></span>分行头寸预报总览</h2>
19
+ </template>
20
+
21
+ <div class="overview-body">
22
+ <!-- 图例区域:展示两个分类的颜色标识 -->
23
+ <div style="display: flex; gap: 24px; font-size: 13px; color: #111827; padding-top: 4px">
24
+ <span><i style="display: inline-block; width: 10px; height: 10px; border-radius: 2px; background: #126bff; vertical-align: -1px; margin-right: 6px"></i>本分行头寸预报</span>
25
+ <span><i style="display: inline-block; width: 10px; height: 10px; border-radius: 2px; background: #0bb06e; vertical-align: -1px; margin-right: 6px"></i>收支预报</span>
26
+ </div>
27
+
28
+ <!-- 内容区域:左侧饼图 + 右侧指标表格 -->
29
+ <div style="display: grid; flex: 1; grid-template-columns: 1fr 1fr; align-items: center; column-gap: 14px">
30
+ <!-- 饼图容器,由 ECharts 渲染,展示本分行头寸预报和收支预报的占比 -->
31
+ <div ref="pieChartRef" style="width: 100%; height: 180px"></div>
32
+
33
+ <!-- 指标表格:展示各项目在今日、本月、今年的完成比例 -->
34
+ <el-table :data="forecastData" size="small" :show-header="true" :header-cell-style="{ background: 'transparent', fontWeight: 600 }" :cell-style="{ fontSize: '13px' }">
35
+ <!-- 项目名称列,带颜色圆点标识 -->
36
+ <el-table-column prop="name" label="项目" min-width="90">
37
+ <template #default="{ row }">
38
+ <span><i :style="{ display: 'inline-block', width: '8px', height: '8px', borderRadius: '50%', background: row.color, verticalAlign: '-1px', marginRight: '6px' }"></i>{{ row.name }}</span>
39
+ </template>
40
+ </el-table-column>
41
+ <el-table-column prop="today" label="今日" align="center" />
42
+ <el-table-column prop="month" label="本月" align="center" />
43
+ <el-table-column prop="year" label="今年" align="center" />
44
+ </el-table>
45
+ </div>
46
+ </div>
47
+ </el-card>
48
+ </el-col>
49
+
50
+ <!-- 右列:待处理任务 -->
51
+ <el-col :xs="24" :lg="12">
52
+ <el-card class="dashboard-card task-card" shadow="never">
53
+ <template #header>
54
+ <h2 class="card-title"><span aria-hidden="true"></span>待处理任务</h2>
55
+ </template>
56
+
57
+ <!-- 待处理任务表格 -->
58
+ <!-- 状态列:使用 el-checkbox 展示是否完成 -->
59
+ <!-- 任务结果列:使用 el-tag 展示成功/警告状态 -->
60
+ <el-table :data="pendingTasks" size="small" :show-header="true" :header-cell-style="{ background: 'transparent', fontWeight: 600 }" :cell-style="{ fontSize: '13px' }">
61
+ <el-table-column prop="status" label="状态" align="center" width="60">
62
+ <template #default="{ row }">
63
+ <el-checkbox :model-value="row.status" />
64
+ </template>
65
+ </el-table-column>
66
+ <el-table-column prop="process" label="流程" align="center" />
67
+ <el-table-column prop="task" label="任务" align="center" />
68
+ <el-table-column prop="pending" label="待办" align="center" />
69
+ <el-table-column prop="delay" label="延迟" align="center" />
70
+ <el-table-column prop="timeout" label="超时" align="center" />
71
+ <!-- 任务结果:success 显示绿色勾,warning 显示橙色叹号 -->
72
+ <el-table-column prop="result" label="任务" align="center" width="60">
73
+ <template #default="{ row }">
74
+ <el-tag :type="row.result === 'success' ? 'success' : 'warning'" size="small" effect="dark" style="border: none" round>
75
+ <el-icon><component :is="row.result === 'success' ? Check : Warning" /></el-icon>
76
+ </el-tag>
77
+ </template>
78
+ </el-table-column>
79
+ </el-table>
80
+ </el-card>
81
+ </el-col>
82
+ </el-row>
83
+
84
+ <!-- ======== 下半部分:分行头寸预报偏离度计算结果表 ======== -->
85
+ <el-card class="dashboard-card result-card" shadow="never">
86
+ <template #header>
87
+ <h2 class="card-title"><span aria-hidden="true"></span>分行头寸预报偏离度计算结果表</h2>
88
+ </template>
89
+
90
+ <!-- 筛选栏:支持按管辖分行、日期范围、偏离度类型、确认结果进行筛选 -->
91
+ <div style="display: flex; align-items: center; justify-content: space-between; padding: 12px 0">
92
+ <div style="display: flex; align-items: center; gap: 12px">
93
+ <!-- 管辖分行筛选 -->
94
+ <el-select v-model="filter.region" placeholder="请选择管辖分行" style="width: 200px" clearable>
95
+ <el-option label="全部" value="" />
96
+ <el-option label="14分行" value="14分行" />
97
+ <el-option label="本行头寸预报本行" value="本行头寸预报本行" />
98
+ </el-select>
99
+ <!-- 日期范围筛选 -->
100
+ <el-date-picker
101
+ v-model="filter.dateRange"
102
+ type="daterange"
103
+ start-placeholder="开始日期"
104
+ end-placeholder="截止日期"
105
+ range-separator=""
106
+ style="width: 240px"
107
+ />
108
+ <!-- 偏离度类型筛选 -->
109
+ <span style="font-size: 13px; color: #1f2937">偏离度类型</span>
110
+ <el-select v-model="filter.type" style="width: 100px">
111
+ <el-option label="全部类型" value="" />
112
+ <el-option label="总账活期" value="总账活期" />
113
+ <el-option label="总账定期" value="总账定期" />
114
+ </el-select>
115
+ <!-- 确认结果筛选 -->
116
+ <span style="font-size: 13px; color: #1f2937">确认结果</span>
117
+ <el-select v-model="filter.result" style="width: 90px">
118
+ <el-option label="全部" value="" />
119
+ <el-option label="已确认" value="confirmed" />
120
+ <el-option label="未确认" value="unconfirmed" />
121
+ </el-select>
122
+ <!-- 操作按钮 -->
123
+ <el-button @click="resetFilter">重置</el-button>
124
+ <el-button type="primary" @click="search">查询</el-button>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- 偏离度计算结果表格:展示辖区、分行内码、来源类型、预报日期等详细信息 -->
129
+ <el-table :data="positionResults" class="result-table" border>
130
+ <el-table-column type="index" label="#" width="74" align="center" />
131
+ <el-table-column prop="region" label="辖区" min-width="170" align="center" />
132
+ <el-table-column prop="branchCode" label="分行内码" min-width="170" align="center" />
133
+ <el-table-column prop="sourceType" label="头寸预报来源类型" min-width="190" align="center" />
134
+ <el-table-column prop="forecastDate" label="预报日期" min-width="190" align="center" />
135
+ <el-table-column prop="receiveTime" label="数据接收时间" min-width="190" align="center" />
136
+ <el-table-column prop="responsibleBranch" label="数据预报责任岗" min-width="190" align="center" />
137
+ <el-table-column prop="deviation" label="偏离度" min-width="170" align="center" />
138
+ <!-- 详情操作列:支持预览和查看 -->
139
+ <el-table-column label="详情" width="170" align="center">
140
+ <template #default>
141
+ <el-button link type="primary">预览</el-button>
142
+ <el-button link type="primary">查看</el-button>
143
+ </template>
144
+ </el-table-column>
145
+ </el-table>
146
+
147
+ <!-- 分页组件:使用 Element Plus 内置分页,支持总数显示、每页条数切换、页码跳转 -->
148
+ <div style="display: flex; justify-content: flex-end; padding-top: 16px">
149
+ <el-pagination
150
+ v-model:current-page="currentPage"
151
+ v-model:page-size="pageSize"
152
+ :total="total"
153
+ :page-sizes="[10, 20, 50]"
154
+ layout="total, sizes, prev, pager, next, jumper"
155
+ background
156
+ />
157
+ </div>
158
+ </el-card>
159
+ </section>
160
+ </template>
161
+
162
+ <script setup lang="ts">
163
+ import { nextTick, onMounted, onUnmounted, ref } from 'vue'
164
+ import * as echarts from 'echarts'
165
+ import { Check, Warning } from '@element-plus/icons-vue'
166
+
167
+ /**
168
+ * 日期范围类型定义
169
+ * 用于筛选栏的 el-date-picker,值为 [开始日期, 结束日期] 或空数组
170
+ */
171
+ type DateRange = [Date, Date] | []
172
+
173
+ /** 饼图 DOM 容器引用 */
174
+ const pieChartRef = ref<HTMLElement | null>(null)
175
+
176
+ /** ECharts 实例,用于后续 resize 和 dispose */
177
+ let pieChart: echarts.ECharts | null = null
178
+
179
+ /** 筛选条件表单 */
180
+ const filter = ref({
181
+ region: '', // 管辖分行
182
+ dateRange: [] as DateRange, // 计算日期范围
183
+ type: '', // 偏离度类型:总账活期 / 总账定期
184
+ result: '', // 确认结果:confirmed / unconfirmed
185
+ })
186
+
187
+ /** 分页相关状态 */
188
+ const currentPage = ref(1) // 当前页码
189
+ const pageSize = ref(10) // 每页条数
190
+ const total = ref(47) // 总记录数
191
+
192
+ /**
193
+ * 分行头寸预报总览数据
194
+ * name: 项目名称
195
+ * today/month/year: 今日/本月/今年的完成比例
196
+ * color: 对应的颜色标识
197
+ */
198
+ const forecastData = [
199
+ { name: '借款', today: '125%', month: '-', year: '264.95%', color: '#ff9500' },
200
+ { name: '结算性存款', today: '35%', month: '14.61%', year: '136.92%', color: '#ff9500' },
201
+ { name: '保证金存款', today: '52%', month: '14.35%', year: '166.25%', color: '#0bb06e' },
202
+ { name: '拆放款', today: '-', month: '-', year: '-', color: '#126bff' },
203
+ ]
204
+
205
+ /**
206
+ * 待处理任务数据
207
+ * status: 是否已完成(true=已完成,显示勾选框)
208
+ * process: 所属流程名称
209
+ * task: 具体任务名称
210
+ * pending/delay/timeout: 待办/延迟/超时的数量
211
+ * result: 任务执行结果(success=成功,warning=警告)
212
+ */
213
+ const pendingTasks = [
214
+ { status: true, process: '头寸预报', task: '待办处理', pending: '37235', delay: '1259.34', timeout: '', result: 'success' },
215
+ { status: false, process: '额度管理', task: '额度审批', pending: '57285', delay: '13785', timeout: '', result: 'success' },
216
+ { status: false, process: '监控与预警', task: '预警处理', pending: '696', delay: '', timeout: '1522.22', result: 'warning' },
217
+ ]
218
+
219
+ /**
220
+ * 偏离度计算结果数据
221
+ * region: 辖区
222
+ * branchCode: 分行内码
223
+ * sourceType: 头寸预报来源类型(总账活期/总账定期)
224
+ * forecastDate: 预报日期
225
+ * receiveTime: 数据接收时间(此处实际存储的是管辖分行信息)
226
+ * responsibleBranch: 数据预报责任岗
227
+ * deviation: 偏离度数值
228
+ */
229
+ const positionResults = [
230
+ { region: '本行头寸预报本行', branchCode: '20210114-027', sourceType: '总账活期', forecastDate: '2021-06-52 16:33', receiveTime: '14分行', responsibleBranch: '岗1', deviation: '12.300000' },
231
+ { region: '分行头寸预报本行', branchCode: '20210114-027', sourceType: '总账定期', forecastDate: '2021-06-52 16:33', receiveTime: '14分行', responsibleBranch: '岗1', deviation: '12.300000' },
232
+ { region: '本行头寸预报本行', branchCode: '20210114-027', sourceType: '总账活期', forecastDate: '2021-04-58 12:33', receiveTime: '14分行', responsibleBranch: '岗1', deviation: '12.300000' },
233
+ { region: '分行头寸预报本行', branchCode: '20210114-027', sourceType: '总账活期', forecastDate: '2021-02-58 10:33', receiveTime: '14分行', responsibleBranch: '岗1', deviation: '12.300000' },
234
+ { region: '分行头寸预报本行', branchCode: '20210114-027', sourceType: '总账活期', forecastDate: '2021-01-53 16:33', receiveTime: '14分行', responsibleBranch: '岗1', deviation: '12.300000' },
235
+ ]
236
+
237
+ /**
238
+ * 初始化饼图
239
+ * 使用 ECharts 渲染环形图,展示本分行头寸预报和收支预报的占比
240
+ * 中心文字显示收支预报的总百分比
241
+ */
242
+ const initPieChart = () => {
243
+ if (!pieChartRef.value) return
244
+
245
+ pieChart = echarts.init(pieChartRef.value)
246
+ const option: echarts.EChartsOption = {
247
+ color: ['#126bff', '#ffb73f'],
248
+ tooltip: {
249
+ trigger: 'item',
250
+ formatter: (params) => {
251
+ const item = Array.isArray(params) ? params[0] : params
252
+ return item ? `${item.name}<br/>${item.value}%` : ''
253
+ },
254
+ },
255
+ // 中心文字:显示总百分比和副标题
256
+ graphic: [
257
+ {
258
+ type: 'text',
259
+ left: 'center',
260
+ top: '42%',
261
+ style: {
262
+ text: '7539%',
263
+ fill: '#126bff',
264
+ fontSize: 20,
265
+ fontWeight: 700,
266
+ },
267
+ },
268
+ {
269
+ type: 'text',
270
+ left: 'center',
271
+ top: '56%',
272
+ style: {
273
+ text: '收支预报',
274
+ fill: '#6b7280',
275
+ fontSize: 12,
276
+ },
277
+ },
278
+ ],
279
+ series: [
280
+ {
281
+ type: 'pie',
282
+ radius: ['52%', '76%'], // 内外半径,形成环形图
283
+ center: ['50%', '50%'],
284
+ startAngle: 90,
285
+ minAngle: 14,
286
+ avoidLabelOverlap: true,
287
+ // 标签:显示百分比数值
288
+ label: {
289
+ show: true,
290
+ formatter: ({ value }) => `${value}%`,
291
+ color: '#126bff',
292
+ fontSize: 12,
293
+ fontWeight: 600,
294
+ },
295
+ // 标签引导线
296
+ labelLine: {
297
+ show: true,
298
+ length: 18,
299
+ length2: 52,
300
+ lineStyle: { width: 1 },
301
+ },
302
+ itemStyle: {
303
+ borderColor: '#fff',
304
+ borderWidth: 2,
305
+ },
306
+ // 两个数据系列:本分行头寸预报(蓝色)和收支预报(橙色)
307
+ data: [
308
+ {
309
+ value: 96.28,
310
+ name: '本分行头寸预报',
311
+ itemStyle: { color: '#126bff' },
312
+ label: { color: '#126bff' },
313
+ labelLine: { lineStyle: { color: '#126bff' } },
314
+ },
315
+ {
316
+ value: 22.28,
317
+ name: '收支预报',
318
+ itemStyle: { color: '#ffb73f' },
319
+ label: { color: '#ff9500' },
320
+ labelLine: { lineStyle: { color: '#ff9500' } },
321
+ },
322
+ ],
323
+ },
324
+ ],
325
+ }
326
+
327
+ pieChart.setOption(option)
328
+ }
329
+
330
+ /** 窗口 resize 时重新调整饼图尺寸 */
331
+ const handleResize = () => {
332
+ pieChart?.resize()
333
+ }
334
+
335
+ /** 重置筛选条件为初始状态 */
336
+ const resetFilter = () => {
337
+ filter.value = {
338
+ region: '',
339
+ dateRange: [],
340
+ type: '',
341
+ result: '',
342
+ }
343
+ }
344
+
345
+ /** 执行查询(待接入后端接口) */
346
+ const search = () => {
347
+ console.log('搜索条件:', filter.value)
348
+ }
349
+
350
+ /**
351
+ * 生命周期:组件挂载后初始化饼图,并监听窗口 resize 事件
352
+ * 使用 nextTick 确保 DOM 已渲染完成
353
+ */
354
+ onMounted(() => {
355
+ nextTick(() => {
356
+ initPieChart()
357
+ window.addEventListener('resize', handleResize)
358
+ })
359
+ })
360
+
361
+ /**
362
+ * 生命周期:组件卸载前清理资源
363
+ * 移除 resize 监听,销毁 ECharts 实例释放内存
364
+ */
365
+ onUnmounted(() => {
366
+ window.removeEventListener('resize', handleResize)
367
+ pieChart?.dispose()
368
+ })
369
+ </script>
370
+
371
+ <style scoped>
372
+ /* 页面根容器,宽度自适应父组件 */
373
+ .position-forecast-page {
374
+ width: 100%;
375
+ padding: 0;
376
+ color: #1f2937;
377
+ }
378
+
379
+ /* 上半部分两列之间的底部间距 */
380
+ .top-grid {
381
+ margin-bottom: 16px;
382
+ }
383
+
384
+ /* 所有卡片的头部样式:去掉底部边框,调整内边距 */
385
+ .dashboard-card :deep(.el-card__header) {
386
+ border-bottom: 0;
387
+ padding: 14px 20px 6px;
388
+ }
389
+
390
+ /* 所有卡片的内容区域样式 */
391
+ .dashboard-card :deep(.el-card__body) {
392
+ padding: 0 20px 16px;
393
+ }
394
+
395
+ /* 卡片标题样式:左侧带蓝色竖条装饰 */
396
+ .card-title {
397
+ display: flex;
398
+ align-items: center;
399
+ gap: 8px;
400
+ margin: 0;
401
+ color: #111827;
402
+ font-size: 15px;
403
+ font-weight: 600;
404
+ line-height: 1;
405
+ }
406
+
407
+ /* 标题左侧的蓝色竖条 */
408
+ .card-title span {
409
+ width: 4px;
410
+ height: 14px;
411
+ border-radius: 1px;
412
+ background: #126bff;
413
+ }
414
+
415
+ /* 上半部分左右两张卡片的固定高度 */
416
+ .overview-card,
417
+ .task-card {
418
+ height: 270px;
419
+ }
420
+
421
+ /* 总览卡片内容区域的纵向布局 */
422
+ .overview-body {
423
+ display: flex;
424
+ flex-direction: column;
425
+ }
426
+
427
+ /* 偏离度结果表卡片头部额外上边距 */
428
+ .result-card :deep(.el-card__header) {
429
+ padding-top: 14px;
430
+ }
431
+
432
+ /* 偏离度结果表的表头背景色和加粗 */
433
+ .result-table :deep(.el-table__header th) {
434
+ background: #f7f9fd;
435
+ font-weight: 600;
436
+ }
437
+ </style>
@@ -1,4 +1,6 @@
1
1
  <template>
2
+
3
+ 111
2
4
  <ElTablePro
3
5
  :data="tableData"
4
6
  :total="total"
@@ -15,6 +17,27 @@
15
17
  <el-button type="primary" @click="handleAdd">新增</el-button>
16
18
  </template>
17
19
 
20
+ <el-table-column type="expand">
21
+ <template #default="{ row }">
22
+ <div class="expand-detail">
23
+ <el-descriptions :column="2" border size="small">
24
+ <el-descriptions-item label="商品 ID">{{ row.id }}</el-descriptions-item>
25
+ <el-descriptions-item label="商品名称">{{ row.name }}</el-descriptions-item>
26
+ <el-descriptions-item label="分类">
27
+ <el-tag :type="categoryTagType(row.category)">{{ row.category }}</el-tag>
28
+ </el-descriptions-item>
29
+ <el-descriptions-item label="价格">{{ formatPrice(row.price) }}</el-descriptions-item>
30
+ <el-descriptions-item label="库存">{{ row.stock }}</el-descriptions-item>
31
+ <el-descriptions-item label="状态">
32
+ <el-tag :type="row.status ? 'success' : 'danger'">
33
+ {{ row.status ? '启用' : '禁用' }}
34
+ </el-tag>
35
+ </el-descriptions-item>
36
+ </el-descriptions>
37
+ </div>
38
+ </template>
39
+ </el-table-column>
40
+
18
41
  <el-table-column
19
42
  v-for="col in visibleColumns"
20
43
  :key="col.prop"
@@ -23,19 +46,20 @@
23
46
  :width="col.width"
24
47
  :min-width="col.minWidth"
25
48
  :visible="col.visible"
49
+ :formatter="col.formatter"
26
50
  >
27
51
  <template v-if="col.slot" #default="{ row }">
28
52
  <el-tag
29
- v-if="col.prop === 'status'"
30
- :type="row.status ? 'success' : 'danger'"
53
+ v-if="col.prop === 'category'"
54
+ :type="categoryTagType(row.category)"
31
55
  >
32
- {{ row.status ? '启用' : '禁用' }}
56
+ {{ row.category }}
33
57
  </el-tag>
34
58
  <el-tag
35
- v-else-if="col.prop === 'category'"
36
- :type="categoryTagType(row.category)"
59
+ v-else-if="col.prop === 'status'"
60
+ :type="row.status ? 'success' : 'danger'"
37
61
  >
38
- {{ row.category }}
62
+ {{ row.status ? '启用' : '禁用' }}
39
63
  </el-tag>
40
64
  <template v-else-if="col.prop === 'action'">
41
65
  <el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
@@ -50,6 +74,7 @@
50
74
  import { ref, reactive, onMounted } from 'vue'
51
75
  import { ElMessage, ElMessageBox } from 'element-plus'
52
76
  import ElTablePro from '@/components/ElTablePro.vue'
77
+ import { priceFormatter } from '@/utils/formatters'
53
78
 
54
79
  interface ColumnConfig {
55
80
  prop: string
@@ -58,13 +83,14 @@ interface ColumnConfig {
58
83
  minWidth?: number | string
59
84
  slot?: boolean
60
85
  visible?: boolean
86
+ formatter?: (row: any, column: any, cellValue: any, index: number) => string
61
87
  }
62
88
 
63
89
  const columns: ColumnConfig[] = [
64
90
  { prop: 'id', label: 'ID', width: 80 },
65
91
  { prop: 'name', label: '商品名称', minWidth: 150, visible: false },
66
92
  { prop: 'category', label: '分类', width: 120, slot: true },
67
- { prop: 'price', label: '价格', width: 100 },
93
+ { prop: 'price', label: '价格', width: 100, formatter: priceFormatter },
68
94
  { prop: 'stock', label: '库存', width: 80 },
69
95
  { prop: 'status', label: '状态', width: 100, slot: true },
70
96
  { prop: 'action', label: '操作', width: 180, slot: true },
@@ -107,6 +133,8 @@ const categoryTagType = (category: string) => {
107
133
  return map[category] || 'info'
108
134
  }
109
135
 
136
+ const formatPrice = (value: number) => `¥${Number(value).toLocaleString()}`
137
+
110
138
  const getList = () => {
111
139
  loading.value = true
112
140
  setTimeout(() => {
@@ -144,3 +172,9 @@ onMounted(() => {
144
172
  getList()
145
173
  })
146
174
  </script>
175
+
176
+ <style scoped>
177
+ .expand-detail {
178
+ padding: 12px 24px;
179
+ }
180
+ </style>
package/vite.config.ts CHANGED
@@ -6,6 +6,10 @@ import vueDevTools from 'vite-plugin-vue-devtools'
6
6
 
7
7
  // https://vite.dev/config/
8
8
  export default defineConfig({
9
+ server: {
10
+ port: 5173,
11
+ strictPort: false,
12
+ },
9
13
  plugins: [
10
14
  vue(),
11
15
  vueDevTools(),