| | |
| | | <div class="dashboard"> |
| | | <!-- 顶部横向两栏 --> |
| | | <div class="dashboard-top"> |
| | | <!-- 左:企业信息+三大数据卡片(上下排列) --> |
| | | <!-- 左:企业信息 --> |
| | | <div class="top-left"> |
| | | <div class="company-info"> |
| | | <!-- 顶部问候条 --> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 销售/采购/库存数据卡片 - 注释掉 --> |
| | | <!-- |
| | | <div class="data-cards"> |
| | | <div class="data-card sales"> |
| | |
| | | <div class="data-value">{{ businessInfo.monthSaleHaveMoney }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | </div> |
| | | <div class="data-card purchase"> |
| | | <div class="data-title">采购数据</div> |
| | |
| | | </div> |
| | | </div> |
| | | --> |
| | | <!-- 右:待办事项 --> |
| | | |
| | | <!-- 待办事项 - 注释掉 --> |
| | | <!-- |
| | | <div class="todo-panel"> |
| | | <div class="section-title">待办事项</div> |
| | |
| | | </div> |
| | | --> |
| | | </div> |
| | | <!-- |
| | | |
| | | <!-- 工序数据生产统计明细 - 生产相关,保留 --> |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel process-panel"> |
| | | <div class="process-panel__header"> |
| | |
| | | |
| | | <div class="process-card"> |
| | | <div class="process-card__label">累计总投入</div> |
| | | <div class="process-card__value">{{ formatAmount(processAside.totalInput) }} |
| | | </div> |
| | | <div class="process-card__value">{{ formatAmount(processAside.totalInput) }}</div> |
| | | </div> |
| | | <div class="process-card"> |
| | | <div class="process-card__label">累计总报废</div> |
| | | <div class="process-card__value">{{ formatAmount(processAside.totalScrap) }} |
| | | </div> |
| | | <div class="process-card__value">{{ formatAmount(processAside.totalScrap) }}</div> |
| | | </div> |
| | | <div class="process-card"> |
| | | <div class="process-card__label">累计总产出</div> |
| | | <div class="process-card__value">{{ formatAmount(processAside.totalOutput) }} |
| | | </div> |
| | | <div class="process-card__value">{{ formatAmount(processAside.totalOutput) }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 选择工序对话框 --> |
| | | <el-dialog v-model="processDialogVisible" title="选择工序" width="500px" append-to-body> |
| | | <div class="process-selection-wrapper"> |
| | | <el-checkbox-group v-model="tempProcessIds"> |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | --> |
| | | <!-- 中部:客户合同金额分析 --> |
| | | |
| | | <!-- 客户合同金额分析 - 注释掉 --> |
| | | <!-- |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel contract-panel"> |
| | | <div class="section-title">客户合同金额分析</div> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | --> |
| | | |
| | | <!-- 底部横向两栏 --> |
| | | <!-- 底部横向两栏 - 质量统计和回款开票 - 注释掉 --> |
| | | <!-- |
| | | <div class="dashboard-row"> |
| | | <div class="main-panel"> |
| | |
| | | <script setup> |
| | | import { ref, onMounted, computed, reactive } from 'vue' |
| | | import Echarts from "@/components/Echarts/echarts.vue"; |
| | | import * as echarts from 'echarts'; |
| | | import useUserStore from "@/store/modules/user.js"; |
| | | import { |
| | | analysisCustomerContractAmounts, getAmountHalfYear, |
| | | getBusiness, |
| | | homeTodos, |
| | | processDataProductionStatistics, |
| | | statisticsReceivablePayable, |
| | | qualityInspectionStatistics |
| | | } from "@/api/viewIndex.js"; |
| | | import { list } from '@/api/productionManagement/productionProcess'; |
| | | |
| | | |
| | | const userStore = useUserStore() |
| | | |
| | |
| | | const tempProcessIds = ref([]) |
| | | const processDialogVisible = ref(false) |
| | | const activeProcessIndex = ref(0) |
| | | |
| | | const businessInfo = ref({ |
| | | inventoryNum: 0, |
| | | monthPurchaseHaveMoney: 0, |
| | | monthPurchaseMoney: 0, |
| | | monthSaleHaveMoney: 0, |
| | | monthSaleMoney: 0, |
| | | todayInventoryNum: 0, |
| | | }) |
| | | const qualityStatisticsObject = ref({ |
| | | supplierNum: 0, |
| | | processNum: 0, |
| | | factoryNum: 0, |
| | | }) |
| | | const sum = ref(0) |
| | | const yny = ref(0) |
| | | const chain = ref(0) |
| | | |
| | | const pieLegend = reactive({ |
| | | show: false, |
| | | }) |
| | | const barSeries = ref([ |
| | | { |
| | | type: 'bar', |
| | | data: [], |
| | | label: { |
| | | show: true, |
| | | } |
| | | }, |
| | | ]) |
| | | |
| | | const barSeries1 = ref([ |
| | | { |
| | | name: '原材料不合格数', |
| | | type: 'bar', |
| | | barGap: 0, |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: '过程不合格数', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [] |
| | | }, |
| | | { |
| | | name: '出厂不合格数', |
| | | type: 'bar', |
| | | emphasis: { |
| | | focus: 'series' |
| | | }, |
| | | data: [] |
| | | }, |
| | | ]) |
| | | const chartStyle = { |
| | | width: '100%', |
| | | height: '100%' |
| | | } |
| | | const chartStylePie = { |
| | | width: '140%', |
| | | height: '140%' |
| | | } |
| | | const grid = { |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | containLabel: true |
| | | } |
| | | const barLegend = { |
| | | show: true, |
| | | data: ['原材料不合格数', '过程不合格数', '出厂不合格数'] |
| | | } |
| | | const barLegend1 = { |
| | | show: true, |
| | | data: ['预付账款', '应付账款', '预收账款', '应收账款'] |
| | | } |
| | | const lineLegend = { |
| | | show: true, |
| | | data: ['开票', '回款'] |
| | | } |
| | | const tooltip = { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | } |
| | | } |
| | | const xAxis = [{ |
| | | type: 'value', |
| | | }] |
| | | const xAxis1 = ref([{ |
| | | type: 'category', |
| | | axisTick: { show: false }, |
| | | data: [] |
| | | }]) |
| | | const yAxis = [{ |
| | | type: 'category', |
| | | data: ['应付账款', '应收账款',] |
| | | }] |
| | | const yAxis1 = [{ |
| | | type: 'value' |
| | | }] |
| | | const pieTooltip = reactive({ |
| | | trigger: 'item', |
| | | formatter: function (params) { |
| | | const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额'; |
| | | return `${description} ${formatNumber(params.value)}元 ${params.percent}%`; |
| | | }, |
| | | position: 'right' |
| | | }) |
| | | const materialPieSeries = ref([ |
| | | { |
| | | type: 'pie', |
| | | radius: ['66%', '90%'], |
| | | avoidLabelOverlap: false, |
| | | itemStyle: { |
| | | borderColor: '#fff', |
| | | borderWidth: 2 |
| | | }, |
| | | label: { |
| | | show: false |
| | | }, |
| | | 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 todoList = ref([]) |
| | | const radio1 = ref(1) |
| | | const qualityRange = ref(1) |
| | | |
| | | const barChart = ref(null) |
| | | const lineChart = ref(null) |
| | | const barColors2 = ['#5181DB', '#D369E0', '#F2CA6D', '#60CCA8'] |
| | | |
| | | const getRandomColor = () => { |
| | | return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0'); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getBusinessData() |
| | | analysisCustomer() |
| | | todoInfoS() |
| | | statisticsReceivable() |
| | | qualityStatisticsInfo() |
| | | getAmountHalfYearNum() |
| | | getProcessList() |
| | | }) |
| | | |
| | | const getBusinessData = () => { |
| | | getBusiness().then((res) => { |
| | | businessInfo.value = { ...res.data } |
| | | }) |
| | | } |
| | | |
| | | 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 todoInfoS = () => { |
| | | homeTodos().then((res) => { |
| | | todoList.value = res.data |
| | | }) |
| | | } |
| | | |
| | | const getProcessList = () => { |
| | | list().then(res => { |
| | | processOptions.value = res.data.records |
| | | }) |
| | | } |
| | | |
| | | const openProcessDialog = () => { |
| | | tempProcessIds.value = [...selectedProcessIds.value] |
| | | processDialogVisible.value = true |
| | | } |
| | | |
| | | const handleProcessDialogConfirm = () => { |
| | | selectedProcessIds.value = [...tempProcessIds.value] |
| | | processDialogVisible.value = false |
| | | refreshProcessStats() |
| | | } |
| | | |
| | | const resetProcessFilter = () => { |
| | | selectedProcessIds.value = [] |
| | | tempProcessIds.value = [] |
| | | refreshProcessStats() |
| | | } |
| | | |
| | | const handleChartClick = (params) => { |
| | | if (params && params.dataIndex !== undefined) { |
| | | activeProcessIndex.value = params.dataIndex |
| | | } |
| | | } |
| | | |
| | | const statisticsReceivable = () => { |
| | | statisticsReceivablePayable({ type: radio1.value }).then((res) => { |
| | | barSeries.value[0].data = [ |
| | | { value: res.data.payableMoney, itemStyle: { color: barColors2[0] } }, |
| | | { value: res.data.receivableMoney, itemStyle: { color: barColors2[1] } } |
| | | ] |
| | | }) |
| | | } |
| | | |
| | | const qualityStatisticsInfo = () => { |
| | | qualityInspectionStatistics({ type: qualityRange.value }).then((res) => { |
| | | xAxis1.value[0].data = [] |
| | | barSeries1.value[0].data = [] |
| | | barSeries1.value[1].data = [] |
| | | barSeries1.value[2].data = [] |
| | | 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 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 |
| | | xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~')); |
| | | lineSeries.value = [ |
| | | { |
| | | name: '开票', |
| | | type: 'line', |
| | | data: invoiceAmount, |
| | | 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: receiptAmount, |
| | | 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 processRange = ref(1) |
| | | const processChartData = ref([]) |
| | |
| | | } |
| | | }) |
| | | |
| | | const refreshProcessStats = async () => { |
| | | const params = { type: processRange.value } |
| | | if (selectedProcessIds.value.length > 0) { |
| | | params.processIds = selectedProcessIds.value.join(',') |
| | | } |
| | | const res = await processDataProductionStatistics(params) |
| | | const list = res.data || [] |
| | | processChartData.value = list |
| | | processYAxis.value[0].data = list.map(i => i.processName) |
| | | processSeries.value[0].data = list.map(i => i.inputNum) |
| | | processSeries.value[1].data = list.map(i => i.scrapNum) |
| | | processSeries.value[2].data = list.map(i => i.outputNum) |
| | | activeProcessIndex.value = 0 |
| | | } |
| | | |
| | | const formatAmount = (num) => { |
| | | if (!num && num !== 0) return '-' |
| | | return Number(num).toLocaleString() |
| | | } |
| | | |
| | | const formatNumber = (num) => { |
| | | if (!num) return '0' |
| | | return num.toLocaleString() |
| | | } |
| | | |
| | | const getProcessList = () => { |
| | | list().then(res => { |
| | | processOptions.value = res.data.records |
| | | }) |
| | | } |
| | | |
| | | const openProcessDialog = () => { |
| | | tempProcessIds.value = [...selectedProcessIds.value] |
| | | processDialogVisible.value = true |
| | | } |
| | | |
| | | const handleProcessDialogConfirm = () => { |
| | | selectedProcessIds.value = [...tempProcessIds.value] |
| | | processDialogVisible.value = false |
| | | refreshProcessStats() |
| | | } |
| | | |
| | | const resetProcessFilter = () => { |
| | | selectedProcessIds.value = [] |
| | | tempProcessIds.value = [] |
| | | refreshProcessStats() |
| | | } |
| | | |
| | | const handleChartClick = (params) => { |
| | | if (params && params.dataIndex !== undefined) { |
| | | activeProcessIndex.value = params.dataIndex |
| | | } |
| | | } |
| | | |
| | | const refreshProcessStats = async () => { |
| | | // 这里调用工序数据统计API |
| | | console.log('刷新工序数据', selectedProcessIds.value, processRange.value) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getProcessList() |
| | | }) |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .dashboard { |
| | | padding: 20px; |
| | | background: #f5f7fa; |
| | | min-height: calc(100vh - 84px); |
| | | } |
| | | |
| | | .dashboard-top { |
| | | display: grid; |
| | | grid-template-columns: 2fr 1fr; |
| | | grid-template-columns: 380px 1fr; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .top-left { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .company-info { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .welcome-banner { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #ebeef5; |
| | | } |
| | | |
| | | .welcome-title { |
| | | font-size: 18px; |
| | | color: #303133; |
| | | |
| | | .welcome-user { |
| | | font-weight: 600; |
| | | color: var(--el-color-primary); |
| | | .company-info { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); |
| | | } |
| | | } |
| | | |
| | | .welcome-time { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | .welcome-banner { |
| | | background: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%); |
| | | padding: 20px; |
| | | color: #fff; |
| | | |
| | | .welcome-title { |
| | | font-size: 16px; |
| | | margin-bottom: 8px; |
| | | |
| | | .welcome-user { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | } |
| | | } |
| | | |
| | | .welcome-time { |
| | | font-size: 13px; |
| | | opacity: 0.9; |
| | | } |
| | | } |
| | | |
| | | .user-card { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .avatar { |
| | | width: 64px; |
| | | height: 64px; |
| | | border-radius: 50%; |
| | | object-fit: cover; |
| | | border: 3px solid var(--el-color-primary-light-8); |
| | | } |
| | | |
| | | .user-card-main { |
| | | .user-name { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .user-role { |
| | | font-size: 13px; |
| | | color: var(--el-color-primary); |
| | | background: var(--el-color-primary-light-9); |
| | | padding: 2px 10px; |
| | | border-radius: 4px; |
| | | display: inline-block; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .user-meta { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | |
| | | .sep { |
| | | margin: 0 8px; |
| | | color: #dcdfe6; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .data-cards { |
| | | display: grid; |
| | | grid-template-columns: repeat(3, 1fr); |
| | | gap: 16px; |
| | | } |
| | | |
| | | .data-card { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 16px; |
| | | |
| | | .data-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .data-num { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .data-desc { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | |
| | | .data-value { |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | .todo-panel { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 16px; |
| | | position: relative; |
| | | padding-left: 12px; |
| | | |
| | | &::before { |
| | | content: ''; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 50%; |
| | | transform: translateY(-50%); |
| | | width: 4px; |
| | | height: 16px; |
| | | background: var(--el-color-primary); |
| | | border-radius: 2px; |
| | | .avatar { |
| | | width: 64px; |
| | | height: 64px; |
| | | border-radius: 50%; |
| | | object-fit: cover; |
| | | border: 3px solid var(--el-color-primary-light-8); |
| | | } |
| | | } |
| | | |
| | | .todo-list { |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | .user-card-main { |
| | | flex: 1; |
| | | |
| | | li { |
| | | padding: 12px 0; |
| | | border-bottom: 1px solid #ebeef5; |
| | | .user-name { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | .user-role { |
| | | font-size: 13px; |
| | | color: var(--el-color-primary); |
| | | background: var(--el-color-primary-light-9); |
| | | display: inline-block; |
| | | padding: 2px 10px; |
| | | border-radius: 12px; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .user-meta { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | |
| | | .sep { |
| | | margin: 0 8px; |
| | | color: #dcdfe6; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .dashboard-row { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | grid-template-columns: 1fr; |
| | | gap: 20px; |
| | | margin-bottom: 20px; |
| | | } |
| | |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 20px; |
| | | box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); |
| | | } |
| | | |
| | | .contract-panel { |
| | | grid-column: 1 / -1; |
| | | } |
| | | |
| | | .contract-summary { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .contract-info { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .contract-card { |
| | | .contract-name { |
| | | font-size: 14px; |
| | | color: #909399; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .contract-meta { |
| | | .main-amount { |
| | | font-size: 28px; |
| | | font-weight: 700; |
| | | color: #303133; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .up { |
| | | color: #67c23a; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .contract-chart-wrapper { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 40px; |
| | | justify-content: center; |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .chart-container { |
| | | width: 320px; |
| | | height: 280px; |
| | | } |
| | | |
| | | .contract-list { |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | min-width: 300px; |
| | | |
| | | li { |
| | | margin-bottom: 12px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .contract-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | padding: 10px 16px; |
| | | background: #f5f7fa; |
| | | border-radius: 8px; |
| | | |
| | | .contract-dot { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-radius: 50%; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .contract-name { |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .contract-rate { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | min-width: 50px; |
| | | text-align: right; |
| | | } |
| | | |
| | | .contract-value { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | min-width: 100px; |
| | | text-align: right; |
| | | } |
| | | } |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .process-panel { |
| | | grid-column: 1 / -1; |
| | | } |
| | | &__header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .process-panel__header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20px; |
| | | } |
| | | &__body { |
| | | display: flex; |
| | | gap: 20px; |
| | | height: 400px; |
| | | } |
| | | |
| | | .process-panel__body { |
| | | display: grid; |
| | | grid-template-columns: 1fr 280px; |
| | | gap: 20px; |
| | | height: 300px; |
| | | } |
| | | &__chart { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .process-panel__chart { |
| | | height: 100%; |
| | | } |
| | | |
| | | .process-panel__aside { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | &__aside { |
| | | width: 200px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | } |
| | | } |
| | | |
| | | .process-legend { |
| | | display: flex; |
| | | gap: 16px; |
| | | margin-bottom: 8px; |
| | | } |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | |
| | | .process-legend__item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6px; |
| | | font-size: 13px; |
| | | color: #606266; |
| | | &__item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | font-size: 13px; |
| | | color: #606266; |
| | | |
| | | .dot { |
| | | width: 8px; |
| | | height: 8px; |
| | | border-radius: 50%; |
| | | .dot { |
| | | width: 10px; |
| | | height: 10px; |
| | | border-radius: 50%; |
| | | |
| | | &.dot-blue { |
| | | background: #2D99FF; |
| | | } |
| | | &.dot-blue { |
| | | background: #2D99FF; |
| | | } |
| | | |
| | | &.dot-yellow { |
| | | background: #F2CA6D; |
| | | } |
| | | &.dot-yellow { |
| | | background: #F2CA6D; |
| | | } |
| | | |
| | | &.dot-teal { |
| | | background: #5EE9C0; |
| | | &.dot-teal { |
| | | background: #5EE9C0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | .process-card { |
| | | background: #f5f7fa; |
| | | border-radius: 8px; |
| | | padding: 12px 16px; |
| | | padding: 16px; |
| | | |
| | | &--name { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | text-align: center; |
| | | background: var(--el-color-primary-light-9); |
| | | color: var(--el-color-primary); |
| | | } |
| | | |
| | | &__label { |
| | |
| | | } |
| | | |
| | | &__value { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | } |
| | | |
| | | .quality-cards { |
| | | display: flex; |
| | | gap: 12px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .quality-card { |
| | | flex: 1; |
| | | background: #f5f7fa; |
| | | border-radius: 8px; |
| | | padding: 12px; |
| | | font-size: 13px; |
| | | color: #606266; |
| | | |
| | | span { |
| | | display: block; |
| | | font-size: 20px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | margin-top: 4px; |
| | | } |
| | | |
| | | &.one { |
| | | background: #e6f7ff; |
| | | color: #1890ff; |
| | | |
| | | span { |
| | | color: #1890ff; |
| | | } |
| | | } |
| | | |
| | | &.two { |
| | | background: #f6ffed; |
| | | color: #52c41a; |
| | | |
| | | span { |
| | | color: #52c41a; |
| | | } |
| | | } |
| | | |
| | | &.three { |
| | | background: #fff7e6; |
| | | color: #fa8c16; |
| | | |
| | | span { |
| | | color: #fa8c16; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | grid-template-columns: repeat(2, 1fr); |
| | | gap: 12px; |
| | | } |
| | | |
| | | @media (max-width: 1200px) { |
| | | .dashboard-top { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .process-panel__body { |
| | | flex-direction: column; |
| | | height: auto; |
| | | } |
| | | |
| | | .process-panel__aside { |
| | | width: 100%; |
| | | flex-direction: row; |
| | | flex-wrap: wrap; |
| | | |
| | | .process-card { |
| | | flex: 1; |
| | | min-width: 150px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .process-panel__header { |
| | | flex-direction: column; |
| | | gap: 12px; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .process-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | } |
| | | </style> |