| | |
| | | <template> |
| | | <div class="data-dashboard"> |
| | | <!-- 全屏按钮 - 移动到左上角 --> |
| | | <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"> |
| | | <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/> |
| | | </svg> |
| | | <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| | | <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/> |
| | | </svg> |
| | | </button> |
| | | <div class="scale-container"> |
| | | <div class="data-dashboard" :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"> |
| | | <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/> |
| | | </svg> |
| | | <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| | | <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/> |
| | | </svg> |
| | | </button> |
| | | |
| | | <!-- 顶部标题栏 --> |
| | | <div class="dashboard-header"> |
| | | </div> |
| | | |
| | | <!-- 主要内容区域 --> |
| | | <div class="dashboard-content"> |
| | | <!-- 左侧区域 --> |
| | | <div class="left-panel"> |
| | | <!-- 客户信息统计分析 --> |
| | | <div class="panel-header"> |
| | | <span class="panel-title">客户信息统计分析</span> |
| | | </div> |
| | | <div class="panel-item-customers"> |
| | | <div class="panel-title-second"> |
| | | <div class="panel-title-icon"></div> |
| | | <div class="total-customers"> |
| | | <span class="label">总合同金额(元)</span> |
| | | <span class="value">{{sum}}</span> |
| | | </div> |
| | | <!-- <div class="jiantou"></div>--> |
| | | </div> |
| | | <!-- 饼图区域 --> |
| | | <div style="display: flex;align-items: center;gap: 20px;justify-content: space-evenly;height: 82%;margin-top: 20px"> |
| | | <div style="width: 240px; height: 240px; background-image: url('/src/assets/BI/zonghetongbingtubiankuang@2x.png'); background-size: contain; background-position: center; background-repeat: no-repeat; display: flex; align-items: center; justify-content: center;"> |
| | | <Echarts ref="chart" :legend="pieLegend" :chartStyle="chartStylePie" |
| | | :series="materialPieSeries" |
| | | :tooltip="pieTooltip" |
| | | :options="{backgroundColor: 'transparent'}" |
| | | style="margin-left: 5px;"></Echarts> |
| | | </div> |
| | | <ul class="contract-list" style="margin: 0; padding: 0; display: flex; flex-direction: column;justify-content: space-around; height: 100%; overflow-y: auto; scroll-behavior: smooth;" ref="refContractList"> |
| | | <li v-for="item in materialPieSeries[0].data" :key="item.name" style="list-style: none; margin-bottom: 12px;"> |
| | | <div style="display: flex;align-items: center;justify-content: space-between;width: 100%"> |
| | | <div class="line" :style="{color: item.itemStyle.color}">■ {{item.name}}</div> |
| | | <div style="font-weight: 700;font-size: 16px;color: #85B1E4;">¥{{item.value}}</div> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 质量统计 --> |
| | | <div class="panel-header"> |
| | | <span class="panel-title">质量统计</span> |
| | | </div> |
| | | <div class="main-panel"> |
| | | <div class="panel-item-customers"> |
| | | <div class="quality-cards"> |
| | | <div class="quality-cardSec"> |
| | | <div class="quality-card one"></div> |
| | | <div class="quality-cardTitle"> |
| | | <div>原材料已检测数</div> |
| | | <div>{{qualityStatisticsObject.supplierNum}}件</div> |
| | | </div> |
| | | </div> |
| | | <div class="quality-cardSec"> |
| | | <div class="quality-card two"></div> |
| | | <div class="quality-cardTitle"> |
| | | <div>过程检验数量</div> |
| | | <div>{{qualityStatisticsObject.processNum}}件</div> |
| | | </div> |
| | | </div> |
| | | <div class="quality-cardSec"> |
| | | <div class="quality-card three"></div> |
| | | <div class="quality-cardTitle"> |
| | | <div>出厂已检数量</div> |
| | | <div>{{qualityStatisticsObject.factoryNum}}件</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <Echarts ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="barLegend" |
| | | :series="barSeries1" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | :options="{backgroundColor: 'transparent', textStyle: {color: '#B8C8E0'}}" |
| | | style="height: 260px"></Echarts> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 中间区域 --> |
| | | <div class="center-panel"> |
| | | <!-- 顶部统计卡片 --> |
| | | <div class="stats-cards"> |
| | | <div class="stat-card"> |
| | | <img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" /> |
| | | <div class="card-content"> |
| | | <span class="card-label">员工总数</span> |
| | | <span class="card-value">{{totalStaff}}</span> |
| | | </div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" /> |
| | | <div class="card-content"> |
| | | <span class="card-label">客户总数</span> |
| | | <span class="card-value">{{totalCustomers}}</span> |
| | | </div> |
| | | </div> |
| | | <div class="stat-card"> |
| | | <img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" /> |
| | | <div class="card-content"> |
| | | <span class="card-label">供应商总数</span> |
| | | <span class="card-value">{{totalSuppliers}}</span> |
| | | </div> |
| | | </div> |
| | | </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> |
| | | <div class="equipment-items"> |
| | | <div class="equipment-item"> |
| | | <span class="equipment-value">{{equipmentNum}}</span> |
| | | <span class="equipment-label">设备总数</span> |
| | | </div> |
| | | <div class="equipment-item"> |
| | | <span class="equipment-value">{{equipmentRepair}}</span> |
| | | <span class="equipment-label">待维修设备</span> |
| | | </div> |
| | | <div class="equipment-item"> |
| | | <span class="equipment-value">{{equipmentMaintain}}</span> |
| | | <span class="equipment-label">待保养设备</span> |
| | | </div> |
| | | <div class="equipment-item"> |
| | | <span class="equipment-value">{{totalMeasuring}}</span> |
| | | <span class="equipment-label">计量器具总数</span> |
| | | </div> |
| | | </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> |
| | | <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 style="display: flex;justify-content: space-between;align-items: center;"> |
| | | <div class="todo-title">待办编号:{{item.approveId}}</div> |
| | | <div class="todo-division">部门:{{item.approveDeptName}}</div> |
| | | <div class="todo-time">{{item.approveTime}}</div> |
| | | </div> |
| | | <div class="todo-division">待办事由:{{item.approveReason}}</div> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | <div v-else style="text-align: center"> |
| | | 暂无数据 |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="financial-header"> |
| | | <span class="financial-title">财务分析</span> |
| | | </div> |
| | | <div class="main-panel"> |
| | | <div class="panel-item-customers"> |
| | | <div class="event-header"> |
| | | <img src="@/assets/BI/shijianmingxiicon@2x.png" alt="图标" class="event-icon" /> |
| | | <span class="event-title">经营成果分析</span> |
| | | </div> |
| | | <Echarts ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="barLegend1" |
| | | :series="barSeries11" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis3" |
| | | :yAxis="yAxis3" |
| | | :options="{backgroundColor: 'transparent', textStyle: {color: '#B8C8E0'}}" |
| | | style="height: 300px"></Echarts> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 右侧区域 --> |
| | | <div class="right-panel"> |
| | | <!-- 应收应付统计 --> |
| | | <div class="panel-header"> |
| | | <span class="panel-title">应收应付统计</span> |
| | | </div> |
| | | <div class="panel-item-customers"> |
| | | <div style="display: flex;justify-content: space-between;margin-bottom: 20px;"> |
| | | <div class="section-title">应收应付统计</div> |
| | | <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable" class="custom-radio-group"> |
| | | <el-radio-button label="按周" :value="1" /> |
| | | <el-radio-button label="按月" :value="2" /> |
| | | <el-radio-button label="按季度" :value="3" /> |
| | | </el-radio-group> |
| | | </div> |
| | | <Echarts ref="chart" |
| | | :color="barColors2" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="barLegend2" |
| | | :series="barSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis" |
| | | :yAxis="yAxis" |
| | | :options="{backgroundColor: 'transparent', textStyle: {color: '#B8C8E0'}}" |
| | | style="height: 260px"></Echarts> |
| | | </div> |
| | | |
| | | <!-- 回款与开票分析 --> |
| | | <div class="panel-header"> |
| | | <span class="panel-title">回款与开票分析</span> |
| | | </div> |
| | | <div class="panel-item-customers" style="padding-top: 60px;"> |
| | | <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries" |
| | | :tooltip="tooltipLine" :xAxis="xAxis2" :yAxis="yAxis2" :options="{backgroundColor: 'transparent', textStyle: {color: '#FFFFFF'}}" style="height: 270px;"></Echarts> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 顶部标题栏 --> |
| | | <div class="dashboard-header"> |
| | | <div class="factory-name">基础数据</div> |
| | | </div> |
| | | |
| | | <!-- 主要内容区域 --> |
| | | <div class="dashboard-content"> |
| | | <!-- 左侧区域 --> |
| | | <div class="left-panel"> |
| | | <!-- 客户信息统计分析 --> |
| | | <LeftTop /> |
| | | |
| | | <!-- 质量统计 --> |
| | | <LeftBottom /> |
| | | </div> |
| | | |
| | | <!-- 中间区域 --> |
| | | <div class="center-panel"> |
| | | <CenterTop /> |
| | | |
| | | <CenterBottom /> |
| | | </div> |
| | | |
| | | <!-- 右侧区域 --> |
| | | <div class="right-panel"> |
| | | <!-- 应收应付统计 --> |
| | | <RightTop /> |
| | | |
| | | <!-- 回款与开票分析 --> |
| | | <RightBottom /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import * as echarts from 'echarts' |
| | | import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' |
| | | import autofit from 'autofit.js' |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import { |
| | | analysisCustomerContractAmounts, getAmountHalfYear, |
| | | homeTodos, |
| | | qualityStatistics, |
| | | statisticsReceivablePayable |
| | | } from "@/api/viewIndex.js"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/employeeRecord.js"; |
| | | import {listCustomer} from "@/api/basicData/customerFile.js"; |
| | | import {listSupplier} from "@/api/basicData/supplierManageFile.js"; |
| | | import {getLedgerPage} from "@/api/equipmentManagement/ledger.js"; |
| | | import {getRepairPage} from "@/api/equipmentManagement/repair.js"; |
| | | import {getUpkeepPage} from "@/api/equipmentManagement/upkeep.js"; |
| | | import {measuringInstrumentListPage} from "@/api/equipmentManagement/measurementEquipment.js"; |
| | | import {listPageAnalysis} from "@/api/financialManagement/expenseManagement.js"; |
| | | import LeftTop from './components/basic/left-top.vue' |
| | | import LeftBottom from './components/basic/left-bottom.vue' |
| | | import CenterTop from './components/basic/center-top.vue' |
| | | import CenterBottom from './components/basic/center-bottom.vue' |
| | | import RightTop from './components/basic/right-top.vue' |
| | | import RightBottom from './components/basic/right-bottom.vue' |
| | | import useUserStore from '@/store/modules/user' |
| | | |
| | | // 全屏相关状态 |
| | | const isFullscreen = ref(false); |
| | | |
| | | // 响应式数据 |
| | | const currentTime = ref('') |
| | | const currentDate = ref('') |
| | | const timer = ref(null) |
| | | const charts = ref([]) |
| | | // 缩放比例 |
| | | const scaleRatio = ref(1) |
| | | // 设计尺寸(基准尺寸)- 根据实际设计稿调整 |
| | | const designWidth = 1920 |
| | | const designHeight = 1080 |
| | | |
| | | // 图表引用 |
| | | const customerPieChartRef = ref(null) |
| | | const salesBarChartRef = ref(null) |
| | | const dataBarChartRef = ref(null) |
| | | const financialAreaChartRef = ref(null) |
| | | const realtimeLineChartRef = ref(null) |
| | | const refContractList = ref(null) |
| | | const refTodoList = ref(null) |
| | | const timerScroll = ref(null) |
| | | // 用户store |
| | | const userStore = useUserStore() |
| | | |
| | | const chartStylePie = { |
| | | width: '140%', |
| | | height: '140%' // 设置图表容器的高度 |
| | | } |
| | | const materialPieSeries = ref([ |
| | | { |
| | | type: 'pie', |
| | | radius: ['0%', '90%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 0 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | data: [] |
| | | } |
| | | ]) |
| | | const pieLegend = reactive({ |
| | | show: false, |
| | | }) |
| | | const sum = ref(0) |
| | | const totalStaff = ref(0) |
| | | const totalCustomers = ref(0) |
| | | const totalSuppliers = ref(0) |
| | | const yny = ref(0) |
| | | const chain = ref(0) |
| | | const equipmentNum = ref(0) |
| | | const equipmentRepair = ref(0) |
| | | const equipmentMaintain = ref(0) |
| | | const totalMeasuring = ref(0) |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function (params) { |
| | | // 动态生成提示信息,基于数据项的 name 属性 |
| | | const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额'; |
| | | return `<div style="color: #B8C8E0">${description} ${params.value}元 ${params.percent}%</div>`; |
| | | }, |
| | | position: 'right' |
| | | }) |
| | | // 计算缩放比例 |
| | | const calculateScale = () => { |
| | | const container = document.querySelector('.scale-container') |
| | | if (!container) return |
| | | |
| | | const qualityStatisticsObject = ref({ |
| | | supplierNum: 0, |
| | | processNum: 0, |
| | | factoryNum: 0, |
| | | }) |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '150%' // 设置图表容器的高度 |
| | | } |
| | | const barSeries = ref([ |
| | | { |
| | | name: '应付金额', |
| | | type: 'bar', |
| | | data: [], |
| | | label: { |
| | | show: true, |
| | | }, |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: '#00A4ED' }, |
| | | { offset: 1, color: '#4EE4FF' } |
| | | ]) |
| | | } |
| | | }, |
| | | { |
| | | name: '应收金额', |
| | | type: 'bar', |
| | | data: [], |
| | | label: { |
| | | show: true, |
| | | }, |
| | | itemStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: '#537EF5' }, |
| | | { offset: 1, color: '#9061F8' } |
| | | ]) |
| | | } |
| | | } |
| | | ]) |
| | | const radio1 = ref(1) |
| | | const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8'] |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | } |
| | | const lineLegend = { |
| | | show: true, |
| | | textStyle: { color: '#B8C8E0' }, |
| | | data: ['开票', '回款'] |
| | | } |
| | | const lineSeries = ref([ |
| | | { |
| | | type: 'line', |
| | | data: [], |
| | | label: { |
| | | show: true |
| | | }, |
| | | showSymbol: true, // 显示圆点 |
| | | }, |
| | | ]) |
| | | const tooltipLine = { |
| | | trigger: 'axis', |
| | | } |
| | | const yAxis2 = ref([ |
| | | { |
| | | type: 'value', |
| | | } |
| | | ]) |
| | | const xAxis2 = ref([ |
| | | { |
| | | type: 'category', |
| | | data: [], |
| | | axisLabel: { |
| | | interval: 0, |
| | | formatter: function(value) { |
| | | return value.replace(/~/g, '\n'); |
| | | }, |
| | | } |
| | | } |
| | | ]) |
| | | const barLegend2 = { |
| | | show: true, |
| | | textStyle: { color: '#B8C8E0' }, |
| | | data: ['应付金额', '应收金额'] |
| | | } |
| | | const barLegend = { |
| | | show: true, |
| | | textStyle: { color: '#B8C8E0' }, |
| | | data: ['原材料不合格数', '过程不合格数', '出厂不合格数'] |
| | | } |
| | | const barLegend1 = { |
| | | show: true, |
| | | textStyle: { color: '#B8C8E0' }, |
| | | data: ['总收入', '总支出', '净收入'] |
| | | } |
| | | const barSeries11 = ref([ |
| | | { |
| | | name: '总收入', |
| | | type: 'bar', |
| | | barGap: 0, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: '#00A4ED' }, |
| | | { offset: 0, color: '#4EE4FF' } |
| | | ] |
| | | } |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: '总支出', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: '#3378FF' }, |
| | | { offset: 0, color: '#4E8AFF' } |
| | | ] |
| | | } |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: '净收入', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: '#537EF5' }, |
| | | { offset: 0, color: '#9061F8' } |
| | | ] |
| | | } |
| | | }, |
| | | data: [] |
| | | }, |
| | | ]) |
| | | const barSeries1 = ref([ |
| | | { |
| | | name: '原材料不合格数', |
| | | type: 'bar', |
| | | barGap: 0, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: '#00A4ED' }, |
| | | { offset: 0, color: '#4EE4FF' } |
| | | ] |
| | | } |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: '过程不合格数', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: '#3378FF' }, |
| | | { offset: 0, color: '#4E8AFF' } |
| | | ] |
| | | } |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: '出厂不合格数', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | itemStyle: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { offset: 1, color: '#537EF5' }, |
| | | { offset: 0, color: '#9061F8' } |
| | | ] |
| | | } |
| | | }, |
| | | data: [] |
| | | }, |
| | | ]) |
| | | const tooltip = { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | }, |
| | | formatter: function (params) { |
| | | let result = params[0].axisValueLabel + '<br/>'; |
| | | params.forEach(item => { |
| | | result += `<div style="color: #B8C8E0">${item.marker} ${item.seriesName}: ${item.value}</div>`; |
| | | }); |
| | | return result; |
| | | } |
| | | } |
| | | const xAxis = [{ |
| | | type: 'value', |
| | | }] |
| | | const yAxis = [{ |
| | | type: 'category', |
| | | data: ['应收应付统计'] |
| | | }] |
| | | const xAxis1 = ref([{ |
| | | type: 'category', |
| | | axisTick: { show: false }, |
| | | axisLabel: { color: '#B8C8E0' }, |
| | | data: [] |
| | | }]) |
| | | const yAxis1 = [{ |
| | | type: 'value', |
| | | axisLabel: { color: '#B8C8E0' } |
| | | }] |
| | | const xAxis3 = ref([{ |
| | | type: 'category', |
| | | axisTick: { show: false }, |
| | | axisLabel: { color: '#B8C8E0' }, |
| | | data: [] |
| | | }]) |
| | | const yAxis3 = [{ |
| | | type: 'value', |
| | | axisLabel: { color: '#B8C8E0' } |
| | | }] |
| | | // 获取容器的实际尺寸 |
| | | const rect = container.getBoundingClientRect?.() |
| | | const containerWidth = container.clientWidth || rect?.width || window.innerWidth |
| | | const containerHeight = container.clientHeight || rect?.height || window.innerHeight |
| | | |
| | | // 待办事项 |
| | | const todoList = ref([]) |
| | | // 计算宽高缩放比例,取较小值以保证内容完整显示(等比缩放) |
| | | const scaleX = containerWidth / designWidth |
| | | const scaleY = containerHeight / designHeight |
| | | scaleRatio.value = Math.min(scaleX, scaleY) |
| | | } |
| | | |
| | | // 窗口大小变化处理 |
| | | const handleResize = () => { |
| | | charts.value.forEach(chart => { |
| | | if (chart && chart.resize) { |
| | | chart.resize() |
| | | } |
| | | }) |
| | | // 延迟执行,确保DOM更新完成 |
| | | setTimeout(() => { |
| | | calculateScale() |
| | | }, 100) |
| | | } |
| | | |
| | | // 销毁图表实例 |
| | | const disposeCharts = () => { |
| | | charts.value.forEach(chart => { |
| | | if (chart && chart.dispose) { |
| | | chart.dispose() |
| | | } |
| | | }) |
| | | charts.value = [] |
| | | } |
| | | // 合同金额 |
| | | const analysisCustomer = () => { |
| | | analysisCustomerContractAmounts().then((res) => { |
| | | sum.value = res.data.sum |
| | | yny.value = res.data.yny |
| | | chain.value = res.data.chain |
| | | // 为每个数据项分配随机颜色 |
| | | materialPieSeries.value[0].data = res.data.item.map(item => ({ |
| | | ...item, |
| | | itemStyle: { color: getRandomColor() } |
| | | })) |
| | | }) |
| | | } |
| | | // 质检统计 |
| | | const qualityStatisticsInfo = () => { |
| | | qualityStatistics().then((res) => { |
| | | res.data.item.forEach(item => { |
| | | xAxis1.value[0].data.push(item.date) |
| | | barSeries1.value[0].data.push(item.supplierNum) |
| | | barSeries1.value[1].data.push(item.processNum) |
| | | barSeries1.value[2].data.push(item.factoryNum) |
| | | }) |
| | | qualityStatisticsObject.value.supplierNum = res.data.supplierNum |
| | | qualityStatisticsObject.value.processNum = res.data.processNum |
| | | qualityStatisticsObject.value.factoryNum = res.data.factoryNum |
| | | }) |
| | | } |
| | | // 财务统计 |
| | | const accountStatisticsInfo = () => { |
| | | listPageAnalysis().then((res) => { |
| | | xAxis3.value[0].data = res.data.days |
| | | barSeries11.value[0].data = res.data.totalIncome |
| | | barSeries11.value[1].data = res.data.totalExpense |
| | | barSeries11.value[2].data = res.data.netIncome |
| | | }) |
| | | } |
| | | const getNum = () => { |
| | | const params = { |
| | | pageNum: -1, |
| | | pageSize: -1, |
| | | } |
| | | staffOnJobListPage({...params, staffState: 1}).then(res => { |
| | | totalStaff.value = res.data.total |
| | | }) |
| | | listCustomer(params).then((res) => { |
| | | totalCustomers.value = res.total; |
| | | }); |
| | | listSupplier(params).then((res) => { |
| | | totalSuppliers.value = res.data.total |
| | | }); |
| | | } |
| | | const getLedgerNum = () => { |
| | | const params = { |
| | | pageNum: -1, |
| | | pageSize: -1, |
| | | } |
| | | getLedgerPage(params).then((res) => { |
| | | equipmentNum.value = res.data.total |
| | | }); |
| | | getRepairPage(params).then((res) => { |
| | | equipmentRepair.value = res.data.total |
| | | }); |
| | | getUpkeepPage(params).then((res) => { |
| | | equipmentMaintain.value = res.data.total |
| | | }); |
| | | measuringInstrumentListPage(params).then((res) => { |
| | | totalMeasuring.value = res.data.total |
| | | }); |
| | | } |
| | | // 待办事项 |
| | | const todoInfoS = () => { |
| | | homeTodos().then((res) => { |
| | | todoList.value = res.data |
| | | // 在获取到待办事项数据后,初始化滚动功能 |
| | | nextTick(() => { |
| | | initTodoListScroll() |
| | | }) |
| | | }) |
| | | } |
| | | // 应付应收统计 |
| | | const statisticsReceivable = (type) => { |
| | | statisticsReceivablePayable({type: radio1.value}).then((res) => { |
| | | // 设置应付金额数据 |
| | | barSeries.value[0].data = [ |
| | | { value: res.data.payableMoney } |
| | | ] |
| | | // 设置应收金额数据 |
| | | barSeries.value[1].data = [ |
| | | { value: res.data.receivableMoney } |
| | | ] |
| | | }) |
| | | } |
| | | const getAmountHalfYearNum = async () => { |
| | | const res = await getAmountHalfYear() |
| | | console.log(res) |
| | | const monthName = [] |
| | | const receiptAmount = [] |
| | | const invoiceAmount = [] |
| | | res.data.forEach(item => { |
| | | monthName.push(item.month) |
| | | receiptAmount.push(item.receiptAmount) |
| | | invoiceAmount.push(item.invoiceAmount) |
| | | }) |
| | | // 正确响应式赋值:创建新的 xAxis 和 series 对象 |
| | | xAxis2.value[0].data = monthName |
| | | xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~')); |
| | | lineSeries.value = [ |
| | | { |
| | | name: '开票', |
| | | type: 'line', |
| | | data: receiptAmount, |
| | | stack: 'Total', |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(131, 207, 255, 1)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(186, 228, 255, 1)' |
| | | } |
| | | ]) |
| | | }, |
| | | itemStyle: { |
| | | color: '#2D99FF', |
| | | borderColor: '#2D99FF' |
| | | }, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | lineStyle: { |
| | | width: 0 |
| | | }, |
| | | showSymbol: true, |
| | | }, |
| | | { |
| | | name: '回款', |
| | | type: 'line', |
| | | data: invoiceAmount, |
| | | stack: 'Total', |
| | | lineStyle: { |
| | | width: 0 |
| | | }, |
| | | itemStyle: { |
| | | color: '#83CFFF', |
| | | borderColor: '#83CFFF' |
| | | }, |
| | | showSymbol: true, |
| | | areaStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(54, 153, 255, 1)' |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(89, 169, 254, 1)' |
| | | } |
| | | ]) |
| | | }, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | } |
| | | ] |
| | | } |
| | | |
| | | // 自动轮换周、月、季度的定时器 |
| | | const autoSwitchTimer = ref(null) |
| | | // 初始化待办事项列表滚动功能 |
| | | const initTodoListScroll = () => { |
| | | const todoList = refTodoList.value |
| | | // 强制启用滚动,不检查任何条件 |
| | | if (todoList) { |
| | | // 创建一个克隆项,用于实现无缝滚动 |
| | | const scrollItems = Array.from(todoList.querySelectorAll('li')) |
| | | if (scrollItems.length > 0) { |
| | | // 确保有足够的项目用于滚动 |
| | | // 如果项目太少,多复制几次以确保滚动效果 |
| | | if (scrollItems.length < 4) { |
| | | const originalItems = [...scrollItems] |
| | | for (let i = 0; i < 4; i++) { |
| | | originalItems.forEach(item => { |
| | | const clone = item.cloneNode(true) |
| | | todoList.appendChild(clone) |
| | | }) |
| | | } |
| | | // 重新获取所有项目 |
| | | scrollItems.push(...Array.from(todoList.querySelectorAll('li')).slice(scrollItems.length)); |
| | | } |
| | | const itemHeight = scrollItems[0]?.offsetHeight || 0 |
| | | const containerHeight = todoList.clientHeight |
| | | const cloneCount = Math.ceil(containerHeight / itemHeight) + 2 |
| | | |
| | | // 克隆前几个项目并添加到列表末尾,实现无缝滚动 |
| | | for (let i = 0; i < cloneCount; i++) { |
| | | const clone = scrollItems[i % scrollItems.length].cloneNode(true) |
| | | todoList.appendChild(clone) |
| | | } |
| | | |
| | | let scrollPosition = 0 |
| | | const scrollSpeed = 1.5 // 增加滚动速度,使滚动更加明显 |
| | | const pauseTime = 3000 // 滚动暂停时间 |
| | | let isPaused = false |
| | | let lastTimestamp = 0 |
| | | |
| | | // 连续滚动动画函数 |
| | | function scrollAnimation(timestamp) { |
| | | if (!lastTimestamp) lastTimestamp = timestamp |
| | | const deltaTime = timestamp - lastTimestamp |
| | | lastTimestamp = timestamp |
| | | |
| | | if (!isPaused) { |
| | | scrollPosition += scrollSpeed * (deltaTime / 16) // 标准化为60fps的速度 |
| | | |
| | | // 当滚动超过原始内容长度时,重置位置实现无缝滚动 |
| | | const maxScroll = Math.max(todoList.scrollHeight - containerHeight - cloneCount * itemHeight, itemHeight * scrollItems.length) |
| | | if (scrollPosition >= maxScroll) { |
| | | scrollPosition = 0 |
| | | todoList.scrollTop = 0 |
| | | } else { |
| | | todoList.scrollTop = scrollPosition |
| | | } |
| | | } |
| | | |
| | | todoList._animationFrame = requestAnimationFrame(scrollAnimation) |
| | | } |
| | | |
| | | // 启动滚动动画 |
| | | todoList._animationFrame = requestAnimationFrame(scrollAnimation) |
| | | |
| | | // 设置滚动-暂停-滚动的循环效果 |
| | | const pauseTimer = setInterval(() => { |
| | | isPaused = !isPaused |
| | | }, pauseTime) |
| | | |
| | | // 清理定时器 |
| | | todoList._pauseTimer = pauseTimer |
| | | } |
| | | } |
| | | } |
| | | const getRandomColor = () => { |
| | | // 生成浅色:R、G、B 分量都在 150-255 之间 |
| | | const r = Math.floor(Math.random() * 106) + 150; // 150-255 |
| | | const g = Math.floor(Math.random() * 106) + 150; // 150-255 |
| | | const b = Math.floor(Math.random() * 106) + 150; // 150-255 |
| | | // 将 RGB 转换为十六进制颜色 |
| | | return '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0'); |
| | | } |
| | | |
| | | // 更新时间 |
| | | const updateTime = () => { |
| | | const now = new Date() |
| | | currentTime.value = now.toLocaleTimeString('zh-CN', { hour12: false }) |
| | | currentDate.value = now.toLocaleDateString('zh-CN', { |
| | | year: 'numeric', |
| | | month: '2-digit', |
| | | day: '2-digit', |
| | | weekday: 'long' |
| | | }) |
| | | } |
| | | |
| | | // 初始化时间 |
| | | const initTime = () => { |
| | | updateTime() |
| | | timer.value = setInterval(updateTime, 1000) |
| | | } |
| | | // 全屏功能实现 - 针对data-dashboard元素 |
| | | // 全屏功能实现 - 针对scale-container元素 |
| | | const toggleFullscreen = () => { |
| | | const element = document.querySelector('.data-dashboard') |
| | | |
| | | if (!element) return |
| | | |
| | | if (!isFullscreen.value) { |
| | | if (element.requestFullscreen) { |
| | | element.requestFullscreen() |
| | | } else if (element.webkitRequestFullscreen) { |
| | | element.webkitRequestFullscreen() |
| | | } else if (element.msRequestFullscreen) { |
| | | element.msRequestFullscreen() |
| | | } |
| | | } else { |
| | | if (document.exitFullscreen) { |
| | | document.exitFullscreen() |
| | | } else if (document.webkitExitFullscreen) { |
| | | document.webkitExitFullscreen() |
| | | } else if (document.msExitFullscreen) { |
| | | document.msExitFullscreen() |
| | | } |
| | | } |
| | | const element = document.querySelector('.scale-container') |
| | | |
| | | if (!element) return |
| | | |
| | | if (!isFullscreen.value) { |
| | | if (element.requestFullscreen) { |
| | | element.requestFullscreen() |
| | | } else if (element.webkitRequestFullscreen) { |
| | | element.webkitRequestFullscreen() |
| | | } else if (element.msRequestFullscreen) { |
| | | element.msRequestFullscreen() |
| | | } |
| | | } else { |
| | | if (document.exitFullscreen) { |
| | | document.exitFullscreen() |
| | | } else if (document.webkitExitFullscreen) { |
| | | document.webkitExitFullscreen() |
| | | } else if (document.msExitFullscreen) { |
| | | document.msExitFullscreen() |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 监听全屏变化事件 |
| | |
| | | const fullscreenElement = document.fullscreenElement || |
| | | document.webkitFullscreenElement || |
| | | document.msFullscreenElement |
| | | isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('data-dashboard') |
| | | isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container') |
| | | |
| | | // 全屏状态变化时,延迟重新计算缩放比例(确保DOM更新完成) |
| | | setTimeout(() => { |
| | | calculateScale() |
| | | }, 200) |
| | | } |
| | | |
| | | // 生命周期钩子 |
| | | onMounted(() => { |
| | | initTime() |
| | | // 使用nextTick确保DOM完全渲染后再初始化图表 |
| | | // 使用nextTick确保DOM完全渲染后再初始化 |
| | | nextTick(() => { |
| | | // 初始化autofit自适应 |
| | | autofit.init({ dh: 1440, dw: 2560, el: '.data-dashboard', resize: true }, false) |
| | | |
| | | // 添加自动滚动动画效果 - 客户信息列表 |
| | | const contractList = refContractList.value |
| | | if (contractList && contractList.scrollHeight > contractList.clientHeight) { |
| | | // 创建一个克隆项,用于实现无缝滚动 |
| | | const scrollItems = Array.from(contractList.querySelectorAll('li')) |
| | | const itemHeight = scrollItems[0]?.offsetHeight || 0 |
| | | const containerHeight = contractList.clientHeight |
| | | const cloneCount = Math.ceil(containerHeight / itemHeight) + 2 |
| | | |
| | | // 克隆前几个项目并添加到列表末尾,实现无缝滚动 |
| | | for (let i = 0; i < cloneCount; i++) { |
| | | const clone = scrollItems[i % scrollItems.length].cloneNode(true) |
| | | contractList.appendChild(clone) |
| | | } |
| | | |
| | | let scrollPosition = 0 |
| | | const scrollSpeed = 1.5 // 增加滚动速度,使滚动更加明显 |
| | | const pauseTime = 3000 // 滚动暂停时间 |
| | | let isPaused = false |
| | | let lastTimestamp = 0 |
| | | |
| | | // 连续滚动动画函数 |
| | | function scrollAnimation(timestamp) { |
| | | if (!lastTimestamp) lastTimestamp = timestamp |
| | | const deltaTime = timestamp - lastTimestamp |
| | | lastTimestamp = timestamp |
| | | |
| | | if (!isPaused) { |
| | | scrollPosition += scrollSpeed * (deltaTime / 16) // 标准化为60fps的速度 |
| | | |
| | | // 当滚动超过原始内容长度时,重置位置实现无缝滚动 |
| | | if (scrollPosition >= contractList.scrollHeight - containerHeight - cloneCount * itemHeight) { |
| | | scrollPosition = 0 |
| | | contractList.scrollTop = 0 |
| | | } else { |
| | | contractList.scrollTop = scrollPosition |
| | | } |
| | | } |
| | | |
| | | timerScroll.value = requestAnimationFrame(scrollAnimation) |
| | | } |
| | | |
| | | // 启动滚动动画 |
| | | timerScroll.value = requestAnimationFrame(scrollAnimation) |
| | | |
| | | // 设置滚动-暂停-滚动的循环效果 |
| | | const pauseTimer = setInterval(() => { |
| | | isPaused = !isPaused |
| | | }, pauseTime) |
| | | |
| | | // 清理定时器 |
| | | contractList._pauseTimer = pauseTimer |
| | | } |
| | | |
| | | // 待办事项列表滚动功能已移至todoInfoS函数中,在获取数据后初始化 |
| | | // 计算初始缩放比例 |
| | | calculateScale() |
| | | }) |
| | | |
| | | |
| | | window.addEventListener('resize', handleResize) |
| | | analysisCustomer() |
| | | qualityStatisticsInfo() |
| | | accountStatisticsInfo() |
| | | getNum() |
| | | getLedgerNum() |
| | | todoInfoS() |
| | | statisticsReceivable() |
| | | getAmountHalfYearNum() |
| | | |
| | | // 设置自动轮换周、月、季度的定时器,每10秒切换一次 |
| | | autoSwitchTimer.value = setInterval(() => { |
| | | // 循环切换:1(周) -> 2(月) -> 3(季度) -> 1(周) |
| | | radio1.value = radio1.value === 3 ? 1 : radio1.value + 1 |
| | | statisticsReceivable() |
| | | }, 10000) // 10秒切换一次 |
| | | window.addEventListener('fullscreenchange', handleFullscreenChange) |
| | | window.addEventListener('webkitfullscreenchange', handleFullscreenChange) |
| | | window.addEventListener('MSFullscreenChange', handleFullscreenChange) |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | | if (timer.value) { |
| | | clearInterval(timer.value) |
| | | } |
| | | if (timerScroll.value) { |
| | | cancelAnimationFrame(timerScroll.value) |
| | | } |
| | | // 清理滚动列表的暂停定时器 |
| | | const contractList = refContractList.value |
| | | if (contractList && contractList._pauseTimer) { |
| | | clearInterval(contractList._pauseTimer) |
| | | } |
| | | |
| | | // 清理待办事项列表的动画和定时器 |
| | | const todoList = refTodoList.value |
| | | if (todoList) { |
| | | if (todoList._animationFrame) { |
| | | cancelAnimationFrame(todoList._animationFrame) |
| | | todoList._animationFrame = null |
| | | } |
| | | if (todoList._pauseTimer) { |
| | | clearInterval(todoList._pauseTimer) |
| | | todoList._pauseTimer = null |
| | | } |
| | | } |
| | | |
| | | // 清理自动轮换周、月、季度的定时器 |
| | | if (autoSwitchTimer.value) { |
| | | clearInterval(autoSwitchTimer.value) |
| | | autoSwitchTimer.value = null |
| | | } |
| | | |
| | | window.removeEventListener('resize', handleResize) |
| | | window.removeEventListener('fullscreenchange', handleFullscreenChange) |
| | | window.removeEventListener('webkitfullscreenchange', handleFullscreenChange) |
| | |
| | | window.removeEventListener('resize', window._autofitUpdateHandler) |
| | | delete window._autofitUpdateHandler |
| | | } |
| | | disposeCharts() |
| | | // 关闭autofit |
| | | autofit.off() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .data-dashboard { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 100%; |
| | | overflow: hidden; |
| | | background-image: url("@/assets/BI/backImage@2x.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | /* 外部缩放容器 - 占据整个视口 */ |
| | | .scale-container { |
| | | position: relative; |
| | | width: 100%; |
| | | /* 页面在常规布局下(有顶栏)默认减去 84px,避免内容被裁切 */ |
| | | height: calc(100vh - 84px); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background-color: #000; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* 全屏状态的样式 */ |
| | | .data-dashboard:fullscreen { |
| | | width: 100%; |
| | | height: 100%; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: inherit; |
| | | z-index: 9999; |
| | | /* 内部内容区域 - 固定设计尺寸 */ |
| | | .data-dashboard { |
| | | 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; |
| | | } |
| | | |
| | | /* 全屏状态的样式 - 作用于scale-container */ |
| | | .scale-container:fullscreen { |
| | | width: 100vw; |
| | | height: 100vh; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #000; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | /* Webkit浏览器前缀 */ |
| | | .data-dashboard:-webkit-full-screen { |
| | | width: 100%; |
| | | height: 100%; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: inherit; |
| | | z-index: 9999; |
| | | .scale-container:-webkit-full-screen { |
| | | width: 100vw; |
| | | height: 100vh; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #000; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | /* MS浏览器前缀 */ |
| | | .data-dashboard:-ms-fullscreen { |
| | | width: 100%; |
| | | height: 100%; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: inherit; |
| | | z-index: 9999; |
| | | .scale-container:-ms-fullscreen { |
| | | width: 100vw; |
| | | height: 100vh; |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #000; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | |
| | | .dashboard-header { |
| | | position: relative; |
| | | z-index: 1; |
| | | height: 170px; |
| | | background-image: url("@/assets/BI/biaoti.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | 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; |
| | | } |
| | | |
| | | .factory-name { |
| | | font-weight: 600; |
| | | font-size: 52px; |
| | | color: #FFFFFF; |
| | | top: 16px; |
| | | position: absolute; |
| | | } |
| | | |
| | | .fullscreen-btn { |
| | | position: absolute; |
| | | top: 10px; |
| | | 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; |
| | | cursor: pointer; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s; |
| | | z-index: 10000; |
| | | position: absolute; |
| | | top: 10px; |
| | | 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; |
| | | cursor: pointer; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s; |
| | | z-index: 10000; |
| | | } |
| | | |
| | | .fullscreen-btn:hover { |
| | | background: rgba(0, 30, 90, 0.9); |
| | | border-color: rgba(0, 212, 255, 0.5); |
| | | background: rgba(0, 30, 90, 0.9); |
| | | border-color: rgba(0, 212, 255, 0.5); |
| | | } |
| | | |
| | | .dashboard-content { |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | gap: 30px; |
| | | padding: 0 30px; |
| | | height: calc(100% - 120px); |
| | | overflow: hidden; |
| | | position: relative; |
| | | z-index: 1; |
| | | display: flex; |
| | | gap: 30px; |
| | | padding: 0 30px; |
| | | height: calc(100% - 86px); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* 确保各面板能够正确显示 */ |
| | | .left-panel, .center-panel, .right-panel { |
| | | overflow: hidden; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .left-panel, |
| | | .right-panel { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24px; |
| | | width: 520px; |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 24px; |
| | | width: 520px; |
| | | } |
| | | |
| | | .center-panel { |
| | | flex: 1.5; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | .panel-item-customers { |
| | | border: 1px solid #1A58B0; |
| | | padding: 18px; |
| | | width: 100%; |
| | | height: 540px; |
| | | } |
| | | .panel-title-second { |
| | | height: 60px; |
| | | display: flex; |
| | | gap: 12px; |
| | | margin-bottom: 20px; |
| | | align-items: center; |
| | | } |
| | | .quality-cards { |
| | | display: flex; |
| | | gap: 12px; |
| | | width: 100%; |
| | | height: 94px; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | .quality-cardSec { |
| | | display: flex; |
| | | } |
| | | .quality-cardTitle { |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #FFFFFF; |
| | | display: flex; |
| | | align-items: flex-start; |
| | | flex-direction: column; |
| | | } |
| | | .quality-card { |
| | | width: 80px; |
| | | height: 60px; |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | .quality-card.one { |
| | | background-image: url("@/assets/BI/yuancailiaoyijianicon@2x.png"); |
| | | } |
| | | .quality-card.two { |
| | | background-image: url("@/assets/BI/guochengyijianicon@2x.png"); |
| | | } |
| | | .quality-card.three { |
| | | background-image: url("@/assets/BI/chuchangyijianicon@2x.png"); |
| | | |
| | | } |
| | | .panel-title-icon { |
| | | width: 60px; |
| | | height: 60px; |
| | | background-image: url("@/assets/BI/hetongicon.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | flex: 1.5; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | .total-customers { |
| | | background-image: url("@/assets/BI/hetongjineback@2x.png"); |
| | | background-size: cover; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | width: 90%; |
| | | height: 60px; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 20px; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .total-customers .label { |
| | | font-weight: 500; |
| | | font-size: 16px; |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | .total-customers .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; |
| | | } |
| | | |
| | | .contract-list { |
| | | margin-top: 16px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | list-style: none; |
| | | padding: 0; |
| | | height: 82%; |
| | | overflow-y: auto; |
| | | width: 460px; |
| | | /* 隐藏滚动条但保留滚动功能 */ |
| | | scrollbar-width: none; /* Firefox */ |
| | | -ms-overflow-style: none; /* IE和Edge */ |
| | | } |
| | | |
| | | /* Chrome、Safari和Opera */ |
| | | .contract-list::-webkit-scrollbar { |
| | | display: none; |
| | | } |
| | | .line { |
| | | position: relative; |
| | | width: 230px; |
| | | } |
| | | .line::after { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 2px; |
| | | top: 0; |
| | | bottom: 0; |
| | | width: 1px; |
| | | background-color: #C9C5C5; |
| | | border-radius: 2px; |
| | | } |
| | | .contract-list li { |
| | | margin-top: 10px; |
| | | } |
| | | .stats-cards { |
| | | display: flex; |
| | | gap: 30px; |
| | | } |
| | | |
| | | .stat-card { |
| | | 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; |
| | | height: 142px; |
| | | } |
| | | |
| | | .card-icon { |
| | | width: 100px; |
| | | height: 100px; |
| | | margin: 20px 20px 0 10px; |
| | | } |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .card-label { |
| | | font-weight: 400; |
| | | font-size: 19px; |
| | | color: rgba(208,231,255,0.7); |
| | | } |
| | | |
| | | .equipment-stats { |
| | | border: 1px solid #1A58B0; |
| | | padding: 18px; |
| | | height: 240px; |
| | | } |
| | | .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: 21px; |
| | | 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; |
| | | } |
| | | .equipment-items { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | gap: 30px; |
| | | } |
| | | |
| | | .equipment-item { |
| | | text-align: center; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .equipment-label { |
| | | font-weight: 500; |
| | | font-size: 21px; |
| | | color: #FFFFFE; |
| | | } |
| | | |
| | | .event-info { |
| | | background-image: url("@/assets/BI/shijianmingchengbeijing@2x.png"); |
| | | background-size: 100% 100%; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | padding: 20px; |
| | | height: 186px; |
| | | } |
| | | .event-header { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .event-icon { |
| | | width: 40px; |
| | | height: 40px; |
| | | } |
| | | .event-title { |
| | | font-weight: 500; |
| | | font-size: 24px; |
| | | color: #FFFFFE; |
| | | line-height: 30px; |
| | | } |
| | | .todo-list { |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | height: 120px; /* 按用户要求调整高度 */ |
| | | overflow: hidden; |
| | | font-size: 15px; |
| | | } |
| | | .todo-list li { |
| | | border-radius: 8px; |
| | | margin-bottom: 12px; |
| | | padding: 12px 40px; |
| | | height: 74px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | .todo-title { |
| | | font-weight: 400; |
| | | font-size: 20px; |
| | | color: #FFFFFE; |
| | | position: relative; |
| | | } |
| | | .todo-title::before { |
| | | content: ''; /* 必需,表示这里有一个内容 */ |
| | | position: absolute; |
| | | left: -10px; /* 定位到左侧 */ |
| | | top: 50%; /* 垂直居中 */ |
| | | transform: translateY(-50%); /* 微调垂直居中 */ |
| | | width: 6px; /* 圆的直径 */ |
| | | height: 6px; /* 圆的直径 */ |
| | | background: #498CEB; |
| | | border-radius: 50%; /* 让其变成圆形 */ |
| | | } |
| | | .todo-division { |
| | | font-weight: 400; |
| | | font-size: 20px; |
| | | color: #FFFFFE; |
| | | } |
| | | .todo-time { |
| | | font-weight: 400; |
| | | font-size: 20px; |
| | | color: #FFFFFE; |
| | | } |
| | | .financial-header { |
| | | background-image: url("@/assets/BI/caiwufenxiback@2x.png"); |
| | | background-size: 100% 100%; |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | } |
| | | .financial-title { |
| | | width: 100%; |
| | | font-weight: 500; |
| | | font-size: 16px; |
| | | color: #D9ECFF; |
| | | padding-left: 46px; |
| | | line-height: 36px; |
| | | } |
| | | |
| | | /* 自定义单选按钮组样式 */ |
| | | .custom-radio-group :deep(.el-radio-button__inner) { |
| | | background-color: transparent; |
| | | color: white; |
| | | border-color: rgba(255, 255, 255, 0.3); |
| | | } |
| | | |
| | | .custom-radio-group :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) { |
| | | background-color: rgba(255, 255, 255, 0.2); |
| | | color: white; |
| | | border-color: rgba(255, 255, 255, 0.5); |
| | | box-shadow: -1px 0 0 0 rgba(255, 255, 255, 0.5); |
| | | } |
| | | </style> |