<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="dashboard-header">
|
<div class="factory-name">{{ userStore.currentFactoryName }}</div>
|
</div>
|
|
<div class="dashboard-content">
|
<div class="content-main">
|
<div class="top-panels">
|
<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>
|
<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;"
|
/>
|
</div>
|
<ul
|
ref="refContractList"
|
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;"
|
>
|
<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>
|
|
<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="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 v-if="todoList.length > 0" ref="refTodoList" class="todo-list">
|
<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>
|
</div>
|
|
<div class="financial-header">
|
<span class="financial-title">财务分析</span>
|
</div>
|
<div class="main-panel">
|
<div class="panel-item-customers1 financial-panel1">
|
<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="chartStyle2"
|
:grid="grid"
|
:legend="barLegend1"
|
:series="barSeries11"
|
:tooltip="tooltip"
|
:xAxis="xAxis3"
|
:yAxis="yAxis3"
|
:options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }"
|
style="height: 300px"
|
/>
|
</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"
|
/>
|
</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;"
|
/>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import * as echarts from 'echarts'
|
import { reactive, ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
import autofit from 'autofit.js'
|
import Echarts from "@/components/Echarts/echarts.vue";
|
import useUserStore from '@/store/modules/user'
|
import {
|
analysisCustomerContractAmounts, getAmountHalfYear,
|
homeTodos,
|
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 {listPageAnalysis} from "@/api/financialManagement/expenseManagement.js";
|
|
const isFullscreen = ref(false);
|
const userStore = useUserStore()
|
const currentTime = ref('')
|
const currentDate = ref('')
|
const timer = ref(null)
|
const charts = ref([])
|
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)
|
|
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 pieTooltip = reactive({
|
trigger: 'item',
|
formatter: function (params) {
|
const description = params.name === '鏈湀鍥炴閲戦' ? '鏈湀鍥炴閲戦' : '搴旀敹娆鹃噾棰?';
|
return `<div style="color: #B8C8E0">${description} ${params.value}鍏?${params.percent}%</div>`;
|
},
|
position: 'right'
|
})
|
|
const chartStyle = {
|
width: '100%',
|
height: '120%'
|
}
|
const chartStyle2 = {
|
width: '100%',
|
height: '100%'
|
}
|
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 xAxis3 = ref([{
|
type: 'category',
|
axisTick: { show: false },
|
axisLabel: { color: '#B8C8E0' },
|
data: []
|
}])
|
const yAxis3 = [{
|
type: 'value',
|
axisLabel: { color: '#B8C8E0' }
|
}]
|
|
const todoList = ref([])
|
|
const handleResize = () => {
|
charts.value.forEach(chart => {
|
if (chart && chart.resize) {
|
chart.resize()
|
}
|
})
|
}
|
|
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 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 todoInfoS = () => {
|
homeTodos().then((res) => {
|
todoList.value = res.data
|
nextTick(() => {
|
initTodoListScroll()
|
})
|
})
|
}
|
const statisticsReceivable = () => {
|
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()
|
const monthName = []
|
const receiptAmount = []
|
const invoiceAmount = []
|
res.data.forEach(item => {
|
monthName.push(item.month)
|
receiptAmount.push(item.receiptAmount)
|
invoiceAmount.push(item.invoiceAmount)
|
})
|
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)
|
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 = () => {
|
const r = Math.floor(Math.random() * 106) + 150;
|
const g = Math.floor(Math.random() * 106) + 150;
|
const b = Math.floor(Math.random() * 106) + 150;
|
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)
|
}
|
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 handleFullscreenChange = () => {
|
const fullscreenElement = document.fullscreenElement ||
|
document.webkitFullscreenElement ||
|
document.msFullscreenElement
|
isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('data-dashboard')
|
}
|
|
onMounted(() => {
|
initTime()
|
nextTick(() => {
|
autofit.init({ dh: 1080, dw: 1920, 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)
|
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
|
}
|
})
|
|
window.addEventListener('resize', handleResize)
|
analysisCustomer()
|
accountStatisticsInfo()
|
getNum()
|
todoInfoS()
|
statisticsReceivable()
|
getAmountHalfYearNum()
|
|
autoSwitchTimer.value = setInterval(() => {
|
radio1.value = radio1.value === 3 ? 1 : radio1.value + 1
|
statisticsReceivable()
|
}, 10000)
|
})
|
|
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('MSFullscreenChange', handleFullscreenChange)
|
if (window._autofitUpdateHandler) {
|
window.removeEventListener('resize', window._autofitUpdateHandler)
|
delete window._autofitUpdateHandler
|
}
|
disposeCharts()
|
autofit.off()
|
})
|
</script>
|
|
<style scoped>
|
.data-dashboard {
|
position: relative;
|
width: 100%;
|
height: 100%;
|
background-image: url("@/assets/BI/backImage@2x.png");
|
background-size: cover;
|
background-position: center;
|
background-repeat: no-repeat;
|
}
|
|
.data-dashboard:fullscreen,
|
.data-dashboard:-webkit-full-screen,
|
.data-dashboard:-ms-fullscreen {
|
width: 100%;
|
height: 100%;
|
margin: 0;
|
padding: 0;
|
background-color: inherit;
|
z-index: 9999;
|
}
|
|
.dashboard-header {
|
position: relative;
|
z-index: 1;
|
height: 60px;
|
background-image: url("@/assets/BI/biaoti.png");
|
background-size: cover;
|
background-position: center;
|
background-repeat: no-repeat;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.factory-name {
|
font-weight: 600;
|
font-size: 30px;
|
color: #FFFFFF;
|
top: 6px;
|
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;
|
}
|
|
.fullscreen-btn:hover {
|
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% - 80px);
|
overflow: hidden;
|
}
|
|
.content-main {
|
flex: 2.5;
|
min-width: 0;
|
display: flex;
|
flex-direction: column;
|
gap: 24px;
|
}
|
|
.top-panels {
|
display: flex;
|
gap: 30px;
|
}
|
|
.left-panel,
|
.center-panel,
|
.right-panel {
|
overflow: hidden;
|
}
|
|
.left-panel,
|
.right-panel {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
gap: 24px;
|
width: 520px;
|
}
|
|
.center-panel {
|
flex: 1.5;
|
display: flex;
|
flex-direction: column;
|
gap: 20px;
|
min-width: 0;
|
}
|
|
.panel-item-customers {
|
border: 1px solid #1A58B0;
|
padding: 18px;
|
width: 100%;
|
height: 500px;
|
}
|
.panel-item-customers1 {
|
border: 1px solid #1A58B0;
|
padding: 18px;
|
width: 100%;
|
height: 690px;
|
}
|
|
.panel-title-second {
|
height: 60px;
|
display: flex;
|
gap: 12px;
|
margin-bottom: 20px;
|
align-items: center;
|
}
|
|
.panel-title-icon {
|
width: 60px;
|
height: 60px;
|
background-image: url("@/assets/BI/hetongicon.png");
|
background-size: cover;
|
background-position: center;
|
background-repeat: no-repeat;
|
}
|
|
.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;
|
-ms-overflow-style: none;
|
}
|
|
.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: 128px;
|
}
|
|
.card-icon {
|
width: 80px;
|
height: 80px;
|
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: 16px;
|
color: rgba(208,231,255,0.7);
|
}
|
|
.event-info {
|
background-image: url("@/assets/BI/shijianmingchengbeijing@2x.png");
|
background-size: 100% 100%;
|
background-position: center;
|
background-repeat: no-repeat;
|
padding: 20px;
|
height: 400px;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.event-header {
|
display: flex;
|
align-items: center;
|
}
|
|
.event-content {
|
flex: 1;
|
min-height: 0;
|
margin-top: 12px;
|
}
|
|
.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: 100%;
|
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,
|
.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;
|
}
|
|
.financial-panel {
|
height: 360px;
|
}
|
.financial-panel1 {
|
height: 420px;
|
}
|
|
.main-panel .panel-item-customers {
|
height: 360px;
|
}
|
|
.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>
|