refactor(wel): 重构欢迎页为模块化组件
- 将欢迎页整体结构拆分为多个功能区组件,提高代码可维护性 - 移除原有欢迎横幅、指标卡、图表区、下方区及快捷入口的静态实现 - 引入并注册 SectionKpi、SectionStructure、SectionBusiness 等八个子组件 - 删除冗余样式,更新整体背景和布局样式 - 精简模板内容,仅保留子组件渲染及页脚版权信息 - 移除无用的响应式数据、计算属性及方法 - 包含依赖包 echarts 的新增,支持后续图表功能开发
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# 2026-09-30
|
||||
|
||||
## /wel/index 首页重构为运营数据看板(按蓝湖效果图)
|
||||
- 新增依赖:echarts ^6.1.0(corepack pnpm add echarts)。
|
||||
- 重构 `src/views/wel/index.vue`:改为「核心经营指标 / 运输结构 / 业务单元情况 / 项目情况 / 财务情况 / 车队与线路 / 车务管理 / 底部占比与趋势」8 大板块 + 版权页脚,删除原欢迎横幅/快捷入口。
|
||||
- 新增文件:
|
||||
- `src/views/wel/chart-options.js`:排行条形/盈亏双向/环形/分组柱/面积折线/堆叠柱 6 类 option 生成器(fmt 千分位、红涨绿跌 rich label)。
|
||||
- `src/views/wel/dashboard.scss`:看板公共样式,各组件 scoped `@import '../dashboard.scss'`(子目录注意路径)。
|
||||
- `src/views/wel/components/`:wel-chart.vue(ECharts 封装:ResizeObserver + activated resize + dispose)、wel-filters.vue(统计时间/条件筛选 mock)、rank-panel.vue(排行/盈亏通用面板)、section-kpi / section-structure / section-business / section-project / section-finance / section-fleet / section-vehicle / section-summary.vue。
|
||||
- 数据全部为 Mock,组件内已注释标注对接点;蓝湖红字标注的指标口径未定,待后端统计接口(估 12~18 个)确定后替换。
|
||||
- 自检:dev server(2889) 下所有新文件及 `?vue&type=style&...&lang.scss` 均 200;`/node_modules/.vite/deps/echarts.js` 200。vite build 因 facelogin.vue 沙箱中断不可用,未跑。
|
||||
@@ -23,6 +23,7 @@
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.10.6",
|
||||
"disable-devtool": "^0.3.8",
|
||||
"echarts": "^6.1.0",
|
||||
"element-plus": "^2.14.2",
|
||||
"exceljs": "^4.4.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
|
||||
Generated
+62
-57
@@ -5,9 +5,7 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
'@smallwei/avue':
|
||||
hash: 5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db
|
||||
path: patches/@smallwei__avue.patch
|
||||
'@smallwei/avue': 5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db
|
||||
|
||||
importers:
|
||||
|
||||
@@ -33,16 +31,16 @@ importers:
|
||||
version: 1.7.0
|
||||
'@smallwei/avue':
|
||||
specifier: ~3.9.2
|
||||
version: 3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)
|
||||
version: 3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)
|
||||
animate.css:
|
||||
specifier: ^4.1.1
|
||||
version: 4.1.1
|
||||
avue-plugin-ueditor:
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(axios@1.16.0)(vue@3.5.34)
|
||||
version: 1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34)
|
||||
axios:
|
||||
specifier: ^1.13.5
|
||||
version: 1.16.0
|
||||
version: 1.16.0(debug@4.4.3(supports-color@7.2.0))
|
||||
codemirror:
|
||||
specifier: ^5.65.21
|
||||
version: 5.65.21
|
||||
@@ -55,6 +53,9 @@ importers:
|
||||
disable-devtool:
|
||||
specifier: ^0.3.8
|
||||
version: 0.3.9
|
||||
echarts:
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0
|
||||
element-plus:
|
||||
specifier: ^2.14.2
|
||||
version: 2.14.2(vue@3.5.34)
|
||||
@@ -63,13 +64,13 @@ importers:
|
||||
version: 4.4.0
|
||||
highlight.js:
|
||||
specifier: ^11.9.0
|
||||
version: 11.11.1
|
||||
version: 11.12.0
|
||||
js-base64:
|
||||
specifier: ^3.7.4
|
||||
version: 3.7.8
|
||||
version: 3.9.4
|
||||
js-cookie:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.5
|
||||
version: 3.0.8
|
||||
js-md5:
|
||||
specifier: ^0.7.3
|
||||
version: 0.7.3
|
||||
@@ -127,7 +128,7 @@ importers:
|
||||
version: 5.4.21(sass@1.99.0)(terser@5.46.2)
|
||||
vite-plugin-compression:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1(vite@5.4.21(sass@1.99.0)(terser@5.46.2))
|
||||
version: 0.5.1(supports-color@7.2.0)(vite@5.4.21(sass@1.99.0)(terser@5.46.2))
|
||||
vite-plugin-vue-setup-extend:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(vite@5.4.21(sass@1.99.0)(terser@5.46.2))
|
||||
@@ -1208,6 +1209,7 @@ packages:
|
||||
|
||||
crypto-js@4.2.0:
|
||||
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
|
||||
deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained.
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
@@ -1453,8 +1455,8 @@ packages:
|
||||
heic2any@0.0.4:
|
||||
resolution: {integrity: sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==}
|
||||
|
||||
highlight.js@11.11.1:
|
||||
resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==}
|
||||
highlight.js@11.12.0:
|
||||
resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
hls.js@1.6.16:
|
||||
@@ -1532,12 +1534,11 @@ packages:
|
||||
isarray@1.0.0:
|
||||
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
|
||||
|
||||
js-base64@3.7.8:
|
||||
resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==}
|
||||
js-base64@3.9.4:
|
||||
resolution: {integrity: sha512-PtOMXpEGuP0RRiRXsjzHzl44dMHxSu2CPvAhinupR1tBa88me+1DPqsobl7eupn5UukjLkln1ZnAOAOXOrYG0Q==}
|
||||
|
||||
js-cookie@3.0.5:
|
||||
resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
|
||||
engines: {node: '>=14'}
|
||||
js-cookie@3.0.8:
|
||||
resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
|
||||
|
||||
js-md5@0.7.3:
|
||||
resolution: {integrity: sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==}
|
||||
@@ -2653,10 +2654,10 @@ snapshots:
|
||||
countup.js: 2.10.0
|
||||
signature_pad: 5.1.3
|
||||
|
||||
'@smallwei/avue@3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)':
|
||||
'@smallwei/avue@3.9.2(patch_hash=5ba15ad41b2c301250bf24adf39da2ed526b8375143da2c5a495361b29e862db)(@element-plus/icons-vue@2.3.2(vue@3.5.34))(debug@4.4.3(supports-color@7.2.0))(element-plus@2.14.2(vue@3.5.34))(vue@3.5.34)':
|
||||
dependencies:
|
||||
'@element-plus/icons-vue': 2.3.2(vue@3.5.34)
|
||||
axios: 0.21.4
|
||||
axios: 0.21.4(debug@4.4.3(supports-color@7.2.0))
|
||||
countup.js: 1.9.3
|
||||
dayjs: 1.11.20
|
||||
element-plus: 2.14.2(vue@3.5.34)
|
||||
@@ -2792,25 +2793,25 @@ snapshots:
|
||||
dependencies:
|
||||
vue: 3.5.34
|
||||
|
||||
'@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 3.0.0
|
||||
is-url: 1.2.4
|
||||
lodash.throttle: 4.1.1
|
||||
nanoid: 3.3.12
|
||||
slate: 0.72.8
|
||||
nanoid: 5.1.11
|
||||
slate: 0.123.0
|
||||
snabbdom: 3.6.3
|
||||
|
||||
'@wangeditor-next/code-highlight@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/code-highlight@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 4.0.6
|
||||
prismjs: 1.30.0
|
||||
slate: 0.123.0
|
||||
snabbdom: 3.6.3
|
||||
|
||||
'@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@types/event-emitter': 0.3.5
|
||||
'@uppy/core': 2.3.4
|
||||
@@ -2826,9 +2827,9 @@ snapshots:
|
||||
lodash.foreach: 4.5.0
|
||||
lodash.throttle: 4.1.1
|
||||
lodash.toarray: 4.4.0
|
||||
nanoid: 3.3.12
|
||||
nanoid: 5.1.11
|
||||
scroll-into-view-if-needed: 3.1.0
|
||||
slate: 0.72.8
|
||||
slate: 0.123.0
|
||||
slate-history: 0.115.0(slate@0.123.0)
|
||||
snabbdom: 3.6.3
|
||||
|
||||
@@ -2836,13 +2837,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@uppy/core': 2.3.4
|
||||
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
|
||||
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/code-highlight': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/list-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/table-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/upload-image-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/video-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/code-highlight': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/list-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/table-module': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/upload-image-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/video-module': 2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 4.0.6
|
||||
is-hotkey: 0.2.0
|
||||
lodash.camelcase: 4.3.0
|
||||
@@ -2855,16 +2856,16 @@ snapshots:
|
||||
slate: 0.123.0
|
||||
snabbdom: 3.6.3
|
||||
|
||||
'@wangeditor-next/list-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/list-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 4.0.6
|
||||
slate: 0.123.0
|
||||
snabbdom: 3.6.3
|
||||
|
||||
'@wangeditor-next/table-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/table-module@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 4.0.6
|
||||
lodash.debounce: 4.0.8
|
||||
lodash.throttle: 4.1.1
|
||||
@@ -2872,22 +2873,22 @@ snapshots:
|
||||
slate: 0.123.0
|
||||
snabbdom: 3.6.3
|
||||
|
||||
'@wangeditor-next/upload-image-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/upload-image-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/basic-modules@2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@uppy/core': 2.3.4
|
||||
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
|
||||
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/basic-modules': 2.0.0(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 4.0.6
|
||||
lodash.foreach: 4.5.0
|
||||
slate: 0.123.0
|
||||
snabbdom: 3.6.3
|
||||
|
||||
'@wangeditor-next/video-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@4.0.6)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
'@wangeditor-next/video-module@2.0.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor-next/core@1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3))(dom7@4.0.6)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)':
|
||||
dependencies:
|
||||
'@uppy/core': 2.3.4
|
||||
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.12)(slate@0.72.8)(snabbdom@3.6.3)
|
||||
'@wangeditor-next/core': 1.8.0(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.11)(slate@0.123.0)(snabbdom@3.6.3)
|
||||
dom7: 4.0.6
|
||||
nanoid: 5.1.11
|
||||
slate: 0.123.0
|
||||
@@ -3064,22 +3065,22 @@ snapshots:
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
avue-plugin-ueditor@1.0.4(axios@1.16.0)(vue@3.5.34):
|
||||
avue-plugin-ueditor@1.0.4(axios@1.16.0(debug@4.4.3(supports-color@7.2.0)))(vue@3.5.34):
|
||||
dependencies:
|
||||
'@wangeditor/editor': 5.1.23
|
||||
'@wangeditor/editor-for-vue': 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.34)
|
||||
axios: 1.16.0
|
||||
axios: 1.16.0(debug@4.4.3(supports-color@7.2.0))
|
||||
vue: 3.5.34
|
||||
|
||||
axios@0.21.4:
|
||||
axios@0.21.4(debug@4.4.3(supports-color@7.2.0)):
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0
|
||||
follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0))
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
axios@1.16.0:
|
||||
axios@1.16.0(debug@4.4.3(supports-color@7.2.0)):
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0
|
||||
follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0))
|
||||
form-data: 4.0.5
|
||||
proxy-from-env: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
@@ -3247,9 +3248,11 @@ snapshots:
|
||||
|
||||
dayjs@1.11.20: {}
|
||||
|
||||
debug@4.4.3:
|
||||
debug@4.4.3(supports-color@7.2.0):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 7.2.0
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
@@ -3457,7 +3460,9 @@ snapshots:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
follow-redirects@1.16.0: {}
|
||||
follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)):
|
||||
optionalDependencies:
|
||||
debug: 4.4.3(supports-color@7.2.0)
|
||||
|
||||
form-data@4.0.5:
|
||||
dependencies:
|
||||
@@ -3546,7 +3551,7 @@ snapshots:
|
||||
|
||||
heic2any@0.0.4: {}
|
||||
|
||||
highlight.js@11.11.1: {}
|
||||
highlight.js@11.12.0: {}
|
||||
|
||||
hls.js@1.6.16: {}
|
||||
|
||||
@@ -3605,9 +3610,9 @@ snapshots:
|
||||
|
||||
isarray@1.0.0: {}
|
||||
|
||||
js-base64@3.7.8: {}
|
||||
js-base64@3.9.4: {}
|
||||
|
||||
js-cookie@3.0.5: {}
|
||||
js-cookie@3.0.8: {}
|
||||
|
||||
js-md5@0.7.3: {}
|
||||
|
||||
@@ -4152,10 +4157,10 @@ snapshots:
|
||||
|
||||
uuid@8.3.2: {}
|
||||
|
||||
vite-plugin-compression@0.5.1(vite@5.4.21(sass@1.99.0)(terser@5.46.2)):
|
||||
vite-plugin-compression@0.5.1(supports-color@7.2.0)(vite@5.4.21(sass@1.99.0)(terser@5.46.2)):
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.4.3
|
||||
debug: 4.4.3(supports-color@7.2.0)
|
||||
fs-extra: 10.1.0
|
||||
vite: 5.4.21(sass@1.99.0)(terser@5.46.2)
|
||||
transitivePeerDependencies:
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
packages:
|
||||
- '.'
|
||||
|
||||
allowBuilds:
|
||||
'@parcel/watcher': set this to true or false
|
||||
es5-ext: set this to true or false
|
||||
esbuild: set this to true or false
|
||||
|
||||
patchedDependencies:
|
||||
'@smallwei/avue': patches/@smallwei__avue.patch
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* 首页看板 ECharts 通用配置生成器
|
||||
* 所有数据均为 Mock,对接后端统计接口后由各板块组件传入真实数据
|
||||
*/
|
||||
|
||||
/* 横向渐变色(排行条形图) */
|
||||
function hGrad(color, from = 0.9, to = 0.55) {
|
||||
return {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 1,
|
||||
y2: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: color },
|
||||
{ offset: 1, color: hexFade(color, to) },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/* 十六进制颜色加透明度 */
|
||||
function hexFade(hex, alpha) {
|
||||
const h = hex.replace('#', '');
|
||||
const r = parseInt(h.slice(0, 2), 16);
|
||||
const g = parseInt(h.slice(2, 4), 16);
|
||||
const b = parseInt(h.slice(4, 6), 16);
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
|
||||
/* 千分位 */
|
||||
export function fmt(n) {
|
||||
if (n === null || n === undefined || n === '') return '-';
|
||||
const num = Number(n);
|
||||
if (Number.isNaN(num)) return n;
|
||||
return num.toLocaleString('zh-CN', { maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
/**
|
||||
* 排行条形图(左名称 + 中条形 + 右数值/涨跌)
|
||||
* items: [{ name, value, change? }] change 为百分比涨跌(number,可空)
|
||||
*/
|
||||
export function rankOption(items, { color = '#4a90e2', unit = '', nameWidth = 100 } = {}) {
|
||||
const sorted = [...items].sort((a, b) => a.value - b.value);
|
||||
const hasChange = sorted.some(i => typeof i.change === 'number');
|
||||
const rich = {
|
||||
v: { color: '#303133', fontWeight: 600, fontSize: 12, align: 'right' },
|
||||
u: { color: '#909399', fontSize: 11, align: 'right' },
|
||||
up: { color: '#f04747', fontSize: 11, align: 'right' },
|
||||
down: { color: '#2ba471', fontSize: 11, align: 'right' },
|
||||
};
|
||||
return {
|
||||
grid: { left: 4, right: hasChange ? 96 : 58, top: 6, bottom: 6, containLabel: true },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
formatter: p => {
|
||||
const d = p[0].data;
|
||||
let s = `${p[0].name}<br/>${d.value}${unit}`;
|
||||
if (typeof d.change === 'number') {
|
||||
s += `(环比 ${d.change >= 0 ? '+' : ''}${d.change}%)`;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
},
|
||||
xAxis: { type: 'value', show: false },
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: sorted.map(i => i.name),
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: false },
|
||||
axisLabel: { color: '#606266', fontSize: 12, width: nameWidth, overflow: 'truncate' },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 10,
|
||||
showBackground: true,
|
||||
backgroundStyle: { color: '#f0f2f5', borderRadius: [0, 5, 5, 0] },
|
||||
itemStyle: { color: hGrad(color), borderRadius: [0, 5, 5, 0] },
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
distance: 8,
|
||||
formatter: p => {
|
||||
const d = p.data;
|
||||
let s = `{v|${fmt(d.value)}${unit}}`;
|
||||
if (typeof d.change === 'number') {
|
||||
const cls = d.change >= 0 ? 'up' : 'down';
|
||||
const txt = `${d.change >= 0 ? '↑ +' : '↓ '}${d.change}%`;
|
||||
s += ` {${cls}|${txt}}`;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
rich,
|
||||
},
|
||||
data: sorted.map(i => ({ value: i.value, change: i.change })),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 盈亏双向对比条形图(正=红,负=绿)
|
||||
* items: [{ name, value }]
|
||||
*/
|
||||
export function diffOption(items) {
|
||||
const sorted = [...items].sort((a, b) => b.value - a.value);
|
||||
return {
|
||||
grid: { left: 4, right: 60, top: 6, bottom: 6, containLabel: true },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
formatter: p => `${p[0].name}<br/>${p[0].value > 0 ? '盈利' : '亏损'} ${fmt(Math.abs(p[0].value))} 万元`,
|
||||
},
|
||||
xAxis: { type: 'value', show: false },
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: sorted.map(i => i.name),
|
||||
axisTick: { show: false },
|
||||
axisLine: { show: false },
|
||||
axisLabel: { color: '#606266', fontSize: 12, width: 100, overflow: 'truncate' },
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
barWidth: 10,
|
||||
itemStyle: {
|
||||
borderRadius: 5,
|
||||
color: p => (p.value >= 0 ? '#e04b4b' : '#3aa15c'),
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
distance: 8,
|
||||
formatter: p => `${p.value > 0 ? '+' : ''}${fmt(p.value)}`,
|
||||
color: '#303133',
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
},
|
||||
data: sorted.map(i => i.value),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 环形占比图
|
||||
* items: [{ name, value }]
|
||||
*/
|
||||
export function donutOption(items, { centerTitle = '', centerSub = '' } = {}) {
|
||||
const palette = ['#4a90e2', '#2bb3a3', '#e0b14c', '#8b5cf6', '#e04b4b', '#3aa15c', '#9aa5b5', '#5aa9ff'];
|
||||
return {
|
||||
color: palette,
|
||||
tooltip: { trigger: 'item', formatter: p => `${p.name}<br/>${fmt(p.value)}(${p.percent}%)` },
|
||||
legend: { show: false },
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['52%', '74%'],
|
||||
center: ['50%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
itemStyle: { borderColor: '#fff', borderWidth: 2 },
|
||||
label: {
|
||||
show: true,
|
||||
color: '#606266',
|
||||
fontSize: 11,
|
||||
formatter: p => `${p.name}\n${p.percent}%`,
|
||||
},
|
||||
labelLine: { length: 10, length2: 8, lineStyle: { color: '#c0c4cc' } },
|
||||
data: items,
|
||||
},
|
||||
],
|
||||
graphic: centerTitle
|
||||
? [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '44%',
|
||||
style: { text: centerTitle, fontSize: 18, fontWeight: 700, fill: '#303133' },
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '56%',
|
||||
style: { text: centerSub, fontSize: 11, fill: '#909399' },
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 分组柱状图(收入/费用对比)
|
||||
* categories: string[];series: [{ name, data, color }]
|
||||
*/
|
||||
export function groupBarOption(categories, series) {
|
||||
return {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { top: 0, right: 0, itemWidth: 14, itemHeight: 8, textStyle: { color: '#606266', fontSize: 12 } },
|
||||
grid: { left: 8, right: 16, top: 36, bottom: 4, containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: categories,
|
||||
axisTick: { show: false },
|
||||
axisLine: { lineStyle: { color: '#e4e7ed' } },
|
||||
axisLabel: { color: '#909399', fontSize: 11, interval: 0, rotate: categories.length > 8 ? 30 : 0 },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: { lineStyle: { color: '#f0f2f5' } },
|
||||
axisLabel: { color: '#909399', fontSize: 11 },
|
||||
},
|
||||
series: series.map(s => ({
|
||||
name: s.name,
|
||||
type: 'bar',
|
||||
barWidth: 14,
|
||||
itemStyle: { color: s.color, borderRadius: [3, 3, 0, 0] },
|
||||
data: s.data,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 面积折线图
|
||||
* categories: string[];series: [{ name, data, color }]
|
||||
*/
|
||||
export function lineAreaOption(categories, series, { rotate = false } = {}) {
|
||||
return {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: series.length > 1
|
||||
? { top: 0, right: 0, itemWidth: 16, itemHeight: 8, textStyle: { color: '#606266', fontSize: 12 } }
|
||||
: { show: false },
|
||||
grid: { left: 8, right: 20, top: series.length > 1 ? 36 : 20, bottom: 4, containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: categories,
|
||||
axisTick: { show: false },
|
||||
axisLine: { lineStyle: { color: '#e4e7ed' } },
|
||||
axisLabel: { color: '#909399', fontSize: 11, rotate: rotate ? 30 : 0 },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: { lineStyle: { color: '#f0f2f5' } },
|
||||
axisLabel: { color: '#909399', fontSize: 11 },
|
||||
},
|
||||
series: series.map(s => ({
|
||||
name: s.name,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 5,
|
||||
lineStyle: { color: s.color, width: 2 },
|
||||
itemStyle: { color: s.color },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: hexFade(s.color, 0.18) },
|
||||
{ offset: 1, color: hexFade(s.color, 0.02) },
|
||||
],
|
||||
},
|
||||
},
|
||||
data: s.data,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 堆叠柱状图
|
||||
* categories: string[];series: [{ name, data, color }]
|
||||
*/
|
||||
export function stackBarOption(categories, series) {
|
||||
return {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { top: 0, right: 0, itemWidth: 14, itemHeight: 8, textStyle: { color: '#606266', fontSize: 12 } },
|
||||
grid: { left: 8, right: 16, top: 36, bottom: 4, containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: categories,
|
||||
axisTick: { show: false },
|
||||
axisLine: { lineStyle: { color: '#e4e7ed' } },
|
||||
axisLabel: { color: '#909399', fontSize: 11, interval: 0, rotate: categories.length > 8 ? 30 : 0 },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: { lineStyle: { color: '#f0f2f5' } },
|
||||
axisLabel: { color: '#909399', fontSize: 11 },
|
||||
},
|
||||
series: series.map(s => ({
|
||||
name: s.name,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
barWidth: 22,
|
||||
itemStyle: { color: s.color },
|
||||
data: s.data,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="wel-card rank-panel">
|
||||
<div class="panel-title">
|
||||
{{ title }}
|
||||
<span v-if="unit" class="panel-title__unit">{{ unit }}</span>
|
||||
</div>
|
||||
<wel-chart
|
||||
v-if="type === 'rank'"
|
||||
:option="rankOpt"
|
||||
:height="height"
|
||||
/>
|
||||
<wel-chart v-else :option="diffOpt" :height="height" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WelChart from './wel-chart.vue';
|
||||
import { rankOption, diffOption } from '../chart-options';
|
||||
|
||||
export default {
|
||||
name: 'rank-panel',
|
||||
components: { WelChart },
|
||||
props: {
|
||||
title: { type: String, required: true },
|
||||
unit: { type: String, default: '' },
|
||||
/* rank = 普通排行;diff = 盈亏双向对比 */
|
||||
type: { type: String, default: 'rank' },
|
||||
color: { type: String, default: '#4a90e2' },
|
||||
items: { type: Array, required: true },
|
||||
height: { type: String, default: '300px' },
|
||||
nameWidth: { type: Number, default: 100 },
|
||||
},
|
||||
computed: {
|
||||
rankOpt() {
|
||||
return rankOption(this.items, { color: this.color, unit: this.unit, nameWidth: this.nameWidth });
|
||||
},
|
||||
diffOpt() {
|
||||
return diffOption(this.items);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">业务单元情况</span>
|
||||
<wel-filters condition-label="业务单元" />
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="p in panels" :key="p.title" :xs="24" :md="12">
|
||||
<rank-panel
|
||||
:title="p.title"
|
||||
:unit="p.unit"
|
||||
:type="p.type"
|
||||
:color="p.color"
|
||||
:items="p.items"
|
||||
:height="p.height"
|
||||
:name-width="p.nameWidth"
|
||||
class="business-panel"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RankPanel from './rank-panel.vue';
|
||||
import WelFilters from './wel-filters.vue';
|
||||
|
||||
export default {
|
||||
name: 'section-business',
|
||||
components: { RankPanel, WelFilters },
|
||||
data() {
|
||||
return {
|
||||
// Mock:业务单元排行数据,对接统计接口后替换
|
||||
panels: [
|
||||
{
|
||||
title: '营业收入排行',
|
||||
unit: '万元',
|
||||
type: 'rank',
|
||||
color: '#4a90e2',
|
||||
height: '320px',
|
||||
items: [
|
||||
{ name: '广西物产集团', value: 5218.7, change: 12.3 },
|
||||
{ name: '南宁港务物流', value: 4183.4, change: 8.6 },
|
||||
{ name: '柳州钢铁物流', value: 3603.9, change: -2.4 },
|
||||
{ name: '北部湾港务', value: 3131.7, change: 5.1 },
|
||||
{ name: '桂林粮油储运', value: 2750.3, change: 9.8 },
|
||||
{ name: '玉林建材集团', value: 2455.5, change: -1.2 },
|
||||
{ name: '钦州石化物流', value: 2088.3, change: 6.5 },
|
||||
{ name: '贵港航运公司', value: 1823.5, change: 3.9 },
|
||||
{ name: '百色矿产运输', value: 1588.4, change: -4.7 },
|
||||
{ name: '梧州商贸物流', value: 1214.8, change: 2.2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '运输情况排行',
|
||||
unit: '单',
|
||||
type: 'rank',
|
||||
color: '#2bb3a3',
|
||||
height: '320px',
|
||||
items: [
|
||||
{ name: '广西物产集团', value: 238.2, change: 9.4 },
|
||||
{ name: '南宁港务物流', value: 196.3, change: 6.7 },
|
||||
{ name: '柳州钢铁物流', value: 168.1, change: 3.2 },
|
||||
{ name: '北部湾港务', value: 154.7, change: -2.8 },
|
||||
{ name: '桂林粮油储运', value: 139.2, change: 7.5 },
|
||||
{ name: '玉林建材集团', value: 128.6, change: 4.1 },
|
||||
{ name: '钦州石化物流', value: 118.3, change: -1.6 },
|
||||
{ name: '贵港航运公司', value: 106.2, change: 5.3 },
|
||||
{ name: '百色矿产运输', value: 98.8, change: 2.9 },
|
||||
{ name: '梧州商贸物流', value: 89.6, change: -3.4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '各部门毛利占比',
|
||||
unit: '%',
|
||||
type: 'rank',
|
||||
color: '#c9a227',
|
||||
height: '320px',
|
||||
items: [
|
||||
{ name: '公路运输一部', value: 30.7 },
|
||||
{ name: '公路运输二部', value: 28.2 },
|
||||
{ name: '铁路物流部', value: 23.2 },
|
||||
{ name: '水运事业部', value: 19.6 },
|
||||
{ name: '多式联运部', value: 15.2 },
|
||||
{ name: '仓储配送部', value: 13.1 },
|
||||
{ name: '冷链事业部', value: 10.4 },
|
||||
{ name: '大件运输部', value: 8.6 },
|
||||
{ name: '危险品运输部', value: 5.2 },
|
||||
{ name: '综合业务部', value: 2.8 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '项目盈亏对比',
|
||||
unit: '万元',
|
||||
type: 'diff',
|
||||
color: '#e04b4b',
|
||||
height: '320px',
|
||||
items: [
|
||||
{ name: '铝锭外运项目', value: 156.5 },
|
||||
{ name: '冷链城配项目', value: 134.8 },
|
||||
{ name: '钢材南下项目', value: 118.2 },
|
||||
{ name: '粮食储备调运', value: 96.1 },
|
||||
{ name: '矿石短驳项目', value: 82.6 },
|
||||
{ name: '集装箱班列', value: 74.3 },
|
||||
{ name: '化工原料运输', value: 61.2 },
|
||||
{ name: '建材集散运输', value: 48.9 },
|
||||
{ name: '汽车零部件配送', value: 21.5 },
|
||||
{ name: '设备大件运输', value: 16.2 },
|
||||
{ name: '生鲜冷链专线', value: -8.3 },
|
||||
{ name: '危化品试点线', value: -25.4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '运单量排行',
|
||||
unit: '单',
|
||||
type: 'rank',
|
||||
color: '#4a90e2',
|
||||
height: '320px',
|
||||
items: [
|
||||
{ name: '南宁分部', value: 320, change: 6.3 },
|
||||
{ name: '柳州分部', value: 273, change: 4.8 },
|
||||
{ name: '桂林分部', value: 241, change: -2.1 },
|
||||
{ name: '玉林分部', value: 215, change: 8.2 },
|
||||
{ name: '钦州港分部', value: 203, change: 3.6 },
|
||||
{ name: '贵港分部', value: 187, change: -1.4 },
|
||||
{ name: '百色分部', value: 158, change: 5.7 },
|
||||
{ name: '梧州分部', value: 132, change: 2.3 },
|
||||
{ name: '北海分部', value: 108, change: -3.9 },
|
||||
{ name: '河池分部', value: 86, change: 1.6 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '运输收入排行',
|
||||
unit: '万元',
|
||||
type: 'rank',
|
||||
color: '#8b5cf6',
|
||||
height: '320px',
|
||||
items: [
|
||||
{ name: '公路整车', value: 1863.2, change: 7.1 },
|
||||
{ name: '铁路整车', value: 1421.8, change: 4.5 },
|
||||
{ name: '水运集装箱', value: 1182.3, change: 9.2 },
|
||||
{ name: '公路零担', value: 968.5, change: 2.8 },
|
||||
{ name: '多式联运', value: 812.4, change: 11.6 },
|
||||
{ name: '航空货运', value: 655.7, change: -1.8 },
|
||||
{ name: '冷链专线', value: 542.1, change: 6.4 },
|
||||
{ name: '大件运输', value: 388.9, change: 3.1 },
|
||||
{ name: '危险品运输', value: 296.3, change: -2.5 },
|
||||
{ name: '仓储增值服务', value: 184.6, change: 8.8 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.business-panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">财务情况</span>
|
||||
<wel-filters condition-label="核算口径" />
|
||||
</div>
|
||||
|
||||
<div class="wel-card finance-panel">
|
||||
<div class="panel-title">
|
||||
营业收支对比
|
||||
<span class="panel-title__unit">万元</span>
|
||||
</div>
|
||||
<wel-chart :option="barOpt" height="300px" />
|
||||
</div>
|
||||
|
||||
<div class="wel-card finance-panel">
|
||||
<div class="panel-title">
|
||||
主营业务收入/费用趋势
|
||||
<span class="panel-title__unit">万元</span>
|
||||
</div>
|
||||
<wel-chart :option="lineOpt" height="280px" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WelChart from './wel-chart.vue';
|
||||
import WelFilters from './wel-filters.vue';
|
||||
import { groupBarOption, lineAreaOption } from '../chart-options';
|
||||
|
||||
export default {
|
||||
name: 'section-finance',
|
||||
components: { WelChart, WelFilters },
|
||||
data() {
|
||||
const months = Array.from({ length: 12 }, (_, i) => `${i + 1}月`);
|
||||
return {
|
||||
months,
|
||||
// Mock:财务收支数据,对接统计接口后替换
|
||||
incomeData: [2860, 1520, 1180, 1150, 980, 920, 760, 720, 690, 520, 380, 260],
|
||||
costData: [2410, 1330, 1010, 980, 850, 800, 660, 630, 610, 470, 340, 230],
|
||||
trendIncome: [1780, 1850, 1810, 1880, 1920, 1950, 1720, 1650, 1830, 1900, 1980, 2060],
|
||||
trendCost: [1520, 1560, 1540, 1590, 1610, 1620, 1450, 1400, 1530, 1580, 1640, 1700],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
barOpt() {
|
||||
return groupBarOption(this.months, [
|
||||
{ name: '收入', data: this.incomeData, color: '#4a90e2' },
|
||||
{ name: '费用', data: this.costData, color: '#d9dce3' },
|
||||
]);
|
||||
},
|
||||
lineOpt() {
|
||||
return lineAreaOption(this.months, [
|
||||
{ name: '主营业务收入', data: this.trendIncome, color: '#4a90e2' },
|
||||
{ name: '主营业务费用', data: this.trendCost, color: '#2bb3a3' },
|
||||
]);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.finance-panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">车队与线路</span>
|
||||
<wel-filters condition-label="运输方式" />
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :md="12">
|
||||
<rank-panel
|
||||
title="下游车队 TOP10"
|
||||
unit="单"
|
||||
color="#1a9e94"
|
||||
:items="fleetItems"
|
||||
height="320px"
|
||||
:name-width="120"
|
||||
class="fleet-panel"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<rank-panel
|
||||
title="运输线路 TOP5"
|
||||
unit="单"
|
||||
color="#c9a227"
|
||||
:items="routeItems"
|
||||
height="320px"
|
||||
:name-width="120"
|
||||
class="fleet-panel"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RankPanel from './rank-panel.vue';
|
||||
import WelFilters from './wel-filters.vue';
|
||||
|
||||
export default {
|
||||
name: 'section-fleet',
|
||||
components: { RankPanel, WelFilters },
|
||||
data() {
|
||||
return {
|
||||
// Mock:车队/线路排行数据,对接统计接口后替换
|
||||
fleetItems: [
|
||||
{ name: '顺达车队', value: 186.5 },
|
||||
{ name: '穗联物流车队', value: 158.2 },
|
||||
{ name: '宏运车队', value: 132.6 },
|
||||
{ name: '桂海运输车队', value: 118.4 },
|
||||
{ name: '南北快运车队', value: 96.7 },
|
||||
{ name: '恒通货运车队', value: 85.3 },
|
||||
{ name: '运达车队', value: 74.2 },
|
||||
{ name: '捷安物流车队', value: 62.8 },
|
||||
{ name: '广通运输车队', value: 54.6 },
|
||||
{ name: '长顺车队', value: 43.1 },
|
||||
],
|
||||
routeItems: [
|
||||
{ name: '南宁 → 柳州', value: 342.5 },
|
||||
{ name: '钦州港 → 南宁', value: 285.6 },
|
||||
{ name: '贵港 → 广州', value: 226.3 },
|
||||
{ name: '桂林 → 南宁', value: 168.2 },
|
||||
{ name: '玉林 → 深圳', value: 142.6 },
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.fleet-panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">核心经营指标</span>
|
||||
<wel-filters />
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="card in cards" :key="card.label" :xs="24" :md="8">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-card__top">
|
||||
<span class="kpi-card__label">{{ card.label }}</span>
|
||||
<span
|
||||
class="kpi-card__badge"
|
||||
:class="card.trend >= 0 ? 'is-up' : 'is-down'"
|
||||
>
|
||||
环比 {{ card.trend >= 0 ? '+' : '' }}{{ card.trend }}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="kpi-card__value">
|
||||
{{ card.value }}<span class="kpi-card__unit">{{ card.unit }}</span>
|
||||
</div>
|
||||
<ul class="kpi-card__subs">
|
||||
<li v-for="s in card.subs" :key="s.name">
|
||||
<span class="kpi-card__sub-name">{{ s.name }}</span>
|
||||
<span class="kpi-card__sub-value">{{ s.value }}</span>
|
||||
<span
|
||||
v-if="typeof s.change === 'number'"
|
||||
class="kpi-card__sub-change"
|
||||
:class="s.change >= 0 ? 'is-up' : 'is-down'"
|
||||
>
|
||||
{{ s.change >= 0 ? '↑' : '↓' }} {{ Math.abs(s.change) }}%
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WelFilters from './wel-filters.vue';
|
||||
|
||||
export default {
|
||||
name: 'section-kpi',
|
||||
components: { WelFilters },
|
||||
data() {
|
||||
return {
|
||||
// Mock:营业额/运单量/利润等核心指标,对接统计接口后替换
|
||||
cards: [
|
||||
{
|
||||
label: '营业额',
|
||||
value: '58,814.0',
|
||||
unit: '万元',
|
||||
trend: 12.6,
|
||||
subs: [
|
||||
{ name: '毛利率', value: '23.5%', change: 1.2 },
|
||||
{ name: '运输成本', value: '45,031 万元', change: -0.8 },
|
||||
{ name: '回款金额', value: '39,640 万元', change: 3.4 },
|
||||
{ name: '应收账款', value: '19,174 万元', change: -2.1 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '运单量',
|
||||
value: '1,497.1',
|
||||
unit: '单',
|
||||
trend: 8.4,
|
||||
subs: [
|
||||
{ name: '已完成', value: '1,205 单', change: 6.2 },
|
||||
{ name: '在途', value: '135 单', change: 12.0 },
|
||||
{ name: '待发运', value: '92 单', change: -4.5 },
|
||||
{ name: '异常单', value: '65 单', change: 1.8 },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '运输利润',
|
||||
value: '2,058.2',
|
||||
unit: '万元',
|
||||
trend: 15.7,
|
||||
subs: [
|
||||
{ name: '利润率', value: '13.7%', change: 1.5 },
|
||||
{ name: '单均利润', value: '1.38 万元', change: 5.6 },
|
||||
{ name: '燃油成本', value: '8,931 万元', change: -3.2 },
|
||||
{ name: '过路费用', value: '4,208 万元', change: 0.9 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.kpi-card {
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.kpi-card__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.kpi-card__label {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.kpi-card__badge {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
|
||||
&.is-up {
|
||||
color: #f04747;
|
||||
background: #fdecec;
|
||||
}
|
||||
|
||||
&.is-down {
|
||||
color: #2ba471;
|
||||
background: #e8f7ef;
|
||||
}
|
||||
}
|
||||
|
||||
.kpi-card__value {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
margin: 8px 0 12px;
|
||||
}
|
||||
|
||||
.kpi-card__unit {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.kpi-card__subs {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 10px 0 0;
|
||||
border-top: 1px solid #f0f2f5;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.kpi-card__sub-name {
|
||||
color: #909399;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.kpi-card__sub-value {
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.kpi-card__sub-change {
|
||||
width: 56px;
|
||||
text-align: right;
|
||||
|
||||
&.is-up {
|
||||
color: #f04747;
|
||||
}
|
||||
|
||||
&.is-down {
|
||||
color: #2ba471;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">项目情况</span>
|
||||
<wel-filters condition-label="项目状态" />
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :md="12">
|
||||
<rank-panel
|
||||
title="项目收入 TOP5"
|
||||
unit="万元"
|
||||
color="#8b5cf6"
|
||||
:items="incomeItems"
|
||||
height="280px"
|
||||
:name-width="180"
|
||||
class="project-panel"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<rank-panel
|
||||
title="项目运量 TOP5"
|
||||
unit="万吨"
|
||||
color="#2bb3a3"
|
||||
:items="volumeItems"
|
||||
height="280px"
|
||||
:name-width="180"
|
||||
class="project-panel"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RankPanel from './rank-panel.vue';
|
||||
import WelFilters from './wel-filters.vue';
|
||||
|
||||
export default {
|
||||
name: 'section-project',
|
||||
components: { RankPanel, WelFilters },
|
||||
data() {
|
||||
return {
|
||||
// Mock:项目排行数据,对接统计接口后替换
|
||||
incomeItems: [
|
||||
{ name: '广西华谊氯碱化工物流项目', value: 1285.6 },
|
||||
{ name: '柳钢集团钢材外运项目(二期)', value: 982.4 },
|
||||
{ name: '南宁轨道交通建材配送项目', value: 756.3 },
|
||||
{ name: '北部湾港集装箱短驳项目', value: 621.7 },
|
||||
{ name: '中粮集团粮食储备调运项目', value: 468.2 },
|
||||
],
|
||||
volumeItems: [
|
||||
{ name: '广西华谊氯碱化工物流项目', value: 32.5 },
|
||||
{ name: '柳钢集团钢材外运项目(二期)', value: 26.8 },
|
||||
{ name: '南宁轨道交通建材配送项目', value: 21.4 },
|
||||
{ name: '北部湾港集装箱短驳项目', value: 18.6 },
|
||||
{ name: '中粮集团粮食储备调运项目', value: 15.2 },
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.project-panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">运输结构</span>
|
||||
<wel-filters condition-label="业务类型" />
|
||||
</div>
|
||||
|
||||
<!-- 小指标卡 -->
|
||||
<el-row :gutter="16" class="structure-cards">
|
||||
<el-col v-for="c in miniCards" :key="c.label" :xs="12" :md="6">
|
||||
<div class="mini-card structure-cards__item">
|
||||
<div class="mini-card__label">{{ c.label }}</div>
|
||||
<div class="mini-card__value">
|
||||
{{ c.value }}<span class="mini-card__unit">{{ c.unit }}</span>
|
||||
</div>
|
||||
<div v-if="typeof c.change === 'number'" class="mini-card__trend" :class="c.change >= 0 ? 'is-up' : 'is-down'">
|
||||
环比 {{ c.change >= 0 ? '+' : '' }}{{ c.change }}%
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 三个占比环形图 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col v-for="d in donuts" :key="d.title" :xs="24" :md="8">
|
||||
<div class="wel-card donut-panel">
|
||||
<div class="panel-title">
|
||||
{{ d.title }}
|
||||
<span class="panel-title__unit">{{ d.unit }}</span>
|
||||
</div>
|
||||
<wel-chart :option="donutOpt(d)" height="230px" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WelChart from './wel-chart.vue';
|
||||
import WelFilters from './wel-filters.vue';
|
||||
import { donutOption } from '../chart-options';
|
||||
|
||||
export default {
|
||||
name: 'section-structure',
|
||||
components: { WelChart, WelFilters },
|
||||
data() {
|
||||
return {
|
||||
// Mock:运输结构统计,对接统计接口后替换
|
||||
miniCards: [
|
||||
{ label: '总运单量', value: '1,391', unit: '单', change: 30 },
|
||||
{ label: '在途运单', value: '487', unit: '单', change: 12.5 },
|
||||
{ label: '待发运', value: '135', unit: '单', change: -6.3 },
|
||||
{ label: '总运量', value: '15.2', unit: '万吨', change: 8.1 },
|
||||
],
|
||||
donuts: [
|
||||
{
|
||||
title: '运输方式占比',
|
||||
unit: '按运单量',
|
||||
centerTitle: '1,391',
|
||||
centerSub: '总运单(单)',
|
||||
items: [
|
||||
{ name: '公路', value: 620 },
|
||||
{ name: '铁路', value: 335 },
|
||||
{ name: '水运', value: 215 },
|
||||
{ name: '航空', value: 121 },
|
||||
{ name: '多式联运', value: 100 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '货物类型占比',
|
||||
unit: '按运量',
|
||||
centerTitle: '15.2',
|
||||
centerSub: '总运量(万吨)',
|
||||
items: [
|
||||
{ name: '普货', value: 6.4 },
|
||||
{ name: '冷链', value: 3.2 },
|
||||
{ name: '大件', value: 2.6 },
|
||||
{ name: '危险品', value: 1.8 },
|
||||
{ name: '其他', value: 1.2 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '客户类型占比',
|
||||
unit: '按运单量',
|
||||
centerTitle: '326',
|
||||
centerSub: '合作客户(家)',
|
||||
items: [
|
||||
{ name: '合同客户', value: 810 },
|
||||
{ name: '散单客户', value: 372 },
|
||||
{ name: '平台客户', value: 209 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
donutOpt(d) {
|
||||
return donutOption(d.items, { centerTitle: d.centerTitle, centerSub: d.centerSub });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.structure-cards__item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.donut-panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :md="12">
|
||||
<div class="wel-card summary-panel">
|
||||
<div class="panel-title">
|
||||
车辆耗时占比
|
||||
<span class="panel-title__unit">按小时</span>
|
||||
</div>
|
||||
<wel-chart :option="donutOpt" height="260px" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<div class="wel-card summary-panel">
|
||||
<div class="panel-title">
|
||||
单据完成趋势
|
||||
<span class="panel-title__unit">按周</span>
|
||||
</div>
|
||||
<wel-chart :option="lineOpt" height="260px" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WelChart from './wel-chart.vue';
|
||||
import { donutOption, lineAreaOption } from '../chart-options';
|
||||
|
||||
export default {
|
||||
name: 'section-summary',
|
||||
components: { WelChart },
|
||||
data() {
|
||||
return {
|
||||
// Mock:车辆耗时与单据完成数据,对接统计接口后替换
|
||||
timeItems: [
|
||||
{ name: '在途运输', value: 86.2 },
|
||||
{ name: '装货等待', value: 32.5 },
|
||||
{ name: '卸货作业', value: 28.4 },
|
||||
{ name: '返程空驶', value: 21.6 },
|
||||
{ name: '场站等待', value: 18.3 },
|
||||
{ name: '维修保养', value: 9.8 },
|
||||
],
|
||||
weeks: ['1周', '2周', '3周', '4周', '5周', '6周', '7周'],
|
||||
doneData: [520, 545, 508, 532, 560, 548, 572],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
donutOpt() {
|
||||
return donutOption(this.timeItems, { centerTitle: '196.8', centerSub: '总耗时(小时)' });
|
||||
},
|
||||
lineOpt() {
|
||||
return lineAreaOption(this.weeks, [
|
||||
{ name: '完成单据', data: this.doneData, color: '#1a9e94' },
|
||||
]);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.summary-panel {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<section class="wel-sec">
|
||||
<div class="wel-sec__head">
|
||||
<span class="wel-sec__title">车务管理</span>
|
||||
<wel-filters condition-label="车辆类型" />
|
||||
</div>
|
||||
|
||||
<!-- 车务小指标卡 -->
|
||||
<el-row :gutter="16" class="vehicle-cards">
|
||||
<el-col v-for="c in miniCards" :key="c.label" :xs="12" :md="6">
|
||||
<div class="mini-card vehicle-cards__item">
|
||||
<div class="mini-card__label">{{ c.label }}</div>
|
||||
<div class="mini-card__value">
|
||||
{{ c.value }}<span class="mini-card__unit">{{ c.unit }}</span>
|
||||
</div>
|
||||
<div v-if="typeof c.change === 'number'" class="mini-card__trend" :class="c.change >= 0 ? 'is-up' : 'is-down'">
|
||||
环比 {{ c.change >= 0 ? '+' : '' }}{{ c.change }}%
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 车辆耗费构成堆叠柱状图 -->
|
||||
<div class="wel-card">
|
||||
<div class="panel-title">
|
||||
车辆耗费构成
|
||||
<span class="panel-title__unit">万元</span>
|
||||
</div>
|
||||
<wel-chart :option="stackOpt" height="320px" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WelChart from './wel-chart.vue';
|
||||
import WelFilters from './wel-filters.vue';
|
||||
import { stackBarOption } from '../chart-options';
|
||||
|
||||
export default {
|
||||
name: 'section-vehicle',
|
||||
components: { WelChart, WelFilters },
|
||||
data() {
|
||||
const months = Array.from({ length: 12 }, (_, i) => `${i + 1}月`);
|
||||
return {
|
||||
months,
|
||||
// Mock:车务统计与耗费数据,对接统计接口后替换
|
||||
miniCards: [
|
||||
{ label: '可用车辆', value: 47, unit: '辆', change: 4.2 },
|
||||
{ label: '维修保养中', value: 7, unit: '辆', change: -8.6 },
|
||||
{ label: '年检临期', value: 9, unit: '辆', change: 12.5 },
|
||||
{ label: '违章未处理', value: 12, unit: '起', change: -2.1 },
|
||||
],
|
||||
fuelData: [68, 52, 41, 36, 32, 38, 24, 46, 38, 28, 12, 14],
|
||||
repairData: [22, 25, 18, 20, 16, 18, 14, 12, 15, 11, 6, 8],
|
||||
otherData: [15, 14, 12, 13, 11, 12, 9, 11, 10, 8, 5, 6],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
stackOpt() {
|
||||
return stackBarOption(this.months, [
|
||||
{ name: '油费', data: this.fuelData, color: '#e0b14c' },
|
||||
{ name: '维修保养', data: this.repairData, color: '#58b98a' },
|
||||
{ name: '其他费用', data: this.otherData, color: '#4a90e2' },
|
||||
]);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.vehicle-cards__item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div ref="chartEl" class="wel-chart" :style="{ height: height }"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
name: 'wel-chart',
|
||||
props: {
|
||||
option: { type: Object, required: true },
|
||||
height: { type: String, default: '260px' },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
resizeObserver: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
option: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.render();
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.chart = echarts.init(this.$refs.chartEl);
|
||||
this.render();
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.chart && this.chart.resize();
|
||||
});
|
||||
this.resizeObserver.observe(this.$refs.chartEl);
|
||||
},
|
||||
activated() {
|
||||
/* keep-alive 恢复时重算尺寸 */
|
||||
this.chart && this.chart.resize();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.resizeObserver && this.resizeObserver.disconnect();
|
||||
this.chart && this.chart.dispose();
|
||||
this.chart = null;
|
||||
},
|
||||
methods: {
|
||||
render() {
|
||||
this.chart && this.chart.setOption(this.option, true);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wel-chart {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="wel-filter">
|
||||
<span class="wel-filter__label">统计时间</span>
|
||||
<el-select v-model="year" size="small">
|
||||
<el-option v-for="y in yearOptions" :key="y" :label="y + '年'" :value="y" />
|
||||
</el-select>
|
||||
<el-select v-model="range" size="small" class="wel-filter__range">
|
||||
<el-option v-for="r in rangeOptions" :key="r.value" :label="r.label" :value="r.value" />
|
||||
</el-select>
|
||||
<template v-if="conditionLabel">
|
||||
<span class="wel-filter__label">{{ conditionLabel }}</span>
|
||||
<el-select v-model="condition" size="small">
|
||||
<el-option label="全部" value="all" />
|
||||
</el-select>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wel-filters',
|
||||
props: {
|
||||
conditionLabel: { type: String, default: '' },
|
||||
},
|
||||
data() {
|
||||
const now = new Date().getFullYear();
|
||||
return {
|
||||
year: now,
|
||||
yearOptions: [now, now - 1, now - 2, now - 3],
|
||||
range: 'year',
|
||||
rangeOptions: [
|
||||
{ label: '全年', value: 'year' },
|
||||
{ label: '上半年', value: 'h1' },
|
||||
{ label: '下半年', value: 'h2' },
|
||||
{ label: '自定义', value: 'custom' },
|
||||
],
|
||||
condition: 'all',
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../dashboard.scss';
|
||||
|
||||
.wel-filter__range {
|
||||
width: 110px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,96 @@
|
||||
/* 首页看板公共样式:各板块组件 scoped 引入 */
|
||||
.wel-card {
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 16px 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.wel-sec__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.wel-sec__title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.wel-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wel-filter__label {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wel-filter .el-select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.panel-title__unit {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.mini-card {
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 14px 18px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
|
||||
.mini-card__label {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.mini-card__value {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.mini-card__unit {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.mini-card__trend {
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
color: #909399;
|
||||
|
||||
&.is-up {
|
||||
color: #f04747;
|
||||
}
|
||||
|
||||
&.is-down {
|
||||
color: #2ba471;
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-767
@@ -1,788 +1,56 @@
|
||||
<template>
|
||||
<div class="wel-dashboard">
|
||||
<!-- 欢迎横幅 -->
|
||||
<div class="wel-banner">
|
||||
<div class="wel-banner__deco">
|
||||
<span class="wel-banner__circle wel-banner__circle--1"></span>
|
||||
<span class="wel-banner__circle wel-banner__circle--2"></span>
|
||||
<span class="wel-banner__circle wel-banner__circle--3"></span>
|
||||
<span class="wel-banner__dot-grid">
|
||||
<i v-for="n in 6" :key="'d'+n"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="wel-banner__left">
|
||||
<div class="wel-banner__avatar">
|
||||
<el-icon><Avatar /></el-icon>
|
||||
</div>
|
||||
<div class="wel-banner__info">
|
||||
<h2 class="wel-banner__hello">
|
||||
你好,{{ userName }}<span class="wel-banner__wave">👋</span>
|
||||
</h2>
|
||||
<p class="wel-banner__sub">
|
||||
TMS-ERP 货运管理系统 · {{ todayText }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wel-banner__right">
|
||||
<el-button type="primary" :icon="Plus" @click="goPath('/business/waybill-manage')">
|
||||
新建运单
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<section-kpi />
|
||||
<section-structure />
|
||||
<section-business />
|
||||
<section-project />
|
||||
<section-finance />
|
||||
<section-fleet />
|
||||
<section-vehicle />
|
||||
<section-summary />
|
||||
|
||||
<!-- 核心指标卡 -->
|
||||
<el-row :gutter="16" class="wel-metrics">
|
||||
<el-col v-for="m in metrics" :key="m.key" :xs="12" :sm="12" :md="6">
|
||||
<div class="metric-card" :style="{ '--accent': m.color }">
|
||||
<div class="metric-card__icon">
|
||||
<el-icon><component :is="m.icon" /></el-icon>
|
||||
</div>
|
||||
<div class="metric-card__body">
|
||||
<div class="metric-card__label">{{ m.label }}</div>
|
||||
<div class="metric-card__value">
|
||||
{{ m.value }}<span class="metric-card__unit">{{ m.unit }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="metric-card__trend"
|
||||
:class="m.trend >= 0 ? 'is-up' : 'is-down'"
|
||||
>
|
||||
<el-icon>
|
||||
<component :is="m.trend >= 0 ? 'Top' : 'Bottom'" />
|
||||
</el-icon>
|
||||
{{ Math.abs(m.trend) }}%
|
||||
<span class="metric-card__trend-sub">{{ m.sub }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 图表区 -->
|
||||
<el-row :gutter="16" class="wel-charts">
|
||||
<el-col :xs="24" :md="16">
|
||||
<div class="panel">
|
||||
<div class="panel__head">
|
||||
<span class="panel__title">运单量趋势</span>
|
||||
<span class="panel__extra">近 7 日(单)</span>
|
||||
</div>
|
||||
<div class="trend">
|
||||
<div v-for="b in trendBars" :key="b.label" class="trend__col">
|
||||
<div class="trend__val">{{ b.value }}</div>
|
||||
<div class="trend__bar">
|
||||
<div
|
||||
class="trend__fill"
|
||||
:style="{ height: b.percent + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="trend__label">{{ b.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :md="8">
|
||||
<div class="panel">
|
||||
<div class="panel__head">
|
||||
<span class="panel__title">货物类型分布</span>
|
||||
</div>
|
||||
<div class="donut-wrap">
|
||||
<div class="donut">
|
||||
<svg viewBox="0 0 140 140" class="donut__svg">
|
||||
<circle
|
||||
cx="70"
|
||||
cy="70"
|
||||
r="52"
|
||||
fill="none"
|
||||
stroke="#eef0f5"
|
||||
stroke-width="14"
|
||||
/>
|
||||
<circle
|
||||
v-for="(s, i) in donutSegments"
|
||||
:key="i"
|
||||
cx="70"
|
||||
cy="70"
|
||||
r="52"
|
||||
fill="none"
|
||||
:stroke="s.color"
|
||||
stroke-width="14"
|
||||
:stroke-dasharray="s.dash"
|
||||
:stroke-dashoffset="s.offset"
|
||||
transform="rotate(-90 70 70)"
|
||||
/>
|
||||
</svg>
|
||||
<div class="donut__center">
|
||||
<div class="donut__num">{{ donutTotal }}</div>
|
||||
<div class="donut__unit">总运单</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="donut-legend">
|
||||
<li v-for="(s, i) in donutSegments" :key="i">
|
||||
<span class="dot" :style="{ background: s.color }"></span>
|
||||
{{ s.name }}
|
||||
<em>{{ s.value }}%</em>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 下方区 -->
|
||||
<el-row :gutter="16" class="wel-bottom">
|
||||
<el-col :xs="24" :md="14">
|
||||
<div class="panel">
|
||||
<div class="panel__head">
|
||||
<span class="panel__title">运输方式占比</span>
|
||||
</div>
|
||||
<div class="bar-list">
|
||||
<div v-for="t in transportData" :key="t.name" class="bar-row">
|
||||
<div class="bar-row__name">{{ t.name }}</div>
|
||||
<div class="bar-row__track">
|
||||
<div
|
||||
class="bar-row__fill"
|
||||
:style="{ width: t.value + '%', background: t.color }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="bar-row__val">{{ t.value }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :md="10">
|
||||
<div class="panel">
|
||||
<div class="panel__head">
|
||||
<span class="panel__title">预警提醒</span>
|
||||
<el-badge :value="alerts.length" class="panel__badge" />
|
||||
</div>
|
||||
<ul class="alert-list">
|
||||
<li
|
||||
v-for="(a, i) in alerts"
|
||||
:key="i"
|
||||
class="alert-item"
|
||||
:class="'lv-' + a.level"
|
||||
>
|
||||
<span class="alert-item__dot"></span>
|
||||
<div class="alert-item__main">
|
||||
<div class="alert-item__title">{{ a.title }}</div>
|
||||
<div class="alert-item__desc">{{ a.desc }}</div>
|
||||
</div>
|
||||
<div class="alert-item__time">{{ a.time }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 快捷入口 -->
|
||||
<div class="panel wel-quick">
|
||||
<div class="panel__head">
|
||||
<span class="panel__title">快捷入口</span>
|
||||
</div>
|
||||
<div class="quick-grid">
|
||||
<div
|
||||
v-for="q in quickEntries"
|
||||
:key="q.path"
|
||||
class="quick-item"
|
||||
:style="{ '--accent': q.color }"
|
||||
@click="goPath(q.path)"
|
||||
>
|
||||
<div class="quick-item__icon">
|
||||
<el-icon><component :is="q.icon" /></el-icon>
|
||||
</div>
|
||||
<div class="quick-item__name">{{ q.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="wel-footer">
|
||||
© 2026 gxwebsoft · TMS-ERP 货运管理系统
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import SectionKpi from './components/section-kpi.vue';
|
||||
import SectionStructure from './components/section-structure.vue';
|
||||
import SectionBusiness from './components/section-business.vue';
|
||||
import SectionProject from './components/section-project.vue';
|
||||
import SectionFinance from './components/section-finance.vue';
|
||||
import SectionFleet from './components/section-fleet.vue';
|
||||
import SectionVehicle from './components/section-vehicle.vue';
|
||||
import SectionSummary from './components/section-summary.vue';
|
||||
|
||||
export default {
|
||||
name: 'welIndex',
|
||||
data() {
|
||||
return {
|
||||
Plus,
|
||||
// ===== Mock 数据:实际接入时替换为对应后端接口返回值 =====
|
||||
// 核心指标:可对接各模块统计接口(如 /blade-business/waybill/count)
|
||||
metrics: [
|
||||
{ key: 'waybill', label: '今日运单', value: 186, unit: '单', icon: 'Document', color: '#409eff', trend: 12.5, sub: '较昨日' },
|
||||
{ key: 'transit', label: '在途运输', value: 43, unit: '单', icon: 'Van', color: '#67c23a', trend: 3.2, sub: '在途运单' },
|
||||
{ key: 'todo', label: '待办事项', value: 9, unit: '项', icon: 'Bell', color: '#e6a23c', trend: -2.0, sub: '待处理' },
|
||||
{ key: 'capacity', label: '运力车辆', value: 128, unit: '辆', icon: 'Truck', color: '#9254de', trend: 5.1, sub: '在运+待命' },
|
||||
],
|
||||
// 运单趋势:可对接 /blade-business/waybill/trend?days=7
|
||||
trendData: [
|
||||
{ label: '周一', value: 120 },
|
||||
{ label: '周二', value: 132 },
|
||||
{ label: '周三', value: 101 },
|
||||
{ label: '周四', value: 158 },
|
||||
{ label: '周五', value: 190 },
|
||||
{ label: '周六', value: 142 },
|
||||
{ label: '周日', value: 186 },
|
||||
],
|
||||
// 货物类型分布:可对接 /blade-business/waybill/cargoStat
|
||||
cargoData: [
|
||||
{ name: '普货', value: 42, color: '#409eff' },
|
||||
{ name: '冷链', value: 21, color: '#13c2c2' },
|
||||
{ name: '大件', value: 16, color: '#fa8c16' },
|
||||
{ name: '危险品', value: 12, color: '#f5222d' },
|
||||
{ name: '其他', value: 9, color: '#bfbfbf' },
|
||||
],
|
||||
// 运输方式占比:可对接 /blade-business/waybill/transportStat
|
||||
transportData: [
|
||||
{ name: '海运', value: 48, color: '#409eff' },
|
||||
{ name: '铁路', value: 22, color: '#52c41a' },
|
||||
{ name: '公路', value: 20, color: '#fa8c16' },
|
||||
{ name: '空运', value: 10, color: '#722ed1' },
|
||||
],
|
||||
// 预警提醒:可对接车辆年检/保险/违章 + 合同待审批等接口聚合
|
||||
alerts: [
|
||||
{ level: 'danger', title: '3 辆车年检临期', desc: '桂B·12345 等将于 7 日内到期', time: '今天' },
|
||||
{ level: 'warning', title: '5 单合同待审批', desc: '客户「广西物产」等提交待审核', time: '今天' },
|
||||
{ level: 'danger', title: '2 起违章未处理', desc: '桂A·88xxx 超速记录待缴纳', time: '昨天' },
|
||||
{ level: 'info', title: '4 辆保险下月到期', desc: '建议提前安排续保', time: '本周' },
|
||||
],
|
||||
// 快捷入口:path 与菜单路由一致
|
||||
quickEntries: [
|
||||
{ name: '运单管理', path: '/business/waybill-manage', icon: 'Document', color: '#409eff' },
|
||||
{ name: '运输计划', path: '/business/transport-plan', icon: 'Promotion', color: '#13c2c2' },
|
||||
{ name: '合同管理', path: '/business/contract-manage', icon: 'Files', color: '#fa8c16' },
|
||||
{ name: '车辆管理', path: '/vehicle/customer-archive', icon: 'Van', color: '#67c23a' },
|
||||
{ name: '维修记录', path: '/vehicle/maintenance-record', icon: 'Tools', color: '#9254de' },
|
||||
{ name: '违章记录', path: '/vehicle/violation-record', icon: 'Warning', color: '#f5222d' },
|
||||
{ name: '工作待办', path: '/work/todo', icon: 'Bell', color: '#e6a23c' },
|
||||
{ name: '报表中心', path: '/report/reportlist', icon: 'DataLine', color: '#1677ff' },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
userName() {
|
||||
return this.userInfo.real_name || this.userInfo.user_name || '用户';
|
||||
},
|
||||
todayText() {
|
||||
const d = new Date();
|
||||
const week = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
const pad = n => (n < 10 ? '0' + n : n);
|
||||
return `${d.getFullYear()}年${pad(d.getMonth() + 1)}月${pad(d.getDate())}日 ${week[d.getDay()]}`;
|
||||
},
|
||||
trendBars() {
|
||||
const max = Math.max(...this.trendData.map(d => d.value)) || 1;
|
||||
return this.trendData.map(d => ({
|
||||
...d,
|
||||
percent: Math.round((d.value / max) * 100),
|
||||
}));
|
||||
},
|
||||
donutTotal() {
|
||||
return this.cargoData.reduce((s, d) => s + d.value, 0);
|
||||
},
|
||||
donutSegments() {
|
||||
const total = this.cargoData.reduce((s, d) => s + d.value, 0) || 1;
|
||||
const r = 52;
|
||||
const c = 2 * Math.PI * r;
|
||||
let acc = 0;
|
||||
return this.cargoData.map(d => {
|
||||
const len = (d.value / total) * c;
|
||||
const seg = { ...d, dash: `${len} ${c - len}`, offset: -acc };
|
||||
acc += len;
|
||||
return seg;
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
goPath(path) {
|
||||
this.$router.push({ path });
|
||||
},
|
||||
components: {
|
||||
SectionKpi,
|
||||
SectionStructure,
|
||||
SectionBusiness,
|
||||
SectionProject,
|
||||
SectionFinance,
|
||||
SectionFleet,
|
||||
SectionVehicle,
|
||||
SectionSummary,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wel-dashboard {
|
||||
padding: 8px 4px;
|
||||
padding: 12px 4px 0;
|
||||
background: #f5f7fa;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* 横幅 */
|
||||
.wel-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 26px 30px;
|
||||
color: #303133;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 横幅装饰元素 */
|
||||
.wel-banner__deco {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
.wel-banner__circle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(115, 209, 61, 0.07);
|
||||
&--1 {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
right: -40px;
|
||||
top: -60px;
|
||||
background: rgba(115, 209, 61, 0.1);
|
||||
}
|
||||
&--2 {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
right: 120px;
|
||||
bottom: -50px;
|
||||
background: rgba(115, 209, 61, 0.06);
|
||||
}
|
||||
&--3 {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
left: 25%;
|
||||
top: -20px;
|
||||
background: rgba(115, 209, 61, 0.05);
|
||||
}
|
||||
}
|
||||
.wel-banner__dot-grid {
|
||||
position: absolute;
|
||||
right: 28px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 5px);
|
||||
gap: 8px;
|
||||
i {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: rgba(115, 209, 61, 0.2);
|
||||
&:nth-child(3n) { background: rgba(115, 209, 61, 0.35); }
|
||||
&:nth-child(3n + 1) { background: rgba(115, 209, 61, 0.12); }
|
||||
}
|
||||
}
|
||||
.wel-banner__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.wel-banner__avatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
background: rgba(115, 209, 61, 0.15);
|
||||
color: #389e0d;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30px;
|
||||
}
|
||||
.wel-banner__hello {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.wel-banner__wave {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.wel-banner__sub {
|
||||
margin: 6px 0 0;
|
||||
font-size: 13px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.wel-banner__right {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 指标卡 */
|
||||
.wel-metrics {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 图表区:左右面板等高 */
|
||||
.wel-charts {
|
||||
margin-bottom: 0;
|
||||
align-items: stretch;
|
||||
|
||||
.el-col {
|
||||
display: flex;
|
||||
|
||||
> .panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 下方区:左右面板等高 */
|
||||
.wel-bottom {
|
||||
align-items: stretch;
|
||||
|
||||
.el-col {
|
||||
display: flex;
|
||||
|
||||
> .panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
.metric-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
border-left: 4px solid var(--accent);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
.metric-card__icon {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 26px;
|
||||
color: #fff;
|
||||
background: var(--accent);
|
||||
}
|
||||
.metric-card__label {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
.metric-card__value {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.metric-card__unit {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #909399;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.metric-card__trend {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
&.is-up {
|
||||
color: #67c23a;
|
||||
}
|
||||
&.is-down {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
.metric-card__trend-sub {
|
||||
color: #c0c4cc;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* 面板通用 */
|
||||
.panel {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 18px 20px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
|
||||
// 当面板在 flex 列容器中时,内容区自动撑满
|
||||
.trend,
|
||||
.donut-wrap,
|
||||
.bar-list,
|
||||
.alert-list {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.panel__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
padding-left: 8px;
|
||||
border-left: 4px solid #409eff;
|
||||
}
|
||||
.panel__title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.panel__extra {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
.panel__badge {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
/* 趋势柱 */
|
||||
.trend {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
height: 200px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
.trend__col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
.trend__val {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.trend__bar {
|
||||
flex: 1;
|
||||
width: 60%;
|
||||
max-width: 36px;
|
||||
background: #f0f2f5;
|
||||
border-radius: 6px 6px 0 0;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
overflow: hidden;
|
||||
}
|
||||
.trend__fill {
|
||||
width: 100%;
|
||||
background: linear-gradient(180deg, #5aa9ff, #409eff);
|
||||
border-radius: 6px 6px 0 0;
|
||||
transition: height 0.4s ease;
|
||||
}
|
||||
.trend__label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 环形图 */
|
||||
.donut-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
min-height: 200px;
|
||||
}
|
||||
.donut {
|
||||
position: relative;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.donut__svg {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
.donut__center {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.donut__num {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
}
|
||||
.donut__unit {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
.donut-legend {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
padding: 5px 0;
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
em {
|
||||
margin-left: auto;
|
||||
font-style: normal;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 横向条 */
|
||||
.bar-list {
|
||||
padding: 4px 0;
|
||||
}
|
||||
.bar-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.bar-row__name {
|
||||
width: 48px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
.bar-row__track {
|
||||
flex: 1;
|
||||
height: 10px;
|
||||
background: #f0f2f5;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin: 0 12px;
|
||||
}
|
||||
.bar-row__fill {
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
.bar-row__val {
|
||||
width: 44px;
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 预警列表 */
|
||||
.alert-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.alert-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
.alert-item__dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.alert-item.lv-danger .alert-item__dot {
|
||||
background: #f56c6c;
|
||||
}
|
||||
.alert-item.lv-warning .alert-item__dot {
|
||||
background: #e6a23c;
|
||||
}
|
||||
.alert-item.lv-info .alert-item__dot {
|
||||
background: #409eff;
|
||||
}
|
||||
.alert-item__main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.alert-item__title {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
.alert-item__desc {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.alert-item__time {
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
flex-shrink: 0;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
/* 快捷入口 */
|
||||
.wel-quick {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.quick-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.quick-item {
|
||||
background: #fafbfc;
|
||||
border: 1px solid #eef0f5;
|
||||
border-radius: 10px;
|
||||
padding: 18px 12px;
|
||||
.wel-footer {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
&:hover {
|
||||
border-color: var(--accent);
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
.quick-item__icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 0 auto 10px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: var(--accent);
|
||||
}
|
||||
.quick-item__name {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.quick-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
.wel-banner {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
}
|
||||
font-size: 12px;
|
||||
color: #c0c4cc;
|
||||
padding: 8px 0 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user