From 1c829f9177eab675b22be4d20ef2761ed1bbb423 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 15 九月 2026 16:15:48 +0800
Subject: [PATCH] refactor(layout): 调整布局样式并优化轮播卡片组件
---
src/assets/styles/index.scss | 1
src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue | 122
src/views/reportAnalysis/productionAnalysis/components/DateTypeSwitch.vue | 24
src/views/reportAnalysis/productionAnalysis/index.vue | 56
src/layout/index.vue | 2
src/views/reportAnalysis/PSIDataAnalysis/components/right-top.vue | 253 ++
src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue | 240 -
src/views/reportAnalysis/productionAnalysis/components/left-top.vue | 150
src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue | 138 -
src/views/reportAnalysis/PSIDataAnalysis/components/right-bottom.vue | 278 +++
src/views/reportAnalysis/dataDashboard/theme.js | 38
src/views/reportAnalysis/productionAnalysis/components/right-top.vue | 62
src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue | 33
src/views/reportAnalysis/qualityAnalysis/components/right-top.vue | 46
src/layout/components/Sidebar/Logo.vue | 251 +-
src/views/reportAnalysis/PSIDataAnalysis/components/ProductTypeSwitch.vue | 28
src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue | 25
src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue | 30
src/views/reportAnalysis/qualityAnalysis/components/left-top.vue | 183 -
src/settings.js | 2
src/views/productionManagement/safetyMonitoring/index.vue | 2
src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue | 215 +
src/views/reportAnalysis/productionAnalysis/components/center-top.vue | 54
src/views/reportAnalysis/qualityAnalysis/index.vue | 114
src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue | 57
src/views/reportAnalysis/financialAnalysis/index.vue | 2
src/views/reportAnalysis/qualityAnalysis/theme.js | 28
src/views/reportAnalysis/dataDashboard/index0.vue | 2
src/assets/styles/dashboard-dark.scss | 56
src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue | 294 +-
src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue | 99
src/views/projectManagement/projectType/index.vue | 2
src/views/reportAnalysis/dataDashboard/index.vue | 81
src/views/reportAnalysis/qualityAnalysis/components/center-center.vue | 223 -
src/layout/components/Navbar.vue | 181 -
src/views/reportAnalysis/PSIDataAnalysis/index.vue | 85
src/assets/styles/variables.module.scss | 32
src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue | 25
src/views/reportAnalysis/productionAnalysis/components/center-center.vue | 109
src/views/reportAnalysis/dataDashboard/components/DateTypeSwitch.vue | 32
src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue | 111
src/views/reportAnalysis/dataDashboard/components/PanelHeader.vue | 25
src/layout/components/Sidebar/index.vue | 78
src/views/reportAnalysis/PSIDataAnalysis/components/DateTypeSwitch.vue | 32
src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue | 60
src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue | 74
src/assets/styles/sidebar.scss | 58
src/views/reportAnalysis/PSIDataAnalysis/theme.js | 38
src/layout/components/TagsView/index.vue | 65
src/views/reportAnalysis/PSIDataAnalysis/components/CarouselCards.vue | 70
src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue | 131
src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue | 83
/dev/null | 85
src/views/reportAnalysis/productionAnalysis/components/ProductTypeSwitch.vue | 20
src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue | 79
src/views/salesManagement/indicatorStats/index.vue | 2
src/views/reportAnalysis/productionAnalysis/components/PanelHeader.vue | 22
src/layout/components/AppMain.vue | 12
src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue | 35
src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue | 70
src/views/reportAnalysis/qualityAnalysis/components/center-top.vue | 80
src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue | 372 ++-
src/views/reportAnalysis/productionAnalysis/theme.js | 71
63 files changed, 2,966 insertions(+), 2,362 deletions(-)
diff --git a/src/assets/styles/dashboard-dark.scss b/src/assets/styles/dashboard-dark.scss
new file mode 100644
index 0000000..36a0057
--- /dev/null
+++ b/src/assets/styles/dashboard-dark.scss
@@ -0,0 +1,56 @@
+// 鏆楄壊澶у睆璁捐浠ょ墝锛氭繁钃濆搧鐗岄锛堝懠搴旂櫥褰曢〉 #09183a 鏆楅儴 + 鍝佺墝钃� #2c51d9锛�
+// 鐢ㄦ硶锛氬ぇ灞忔牴瀹瑰櫒鎸� .dashboard-dark 绫伙紝鍐呴儴鐢� .bi-panel / .bi-title-bar 绛夊伐鍏风被
+// 渚� reportAnalysis 涓� 1920x1080 缂╂斁寮忓ぇ灞忓鐢紱ECharts 閰嶈壊瑙佸悇椤甸潰 theme.js
+.dashboard-dark {
+ --bi-bg-from: #0a1120;
+ --bi-bg-to: #0e1a33;
+ --bi-surface: #111c33;
+ --bi-surface-2: #16233d;
+ --bi-border: #1f2f4d;
+ --bi-divider: rgba(159, 176, 207, 0.14);
+ --bi-text: #e8eefc;
+ --bi-text-secondary: #9fb0cf;
+ --bi-text-tertiary: #64748f;
+ --bi-primary: #2c51d9;
+ --bi-accent: #5b8cff;
+ --bi-success: #2dbd85;
+ --bi-warning: #f5a524;
+ --bi-danger: #f2635b;
+ --bi-radius: 10px;
+
+ // 缁熶竴闈㈡澘瀹瑰櫒锛堟浛浠fā鏉垮浘鐗囪竟妗�/闈掕壊鎻忚竟锛�
+ .bi-panel {
+ background: var(--bi-surface);
+ border: 1px solid var(--bi-border);
+ border-radius: var(--bi-radius);
+ box-sizing: border-box;
+ }
+
+ // 闈㈡澘鏍囬锛氬乏绔栨潯 + 搴曢儴缁嗗垎闅旂嚎锛岀粨鏋勫榻愰椤� HomePanel
+ .bi-title-bar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ height: 36px;
+ padding: 0 12px;
+ font-weight: 600;
+ font-size: 16px;
+ color: var(--bi-text);
+ border-bottom: 1px solid var(--bi-divider);
+
+ &::before {
+ content: '';
+ width: 3px;
+ height: 16px;
+ border-radius: 2px;
+ background: var(--bi-accent);
+ flex-shrink: 0;
+ }
+ }
+
+ // 绛夊鏁板瓧锛岄鏍煎榻� .app-num
+ .bi-num {
+ font-variant-numeric: tabular-nums;
+ font-feature-settings: 'tnum';
+ }
+}
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 5b7949a..7c83946 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -1,4 +1,5 @@
@import './tokens.scss';
+@import './dashboard-dark.scss';
@import './variables.module.scss';
@import './mixin.scss';
@import './transition.scss';
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index 8534448..5f35ac8 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -22,8 +22,8 @@
left: 0;
z-index: 1001;
overflow: hidden;
- -webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
- box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 2px 0 8px rgba(0, 21, 41, 0.12);
+ box-shadow: 2px 0 8px rgba(0, 21, 41, 0.12);
// reset element-ui css
.horizontal-collapse-transition {
@@ -45,7 +45,7 @@
&.has-logo {
.el-scrollbar {
- height: calc(100% - 50px);
+ height: calc(100% - 60px);
}
}
@@ -80,46 +80,48 @@
display: inline-block !important;
}
- // menu hover
+ // menu hover锛氱粺涓�璧版繁搴曚寒鑹� token
.sub-menu-title-noDropdown,
.el-sub-menu__title {
&:hover {
- background-color: rgba(212, 221, 255, 0.8) !important;
+ background-color: var(--menu-hover) !important;
}
}
- & .theme-light .is-active > .el-sub-menu__title {
- color: #fff !important;
+
+ .is-active > .el-sub-menu__title {
+ color: var(--menu-active-text) !important;
}
& .nest-menu .el-sub-menu > .el-sub-menu__title,
& .el-sub-menu .el-menu-item {
min-width: $base-sidebar-width !important;
+ background-color: var(--sidebar-sub-bg) !important;
&:hover {
- background-color: rgba(212, 221, 255, 0.8) !important;
+ background-color: var(--menu-hover) !important;
}
&.is-active {
- background-color: #fff !important;
- }
- }
-
- & .theme-light .nest-menu .el-sub-menu > .el-sub-menu__title,
- & .theme-light .el-sub-menu .el-menu-item {
- //background-color: transparent;
-
- &:hover {
- background-color: rgba(212, 221, 255, 0.8) !important;
+ background-color: var(--menu-active-bg) !important;
+ color: var(--menu-active-text) !important;
}
}
}
.hideSidebar {
.sidebar-container {
- width: 54px !important;
+ width: 64px !important;
}
.main-container {
- margin-left: 54px;
+ margin-left: 64px;
+ }
+
+ // 鎶樺彔鎬佽彍鍗曢」鏀瑰渾瑙掑潡銆佸浘鏍囧眳涓�
+ .el-menu-item,
+ .el-sub-menu__title {
+ margin: 4px 8px;
+ border-radius: 8px;
+ text-align: center;
}
.sub-menu-title-noDropdown {
@@ -130,7 +132,7 @@
padding: 0 !important;
.svg-icon {
- margin-left: 20px;
+ margin-left: 16px;
}
}
}
@@ -141,7 +143,7 @@
padding: 0 !important;
.svg-icon {
- margin-left: 20px;
+ margin-left: 16px;
}
}
}
@@ -208,14 +210,15 @@
}
}
+ // 鎶樺彔寮瑰嚭鐨勪簩绾ц彍鍗曪細璺熼殢渚ф爮娣卞簳
.nest-menu .el-sub-menu > .el-sub-menu__title,
.el-menu-item {
&:hover {
- // you can use $sub-menuHover
- background-color: rgba(212, 221, 255, 0.56) !important;
+ background-color: var(--menu-hover) !important;
}
&.is-active {
- background-color: rgba(212, 221, 255, 0.56) !important;
+ background-color: var(--menu-active-bg) !important;
+ color: var(--menu-active-text) !important;
}
}
@@ -223,9 +226,10 @@
> .el-menu--popup {
max-height: 100vh;
overflow-y: auto;
+ background-color: var(--sidebar-bg) !important;
&::-webkit-scrollbar-track-piece {
- background: #d3dce6;
+ background: transparent;
}
&::-webkit-scrollbar {
@@ -233,7 +237,7 @@
}
&::-webkit-scrollbar-thumb {
- background: #99a9bf;
+ background: rgba(255, 255, 255, 0.18);
border-radius: 20px;
}
}
diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss
index 3194051..cc8e25c 100644
--- a/src/assets/styles/variables.module.scss
+++ b/src/assets/styles/variables.module.scss
@@ -21,8 +21,8 @@
$menuLightActiveText: #002fa7;
// 鍩虹鍙橀噺
-$base-sidebar-width: 200px;
-$sideBarWidth: 200px;
+$base-sidebar-width: 220px;
+$sideBarWidth: 220px;
// 鑿滃崟鏆楄壊鍙橀噺
$base-menu-color: #bfcbd9;
@@ -67,13 +67,27 @@
// CSS鍙橀噺瀹氫箟
:root {
- /* 浜壊妯″紡鍙橀噺 */
- --sidebar-bg: #{$menuBg};
- --sidebar-text: #{$menuText};
- --menu-hover: #{$menuHover};
+ /* 渚ц竟鏍忥細绉戞妧钃濇笎鍙橈紙鍛煎簲鐧诲綍椤� hero 瀹濊摑鈫掓繁钃濓級 */
+ --sidebar-bg: #1b3a94;
+ --sidebar-sub-bg: rgba(0, 0, 0, 0.16);
+ --sidebar-grad-from: #2f57e0;
+ --sidebar-grad-to: #14264f;
+ --sidebar-text: rgba(255, 255, 255, 0.72);
+ --menu-hover: rgba(255, 255, 255, 0.10);
+ --menu-active-bg: rgba(255, 255, 255, 0.18);
+ --menu-active-text: #ffffff;
+ --menu-active-indicator: #ffffff;
--navbar-bg: #ffffff;
--navbar-text: #303133;
+ --navbar-border: #e8ecf1;
+
+ /* 椤电鏍� */
+ --tags-bg: #ffffff;
+ --tags-item-bg: #ffffff;
+ --tags-item-border: #dfe5ee;
+ --tags-item-text: #4e5969;
+ --tags-item-hover: #f2f5fa;
/* splitpanes default-theme 鍙橀噺 */
--splitpanes-default-bg: #ffffff;
@@ -91,14 +105,20 @@
/* 渚ц竟鏍� */
--sidebar-bg: #141414;
+ --sidebar-sub-bg: #1d1e1f;
+ --sidebar-grad-from: #1d1e1f;
+ --sidebar-grad-to: #141414;
--sidebar-text: #ffffff;
--menu-hover: #2d2d2d;
+ --menu-active-bg: #2d2d2d;
--menu-active-text: #{$menuActiveText};
+ --menu-active-indicator: #{$menuActiveText};
/* 椤堕儴瀵艰埅鏍� */
--navbar-bg: #141414;
--navbar-text: #ffffff;
--navbar-hover: #141414;
+ --navbar-border: #303030;
/* 鏍囩鏍� */
--tags-bg: #141414;
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 1e7a78b..8d4604a 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -38,8 +38,8 @@
<style lang="scss" scoped>
.app-main {
- /* 50= navbar 50 */
- min-height: calc(100vh - 50px);
+ /* 60 = navbar */
+ min-height: calc(100vh - 60px);
width: 100%;
position: relative;
overflow: hidden;
@@ -52,17 +52,17 @@
}
.fixed-header + .app-main {
- padding-top: 50px;
+ padding-top: 60px;
}
.hasTagsView {
.app-main {
- /* 84 = navbar + tags-view = 50 + 34 */
- min-height: calc(100vh - 84px);
+ /* 100 = navbar + tags-view = 60 + 40 */
+ min-height: calc(100vh - 100px);
}
.fixed-header + .app-main {
- padding-top: 84px;
+ padding-top: 100px;
}
}
</style>
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index d4e938d..f774699 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -1,11 +1,10 @@
<template>
<div class="navbar">
- <div>
+ <div class="left-menu">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"
@toggleClick="toggleSideBar" />
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
</div>
- <!-- <top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />-->
<div class="right-menu">
<!-- 娑堟伅閫氱煡 -->
<el-popover
@@ -19,7 +18,7 @@
<template #reference>
<div class="notification-container right-menu-item hover-effect">
<el-badge :value="unreadCount" :hidden="unreadCount === 0" class="notification-badge">
- <el-icon :size="20" style="cursor: pointer;">
+ <el-icon :size="20">
<Bell />
</el-icon>
</el-badge>
@@ -30,27 +29,26 @@
ref="notificationCenterRef"
/>
</el-popover>
- <div class="avatar-container">
- <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
- <div class="avatar-wrapper">
- <img :src="userStore.avatar" class="user-avatar" />
- <el-icon><caret-bottom /></el-icon>
- </div>
- <template #dropdown>
- <el-dropdown-menu>
- <router-link to="/user/profile">
- <el-dropdown-item>涓汉涓績</el-dropdown-item>
- </router-link>
- <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
- <span>甯冨眬璁剧疆</span>
- </el-dropdown-item>
- <el-dropdown-item divided command="logout">
- <span>閫�鍑虹櫥褰�</span>
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
+ <el-dropdown @command="handleCommand" trigger="click">
+ <div class="avatar-container right-menu-item hover-effect">
+ <img :src="userStore.avatar" class="user-avatar" />
+ <span class="user-name">{{ userStore.nickName || userStore.name }}</span>
+ <el-icon class="user-caret"><caret-bottom /></el-icon>
+ </div>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <router-link to="/user/profile">
+ <el-dropdown-item>涓汉涓績</el-dropdown-item>
+ </router-link>
+ <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
+ <span>甯冨眬璁剧疆</span>
+ </el-dropdown-item>
+ <el-dropdown-item divided command="logout">
+ <span>閫�鍑虹櫥褰�</span>
+ </el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
</div>
</div>
</template>
@@ -59,13 +57,7 @@
import { ElMessageBox } from 'element-plus'
import { Bell } from '@element-plus/icons-vue'
import Breadcrumb from '@/components/Breadcrumb'
-import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
-import Screenfull from '@/components/Screenfull'
-import SizeSelect from '@/components/SizeSelect'
-import HeaderSearch from '@/components/HeaderSearch'
-import RuoYiGit from '@/components/RuoYi/Git'
-import RuoYiDoc from '@/components/RuoYi/Doc'
import NotificationCenter from './NotificationCenter/index.vue'
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
@@ -115,10 +107,6 @@
emits('setLayout')
}
-function toggleTheme() {
- settingsStore.toggleTheme()
-}
-
// 娑堟伅閫氱煡鐩稿叧
function handleUnreadCountChange(count) {
unreadCount.value = count
@@ -159,114 +147,95 @@
<style lang='scss' scoped>
.navbar {
- height: 50px;
- overflow: hidden;
+ height: 60px;
position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-right: 16px;
background: var(--navbar-bg);
- box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+ border-bottom: 1px solid var(--navbar-border);
+
+ .left-menu {
+ display: flex;
+ align-items: center;
+ height: 100%;
+ }
.hamburger-container {
- line-height: 46px;
+ display: flex;
+ align-items: center;
height: 100%;
- float: left;
+ padding: 0 14px;
cursor: pointer;
- transition: background 0.3s;
+ transition: background 0.2s;
-webkit-tap-highlight-color: transparent;
&:hover {
- background: rgba(0, 0, 0, 0.025);
+ background: var(--app-surface-hover);
}
}
- .breadcrumb-container {
- float: left;
- }
-
- .topmenu-container {
- position: absolute;
- left: 50px;
- }
-
- .errLog-container {
- display: inline-block;
- vertical-align: top;
- }
-
.right-menu {
- float: right;
- height: 100%;
- line-height: 50px;
display: flex;
+ align-items: center;
+ gap: 6px;
+ height: 100%;
&:focus {
outline: none;
}
+ // 姣忎釜鎸夐挳缁熶竴鐨勫渾瑙掓偓鍋滃簳
.right-menu-item {
- display: inline-block;
- padding: 0 8px;
- height: 100%;
+ display: flex;
+ align-items: center;
+ height: 40px;
+ padding: 0 12px;
font-size: 18px;
color: var(--navbar-text);
- vertical-align: text-bottom;
+ border-radius: 10px;
+ cursor: pointer;
+ transition: background 0.2s;
- &.hover-effect {
- cursor: pointer;
- transition: background 0.3s;
-
- &:hover {
- background: rgba(0, 0, 0, 0.025);
- }
- }
-
- &.theme-switch-wrapper {
- display: flex;
- align-items: center;
-
- svg {
- transition: transform 0.3s;
-
- &:hover {
- transform: scale(1.15);
- }
- }
+ &.hover-effect:hover {
+ background: var(--app-surface-hover);
}
}
- .notification-container {
- margin-right: 20px;
+ .notification-badge {
display: flex;
align-items: center;
- cursor: pointer;
- .notification-badge {
- :deep(.el-badge__content) {
- border: none;
- }
+ :deep(.el-badge__content) {
+ border: none;
}
}
.avatar-container {
- margin-right: 40px;
+ gap: 8px;
- .avatar-wrapper {
- margin-top: 5px;
- position: relative;
+ .user-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ object-fit: cover;
+ background: var(--app-surface-hover);
+ }
- .user-avatar {
- cursor: pointer;
- width: 40px;
- height: 40px;
- border-radius: 50px;
- }
+ .user-name {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--app-text);
+ max-width: 120px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
- i {
- cursor: pointer;
- position: absolute;
- right: -20px;
- top: 14px;
- font-size: 12px;
- }
+ .user-caret {
+ font-size: 12px;
+ color: var(--app-text-tertiary);
}
}
}
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index f8f1da3..eba3890 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -1,129 +1,122 @@
-<template>
- <div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
- <transition name="sidebarLogoFade">
- <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
- <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
- <h1 class="sidebar-title">{{ title }}</h1>
- </router-link>
- <router-link v-else key="expand" class="sidebar-logo-link" to="/">
- <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
- <h1 class="sidebar-title">{{ title }}</h1>
- </router-link>
- </transition>
- </div>
-</template>
-
-<script setup>
-import { ref, computed, onMounted, watch } from 'vue'
-import useUserStore from '@/store/modules/user'
-import defaultLogo from '@/assets/logo/logo.png' // 瀵煎叆榛樿logo
-
-defineProps({
- collapse: {
- type: Boolean,
- required: true
- }
-})
-
-const title = import.meta.env.VITE_APP_TITLE
-const userStore = useUserStore()
-
-// 澶勭悊宸ュ巶鍚嶇О锛岀敓鎴愬悎娉曠殑鏂囦欢鍚�
-const cleanFactoryName = computed(() => {
- if (!userStore.currentFactoryName) return ''
- return userStore.currentFactoryName.trim()
-})
-
-// 鍔ㄦ�乴ogo璺緞
-const logoUrl = ref('')
-
-// 妫�鏌ogo鏄惁瀛樺湪骞惰缃畊rl
-const updateLogoUrl = () => {
- if (!cleanFactoryName.value) {
- logoUrl.value = defaultLogo
- return
- }
-
- // 浣跨敤Vite鐨勫姩鎬佸鍏�
- try {
- const dynamicLogo = import.meta.glob('/src/assets/logo/*.png', { eager: true })
- const logoPath = `/src/assets/logo/${cleanFactoryName.value}.png`
-
- if (dynamicLogo[logoPath]) {
- logoUrl.value = dynamicLogo[logoPath].default
- } else {
- logoUrl.value = defaultLogo
- }
- } catch (error) {
- console.error('鍔犺浇宸ュ巶Logo澶辫触:', error)
- logoUrl.value = defaultLogo
- }
-}
-
-// 鍒濆鍖栧拰鐩戝惉鍙樺寲
-onMounted(() => {
- updateLogoUrl()
-
- // 鐩戝惉宸ュ巶鍚嶇О鍙樺寲
- watch(() => userStore.currentFactoryName, updateLogoUrl)
-})
-
-// 鍥剧墖鍔犺浇閿欒澶勭悊
-const handleImageError = (event) => {
- console.warn('Logo鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ogo')
- logoUrl.value = defaultLogo
-}
-</script>
-
-<style lang="scss" scoped>
-@import '@/assets/styles/variables.module.scss';
-
-.sidebarLogoFade-enter-active {
- transition: opacity 1.5s;
-}
-
-.sidebarLogoFade-enter,
-.sidebarLogoFade-leave-to {
- opacity: 0;
-}
-
-.sidebar-logo-container {
- position: relative;
- width: 100% !important;
- height: 50px !important;
- line-height: 50px;
- background: #fff;
- text-align: center;
- overflow: hidden;
-
- & .sidebar-logo-link {
- height: 100%;
- width: 100%;
-
- & .sidebar-logo {
- width: 100%;
- height: 100%;
- // height: 32px;
- vertical-align: middle;
- margin-right: 12px;
- }
-
- & .sidebar-title {
- display: inline-block;
- margin: 0;
- color: v-bind(getLogoTextColor);
- font-weight: 600;
- line-height: 50px;
- font-size: 14px;
- font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
- vertical-align: middle;
- }
- }
-
- &.collapse {
- .sidebar-logo {
- margin-right: 0px;
- }
- }
-}
-</style>
+<template>
+ <div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
+ <transition name="sidebarLogoFade">
+ <router-link key="logo" class="sidebar-logo-link" to="/">
+ <span class="sidebar-logo-patch">
+ <img v-if="logoUrl" :src="logoUrl" class="sidebar-logo" @error="handleImageError" alt="鍏徃Logo" />
+ </span>
+ </router-link>
+ </transition>
+ </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, watch } from 'vue'
+import useUserStore from '@/store/modules/user'
+import defaultLogo from '@/assets/login/JCCKLogo.png' // 涓庣櫥褰曢〉淇濇寔涓�鑷寸殑鍝佺墝 Logo
+
+defineProps({
+ collapse: {
+ type: Boolean,
+ required: true
+ }
+})
+
+const userStore = useUserStore()
+
+// 澶勭悊宸ュ巶鍚嶇О锛岀敓鎴愬悎娉曠殑鏂囦欢鍚�
+const cleanFactoryName = computed(() => {
+ if (!userStore.currentFactoryName) return ''
+ return userStore.currentFactoryName.trim()
+})
+
+// 鍔ㄦ�乴ogo璺緞
+const logoUrl = ref('')
+
+// 妫�鏌ogo鏄惁瀛樺湪骞惰缃畊rl
+const updateLogoUrl = () => {
+ if (!cleanFactoryName.value) {
+ logoUrl.value = defaultLogo
+ return
+ }
+
+ // 浣跨敤Vite鐨勫姩鎬佸鍏�
+ try {
+ const dynamicLogo = import.meta.glob('/src/assets/logo/*.png', { eager: true })
+ const logoPath = `/src/assets/logo/${cleanFactoryName.value}.png`
+
+ if (dynamicLogo[logoPath]) {
+ logoUrl.value = dynamicLogo[logoPath].default
+ } else {
+ logoUrl.value = defaultLogo
+ }
+ } catch (error) {
+ console.error('鍔犺浇宸ュ巶Logo澶辫触:', error)
+ logoUrl.value = defaultLogo
+ }
+}
+
+// 鍒濆鍖栧拰鐩戝惉鍙樺寲
+onMounted(() => {
+ updateLogoUrl()
+
+ // 鐩戝惉宸ュ巶鍚嶇О鍙樺寲
+ watch(() => userStore.currentFactoryName, updateLogoUrl)
+})
+
+// 鍥剧墖鍔犺浇閿欒澶勭悊
+const handleImageError = (event) => {
+ console.warn('Logo鍔犺浇澶辫触锛屼娇鐢ㄩ粯璁ogo')
+ logoUrl.value = defaultLogo
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebarLogoFade-enter-active {
+ transition: opacity 1.5s;
+}
+
+.sidebarLogoFade-enter,
+.sidebarLogoFade-leave-to {
+ opacity: 0;
+}
+
+.sidebar-logo-container {
+ position: relative;
+ width: 100%;
+ height: 60px;
+ background: transparent;
+ overflow: hidden;
+
+ & .sidebar-logo-link {
+ display: flex;
+ align-items: center;
+ height: 100%;
+ width: 100%;
+ padding: 7px 10px;
+ box-sizing: border-box;
+
+ // logo 鍘熷浘鑷甫鐧藉簳锛岀敤鐧藉渾瑙掕创鐗囧~婊℃暣鏉� Logo 鏍�
+ & .sidebar-logo-patch {
+ flex: 1;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 2px 6px;
+ box-sizing: border-box;
+ background: #fff;
+ border-radius: 8px;
+ overflow: hidden;
+ }
+
+ & .sidebar-logo {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ }
+ }
+}
+</style>
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index 9044945..f399cb0 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -2,9 +2,9 @@
<div :class="{ 'has-logo': showLogo }" class="sidebar-container">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
- <el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="getMenuBackground"
- :text-color="getMenuTextColor" :unique-opened="true" :active-text-color="theme" :collapse-transition="false"
- mode="vertical" :class="sideTheme">
+ <el-menu :default-active="activeMenu" :collapse="isCollapse" background-color="transparent"
+ text-color="var(--sidebar-text)" :unique-opened="true" active-text-color="var(--menu-active-text)"
+ :collapse-transition="false" mode="vertical" :class="sideTheme">
<sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route"
:base-path="route.path" />
</el-menu>
@@ -15,7 +15,6 @@
<script setup>
import Logo from './Logo'
import SidebarItem from './SidebarItem'
-import variables from '@/assets/styles/variables.module.scss'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
@@ -28,25 +27,7 @@
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
const showLogo = computed(() => settingsStore.sidebarLogo)
const sideTheme = computed(() => settingsStore.sideTheme)
-const theme = computed(() => settingsStore.theme)
const isCollapse = computed(() => !appStore.sidebar.opened)
-
-// 鑾峰彇鑿滃崟鑳屾櫙鑹�
-const getMenuBackground = computed(() => {
- if (settingsStore.isDark) {
- return 'var(--sidebar-bg)'
- }
- // 娴呰壊涓婚鏃讹紝鐩存帴鐢ㄤ富棰樿壊
- return sideTheme.value === 'theme-dark' ? variables.menuBg : settingsStore.theme
-})
-
-// 鑾峰彇鑿滃崟鏂囧瓧棰滆壊
-const getMenuTextColor = computed(() => {
- if (settingsStore.isDark) {
- return 'var(--sidebar-text)'
- }
- return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText
-})
const activeMenu = computed(() => {
const { meta, path } = route
@@ -59,35 +40,64 @@
<style lang="scss" scoped>
.sidebar-container {
- background-color: v-bind(getMenuBackground);
+ background: linear-gradient(180deg, var(--sidebar-grad-from) 0%, var(--sidebar-grad-to) 100%);
.scrollbar-wrapper {
- background-color: v-bind(getMenuBackground);
+ background: transparent;
}
.el-menu {
border: none;
height: 100%;
width: 100% !important;
+ --el-menu-item-height: 46px;
+ --el-menu-sub-item-height: 42px;
+ padding: 6px 0;
- .el-menu-item,
- .el-sub-menu__title {
+ // 鍦嗚琛屽潡 + hover/婵�娲绘��
+ :deep(.el-menu-item),
+ :deep(.el-sub-menu__title) {
+ position: relative;
+ margin: 3px 10px;
+ border-radius: 8px;
+ color: var(--sidebar-text);
+ background-color: transparent;
+
&:hover {
- background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
+ background-color: var(--menu-hover) !important;
+ color: var(--menu-active-text);
}
}
- .el-menu-item {
- color: v-bind(getMenuTextColor);
+ :deep(.el-menu-item.is-active) {
+ background-color: var(--menu-active-bg) !important;
+ color: var(--menu-active-text) !important;
+ font-weight: 600;
- &.is-active {
- color: var(--menu-active-text, #409eff);
- background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
+ // 璐翠晶鏍忓乏缂樼殑浜摑鎸囩ず鏉�
+ &::before {
+ content: '';
+ position: absolute;
+ left: -10px;
+ top: 9px;
+ bottom: 9px;
+ width: 3px;
+ border-radius: 0 3px 3px 0;
+ background: var(--menu-active-indicator);
}
}
- .el-sub-menu__title {
- color: v-bind(getMenuTextColor);
+ // 瀛愯彍鍗曞眰鏇存繁涓�妗e簳鑹�
+ :deep(.el-sub-menu .el-menu) {
+ background-color: var(--sidebar-sub-bg) !important;
+
+ .el-menu-item {
+ background-color: transparent;
+ }
+
+ .el-menu-item.is-active {
+ background-color: var(--menu-active-bg) !important;
+ }
}
}
}
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index d3aa736..1a95e27 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -8,7 +8,6 @@
:class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
class="tags-view-item"
- :style="activeStyle(tag)"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)"
>
@@ -45,7 +44,6 @@
import ScrollPane from './ScrollPane'
import { getNormalPath } from '@/utils/ruoyi'
import useTagsViewStore from '@/store/modules/tagsView'
-import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
const visible = ref(false)
@@ -61,7 +59,6 @@
const visitedViews = computed(() => useTagsViewStore().visitedViews)
const routes = computed(() => usePermissionStore().routes)
-const theme = computed(() => useSettingsStore().theme)
watch(route, () => {
addTags()
@@ -83,14 +80,6 @@
function isActive(r) {
return r.path === route.path
-}
-
-function activeStyle(tag) {
- if (!isActive(tag)) return {}
- return {
- "background-color": theme.value,
- "border-color": theme.value
- }
}
function isAffix(tag) {
@@ -259,42 +248,44 @@
<style lang="scss" scoped>
.tags-view-container {
- height: 34px;
+ height: 40px;
width: 100%;
- background: transparent;
+ background: var(--tags-bg);
+ border-bottom: 1px solid var(--navbar-border);
.tags-view-wrapper {
.tags-view-item {
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
position: relative;
cursor: pointer;
- height: 32px;
- line-height: 32px;
- //border: 1px solid var(--tags-item-border, #d8dce5);
- color: #4E5463;
- background: #E5E7EA;
- padding: 0 16px;
+ height: 26px;
+ margin: 7px 0 7px 8px;
+ padding: 0 12px;
font-size: 12px;
- //margin-left: 5px;
- //margin-top: 4px;
+ color: var(--tags-item-text);
+ background: var(--tags-item-bg);
+ border: 1px solid var(--tags-item-border);
+ border-radius: 8px;
+ transition: color 0.2s, background-color 0.2s, border-color 0.2s;
- //&:first-of-type {
- // margin-left: 8px;
- //}
- //
- //&:last-of-type {
- // margin-right: 15px;
- //}
+ &:last-of-type {
+ margin-right: 8px;
+ }
+
+ &:not(.active):hover {
+ color: var(--app-primary);
+ background: var(--tags-item-hover);
+ }
&.active {
- background-color: #FFFFFF !important;
- color: #2C51D9;
+ color: var(--app-primary);
+ background: var(--app-primary-soft);
+ border-color: var(--app-primary);
+ font-weight: 500;
}
}
- //.tags-view-item div {
- // transform: skew(12deg);
- // display: inline-block;
- //}
}
.contextmenu {
@@ -304,11 +295,11 @@
position: absolute;
list-style-type: none;
padding: 5px 0;
- border-radius: 4px;
+ border-radius: var(--app-radius-sm);
font-size: 12px;
font-weight: 400;
color: var(--tags-item-text, #333);
- box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
+ box-shadow: var(--app-shadow-md);
border: 1px solid var(--el-border-color-light, #e4e7ed);
li {
diff --git a/src/layout/index.vue b/src/layout/index.vue
index 67ac803..c28db8c 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -101,7 +101,7 @@
}
.hideSidebar .fixed-header {
- width: calc(100% - 54px);
+ width: calc(100% - 64px);
}
.sidebarHide .fixed-header {
diff --git a/src/settings.js b/src/settings.js
index f944e2d..4128a37 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -6,7 +6,7 @@
/**
* 渚ц竟鏍忎富棰� 娣辫壊涓婚theme-dark锛屾祬鑹蹭富棰榯heme-light
*/
- sideTheme: 'theme-light',
+ sideTheme: 'theme-dark',
/**
* 鏄惁绯荤粺甯冨眬閰嶇疆
*/
diff --git a/src/views/productionManagement/safetyMonitoring/index.vue b/src/views/productionManagement/safetyMonitoring/index.vue
index 12922e7..912786a 100644
--- a/src/views/productionManagement/safetyMonitoring/index.vue
+++ b/src/views/productionManagement/safetyMonitoring/index.vue
@@ -661,7 +661,7 @@
.safety-monitoring {
padding: 20px;
background-color: #f5f7fa;
- min-height: calc(100vh - 84px);
+ min-height: calc(100vh - 100px);
}
.monitoring-card, .control-card {
diff --git a/src/views/projectManagement/projectType/index.vue b/src/views/projectManagement/projectType/index.vue
index c2ea441..08ed26b 100644
--- a/src/views/projectManagement/projectType/index.vue
+++ b/src/views/projectManagement/projectType/index.vue
@@ -267,7 +267,7 @@
<style scoped lang="scss">
.app-container {
background-color: #f5f7fa;
- height: calc(100vh - 84px);
+ height: calc(100vh - 100px);
padding: 20px;
display: flex;
flex-direction: column;
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/CarouselCards.vue
index 0498824..b9c4dd4 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/CarouselCards.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/CarouselCards.vue
@@ -1,11 +1,14 @@
<template>
<div class="carousel-cards">
- <button
+ <button
v-if="canScrollLeft"
class="nav-button nav-button-left"
+ aria-label="鍚戝乏婊氬姩"
@click="scrollLeftFn"
>
- <img src="@/assets/BI/jiantou.png" alt="宸︾澶�" />
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+ <path d="M15 18l-6-6 6-6"/>
+ </svg>
</button>
<div
class="cards-container"
@@ -30,12 +33,15 @@
</div>
</div>
</div>
- <button
+ <button
v-if="canScrollRight"
class="nav-button nav-button-right"
+ aria-label="鍚戝彸婊氬姩"
@click="scrollRightFn"
>
- <img src="@/assets/BI/jiantou.png" alt="鍙崇澶�" />
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+ <path d="M9 6l6 6-6 6"/>
+ </svg>
</button>
</div>
</template>
@@ -186,42 +192,32 @@
position: absolute;
top: 50%;
transform: translateY(-50%);
- width: 32px;
- height: 32px;
- background: rgba(26, 88, 176, 0.6);
- border: 1px solid rgba(26, 88, 176, 0.8);
+ width: 28px;
+ height: 28px;
+ background: var(--bi-panel-bg, rgba(20, 32, 66, 0.55));
+ border: 1px solid var(--bi-panel-border, #1e3160);
border-radius: 50%;
+ color: var(--bi-text, #8fa3c8);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
- transition: all 0.3s ease;
+ transition: color 0.2s ease, border-color 0.2s ease;
padding: 0;
}
.nav-button:hover {
- background: rgba(26, 88, 176, 0.8);
- transform: translateY(-50%) scale(1.1);
+ color: var(--bi-text-strong, #eaf1ff);
+ border-color: var(--bi-accent-bright, #4a6cf0);
}
.nav-button-left {
- left: -16px;
-}
-
-.nav-button-left img {
- width: 16px;
- height: 16px;
- transform: rotate(180deg);
+ left: -14px;
}
.nav-button-right {
- right: -16px;
-}
-
-.nav-button-right img {
- width: 16px;
- height: 16px;
+ right: -14px;
}
.card-item {
@@ -229,14 +225,17 @@
min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
display: flex;
align-items: center;
- background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
- border-radius: 8px 8px 8px 8px;
+ background: var(--bi-panel-bg, rgba(20, 32, 66, 0.55));
+ border: 1px solid var(--bi-panel-border, #1e3160);
+ border-left: 2px solid var(--bi-accent-bright, #4a6cf0);
+ border-radius: 6px;
padding: 12px 16px;
- transition: all 0.3s ease;
+ transition: border-color 0.2s ease, background-color 0.2s ease;
}
.card-item:hover {
- transform: translateY(-2px);
+ background: rgba(33, 49, 96, 0.65);
+ border-color: var(--bi-accent-bright, #4a6cf0);
}
.card-icon {
@@ -259,7 +258,7 @@
.card-label {
font-weight: 400;
font-size: 14px;
- color: #FFFFFF;
+ color: var(--bi-text, #8fa3c8);
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
@@ -280,7 +279,7 @@
gap: 6px;
font-weight: 400;
font-size: 12px;
- color: rgba(255, 255, 255, 0.85);
+ color: var(--bi-text, #8fa3c8);
}
.rate-label {
@@ -292,15 +291,16 @@
}
.value-number {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
+ font-weight: 500;
+ font-size: 16px;
+ color: var(--bi-text-strong, #eaf1ff);
line-height: 1;
+ font-variant-numeric: tabular-nums;
}
.value-unit {
- font-size: 14px;
- color: #FFFFFF;
+ font-size: 12px;
+ color: var(--bi-text, #8fa3c8);
font-weight: 400;
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/DateTypeSwitch.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/DateTypeSwitch.vue
index 0c57b25..ce12ccb 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/DateTypeSwitch.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/DateTypeSwitch.vue
@@ -16,7 +16,7 @@
const props = defineProps({
modelValue: {
type: Number,
- default: 1, // 榛樿閫変腑"鍛�"
+ default: 3, // 榛樿閫変腑"瀛e害"
},
})
@@ -46,49 +46,49 @@
/* 鏈�変腑鐘舵�佺殑鏍峰紡 */
.date-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
+ background-color: rgba(20, 32, 66, 0.55);
+ color: #8fa3c8;
+ border-color: #1e3160;
border-radius: 0;
padding: 6px 20px;
font-size: 14px;
- transition: all 0.3s;
+ transition: color 0.2s ease, background-color 0.2s ease;
}
/* 绗竴涓寜閽乏渚у渾瑙� */
.date-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
}
/* 鏈�鍚庝竴涓寜閽彸渚у渾瑙� */
.date-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px;
}
/* 鎸夐挳涔嬮棿鐨勫垎闅旂嚎 */
.date-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
+ border-right: 1px solid #1e3160;
}
-/* 閫変腑鐘舵�佺殑鏍峰紡 */
+/* 閫変腑鐘舵�佺殑鏍峰紡锛氬搧鐗岃摑瀹炶壊 */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ background: #2c51d9;
color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
+ border-color: #2c51d9;
box-shadow: none;
}
/* 鎮仠鏁堟灉 */
.date-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
+ color: #eaf1ff;
}
/* 閫変腑鐘舵�佹偓鍋� */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ background: #4a6cf0;
+ border-color: #4a6cf0;
color: #ffffff;
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue
index 313f1df..f463ff4 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue
@@ -16,18 +16,27 @@
<style scoped>
.panel-header {
- background-image: url("@/assets/BI/kehuhetongback@2x.png");
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 0 4px;
+ height: 36px;
+}
+
+/* 鍝佺墝钃濈粏绔栨潯锛屽懠搴� HomePanel 鏍囬鏍峰紡 */
+.panel-header::before {
+ content: '';
+ width: 3px;
+ height: 14px;
+ border-radius: 2px;
+ background: linear-gradient(180deg, var(--bi-accent-bright, #4a6cf0) 0%, var(--bi-accent, #2c51d9) 100%);
+ flex-shrink: 0;
}
.panel-title {
- width: 100%;
font-weight: 500;
font-size: 16px;
- color: #D9ECFF;
- padding-left: 46px;
- line-height: 36px;
+ color: var(--bi-text-strong, #eaf1ff);
+ letter-spacing: 1px;
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/ProductTypeSwitch.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/ProductTypeSwitch.vue
index 87cde44..f654c5d 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/ProductTypeSwitch.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/ProductTypeSwitch.vue
@@ -43,43 +43,43 @@
}
.product-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
+ background-color: rgba(20, 32, 66, 0.55);
+ color: #8fa3c8;
+ border-color: #1e3160;
border-radius: 0;
padding: 6px 20px;
font-size: 14px;
- transition: all 0.3s;
+ transition: color 0.2s ease, background-color 0.2s ease;
}
.product-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
}
.product-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px;
}
.product-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
+ border-right: 1px solid #1e3160;
}
.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ background: #2c51d9;
color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
+ border-color: #2c51d9;
box-shadow: none;
}
.product-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
+ color: #eaf1ff;
}
.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ background: #4a6cf0;
+ border-color: #4a6cf0;
color: #ffffff;
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
index ccd7504..b819dba 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-bottom.vue
@@ -15,7 +15,7 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: '#8FA3C8' } }"
style="height: 260px"
/>
</div>
@@ -29,8 +29,9 @@
import PanelHeader from './PanelHeader.vue'
import ProductTypeSwitch from './ProductTypeSwitch.vue'
import { productInOutAnalysis } from '@/api/viewIndex.js'
+import { BI, withTooltip } from '../theme.js'
-const productType = ref(1) // 1=鍘熸潗鏂� 2=鍗婃垚鍝� 3=鎴愬搧
+const productType = ref(1) // 1=鍘熸潗鏂� 2=鎴愬搧 3=鍗婃垚鍝侊紙涓� ProductTypeSwitch 涓�鑷达級
const chartStyle = { width: '100%', height: '130%' }
@@ -49,10 +50,10 @@
itemGap: 24,
itemWidth: 12,
itemHeight: 12,
- textStyle: { color: '#B8C8E0', fontSize: 14 },
+ textStyle: { color: BI.text, fontSize: 14 },
data: [
- { name: '鍑哄簱', itemStyle: { color: 'rgba(11, 137, 254, 1)' } },
- { name: '鍏ュ簱', itemStyle: { color: 'rgba(11, 249, 254, 1)' } },
+ { name: '鍑哄簱', itemStyle: { color: BI.accentBright } },
+ { name: '鍏ュ簱', itemStyle: { color: '#35C6D6' } },
],
}
@@ -61,9 +62,9 @@
type: 'category',
data: [],
axisTick: { show: false },
- axisLine: { show: false, lineStyle: { color: 'rgba(184, 200, 224, 0.3)' } },
- axisLabel: { color: '#B8C8E0', fontSize: 12 },
- splitLine: { show: false, lineStyle: { type: 'dashed', color: 'rgba(184, 200, 224, 0.2)' } },
+ axisLine: { show: false },
+ axisLabel: { color: BI.text, fontSize: 12 },
+ splitLine: { show: false },
},
])
@@ -71,11 +72,11 @@
{
type: 'value',
name: '鍗曚綅: 浠�',
- nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 0] },
+ nameTextStyle: { color: BI.text, fontSize: 12, padding: [0, 0, 0, 0] },
axisLine: { show: false },
axisTick: { show: false },
- axisLabel: { color: '#B8C8E0', fontSize: 12 },
- splitLine: { lineStyle: { color: '#B8C8E0' } },
+ axisLabel: { color: BI.text, fontSize: 12 },
+ splitLine: { lineStyle: { color: 'rgba(143, 163, 200, 0.15)' } },
},
]
@@ -83,16 +84,16 @@
{
name: '鍑哄簱',
type: 'line',
- smooth: false,
+ smooth: true,
showSymbol: true,
symbol: 'circle',
- symbolSize: 8,
- lineStyle: { color: 'rgba(11, 137, 254, 1)', width: 2 },
- itemStyle: { color: 'rgba(11, 137, 254, 1)', borderWidth: 0 },
+ symbolSize: 6,
+ lineStyle: { color: BI.accentBright, width: 2 },
+ itemStyle: { color: BI.accentBright, borderWidth: 0 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(11, 137, 254, 0.40)' },
- { offset: 1, color: 'rgba(11, 137, 254, 0.05)' },
+ { offset: 0, color: 'rgba(74, 108, 240, 0.25)' },
+ { offset: 1, color: 'rgba(74, 108, 240, 0)' },
]),
},
data: [],
@@ -101,16 +102,16 @@
{
name: '鍏ュ簱',
type: 'line',
- smooth: false,
+ smooth: true,
showSymbol: true,
symbol: 'circle',
- symbolSize: 8,
- lineStyle: { color: 'rgba(11, 249, 254, 1)', width: 2 },
- itemStyle: { color: 'rgba(11, 249, 254, 1)', borderWidth: 0 },
+ symbolSize: 6,
+ lineStyle: { color: '#35C6D6', width: 2 },
+ itemStyle: { color: '#35C6D6', borderWidth: 0 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(11, 249, 254, 0.5)' },
- { offset: 1, color: 'rgba(11, 249, 254, 0.05)' },
+ { offset: 0, color: 'rgba(53, 198, 214, 0.25)' },
+ { offset: 1, color: 'rgba(53, 198, 214, 0)' },
]),
},
data: [],
@@ -118,11 +119,9 @@
},
])
-const tooltip = {
+const tooltip = withTooltip({
trigger: 'axis',
axisPointer: { type: 'line' },
- borderWidth: 1,
- textStyle: { fontSize: 12 },
formatter(params) {
let result = params[0].axisValue + '<br/>'
params.forEach((item) => {
@@ -130,7 +129,7 @@
})
return result
},
-}
+})
const fetchData = () => {
productInOutAnalysis({ type: productType.value })
@@ -172,7 +171,9 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 428px;
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
index 0f3ec84..c7d5eb6 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-center.vue
@@ -1,15 +1,8 @@
<template>
<div>
- <!-- 璁惧缁熻 -->
+ <!-- 浜у搧鍛ㄨ浆澶╂暟 -->
<div class="equipment-stats">
- <div class="equipment-header">
- <img
- src="@/assets/BI/shujutongjiicon@2x.png"
- alt="鍥炬爣"
- class="equipment-icon"
- />
- <span class="equipment-title">浜у搧鍛ㄨ浆澶╂暟</span>
- </div>
+ <PanelHeader title="浜у搧鍛ㄨ浆澶╂暟" />
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -19,7 +12,7 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="baseOptions"
style="height: 260px"
/>
</div>
@@ -29,11 +22,14 @@
<script setup>
import { ref, onMounted } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
import { productTurnoverDays } from '@/api/viewIndex.js'
+import { BI, baseChartOptions, withTooltip } from '../theme.js'
const chartStyle = { width: '100%', height: '100%' }
const grid = { left: '3%', right: '4%', bottom: '3%', top: '4%', containLabel: true }
-const barLegend = { show: false, textStyle: { color: '#B8C8E0' }, data: ['鍛ㄨ浆澶╂暟'] }
+const barLegend = { show: false, textStyle: { color: BI.text }, data: ['鍛ㄨ浆澶╂暟'] }
+const baseOptions = baseChartOptions
const barSeries1 = ref([
{
name: '鍛ㄨ浆澶╂暟',
@@ -42,19 +38,20 @@
barWidth: 30,
emphasis: { focus: 'series' },
itemStyle: {
+ borderRadius: [4, 4, 0, 0],
color: {
type: 'linear',
x: 0, y: 1, x2: 0, y2: 0,
colorStops: [
- { offset: 0, color: 'rgba(0,164,237,0)' },
- { offset: 1, color: '#4EE4FF' },
+ { offset: 0, color: 'rgba(74, 108, 240, 0.05)' },
+ { offset: 1, color: BI.accentBright },
],
},
},
data: [],
},
])
-const tooltip = {
+const tooltip = withTooltip({
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter(params) {
@@ -64,9 +61,9 @@
})
return result
},
-}
-const xAxis1 = ref([{ type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] }])
-const yAxis1 = [{ type: 'value', axisLabel: { color: '#B8C8E0' } }]
+})
+const xAxis1 = ref([{ type: 'category', axisTick: { show: false }, axisLabel: { color: BI.text }, data: [] }])
+const yAxis1 = [{ type: 'value', axisLabel: { color: BI.text } }]
const fetchData = () => {
productTurnoverDays()
@@ -89,41 +86,12 @@
<style scoped>
.equipment-stats {
- border: 1px solid #1a58b0;
- padding: 0 18px 18px;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
+ padding: 10px 18px 18px;
display: flex;
flex-direction: column;
- gap: 16px;
-}
-
-.equipment-header {
- font-weight: 500;
- font-size: 21px;
- display: flex;
- border-bottom: 1px solid;
- border-image: linear-gradient(
- 270deg,
- rgba(0, 126, 255, 0) 0%,
- rgba(0, 126, 255, 0.4549) 35%,
- #007eff 78%,
- #007eff 100%
- )
- 1;
- padding-bottom: 2px;
-}
-
-.equipment-title {
- font-weight: 500;
- font-size: 18px;
- background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- line-height: 50px;
-}
-
-.equipment-icon {
- width: 50px;
- height: 50px;
+ gap: 6px;
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
index 0937b32..b07561b 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue
@@ -3,11 +3,27 @@
<!-- 椤堕儴缁熻鍗$墖 -->
<div class="stats-cards">
<div
- v-for="item in statItems"
+ v-for="(item, index) in statItems"
:key="item.name"
class="stat-card"
>
- <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+ <div class="card-icon" :style="{ color: iconColors[index % iconColors.length] }">
+ <!-- 閿�鍞� -->
+ <svg v-if="index % 3 === 0" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <path d="M3 17l6-6 4 4 8-8" />
+ <path d="M15 7h6v6" />
+ </svg>
+ <!-- 閲囪喘 -->
+ <svg v-else-if="index % 3 === 1" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <path d="M21 8l-9-5-9 5 9 5 9-5z" />
+ <path d="M3 8v8l9 5 9-5V8" />
+ </svg>
+ <!-- 搴撳瓨 -->
+ <svg v-else width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <rect x="3" y="4" width="18" height="16" rx="2" />
+ <path d="M3 10h18M12 4v16" />
+ </svg>
+ </div>
<div class="card-content">
<span class="card-label">{{ item.name }}</span>
<span class="card-value">{{ item.value }}</span>
@@ -26,8 +42,10 @@
<script setup>
import { ref, onMounted } from 'vue'
import { salesPurchaseStorageProductCount } from '@/api/viewIndex.js'
+import { BI } from '../theme.js'
const statItems = ref([])
+const iconColors = [BI.accentBright, '#35C6D6', '#8E7CE6']
const formatPercent = (val) => {
const num = Number(val) || 0
@@ -67,38 +85,56 @@
flex: 1;
display: flex;
align-items: center;
- background-image: url('@/assets/BI/border@2x.png');
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ background: var(--bi-panel-bg);
+ border: 1px solid var(--bi-panel-border);
+ border-radius: 8px;
+ position: relative;
+ overflow: hidden;
height: 142px;
}
+/* 椤堕儴鍝佺墝钃濅寒绾匡紝鏇夸唬鍥剧墖鎻忚竟 */
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, var(--bi-accent) 0%, transparent 70%);
+}
+
.card-icon {
- width: 100px;
- height: 100px;
- margin: 20px 20px 0 10px;
+ width: 52px;
+ height: 52px;
+ margin: 0 16px 0 20px;
+ border-radius: 8px;
+ background: rgba(74, 108, 240, 0.12);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
}
.card-content {
display: flex;
flex-direction: column;
- gap: 10px;
+ gap: 8px;
+ min-width: 0;
}
.card-value {
- font-weight: 500;
- font-size: 40px;
- background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
+ font-weight: 600;
+ font-size: 36px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+ line-height: 1.1;
}
.card-label {
font-weight: 400;
- font-size: 19px;
- color: rgba(208, 231, 255, 0.7);
+ font-size: 16px;
+ color: var(--bi-text);
}
.card-compare {
@@ -111,11 +147,12 @@
.card-compare > span:first-child {
font-size: 13px;
- opacity: 0.8;
+ color: var(--bi-text);
}
.compare-value {
font-weight: 600;
+ font-variant-numeric: tabular-nums;
}
.compare-icon {
@@ -126,12 +163,12 @@
.compare-up .compare-value,
.compare-up .compare-icon {
- color: #00c853;
+ color: var(--bi-up, #34c77b);
}
.compare-down .compare-value,
.compare-down .compare-icon {
- color: #ff5252;
+ color: var(--bi-down, #ff6b6b);
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
index 669c826..eb2b081 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-bottom.vue
@@ -3,8 +3,11 @@
<PanelHeader title="閲囪喘鍝佸垎甯�" />
<div class="main-panel panel-item-customers">
<CarouselCards :items="cardItems" :visible-count="3" />
- <div class="pie-chart-wrapper" ref="pieWrapperRef">
- <div class="pie-background" ref="pieBackgroundRef"></div>
+ <div class="pie-chart-wrapper">
+ <div class="pie-center">
+ <span class="pie-center-value">{{ totalAmount }}</span>
+ <span class="pie-center-label">鍚堣(鍏�)</span>
+ </div>
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -22,15 +25,12 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ref, onMounted, computed } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
import { rawMaterialPurchaseAmountRatio } from '@/api/viewIndex.js'
-import { useChartBackground } from '@/hooks/useChartBackground.js'
-
-const pieWrapperRef = ref(null)
-const pieBackgroundRef = ref(null)
+import { BI, baseChartOptions, withTooltip, formatAmount } from '../theme.js'
/**
* @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
@@ -53,9 +53,13 @@
const cardItems = ref([])
// 棰滆壊鍒楄〃
-const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF']
+const landColors = BI.series
const landObjData = computed(() => array2obj(dataList.value, 'name'))
+
+const totalAmount = computed(() =>
+ formatAmount(dataList.value.reduce((sum, d) => sum + (Number(d.value) || 0), 0))
+)
// 鍥句緥閰嶇疆锛堝彸渚х珫鎺掞級
const landLegend = computed(() => {
@@ -63,7 +67,7 @@
name: d.name,
icon: 'circle',
textStyle: {
- fontSize: 18,
+ fontSize: 14,
color: landColors[idx % landColors.length],
},
}))
@@ -72,34 +76,34 @@
orient: 'vertical',
top: 'center',
left: '52%',
- itemGap: 30,
+ itemGap: 24,
data: data,
formatter: function (name) {
const item = landObjData.value[name]
if (!item) return name
- return `{title|${name}}{value|${item.value}}{unit|鍏儅{percent|${item.rate}}{unit|%}`
+ return `{title|${name}}{value|${formatAmount(item.value)}}{percent|${item.rate}}{unit|%}`
},
textStyle: {
rich: {
- value: {
- color: '#43e8fc',
+ title: {
fontSize: 14,
- fontWeight: 600,
- padding: [0, 0, 0, 10],
- },
- unit: {
- color: '#82baff',
- fontSize: 12,
- fontWeight: 600,
- padding: [0, 10, 0, 0],
- },
- percent: {
- color: '#43e8fc',
- fontSize: 14,
- fontWeight: 600,
+ color: BI.text,
padding: [0, 0, 0, 0],
},
- title: {
+ value: {
+ color: BI.textStrong,
+ fontSize: 14,
+ fontWeight: 600,
+ padding: [0, 6, 0, 10],
+ },
+ percent: {
+ color: BI.accentBright,
+ fontSize: 14,
+ fontWeight: 600,
+ padding: [0, 0, 0, 6],
+ },
+ unit: {
+ color: BI.text,
fontSize: 12,
padding: [0, 0, 0, 0],
},
@@ -109,21 +113,22 @@
})
// 鎻愮ず妗�
-const landTooltip = {
+const landTooltip = withTooltip({
trigger: 'item',
formatter: '{a} <br/>{b} : {c}鍏� ({d}%)',
-}
+})
// 鍙屽眰鐜舰楗煎浘
const landSeries = ref([
{
name: '浜у搧閲囪喘閲戦鍒嗘瀽',
type: 'pie',
- radius: ['40%', '60%'],
+ radius: ['48%', '68%'],
center: ['25%', '50%'],
itemStyle: {
- borderColor: '#0a1c3a',
+ borderColor: BI.sliceBorder,
borderWidth: 2,
+ borderRadius: 4,
color: function (params) {
return landColors[params.dataIndex % landColors.length]
},
@@ -140,9 +145,9 @@
},
},
{
- // 鍐呭湀
+ // 鍐呭湀杞ㄩ亾鐜�
type: 'pie',
- radius: ['40%', '45%'],
+ radius: ['40%', '43%'],
center: ['25%', '50%'],
silent: true,
label: {
@@ -152,7 +157,7 @@
show: false,
},
itemStyle: {
- color: 'rgba(0, 127, 255, 0.25)',
+ color: 'rgba(74, 108, 240, 0.22)',
},
data: [1],
},
@@ -163,22 +168,7 @@
height: '100%',
}
-const pieOptions = {
- backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
-}
-
-// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉�
-// 鍥捐〃涓績鏄� ['25%', '50%']锛岃儗鏅渶瑕佸榻愬埌杩欎釜浣嶇疆
-const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
- wrapperRef: pieWrapperRef,
- backgroundRef: pieBackgroundRef,
- left: '25%', // 鍥捐〃涓績 X 鏄� 25%
- top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
- offsetX: '-51.5%', // X 杞村亸绉�
- offsetY: '-50%', // Y 杞村亸绉�
- watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
-})
+const pieOptions = baseChartOptions
const fetchData = () => {
rawMaterialPurchaseAmountRatio()
@@ -207,11 +197,6 @@
onMounted(() => {
fetchData()
- initBackground()
-})
-
-onBeforeUnmount(() => {
- cleanupBackground()
})
</script>
@@ -223,7 +208,9 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 449px;
@@ -236,20 +223,28 @@
background: transparent;
}
-
-.pie-background {
+.pie-center {
position: absolute;
- width: 310px;
- height: 310px;
- background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- z-index: 1;
- pointer-events: none;
- /* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
left: 25%;
top: 50%;
- transform: translate(-51.5%, -50%);
+ transform: translate(-50%, -50%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.pie-center-value {
+ font-weight: 600;
+ font-size: 20px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+}
+
+.pie-center-label {
+ font-size: 12px;
+ color: var(--bi-text);
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
index 8fcaa42..5c84946 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/left-top.vue
@@ -3,8 +3,11 @@
<PanelHeader title="閿�鍞搧鍒嗗竷" />
<div class="main-panel panel-item-customers">
<CarouselCards :items="cardItems" :visible-count="3" />
- <div class="pie-chart-wrapper" ref="pieWrapperRef">
- <div class="pie-background" ref="pieBackgroundRef"></div>
+ <div class="pie-chart-wrapper">
+ <div class="pie-center">
+ <span class="pie-center-value">{{ totalAmount }}</span>
+ <span class="pie-center-label">鍚堣(鍏�)</span>
+ </div>
<Echarts
ref="echartsRef"
:chartStyle="chartStyle"
@@ -21,15 +24,12 @@
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ref, onMounted, computed } from 'vue'
import { productSalesAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
import Echarts from '@/components/Echarts/echarts.vue'
-import { useChartBackground } from '@/hooks/useChartBackground.js'
-
-const pieWrapperRef = ref(null)
-const pieBackgroundRef = ref(null)
+import { BI, baseChartOptions, withTooltip, formatAmount } from '../theme.js'
/**
* @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
@@ -52,16 +52,20 @@
const echartsRef = ref(null)
const pieDatas = ref([])
-const pieColors = ['#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF', '#43e8fc', '#27EBE7']
+const pieColors = BI.series
const pieObjData = computed(() => array2obj(pieDatas.value, 'name'))
+
+const totalAmount = computed(() =>
+ formatAmount(pieDatas.value.reduce((sum, d) => sum + (Number(d.value) || 0), 0))
+)
const pieLegend = computed(() => {
const data = pieDatas.value.map((d, idx) => ({
name: d.name,
icon: 'circle',
textStyle: {
- fontSize: 18,
+ fontSize: 14,
color: pieColors[idx % pieColors.length],
},
}))
@@ -70,34 +74,34 @@
orient: 'vertical',
top: 'center',
left: '52%',
- itemGap: 30,
+ itemGap: 24,
data: data,
formatter: function (name) {
const item = pieObjData.value[name]
if (!item) return name
- return `{title|${name}}{value|${item.value}}{unit|鍏儅{percent|${item.rate}}{unit|%}`
+ return `{title|${name}}{value|${formatAmount(item.value)}}{percent|${item.rate}}{unit|%}`
},
textStyle: {
rich: {
- value: {
- color: '#43e8fc',
+ title: {
fontSize: 14,
- fontWeight: 600,
- padding: [0, 0, 0, 10],
- },
- unit: {
- color: '#82baff',
- fontSize: 12,
- fontWeight: 600,
- padding: [0, 10, 0, 0],
- },
- percent: {
- color: '#43e8fc',
- fontSize: 14,
- fontWeight: 600,
+ color: BI.text,
padding: [0, 0, 0, 0],
},
- title: {
+ value: {
+ color: BI.textStrong,
+ fontSize: 14,
+ fontWeight: 600,
+ padding: [0, 6, 0, 10],
+ },
+ percent: {
+ color: BI.accentBright,
+ fontSize: 14,
+ fontWeight: 600,
+ padding: [0, 0, 0, 6],
+ },
+ unit: {
+ color: BI.text,
fontSize: 12,
padding: [0, 0, 0, 0],
},
@@ -106,20 +110,21 @@
}
})
-const pieTooltip = {
+const pieTooltip = withTooltip({
trigger: 'item',
formatter: '{a} <br/>{b} : {c}鍏� ({d}%)',
-}
+})
const pieSeries = computed(() => [
{
name: '浜у搧閿�鍞噾棰濆垎鏋�',
type: 'pie',
- radius: '60%',
+ radius: ['48%', '68%'],
center: ['25%', '50%'],
itemStyle: {
- borderColor: '#0a1c3a',
+ borderColor: BI.sliceBorder,
borderWidth: 2,
+ borderRadius: 4,
},
label: {
show: false
@@ -134,23 +139,9 @@
},
])
-const pieOptions = {
- backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
-}
+const pieOptions = baseChartOptions
const cardItems = ref([])
-
-// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉曪紙涓庡叾浠栨枃浠朵繚鎸佷竴鑷达級
-const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
- wrapperRef: pieWrapperRef,
- backgroundRef: pieBackgroundRef,
- left: '25%', // 鍥捐〃涓績 X 鏄� 25%
- top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
- offsetX: '-51.5%', // X 杞村亸绉�
- offsetY: '-50%', // Y 杞村亸绉�
- watchData: pieDatas // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
-})
const fetchData = () => {
productSalesAnalysis()
@@ -177,11 +168,6 @@
onMounted(() => {
fetchData()
- initBackground()
-})
-
-onBeforeUnmount(() => {
- cleanupBackground()
})
</script>
@@ -193,28 +179,42 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 449px;
}
+
.pie-chart-wrapper {
position: relative;
width: 100%;
height: 320px;
}
-.pie-background {
+
+.pie-center {
position: absolute;
left: 25%;
top: 50%;
- transform: translate(-51.5%, -50%);
- width: 310px;
- height: 310px;
- background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- z-index: 1;
+ transform: translate(-50%, -50%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
pointer-events: none;
+ z-index: 2;
+}
+
+.pie-center-value {
+ font-weight: 600;
+ font-size: 20px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+}
+
+.pie-center-label {
+ font-size: 12px;
+ color: var(--bi-text);
}
</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/right-bottom.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/right-bottom.vue
new file mode 100644
index 0000000..4f04d64
--- /dev/null
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/right-bottom.vue
@@ -0,0 +1,278 @@
+<template>
+ <div>
+ <PanelHeader title="瀹㈡埛璐$尞鎺掑悕" />
+ <div class="panel-item-customers">
+ <div class="switch-container">
+ <DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
+ </div>
+ <Echarts
+ ref="chart"
+ :chartStyle="chartStyle"
+ :grid="grid"
+ :legend="{ show: false }"
+ :series="series"
+ :tooltip="tooltip"
+ :xAxis="xAxis"
+ :yAxis="yAxis"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI.text } }"
+ style="height: 360px"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, computed } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
+import DateTypeSwitch from './DateTypeSwitch.vue'
+import { customerContributionRanking } from '@/api/viewIndex.js'
+import { BI, withTooltip } from '../theme.js'
+
+const chartStyle = {
+ width: '100%',
+ height: '100%',
+}
+
+const dateType = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
+
+// 鍘熷鏁版嵁锛堢粺涓�鎴� { NAME, NUM }锛�
+const dataArr = ref([])
+
+const dataArray = computed(() => {
+ const sortedAsc = [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
+ return sortedAsc.length > 5 ? sortedAsc.slice(-5) : sortedAsc
+})
+
+const total = computed(() => dataArray.value.reduce((sum, v) => sum + Number(v.NUM || 0), 0))
+
+const xdataName = computed(() => dataArray.value.map((v) => v.NAME))
+
+// 鎺掑悕閰嶈壊锛歍op1 閲戣壊锛孴op2-3 浜摑锛屽叾浣欐殫钃�
+const rankColor = (rank) => {
+ if (rank === 1) return '#F0B14A'
+ if (rank <= 3) return BI.accentBright
+ return '#2C4A9E'
+}
+
+const getRank = (i) => dataArray.value.length - i
+
+const dataNum = computed(() => {
+ return dataArray.value.map((v, i) => {
+ const rank = getRank(i)
+ return {
+ value: Number(v.NUM),
+ itemStyle: {
+ color: rankColor(rank),
+ barBorderRadius: [0, 10, 10, 0],
+ },
+ }
+ })
+})
+
+const bgData = computed(() => {
+ const maxValue = dataNum.value.length ? Math.max(...dataNum.value.map((v) => v.value)) : 0
+ return dataNum.value.map(() => maxValue * 1.08 || 1)
+})
+
+const tooltip = withTooltip({
+ trigger: 'axis',
+ formatter: function (params) {
+ let result = params[0].axisValueLabel + '<br/>'
+ result += `<div>${params[0].marker}${params[0].value}鍏�</div>`
+ return result
+ },
+})
+
+const grid = computed(() => ({ top: 0, left: '20%', right: '10%', bottom: 0 }))
+
+const xAxis = computed(() => [
+ {
+ splitLine: { show: false },
+ axisLine: { show: false },
+ axisLabel: { show: false },
+ axisTick: { show: false },
+ },
+])
+
+const yAxis = computed(() => [
+ {
+ type: 'category',
+ inverse: false,
+ data: xdataName.value,
+ axisLabel: {
+ formatter: (value) => {
+ if (!value) return ''
+ const maxLen = 6 // 姣忚鏈�澶氬瓧绗︽暟锛屽彲鎸夐渶璋冩暣
+ if (value.length <= maxLen) return `{a|${value}}`
+
+ const lines = []
+ for (let i = 0; i < value.length; i += maxLen) {
+ lines.push(value.slice(i, i + maxLen))
+ }
+ return lines.map((line) => `{a|${line}}`).join('\n')
+ },
+ rich: {
+ a: {
+ width: 120,
+ fontSize: 14,
+ color: BI.textStrong,
+ padding: [5, 4, 5, 0],
+ align: 'right',
+ },
+ },
+ },
+ axisLine: { show: false },
+ axisTick: { show: false },
+ splitLine: { show: false },
+ },
+ {
+ type: 'category',
+ data: dataNum.value.map((item) => item.value),
+ axisLabel: {
+ formatter: (params, index) => {
+ const value = typeof params === 'object' ? params.value : params
+ const percent = total.value ? ((value / total.value) * 100).toFixed(0) : 0
+ const rank = getRank(index)
+ return `{a${rank}|${percent} }{b${rank}|%}`
+ },
+ rich: dataNum.value.reduce((rich, _, i) => {
+ const rank = getRank(i)
+ const color = rank === 1 ? '#F0B14A' : rank <= 3 ? BI.accentBright : BI.text
+ rich[`a${rank}`] = { fontSize: 16, color, verticalAlign: 'bottom' }
+ rich[`b${rank}`] = { fontSize: 12, color, verticalAlign: 'bottom' }
+ return rich
+ }, {}),
+ },
+ axisLine: { show: false },
+ axisTick: { show: false },
+ splitLine: { show: false },
+ },
+])
+
+const series = computed(() => [
+ {
+ name: '鑳屾櫙',
+ z: 5,
+ type: 'bar',
+ barWidth: 18,
+ tooltip: { show: false },
+ itemStyle: {
+ color: 'rgba(143, 163, 200, 0.12)',
+ barBorderRadius: [0, 10, 10, 0],
+ },
+ data: bgData.value,
+ },
+ {
+ name: '閲戦',
+ z: 6,
+ type: 'bar',
+ barWidth: 18,
+ barGap: '-100%',
+ data: dataNum.value,
+ },
+])
+
+const normalizeItem = (item) => {
+ const name =
+ item?.NAME ??
+ item?.name ??
+ item?.customerName ??
+ item?.customer ??
+ item?.label ??
+ '-'
+
+ const num =
+ item?.NUM ??
+ item?.num ??
+ item?.value ??
+ item?.amount ??
+ item?.money ??
+ 0
+
+ return { NAME: String(name), NUM: Number(num) || 0 }
+}
+
+const getMockListByType = (type) => {
+ // 妯℃嫙鍋囨暟鎹紙閲戦璐$尞鎺掑悕锛�
+ // type: 1=鍛� 2=鏈� 3=瀛e害
+ if (type === 2) {
+ return [
+ { NAME: '鍗庝笢绮惧瘑', NUM: 5120000 },
+ { NAME: '鏄熻景鐢靛瓙', NUM: 3860000 },
+ { NAME: '鍚埅绉戞妧', NUM: 2720000 },
+ { NAME: '閾瘹鍒堕��', NUM: 2160000 },
+ { NAME: '杩滄櫙鏉愭枡', NUM: 1430000 },
+ { NAME: '寰锋鼎璐告槗', NUM: 910000 },
+ { NAME: '瀹忚揪閰嶅', NUM: 680000 },
+ ]
+ }
+ if (type === 3) {
+ return [
+ { NAME: '鍗庝笢绮惧瘑', NUM: 16800000 },
+ { NAME: '鏄熻景鐢靛瓙', NUM: 12960000 },
+ { NAME: '鍚埅绉戞妧', NUM: 9720000 },
+ { NAME: '閾瘹鍒堕��', NUM: 7560000 },
+ { NAME: '杩滄櫙鏉愭枡', NUM: 5430000 },
+ { NAME: '寰锋鼎璐告槗', NUM: 3910000 },
+ { NAME: '瀹忚揪閰嶅', NUM: 2680000 },
+ ]
+ }
+ return [
+ { NAME: '鍗庝笢绮惧瘑', NUM: 1280000 },
+ { NAME: '鏄熻景鐢靛瓙', NUM: 860000 },
+ { NAME: '鍚埅绉戞妧', NUM: 720000 },
+ { NAME: '閾瘹鍒堕��', NUM: 560000 },
+ { NAME: '杩滄櫙鏉愭枡', NUM: 430000 },
+ { NAME: '寰锋鼎璐告槗', NUM: 310000 },
+ { NAME: '瀹忚揪閰嶅', NUM: 180000 },
+ ]
+}
+
+const setMockData = (type) => {
+ dataArr.value = getMockListByType(type).map(normalizeItem)
+}
+
+const fetchCustomerRanking = () => {
+ customerContributionRanking({ type: dateType.value })
+ .then((res) => {
+ if (res.code === 200 && Array.isArray(res.data)) {
+ dataArr.value = res.data.map(item => ({
+ NAME: item.customerName,
+ NUM: item.totalAmount
+ }))
+ } else {
+ setMockData(dateType.value)
+ }
+ })
+ .catch((error) => {
+ console.error('鑾峰彇瀹㈡埛閲戦璐$尞鎺掑悕澶辫触:', error)
+ setMockData(dateType.value)
+ })
+}
+
+const handleDateTypeChange = () => {
+ fetchCustomerRanking()
+}
+
+onMounted(() => {
+ fetchCustomerRanking()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
+ padding: 18px;
+ width: 100%;
+ height: 449px;
+}
+
+.switch-container {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 16px;
+}
+</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/components/right-top.vue b/src/views/reportAnalysis/PSIDataAnalysis/components/right-top.vue
new file mode 100644
index 0000000..c925897
--- /dev/null
+++ b/src/views/reportAnalysis/PSIDataAnalysis/components/right-top.vue
@@ -0,0 +1,253 @@
+<template>
+ <div>
+ <PanelHeader title="渚涘簲鍟嗛噰璐帓鍚�" />
+ <div class="panel-item-customers">
+ <div class="switch-container">
+ <DateTypeSwitch v-model="radio1" @change="handleDateTypeChange" />
+ </div>
+ <Echarts
+ ref="chart"
+ :chartStyle="chartStyle"
+ :grid="grid"
+ :series="series"
+ :tooltip="tooltip"
+ :xAxis="xAxis"
+ :yAxis="yAxis"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI.text } }"
+ style="height: 360px"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, computed } from 'vue'
+import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
+import DateTypeSwitch from './DateTypeSwitch.vue'
+import { supplierPurchaseRanking } from '@/api/viewIndex.js'
+import { BI, withTooltip } from '../theme.js'
+
+const chartStyle = {
+ width: '100%',
+ height: '100%',
+}
+
+const radio1 = ref(3)
+
+// 鍘熷鏁版嵁
+const dataArr = ref([])
+
+// 鎺掑簭鍚庣殑鏁版嵁锛堝崌搴忥細鍊艰秺澶ц秺闈犱笂锛�
+const dataArray = computed(() => {
+ return [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
+})
+
+// 璁$畻鎬绘暟
+const total = computed(() => {
+ return dataArray.value.reduce((sum, v) => sum + Number(v.NUM), 0)
+})
+
+// x杞存暟鎹紙鍚嶇О锛�
+const xdataName = computed(() => {
+ return dataArray.value.map((v) => v.NAME)
+})
+
+// 鎺掑悕閰嶈壊锛歍op1 閲戣壊锛孴op2-3 浜摑锛屽叾浣欐殫钃�
+const rankColor = (rank) => {
+ if (rank === 1) return '#F0B14A'
+ if (rank <= 3) return BI.accentBright
+ return '#2C4A9E'
+}
+
+const getRank = (i) => dataArray.value.length - i
+
+// y杞存暟鎹紙鏁板�硷紝甯︽牱寮忥級
+const dataNum = computed(() => {
+ return dataArray.value.map((v, i) => {
+ const rank = getRank(i)
+ return {
+ value: Number(v.NUM),
+ itemStyle: {
+ color: rankColor(rank),
+ barBorderRadius: [0, 10, 10, 0],
+ },
+ }
+ })
+})
+
+// 鑳屾櫙杞ㄩ亾鏁版嵁
+const bgData = computed(() => {
+ const maxValue = dataNum.value.length ? Math.max(...dataNum.value.map((v) => v.value)) : 0
+ return dataNum.value.map(() => maxValue * 1.08 || 1)
+})
+
+// tooltip
+const tooltip = withTooltip({
+ trigger: 'axis',
+ formatter: function (params) {
+ let result = params[0].axisValueLabel + '<br/>'
+ result += `<div>${params[0].marker}${params[0].value}鍏�</div>`
+ return result
+ },
+})
+
+// grid
+const grid = computed(() => {
+ return { top: 0, left: '20%', right: '10%', bottom: 0 }
+})
+
+// xAxis
+const xAxis = computed(() => {
+ return [
+ {
+ splitLine: { show: false },
+ axisLine: { show: false },
+ axisLabel: { show: false },
+ axisTick: { show: false },
+ },
+ ]
+})
+
+// yAxis
+const yAxis = computed(() => {
+ return [
+ {
+ type: 'category',
+ inverse: false,
+ data: xdataName.value,
+ axisLabel: {
+ formatter: (value) => {
+ if (!value) return ''
+ const maxLen = 6 // 姣忚鏈�澶氬瓧绗︽暟锛屽彲鎸夐渶璋冩暣
+ if (value.length <= maxLen) return `{a|${value}}`
+
+ const lines = []
+ for (let i = 0; i < value.length; i += maxLen) {
+ lines.push(value.slice(i, i + maxLen))
+ }
+ // 澶氳鏂囨湰锛屾瘡琛岄兘濂楀悓涓�涓� rich 鏍峰紡
+ return lines.map((line) => `{a|${line}}`).join('\n')
+ },
+ rich: {
+ a: {
+ width: 120,
+ fontSize: 14,
+ color: BI.textStrong,
+ padding: [5, 4, 5, 0],
+ align: 'right',
+ },
+ },
+ },
+ axisLine: { show: false },
+ axisTick: { show: false },
+ splitLine: { show: false },
+ },
+ {
+ type: 'category',
+ data: dataNum.value.map((item) => item.value),
+ axisLabel: {
+ formatter: (params, index) => {
+ const value = typeof params === 'object' ? params.value : params
+ const percent = total.value ? ((value / total.value) * 100).toFixed(0) : 0
+ const rank = getRank(index)
+ return `{a${rank}|${percent} }{b${rank}|%}`
+ },
+ rich: dataNum.value.reduce((rich, _, i) => {
+ const rank = getRank(i)
+ const color = rank === 1 ? '#F0B14A' : rank <= 3 ? BI.accentBright : BI.text
+ rich[`a${rank}`] = { fontSize: 16, color, verticalAlign: 'bottom' }
+ rich[`b${rank}`] = { fontSize: 12, color, verticalAlign: 'bottom' }
+ return rich
+ }, {}),
+ },
+ axisLine: { show: false },
+ axisTick: { show: false },
+ splitLine: { show: false },
+ },
+ ]
+})
+
+// series锛氳儗鏅建閬� + 瀹炶壊娓愬彉鏉�
+const series = computed(() => [
+ {
+ name: '鑳屾櫙',
+ z: 5,
+ type: 'bar',
+ barWidth: 18,
+ tooltip: { show: false },
+ itemStyle: {
+ color: 'rgba(143, 163, 200, 0.12)',
+ barBorderRadius: [0, 10, 10, 0],
+ },
+ data: bgData.value,
+ },
+ {
+ name: '閲戦',
+ z: 6,
+ type: 'bar',
+ barWidth: 18,
+ barGap: '-100%',
+ data: dataNum.value,
+ },
+])
+
+// 渚涘簲鍟嗛噰璐帓鍚�
+const fetchSupplierRanking = () => {
+ supplierPurchaseRanking({ type: radio1.value })
+ .then((res) => {
+ if (res.code === 200 && Array.isArray(res.data)) {
+ dataArr.value = res.data.map(item => ({
+ NAME: item.supplierName,
+ NUM: item.totalAmount
+ }))
+ } else {
+ // 濡傛灉娌℃湁鏁版嵁锛屼娇鐢ㄦā鎷熸暟鎹�
+ dataArr.value = [
+ { NAME: '渚涘簲鍟咥', NUM: 102 },
+ { NAME: '渚涘簲鍟咮', NUM: 122 },
+ { NAME: '渚涘簲鍟咰', NUM: 282 },
+ { NAME: '渚涘簲鍟咲', NUM: 453 },
+ { NAME: '渚涘簲鍟咵', NUM: 753 },
+ ]
+ }
+ })
+ .catch((error) => {
+ console.error('鑾峰彇渚涘簲鍟嗛噰璐帓鍚嶅け璐�:', error)
+ // 浣跨敤妯℃嫙鏁版嵁
+ dataArr.value = [
+ { NAME: '渚涘簲鍟咥', NUM: 102 },
+ { NAME: '渚涘簲鍟咮', NUM: 122 },
+ { NAME: '渚涘簲鍟咰', NUM: 282 },
+ { NAME: '渚涘簲鍟咲', NUM: 453 },
+ { NAME: '渚涘簲鍟咵', NUM: 753 },
+ ]
+ })
+}
+
+// 澶勭悊鏃ユ湡绫诲瀷鍒囨崲
+const handleDateTypeChange = () => {
+ fetchSupplierRanking()
+}
+
+onMounted(() => {
+ fetchSupplierRanking()
+})
+</script>
+
+<style scoped>
+.panel-item-customers {
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
+ padding: 18px;
+ width: 100%;
+ height: 449px;
+}
+
+.switch-container {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 16px;
+}
+</style>
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/index.vue b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
index 065b59d..83e3a3d 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/index.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
@@ -13,7 +13,9 @@
<!-- 椤堕儴鏍囬鏍� -->
<div class="dashboard-header">
+ <div class="header-line"></div>
<div class="factory-name">PSI 鏁版嵁鍒嗘瀽</div>
+ <div class="header-line header-line-right"></div>
</div>
<!-- 涓昏鍐呭鍖哄煙 -->
@@ -47,8 +49,8 @@
import autofit from 'autofit.js'
import LeftBottom from './components/left-bottom.vue'
import CenterCenter from './components/center-center.vue'
-import RightTop from '../dataDashboard/components/basic/right-top.vue'
-import RightBottom from '../dataDashboard/components/basic/right-bottom.vue'
+import RightTop from './components/right-top.vue'
+import RightBottom from './components/right-bottom.vue'
import useUserStore from '@/store/modules/user'
import LeftTop from './components/left-top.vue'
import CenterTop from './components/center-top.vue'
@@ -163,7 +165,7 @@
position: relative;
width: 100%;
/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
+height: calc(100vh - 100px);
display: flex;
align-items: center;
justify-content: center;
@@ -176,11 +178,34 @@
position: relative;
width: 1920px;
height: 1080px;
-background-image: url("@/assets/BI/backImage@2x.png");
-background-size: cover;
-background-position: center;
-background-repeat: no-repeat;
transform-origin: center center;
+
+/* 澶у睆涓婚浠ょ墝锛屽瓙缁勪欢閫氳繃 CSS 鍙橀噺缁ф壙 */
+--bi-panel-bg: rgba(20, 32, 66, 0.55);
+--bi-panel-border: #1e3160;
+--bi-text: #8fa3c8;
+--bi-text-strong: #eaf1ff;
+--bi-accent: #2c51d9;
+--bi-accent-bright: #4a6cf0;
+--bi-up: #34c77b;
+--bi-down: #ff6b6b;
+
+background:
+ radial-gradient(1200px 600px at 50% -10%, rgba(44, 81, 217, 0.18) 0%, transparent 60%),
+ linear-gradient(180deg, #0a1229 0%, #0d1834 55%, #0a1430 100%);
+}
+
+/* 鏋佹贰缃戞牸绾圭悊锛屾浛浠fā鏉挎劅搴曞浘 */
+.data-dashboard::before {
+content: '';
+position: absolute;
+inset: 0;
+pointer-events: none;
+background-image:
+ linear-gradient(rgba(74, 108, 240, 0.045) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(74, 108, 240, 0.045) 1px, transparent 1px);
+background-size: 48px 48px;
+mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.35) 100%);
}
/* 鍏ㄥ睆鐘舵�佺殑鏍峰紡 - 浣滅敤浜巗cale-container */
@@ -218,20 +243,42 @@
position: relative;
z-index: 1;
height: 86px;
-background-image: url("@/assets/BI/biaoti.png");
-background-size: cover;
-background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
+gap: 28px;
+}
+
+/* 鏍囬涓や晶娓愰殣鍒嗛殧绾� */
+.header-line {
+width: 420px;
+height: 1px;
+background: linear-gradient(90deg, transparent 0%, rgba(74, 108, 240, 0.5) 100%);
+}
+
+.header-line-right {
+background: linear-gradient(270deg, transparent 0%, rgba(74, 108, 240, 0.5) 100%);
}
.factory-name {
font-weight: 600;
-font-size: 52px;
-color: #FFFFFF;
-top: 16px;
+font-size: 30px;
+letter-spacing: 4px;
+color: var(--bi-text-strong);
+position: relative;
+white-space: nowrap;
+}
+
+.factory-name::after {
+content: '';
position: absolute;
+left: 50%;
+bottom: -8px;
+transform: translateX(-50%);
+width: 48px;
+height: 2px;
+background: var(--bi-accent-bright);
+border-radius: 1px;
}
.fullscreen-btn {
@@ -240,21 +287,21 @@
left: 20px;
width: 40px;
height: 40px;
-background: rgba(0, 20, 60, 0.8);
-border: 1px solid rgba(0, 212, 255, 0.3);
+background: var(--bi-panel-bg);
+border: 1px solid var(--bi-panel-border);
border-radius: 6px;
-color: #00d4ff;
+color: var(--bi-text);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
-transition: all 0.3s;
+transition: color 0.2s ease, border-color 0.2s ease;
z-index: 10000;
}
.fullscreen-btn:hover {
-background: rgba(0, 30, 90, 0.9);
-border-color: rgba(0, 212, 255, 0.5);
+color: var(--bi-text-strong);
+border-color: var(--bi-accent-bright);
}
.dashboard-content {
diff --git a/src/views/reportAnalysis/PSIDataAnalysis/theme.js b/src/views/reportAnalysis/PSIDataAnalysis/theme.js
new file mode 100644
index 0000000..84170ae
--- /dev/null
+++ b/src/views/reportAnalysis/PSIDataAnalysis/theme.js
@@ -0,0 +1,38 @@
+// PSI 澶у睆缁熶竴瑙嗚浠ょ墝锛堝搧鐗岃摑 #2c51d9 浣撶郴鐨勫厠鍒舵繁鑹蹭富棰橈級
+export const BI = {
+ // 鍥捐〃搴忓垪鑹诧紙鍏嬪埗涓撲笟锛屾浛浠i潚钃濊崸鍏夛級
+ series: ['#4A6CF0', '#35C6D6', '#8E7CE6', '#F0B14A', '#2FB8A8', '#E86D6D'],
+ text: '#8FA3C8',
+ textStrong: '#EAF1FF',
+ panelBg: 'rgba(20, 32, 66, 0.55)',
+ panelBorder: '#1E3160',
+ sliceBorder: '#0D1834',
+ accent: '#2C51D9',
+ accentBright: '#4A6CF0',
+ up: '#34C77B',
+ down: '#FF6B6B',
+}
+
+export const baseChartOptions = {
+ backgroundColor: 'transparent',
+ textStyle: { color: BI.text },
+}
+
+// 娣辫壊缁熶竴 tooltip 鏍峰紡锛屼繚鐣欏悇缁勪欢鑷畾涔� formatter
+export const withTooltip = (tooltip = {}) => ({
+ backgroundColor: 'rgba(13, 24, 52, 0.92)',
+ borderColor: BI.panelBorder,
+ borderWidth: 1,
+ textStyle: { color: BI.textStrong, fontSize: 12 },
+ ...tooltip,
+})
+
+// 閲戦鏍煎紡鍖栵細澶ф暟瀛楁姌绠椾负銆屼竾銆嶏紝绌哄�煎厹搴�
+export const formatAmount = (val, unit = '') => {
+ const num = Number(val)
+ if (!Number.isFinite(num)) return `0${unit}`
+ if (Math.abs(num) >= 10000) {
+ return `${(num / 10000).toLocaleString('zh-CN', { maximumFractionDigits: 2 })}涓�${unit}`
+ }
+ return `${num.toLocaleString('zh-CN', { maximumFractionDigits: 2 })}${unit}`
+}
diff --git a/src/views/reportAnalysis/dataDashboard/components/DateTypeSwitch.vue b/src/views/reportAnalysis/dataDashboard/components/DateTypeSwitch.vue
index 0c57b25..ce12ccb 100644
--- a/src/views/reportAnalysis/dataDashboard/components/DateTypeSwitch.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/DateTypeSwitch.vue
@@ -16,7 +16,7 @@
const props = defineProps({
modelValue: {
type: Number,
- default: 1, // 榛樿閫変腑"鍛�"
+ default: 3, // 榛樿閫変腑"瀛e害"
},
})
@@ -46,49 +46,49 @@
/* 鏈�変腑鐘舵�佺殑鏍峰紡 */
.date-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
+ background-color: rgba(20, 32, 66, 0.55);
+ color: #8fa3c8;
+ border-color: #1e3160;
border-radius: 0;
padding: 6px 20px;
font-size: 14px;
- transition: all 0.3s;
+ transition: color 0.2s ease, background-color 0.2s ease;
}
/* 绗竴涓寜閽乏渚у渾瑙� */
.date-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
}
/* 鏈�鍚庝竴涓寜閽彸渚у渾瑙� */
.date-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px;
}
/* 鎸夐挳涔嬮棿鐨勫垎闅旂嚎 */
.date-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
+ border-right: 1px solid #1e3160;
}
-/* 閫変腑鐘舵�佺殑鏍峰紡 */
+/* 閫変腑鐘舵�佺殑鏍峰紡锛氬搧鐗岃摑瀹炶壊 */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ background: #2c51d9;
color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
+ border-color: #2c51d9;
box-shadow: none;
}
/* 鎮仠鏁堟灉 */
.date-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
+ color: #eaf1ff;
}
/* 閫変腑鐘舵�佹偓鍋� */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ background: #4a6cf0;
+ border-color: #4a6cf0;
color: #ffffff;
}
</style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/PanelHeader.vue b/src/views/reportAnalysis/dataDashboard/components/PanelHeader.vue
index 313f1df..95d39dd 100644
--- a/src/views/reportAnalysis/dataDashboard/components/PanelHeader.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/PanelHeader.vue
@@ -16,18 +16,27 @@
<style scoped>
.panel-header {
- background-image: url("@/assets/BI/kehuhetongback@2x.png");
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 0 4px;
+ height: 36px;
+}
+
+/* 鍝佺墝钃濈粏绔栨潯锛屽懠搴� PSI 澶у睆鏍囬鏍峰紡 */
+.panel-header::before {
+ content: '';
+ width: 3px;
+ height: 14px;
+ border-radius: 2px;
+ background: linear-gradient(180deg, var(--bi-accent-bright, #4a6cf0) 0%, var(--bi-accent, #2c51d9) 100%);
+ flex-shrink: 0;
}
.panel-title {
- width: 100%;
font-weight: 500;
font-size: 16px;
- color: #D9ECFF;
- padding-left: 46px;
- line-height: 36px;
+ color: var(--bi-text-strong, #eaf1ff);
+ letter-spacing: 1px;
}
</style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
index a4824a2..9a36bcb 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/center-bottom.vue
@@ -3,7 +3,10 @@
<PanelHeader title="浜哄憳鍒嗗竷" />
<div class="main-panel panel-item-customers">
<div class="pie-chart-wrapper">
- <div class="pie-background" :style="{ backgroundImage: `url(${roseBorderImg})` }"></div>
+ <div class="pie-center">
+ <span class="pie-center-value">{{ totalStaff }}</span>
+ <span class="pie-center-label">鍚堣(浜�)</span>
+ </div>
<Echarts
ref="echartsRef"
:chartStyle="chartStyle"
@@ -12,8 +15,10 @@
:tooltip="pieTooltip"
:color="pieColors"
:options="pieOptions"
- style="height: 320px"
- />
+ style="height: 100%"
+ >
+ <div v-if="loaded && !pieDatas.length" class="chart-empty">鏆傛棤鏁版嵁</div>
+ </Echarts>
</div>
</div>
</div>
@@ -24,128 +29,150 @@
import { deptStaffDistribution } from '@/api/viewIndex.js'
import PanelHeader from '../PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
-import roseBorderImg from '@/assets/BI/鐜懓鍥捐竟妗�.png'
+import { BI, baseChartOptions, withTooltip } from '../../theme.js'
-/**
- * @introduction 鎶婃暟缁勪腑key鍊肩浉鍚岀殑閭d竴椤规彁鍙栧嚭鏉ワ紝缁勬垚涓�涓璞�
- * @param {鍙傛暟绫诲瀷} array 浼犲叆鐨勬暟缁� [{a:"1",b:"2"},{a:"2",b:"3"}]
- * @param {鍙傛暟绫诲瀷} key 灞炴�у悕 a
- * @return {杩斿洖绫诲瀷璇存槑}
- */
-function array2obj(array, key) {
- const resObj = {}
- for (let i = 0; i < array.length; i++) {
- resObj[array[i][key]] = array[i]
- }
- return resObj
-}
+const echartsRef = ref(null)
+const pieDatas = ref([])
+const loaded = ref(false)
+
+const pieColors = BI.series
+
+// 浜哄憳鎬绘暟
+const totalStaff = computed(() =>
+ pieDatas.value.reduce((sum, d) => sum + (Number(d.value) || 0), 0)
+)
const chartStyle = {
width: '100%',
height: '100%',
}
-const echartsRef = ref(null)
-const pieDatas = ref([])
-const pieColors = ['#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF', '#43e8fc', '#27EBE7']
+const pieObjData = computed(() => {
+ const obj = {}
+ pieDatas.value.forEach((d) => {
+ obj[d.name] = d
+ })
+ return obj
+})
-const pieObjData = computed(() => array2obj(pieDatas.value, 'name'))
-
+// 鍥句緥閰嶇疆锛堝彸渚х珫鎺掞級
const pieLegend = computed(() => {
const data = pieDatas.value.map((d, idx) => ({
name: d.name,
icon: 'circle',
textStyle: {
- fontSize: 18,
+ fontSize: 14,
color: pieColors[idx % pieColors.length],
},
}))
return {
+ show: data.length > 0,
orient: 'vertical',
top: 'center',
left: '50%',
- itemGap: 30,
- data: data,
- formatter: function (name) {
+ itemGap: 18,
+ itemWidth: 8,
+ itemHeight: 8,
+ data,
+ formatter(name) {
const item = pieObjData.value[name]
if (!item) return name
- return `{title|${name}}{value|${item.value}}{unit|浜簘{percent|${item.rate}}{unit|%}`
+ const short = name.length > 6 ? `${name.slice(0, 6)}鈥 : name
+ return `{title|${short}}{value|${item.value}}{unit|浜簘`
},
textStyle: {
rich: {
- value: {
- color: '#43e8fc',
- fontSize: 18,
- fontWeight: 600,
- padding: [0, 10, 0, 30],
+ title: {
+ fontSize: 13,
+ color: BI.text,
+ width: 96,
+ overflow: 'truncate',
},
- unit: {
- color: '#82baff',
+ value: {
+ color: BI.textStrong,
fontSize: 14,
fontWeight: 600,
- padding: [0, 50, 0, 0],
+ padding: [0, 0, 0, 10],
},
- percent: {
- color: '#43e8fc',
- fontSize: 18,
- fontWeight: 600,
- padding: [0, 10, 0, 0],
- },
- title: {
- fontSize: 18,
- padding: [0, 0, 0, 0],
+ unit: {
+ color: BI.text,
+ fontSize: 12,
},
},
},
}
})
-const pieTooltip = {
+// 鎻愮ず妗嗭細浜烘暟 + 鍗犳瘮
+const pieTooltip = withTooltip({
trigger: 'item',
- formatter: '{a} <br/>{b} : {c} ({d}%)',
-}
+ confine: true,
+ formatter(params) {
+ const d = params.data || {}
+ const val = Number(d.value || 0)
+ return `${params.marker}<b>${d.name || ''}</b> ${val} 浜� 路 ${params.percent}%`
+ },
+})
+// 鍙屽眰鐜舰楗煎浘
const pieSeries = computed(() => [
{
name: '浜哄憳鍒嗗竷',
type: 'pie',
- radius: '70%',
+ radius: ['48%', '68%'],
center: ['20%', '50%'],
itemStyle: {
- borderColor: '#0a1c3a',
- borderWidth:5,
+ borderColor: BI.sliceBorder,
+ borderWidth: 2,
+ borderRadius: 4,
},
label: {
- show: false
+ show: false,
},
- minAngle: 15,
+ minAngle: 8,
data: pieDatas.value,
animationType: 'scale',
animationEasing: 'elasticOut',
- animationDelay: function () {
- return Math.random() * 200
+ },
+ {
+ // 鍐呭湀杞ㄩ亾鐜�
+ type: 'pie',
+ radius: ['40%', '43%'],
+ center: ['20%', '50%'],
+ silent: true,
+ label: {
+ show: false,
},
+ labelLine: {
+ show: false,
+ },
+ itemStyle: {
+ color: 'rgba(74, 108, 240, 0.22)',
+ },
+ data: [1],
},
])
-const pieOptions = {
- backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
-}
+const pieOptions = baseChartOptions
const getDeptStaffDistribution = () => {
- deptStaffDistribution().then(res => {
- if (res.code === 200) {
- pieDatas.value = res.data.items.map(item => ({
+ deptStaffDistribution()
+ .then((res) => {
+ const items = res.data?.items || []
+ pieDatas.value = items.map((item) => ({
name: item.name,
- value: parseInt(item.value),
- rate: item.rate
+ value: Number(item.value) || 0,
+ rate: item.rate,
}))
- }
- }).catch(err => {
- console.error('鑾峰彇閮ㄩ棬浜哄憳鍒嗗竷鏁版嵁澶辫触:', err)
- })
+ })
+ .catch((err) => {
+ console.error('鑾峰彇閮ㄩ棬浜哄憳鍒嗗竷鏁版嵁澶辫触:', err)
+ pieDatas.value = []
+ })
+ .finally(() => {
+ loaded.value = true
+ })
}
onMounted(() => {
@@ -160,32 +187,60 @@
gap: 20px;
}
+/* 330 = 涓爮鍓╀綑楂樺害锛屼繚璇佸乏涓彸涓夋爮搴曡竟瀵归綈 */
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
- height: 370px;
+ height: 330px;
}
.pie-chart-wrapper {
position: relative;
width: 100%;
- height: 320px;
+ height: 276px;
background: transparent;
}
-
-.pie-background {
+.pie-center {
position: absolute;
- left: 50%;
+ left: 20%;
top: 50%;
- transform: translate(-113%, -50%);
- width: 360px;
- height: 360px;
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- z-index: 1;
+ transform: translate(-50%, -50%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.pie-center-value {
+ font-weight: 600;
+ font-size: 22px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+}
+
+.pie-center-label {
+ font-size: 12px;
+ color: var(--bi-text);
+}
+
+.chart-empty {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 276px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--bi-text);
+ font-size: 14px;
+ letter-spacing: 1px;
pointer-events: none;
}
</style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
index 950038e..d5b7f2e 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/center-top.vue
@@ -3,38 +3,60 @@
<!-- 椤堕儴缁熻鍗$墖 -->
<div class="stats-cards">
<div class="stat-card">
- <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+ <div class="card-icon" style="color: #4a6cf0">
+ <!-- 鍛樺伐 -->
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <circle cx="12" cy="8" r="4" />
+ <path d="M4 21v-1a8 8 0 0 1 16 0v1" />
+ </svg>
+ </div>
<div class="card-content">
<span class="card-label">鍛樺伐鎬绘暟</span>
<span class="card-value">{{ totalStaff }}</span>
<div class="card-compare" :class="compareClass(staffYoY)">
<span>鍚屾瘮</span>
<span class="compare-value">{{ formatPercent(staffYoY) }}</span>
- <span class="compare-icon">{{ staffYoY >= 0 ? '鈫�' : '鈫�' }}</span>
+ <span v-if="!isZero(staffYoY)" class="compare-icon">{{ staffYoY > 0 ? '鈫�' : '鈫�' }}</span>
</div>
</div>
</div>
<div class="stat-card">
- <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+ <div class="card-icon" style="color: #35c6d6">
+ <!-- 瀹㈡埛 -->
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <circle cx="9" cy="8" r="3.5" />
+ <path d="M2.5 20v-1a6.5 6.5 0 0 1 13 0v1" />
+ <path d="M16 5a3.5 3.5 0 0 1 0 6.5" />
+ <path d="M17.5 13.5A6.5 6.5 0 0 1 21.5 20" />
+ </svg>
+ </div>
<div class="card-content">
<span class="card-label">瀹㈡埛鎬绘暟</span>
<span class="card-value">{{ totalCustomers }}</span>
<div class="card-compare" :class="compareClass(customersYoY)">
<span>鍚屾瘮</span>
<span class="compare-value">{{ formatPercent(customersYoY) }}</span>
- <span class="compare-icon">{{ customersYoY >= 0 ? '鈫�' : '鈫�' }}</span>
+ <span v-if="!isZero(customersYoY)" class="compare-icon">{{ customersYoY > 0 ? '鈫�' : '鈫�' }}</span>
</div>
</div>
</div>
<div class="stat-card">
- <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+ <div class="card-icon" style="color: #8e7ce6">
+ <!-- 渚涘簲鍟� -->
+ <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <rect x="1.5" y="5" width="13" height="11" rx="1.5" />
+ <path d="M14.5 9h4l3 3.5v3.5h-2" />
+ <circle cx="6" cy="18.5" r="1.8" />
+ <circle cx="17.5" cy="18.5" r="1.8" />
+ </svg>
+ </div>
<div class="card-content">
<span class="card-label">渚涘簲鍟嗘�绘暟</span>
<span class="card-value">{{ totalSuppliers }}</span>
<div class="card-compare" :class="compareClass(suppliersYoY)">
<span>鍚屾瘮</span>
<span class="compare-value">{{ formatPercent(suppliersYoY) }}</span>
- <span class="compare-icon">{{ suppliersYoY >= 0 ? '鈫�' : '鈫�' }}</span>
+ <span v-if="!isZero(suppliersYoY)" class="compare-icon">{{ suppliersYoY > 0 ? '鈫�' : '鈫�' }}</span>
</div>
</div>
</div>
@@ -42,13 +64,12 @@
<!-- 璁惧缁熻 -->
<div class="equipment-stats">
- <div class="equipment-header">
- <img
- src="@/assets/BI/shujutongjiicon@2x.png"
- alt="鍥炬爣"
- class="equipment-icon"
- />
- <span class="equipment-title">璁惧缁熻</span>
+ <div class="section-header">
+ <svg class="section-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <circle cx="12" cy="12" r="3" />
+ <path d="M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.7 1.7 0 0 0-1.87-.34 1.7 1.7 0 0 0-1.03 1.55V21a2 2 0 1 1-4 0v-.09a1.7 1.7 0 0 0-1.11-1.55 1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.7 1.7 0 0 0 .34-1.87 1.7 1.7 0 0 0-1.55-1.03H3a2 2 0 1 1 0-4h.09a1.7 1.7 0 0 0 1.55-1.11 1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.7 1.7 0 0 0 1.87.34h.08A1.7 1.7 0 0 0 10.31 3.1V3a2 2 0 1 1 4 0v.09a1.7 1.7 0 0 0 1.03 1.55 1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.7 1.7 0 0 0-.34 1.87v.08a1.7 1.7 0 0 0 1.55 1.03H21a2 2 0 1 1 0 4h-.09a1.7 1.7 0 0 0-1.55 1.03z" />
+ </svg>
+ <span class="section-title">璁惧缁熻</span>
</div>
<div class="equipment-items">
<div class="equipment-item">
@@ -70,40 +91,28 @@
</div>
</div>
- <!-- 浜嬩欢鍚嶇О -->
+ <!-- 寰呭姙浜嬩欢 -->
<div class="event-info">
- <div class="event-header">
- <img
- src="@/assets/BI/shijianmingxiicon@2x.png"
- alt="鍥炬爣"
- class="event-icon"
- />
- <span class="event-title">浜嬩欢鍚嶇О</span>
+ <div class="section-header">
+ <svg class="section-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <rect x="4" y="4" width="16" height="17" rx="2" />
+ <path d="M9 2.5h6v3H9z" />
+ <path d="M8.5 11h7M8.5 15.5h5" />
+ </svg>
+ <span class="section-title">浜嬩欢鍚嶇О</span>
</div>
<div class="event-content">
<ul class="todo-list" v-if="todoList.length > 0" ref="refTodoList">
<li v-for="item in todoList" :key="item.id">
- <div
- style="
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- width: 100%;
- gap: 20px;
- "
- >
- <div class="todo-division">寰呭姙浜嬬敱锛歿{ item.approveReason }}</div>
- <div style="display: flex;justify-content: space-between;align-items: center;"
- >
- <div class="todo-title">鐢宠绫诲瀷锛歿{ item.approveTypeName }}</div>
- <div class="todo-division">鐢宠閮ㄩ棬锛歿{ item.approveDeptName }}</div>
- <div class="todo-time">{{ item.approveTime }}</div>
- </div>
-
+ <div class="todo-row">
+ <span class="todo-division todo-reason">寰呭姙浜嬬敱锛歿{ item.approveReason || '-' }}</span>
+ <span class="todo-title">鐢宠绫诲瀷锛歿{ item.approveTypeName || '-' }}</span>
+ <span class="todo-division">鐢宠閮ㄩ棬锛歿{ item.approveDeptName || '-' }}</span>
+ <span class="todo-time">{{ item.approveTime || '-' }}</span>
</div>
</li>
</ul>
- <div v-else style="text-align: center">鏆傛棤鏁版嵁</div>
+ <div v-else class="event-empty">鏆傛棤鏁版嵁</div>
</div>
</div>
</div>
@@ -139,17 +148,25 @@
return `${Math.abs(num).toFixed(2)}%`
}
-const compareClass = (val) => (val >= 0 ? 'compare-up' : 'compare-down')
+const isZero = (val) => !(Number(val) || 0)
+
+const compareClass = (val) => {
+ const num = Number(val) || 0
+ if (num > 0) return 'compare-up'
+ if (num < 0) return 'compare-down'
+ return 'compare-flat'
+}
// 鑾峰彇鍛樺伐銆佸鎴枫�佷緵搴斿晢鏁伴噺
const getNum = () => {
summaryStatistics().then((res) => {
- totalStaff.value = res.data.totalStaff
- staffYoY.value = res.data.staffGrowthRate
- totalCustomers.value = res.data.totalCustomer
- customersYoY.value = res.data.customerGrowthRate
- totalSuppliers.value = res.data.totalSupplier
- suppliersYoY.value = res.data.supplierGrowthRate
+ const d = res.data || {}
+ totalStaff.value = Number(d.totalStaff) || 0
+ staffYoY.value = Number(d.staffGrowthRate) || 0
+ totalCustomers.value = Number(d.totalCustomer) || 0
+ customersYoY.value = Number(d.customerGrowthRate) || 0
+ totalSuppliers.value = Number(d.totalSupplier) || 0
+ suppliersYoY.value = Number(d.supplierGrowthRate) || 0
}).catch(err => {
console.error('鑾峰彇鍩虹缁熻鏁版嵁澶辫触:', err)
})
@@ -162,16 +179,16 @@
pageSize: -1,
}
getLedgerPage(params).then((res) => {
- equipmentNum.value = res.data.total
+ equipmentNum.value = Number(res.data?.total) || 0
})
getRepairPage({ ...params, status: 0 }).then((res) => {
- equipmentRepair.value = res.data.total
+ equipmentRepair.value = Number(res.data?.total) || 0
})
getUpkeepPage({ ...params, status: 0 }).then((res) => {
- equipmentMaintain.value = res.data.total
+ equipmentMaintain.value = Number(res.data?.total) || 0
})
measuringInstrumentListPage(params).then((res) => {
- totalMeasuring.value = res.data.total
+ totalMeasuring.value = Number(res.data?.total) || 0
})
}
@@ -260,11 +277,14 @@
// 寰呭姙浜嬮」
const todoInfoS = () => {
homeTodos().then((res) => {
- todoList.value = res.data
+ todoList.value = res.data || []
// 鍦ㄨ幏鍙栧埌寰呭姙浜嬮」鏁版嵁鍚庯紝鍒濆鍖栨粴鍔ㄥ姛鑳�
nextTick(() => {
initTodoListScroll()
})
+ }).catch(err => {
+ console.error('鑾峰彇寰呭姙浜嬮」澶辫触:', err)
+ todoList.value = []
})
}
@@ -300,38 +320,56 @@
flex: 1;
display: flex;
align-items: center;
- background-image: url('@/assets/BI/border@2x.png');
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ background: var(--bi-panel-bg);
+ border: 1px solid var(--bi-panel-border);
+ border-radius: 8px;
+ position: relative;
+ overflow: hidden;
height: 142px;
}
+/* 椤堕儴鍝佺墝钃濅寒绾匡紝鏇夸唬鍥剧墖鎻忚竟 */
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, var(--bi-accent) 0%, transparent 70%);
+}
+
.card-icon {
- width: 100px;
- height: 100px;
- margin: 20px 20px 0 10px;
+ width: 52px;
+ height: 52px;
+ margin: 0 16px 0 20px;
+ border-radius: 8px;
+ background: rgba(74, 108, 240, 0.12);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
}
.card-content {
display: flex;
flex-direction: column;
- gap: 10px;
+ gap: 8px;
+ min-width: 0;
}
.card-value {
- font-weight: 500;
- font-size: 40px;
- background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
+ font-weight: 600;
+ font-size: 36px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+ line-height: 1.1;
}
.card-label {
font-weight: 400;
- font-size: 19px;
- color: rgba(208, 231, 255, 0.7);
+ font-size: 16px;
+ color: var(--bi-text);
}
.card-compare {
@@ -339,16 +377,17 @@
align-items: center;
gap: 6px;
font-size: 15px;
- color: #d0e7ff;
}
.card-compare > span:first-child {
font-size: 13px;
- opacity: 0.8;
+ color: var(--bi-text);
}
.compare-value {
font-weight: 600;
+ color: var(--bi-text);
+ font-variant-numeric: tabular-nums;
}
.compare-icon {
@@ -359,163 +398,150 @@
.compare-up .compare-value,
.compare-up .compare-icon {
- color: #00c853;
+ color: var(--bi-up, #34c77b);
}
.compare-down .compare-value,
.compare-down .compare-icon {
- color: #ff5252;
+ color: var(--bi-down, #ff6b6b);
+}
+
+/* 鎸佸钩锛氫笉鏄剧ず绠ご锛岄鑹蹭腑鎬� */
+.compare-flat .compare-value {
+ color: var(--bi-text);
+}
+
+.equipment-stats,
+.event-info {
+ background: var(--bi-panel-bg);
+ border: 1px solid var(--bi-panel-border);
+ border-radius: 8px;
+ padding: 14px 18px;
}
.equipment-stats {
- border: 1px solid #1a58b0;
- padding: 18px;
height: 240px;
- padding-top: 0px;
+ margin-top: 20px;
+ box-sizing: border-box;
}
-.equipment-header {
- font-weight: 500;
- font-size: 21px;
+.event-info {
+ height: 186px;
+ margin-top: 20px;
+ box-sizing: border-box;
+}
+
+.section-header {
display: flex;
- border-bottom: 1px solid;
- border-image: linear-gradient(
- 270deg,
- rgba(0, 126, 255, 0) 0%,
- rgba(0, 126, 255, 0.4549) 35%,
- #007eff 78%,
- #007eff 100%
- )
- 1;
- padding-bottom: 2px;
+ align-items: center;
+ gap: 8px;
+ color: var(--bi-accent-bright);
+ border-bottom: 1px solid rgba(143, 163, 200, 0.15);
+ padding-bottom: 10px;
}
-.equipment-title {
+.section-icon {
+ flex-shrink: 0;
+}
+
+.section-title {
font-weight: 500;
- font-size: 18px;
- background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- line-height: 50px;
-}
-
-.equipment-icon {
- width: 50px;
- height: 50px;
+ font-size: 16px;
+ color: var(--bi-text-strong);
+ letter-spacing: 1px;
}
.equipment-items {
display: flex;
justify-content: space-around;
- gap: 30px;
+ gap: 20px;
+ padding-top: 22px;
}
.equipment-item {
text-align: center;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
}
.equipment-value {
- display: block;
- font-weight: 500;
- font-size: 40px;
- color: #ffffff;
- width: 120px;
- height: 110px;
- line-height: 110px;
- background-image: url('@/assets/BI/shujutongji@2x.png');
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
- margin-bottom: 8px;
+ font-weight: 600;
+ font-size: 36px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+ line-height: 1.1;
}
.equipment-label {
- font-weight: 500;
- font-size: 16px;
- color: #fffffe;
+ font-weight: 400;
+ font-size: 14px;
+ color: var(--bi-text);
}
-.event-info {
- background-image: url('@/assets/BI/shijianmingchengbeijing@2x.png');
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
- padding: 20px;
- padding-top: 10px;
- height: 186px;
-}
-
-.event-header {
- display: flex;
- align-items: center;
-}
-
-.event-icon {
- width: 40px;
- height: 40px;
-}
-
-.event-title {
- font-weight: 500;
- font-size: 18px;
- color: #fffffe;
- line-height: 30px;
+.event-content {
+ padding-top: 8px;
}
.todo-list {
list-style: none;
padding: 0;
margin: 0;
- height: 120px; /* 鎸夌敤鎴疯姹傝皟鏁撮珮搴� */
+ height: 96px;
overflow: hidden;
- font-size: 15px;
+ font-size: 14px;
}
.todo-list li {
- border-radius: 8px;
- margin-bottom: 12px;
- padding: 12px 40px;
- height: 74px;
+ margin-bottom: 8px;
+}
+
+.todo-row {
display: flex;
- justify-content: space-between;
align-items: center;
+ gap: 24px;
+ padding: 9px 14px;
+ background: rgba(13, 24, 52, 0.6);
+ border: 1px solid rgba(30, 49, 96, 0.8);
+ border-radius: 6px;
}
.todo-title {
- font-weight: 400;
- font-size: 16px;
- color: #fffffe;
- position: relative;
+ font-size: 14px;
+ color: var(--bi-text);
+ white-space: nowrap;
}
-
-
.todo-division {
- font-weight: 400;
- font-size: 16px;
- color: #fffffe;
- position: relative;
+ font-size: 14px;
+ color: var(--bi-text);
+ white-space: nowrap;
}
-.todo-division::before {
- content: '';
- position: absolute;
- left: -20px;
- top: 50%;
- transform: translateY(-50%);
- width: 6px;
- height: 6px;
- background: #498ceb;
- border-radius: 50%;
+.todo-reason {
+ color: var(--bi-text-strong);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ min-width: 0;
+ flex: 1;
}
.todo-time {
- font-weight: 400;
- font-size: 16px;
- color: #fffffe;
- background: rgba(24, 93, 190, 0.4);
-border-radius: 5px 5px 5px 5px;
-padding: 5px 10px;
+ font-size: 13px;
+ color: var(--bi-text);
+ background: rgba(74, 108, 240, 0.15);
+ border-radius: 4px;
+ padding: 3px 10px;
+ font-variant-numeric: tabular-nums;
+}
+
+.event-empty {
+ height: 96px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--bi-text);
+ font-size: 14px;
+ letter-spacing: 1px;
}
</style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
index da736e1..225c1f0 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/left-bottom.vue
@@ -32,22 +32,25 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
- style="height: 260px"
- />
+ :options="{ backgroundColor: 'transparent', textStyle: { color: '#8FA3C8' } }"
+ style="height: 370px"
+ >
+ <div v-if="loaded && !hasRevenueData" class="chart-empty">鏆傛棤钀ユ敹鏁版嵁</div>
+ </Echarts>
</div>
</div>
</template>
<script setup>
-import { ref, onMounted } from 'vue'
+import { ref, computed, onMounted } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import DateTypeSwitch from '../DateTypeSwitch.vue'
import { customerRevenueAnalysis } from '@/api/viewIndex.js'
import { listCustomer } from '@/api/basicData/customerFile.js'
+import { BI, withTooltip } from '../../theme.js'
-const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
+const dateType = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
const customerValue = ref(null)
const customerOptions = ref([])
@@ -58,8 +61,16 @@
const chartStyle = {
width: '100%',
- height: '150%',
+ height: '100%',
}
+
+// 鏄惁宸叉嬁鍒板搷搴旓紙鍖哄垎鍔犺浇涓笌鐪熺殑鏃犳暟鎹級
+const loaded = ref(false)
+
+// 鏈夋暟鎹墠鏄剧ず鏌卞瓙
+const hasRevenueData = computed(
+ () => (revenueData.value.items || []).some((i) => Number(i.value) > 0)
+)
const grid = {
left: '3%',
@@ -70,7 +81,7 @@
const barLegend = {
show: false,
- textStyle: { color: '#B8C8E0' },
+ textStyle: { color: BI.text },
data: ['钀ユ敹'],
}
@@ -90,9 +101,8 @@
x2: 0,
y2: 0,
colorStops: [
- // linear-gradient(360deg, rgba(0,164,237,0) 0%, #4EE4FF 100%)
- { offset: 0, color: 'rgba(0,164,237,0)' },
- { offset: 1, color: '#4EE4FF' },
+ { offset: 0, color: 'rgba(74, 108, 240, 0)' },
+ { offset: 1, color: '#4A6CF0' },
],
},
},
@@ -100,7 +110,7 @@
},
])
-const tooltip = {
+const tooltip = withTooltip({
trigger: 'axis',
axisPointer: {
type: 'shadow',
@@ -108,17 +118,19 @@
formatter: function (params) {
let result = params[0].axisValueLabel + '<br/>'
params.forEach((item) => {
- result += `<div>${item.marker} ${item.seriesName}: ${item.value}</div>`
+ const val = Number(item.value || 0).toLocaleString('zh-CN')
+ result += `<div>${item.marker} ${item.seriesName}: ${val} 鍏�</div>`
})
return result
},
-}
+})
const xAxis1 = ref([
{
type: 'category',
axisTick: { show: false },
- axisLabel: { color: '#B8C8E0' },
+ axisLine: { show: false },
+ axisLabel: { color: BI.text, fontSize: 12 },
data: [],
},
])
@@ -126,7 +138,10 @@
const yAxis1 = [
{
type: 'value',
- axisLabel: { color: '#B8C8E0' },
+ axisLabel: { color: BI.text, fontSize: 12 },
+ axisLine: { show: false },
+ axisTick: { show: false },
+ splitLine: { lineStyle: { color: 'rgba(143, 163, 200, 0.15)' } },
},
]
@@ -144,6 +159,7 @@
type: dateType.value
}
+ loaded.value = false
customerRevenueAnalysis(params)
.then((res) => {
xAxis1.value[0].data = []
@@ -152,12 +168,16 @@
const items = res.data?.items || []
items.forEach((item) => {
xAxis1.value[0].data.push(item.name)
- barSeries1.value[0].data.push(item.value)
+ barSeries1.value[0].data.push(Number(item.value) || 0)
})
- revenueData.value = res.data
+ revenueData.value = res.data || { items: [] }
})
.catch((error) => {
console.error('鑾峰彇瀹㈡埛钀ユ敹鍒嗘瀽澶辫触:', error)
+ revenueData.value = { items: [] }
+ })
+ .finally(() => {
+ loaded.value = true
})
}
@@ -219,30 +239,47 @@
/* 涓嬫媺妗嗛鏍硷細涓� DateTypeSwitch 淇濇寔涓�鑷达紙娣辫壊鍗婇�忔槑銆佹祬鑹叉枃瀛椼�佺粏杈规锛� */
.customer-select :deep(.el-input__wrapper),
.customer-select :deep(.el-select__wrapper) {
- background-color: rgba(26, 88, 176, 0.3);
- border: 1px solid rgba(255, 255, 255, 0.2);
+ background-color: rgba(20, 32, 66, 0.55);
+ border: 1px solid #1e3160;
box-shadow: none;
}
.customer-select :deep(.el-input__inner) {
- color: rgba(184, 200, 224, 0.9);
+ color: #8fa3c8;
}
.customer-select :deep(.el-input__inner::placeholder) {
- color: rgba(184, 200, 224, 0.6);
+ color: rgba(143, 163, 200, 0.6);
}
.customer-select :deep(.el-select__caret),
.customer-select :deep(.el-icon) {
- color: rgba(184, 200, 224, 0.8);
+ color: #8fa3c8;
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 478px;
}
+
+.chart-empty {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 370px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--bi-text);
+ font-size: 14px;
+ letter-spacing: 1px;
+ pointer-events: none;
+}
</style>
<style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
index 5b7e29e..27262ae 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/left-top.vue
@@ -2,8 +2,11 @@
<div>
<PanelHeader title="浜у搧澶х被" />
<div class="panel-item-customers">
- <div class="pie-chart-wrapper" ref="pieWrapperRef">
- <div class="pie-background" ref="pieBackgroundRef"></div>
+ <div class="pie-chart-wrapper">
+ <div class="pie-center">
+ <span class="pie-center-value">{{ totalCategories }}</span>
+ <span class="pie-center-label">鍚堣(绫�)</span>
+ </div>
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -14,143 +17,130 @@
:options="pieOptions"
style="height: 100%"
class="land-chart"
- />
+ >
+ <div v-if="loaded && !dataList.length" class="chart-empty">鏆傛棤鏁版嵁</div>
+ </Echarts>
</div>
</div>
</div>
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount } from 'vue'
+import { ref, onMounted, computed } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from '../PanelHeader.vue'
import { productCategoryDistribution } from '@/api/viewIndex.js'
-import { useChartBackground } from '@/hooks/useChartBackground.js'
+import { BI, baseChartOptions, withTooltip } from '../../theme.js'
-const pieWrapperRef = ref(null)
-const pieBackgroundRef = ref(null)
-const chart = ref(null)
-
-// 鏁版嵁鍒楄〃锛堟潵鑷帴鍙o級
const dataList = ref([])
+const loaded = ref(false)
-// 棰滆壊鍒楄〃
-const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF']
+const landColors = BI.series
-// label 瀵屾枃鏈細涓烘瘡涓鑹茬敓鎴愪竴涓皬鍦嗙偣鏍峰紡锛堢‘淇濆湪 label 涓彲瑙侊級
-const dotRich = landColors.reduce((acc, color, idx) => {
- acc[`dot${idx}`] = {
- width: 8,
- height: 8,
- borderRadius: 8,
- backgroundColor: color,
- align: 'center',
- }
- return acc
-}, {})
+// 澶х被鎬绘暟锛堝悇鍒嗙被涓嬬殑绫荤洰鏁颁箣鍜岋級
+const totalCategories = computed(() =>
+ dataList.value.reduce((sum, d) => sum + (Number(d.value) || 0), 0)
+)
+
+const landObjData = computed(() => {
+ const obj = {}
+ dataList.value.forEach((d) => {
+ obj[d.name] = d
+ })
+ return obj
+})
// 鍥句緥閰嶇疆锛堝彸渚х珫鎺掞級
-const landLegend = {
- show: false,
- icon: 'circle',
- data: [],
- right: '8%',
- top: '40%',
- orient: 'vertical',
- itemGap: 14,
- itemWidth: 6,
- itemHeight: 6,
- textStyle: {
- fontSize: 12,
- rich: {
- unit: {
- color: '#fff',
- fontSize: 12,
- padding: [0, 10, 0, 0],
- },
- text: {
- width: 60,
- color: '#fff',
- fontSize: 12,
+const landLegend = computed(() => {
+ const data = dataList.value.map((d, idx) => ({
+ name: d.name,
+ icon: 'circle',
+ textStyle: {
+ fontSize: 14,
+ color: landColors[idx % landColors.length],
+ },
+ }))
+
+ return {
+ show: data.length > 0,
+ orient: 'vertical',
+ top: 'center',
+ left: '50%',
+ itemGap: 20,
+ itemWidth: 8,
+ itemHeight: 8,
+ data,
+ formatter(name) {
+ const item = landObjData.value[name]
+ if (!item) return name
+ const short = name.length > 8 ? `${name.slice(0, 8)}鈥 : name
+ return `{title|${short}}{value|${item.value}}{unit|绫粆`
+ },
+ textStyle: {
+ rich: {
+ title: {
+ fontSize: 13,
+ color: BI.text,
+ width: 108,
+ overflow: 'truncate',
+ },
+ value: {
+ color: BI.textStrong,
+ fontSize: 14,
+ fontWeight: 600,
+ padding: [0, 0, 0, 10],
+ },
+ unit: {
+ color: BI.text,
+ fontSize: 12,
+ },
},
},
- },
- formatter: function (name) {
- const list = dataList.value || []
- const item = list.find((d) => d.name === name)
- if (!item) return name
- const val = Number(item.value || 0)
- const totalValue = list.reduce((sum, it) => sum + Number(it.value || 0), 0)
- const percent = totalValue ? ((val / totalValue) * 100).toFixed(2) : '0.00'
- return `{text|${name}}${val}{unit| 鍏》}${percent}{unit|%}`
- },
-}
+ }
+})
-// 鎻愮ず妗�
-const landTooltip = {
- // triggerOn: 'hover',
- alwaysShowContent: true,
- position: function (pt) {
- return [pt[0], 130]
+// 鎻愮ず妗嗭細鍗犳瘮 + 瀛愬垎绫绘槑缁�
+const landTooltip = withTooltip({
+ trigger: 'item',
+ confine: true,
+ formatter(params) {
+ const d = params.data || {}
+ const val = Number(d.value || 0)
+ let html = `${params.marker}<b>${d.name || ''}</b> ${val} 绫� 路 ${params.percent}%`
+ const children = Array.isArray(d.children) ? d.children : []
+ if (children.length) {
+ const shown = children.slice(0, 8).map((c) => c.name).join('銆�')
+ html += `<br/><span style="color:${BI.text}">瀛愮被锛�</span>${shown}${children.length > 8 ? '鈥�' : ''}`
+ }
+ return html
},
- formatter: function (params) {
- return `${params.name} (${params.value}绫�)`
- },
-}
+})
// 鍙屽眰鐜舰楗煎浘
-const landSeries = ref([
+const landSeries = computed(() => [
{
name: '浜у搧澶х被',
type: 'pie',
- radius: ['35%', '55%'],
- center: ['50%', '50%'],
- label: {
- show: true,
- position: 'outside',
- color: '#fff',
- fontSize: 12,
- lineHeight: 18,
- rich: {
- ...dotRich,
- parent: { fontSize: 14, fontWeight: 600, color: '#fff', lineHeight: 20, overflow: 'break' },
- child: { fontSize: 12, color: '#fff', lineHeight: 18 },
- },
- formatter: function (params) {
- const children = params?.data?.children || []
- const parentName = params?.data?.name || ''
- const rawVal = params?.data?.value
- const parentValue = typeof rawVal === 'number' && !Number.isNaN(rawVal) ? rawVal : (Number(rawVal) || 0)
- const dotKey = `dot${(params?.dataIndex || 0) % landColors.length}`
- const dot = `{${dotKey}|} `
- const parentLine = `${dot}{parent|${parentName} (${parentValue}绫�)}`
- if (!children.length) return parentLine
- // 鐖剁骇鍏ㄩ儴鏄剧ず锛涘瓙绾ф渶澶� 5 涓紝瓒呭嚭鏄剧ず鐪佺暐鍙�
- const displayed = children.slice(0, 5).map((c) => `{child|${c.name}}`)
- if (children.length > 5) displayed.push('{child|鈥')
- return [parentLine, ...displayed].join('\n')
- },
- },
- labelLine: {
- show: true,
- length: 20,
- length2: 20,
- lineStyle: {
- color: '#B8C8E0',
- },
- },
+ radius: ['42%', '58%'],
+ center: ['25%', '50%'],
itemStyle: {
- color: function (params) {
- return landColors[params.dataIndex % landColors.length]
- },
+ borderColor: BI.sliceBorder,
+ borderWidth: 2,
+ borderRadius: 4,
},
+ label: {
+ show: false,
+ },
+ minAngle: 8,
data: dataList.value,
+ animationType: 'scale',
+ animationEasing: 'elasticOut',
},
{
- // 鍐呭湀
+ // 鍐呭湀杞ㄩ亾鐜�
type: 'pie',
- radius: ['35%', '40%'],
- center: ['50%', '50%'],
+ radius: ['33%', '36%'],
+ center: ['25%', '50%'],
silent: true,
label: {
show: false,
@@ -159,7 +149,7 @@
show: false,
},
itemStyle: {
- color: 'rgba(0, 127, 255, 0.25)',
+ color: 'rgba(74, 108, 240, 0.22)',
},
data: [1],
},
@@ -167,22 +157,10 @@
const chartStyle = {
width: '100%',
- height: '126%',
+ height: '100%',
}
-const pieOptions = {
- backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
-}
-
-// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉曪紝鍙嚜瀹氫箟鍋忕Щ鍊�
-const { adjustBackgroundPosition, init: initBackground, cleanup: cleanupBackground } = useChartBackground({
- wrapperRef: pieWrapperRef,
- backgroundRef: pieBackgroundRef,
- offsetX: '-51.5%', // X 杞村亸绉伙紝鍙姩鎬佽皟鏁�
- offsetY: '-39%', // Y 杞村亸绉伙紝鍙姩鎬佽皟鏁�
- watchData: dataList // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
-})
+const pieOptions = baseChartOptions
const loadData = async () => {
try {
@@ -194,32 +172,24 @@
rate: it.rate,
children: Array.isArray(it.children) ? it.children : [],
}))
- landLegend.data = dataList.value.map((d) => d.name)
- landSeries.value[0].data = dataList.value
- // 鏁版嵁鍔犺浇瀹屾垚鍚庤皟鏁磋儗鏅綅缃�
- adjustBackgroundPosition()
} catch (e) {
console.error('鑾峰彇浜у搧澶х被鍒嗗竷澶辫触:', e)
dataList.value = []
- landLegend.data = []
- landSeries.value[0].data = []
+ } finally {
+ loaded.value = true
}
}
-
onMounted(() => {
loadData()
- initBackground()
-})
-
-onBeforeUnmount(() => {
- cleanupBackground()
})
</script>
<style scoped>
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 420px;
@@ -228,23 +198,47 @@
.pie-chart-wrapper {
position: relative;
width: 100%;
- height: 320px;
+ height: 384px;
background: transparent;
}
-.pie-background {
+.pie-center {
position: absolute;
- width: 360px;
- height: 360px;
- background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- z-index: 1;
- pointer-events: none;
- /* 榛樿灞呬腑锛屼細鍦� JS 涓姩鎬佽皟鏁� */
- left: 50%;
+ left: 25%;
top: 50%;
- transform: translate(-51.5%, -39%);
+ transform: translate(-50%, -50%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.pie-center-value {
+ font-weight: 600;
+ font-size: 22px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+}
+
+.pie-center-label {
+ font-size: 12px;
+ color: var(--bi-text);
+}
+
+.chart-empty {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ height: 384px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--bi-text);
+ font-size: 14px;
+ letter-spacing: 1px;
+ pointer-events: none;
}
</style>
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue b/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
index bbcd5f0..ce83474 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/right-bottom.vue
@@ -14,7 +14,7 @@
:tooltip="tooltip"
:xAxis="xAxis"
:yAxis="yAxis"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI.text } }"
style="height: 360px"
/>
</div>
@@ -27,25 +27,14 @@
import PanelHeader from '../PanelHeader.vue'
import DateTypeSwitch from '../DateTypeSwitch.vue'
import { customerContributionRanking } from '@/api/viewIndex.js'
+import { BI, withTooltip } from '../../theme.js'
const chartStyle = {
width: '100%',
height: '100%',
}
-const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
-
-// 椋炴満鍥炬爣 SVG path锛堜笌 right-top 涓�鑷达級
-const aircraft =
- 'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
-
-// 棰滆壊閰嶇疆锛堜笌 right-top 涓�鑷达級
-const color = {
- 0: '#ff5676',
- 1: '#ffd83e',
- 2: '#fbff94',
- 3: '#7daeff',
-}
+const dateType = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
// 鍘熷鏁版嵁锛堢粺涓�鎴� { NAME, NUM }锛�
const dataArr = ref([])
@@ -59,56 +48,41 @@
const xdataName = computed(() => dataArray.value.map((v) => v.NAME))
+// 鎺掑悕閰嶈壊锛歍op1 閲戣壊锛孴op2-3 浜摑锛屽叾浣欐殫钃�
+const rankColor = (rank) => {
+ if (rank === 1) return '#F0B14A'
+ if (rank <= 3) return BI.accentBright
+ return '#2C4A9E'
+}
+
+const getRank = (i) => dataArray.value.length - i
+
const dataNum = computed(() => {
return dataArray.value.map((v, i) => {
- const index = dataArray.value.length - i - 1
- const isTop3 = index < 3
-
+ const rank = getRank(i)
return {
value: Number(v.NUM),
itemStyle: {
- color: {
- type: 'linear',
- x: 1,
- y: 0,
- x2: 0,
- y2: 0,
- colorStops: [
- { offset: 0, color: isTop3 ? '#ffdae1' : '#ecf3ff' },
- { offset: 0.07, color: isTop3 ? color[index] : color[3] },
- {
- offset: 1,
- color: isTop3 ? 'rgba(255, 86, 118, .1)' : 'rgba(125,174,255, .1)',
- },
- ],
- global: false,
- },
- barBorderRadius: [0, 20, 20, 0],
+ color: rankColor(rank),
+ barBorderRadius: [0, 10, 10, 0],
},
- symbol: isTop3 ? aircraft : 'none',
- symbolPosition: 'end',
- symbolSize: [30, 25],
- symbolOffset: [35, 0],
}
})
})
const bgData = computed(() => {
- const maxValue = Math.max(0, ...dataNum.value.map((v) => v.value))
- return dataNum.value.map(() => maxValue + 200)
+ const maxValue = dataNum.value.length ? Math.max(...dataNum.value.map((v) => v.value)) : 0
+ return dataNum.value.map(() => maxValue * 1.08 || 1)
})
-const tooltip = computed(() => ({
+const tooltip = withTooltip({
trigger: 'axis',
- textStyle: { fontSize: '100%' },
formatter: function (params) {
let result = params[0].axisValueLabel + '<br/>'
- params.forEach((item) => {
- result += `<div>${item.marker} ${item.seriesName}: ${item.value}鍏�</div>`
- })
+ result += `<div>${params[0].marker}${Number(params[0].value || 0).toLocaleString('zh-CN')}鍏�</div>`
return result
},
-}))
+})
const grid = computed(() => ({ top: 0, left: '20%', right: '10%', bottom: 0 }))
@@ -142,7 +116,7 @@
a: {
width: 120,
fontSize: 14,
- color: '#fff',
+ color: BI.textStrong,
padding: [5, 4, 5, 0],
align: 'right',
},
@@ -159,21 +133,16 @@
formatter: (params, index) => {
const value = typeof params === 'object' ? params.value : params
const percent = total.value ? ((value / total.value) * 100).toFixed(0) : 0
- const rank = dataArray.value.length - index
- const isTop3 = rank < 4
-
- return `{a${isTop3 ? rank : ''}|${percent} }{b${isTop3 ? rank : ''}|%}`
+ const rank = getRank(index)
+ return `{a${rank}|${percent} }{b${rank}|%}`
},
- rich: {
- a: { fontSize: 18, color: '#98bfff', verticalAlign: 'bottom' },
- a1: { fontSize: 18, color: '#ff7f97', verticalAlign: 'bottom' },
- a2: { fontSize: 18, color: '#ffce64', verticalAlign: 'bottom' },
- a3: { fontSize: 18, color: '#e8ed66', verticalAlign: 'bottom' },
- b: { fontSize: 12, color: '#98bfff', verticalAlign: 'bottom' },
- b1: { fontSize: 12, color: '#ff7f97', verticalAlign: 'bottom' },
- b2: { fontSize: 12, color: '#ffce64', verticalAlign: 'bottom' },
- b3: { fontSize: 12, color: '#e8ed66', verticalAlign: 'bottom' },
- },
+ rich: dataNum.value.reduce((rich, _, i) => {
+ const rank = getRank(i)
+ const color = rank === 1 ? '#F0B14A' : rank <= 3 ? BI.accentBright : BI.text
+ rich[`a${rank}`] = { fontSize: 16, color, verticalAlign: 'bottom' }
+ rich[`b${rank}`] = { fontSize: 12, color, verticalAlign: 'bottom' }
+ return rich
+ }, {}),
},
axisLine: { show: false },
axisTick: { show: false },
@@ -181,47 +150,26 @@
},
])
+// series锛氳儗鏅建閬� + 瀹炶壊鏉�
const series = computed(() => [
{
- name: '閲戦',
- z: 6,
- type: 'pictorialBar',
- data: dataNum.value,
- },
- {
name: '鑳屾櫙',
- z: 6,
+ z: 5,
type: 'bar',
- barWidth: 25,
+ barWidth: 18,
tooltip: { show: false },
itemStyle: {
- color: 'rgba(255,255,255,.1)',
- barBorderRadius: [0, 20, 20, 0],
+ color: 'rgba(143, 163, 200, 0.12)',
+ barBorderRadius: [0, 10, 10, 0],
},
data: bgData.value,
},
{
- name: '閲戦娓愬彉',
+ name: '閲戦',
+ z: 6,
type: 'bar',
- barWidth: 25,
+ barWidth: 18,
barGap: '-100%',
- tooltip: { show: false },
- itemStyle: {
- color: {
- type: 'linear',
- x: 1,
- y: 0,
- x2: 0,
- y2: 0,
- colorStops: [
- { offset: 0, color: 'rgba(255, 218, 220)' },
- { offset: 0.07, color: 'rgba(255, 86, 118)' },
- { offset: 1, color: 'rgba(255, 86, 118, 0)' },
- ],
- global: false,
- },
- barBorderRadius: [0, 20, 20, 0],
- },
data: dataNum.value,
},
])
@@ -289,10 +237,10 @@
const fetchCustomerRanking = () => {
customerContributionRanking({ type: dateType.value })
.then((res) => {
- if (res.code === 200 && Array.isArray(res.data)) {
- dataArr.value = res.data.map(item => ({
+ if (res.code === 200 && Array.isArray(res.data) && res.data.length > 0) {
+ dataArr.value = res.data.map((item) => ({
NAME: item.customerName,
- NUM: item.totalAmount
+ NUM: Number(item.totalAmount) || 0,
}))
} else {
setMockData(dateType.value)
@@ -315,7 +263,9 @@
<style scoped>
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 449px;
diff --git a/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue b/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
index 21696fa..2b514f5 100644
--- a/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
+++ b/src/views/reportAnalysis/dataDashboard/components/basic/right-top.vue
@@ -13,7 +13,7 @@
:tooltip="tooltip"
:xAxis="xAxis"
:yAxis="yAxis"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI.text } }"
style="height: 360px"
/>
</div>
@@ -26,30 +26,19 @@
import PanelHeader from '../PanelHeader.vue'
import DateTypeSwitch from '../DateTypeSwitch.vue'
import { supplierPurchaseRanking } from '@/api/viewIndex.js'
+import { BI, withTooltip } from '../../theme.js'
const chartStyle = {
width: '100%',
height: '100%',
}
-const radio1 = ref(1)
-
-// 椋炴満鍥炬爣 SVG path
-const aircraft =
- 'path://M107.000,71.000 C104.936,71.000 102.665,70.806 100.273,70.467 C94.592,76.922 86.275,81.000 77.000,81.000 C70.794,81.000 65.020,79.170 60.172,76.029 C66.952,74.165 72.647,69.714 76.173,63.817 C69.821,61.362 64.063,58.593 60.000,56.039 L60.000,52.813 C70.456,53.950 80.723,55.000 83.000,55.000 C88.972,55.000 93.000,53.723 93.000,50.000 C93.000,47.071 89.222,45.000 83.000,45.000 C80.723,45.000 70.456,46.050 60.000,47.187 L60.000,43.989 C64.057,41.431 69.807,38.644 76.168,36.173 C72.641,30.281 66.948,25.834 60.172,23.971 C65.020,20.830 70.794,19.000 77.000,19.000 C86.270,19.000 94.584,23.074 100.265,29.524 C102.647,29.191 104.918,29.000 107.000,29.000 C129.644,29.000 148.000,50.000 148.000,50.000 C148.000,50.000 129.644,71.000 107.000,71.000 ZM113.000,38.000 C106.373,38.000 101.000,43.373 101.000,50.000 C101.000,56.627 106.373,62.000 113.000,62.000 C119.627,62.000 125.000,56.627 125.000,50.000 C125.000,43.373 119.627,38.000 113.000,38.000 ZM113.000,56.000 C109.686,56.000 107.000,53.314 107.000,50.000 C107.000,46.686 109.686,44.000 113.000,44.000 C116.314,44.000 119.000,46.686 119.000,50.000 C119.000,53.314 116.314,56.000 113.000,56.000 ZM110.500,19.000 C109.567,19.000 108.763,18.483 108.334,17.726 C100.231,9.857 89.187,5.000 77.000,5.000 C64.813,5.000 53.769,9.857 45.666,17.726 C45.237,18.483 44.433,19.000 43.500,19.000 C42.119,19.000 41.000,17.881 41.000,16.500 C41.000,15.847 41.256,15.259 41.665,14.813 L41.575,14.718 C50.629,5.628 63.156,-0.000 77.000,-0.000 C90.844,-0.000 103.371,5.628 112.425,14.718 L112.335,14.813 C112.744,15.259 113.000,15.847 113.000,16.500 C113.000,17.881 111.881,19.000 110.500,19.000 ZM53.000,49.484 C61.406,48.626 77.810,47.000 81.345,47.000 C87.353,47.000 91.000,48.243 91.000,50.000 C91.000,52.234 87.111,53.000 81.345,53.000 C77.810,53.000 61.406,51.374 53.000,50.516 L53.000,49.484 ZM53.000,47.000 L9.000,50.000 L53.000,53.000 L53.000,56.000 L-0.000,50.000 L53.000,44.000 L53.000,47.000 ZM43.500,81.000 C44.433,81.000 45.237,81.517 45.666,82.274 C53.769,90.143 64.813,95.000 77.000,95.000 C89.187,95.000 100.231,90.143 108.334,82.274 C108.763,81.517 109.567,81.000 110.500,81.000 C111.881,81.000 113.000,82.119 113.000,83.500 C113.000,84.153 112.744,84.741 112.335,85.187 L112.425,85.282 C103.371,94.372 90.844,100.000 77.000,100.000 C63.156,100.000 50.629,94.372 41.575,85.282 L41.665,85.187 C41.256,84.741 41.000,84.153 41.000,83.500 C41.000,82.119 42.119,81.000 43.500,81.000 Z'
-
-// 棰滆壊閰嶇疆
-const color = {
- 0: '#ff5676',
- 1: '#ffd83e',
- 2: '#fbff94',
- 3: '#7daeff',
-}
+const radio1 = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
// 鍘熷鏁版嵁
const dataArr = ref([])
-// 鎺掑簭鍚庣殑鏁版嵁
+// 鎺掑簭鍚庣殑鏁版嵁锛堝崌搴忥細鍊艰秺澶ц秺闈犱笂锛�
const dataArray = computed(() => {
return [...dataArr.value].sort((a, b) => a.NUM - b.NUM)
})
@@ -64,66 +53,43 @@
return dataArray.value.map((v) => v.NAME)
})
+// 鎺掑悕閰嶈壊锛歍op1 閲戣壊锛孴op2-3 浜摑锛屽叾浣欐殫钃�
+const rankColor = (rank) => {
+ if (rank === 1) return '#F0B14A'
+ if (rank <= 3) return BI.accentBright
+ return '#2C4A9E'
+}
+
+const getRank = (i) => dataArray.value.length - i
+
// y杞存暟鎹紙鏁板�硷紝甯︽牱寮忥級
const dataNum = computed(() => {
return dataArray.value.map((v, i) => {
- const index = dataArray.value.length - i - 1
- const isTop3 = index < 3
-
+ const rank = getRank(i)
return {
value: Number(v.NUM),
itemStyle: {
- color: {
- type: 'linear',
- x: 1,
- y: 0,
- x2: 0,
- y2: 0,
- colorStops: [
- {
- offset: 0,
- color: isTop3 ? '#ffdae1' : '#ecf3ff',
- },
- {
- offset: 0.07,
- color: isTop3 ? color[index] : color[3],
- },
- {
- offset: 1,
- color: isTop3
- ? 'rgba(255, 86, 118, .1)'
- : 'rgba(125,174,255, .1)',
- },
- ],
- global: false,
- },
- barBorderRadius: [0, 20, 20, 0],
+ color: rankColor(rank),
+ barBorderRadius: [0, 10, 10, 0],
},
- symbol: isTop3 ? aircraft : 'none',
- symbolPosition: 'end',
- symbolSize: [30, 25],
- symbolOffset: [35, 0],
}
})
})
-// 鑳屾櫙鏁版嵁
+// 鑳屾櫙杞ㄩ亾鏁版嵁
const bgData = computed(() => {
- const maxValue = Math.max(...dataNum.value.map((v) => v.value))
- return dataNum.value.map(() => maxValue + 200)
+ const maxValue = dataNum.value.length ? Math.max(...dataNum.value.map((v) => v.value)) : 0
+ return dataNum.value.map(() => maxValue * 1.08 || 1)
})
// tooltip
-const tooltip = computed(() => {
- return {
- trigger: 'axis',
- textStyle: { fontSize: '100%' },
- formatter: function (params) {
- let result = params[0].axisValueLabel + '<br/>'
- result += `<div>${params[0].marker}${params[0].value}鍏�</div>`
- return result
- },
- }
+const tooltip = withTooltip({
+ trigger: 'axis',
+ formatter: function (params) {
+ let result = params[0].axisValueLabel + '<br/>'
+ result += `<div>${params[0].marker}${Number(params[0].value || 0).toLocaleString('zh-CN')}鍏�</div>`
+ return result
+ },
})
// grid
@@ -167,7 +133,7 @@
a: {
width: 120,
fontSize: 14,
- color: '#fff',
+ color: BI.textStrong,
padding: [5, 4, 5, 0],
align: 'right',
},
@@ -183,54 +149,17 @@
axisLabel: {
formatter: (params, index) => {
const value = typeof params === 'object' ? params.value : params
- const percent = ((value / total.value) * 100).toFixed(0)
- const rank = dataArray.value.length - index
- const isTop3 = rank < 4
-
- return `{a${isTop3 ? rank : ''}|${percent} }{b${isTop3 ? rank : ''}|%}`
+ const percent = total.value ? ((value / total.value) * 100).toFixed(0) : 0
+ const rank = getRank(index)
+ return `{a${rank}|${percent} }{b${rank}|%}`
},
- rich: {
- a: {
- fontSize: 18,
- color: '#98bfff',
- verticalAlign: 'bottom',
- },
- a1: {
- fontSize: 18,
- color: '#ff7f97',
- verticalAlign: 'bottom',
- },
- a2: {
- fontSize: 18,
- color: '#ffce64',
- verticalAlign: 'bottom',
- },
- a3: {
- fontSize: 18,
- color: '#e8ed66',
- verticalAlign: 'bottom',
- },
- b: {
- fontSize: 12,
- color: '#98bfff',
- verticalAlign: 'bottom',
- },
- b1: {
- fontSize: 12,
- color: '#ff7f97',
- verticalAlign: 'bottom',
- },
- b2: {
- fontSize: 12,
- color: '#ffce64',
- verticalAlign: 'bottom',
- },
- b3: {
- fontSize: 12,
- color: '#e8ed66',
- verticalAlign: 'bottom',
- },
- },
+ rich: dataNum.value.reduce((rich, _, i) => {
+ const rank = getRank(i)
+ const color = rank === 1 ? '#F0B14A' : rank <= 3 ? BI.accentBright : BI.text
+ rich[`a${rank}`] = { fontSize: 16, color, verticalAlign: 'bottom' }
+ rich[`b${rank}`] = { fontSize: 12, color, verticalAlign: 'bottom' }
+ return rich
+ }, {}),
},
axisLine: { show: false },
axisTick: { show: false },
@@ -239,71 +168,41 @@
]
})
-// series
-const series = computed(() => {
- return [
- {
- z: 6,
- type: 'pictorialBar',
- data: dataNum.value,
+// series锛氳儗鏅建閬� + 瀹炶壊鏉�
+const series = computed(() => [
+ {
+ name: '鑳屾櫙',
+ z: 5,
+ type: 'bar',
+ barWidth: 18,
+ tooltip: { show: false },
+ itemStyle: {
+ color: 'rgba(143, 163, 200, 0.12)',
+ barBorderRadius: [0, 10, 10, 0],
},
- {
- z: 6,
- type: 'bar',
- barWidth: 25,
- tooltip: { show: false },
- itemStyle: {
- color: 'rgba(255,255,255,.1)',
- barBorderRadius: [0, 20, 20, 0],
- },
- data: bgData.value,
- },
- {
- type: 'bar',
- barWidth: 25,
- barGap: '-100%',
- tooltip: { show: false },
- itemStyle: {
- color: {
- type: 'linear',
- x: 1,
- y: 0,
- x2: 0,
- y2: 0,
- colorStops: [
- {
- offset: 0,
- color: 'rgba(255, 218, 220)',
- },
- {
- offset: 0.07,
- color: 'rgba(255, 86, 118)',
- },
- {
- offset: 1,
- color: 'rgba(255, 86, 118, 0)',
- },
- ],
- global: false,
- },
- barBorderRadius: [0, 20, 20, 0],
- },
- data: dataNum.value,
- },
- ]
-})
+ data: bgData.value,
+ },
+ {
+ name: '閲戦',
+ z: 6,
+ type: 'bar',
+ barWidth: 18,
+ barGap: '-100%',
+ data: dataNum.value,
+ },
+])
// 渚涘簲鍟嗛噰璐帓鍚�
const fetchSupplierRanking = () => {
supplierPurchaseRanking({ type: radio1.value })
.then((res) => {
- if (res.code === 200 && Array.isArray(res.data)) {
- dataArr.value = res.data.map(item => ({
+ if (res.code === 200 && Array.isArray(res.data) && res.data.length > 0) {
+ dataArr.value = res.data.map((item) => ({
NAME: item.supplierName,
- NUM: item.totalAmount
+ NUM: Number(item.totalAmount) || 0,
}))
} else {
- // 濡傛灉娌℃湁鏁版嵁锛屼娇鐢ㄦā鎷熸暟鎹�
+ // 鎺ュ彛鏃犳暟鎹椂缁欎竴缁勬ā鎷熶緵搴斿晢锛屼繚璇乁I鍙敤
dataArr.value = [
{ NAME: '渚涘簲鍟咥', NUM: 102 },
{ NAME: '渚涘簲鍟咮', NUM: 122 },
@@ -315,7 +214,6 @@
})
.catch((error) => {
console.error('鑾峰彇渚涘簲鍟嗛噰璐帓鍚嶅け璐�:', error)
- // 浣跨敤妯℃嫙鏁版嵁
dataArr.value = [
{ NAME: '渚涘簲鍟咥', NUM: 102 },
{ NAME: '渚涘簲鍟咮', NUM: 122 },
@@ -327,7 +225,7 @@
}
// 澶勭悊鏃ユ湡绫诲瀷鍒囨崲
-const handleDateTypeChange = (value) => {
+const handleDateTypeChange = () => {
fetchSupplierRanking()
}
@@ -338,7 +236,9 @@
<style scoped>
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
height: 449px;
@@ -348,11 +248,5 @@
display: flex;
justify-content: flex-end;
margin-bottom: 16px;
-}
-
-.section-title {
- font-weight: 500;
- font-size: 16px;
- color: #d9ecff;
}
</style>
diff --git a/src/views/reportAnalysis/dataDashboard/index.vue b/src/views/reportAnalysis/dataDashboard/index.vue
index 67a700f..2f3b8f2 100644
--- a/src/views/reportAnalysis/dataDashboard/index.vue
+++ b/src/views/reportAnalysis/dataDashboard/index.vue
@@ -13,7 +13,9 @@
<!-- 椤堕儴鏍囬鏍� -->
<div class="dashboard-header">
+ <div class="header-line"></div>
<div class="factory-name">鍩虹鏁版嵁</div>
+ <div class="header-line header-line-right"></div>
</div>
<!-- 涓昏鍐呭鍖哄煙 -->
@@ -163,7 +165,7 @@
position: relative;
width: 100%;
/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
+height: calc(100vh - 100px);
display: flex;
align-items: center;
justify-content: center;
@@ -176,11 +178,34 @@
position: relative;
width: 1920px;
height: 1080px;
-background-image: url("@/assets/BI/backImage@2x.png");
-background-size: cover;
-background-position: center;
-background-repeat: no-repeat;
transform-origin: center center;
+
+/* 澶у睆涓婚浠ょ墝锛屽瓙缁勪欢閫氳繃 CSS 鍙橀噺缁ф壙 */
+--bi-panel-bg: rgba(20, 32, 66, 0.55);
+--bi-panel-border: #1e3160;
+--bi-text: #8fa3c8;
+--bi-text-strong: #eaf1ff;
+--bi-accent: #2c51d9;
+--bi-accent-bright: #4a6cf0;
+--bi-up: #34c77b;
+--bi-down: #ff6b6b;
+
+background:
+ radial-gradient(1200px 600px at 50% -10%, rgba(44, 81, 217, 0.18) 0%, transparent 60%),
+ linear-gradient(180deg, #0a1229 0%, #0d1834 55%, #0a1430 100%);
+}
+
+/* 鏋佹贰缃戞牸绾圭悊锛屾浛浠fā鏉挎劅搴曞浘 */
+.data-dashboard::before {
+content: '';
+position: absolute;
+inset: 0;
+pointer-events: none;
+background-image:
+ linear-gradient(rgba(74, 108, 240, 0.045) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(74, 108, 240, 0.045) 1px, transparent 1px);
+background-size: 48px 48px;
+mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.35) 100%);
}
/* 鍏ㄥ睆鐘舵�佺殑鏍峰紡 - 浣滅敤浜巗cale-container */
@@ -218,20 +243,42 @@
position: relative;
z-index: 1;
height: 86px;
-background-image: url("@/assets/BI/biaoti.png");
-background-size: cover;
-background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
+gap: 28px;
+}
+
+/* 鏍囬涓や晶娓愰殣鍒嗛殧绾� */
+.header-line {
+width: 420px;
+height: 1px;
+background: linear-gradient(90deg, transparent 0%, rgba(74, 108, 240, 0.5) 100%);
+}
+
+.header-line-right {
+background: linear-gradient(270deg, transparent 0%, rgba(74, 108, 240, 0.5) 100%);
}
.factory-name {
font-weight: 600;
-font-size: 52px;
-color: #FFFFFF;
-top: 16px;
+font-size: 30px;
+letter-spacing: 4px;
+color: var(--bi-text-strong);
+position: relative;
+white-space: nowrap;
+}
+
+.factory-name::after {
+content: '';
position: absolute;
+left: 50%;
+bottom: -8px;
+transform: translateX(-50%);
+width: 48px;
+height: 2px;
+background: var(--bi-accent-bright);
+border-radius: 1px;
}
.fullscreen-btn {
@@ -240,21 +287,21 @@
left: 20px;
width: 40px;
height: 40px;
-background: rgba(0, 20, 60, 0.8);
-border: 1px solid rgba(0, 212, 255, 0.3);
+background: var(--bi-panel-bg);
+border: 1px solid var(--bi-panel-border);
border-radius: 6px;
-color: #00d4ff;
+color: var(--bi-text);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
-transition: all 0.3s;
+transition: color 0.2s ease, border-color 0.2s ease;
z-index: 10000;
}
.fullscreen-btn:hover {
-background: rgba(0, 30, 90, 0.9);
-border-color: rgba(0, 212, 255, 0.5);
+color: var(--bi-text-strong);
+border-color: var(--bi-accent-bright);
}
.dashboard-content {
diff --git a/src/views/reportAnalysis/dataDashboard/index0.vue b/src/views/reportAnalysis/dataDashboard/index0.vue
index 5c318c8..a854c3c 100644
--- a/src/views/reportAnalysis/dataDashboard/index0.vue
+++ b/src/views/reportAnalysis/dataDashboard/index0.vue
@@ -1473,7 +1473,7 @@
position: relative;
width: 100%;
/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
- height: calc(100vh - 84px);
+ height: calc(100vh - 100px);
display: flex;
align-items: center;
justify-content: center;
diff --git a/src/views/reportAnalysis/dataDashboard/theme.js b/src/views/reportAnalysis/dataDashboard/theme.js
new file mode 100644
index 0000000..0e332e0
--- /dev/null
+++ b/src/views/reportAnalysis/dataDashboard/theme.js
@@ -0,0 +1,38 @@
+// 鍩虹鏁版嵁澶у睆缁熶竴瑙嗚浠ょ墝锛堜笌 PSI 澶у睆鍚屼竴濂楀搧鐗岃摑鍏嬪埗娣辫壊涓婚锛�
+export const BI = {
+ // 鍥捐〃搴忓垪鑹诧紙鍏嬪埗涓撲笟锛屾浛浠i潚钃濊崸鍏夛級
+ series: ['#4A6CF0', '#35C6D6', '#8E7CE6', '#F0B14A', '#2FB8A8', '#E86D6D'],
+ text: '#8FA3C8',
+ textStrong: '#EAF1FF',
+ panelBg: 'rgba(20, 32, 66, 0.55)',
+ panelBorder: '#1E3160',
+ sliceBorder: '#0D1834',
+ accent: '#2C51D9',
+ accentBright: '#4A6CF0',
+ up: '#34C77B',
+ down: '#FF6B6B',
+}
+
+export const baseChartOptions = {
+ backgroundColor: 'transparent',
+ textStyle: { color: BI.text },
+}
+
+// 娣辫壊缁熶竴 tooltip 鏍峰紡锛屼繚鐣欏悇缁勪欢鑷畾涔� formatter
+export const withTooltip = (tooltip = {}) => ({
+ backgroundColor: 'rgba(13, 24, 52, 0.92)',
+ borderColor: BI.panelBorder,
+ borderWidth: 1,
+ textStyle: { color: BI.textStrong, fontSize: 12 },
+ ...tooltip,
+})
+
+// 鏁板�兼牸寮忓寲锛氬ぇ鏁板瓧鎶樼畻涓恒�屼竾銆嶏紝绌哄�煎厹搴�
+export const formatNumber = (val, unit = '') => {
+ const num = Number(val)
+ if (!Number.isFinite(num)) return `0${unit}`
+ if (Math.abs(num) >= 10000) {
+ return `${(num / 10000).toLocaleString('zh-CN', { maximumFractionDigits: 2 })}涓�${unit}`
+ }
+ return `${num.toLocaleString('zh-CN', { maximumFractionDigits: 2 })}${unit}`
+}
diff --git a/src/views/reportAnalysis/financialAnalysis/index.vue b/src/views/reportAnalysis/financialAnalysis/index.vue
index 8d90a59..7cada56 100644
--- a/src/views/reportAnalysis/financialAnalysis/index.vue
+++ b/src/views/reportAnalysis/financialAnalysis/index.vue
@@ -163,7 +163,7 @@
position: relative;
width: 100%;
/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
+height: calc(100vh - 100px);
display: flex;
align-items: center;
justify-content: center;
diff --git a/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue
index 0498824..df4f9e2 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/CarouselCards.vue
@@ -1,14 +1,15 @@
<template>
<div class="carousel-cards">
- <button
+ <button
v-if="canScrollLeft"
class="nav-button nav-button-left"
+ aria-label="鍚戝乏婊氬姩"
@click="scrollLeftFn"
>
- <img src="@/assets/BI/jiantou.png" alt="宸︾澶�" />
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
</button>
- <div
- class="cards-container"
+ <div
+ class="cards-container"
:style="{ '--visible-count': visibleCount }"
ref="cardsContainerRef"
>
@@ -17,7 +18,6 @@
:key="index"
class="card-item"
>
- <div v-if="item.icon" class="card-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
<div class="card-title">
<div class="card-label">{{ item.label }}</div>
<div class="card-value">
@@ -30,12 +30,13 @@
</div>
</div>
</div>
- <button
+ <button
v-if="canScrollRight"
class="nav-button nav-button-right"
+ aria-label="鍚戝彸婊氬姩"
@click="scrollRightFn"
>
- <img src="@/assets/BI/jiantou.png" alt="鍙崇澶�" />
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
</template>
@@ -48,9 +49,9 @@
type: Array,
default: () => [],
validator: (value) => {
- return value.every(item =>
- item && typeof item.label !== 'undefined' &&
- typeof item.value !== 'undefined' &&
+ return value.every(item =>
+ item && typeof item.label !== 'undefined' &&
+ typeof item.value !== 'undefined' &&
typeof item.unit !== 'undefined'
)
}
@@ -79,7 +80,7 @@
const updateScrollState = () => {
const container = cardsContainerRef.value
if (!container) return
-
+
currentScrollLeft.value = container.scrollLeft
maxScrollLeft.value = container.scrollWidth - container.clientWidth
}
@@ -88,19 +89,19 @@
const scrollLeftFn = () => {
const container = cardsContainerRef.value
if (!container) return
-
+
const scrollItems = Array.from(container.querySelectorAll('.card-item'))
if (scrollItems.length === 0) return
-
+
const itemWidth = scrollItems[0]?.offsetWidth || 0
const gap = 12
const scrollDistance = itemWidth + gap
-
+
container.scrollBy({
left: -scrollDistance,
behavior: 'smooth'
})
-
+
// 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
setTimeout(() => {
updateScrollState()
@@ -111,19 +112,19 @@
const scrollRightFn = () => {
const container = cardsContainerRef.value
if (!container) return
-
+
const scrollItems = Array.from(container.querySelectorAll('.card-item'))
if (scrollItems.length === 0) return
-
+
const itemWidth = scrollItems[0]?.offsetWidth || 0
const gap = 12
const scrollDistance = itemWidth + gap
-
+
container.scrollBy({
left: scrollDistance,
behavior: 'smooth'
})
-
+
// 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
setTimeout(() => {
updateScrollState()
@@ -188,40 +189,30 @@
transform: translateY(-50%);
width: 32px;
height: 32px;
- background: rgba(26, 88, 176, 0.6);
- border: 1px solid rgba(26, 88, 176, 0.8);
+ background: var(--bi-surface-2);
+ border: 1px solid var(--bi-border);
border-radius: 50%;
+ color: var(--bi-text-secondary);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
- transition: all 0.3s ease;
+ transition: color 0.2s ease, border-color 0.2s ease;
padding: 0;
}
.nav-button:hover {
- background: rgba(26, 88, 176, 0.8);
- transform: translateY(-50%) scale(1.1);
+ color: var(--bi-accent);
+ border-color: var(--bi-accent);
}
.nav-button-left {
left: -16px;
}
-.nav-button-left img {
- width: 16px;
- height: 16px;
- transform: rotate(180deg);
-}
-
.nav-button-right {
right: -16px;
-}
-
-.nav-button-right img {
- width: 16px;
- height: 16px;
}
.card-item {
@@ -229,24 +220,15 @@
min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
display: flex;
align-items: center;
- background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
- border-radius: 8px 8px 8px 8px;
+ background: var(--bi-surface-2);
+ border: 1px solid var(--bi-border);
+ border-radius: 8px;
padding: 12px 16px;
- transition: all 0.3s ease;
+ transition: border-color 0.2s ease;
}
.card-item:hover {
- transform: translateY(-2px);
-}
-
-.card-icon {
- width: 80px;
- height: 60px;
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- flex-shrink: 0;
- margin-right: 12px;
+ border-color: rgba(91, 140, 255, 0.45);
}
.card-title {
@@ -254,12 +236,13 @@
align-items: flex-start;
flex-direction: column;
flex: 1;
+ min-width: 0;
}
.card-label {
font-weight: 400;
font-size: 14px;
- color: #FFFFFF;
+ color: var(--bi-text-secondary);
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
@@ -280,7 +263,7 @@
gap: 6px;
font-weight: 400;
font-size: 12px;
- color: rgba(255, 255, 255, 0.85);
+ color: var(--bi-text-secondary);
}
.rate-label {
@@ -292,15 +275,17 @@
}
.value-number {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- line-height: 1;
+ font-weight: 600;
+ font-size: 22px;
+ color: var(--bi-text);
+ line-height: 1.1;
+ font-variant-numeric: tabular-nums;
+ font-feature-settings: 'tnum';
}
.value-unit {
- font-size: 14px;
- color: #FFFFFF;
+ font-size: 12px;
+ color: var(--bi-text-secondary);
font-weight: 400;
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/DateTypeSwitch.vue b/src/views/reportAnalysis/productionAnalysis/components/DateTypeSwitch.vue
index 0c57b25..f6b50d8 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/DateTypeSwitch.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/DateTypeSwitch.vue
@@ -16,7 +16,7 @@
const props = defineProps({
modelValue: {
type: Number,
- default: 1, // 榛樿閫変腑"鍛�"
+ default: 3, // 榛樿閫変腑"瀛e害"
},
})
@@ -46,13 +46,13 @@
/* 鏈�変腑鐘舵�佺殑鏍峰紡 */
.date-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
+ background-color: var(--bi-surface);
+ color: var(--bi-text-secondary);
+ border-color: var(--bi-border);
border-radius: 0;
padding: 6px 20px;
font-size: 14px;
- transition: all 0.3s;
+ transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
/* 绗竴涓寜閽乏渚у渾瑙� */
@@ -69,26 +69,26 @@
/* 鎸夐挳涔嬮棿鐨勫垎闅旂嚎 */
.date-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
+ border-right: 1px solid var(--bi-border);
}
-/* 閫変腑鐘舵�佺殑鏍峰紡 */
+/* 閫変腑鐘舵�佺殑鏍峰紡锛氬搧鐗岃摑绾壊 */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ background-color: var(--bi-primary);
color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
+ border-color: var(--bi-primary);
box-shadow: none;
}
/* 鎮仠鏁堟灉 */
.date-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
+ color: var(--bi-text);
+ border-color: var(--bi-accent);
}
/* 閫変腑鐘舵�佹偓鍋� */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ background-color: var(--app-primary-hover);
color: #ffffff;
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/PanelHeader.vue b/src/views/reportAnalysis/productionAnalysis/components/PanelHeader.vue
index 313f1df..b5e5cf8 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/PanelHeader.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/PanelHeader.vue
@@ -1,7 +1,5 @@
<template>
- <div class="panel-header">
- <span class="panel-title">{{ title }}</span>
- </div>
+ <div class="panel-header bi-title-bar">{{ title }}</div>
</template>
<script setup>
@@ -13,21 +11,3 @@
}
})
</script>
-
-<style scoped>
-.panel-header {
- background-image: url("@/assets/BI/kehuhetongback@2x.png");
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
-}
-
-.panel-title {
- width: 100%;
- font-weight: 500;
- font-size: 16px;
- color: #D9ECFF;
- padding-left: 46px;
- line-height: 36px;
-}
-</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/ProductTypeSwitch.vue b/src/views/reportAnalysis/productionAnalysis/components/ProductTypeSwitch.vue
index 87cde44..58c8866 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/ProductTypeSwitch.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/ProductTypeSwitch.vue
@@ -43,13 +43,13 @@
}
.product-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
+ background-color: var(--bi-surface);
+ color: var(--bi-text-secondary);
+ border-color: var(--bi-border);
border-radius: 0;
padding: 6px 20px;
font-size: 14px;
- transition: all 0.3s;
+ transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.product-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
@@ -63,23 +63,23 @@
}
.product-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
+ border-right: 1px solid var(--bi-border);
}
.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ background-color: var(--bi-primary);
color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
+ border-color: var(--bi-primary);
box-shadow: none;
}
.product-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
+ color: var(--bi-text);
+ border-color: var(--bi-accent);
}
.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ background-color: var(--app-primary-hover);
color: #ffffff;
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
index 46a870a..216af11 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
@@ -2,7 +2,7 @@
<div>
<PanelHeader title="鐢熶骇璁㈠崟瀹屾垚杩涘害" />
<div class="main-panel">
- <div class="panel-item-customers">
+ <div class="panel-item-customers bi-panel">
<CarouselCards :items="cardItems" :visible-count="4" />
<div
class="progress-table-container"
@@ -55,6 +55,7 @@
import { getProgressStatistics } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
+import { BI_COLORS } from '../theme.js'
const progressTableRef = ref(null)
const progressTableScrollTimer = ref(null)
@@ -92,10 +93,10 @@
// 鏍规嵁杩涘害鐧惧垎姣旇繑鍥為鑹�
const progressColor = (percentage) => {
const p = percentage || 0
- if (p < 30) return '#f56c6c'
- if (p < 50) return '#e6a23c'
- if (p < 80) return '#409eff'
- return '#67c23a'
+ if (p < 30) return BI_COLORS.red
+ if (p < 50) return BI_COLORS.amber
+ if (p < 80) return BI_COLORS.primary
+ return BI_COLORS.green
}
const setRowRef = (el, index) => {
@@ -248,17 +249,19 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
padding: 18px;
width: 100%;
height: 428px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
}
.progress-table-container {
- height: 320px;
+ flex: 1;
+ min-height: 0;
overflow-y: auto;
overflow-x: hidden;
- margin-top: 10px;
scrollbar-width: none;
-ms-overflow-style: none;
}
@@ -270,7 +273,7 @@
.progress-table {
width: 100%;
border-collapse: collapse;
- color: #b8c8e0;
+ color: var(--bi-text-secondary);
font-size: 12px;
table-layout: fixed;
}
@@ -278,7 +281,7 @@
.progress-table thead {
position: sticky;
top: 0;
- background-color: rgba(26, 88, 176, 0.9);
+ background-color: var(--bi-surface-2);
z-index: 10;
}
@@ -286,8 +289,8 @@
padding: 8px 6px;
text-align: left;
font-weight: 500;
- border-bottom: 1px solid rgba(184, 200, 224, 0.3);
- color: #b8c8e0;
+ border-bottom: 1px solid var(--bi-divider);
+ color: var(--bi-text);
font-size: 12px;
white-space: nowrap;
overflow: hidden;
@@ -320,7 +323,7 @@
.progress-table td {
padding: 8px 6px;
- border-bottom: 1px solid rgba(184, 200, 224, 0.1);
+ border-bottom: 1px solid var(--bi-divider);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -329,7 +332,7 @@
}
.progress-table tbody tr:hover {
- background-color: rgba(184, 200, 224, 0.1);
+ background-color: rgba(159, 176, 207, 0.06);
}
.progress-table tbody tr.row-under-header {
@@ -341,11 +344,11 @@
}
.progress-table :deep(.el-progress-bar__outer) {
- background-color: rgba(184, 200, 224, 0.2);
+ background-color: rgba(159, 176, 207, 0.15);
}
.progress-table :deep(.el-progress__text) {
- color: #b8c8e0;
+ color: var(--bi-text-secondary);
font-size: 11px;
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-center.vue b/src/views/reportAnalysis/productionAnalysis/components/center-center.vue
index 96bcada..c67eb2a 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-center.vue
@@ -1,15 +1,8 @@
<template>
<div>
- <!-- 璁惧缁熻 -->
- <div class="equipment-stats">
- <div class="equipment-header">
- <img
- src="@/assets/BI/shujutongjiicon@2x.png"
- alt="鍥炬爣"
- class="equipment-icon"
- />
- <span class="equipment-title">鎶曞叆浜у嚭鍒嗘瀽</span>
- </div>
+ <!-- 鎶曞叆浜у嚭鍒嗘瀽 -->
+ <PanelHeader title="鎶曞叆浜у嚭鍒嗘瀽" />
+ <div class="bi-panel chart-box">
<Echarts
ref="chart"
:chartStyle="chartStyle"
@@ -19,7 +12,7 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI_COLORS.text } }"
style="height: 260px"
/>
</div>
@@ -28,9 +21,10 @@
<script setup>
import { ref, onMounted } from 'vue'
-import * as echarts from 'echarts'
import Echarts from '@/components/Echarts/echarts.vue'
+import PanelHeader from './PanelHeader.vue'
import { inputOutputAnalysis } from '@/api/viewIndex.js'
+import { BI_COLORS, BI_TOOLTIP, areaGradient } from '../theme.js'
const chartStyle = { width: '100%', height: '100%' }
const grid = {
@@ -48,10 +42,10 @@
itemGap: 24,
itemWidth: 12,
itemHeight: 12,
- textStyle: { color: '#B8C8E0', fontSize: 14 },
+ textStyle: { color: BI_COLORS.text, fontSize: 14 },
data: [
- { name: '浜у嚭', itemStyle: { color: 'rgba(11, 137, 254, 1)' } },
- { name: '鎶曞叆', itemStyle: { color: 'rgba(11, 249, 254, 1)' } },
+ { name: '浜у嚭', itemStyle: { color: BI_COLORS.primary } },
+ { name: '鎶曞叆', itemStyle: { color: BI_COLORS.teal } },
],
}
@@ -60,9 +54,9 @@
type: 'category',
data: [],
axisTick: { show: false },
- axisLine: { show: false, lineStyle: { color: 'rgba(184, 200, 224, 0.3)' } },
- axisLabel: { color: '#B8C8E0', fontSize: 12 },
- splitLine: { show: false, lineStyle: { type: 'dashed', color: 'rgba(184, 200, 224, 0.2)' } },
+ axisLine: { lineStyle: { color: BI_COLORS.axisLine } },
+ axisLabel: { color: BI_COLORS.text, fontSize: 12 },
+ splitLine: { show: false },
},
])
@@ -70,11 +64,11 @@
{
type: 'value',
name: '鍗曚綅: 浠�',
- nameTextStyle: { color: '#B8C8E0', fontSize: 12, padding: [0, 0, 0, 0] },
+ nameTextStyle: { color: BI_COLORS.text, fontSize: 12 },
axisLine: { show: false },
axisTick: { show: false },
- axisLabel: { color: '#B8C8E0', fontSize: 12 },
- splitLine: { lineStyle: { color: '#B8C8E0' } },
+ axisLabel: { color: BI_COLORS.text, fontSize: 12 },
+ splitLine: { lineStyle: { color: BI_COLORS.splitLine } },
},
]
@@ -82,36 +76,26 @@
{
name: '浜у嚭',
type: 'line',
- smooth: false,
+ smooth: true,
showSymbol: true,
symbol: 'circle',
- symbolSize: 8,
- lineStyle: { color: 'rgba(11, 137, 254, 1)', width: 2 },
- itemStyle: { color: 'rgba(11, 137, 254, 1)', borderWidth: 0 },
- areaStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(11, 137, 254, 0.40)' },
- { offset: 1, color: 'rgba(11, 137, 254, 0.05)' },
- ]),
- },
+ symbolSize: 6,
+ lineStyle: { color: BI_COLORS.primary, width: 2 },
+ itemStyle: { color: BI_COLORS.primary, borderWidth: 0 },
+ areaStyle: { color: areaGradient(BI_COLORS.primary) },
data: [],
emphasis: { focus: 'series' },
},
{
name: '鎶曞叆',
type: 'line',
- smooth: false,
+ smooth: true,
showSymbol: true,
symbol: 'circle',
- symbolSize: 8,
- lineStyle: { color: 'rgba(11, 249, 254, 1)', width: 2 },
- itemStyle: { color: 'rgba(11, 249, 254, 1)', borderWidth: 0 },
- areaStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(11, 249, 254, 0.5)' },
- { offset: 1, color: 'rgba(11, 249, 254, 0.05)' },
- ]),
- },
+ symbolSize: 6,
+ lineStyle: { color: BI_COLORS.teal, width: 2 },
+ itemStyle: { color: BI_COLORS.teal, borderWidth: 0 },
+ areaStyle: { color: areaGradient(BI_COLORS.teal) },
data: [],
emphasis: { focus: 'series' },
},
@@ -120,8 +104,7 @@
const tooltip = {
trigger: 'axis',
axisPointer: { type: 'line' },
- borderWidth: 1,
- textStyle: { fontSize: 12 },
+ ...BI_TOOLTIP,
formatter(params) {
let result = params[0].axisValue + '<br/>'
params.forEach((item) => {
@@ -152,42 +135,8 @@
</script>
<style scoped>
-.equipment-stats {
- border: 1px solid #1a58b0;
- padding: 0 18px 18px;
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-
-.equipment-header {
- font-weight: 500;
- font-size: 21px;
- display: flex;
- border-bottom: 1px solid;
- border-image: linear-gradient(
- 270deg,
- rgba(0, 126, 255, 0) 0%,
- rgba(0, 126, 255, 0.4549) 35%,
- #007eff 78%,
- #007eff 100%
- )
- 1;
- padding-bottom: 2px;
-}
-
-.equipment-title {
- font-weight: 500;
- font-size: 18px;
- background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- line-height: 50px;
-}
-
-.equipment-icon {
- width: 50px;
- height: 50px;
+.chart-box {
+ padding: 12px 18px 18px;
+ margin-top: 4px;
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-top.vue b/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
index 7201828..40603ef 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
@@ -5,12 +5,11 @@
<div
v-for="item in statItems"
:key="item.name"
- class="stat-card"
+ class="stat-card bi-panel"
>
- <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
<div class="card-content">
<span class="card-label">{{ item.name }}</span>
- <span class="card-value">{{ item.value }}</span>
+ <span class="card-value bi-num">{{ item.value }}</span>
<div class="card-compare" :class="compareClass(Number(item.rate))">
<span>鍚屾瘮</span>
<span class="compare-value">{{ formatPercent(item.rate) }}</span>
@@ -18,6 +17,7 @@
</div>
</div>
</div>
+ <div v-if="!statItems.length" class="stats-empty bi-panel">鏆傛棤鏁版嵁</div>
</div>
</div>
@@ -67,38 +67,41 @@
flex: 1;
display: flex;
align-items: center;
- background-image: url('@/assets/BI/border@2x.png');
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ position: relative;
height: 142px;
}
-.card-icon {
- width: 100px;
- height: 100px;
- margin: 20px 20px 0 10px;
+/* 宸︿晶鍝佺墝钃濈珫鏉★紝鍛煎簲棣栭〉闈㈡澘鏍囬 */
+.stat-card::before {
+ content: '';
+ position: absolute;
+ left: 16px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 3px;
+ height: 64px;
+ border-radius: 2px;
+ background: var(--bi-accent);
}
.card-content {
display: flex;
flex-direction: column;
gap: 10px;
+ padding-left: 36px;
}
.card-value {
- font-weight: 500;
+ font-weight: 600;
font-size: 40px;
- background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
+ line-height: 1.1;
+ color: var(--bi-text);
}
.card-label {
font-weight: 400;
font-size: 16px;
- color: rgba(208, 231, 255, 0.7);
+ color: var(--bi-text-secondary);
}
.card-compare {
@@ -106,7 +109,7 @@
align-items: center;
gap: 6px;
font-size: 15px;
- color: #d0e7ff;
+ color: var(--bi-text-secondary);
}
.card-compare > span:first-child {
@@ -121,17 +124,26 @@
.compare-icon {
font-size: 14px;
position: relative;
- top: -1px; /* 杞诲井涓婄Щ锛岃绠ご涓庢枃瀛楀瀭鐩村眳涓榻� */
+ top: -1px;
}
.compare-up .compare-value,
.compare-up .compare-icon {
- color: #00c853;
+ color: var(--bi-success);
}
.compare-down .compare-value,
.compare-down .compare-icon {
- color: #ff5252;
+ color: var(--bi-danger);
}
+.stats-empty {
+ flex: 1;
+ height: 142px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--bi-text-tertiary);
+ font-size: 14px;
+}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
index 33f431d..c5e2904 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-bottom.vue
@@ -1,7 +1,7 @@
<template>
<div>
<PanelHeader title="鍦ㄥ埗鍝佺粺璁″垎鏋�" />
- <div class="main-panel panel-item-customers">
+ <div class="main-panel panel-item-customers bi-panel">
<CarouselCards :items="cardItems" :visible-count="3" />
<div class="chart-wrapper">
<Echarts
@@ -13,7 +13,7 @@
:tooltip="tooltip"
:xAxis="workInProcessXAxis"
:yAxis="workInProcessYAxis"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI_COLORS.text } }"
style="height: 100%"
/>
</div>
@@ -27,6 +27,7 @@
import PanelHeader from './PanelHeader.vue'
import CarouselCards from './CarouselCards.vue'
import { getWorkInProcessTurnover } from '@/api/viewIndex.js'
+import { BI_COLORS, BI_TOOLTIP, barGradient } from '../theme.js'
// 鍦ㄥ埗鍝佸懆杞粺璁″璞�
const workInProcessStatistics = ref({
@@ -53,10 +54,11 @@
const tooltip = {
trigger: 'axis',
axisPointer: { type: 'shadow' },
+ ...BI_TOOLTIP,
formatter: function (params) {
let result = params[0].axisValueLabel + '<br/>'
params.forEach((item) => {
- result += `<div style="color: #B8C8E0">${item.marker} ${item.seriesName}: ${item.value}</div>`
+ result += `<div>${item.marker} ${item.seriesName}: ${item.value}</div>`
})
return result
},
@@ -67,46 +69,38 @@
{
type: 'category',
axisTick: { show: false },
- axisLabel: { color: '#B8C8E0' },
+ axisLabel: { color: BI_COLORS.text },
data: [],
},
])
const workInProcessYAxis = [
{
type: 'value',
- axisLabel: { color: '#B8C8E0' },
+ axisLabel: { color: BI_COLORS.text },
name: '',
+ splitLine: { lineStyle: { color: BI_COLORS.splitLine } },
},
]
const workInProcessBarLegend = {
show: false,
- textStyle: { color: '#B8C8E0' },
+ textStyle: { color: BI_COLORS.text },
data: [],
}
const workInProcessBarSeries = ref([
{
name: '鍦ㄥ埗鍝佹暟閲�',
type: 'bar',
- barWidth: 25,
+ barWidth: 20,
barGap: 0,
emphasis: { focus: 'series' },
itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- { offset: 1, color: 'rgba(0,164,237,0)' },
- { offset: 0, color: '#4EE4FF' },
- ],
- },
+ borderRadius: [3, 3, 0, 0],
+ color: barGradient(BI_COLORS.primary),
},
label: {
show: true,
position: 'top',
- color: '#B8C8E0',
+ color: BI_COLORS.text,
},
data: [],
},
@@ -156,7 +150,6 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
padding: 18px;
width: 100%;
height: 449px;
diff --git a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
index 932def8..fea816a 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/left-top.vue
@@ -1,39 +1,43 @@
<template>
<div>
<PanelHeader title="宸ュ簭浜у嚭鍒嗘瀽" />
- <div class="main-panel panel-item-customers">
+ <div class="panel-item-customers bi-panel">
<div class="filters-row">
<DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
</div>
- <div class="pie-chart-wrapper" ref="pieWrapperRef">
- <div class="pie-background" ref="pieBackgroundRef"></div>
- <Echarts
- ref="echartsRef"
- :chartStyle="chartStyle"
- :legend="pieLegend"
- :series="pieSeries"
- :tooltip="pieTooltip"
- :color="pieColors"
- :options="pieOptions"
- style="height: 320px"
- />
+ <div class="pie-chart-wrapper">
+ <template v-if="hasData">
+ <Echarts
+ ref="echartsRef"
+ :chartStyle="chartStyle"
+ :legend="pieLegend"
+ :series="pieSeries"
+ :tooltip="pieTooltip"
+ :color="pieColors"
+ :options="pieOptions"
+ style="height: 320px"
+ />
+ <!-- 鐜績姹囨�伙紝鏇夸唬鍘熺帿鐟板浘杈规瑁呴グ鍥� -->
+ <div class="pie-center">
+ <span class="center-value">{{ totalValue }}</span>
+ <span class="center-label">鎬讳骇鍑�(浠�)</span>
+ </div>
+ </template>
+ <div v-else class="pie-empty">鏆傛棤鏁版嵁</div>
</div>
</div>
</div>
</template>
<script setup>
-import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
+import { ref, onMounted, computed } from 'vue'
import { processOutputAnalysis } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
-import DateTypeSwitch from '@/views/reportAnalysis/financialAnalysis/components/DateTypeSwitch.vue'
-import { useChartBackground } from '@/hooks/useChartBackground.js'
+import DateTypeSwitch from './DateTypeSwitch.vue'
+import { BI_COLORS, BI_PIE_PALETTE, BI_TOOLTIP } from '../theme.js'
-const pieWrapperRef = ref(null)
-const pieBackgroundRef = ref(null)
-
-const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
+const dateType = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
function array2obj(array, key) {
const resObj = {}
@@ -50,9 +54,17 @@
const echartsRef = ref(null)
const pieDatas = ref([])
-const pieColors = ['#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF', '#43e8fc', '#27EBE7']
+const pieColors = BI_PIE_PALETTE
const pieObjData = computed(() => array2obj(pieDatas.value, 'name'))
+
+// 鐜績鎬婚噺锛堢┖鏁版嵁鍏滃簳 0锛�
+const totalValue = computed(() =>
+ pieDatas.value.reduce((sum, d) => sum + (Number(d.value) || 0), 0)
+)
+
+// 鍏ㄩ浂鏁版嵁涓嶆覆鏌撴墖鍖猴細minAngle 浼氭妸 0 鍊间篃鎾戞垚鏁寸幆锛岄�犳垚璇
+const hasData = computed(() => pieDatas.value.some((d) => Number(d.value) > 0))
const pieLegend = computed(() => {
const data = pieDatas.value.map((d, idx) => ({
@@ -78,24 +90,25 @@
textStyle: {
rich: {
value: {
- color: '#43e8fc',
+ color: BI_COLORS.textStrong,
fontSize: 14,
fontWeight: 600,
padding: [0, 0, 0, 10],
},
unit: {
- color: '#82baff',
+ color: BI_COLORS.text,
fontSize: 12,
fontWeight: 600,
padding: [0, 10, 0, 0],
},
percent: {
- color: '#43e8fc',
+ color: BI_COLORS.primary,
fontSize: 14,
fontWeight: 600,
padding: [0, 0, 0, 0],
},
title: {
+ color: BI_COLORS.text,
fontSize: 12,
padding: [0, 0, 0, 0],
},
@@ -107,47 +120,34 @@
const pieTooltip = {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}浠� ({d}%)',
+ ...BI_TOOLTIP,
}
const pieSeries = computed(() => [
{
name: '宸ュ簭浜у嚭鍒嗘瀽',
type: 'pie',
- radius: '60%',
- center: ['25%', '50%'],
+ radius: ['46%', '68%'],
+ center: ['27%', '50%'],
itemStyle: {
- borderColor: '#0a1c3a',
+ borderColor: BI_COLORS.surface,
borderWidth: 2,
+ borderRadius: 4,
},
label: {
show: false
},
minAngle: 15,
- data: pieDatas.value,
+ data: hasData.value ? pieDatas.value : [],
animationType: 'scale',
- animationEasing: 'elasticOut',
- animationDelay: function () {
- return Math.random() * 200
- },
+ animationEasing: 'cubicOut',
},
])
const pieOptions = {
backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
+ textStyle: { color: BI_COLORS.text },
}
-
-// 浣跨敤灏佽鐨勮儗鏅綅缃皟鏁存柟娉�
-// 鍥捐〃涓績鏄� ['25%', '50%']锛岃儗鏅渶瑕佸榻愬埌杩欎釜浣嶇疆
-const { init: initBackground, cleanup: cleanupBackground } = useChartBackground({
- wrapperRef: pieWrapperRef,
- backgroundRef: pieBackgroundRef,
- left: '25%', // 鍥捐〃涓績 X 鏄� 25%
- top: '50%', // 鍥捐〃涓績 Y 鏄� 50%
- offsetX: '-51.5%', // X 杞村亸绉�
- offsetY: '-50%', // Y 杞村亸绉�
- watchData: pieDatas // 鐩戝惉鏁版嵁鍙樺寲锛岃嚜鍔ㄨ皟鏁翠綅缃�
-})
const fetchData = () => {
processOutputAnalysis({ dateType: dateType.value })
@@ -172,26 +172,17 @@
onMounted(() => {
fetchData()
- initBackground()
-})
-
-onBeforeUnmount(() => {
- cleanupBackground()
})
</script>
<style scoped>
-.main-panel {
- display: flex;
- flex-direction: column;
- gap: 20px;
-}
-
.panel-item-customers {
- border: 1px solid #1a58b0;
padding: 18px;
width: 100%;
height: 449px;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
}
.filters-row {
@@ -206,22 +197,41 @@
position: relative;
width: 100%;
height: 320px;
- background: transparent;
}
-.pie-background {
+/* 鐜績鏁板瓧涓庨ゼ鍥惧渾蹇冿紙27%,50%锛夊榻� */
+.pie-center {
position: absolute;
- width: 310px;
- height: 310px;
- background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- z-index: 1;
- pointer-events: none;
- /* 浣嶇疆鐢� JS 鍔ㄦ�佽缃紝榛樿灞呬腑 */
- left: 25%;
+ left: 27%;
top: 50%;
- transform: translate(-51.5%, -50%);
+ transform: translate(-50%, -50%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+ pointer-events: none;
+}
+
+.center-value {
+ font-weight: 600;
+ font-size: 26px;
+ line-height: 1;
+ color: var(--bi-text);
+ font-variant-numeric: tabular-nums;
+}
+
+.center-label {
+ font-size: 12px;
+ color: var(--bi-text-secondary);
+}
+
+.pie-empty {
+ position: absolute;
+ left: 27%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 14px;
+ color: var(--bi-text-tertiary);
+ pointer-events: none;
}
</style>
diff --git a/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
index 28de03b..0562fba 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/right-bottom.vue
@@ -1,7 +1,7 @@
<template>
<div>
<PanelHeader title="鐢熶骇鏍哥畻鍒嗘瀽" />
- <div class="main-panel panel-item-customers">
+ <div class="main-panel panel-item-customers bi-panel">
<div class="filters-row">
<DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
</div>
@@ -14,7 +14,7 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI_COLORS.text } }"
style="height: 260px"
/>
</div>
@@ -27,19 +27,20 @@
import PanelHeader from './PanelHeader.vue'
import DateTypeSwitch from './DateTypeSwitch.vue'
import Echarts from '@/components/Echarts/echarts.vue'
+import { BI_COLORS, BI_TOOLTIP, barGradient } from '../theme.js'
-const dateType = ref(1)
+const dateType = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
const chartStyle = {
width: '100%',
- height: '140%',
+ height: '100%',
}
const grid = { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true }
const barLegend = {
show: true,
- textStyle: { color: '#B8C8E0' },
+ textStyle: { color: BI_COLORS.text },
data: ['瀹屾垚鏁伴噺', '宸ヨ祫閲戦', '鍚堟牸鐜�'],
}
@@ -51,17 +52,8 @@
barGap: '40%',
emphasis: { focus: 'series' },
itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- { offset: 1, color: 'rgba(0, 164, 237, 0)' },
- { offset: 0, color: 'rgba(78, 228, 255, 1)' },
- ],
- },
+ borderRadius: [3, 3, 0, 0],
+ color: barGradient(BI_COLORS.primary),
},
data: [],
},
@@ -72,17 +64,8 @@
barWidth: 20,
emphasis: { focus: 'series' },
itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- { offset: 1, color: 'rgba(83, 126, 245, 0.19)' },
- { offset: 0, color: 'rgba(144, 97, 248, 1)' },
- ],
- },
+ borderRadius: [3, 3, 0, 0],
+ color: barGradient(BI_COLORS.violet),
},
data: [],
},
@@ -90,42 +73,44 @@
name: '鍚堟牸鐜�',
type: 'line',
yAxisIndex: 1,
+ smooth: true,
showSymbol: true,
symbol: 'circle',
- symbolSize: 8,
- lineStyle: { color: 'rgba(90, 216, 166, 1)', width: 2 },
- itemStyle: { color: 'rgba(90, 216, 166, 1)' },
+ symbolSize: 6,
+ lineStyle: { color: BI_COLORS.amber, width: 2 },
+ itemStyle: { color: BI_COLORS.amber },
data: [],
emphasis: { focus: 'series' },
},
])
const xAxis1 = ref([
- { type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] },
+ { type: 'category', axisTick: { show: false }, axisLine: { lineStyle: { color: BI_COLORS.axisLine } }, axisLabel: { color: BI_COLORS.text }, data: [] },
])
const yAxis1 = [
{
type: 'value',
name: '鏁伴噺/閲戦',
- axisLabel: { color: '#B8C8E0' },
- nameTextStyle: { color: '#B8C8E0' },
- // splitLine: { lineStyle: { color: 'rgba(184, 200, 224, 0.2)' } },
+ axisLabel: { color: BI_COLORS.text },
+ nameTextStyle: { color: BI_COLORS.text },
+ splitLine: { lineStyle: { color: BI_COLORS.splitLine } },
},
{
type: 'value',
name: '鍚堟牸鐜�(%)',
min: 0,
max: 100,
- axisLabel: { color: '#B8C8E0', formatter: '{value}%' },
- nameTextStyle: { color: '#B8C8E0' },
- splitLine: { lineStyle: { color: 'rgba(184, 200, 224, 0.2)' } },
+ axisLabel: { color: BI_COLORS.text, formatter: '{value}%' },
+ nameTextStyle: { color: BI_COLORS.text },
+ splitLine: { show: false },
},
]
const tooltip = {
trigger: 'axis',
axisPointer: { type: 'cross' },
+ ...BI_TOOLTIP,
formatter(params) {
let result = params[0].axisValueLabel + '<br/>'
params.forEach((item) => {
@@ -178,7 +163,6 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
padding: 18px;
width: 100%;
height: 449px;
diff --git a/src/views/reportAnalysis/productionAnalysis/components/right-top.vue b/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
index d3a9eb9..e5a0c8a 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/right-top.vue
@@ -1,7 +1,7 @@
<template>
<div>
<PanelHeader title="宸ュ崟鎵ц鏁堢巼鍒嗘瀽" />
- <div class="main-panel panel-item-customers">
+ <div class="main-panel panel-item-customers bi-panel">
<div class="filters-row">
<DateTypeSwitch v-model="dateType" @change="handleDateTypeChange" />
</div>
@@ -14,7 +14,7 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
+ :options="{ backgroundColor: 'transparent', textStyle: { color: BI_COLORS.text } }"
style="height: 260px"
/>
</div>
@@ -27,19 +27,20 @@
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
import DateTypeSwitch from './DateTypeSwitch.vue'
+import { BI_COLORS, BI_TOOLTIP, barGradient } from '../theme.js'
-const dateType = ref(1) // 1=鍛� 2=鏈� 3=瀛e害
+const dateType = ref(3) // 1=鍛� 2=鏈� 3=瀛e害锛岄粯璁ゅ搴�
const chartStyle = {
width: '100%',
- height: '140%',
+ height: '100%',
}
const grid = { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true }
const barLegend = {
show: true,
- textStyle: { color: '#B8C8E0' },
+ textStyle: { color: BI_COLORS.text },
data: ['寮�宸�', '瀹屾垚', '鑹搧鐜�'],
}
@@ -51,17 +52,8 @@
barGap: '40%',
emphasis: { focus: 'series' },
itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- { offset: 1, color: 'rgba(0, 164, 237, 0)' },
- { offset: 0, color: 'rgba(78, 228, 255, 1)' },
- ],
- },
+ borderRadius: [3, 3, 0, 0],
+ color: barGradient(BI_COLORS.primary),
},
data: [],
},
@@ -72,17 +64,8 @@
barWidth: 20,
emphasis: { focus: 'series' },
itemStyle: {
- color: {
- type: 'linear',
- x: 0,
- y: 0,
- x2: 0,
- y2: 1,
- colorStops: [
- { offset: 1, color: 'rgba(83, 126, 245, 0.19)' },
- { offset: 0, color: 'rgba(144, 97, 248, 1)' },
- ],
- },
+ borderRadius: [3, 3, 0, 0],
+ color: barGradient(BI_COLORS.green),
},
data: [],
},
@@ -90,11 +73,12 @@
name: '鑹搧鐜�',
type: 'line',
yAxisIndex: 1,
+ smooth: true,
showSymbol: true,
symbol: 'circle',
- symbolSize: 8,
- lineStyle: { color: 'rgba(90, 216, 166, 1)', width: 2 },
- itemStyle: { color: 'rgba(90, 216, 166, 1)' },
+ symbolSize: 6,
+ lineStyle: { color: BI_COLORS.amber, width: 2 },
+ itemStyle: { color: BI_COLORS.amber },
data: [],
emphasis: { focus: 'series' },
},
@@ -103,6 +87,7 @@
const tooltip = {
trigger: 'axis',
axisPointer: { type: 'cross' },
+ ...BI_TOOLTIP,
formatter(params) {
let result = params[0].axisValueLabel + '<br/>'
params.forEach((item) => {
@@ -114,19 +99,25 @@
}
const xAxis1 = ref([
- { type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] },
+ { type: 'category', axisTick: { show: false }, axisLine: { lineStyle: { color: BI_COLORS.axisLine } }, axisLabel: { color: BI_COLORS.text }, data: [] },
])
const yAxis1 = [
- { type: 'value', name: '浠�', axisLabel: { color: '#B8C8E0' }, nameTextStyle: { color: '#B8C8E0' } },
+ {
+ type: 'value',
+ name: '浠�',
+ axisLabel: { color: BI_COLORS.text },
+ nameTextStyle: { color: BI_COLORS.text },
+ splitLine: { lineStyle: { color: BI_COLORS.splitLine } },
+ },
{
type: 'value',
name: '鑹搧鐜�(%)',
min: 0,
max: 100,
- axisLabel: { color: '#B8C8E0', formatter: '{value}%' },
- nameTextStyle: { color: '#B8C8E0' },
- splitLine: { lineStyle: { color: 'rgba(184, 200, 224, 0.2)' } },
+ axisLabel: { color: BI_COLORS.text, formatter: '{value}%' },
+ nameTextStyle: { color: BI_COLORS.text },
+ splitLine: { show: false },
},
]
@@ -173,7 +164,6 @@
}
.panel-item-customers {
- border: 1px solid #1a58b0;
padding: 18px;
width: 100%;
height: 449px;
diff --git a/src/views/reportAnalysis/productionAnalysis/index.vue b/src/views/reportAnalysis/productionAnalysis/index.vue
index e179150..5796b39 100644
--- a/src/views/reportAnalysis/productionAnalysis/index.vue
+++ b/src/views/reportAnalysis/productionAnalysis/index.vue
@@ -1,6 +1,6 @@
<template>
<div class="scale-container">
- <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
+ <div class="data-dashboard dashboard-dark" :style="{ transform: `scale(${scaleRatio})` }">
<!-- 鍏ㄥ睆鎸夐挳 - 绉诲姩鍒板乏涓婅 -->
<button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
<svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -164,11 +164,11 @@
position: relative;
width: 100%;
/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
+height: calc(100vh - 100px);
display: flex;
align-items: center;
justify-content: center;
-background-color: #000;
+background-color: #0a1120;
overflow: hidden;
}
@@ -177,10 +177,9 @@
position: relative;
width: 1920px;
height: 1080px;
-background-image: url("@/assets/BI/backImage@2x.png");
-background-size: cover;
-background-position: center;
-background-repeat: no-repeat;
+background:
+ radial-gradient(1200px 520px at 50% -10%, rgba(44, 81, 217, 0.16), transparent 60%),
+ linear-gradient(180deg, var(--bi-bg-to) 0%, var(--bi-bg-from) 100%);
transform-origin: center center;
}
@@ -219,20 +218,35 @@
position: relative;
z-index: 1;
height: 86px;
-background-image: url("@/assets/BI/biaoti.png");
-background-size: cover;
-background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
+gap: 28px;
+padding: 0 80px;
+}
+
+/* 鏍囬涓や晶瀵圭О娓愰殣缁嗙嚎锛屾浛浠e浘鐗囨爣棰樻爮 */
+.dashboard-header::before,
+.dashboard-header::after {
+content: '';
+flex: 1;
+height: 1px;
+}
+
+.dashboard-header::before {
+background: linear-gradient(90deg, transparent, rgba(91, 140, 255, 0.4));
+}
+
+.dashboard-header::after {
+background: linear-gradient(90deg, rgba(91, 140, 255, 0.4), transparent);
}
.factory-name {
font-weight: 600;
-font-size: 52px;
-color: #FFFFFF;
-top: 16px;
-position: absolute;
+font-size: 36px;
+letter-spacing: 6px;
+color: var(--bi-text);
+white-space: nowrap;
}
.fullscreen-btn {
@@ -241,21 +255,21 @@
left: 20px;
width: 40px;
height: 40px;
-background: rgba(0, 20, 60, 0.8);
-border: 1px solid rgba(0, 212, 255, 0.3);
-border-radius: 6px;
-color: #00d4ff;
+background: var(--bi-surface);
+border: 1px solid var(--bi-border);
+border-radius: var(--app-radius-sm);
+color: var(--bi-accent);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
-transition: all 0.3s;
+transition: background-color 0.2s ease, border-color 0.2s ease;
z-index: 10000;
}
.fullscreen-btn:hover {
-background: rgba(0, 30, 90, 0.9);
-border-color: rgba(0, 212, 255, 0.5);
+background: var(--bi-surface-2);
+border-color: var(--bi-accent);
}
.dashboard-content {
diff --git a/src/views/reportAnalysis/productionAnalysis/theme.js b/src/views/reportAnalysis/productionAnalysis/theme.js
new file mode 100644
index 0000000..dd5b422
--- /dev/null
+++ b/src/views/reportAnalysis/productionAnalysis/theme.js
@@ -0,0 +1,71 @@
+// 鏆楄壊澶у睆 ECharts 涓婚甯搁噺锛欳SS 鍙橀噺鏃犳硶鐢ㄤ簬 JS 閰嶇疆锛岀粺涓�鍦ㄦ鏀跺彛
+// 鑹插�间负 tokens.scss 璇箟鑹插湪娣卞簳涓婄殑鎻愪寒鐗堬紝涓� dashboard-dark.scss 淇濇寔瀵瑰簲
+
+const toRgb = (hex) => [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16))
+
+export const BI_COLORS = {
+ primary: '#5b8cff',
+ green: '#2dbd85',
+ amber: '#f5a524',
+ red: '#f2635b',
+ violet: '#8b7cf8',
+ teal: '#2bc8c8',
+ text: '#9fb0cf',
+ textStrong: '#e8eefc',
+ surface: '#111c33',
+ axisLine: 'rgba(159, 176, 207, 0.25)',
+ splitLine: 'rgba(159, 176, 207, 0.12)',
+}
+
+// 棣栭〉 PIE_COLORS 鐨勬殫搴曟彁浜増
+export const BI_PIE_PALETTE = [
+ BI_COLORS.primary,
+ BI_COLORS.green,
+ BI_COLORS.amber,
+ BI_COLORS.red,
+ BI_COLORS.violet,
+ BI_COLORS.teal,
+ '#ff8a3d',
+ '#4a6cf0',
+ '#b37feb',
+ '#1377ef',
+]
+
+export const BI_TOOLTIP = {
+ backgroundColor: '#16233d',
+ borderColor: '#1f2f4d',
+ borderWidth: 1,
+ textStyle: { color: BI_COLORS.textStrong, fontSize: 12 },
+}
+
+// 鏌遍《瀹炶壊銆佹煴搴曟笎闅愶紝鏇夸唬鍘熼潚鑹茶崸鍏夋笎鍙�
+export const barGradient = (hex) => {
+ const rgb = toRgb(hex).join(',')
+ return {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 0, color: `rgba(${rgb}, 0.9)` },
+ { offset: 1, color: `rgba(${rgb}, 0.05)` },
+ ],
+ }
+}
+
+// 鎶樼嚎闈㈢Н娓愬彉锛岄厤鏂瑰榻愰椤� HomeInvoiceChart锛�0.22 鈫� 0.02锛�
+export const areaGradient = (hex) => {
+ const rgb = toRgb(hex).join(',')
+ return {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 0, color: `rgba(${rgb}, 0.22)` },
+ { offset: 1, color: `rgba(${rgb}, 0.02)` },
+ ],
+ }
+}
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue b/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
deleted file mode 100644
index 0498824..0000000
--- a/src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
+++ /dev/null
@@ -1,306 +0,0 @@
-<template>
- <div class="carousel-cards">
- <button
- v-if="canScrollLeft"
- class="nav-button nav-button-left"
- @click="scrollLeftFn"
- >
- <img src="@/assets/BI/jiantou.png" alt="宸︾澶�" />
- </button>
- <div
- class="cards-container"
- :style="{ '--visible-count': visibleCount }"
- ref="cardsContainerRef"
- >
- <div
- v-for="(item, index) in items"
- :key="index"
- class="card-item"
- >
- <div v-if="item.icon" class="card-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
- <div class="card-title">
- <div class="card-label">{{ item.label }}</div>
- <div class="card-value">
- <span class="value-number">{{ item.value }}</span>
- <span class="value-unit">{{ item.unit }}</span>
- </div>
- <div v-if="item.rate ?? item.ratio ?? item.percent" class="card-rate">
- <span class="rate-value">{{ item.rate ?? item.ratio ?? item.percent }}%</span>
- </div>
- </div>
- </div>
- </div>
- <button
- v-if="canScrollRight"
- class="nav-button nav-button-right"
- @click="scrollRightFn"
- >
- <img src="@/assets/BI/jiantou.png" alt="鍙崇澶�" />
- </button>
- </div>
-</template>
-
-<script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
-
-const props = defineProps({
- items: {
- type: Array,
- default: () => [],
- validator: (value) => {
- return value.every(item =>
- item && typeof item.label !== 'undefined' &&
- typeof item.value !== 'undefined' &&
- typeof item.unit !== 'undefined'
- )
- }
- },
- visibleCount: {
- type: Number,
- default: 3
- }
-})
-
-const cardsContainerRef = ref(null)
-const currentScrollLeft = ref(0)
-const maxScrollLeft = ref(0)
-
-// 妫�鏌ユ槸鍚﹀彲浠ュ悜宸︽粴鍔�
-const canScrollLeft = computed(() => {
- return currentScrollLeft.value > 0
-})
-
-// 妫�鏌ユ槸鍚﹀彲浠ュ悜鍙虫粴鍔�
-const canScrollRight = computed(() => {
- return currentScrollLeft.value < maxScrollLeft.value
-})
-
-// 鏇存柊婊氬姩鐘舵��
-const updateScrollState = () => {
- const container = cardsContainerRef.value
- if (!container) return
-
- currentScrollLeft.value = container.scrollLeft
- maxScrollLeft.value = container.scrollWidth - container.clientWidth
-}
-
-// 鍚戝乏婊氬姩
-const scrollLeftFn = () => {
- const container = cardsContainerRef.value
- if (!container) return
-
- const scrollItems = Array.from(container.querySelectorAll('.card-item'))
- if (scrollItems.length === 0) return
-
- const itemWidth = scrollItems[0]?.offsetWidth || 0
- const gap = 12
- const scrollDistance = itemWidth + gap
-
- container.scrollBy({
- left: -scrollDistance,
- behavior: 'smooth'
- })
-
- // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
- setTimeout(() => {
- updateScrollState()
- }, 300)
-}
-
-// 鍚戝彸婊氬姩
-const scrollRightFn = () => {
- const container = cardsContainerRef.value
- if (!container) return
-
- const scrollItems = Array.from(container.querySelectorAll('.card-item'))
- if (scrollItems.length === 0) return
-
- const itemWidth = scrollItems[0]?.offsetWidth || 0
- const gap = 12
- const scrollDistance = itemWidth + gap
-
- container.scrollBy({
- left: scrollDistance,
- behavior: 'smooth'
- })
-
- // 寤惰繜鏇存柊鐘舵�侊紝绛夊緟婊氬姩鍔ㄧ敾瀹屾垚
- setTimeout(() => {
- updateScrollState()
- }, 300)
-}
-
-// 鐩戝惉 items 鍙樺寲锛屾洿鏂版粴鍔ㄧ姸鎬�
-watch(() => props.items, () => {
- nextTick(() => {
- updateScrollState()
- })
-}, { deep: true })
-
-onMounted(() => {
- nextTick(() => {
- updateScrollState()
- // 鐩戝惉婊氬姩浜嬩欢
- const container = cardsContainerRef.value
- if (container) {
- container.addEventListener('scroll', updateScrollState)
- }
- })
-})
-
-onBeforeUnmount(() => {
- // 娓呯悊婊氬姩浜嬩欢鐩戝惉鍣�
- const container = cardsContainerRef.value
- if (container) {
- container.removeEventListener('scroll', updateScrollState)
- }
-})
-</script>
-
-<style scoped>
-.carousel-cards {
- width: 100%;
- overflow: hidden;
- position: relative;
- display: flex;
- align-items: center;
-}
-
-.cards-container {
- display: flex;
- gap: 12px;
- width: 100%;
- overflow-x: auto;
- overflow-y: hidden;
- scrollbar-width: none; /* Firefox */
- -ms-overflow-style: none; /* IE and Edge */
- padding-bottom: 4px;
- scroll-behavior: smooth;
-}
-
-.cards-container::-webkit-scrollbar {
- display: none; /* Chrome, Safari, Opera */
-}
-
-.nav-button {
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- width: 32px;
- height: 32px;
- background: rgba(26, 88, 176, 0.6);
- border: 1px solid rgba(26, 88, 176, 0.8);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- z-index: 10;
- transition: all 0.3s ease;
- padding: 0;
-}
-
-.nav-button:hover {
- background: rgba(26, 88, 176, 0.8);
- transform: translateY(-50%) scale(1.1);
-}
-
-.nav-button-left {
- left: -16px;
-}
-
-.nav-button-left img {
- width: 16px;
- height: 16px;
- transform: rotate(180deg);
-}
-
-.nav-button-right {
- right: -16px;
-}
-
-.nav-button-right img {
- width: 16px;
- height: 16px;
-}
-
-.card-item {
- flex: 0 0 calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
- min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
- display: flex;
- align-items: center;
- background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
- border-radius: 8px 8px 8px 8px;
- padding: 12px 16px;
- transition: all 0.3s ease;
-}
-
-.card-item:hover {
- transform: translateY(-2px);
-}
-
-.card-icon {
- width: 80px;
- height: 60px;
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- flex-shrink: 0;
- margin-right: 12px;
-}
-
-.card-title {
- display: flex;
- align-items: flex-start;
- flex-direction: column;
- flex: 1;
-}
-
-.card-label {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- margin-bottom: 4px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 100%;
-}
-
-.card-value {
- display: flex;
- align-items: baseline;
- gap: 4px;
-}
-
-.card-rate {
- margin-top: 4px;
- display: flex;
- align-items: center;
- gap: 6px;
- font-weight: 400;
- font-size: 12px;
- color: rgba(255, 255, 255, 0.85);
-}
-
-.rate-label {
- opacity: 0.85;
-}
-
-.rate-value {
- font-weight: 500;
-}
-
-.value-number {
- font-weight: 400;
- font-size: 14px;
- color: #FFFFFF;
- line-height: 1;
-}
-
-.value-unit {
- font-size: 14px;
- color: #FFFFFF;
- font-weight: 400;
-}
-</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue b/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue
index 0c57b25..7f58702 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/DateTypeSwitch.vue
@@ -46,49 +46,49 @@
/* 鏈�変腑鐘舵�佺殑鏍峰紡 */
.date-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
+ background-color: rgba(20, 32, 66, 0.55);
+ color: #8fa3c8;
+ border-color: #1e3160;
border-radius: 0;
padding: 6px 20px;
font-size: 14px;
- transition: all 0.3s;
+ transition: color 0.2s ease, background-color 0.2s ease;
}
/* 绗竴涓寜閽乏渚у渾瑙� */
.date-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px;
}
/* 鏈�鍚庝竴涓寜閽彸渚у渾瑙� */
.date-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px;
}
/* 鎸夐挳涔嬮棿鐨勫垎闅旂嚎 */
.date-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
+ border-right: 1px solid #1e3160;
}
-/* 閫変腑鐘舵�佺殑鏍峰紡 */
+/* 閫変腑鐘舵�佺殑鏍峰紡锛氬搧鐗岃摑瀹炶壊 */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
+ background: #2c51d9;
color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
+ border-color: #2c51d9;
box-shadow: none;
}
/* 鎮仠鏁堟灉 */
.date-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
+ color: #eaf1ff;
}
/* 閫変腑鐘舵�佹偓鍋� */
.date-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
+ background: #4a6cf0;
+ border-color: #4a6cf0;
color: #ffffff;
}
</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue b/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue
index 313f1df..8a81c1c 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/PanelHeader.vue
@@ -16,18 +16,27 @@
<style scoped>
.panel-header {
- background-image: url("@/assets/BI/kehuhetongback@2x.png");
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 0 4px;
+ height: 36px;
+}
+
+/* 鍝佺墝钃濈粏绔栨潯锛屼笌鍩虹鏁版嵁澶у睆鏍囬鏍峰紡涓�鑷� */
+.panel-header::before {
+ content: '';
+ width: 3px;
+ height: 14px;
+ border-radius: 2px;
+ background: linear-gradient(180deg, var(--bi-accent-bright, #4a6cf0) 0%, var(--bi-accent, #2c51d9) 100%);
+ flex-shrink: 0;
}
.panel-title {
- width: 100%;
font-weight: 500;
font-size: 16px;
- color: #D9ECFF;
- padding-left: 46px;
- line-height: 36px;
+ color: var(--bi-text-strong, #eaf1ff);
+ letter-spacing: 1px;
}
</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue b/src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue
deleted file mode 100644
index 87cde44..0000000
--- a/src/views/reportAnalysis/qualityAnalysis/components/ProductTypeSwitch.vue
+++ /dev/null
@@ -1,85 +0,0 @@
-<template>
- <el-radio-group
- v-model="currentValue"
- class="product-type-switch"
- @change="handleChange"
- >
- <el-radio-button :label="1">鍘熸潗鏂�</el-radio-button>
- <el-radio-button :label="3">鍗婃垚鍝�</el-radio-button>
- <el-radio-button :label="2">鎴愬搧</el-radio-button>
- </el-radio-group>
-</template>
-
-<script setup>
-import { ref, watch } from 'vue'
-
-const props = defineProps({
- modelValue: {
- type: Number,
- default: 1, // 榛樿閫変腑"鍘熸潗鏂�"
- },
-})
-
-const emit = defineEmits(['update:modelValue', 'change'])
-
-const currentValue = ref(props.modelValue)
-
-watch(
- () => props.modelValue,
- (newVal) => {
- currentValue.value = newVal
- }
-)
-
-const handleChange = (value) => {
- emit('update:modelValue', value)
- emit('change', value)
-}
-</script>
-
-<style scoped>
-.product-type-switch {
- display: inline-flex;
-}
-
-.product-type-switch :deep(.el-radio-button__inner) {
- background-color: rgba(26, 88, 176, 0.3);
- color: rgba(184, 200, 224, 0.8);
- border-color: rgba(255, 255, 255, 0.2);
- border-radius: 0;
- padding: 6px 20px;
- font-size: 14px;
- transition: all 0.3s;
-}
-
-.product-type-switch :deep(.el-radio-button:first-child .el-radio-button__inner) {
- border-top-left-radius: 4px;
- border-bottom-left-radius: 4px;
-}
-
-.product-type-switch :deep(.el-radio-button:last-child .el-radio-button__inner) {
- border-top-right-radius: 4px;
- border-bottom-right-radius: 4px;
-}
-
-.product-type-switch :deep(.el-radio-button:not(:last-child) .el-radio-button__inner) {
- border-right: 1px solid rgba(255, 255, 255, 0.2);
-}
-
-.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
- background: linear-gradient(180deg, #3378ff 0%, #00a4ed 100%);
- color: #ffffff;
- border-color: rgba(51, 120, 255, 0.8);
- box-shadow: none;
-}
-
-.product-type-switch :deep(.el-radio-button__inner:hover) {
- color: rgba(184, 200, 224, 1);
- border-color: rgba(255, 255, 255, 0.3);
-}
-
-.product-type-switch :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner:hover) {
- background: linear-gradient(180deg, #4e8aff 0%, #4ee4ff 100%);
- color: #ffffff;
-}
-</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue
index fe875d0..529ac0b 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/center-bottom.vue
@@ -1,5 +1,5 @@
<template>
- <div>
+ <div class="panel-root">
<div class="chart-header">
<div class="chart-header-title">
<PanelHeader title="瀹屾垚妫�楠屾暟" />
@@ -16,8 +16,8 @@
:tooltip="tooltip"
:xAxis="xAxis1"
:yAxis="yAxis1"
- :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
- style="height: 260px"
+ :options="baseChartOptions"
+ style="height: 100%"
/>
</div>
</div>
@@ -28,17 +28,18 @@
import { completedInspectionCount } from '@/api/viewIndex.js'
import PanelHeader from './PanelHeader.vue'
import Echarts from '@/components/Echarts/echarts.vue'
+import { BI, baseChartOptions, withTooltip } from '../theme.js'
const chartStyle = {
width: '100%',
- height: '140%',
+ height: '100%',
}
const grid = { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true }
const barLegend = {
show: true,
- textStyle: { color: '#B8C8E0' },
+ textStyle: { color: BI.text },
data: ['鍚堟牸', '涓嶅悎鏍�', '鍚堟牸鐜�'],
}
@@ -57,8 +58,8 @@
x2: 0,
y2: 1,
colorStops: [
- { offset: 1, color: 'rgba(0, 164, 237, 0)' },
- { offset: 0, color: 'rgba(78, 228, 255, 1)' },
+ { offset: 1, color: 'rgba(74, 108, 240, 0.05)' },
+ { offset: 0, color: BI.accentBright },
],
},
},
@@ -78,8 +79,8 @@
x2: 0,
y2: 1,
colorStops: [
- { offset: 1, color: 'rgba(83, 126, 245, 0.19)' },
- { offset: 0, color: 'rgba(144, 97, 248, 1)' },
+ { offset: 1, color: 'rgba(142, 124, 230, 0.05)' },
+ { offset: 0, color: BI.series[2] },
],
},
},
@@ -92,14 +93,14 @@
showSymbol: true,
symbol: 'circle',
symbolSize: 8,
- lineStyle: { color: 'rgba(90, 216, 166, 1)', width: 2 },
- itemStyle: { color: 'rgba(90, 216, 166, 1)' },
+ lineStyle: { color: BI.up, width: 2 },
+ itemStyle: { color: BI.up },
data: [],
emphasis: { focus: 'series' },
},
])
-const tooltip = {
+const tooltip = withTooltip({
trigger: 'axis',
axisPointer: { type: 'cross' },
formatter(params) {
@@ -110,22 +111,22 @@
})
return result
},
-}
+})
const xAxis1 = ref([
- { type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] },
+ { type: 'category', axisTick: { show: false }, axisLine: { show: false }, axisLabel: { color: BI.text, fontSize: 12 }, data: [] },
])
const yAxis1 = [
- { type: 'value', name: '浠�', axisLabel: { color: '#B8C8E0' }, nameTextStyle: { color: '#B8C8E0' } },
+ { type: 'value', name: '浠�', axisLabel: { color: BI.text, fontSize: 12 }, nameTextStyle: { color: BI.text }, splitLine: { lineStyle: { color: 'rgba(143, 163, 200, 0.15)' } } },
{
type: 'value',
name: '鍚堟牸鐜�(%)',
min: 0,
max: 100,
- axisLabel: { color: '#B8C8E0', formatter: '{value}%' },
- nameTextStyle: { color: '#B8C8E0' },
- splitLine: { lineStyle: { color: 'rgba(184, 200, 224, 0.2)' } },
+ axisLabel: { color: BI.text, fontSize: 12, formatter: '{value}%' },
+ nameTextStyle: { color: BI.text },
+ splitLine: { show: false },
},
]
@@ -176,36 +177,49 @@
.warn-range {
position: absolute;
right: 0;
- top: 0;
- height: 32px;
- padding: 0 14px;
+ top: 2px;
+ height: 28px;
+ padding: 0 12px;
display: inline-flex;
align-items: center;
justify-content: center;
- border-radius: 4px;
- color: #ffffff;
- font-weight: 600;
- font-size: 14px;
- background: linear-gradient(180deg, rgba(51, 120, 255, 1) 0%, rgba(0, 164, 237, 1) 100%);
- border: 1px solid rgba(78, 228, 255, 0.25);
+ border-radius: 6px;
+ color: var(--bi-text-strong);
+ font-weight: 500;
+ font-size: 13px;
+ background: rgba(74, 108, 240, 0.15);
+ border: 1px solid rgba(74, 108, 240, 0.35);
cursor: pointer;
+ transition: border-color 0.2s ease;
z-index: 10;
}
.warn-range:hover {
- background: linear-gradient(180deg, rgba(51, 140, 255, 1) 0%, rgba(0, 184, 237, 1) 100%);
+ border-color: var(--bi-accent-bright);
+}
+
+.panel-root {
+ flex: 1;
+ height: 100%;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
}
.main-panel {
display: flex;
flex-direction: column;
+ flex: 1;
+ min-height: 0;
gap: 20px;
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
- height: 436px;
+ box-sizing: border-box;
}
</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue b/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue
index 8d28f7a..14b09b8 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/center-center.vue
@@ -1,5 +1,5 @@
<template>
- <div>
+ <div class="panel-root">
<div class="warn-panel">
<div class="warn-header">
<div class="warn-header-left">
@@ -25,26 +25,12 @@
</template>
<script setup>
-import { computed, getCurrentInstance, ref, onMounted } from 'vue'
-import Echarts from '@/components/Echarts/echarts.vue'
+import { getCurrentInstance, ref, onMounted } from 'vue'
import { nonComplianceWarning } from '@/api/viewIndex.js'
const { proxy } = getCurrentInstance() || {}
const warnings = ref([])
-
-// 鍗犳瘮鏁版嵁
-const ratios = ref({
- rawMaterialRatio: 0,
- semiFinishedProductRatio: 0,
- finishedProductRatio: 0,
-})
-
-const TAG_COLORS = {
- raw: '#7C4DFF',
- final: '#F5A000',
- semi: '#FF66CC',
-}
const tagClass = (type) => {
if (type === 'raw') return 'tag-raw'
@@ -60,71 +46,11 @@
return 'raw' // 榛樿鍊�
}
-const pieChartStyle = { width: '100%', height: '100%' }
-
-const pieOptions = {
- backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
-}
-
-const pieTooltip = {
- trigger: 'item',
- formatter: (p) => `${p.name}锛�${p.value}%`,
-}
-
-const pieData = computed(() => {
- return [
- { name: '鍘熸潗鏂�', value: ratios.value.rawMaterialRatio, itemStyle: { color: TAG_COLORS.raw } },
- { name: '鍗婃垚鍝�', value: ratios.value.semiFinishedProductRatio, itemStyle: { color: TAG_COLORS.semi } },
- { name: '鎴愬搧', value: ratios.value.finishedProductRatio, itemStyle: { color: TAG_COLORS.final } },
- ]
-})
-
-const pieSeries = computed(() => {
- return [
- {
- type: 'pie',
- radius: ['0%', '68%'],
- center: ['50%', '50%'],
- startAngle: 90,
- clockwise: true,
- avoidLabelOverlap: true,
- label: { show: false },
- labelLine: { show: false },
- itemStyle: {
- borderColor: '#071a3a',
- borderWidth: 4,
- shadowBlur: 14,
- shadowColor: 'rgba(0, 0, 0, 0.35)',
- },
- data: pieData.value,
- },
- {
- // 鍐呭湀鏆楃幆锛屽寮哄眰娆�
- type: 'pie',
- radius: ['70%', '74%'],
- center: ['50%', '50%'],
- silent: true,
- label: { show: false },
- labelLine: { show: false },
- itemStyle: { color: 'rgba(78, 228, 255, 0.12)' },
- data: [1],
- },
- ]
-})
-
const fetchWarnings = async () => {
try {
const res = await nonComplianceWarning()
if (res?.code === 200 && res?.data) {
const data = res.data
-
- // 鏇存柊鍗犳瘮鏁版嵁
- ratios.value = {
- rawMaterialRatio: data.rawMaterialRatio ?? 0,
- semiFinishedProductRatio: data.semiFinishedProductRatio ?? 0,
- finishedProductRatio: data.finishedProductRatio ?? 0,
- }
// 鏇存柊璀﹀憡鍒楄〃
const children = data.children || []
@@ -167,26 +93,32 @@
</script>
<style scoped>
+.panel-root {
+ flex: 1;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+}
+
.warn-panel {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 0 18px 18px;
display: flex;
flex-direction: column;
gap: 12px;
- height: 100%;
+ flex: 1;
+ min-height: 0;
box-sizing: border-box;
+ overflow: hidden;
}
.warn-header {
display: flex;
align-items: center;
justify-content: space-between;
- border-bottom: 1px solid;
- border-image: linear-gradient(270deg,
- rgba(0, 126, 255, 0) 0%,
- rgba(0, 126, 255, 0.4549) 35%,
- #007eff 78%,
- #007eff 100%) 1;
+ border-bottom: 1px solid rgba(143, 163, 200, 0.15);
padding: 10px 0 6px;
}
@@ -196,43 +128,50 @@
gap: 10px;
}
+/* 鍝佺墝钃濈粏绔栨潯锛屼笌 PanelHeader 鍛煎簲 */
.warn-badge {
- width: 18px;
- height: 18px;
- background: linear-gradient(180deg, #2aa8ff 0%, #4ee4ff 100%);
- clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
- box-shadow: 0 0 12px rgba(78, 228, 255, 0.25);
+ width: 3px;
+ height: 14px;
+ border-radius: 2px;
+ background: linear-gradient(180deg, var(--bi-accent-bright) 0%, var(--bi-accent) 100%);
+ flex-shrink: 0;
}
.warn-title {
- font-weight: 600;
- font-size: 18px;
- background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
+ font-weight: 500;
+ font-size: 16px;
+ color: var(--bi-text-strong);
+ letter-spacing: 1px;
line-height: 24px;
}
.warn-range {
- height: 32px;
- padding: 0 14px;
+ height: 28px;
+ padding: 0 12px;
display: inline-flex;
align-items: center;
justify-content: center;
- border-radius: 4px;
- color: #ffffff;
- font-weight: 600;
- background: linear-gradient(180deg, rgba(51, 120, 255, 1) 0%, rgba(0, 164, 237, 1) 100%);
- border: 1px solid rgba(78, 228, 255, 0.25);
+ border-radius: 6px;
+ color: var(--bi-text-strong);
+ font-weight: 500;
+ font-size: 13px;
+ background: rgba(74, 108, 240, 0.15);
+ border: 1px solid rgba(74, 108, 240, 0.35);
cursor: pointer;
+ transition: border-color 0.2s ease;
+}
+
+.warn-range:hover {
+ border-color: var(--bi-accent-bright);
}
.warn-body {
display: grid;
gap: 18px;
align-items: stretch;
- min-height: 260px;
+ flex: 1;
+ min-height: 0;
+ overflow-y: auto;
}
.warn-list {
@@ -247,7 +186,7 @@
grid-template-columns: 130px 1fr auto 110px;
align-items: center;
gap: 12px;
- color: #b8c8e0;
+ color: var(--bi-text);
font-size: 14px;
line-height: 1.2;
padding: 8px 0;
@@ -256,100 +195,58 @@
}
.warn-item:hover {
- color: #ff4d4f;
- background-color: rgba(255, 77, 79, 0.06);
+ color: var(--bi-down);
+ background-color: rgba(255, 107, 107, 0.06);
}
.warn-item:hover .warn-text {
- color: #ff4d4f;
+ color: var(--bi-down);
}
.warn-tag {
- height: 28px;
+ height: 24px;
padding: 0 10px;
border-radius: 4px;
display: inline-flex;
align-items: center;
justify-content: center;
- font-weight: 700;
+ font-weight: 500;
+ font-size: 12px;
color: #ffffff;
+ white-space: nowrap;
+ overflow: hidden;
}
+/* 搴忓垪鑹插尯鍒嗘娴嬬幆鑺傦紝涓庡ぇ灞忓浘琛ㄩ厤鑹蹭竴鑷� */
.tag-raw {
- background: #7c4dff;
+ background: var(--bi-accent-bright);
}
.tag-final {
- background: #f5a000;
+ background: #8E7CE6;
}
.tag-semi {
- background: #ff66cc;
+ background: #35C6D6;
}
.warn-text {
- color: #e8f1ff;
+ color: var(--bi-text-strong);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.warn-action {
- color: #ff4d4f;
- font-weight: 700;
+ color: var(--bi-down);
+ font-weight: 600;
white-space: nowrap;
cursor: pointer;
}
.warn-date {
- color: rgba(184, 200, 224, 0.75);
+ color: var(--bi-text);
white-space: nowrap;
-}
-
-.warn-chart {
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.chart-frame {
- width: 100%;
- height: 260px;
- border: 2px dashed rgba(184, 200, 224, 0.35);
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- background: radial-gradient(circle at 50% 50%, rgba(78, 228, 255, 0.08) 0%, rgba(0, 0, 0, 0) 65%);
-}
-
-/* 澶栧湀鍒诲害鐜� */
-.chart-frame::before {
- content: '';
- position: absolute;
- width: 220px;
- height: 220px;
- border-radius: 50%;
- background: repeating-conic-gradient(from 0deg, rgba(78, 228, 255, 0.75) 0 1deg, rgba(78, 228, 255, 0) 1deg 9deg);
- -webkit-mask: radial-gradient(circle, transparent 62%, #000 63%);
- mask: radial-gradient(circle, transparent 62%, #000 63%);
- opacity: 0.5;
- pointer-events: none;
-}
-
-/* 鍗佸瓧杈呭姪绾� */
-.chart-frame::after {
- content: '';
- position: absolute;
- width: 240px;
- height: 240px;
- background:
- linear-gradient(to right, rgba(78, 228, 255, 0) 0%, rgba(78, 228, 255, 0.55) 50%, rgba(78, 228, 255, 0) 100%),
- linear-gradient(to bottom, rgba(78, 228, 255, 0) 0%, rgba(78, 228, 255, 0.55) 50%, rgba(78, 228, 255, 0) 100%);
- background-size: 100% 1px, 1px 100%;
- background-position: center, center;
- background-repeat: no-repeat;
- opacity: 0.35;
- pointer-events: none;
+ font-variant-numeric: tabular-nums;
}
</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue b/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue
index 8e46770..e110871 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/center-top.vue
@@ -2,8 +2,25 @@
<div>
<!-- 椤堕儴缁熻鍗$墖 -->
<div class="stats-cards">
- <div v-for="item in statItems" :key="item.name" class="stat-card">
- <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
+ <div v-for="(item, index) in statItems" :key="item.name" class="stat-card">
+ <div class="card-icon" :style="{ color: ICON_COLORS[index % ICON_COLORS.length] }">
+ <!-- 妫�楠屽崟 -->
+ <svg v-if="index % 3 === 0" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <rect x="5" y="4" width="14" height="17" rx="2" />
+ <path d="M9 2.5h6v3H9z" />
+ <path d="M9 13l2 2 4-4" />
+ </svg>
+ <!-- 寰呭畬鎴愶細鏃堕挓 -->
+ <svg v-else-if="index % 3 === 1" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <circle cx="12" cy="12" r="9" />
+ <path d="M12 7v5l3.5 2" />
+ </svg>
+ <!-- 宸插畬鎴愶細瀵瑰嬀鍦嗙幆 -->
+ <svg v-else width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
+ <circle cx="12" cy="12" r="9" />
+ <path d="M8.5 12.5l2.5 2.5 5-5.5" />
+ </svg>
+ </div>
<div class="card-content">
<span class="card-label">{{ item.name }}</span>
<span class="card-value">{{ item.value }}</span>
@@ -22,8 +39,12 @@
<script setup>
import { ref, onMounted } from 'vue'
import { qualityInspectionCount } from '@/api/viewIndex.js'
+import { BI } from '../theme.js'
const statItems = ref([])
+
+// 鍗$墖鍥炬爣鐐圭紑鑹诧紙妫�楠�/寰呭畬鎴�/宸插畬鎴愶級
+const ICON_COLORS = [BI.accentBright, BI.series[3], BI.up]
const formatPercent = (val) => {
const num = Number(val) || 0
@@ -77,38 +98,56 @@
flex: 1;
display: flex;
align-items: center;
- background-image: url('@/assets/BI/border@2x.png');
- background-size: 100% 100%;
- background-position: center;
- background-repeat: no-repeat;
+ background: var(--bi-panel-bg);
+ border: 1px solid var(--bi-panel-border);
+ border-radius: 8px;
+ position: relative;
+ overflow: hidden;
height: 142px;
}
+/* 椤堕儴鍝佺墝钃濅寒绾匡紝鏇夸唬鍥剧墖鎻忚竟 */
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, var(--bi-accent) 0%, transparent 70%);
+}
+
.card-icon {
- width: 100px;
- height: 100px;
- margin: 20px 20px 0 10px;
+ width: 52px;
+ height: 52px;
+ margin: 0 16px 0 20px;
+ border-radius: 8px;
+ background: rgba(74, 108, 240, 0.12);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
}
.card-content {
display: flex;
flex-direction: column;
- gap: 10px;
+ gap: 8px;
+ min-width: 0;
}
.card-value {
- font-weight: 500;
- font-size: 40px;
- background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
+ font-weight: 600;
+ font-size: 36px;
+ color: var(--bi-text-strong);
+ font-variant-numeric: tabular-nums;
+ line-height: 1.1;
}
.card-label {
font-weight: 400;
font-size: 16px;
- color: rgba(208, 231, 255, 0.7);
+ color: var(--bi-text);
}
.card-compare {
@@ -116,7 +155,7 @@
align-items: center;
gap: 6px;
font-size: 15px;
- color: #d0e7ff;
+ color: var(--bi-text);
white-space: nowrap;
flex-wrap: nowrap;
}
@@ -128,6 +167,7 @@
.compare-value {
font-weight: 600;
+ font-variant-numeric: tabular-nums;
}
.compare-icon {
@@ -139,11 +179,11 @@
.compare-up .compare-value,
.compare-up .compare-icon {
- color: #00c853;
+ color: var(--bi-up);
}
.compare-down .compare-value,
.compare-down .compare-icon {
- color: #ff5252;
+ color: var(--bi-down);
}
</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue b/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue
index cce4894..f69c6fb 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/left-top.vue
@@ -1,5 +1,5 @@
<template>
- <div>
+ <div class="panel-root">
<PanelHeader title="璐ㄩ噺鎸囨爣鍚堟牸鍒嗘瀽" />
<div class="main-panel panel-item-customers">
<div v-for="section in sections" :key="section.key" class="inspect-block">
@@ -51,10 +51,11 @@
import PanelHeader from './PanelHeader.vue'
import DateTypeSwitch from './DateTypeSwitch.vue'
import { rawMaterialDetection, processDetection, factoryDetection } from '@/api/viewIndex.js'
+import { BI, baseChartOptions } from '../theme.js'
-const QUALIFIED_COLOR = '#4EE4FF'
-const UNQUALIFIED_COLOR = '#3378FF'
-const TRACK_COLOR = 'rgba(78, 228, 255, 0.12)'
+const QUALIFIED_COLOR = BI.accentBright
+const UNQUALIFIED_COLOR = BI.series[2]
+const TRACK_COLOR = 'rgba(74, 108, 240, 0.12)'
const apiMap = {
raw: rawMaterialDetection,
@@ -89,7 +90,7 @@
{
key: 'raw',
title: '鍘熸潗鏂欐娴�',
- dateType: 1,
+ dateType: 3,
qualifiedCount: 0,
unqualifiedCount: 0,
qualifiedRate: 0,
@@ -98,7 +99,7 @@
{
key: 'process',
title: '杩囩▼妫�娴�',
- dateType: 1,
+ dateType: 3,
qualifiedCount: 0,
unqualifiedCount: 0,
qualifiedRate: 0,
@@ -107,7 +108,7 @@
{
key: 'final',
title: '鎴愬搧鍑哄巶妫�娴�',
- dateType: 1,
+ dateType: 3,
qualifiedCount: 0,
unqualifiedCount: 0,
qualifiedRate: 0,
@@ -120,10 +121,7 @@
height: '110px',
}
-const ringOptions = {
- backgroundColor: 'transparent',
- textStyle: { color: '#B8C8E0' },
-}
+const ringOptions = baseChartOptions
const ringTooltip = {
show: false,
@@ -144,7 +142,7 @@
const unqualified = Number(section.unqualifiedCount || 0)
const total = qualified + unqualified
- return [
+ const series = [
{
type: 'pie',
radius: ['68%', '82%'],
@@ -155,7 +153,10 @@
itemStyle: { color: TRACK_COLOR },
data: [1],
},
- {
+ ]
+
+ // 鏁版嵁涓虹┖鏃朵笉娓叉煋涓荤幆锛岄伩鍏� ECharts 绌洪ゼ鍥炬覆鏌撳嚭鐏拌壊鍦嗙幆
+ if (total > 0) series.push({
name: section.title,
type: 'pie',
radius: ['68%', '82%'],
@@ -167,41 +168,36 @@
clockwise: true,
minAngle: total > 0 ? 8 : 0,
itemStyle: {
- borderColor: 'rgba(10, 28, 58, 0.95)',
+ borderColor: BI.sliceBorder,
borderWidth: 2,
+ borderRadius: 4,
},
data: [
{
value: qualified,
name: '鍚堟牸鏁�',
- itemStyle: {
- color: QUALIFIED_COLOR,
- shadowBlur: 16,
- shadowColor: 'rgba(78, 228, 255, 0.45)',
- },
+ itemStyle: { color: QUALIFIED_COLOR },
},
{
value: unqualified,
name: '涓嶅悎鏍兼暟',
- itemStyle: {
- color: UNQUALIFIED_COLOR,
- shadowBlur: 10,
- shadowColor: 'rgba(51, 120, 255, 0.35)',
- },
+ itemStyle: { color: UNQUALIFIED_COLOR },
},
],
- },
- {
+ })
+
+ series.push({
type: 'pie',
radius: ['52%', '56%'],
center: ['50%', '50%'],
silent: true,
label: { show: false },
labelLine: { show: false },
- itemStyle: { color: 'rgba(0, 127, 255, 0.22)' },
+ itemStyle: { color: 'rgba(74, 108, 240, 0.22)' },
data: [1],
- },
- ]
+ })
+
+ return series
}
const handleDateTypeChange = (key, dateType) => {
@@ -221,10 +217,19 @@
</script>
<style scoped lang="scss">
+.panel-root {
+ flex: 1;
+ height: 100%;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+}
+
.main-panel {
display: flex;
flex-direction: column;
- height: 100%;
+ flex: 1;
+ min-height: 0;
gap: 0;
}
@@ -237,74 +242,35 @@
.filters-row-left {
width: 50%;
- color: white;
- /* 鐢╢lex鏇夸唬float锛岃瀛愬厓绱犲榻愭洿绋冲畾 */
+ color: var(--bi-text-strong);
display: flex;
align-items: center;
+ gap: 8px;
+ /* 鍝佺墝钃濈粏绔栨潯锛屼笌 PanelHeader 鍛煎簲 */
span {
- /* 鏍稿績锛氱埗绾х浉瀵瑰畾浣嶏紝浣滀负浼厓绱犲熀鍑� */
- position: relative;
- display: inline-block;
- /* 缁欎吉鍏冪礌鍜屾枃瀛楃暀绌洪棿 */
- padding-left: 22px;
- /* 鏂囧瓧鍨傜洿灞呬腑 */
- line-height: 23px;
- margin-right: 8px;
-
- &::after {
- content: '';
- display: inline-block;
- width: 16px;
- height: 16px;
- clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
- background: #217AFF;
- position: absolute;
- top: 50%;
- left: 0;
- transform: translateY(-50%);
- /* 纭繚鑿卞舰鍦ㄦ笎鍙樺潡涓婃柟 */
- z-index: 1;
- }
-
- &::before {
- content: '';
- display: inline-block;
- width: 18px;
- height: 7px;
- border-radius: 8px;
- background: linear-gradient(360deg, rgba(33, 133, 255, 0.4) 0%, rgba(33, 221, 255, 0) 100%);
- position: absolute;
- top: 50%;
- left: -1px;
- /* 绮惧噯璐村湪鑿卞舰姝d笅鏂� */
- transform: translateY(calc(0% + 8px));
- z-index: 0;
- }
+ flex-shrink: 0;
+ width: 3px;
+ height: 14px;
+ border-radius: 2px;
+ background: linear-gradient(180deg, var(--bi-accent-bright) 0%, var(--bi-accent) 100%);
}
p {
- width: 100px;
- height: 23px;
- /* 娓愬彉璧峰鑹插拰鑿卞舰缁熶竴锛屾洿鍗忚皟 */
- background: linear-gradient(90deg, #217AFF 0%, rgba(33, 221, 255, 0) 100%);
- /* 绮惧噯鍨傜洿灞呬腑 */
- line-height: 26px;
- text-align: center;
- color: white;
- /* 鐢ㄩ珮搴︾殑涓�鍗婂仛鍦嗚锛岀‘淇濆乏杈规槸瀹岀編鍗婂渾 */
- border-radius: 12px 0 0 12px;
- /* 鍙�夛細鍔犱竴鐐瑰乏鍐呰竟璺濓紝璁╂枃瀛椾笉璐磋竟 */
- padding-left: 4px;
+ font-size: 14px;
+ font-weight: 500;
+ letter-spacing: 1px;
+ color: var(--bi-text-strong);
}
}
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 14px 18px;
width: 100%;
- height: 958px;
box-sizing: border-box;
}
@@ -325,7 +291,7 @@
right: 0;
bottom: 0;
height: 1px;
- background: linear-gradient(90deg, rgba(33, 122, 255, 0) 0%, rgba(33, 122, 255, 0.55) 50%, rgba(33, 122, 255, 0) 100%);
+ background: linear-gradient(90deg, rgba(74, 108, 240, 0) 0%, rgba(74, 108, 240, 0.55) 50%, rgba(74, 108, 240, 0) 100%);
pointer-events: none;
}
@@ -348,32 +314,6 @@
justify-content: center;
}
-/* 澶栧湀鍒诲害锛堢偣鐘剁幆锛� */
-.ring::before {
- content: '';
- position: absolute;
- inset: -8px;
- border-radius: 50%;
- background: repeating-conic-gradient(from 0deg,
- rgba(78, 228, 255, 0.75) 0 1deg,
- rgba(78, 228, 255, 0) 1deg 9deg);
- -webkit-mask: radial-gradient(circle, transparent 62%, #000 63%);
- mask: radial-gradient(circle, transparent 62%, #000 63%);
- opacity: 0.35;
- pointer-events: none;
-}
-
-/* 鏌斿拰鍙戝厜鑳屾櫙 */
-.ring::after {
- content: '';
- position: absolute;
- inset: -20px;
- border-radius: 50%;
- background: radial-gradient(circle, rgba(78, 228, 255, 0.18) 0%, rgba(78, 228, 255, 0.06) 40%, rgba(0, 0, 0, 0) 70%);
- filter: blur(0.2px);
- pointer-events: none;
-}
-
.stats {
width: 240px;
flex: 0 0 240px;
@@ -388,17 +328,16 @@
align-items: center;
height: 100%;
padding: 10px 14px;
- border-radius: 4px;
- border: 1px solid rgba(78, 228, 255, 0.22);
- background: linear-gradient(90deg, rgba(33, 122, 255, 0.28) 0%, rgba(10, 28, 58, 0.35) 55%, rgba(10, 28, 58, 0.2) 100%);
- box-shadow: inset 0 0 18px rgba(16, 45, 95, 0.25);
+ border-radius: 6px;
+ border: 1px solid var(--bi-panel-border);
+ background: rgba(13, 24, 52, 0.6);
}
.stat-left {
display: inline-flex;
align-items: center;
gap: 10px;
- color: #b8c8e0;
+ color: var(--bi-text);
font-size: 12px;
}
@@ -407,15 +346,14 @@
height: 10px;
border-radius: 2px;
display: inline-block;
- box-shadow: 0 0 10px rgba(78, 228, 255, 0.25);
}
.dot-qualified {
- background: rgba(184, 200, 224, 0.85);
+ background: var(--bi-accent-bright);
}
.dot-unqualified {
- background: #4ee4ff;
+ background: #8E7CE6;
}
.stat-right {
@@ -425,19 +363,20 @@
}
.stat-value {
- color: #ffffff;
+ color: var(--bi-text-strong);
font-size: 14px;
font-weight: 600;
min-width: 40px;
text-align: right;
- text-shadow: 0 0 10px rgba(78, 228, 255, 0.15);
+ font-variant-numeric: tabular-nums;
}
.stat-percent {
- color: rgba(184, 200, 224, 0.95);
+ color: var(--bi-text);
font-size: 12px;
min-width: 40px;
text-align: right;
+ font-variant-numeric: tabular-nums;
}
/* 璁╁垏鎹㈡寜閽洿璐磋繎鎴浘锛堟洿绱у噾锛� */
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue b/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue
index f4b0a0c..9a1014e 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/right-bottom.vue
@@ -1,9 +1,8 @@
<template>
- <div>
+ <div class="panel-root">
<PanelHeader title="涓嶅悎鏍兼鍝佸鐞嗗垎鏋�" />
<div class="panel-item-customers">
- <div class="pie-chart-wrapper" ref="pieWrapperRef">
- <div class="pie-background" ref="pieBackgroundRef"></div>
+ <div class="pie-chart-wrapper">
<Echarts ref="chart" :chartStyle="chartStyle" :legend="landLegend" :series="computedSeries"
:tooltip="landTooltip" :color="landColors" :options="pieOptions" style="height: 100%" class="land-chart" />
</div>
@@ -12,21 +11,19 @@
</template>
<script setup>
-import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
+import { ref, computed, onMounted } from 'vue'
import Echarts from '@/components/Echarts/echarts.vue'
import PanelHeader from './PanelHeader.vue'
import { unqualifiedProductProcessingAnalysis } from '@/api/viewIndex.js'
-import { useChartBackground } from '@/hooks/useChartBackground.js'
+import { BI, baseChartOptions, withTooltip } from '../theme.js'
-const pieWrapperRef = ref(null)
-const pieBackgroundRef = ref(null)
const chart = ref(null)
// 鏁版嵁鍒楄〃
const dataList = ref([])
-// 棰滆壊鍒楄〃
-const landColors = ['#26FFCB', '#24CBFF', '#35FBF4', '#2651FF', '#D1E4F5', '#5782F7', '#2F67EF', '#82BAFF']
+// 棰滆壊鍒楄〃锛堝ぇ灞忕粺涓�搴忓垪鑹诧級
+const landColors = BI.series
// label 瀵屾枃鏈牱寮�
const dotRich = landColors.reduce((acc, color, idx) => {
@@ -49,16 +46,16 @@
top: '40%',
orient: 'vertical',
textStyle: {
- color: '#fff',
+ color: BI.textStrong,
rich: {
- unit: { color: '#fff', fontSize: 12, padding: [0, 10, 0, 0] },
- text: { width: 60, color: '#fff', fontSize: 12 },
+ unit: { color: BI.textStrong, fontSize: 12, padding: [0, 10, 0, 0] },
+ text: { width: 60, color: BI.textStrong, fontSize: 12 },
}
}
})
// 鎻愮ず妗嗛厤缃�
-const landTooltip = {
+const landTooltip = withTooltip({
trigger: 'item',
alwaysShowContent: false,
position: function (pt) {
@@ -70,24 +67,33 @@
const { name, value, rate } = params.data
return `${name}<br/>鏁伴噺锛�${value}涓�<br/>鍗犳瘮锛�${rate}%`
},
-}
+})
// 浣跨敤璁$畻灞炴�у鐞� Series
const computedSeries = computed(() => {
+ const isEmpty = dataList.value.length === 0
return [
{
name: '涓嶅悎鏍兼鍝佸鐞嗗垎鏋�',
type: 'pie',
radius: ['35%', '55%'],
center: ['50%', '50%'],
+ silent: isEmpty,
+ itemStyle: isEmpty
+ ? { color: 'rgba(74, 108, 240, 0.12)', borderWidth: 0 }
+ : {
+ borderColor: BI.sliceBorder,
+ borderWidth: 2,
+ borderRadius: 4,
+ },
label: {
- show: true,
+ show: !isEmpty,
position: 'outside',
- color: '#fff',
+ color: BI.textStrong,
rich: {
...dotRich,
- parent: { fontSize: 14, fontWeight: 600, color: '#fff', lineHeight: 20 },
- child: { fontSize: 12, color: '#fff', lineHeight: 18 },
+ parent: { fontSize: 14, fontWeight: 600, color: BI.textStrong, lineHeight: 20 },
+ child: { fontSize: 12, color: BI.text, lineHeight: 18 },
},
formatter: function (params) {
if (!params.data) return ''
@@ -96,11 +102,11 @@
},
},
labelLine: {
- show: true,
+ show: !isEmpty,
length: 20,
- lineStyle: { color: '#B8C8E0' },
+ lineStyle: { color: 'rgba(143, 163, 200, 0.4)' },
},
- data: dataList.value,
+ data: isEmpty ? [1] : dataList.value,
},
{
// 鍐呭湀瑁呴グ
@@ -109,23 +115,14 @@
center: ['50%', '50%'],
silent: true,
label: { show: false },
- itemStyle: { color: 'rgba(0, 127, 255, 0.25)' },
+ itemStyle: { color: 'rgba(74, 108, 240, 0.22)' },
data: [1],
},
]
})
-const chartStyle = { width: '100%', height: '126%' }
-const pieOptions = { backgroundColor: 'transparent' }
-
-// 鑳屾櫙澶勭悊閽╁瓙
-const { adjustBackgroundPosition, init: initBackground, cleanup: cleanupBackground } = useChartBackground({
- wrapperRef: pieWrapperRef,
- backgroundRef: pieBackgroundRef,
- offsetX: '-51.5%',
- offsetY: '-39%',
- watchData: dataList
-})
+const chartStyle = { width: '100%', height: '100%' }
+const pieOptions = baseChartOptions
const loadData = async () => {
try {
@@ -134,14 +131,9 @@
dataList.value = (res.data || []).map((it) => ({
name: it.name,
value: Number(it.value || 0),
- rate: it.rate,
+ rate: it.rate,
}))
landLegend.value.data = dataList.value.map((d) => d.name)
-
- // 鏁版嵁鏇存柊鍚庡井璋冭儗鏅�
- setTimeout(() => {
- adjustBackgroundPosition()
- }, 100)
}
} catch (e) {
console.error('鑾峰彇鏁版嵁澶辫触:', e)
@@ -150,40 +142,33 @@
onMounted(() => {
loadData()
- initBackground()
-})
-
-onBeforeUnmount(() => {
- cleanupBackground()
})
</script>
<style scoped>
+.panel-root {
+ flex: 1;
+ height: 100%;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+}
+
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
- height: 449px;
+ box-sizing: border-box;
+ flex: 1;
+ min-height: 0;
}
.pie-chart-wrapper {
position: relative;
width: 100%;
- height: 320px;
+ height: 100%;
+ min-height: 0;
}
-
-.pie-background {
- position: absolute;
- width: 360px;
- height: 360px;
- background-image: url('@/assets/BI/鐜懓鍥捐竟妗�.png');
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- z-index: 1;
- pointer-events: none;
- left: 50%;
- top: 50%;
- transform: translate(-51.5%, -39%);
-}
-</style>
\ No newline at end of file
+</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue b/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue
index 890e99a..cb7a704 100644
--- a/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/components/right-top.vue
@@ -1,9 +1,9 @@
<template>
- <div>
+ <div class="panel-root">
<PanelHeader title="涓嶅悎鏍间骇鍝佹帓鍚�" />
<div class="main-panel panel-item-customers">
<div class="main-panel-container">
- <div style="color: white" class="main-panel-box" v-for="(item, index) in panelList" :key="index">
+ <div class="main-panel-box" v-for="(item, index) in panelList" :key="index">
<!-- <div style="flex: 1" class="main-panel-box-left">{{ item.rank }}</div> -->
<div style="flex: 1" class="main-panel-box-left">{{ item.productName }}</div>
<div style="flex: 3" class="main-panel-box-right">
@@ -71,13 +71,20 @@
flex-direction: row;
align-items: center;
height: 40px;
+ color: var(--bi-text);
.main-panel-box-left {
- background: red;
- border-radius: 20px;
+ background: rgba(74, 108, 240, 0.15);
+ border: 1px solid rgba(74, 108, 240, 0.35);
+ border-radius: 6px;
+ color: var(--bi-text-strong);
text-align: center;
line-height: 32px;
margin: 0 20px;
+ padding: 0 12px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.main-panel-box-right {
@@ -87,8 +94,8 @@
.main-panel-box-right-title {
font-size: 14px;
- font-weight: 600;
- color: #ffffff;
+ font-weight: 500;
+ color: var(--bi-text-strong);
margin-bottom: 6px;
}
@@ -98,11 +105,20 @@
justify-content: space-between;
padding-right: 60px;
margin-bottom: 4px;
+ font-variant-numeric: tabular-nums;
}
.main-panel-box-right-progress {
:deep(.el-progress__text) {
- color: white !important;
+ color: var(--bi-text) !important;
+ }
+
+ :deep(.el-progress-bar__outer) {
+ background-color: rgba(74, 108, 240, 0.12);
+ }
+
+ :deep(.el-progress-bar__inner) {
+ background: var(--bi-accent-bright);
}
}
}
@@ -116,17 +132,29 @@
overflow-y: auto;
}
+.panel-root {
+ flex: 1;
+ height: 100%;
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+}
+
.main-panel {
display: flex;
flex-direction: column;
+ flex: 1;
+ min-height: 0;
gap: 20px;
}
.panel-item-customers {
- border: 1px solid #1a58b0;
+ border: 1px solid var(--bi-panel-border);
+ background: var(--bi-panel-bg);
+ border-radius: 8px;
padding: 18px;
width: 100%;
- height: 449px;
+ box-sizing: border-box;
overflow: hidden;
}
</style>
diff --git a/src/views/reportAnalysis/qualityAnalysis/index.vue b/src/views/reportAnalysis/qualityAnalysis/index.vue
index 59fcc9b..83d4476 100644
--- a/src/views/reportAnalysis/qualityAnalysis/index.vue
+++ b/src/views/reportAnalysis/qualityAnalysis/index.vue
@@ -13,7 +13,9 @@
<!-- 椤堕儴鏍囬鏍� -->
<div class="dashboard-header">
+ <div class="header-line"></div>
<div class="factory-name">璐ㄩ噺鏁版嵁鍒嗘瀽</div>
+ <div class="header-line header-line-right"></div>
</div>
<!-- 涓昏鍐呭鍖哄煙 -->
@@ -42,7 +44,6 @@
<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
-import autofit from 'autofit.js'
import CenterCenter from './components/center-center.vue'
import RightTop from './components/right-top.vue'
import RightBottom from './components/right-bottom.vue'
@@ -68,23 +69,21 @@
const container = document.querySelector('.scale-container')
if (!container) return
- // 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
- const rect = container.getBoundingClientRect?.()
- const containerWidth = container.clientWidth || rect?.width || window.innerWidth
- const containerHeight = container.clientHeight || rect?.height || window.innerHeight
+ const containerWidth = container.clientWidth || window.innerWidth
+ const containerHeight = container.clientHeight || window.innerHeight
- // 璁$畻瀹介珮缂╂斁姣斾緥锛屽彇杈冨皬鍊间互淇濊瘉鍐呭瀹屾暣鏄剧ず锛堢瓑姣旂缉鏀撅級
- const scaleX = containerWidth / designWidth
- const scaleY = containerHeight / designHeight
- scaleRatio.value = Math.min(scaleX, scaleY)
+ // 绛夋瘮缂╂斁锛屽彇杈冨皬鍊间繚璇� 1920脳1080 鍐呭瀹屾暣鏄剧ず
+ scaleRatio.value = Math.min(containerWidth / designWidth, containerHeight / designHeight)
}
-// 绐楀彛澶у皬鍙樺寲澶勭悊
+// 绐楀彛澶у皬鍙樺寲澶勭悊锛坮AF 鑺傛祦锛岄伩鍏嶉珮棰戦噸鎺掞級
+let resizeRafId = null
const handleResize = () => {
- // 寤惰繜鎵ц锛岀‘淇滵OM鏇存柊瀹屾垚
- setTimeout(() => {
+ if (resizeRafId) cancelAnimationFrame(resizeRafId)
+ resizeRafId = requestAnimationFrame(() => {
+ resizeRafId = null
calculateScale()
- }, 100)
+ })
}
// 鍏ㄥ睆鍔熻兘瀹炵幇 - 閽堝scale-container鍏冪礌
@@ -144,13 +143,7 @@
window.removeEventListener('fullscreenchange', handleFullscreenChange)
window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
- // 绉婚櫎鎴戜滑娣诲姞鐨刟utofit鍔ㄦ�佽皟鏁寸洃鍚櫒
- if (window._autofitUpdateHandler) {
- window.removeEventListener('resize', window._autofitUpdateHandler)
- delete window._autofitUpdateHandler
- }
- // 鍏抽棴autofit
- autofit.off()
+ if (resizeRafId) cancelAnimationFrame(resizeRafId)
})
</script>
@@ -159,8 +152,8 @@
.scale-container {
position: relative;
width: 100%;
-/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
+/* 甯歌甯冨眬涓嬪噺鍘婚《鏍� 60 + 椤电 40 = 100px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
+height: calc(100vh - 100px);
display: flex;
align-items: center;
justify-content: center;
@@ -173,11 +166,34 @@
position: relative;
width: 1920px;
height: 1080px;
-background-image: url("@/assets/BI/backImage@2x.png");
-background-size: cover;
-background-position: center;
-background-repeat: no-repeat;
transform-origin: center center;
+
+/* 澶у睆涓婚浠ょ墝锛屽瓙缁勪欢閫氳繃 CSS 鍙橀噺缁ф壙 */
+--bi-panel-bg: rgba(20, 32, 66, 0.55);
+--bi-panel-border: #1e3160;
+--bi-text: #8fa3c8;
+--bi-text-strong: #eaf1ff;
+--bi-accent: #2c51d9;
+--bi-accent-bright: #4a6cf0;
+--bi-up: #34c77b;
+--bi-down: #ff6b6b;
+
+background:
+ radial-gradient(1200px 600px at 50% -10%, rgba(44, 81, 217, 0.18) 0%, transparent 60%),
+ linear-gradient(180deg, #0a1229 0%, #0d1834 55%, #0a1430 100%);
+}
+
+/* 鏋佹贰缃戞牸绾圭悊锛屾浛浠fā鏉挎劅搴曞浘 */
+.data-dashboard::before {
+content: '';
+position: absolute;
+inset: 0;
+pointer-events: none;
+background-image:
+ linear-gradient(rgba(74, 108, 240, 0.045) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(74, 108, 240, 0.045) 1px, transparent 1px);
+background-size: 48px 48px;
+mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.35) 100%);
}
/* 鍏ㄥ睆鐘舵�佺殑鏍峰紡 - 浣滅敤浜巗cale-container */
@@ -215,20 +231,42 @@
position: relative;
z-index: 1;
height: 86px;
-background-image: url("@/assets/BI/biaoti.png");
-background-size: cover;
-background-repeat: no-repeat;
display: flex;
align-items: center;
justify-content: center;
+gap: 28px;
+}
+
+/* 鏍囬涓や晶娓愰殣鍒嗛殧绾� */
+.header-line {
+width: 420px;
+height: 1px;
+background: linear-gradient(90deg, transparent 0%, rgba(74, 108, 240, 0.5) 100%);
+}
+
+.header-line-right {
+background: linear-gradient(270deg, transparent 0%, rgba(74, 108, 240, 0.5) 100%);
}
.factory-name {
font-weight: 600;
-font-size: 52px;
-color: #FFFFFF;
-top: 16px;
+font-size: 30px;
+letter-spacing: 4px;
+color: var(--bi-text-strong);
+position: relative;
+white-space: nowrap;
+}
+
+.factory-name::after {
+content: '';
position: absolute;
+left: 50%;
+bottom: -8px;
+transform: translateX(-50%);
+width: 48px;
+height: 2px;
+background: var(--bi-accent-bright);
+border-radius: 1px;
}
.fullscreen-btn {
@@ -237,21 +275,21 @@
left: 20px;
width: 40px;
height: 40px;
-background: rgba(0, 20, 60, 0.8);
-border: 1px solid rgba(0, 212, 255, 0.3);
+background: var(--bi-panel-bg);
+border: 1px solid var(--bi-panel-border);
border-radius: 6px;
-color: #00d4ff;
+color: var(--bi-text);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
-transition: all 0.3s;
+transition: color 0.2s ease, border-color 0.2s ease;
z-index: 10000;
}
.fullscreen-btn:hover {
-background: rgba(0, 30, 90, 0.9);
-border-color: rgba(0, 212, 255, 0.5);
+color: var(--bi-text-strong);
+border-color: var(--bi-accent-bright);
}
.dashboard-content {
diff --git a/src/views/reportAnalysis/qualityAnalysis/theme.js b/src/views/reportAnalysis/qualityAnalysis/theme.js
new file mode 100644
index 0000000..fae39db
--- /dev/null
+++ b/src/views/reportAnalysis/qualityAnalysis/theme.js
@@ -0,0 +1,28 @@
+// 璐ㄩ噺鏁版嵁鍒嗘瀽澶у睆缁熶竴瑙嗚浠ょ墝锛堜笌鍩虹鏁版嵁/PSI 澶у睆鍚屼竴濂楀搧鐗岃摑鍏嬪埗娣辫壊涓婚锛�
+export const BI = {
+ // 鍥捐〃搴忓垪鑹诧紙鍏嬪埗涓撲笟锛屾浛浠i潚钃濊崸鍏夛級
+ series: ['#4A6CF0', '#35C6D6', '#8E7CE6', '#F0B14A', '#2FB8A8', '#E86D6D'],
+ text: '#8FA3C8',
+ textStrong: '#EAF1FF',
+ panelBg: 'rgba(20, 32, 66, 0.55)',
+ panelBorder: '#1E3160',
+ sliceBorder: '#0D1834',
+ accent: '#2C51D9',
+ accentBright: '#4A6CF0',
+ up: '#34C77B',
+ down: '#FF6B6B',
+}
+
+export const baseChartOptions = {
+ backgroundColor: 'transparent',
+ textStyle: { color: BI.text },
+}
+
+// 娣辫壊缁熶竴 tooltip 鏍峰紡锛屼繚鐣欏悇缁勪欢鑷畾涔� formatter
+export const withTooltip = (tooltip = {}) => ({
+ backgroundColor: 'rgba(13, 24, 52, 0.92)',
+ borderColor: BI.panelBorder,
+ borderWidth: 1,
+ textStyle: { color: BI.textStrong, fontSize: 12 },
+ ...tooltip,
+})
diff --git a/src/views/salesManagement/indicatorStats/index.vue b/src/views/salesManagement/indicatorStats/index.vue
index 6101920..d1a3ed8 100644
--- a/src/views/salesManagement/indicatorStats/index.vue
+++ b/src/views/salesManagement/indicatorStats/index.vue
@@ -407,7 +407,7 @@
.indicator-stats {
padding: 20px;
background: #f5f7fa;
- min-height: calc(100vh - 84px);
+ min-height: calc(100vh - 100px);
}
.page-header {
--
Gitblit v1.9.3