Compare commits
5 Commits
e770dc4fed
...
05fe91618c
| Author | SHA1 | Date | |
|---|---|---|---|
| 05fe91618c | |||
| b85ceb2542 | |||
| 9b0a768216 | |||
| ee4fdbd45b | |||
| 5465a43bcc |
4
.env.dev
4
.env.dev
@@ -10,9 +10,9 @@ VUE_APP_TITLE = 洛玻集团驾驶舱
|
||||
# VUE_APP_BASE_API = 'http://172.16.33.83:7070'
|
||||
|
||||
# 杨姗姗
|
||||
# VUE_APP_BASE_API = 'http://172.16.20.218:7070'
|
||||
VUE_APP_BASE_API = 'http://172.16.20.218:7070'
|
||||
# 小田
|
||||
VUE_APP_BASE_API = 'http://172.16.19.232:7070'
|
||||
# VUE_APP_BASE_API = 'http://172.16.19.232:7070'
|
||||
# 测试
|
||||
# VUE_APP_BASE_API = 'http://192.168.0.35:8080'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>切片</title>
|
||||
<title>全屏</title>
|
||||
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="01-运营驾驶舱" transform="translate(-1864.000000, -12.000000)" fill="#0B58FF">
|
||||
<g id="icon/可视化/全屏" transform="translate(1864.000000, 12.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>展开备份</title>
|
||||
<title>展开菜单</title>
|
||||
<g id="12-月修改-2-版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="01-运营驾驶舱展开" transform="translate(-1818.000000, -12.000000)" fill="#0B58FF">
|
||||
<g id="展开备份" transform="translate(1818.000000, 12.000000)">
|
||||
<g id="展开菜单" transform="translate(1818.000000, 12.000000)">
|
||||
<rect id="矩形" stroke="#0B58FF" opacity="0" x="0.5" y="0.5" width="31" height="31"></rect>
|
||||
<path d="M26.9054416,1.26315789 L27.1627614,1.2715785 C27.589262,1.29963047 28.0033877,1.39768715 28.3969525,1.56449606 C28.853625,1.75530116 29.2618455,2.03033772 29.6157539,2.38424612 C29.967102,2.73559426 30.2434779,3.1465945 30.4352441,3.6024321 C30.6356746,4.07532525 30.7368421,4.57721075 30.7368421,5.09455843 L30.7368421,26.9054416 L30.7284215,27.1627614 C30.7003695,27.589262 30.6023129,28.0033877 30.4355039,28.3969525 C30.2446988,28.853625 29.9696623,29.2618455 29.6157539,29.6157539 C29.2644057,29.967102 28.8534055,30.2434779 28.3975679,30.4352441 C27.9246748,30.6356746 27.4227892,30.7368421 26.9054416,30.7368421 L5.09455843,30.7368421 L4.83723859,30.7284215 C4.41073802,30.7003695 3.99661229,30.6023129 3.60304751,30.4355039 C3.14637497,30.2446988 2.73815452,29.9696623 2.38424612,29.6157539 C2.03289798,29.2644057 1.75652209,28.8534055 1.56475593,28.3975679 C1.36432537,27.9246748 1.26315789,27.4227892 1.26315789,26.9054416 L1.26315789,5.09455843 L1.2715785,4.83723859 C1.29963047,4.41073802 1.39768715,3.99661229 1.56449606,3.60304751 C1.75530116,3.14637497 2.03033772,2.73815452 2.38424612,2.38424612 C2.73559426,2.03289798 3.1465945,1.75652209 3.6024321,1.56475593 C4.07532525,1.36432537 4.57721075,1.26315789 5.09455843,1.26315789 L26.9054416,1.26315789 Z M26.9054416,3.17216771 L5.09455843,3.17216771 L4.94438644,3.17795455 C3.95326514,3.25463661 3.17216771,4.08386243 3.17216771,5.09455843 L3.17216771,26.9054416 L3.17795455,27.0556136 C3.25463661,28.0467349 4.08386243,28.8278323 5.09455843,28.8278323 L26.9054416,28.8278323 L27.0556136,28.8220454 C28.0467349,28.7453634 28.8278323,27.9161376 28.8278323,26.9054416 L28.8278323,5.09455843 L28.8220454,4.94438644 C28.7453634,3.95326514 27.9161376,3.17216771 26.9054416,3.17216771 Z M29.2015182,9.76714413 L29.2015182,11.8615014 L2.12281432,11.8615014 L2.12281432,9.76714413 L29.2015182,9.76714413 Z" id="形状结合" fill-rule="nonzero" opacity="0.79078311"></path>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,6 +1,6 @@
|
||||
<!--?xml version="1.0" encoding="UTF-8"?-->
|
||||
<svg width="100%" height="100%" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet">
|
||||
<title>icon/可视化/退出全屏</title>
|
||||
<title>退出全屏</title>
|
||||
<g id="00首页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(-1802.000000, -40.000000)" fill="#0B58FF" id="icon/可视化/退出全屏">
|
||||
<g transform="translate(1802.000000, 40.000000)">
|
||||
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-form ref="form" :rules="rules" label-width="110px" :model="form">
|
||||
<el-form ref="form" :rules="rules" label-width="120px" :model="form">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="重点工作" prop="name">
|
||||
@@ -15,11 +15,12 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="所属年份" prop="daySpan">
|
||||
<el-form-item label="所属年份" prop="daySpan1">
|
||||
<el-date-picker
|
||||
v-model="value3"
|
||||
type="year"
|
||||
placeholder="选择年">
|
||||
placeholder="选择年"
|
||||
style="width: 100%;">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
isEdit: false, //是否是编辑
|
||||
rules: {
|
||||
name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
|
||||
code: [{ required: true, message: '请输入产品规格', trigger: 'blur' }],
|
||||
daySpan: [{ required: true, message: '请选择工艺', trigger: 'change' }],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
return {
|
||||
// 图表样式配置项,可以抽离出来方便管理
|
||||
chartConfig: {
|
||||
manageCost: {
|
||||
'管理费用': {
|
||||
name: '管理费用',
|
||||
lineColor: 'rgba(11, 88, 255, .5)',
|
||||
itemColor: 'rgba(11, 88, 255, .5)',
|
||||
@@ -49,7 +49,7 @@ export default {
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]
|
||||
},
|
||||
saleCost: {
|
||||
'销售费用': {
|
||||
name: '销售费用',
|
||||
lineColor: 'rgba(54, 181, 138, .5)',
|
||||
itemColor: 'rgba(54, 181, 138, .5)',
|
||||
@@ -59,7 +59,7 @@ export default {
|
||||
{ offset: 1, color: 'rgba(18, 255, 245, 0)' },
|
||||
]
|
||||
},
|
||||
financeCost: {
|
||||
'财务费用': {
|
||||
name: '财务费用',
|
||||
lineColor: 'rgba(255, 132, 0, .5)',
|
||||
itemColor: 'rgba(255, 132, 0, .5)',
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
// 从 cost.line 中获取任意一个有数据的键的 keys 作为 X 轴
|
||||
const lineData = this.line || {};
|
||||
const firstKey = Object.keys(lineData)[0];
|
||||
return firstKey ? Object.keys(lineData[firstKey]) : [];
|
||||
return firstKey ? Object.keys(lineData[firstKey].real) : [];
|
||||
},
|
||||
// 动态生成 series 数据
|
||||
chartSeries() {
|
||||
@@ -94,7 +94,7 @@ export default {
|
||||
return Object.keys(this.chartConfig).map(key => {
|
||||
const config = this.chartConfig[key];
|
||||
// 确保数据顺序和 X 轴一致
|
||||
const dataValues = xAxisKeys.map(date => lineData[key] ? lineData[key][date] : 0);
|
||||
const dataValues = xAxisKeys.map(date => lineData[key] ? lineData[key].real[date] : 0);
|
||||
|
||||
return {
|
||||
name: config.name,
|
||||
|
||||
@@ -388,7 +388,7 @@ export default {
|
||||
console.log('当前编辑状态:', isEdit, '当前时间维度:', this.timeType);
|
||||
// 基础表格列配置(只读模式使用)
|
||||
const baseTableProps = [
|
||||
{ prop: 'type', label: '指标类型', align: 'center' },
|
||||
// { prop: 'type', label: '指标类型', align: 'center' },
|
||||
{ prop: 'name', label: '指标名称', align: 'center' },
|
||||
{ prop: 'unit', label: '单位', align: 'center', filter: publicFormatter('lb_dw') },
|
||||
{ prop: 'target', label: '预算值', align: 'center' },
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container name="销售重点指标" icon="cockpitItemIcon" size="topBasic" topSize="basic">
|
||||
<Container name="销售重点指标" icon="cockpitItemIcon" size="topBasic" topSize="basic" :isShowTab='true' @tabChange='tabChange'>
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item :sale="sale" :dateData="dateData" />
|
||||
<top-item :sale="saleData" :dateData="dateData" />
|
||||
</div>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
@@ -39,16 +39,39 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
chart: null,
|
||||
saleData:{},
|
||||
currentTap:'month'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sale: {
|
||||
handler(newVal) {
|
||||
if(this.currentTap === 'month') {
|
||||
this.saleData = this.sale.mon
|
||||
}else{
|
||||
this.saleData = this.sale.total
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
this.saleData = this.sale.month
|
||||
},
|
||||
methods: {
|
||||
tabChange(val) {
|
||||
if(val === 'month') {
|
||||
this.currentTap = 'month'
|
||||
this.saleData = this.sale.mon
|
||||
}else{
|
||||
this.currentTap = 'total'
|
||||
this.saleData = this.sale.total
|
||||
}
|
||||
console.log(this.saleData);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
label: { backgroundColor: '#6a7985' }
|
||||
}
|
||||
},
|
||||
grid: { top: 35, bottom: 20, right: 25, left: 70 },
|
||||
grid: { top: 35, bottom: 3, right: 15, left: 10, containLabel: true},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="content-wrapper">
|
||||
<div class="left">
|
||||
<div class="number">{{ item.targetValue }}</div>
|
||||
<div class="title">上月</div>
|
||||
<div class="title">{{currentTap==='month'?'上月':'上年'}}</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<div class="right">
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="number" :style="{ color: getColor(item.currentValue, item.targetValue) }">
|
||||
{{ item.currentValue }}
|
||||
</div>
|
||||
<div class="title">本月</div>
|
||||
<div class="title">{{currentTap==='month'?'本月':'本年'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
@@ -46,6 +46,10 @@ export default {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
currentTap: {
|
||||
type: String,
|
||||
default: 'month'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<!-- 传入点击切换的状态到Container组件 -->
|
||||
<Container name="财务重点指标" nameTwo="费用重点指标" icon="cockpitItemIcon" size="topBasic" @switchTab="handleTabSwitch">
|
||||
<Container name="财务重点指标" nameTwo="费用重点指标" icon="cockpitItemIcon" size="topBasic" @switchTab="handleTabSwitch" @tabChange='tabChange'>
|
||||
<div class="bottom-left-content" style="display: flex;gap: 9px;padding: 14px 16px;flex-direction: column;">
|
||||
<!-- 根据activeTab状态,切换显示采购/存货内容 -->
|
||||
<template v-if="activeTab === 'purchase'">
|
||||
<!-- 采购重点指标对应的内容 -->
|
||||
<coreBottomLeftItem :dateData="dateData" :finance="finance"></coreBottomLeftItem>
|
||||
<coreBottomLeftItem :dateData="dateData" :finance="financeData"></coreBottomLeftItem>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<coreBottomBar :dateData="dateData" :line="finance.line"></coreBottomBar>
|
||||
@@ -14,7 +14,7 @@
|
||||
</template>
|
||||
<template v-else-if="activeTab === 'inventory'">
|
||||
<!-- 存货重点指标对应的内容 -->
|
||||
<costItem :dateData="dateData" :cost="cost"></costItem>
|
||||
<costItem :dateData="dateData" :cost="costData" :currentTap='currentTap'></costItem>
|
||||
<div class="bottom"
|
||||
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<CostsBottomBar :line="cost.line" :dateData="dateData">
|
||||
@@ -52,13 +52,16 @@ export default {
|
||||
dateData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'purchase', // 激活的标签:purchase=采购,inventory=存货
|
||||
showChart: true, // 控制图表是否显示
|
||||
chart: null // 图表实例
|
||||
chart: null, // 图表实例
|
||||
financeData:{},
|
||||
costData:{},
|
||||
currentTap: 'month'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -71,7 +74,28 @@ export default {
|
||||
this.updateChart()
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
finance: {
|
||||
handler() {
|
||||
if(this.currentTap === 'month') {
|
||||
this.financeData = this.finance.mon
|
||||
}else{
|
||||
this.financeData = this.finance.total
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
cost: {
|
||||
handler() {
|
||||
if(this.currentTap === 'month') {
|
||||
this.costData = this.cost.mon
|
||||
}else{
|
||||
this.costData = this.cost.total
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表
|
||||
@@ -85,6 +109,17 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tabChange(val) {
|
||||
if(val === 'month') {
|
||||
this.currentTap = 'month'
|
||||
this.financeData = this.finance.mon
|
||||
this.costData = this.cost.mon
|
||||
}else{
|
||||
this.currentTap = 'total'
|
||||
this.financeData = this.finance.total
|
||||
this.costData = this.cost.total
|
||||
}
|
||||
},
|
||||
// 处理标题点击切换标签
|
||||
handleTabSwitch(tabType) {
|
||||
this.activeTab = tabType // tabType由Container组件传递:'purchase'或'inventory'
|
||||
|
||||
@@ -16,7 +16,17 @@
|
||||
<span class="title-text">{{ nameTwo }}</span>
|
||||
|
||||
</div>
|
||||
<span class="change-text" :class="{ 'change-text-right': isLeftTransparent }">点击切换</span>
|
||||
<!-- <span class="change-text" :class="{ 'change-text-right': isLeftTransparent }">点击切换</span> -->
|
||||
<div class="tab-group">
|
||||
<!-- 月度Tab:点击切换状态,动态绑定样式 -->
|
||||
<div class="tab-item" :class="{ active: activeTab === 'month' }" @click="handleTabClick('month')">
|
||||
月度
|
||||
</div>
|
||||
<!-- 累计Tab:点击切换状态,动态绑定样式 -->
|
||||
<div class="tab-item" :class="{ active: activeTab === 'total' }" @click="handleTabClick('total')">
|
||||
累计
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="container-body">
|
||||
@@ -40,10 +50,18 @@ export default {
|
||||
return {
|
||||
// 初始状态:左侧不透明(1),右侧透明(0.3)
|
||||
isLeftTransparent: true, // 左侧透明度状态(true=1,false=0.3)
|
||||
isRightTransparent: false // 右侧透明度状态(true=1,false=0.3)
|
||||
isRightTransparent: false, // 右侧透明度状态(true=1,false=0.3)
|
||||
activeTab: 'month', // 默认激活的Tab为月度
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleTabClick(tabType) {
|
||||
this.activeTab = tabType;
|
||||
// 向父组件派发Tab切换事件,传递当前选中的Tab类型
|
||||
this.$emit('tabChange', tabType);
|
||||
// 可选:同时传递更详细的信息(如标签名)
|
||||
// this.$emit('tabChange', { type: tabType, name: tabType === 'month' ? '月度' : '累计' });
|
||||
},
|
||||
// 点击左侧标题:左侧保持不透明,右侧变透明,并派发采购标签事件
|
||||
handleLeftClick() {
|
||||
this.isLeftTransparent = true; // 左侧不透明
|
||||
@@ -151,7 +169,7 @@ export default {
|
||||
background: linear-gradient(90deg, #FFFFFF 0%, rgba(253, 255, 255, 0) 100%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 240px;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
@@ -258,4 +276,41 @@ export default {
|
||||
// text-align: left;
|
||||
// font-style: normal;
|
||||
}
|
||||
.tab-group {
|
||||
display: inline-flex;
|
||||
position: absolute;
|
||||
right: 5%;
|
||||
top:20px;
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
gap: 8px; // Tab之间的间距
|
||||
}
|
||||
|
||||
// Tab基础样式(统一)
|
||||
.tab-item {
|
||||
padding: 0 24px;
|
||||
width: 79px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
transition: all 0.2s ease; // 样式切换动画
|
||||
}
|
||||
|
||||
// 未激活的Tab样式(原first-child样式)
|
||||
.tab-item:not(.active) {
|
||||
background: #ECF4FE;
|
||||
color: #0B58FF;
|
||||
}
|
||||
|
||||
// 激活的Tab样式(原last-child样式)
|
||||
.tab-item.active {
|
||||
background: #3071FF;
|
||||
color: #F9FCFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
134
src/views/home/components/heatBarChart.vue
Normal file
134
src/views/home/components/heatBarChart.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div ref="cockpitEffChip" id="coreLineChart" style="width: 100%; height: 214px;"></div>
|
||||
</template>
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
myChart: null // 存储图表实例,避免重复创建
|
||||
};
|
||||
},
|
||||
props: {
|
||||
lineData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
xData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.updateChart();
|
||||
});
|
||||
},
|
||||
|
||||
// 新增:监听 chartData 变化
|
||||
watch: {
|
||||
// 深度监听数据变化,仅更新图表配置(不销毁实例)
|
||||
lineData: {
|
||||
handler() {
|
||||
console.log(this.lineData,'lineData');
|
||||
this.updateChart();
|
||||
},
|
||||
deep: true,
|
||||
immediate: true // 初始化时立即执行
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateChart() {
|
||||
const chartDom = this.$refs.cockpitEffChip;
|
||||
if (!chartDom) {
|
||||
console.error('图表容器未找到!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
}
|
||||
|
||||
this.myChart = echarts.init(chartDom);
|
||||
|
||||
console.log('linedaata',this.lineData)
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
top: 30,
|
||||
bottom: 30,
|
||||
right: 20,
|
||||
left: 60,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: { show: false },
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
|
||||
},
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
interval: 0,
|
||||
padding: [5, 0, 0, 0]
|
||||
},
|
||||
data:this.xData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
// 左侧Y轴:营业收入、成本(单位万元)
|
||||
{
|
||||
type: 'value',
|
||||
name: 'kcal/kg',
|
||||
nameTextStyle: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
align: 'right'
|
||||
},
|
||||
|
||||
splitNumber: 4,
|
||||
axisTick: { show: false },
|
||||
axisLabel: {
|
||||
color: 'rgba(0, 0, 0, 0.45)',
|
||||
fontSize: 12,
|
||||
formatter: '{value}'
|
||||
},
|
||||
splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
axisLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.15)' } },
|
||||
splitNumber: 4
|
||||
},
|
||||
],
|
||||
series: []
|
||||
};
|
||||
|
||||
option && this.myChart.setOption(option);
|
||||
|
||||
// 窗口缩放适配和销毁逻辑保持不变
|
||||
window.addEventListener('resize', () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
});
|
||||
|
||||
this.$once('hook:destroyed', () => {
|
||||
window.removeEventListener('resize', () => {
|
||||
this.myChart && this.myChart.resize();
|
||||
});
|
||||
this.myChart && this.myChart.dispose();
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -169,7 +169,7 @@ export default {
|
||||
|
||||
// 基础表格列配置(只读模式使用)
|
||||
const baseTableProps = [
|
||||
{ prop: 'type', label: '指标类型', align: 'center' },
|
||||
// { prop: 'type', label: '指标类型', align: 'center' },
|
||||
{ prop: 'name', label: '指标名称', align: 'center' },
|
||||
{ prop: 'unitLabel', label: '单位', align: 'center' },
|
||||
{ prop: 'value', label: '实际值', align: 'center' },
|
||||
|
||||
316
src/views/home/components/keyProductContainer.vue
Normal file
316
src/views/home/components/keyProductContainer.vue
Normal file
@@ -0,0 +1,316 @@
|
||||
<template>
|
||||
<div class="cockpitContainer" :class="['cockpitContainer__' + size]">
|
||||
<div class="container-top">
|
||||
<!-- 左侧标题:点击切换到采购标签,并更新透明度 -->
|
||||
<div class="content-top-left title-wrapper" @click="handleLeftClick"
|
||||
:style="{ opacity: isLeftTransparent ? 1 : 0.3 }">
|
||||
<svg-icon class="title-icon" style="font-size: 32px; margin-left: 16px" :icon-class="icon" />
|
||||
<span class="title-text">{{ name }}</span>
|
||||
<!-- <span v-if="!isLeftTransparent" class="change-text">点击切换</span> -->
|
||||
|
||||
</div>
|
||||
<!-- 右侧标题:点击切换到存货标签,并更新透明度 -->
|
||||
<div class="content-top-right title-wrapper" v-if="nameTwo" @click="handleRightClick"
|
||||
:style="{ opacity: isRightTransparent ? 1 : 0.3 }">
|
||||
<svg-icon class="title-icon" style="font-size: 32px; margin-left: 16px" :icon-class="iconTwo || icon" />
|
||||
<span class="title-text">{{ nameTwo }}</span>
|
||||
|
||||
</div>
|
||||
<!-- <span class="change-text" :class="{ 'change-text-right': isLeftTransparent }">点击切换</span> -->
|
||||
<div class="tab-group">
|
||||
<!-- 月度Tab:点击切换状态,动态绑定样式 -->
|
||||
<div class="tab-item" :class="{ active: activeTab === 'month' }" @click="handleTabClick('month')">
|
||||
月度
|
||||
</div>
|
||||
<!-- 累计Tab:点击切换状态,动态绑定样式 -->
|
||||
<div class="tab-item" :class="{ active: activeTab === 'total' }" @click="handleTabClick('total')">
|
||||
累计
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="container-body">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Container',
|
||||
components: {},
|
||||
props: {
|
||||
name: { type: String, required: true },
|
||||
nameTwo: { type: String, required: false },
|
||||
size: { type: String, default: 'default' },
|
||||
icon: { type: String, default: '' },
|
||||
iconTwo: { type: String, default: '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 初始状态:左侧不透明(1),右侧透明(0.3)
|
||||
isLeftTransparent: true, // 左侧透明度状态(true=1,false=0.3)
|
||||
isRightTransparent: false, // 右侧透明度状态(true=1,false=0.3)
|
||||
activeTab: 'month', // 默认激活的Tab为月度
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleTabClick(tabType) {
|
||||
this.activeTab = tabType;
|
||||
// 向父组件派发Tab切换事件,传递当前选中的Tab类型
|
||||
this.$emit('tabChange', tabType);
|
||||
// 可选:同时传递更详细的信息(如标签名)
|
||||
// this.$emit('tabChange', { type: tabType, name: tabType === 'month' ? '月度' : '累计' });
|
||||
},
|
||||
// 点击左侧标题:左侧保持不透明,右侧变透明,并派发采购标签事件
|
||||
handleLeftClick() {
|
||||
this.isLeftTransparent = true; // 左侧不透明
|
||||
this.isRightTransparent = false; // 右侧透明
|
||||
this.$emit('switchTab', 'product'); // 通知父组件切换到采购内容
|
||||
},
|
||||
// 点击右侧标题:右侧保持不透明,左侧变透明,并派发存货标签事件
|
||||
handleRightClick() {
|
||||
this.isLeftTransparent = false; // 左侧透明
|
||||
this.isRightTransparent = true; // 右侧不透明
|
||||
this.$emit('switchTab', 'heat'); // 通知父组件切换到存货内容
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 样式保持不变,确保透明度过渡生效
|
||||
.cockpitContainer {
|
||||
display: inline-block;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
|
||||
.container-top {
|
||||
position: relative;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-top-left {
|
||||
width: 566px;
|
||||
height: 60px;
|
||||
background: linear-gradient(90deg, #FFFFFF 0%, rgba(253, 255, 255, 0) 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
transition: opacity 0.3s ease; // 透明度过渡动画
|
||||
}
|
||||
.title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// margin-left: 10px;
|
||||
/* 垂直居中关键属性 */
|
||||
height: 100%;
|
||||
/* 继承父容器高度,确保垂直居中范围 */
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
font-size: 30px;
|
||||
margin-right: 12px;
|
||||
margin-top: 4px;
|
||||
margin-left: 10px;
|
||||
/* 图标和文字之间的间距 */
|
||||
flex-shrink: 0;
|
||||
/* 防止图标被压缩 */
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
color: #000000;
|
||||
letter-spacing: 3px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
// 移除固定行高,避免影响垂直对齐
|
||||
// line-height: 60px;
|
||||
}
|
||||
/* 左侧标题 - 左上角折现边框 */
|
||||
.content-top-left::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(277deg, rgba(255, 255, 255, 0), rgba(92, 140, 255, 1)) 1 1;
|
||||
clip-path: polygon(20px 0, 100% 0, 100% 100%, 0 100%, 0 20px);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
/* 左侧标题 - 左上角折现细节 */
|
||||
.content-top-left::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: #E1f0fd;
|
||||
border-top: 1px solid rgba(92, 140, 255, 1);
|
||||
border-left: 1px solid rgba(92, 140, 255, 1);
|
||||
transform: rotate(135deg) translate(-50%, -50%);
|
||||
transform-origin: top left;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.content-top-right {
|
||||
width: 368px;
|
||||
height: 60px;
|
||||
background: linear-gradient(90deg, #FFFFFF 0%, rgba(253, 255, 255, 0) 100%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 240px;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s ease; // 透明度过渡动画
|
||||
}
|
||||
|
||||
/* 右侧标题 - 左上角折现边框 */
|
||||
.content-top-right::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid;
|
||||
border-image: linear-gradient(277deg, rgba(255, 255, 255, 0), rgba(92, 140, 255, 1)) 1 1;
|
||||
clip-path: polygon(20px 0, 100% 0, 100% 100%, 0 100%, 0 20px);
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
/* 右侧标题 - 左上角折现细节 */
|
||||
.content-top-right::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: #E1f0fd;
|
||||
border-top: 1px solid rgba(92, 140, 255, 1);
|
||||
border-left: 1px solid rgba(92, 140, 255, 1);
|
||||
transform: rotate(135deg) translate(-50%, -50%);
|
||||
transform-origin: top left;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
margin-left: 6px;
|
||||
height: 32px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
color: #000000;
|
||||
// line-height: 60px;
|
||||
letter-spacing: 3px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__topBasic {
|
||||
background: url(../../../assets/img/top-basic.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
&__bottomBasic {
|
||||
background: url(../../../assets/img/bottom-basic.png) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.container-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.test-body {
|
||||
padding: 20px;
|
||||
color: #666;
|
||||
}
|
||||
.change-text{
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 300px;
|
||||
z-index: 999;
|
||||
// width: 48px;
|
||||
// height: 17px;
|
||||
// margin-left: 80px;
|
||||
font-family: PingFangSC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
color: #0B58FF;
|
||||
line-height: 17px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
.change-text-right {
|
||||
// position: absolute;
|
||||
// top: 26px;
|
||||
left: auto; // 清除左侧定位
|
||||
right: 30px;
|
||||
// z-index: 999;
|
||||
// // width: 48px;
|
||||
// // height: 17px;
|
||||
// // margin-left: 80px;
|
||||
// font-family: PingFangSC, PingFang SC;
|
||||
// font-weight: 400;
|
||||
// font-size: 12px;
|
||||
// color: #0B58FF;
|
||||
// line-height: 17px;
|
||||
// text-align: left;
|
||||
// font-style: normal;
|
||||
}
|
||||
.tab-group {
|
||||
display: inline-flex;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top:20px;
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
gap: 8px; // Tab之间的间距
|
||||
}
|
||||
|
||||
// Tab基础样式(统一)
|
||||
.tab-item {
|
||||
padding: 0 24px;
|
||||
width: 79px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
transition: all 0.2s ease; // 样式切换动画
|
||||
}
|
||||
|
||||
// 未激活的Tab样式(原first-child样式)
|
||||
.tab-item:not(.active) {
|
||||
background: #ECF4FE;
|
||||
color: #0B58FF;
|
||||
}
|
||||
|
||||
// 激活的Tab样式(原last-child样式)
|
||||
.tab-item.active {
|
||||
background: #3071FF;
|
||||
color: #F9FCFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@@ -1,33 +1,49 @@
|
||||
<template>
|
||||
<div style="flex: 1">
|
||||
<Container name="生产重点指标" icon="cockpitItemIcon" size="topBasic" topSize="basic">
|
||||
<Container name="生产重点指标" nameTwo="热耗" icon="cockpitItemIcon" size="topBasic" topSize="basic" @switchTab="handleTabSwitch" @tabChange='tabChange'>
|
||||
<!-- 1. 移除 .kpi-content 的固定高度,改为自适应 -->
|
||||
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
|
||||
<!-- 2. .top 保持 flex,无需固定高度,自动跟随子元素拉伸 -->
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item :rawItemList='productData' :dateData="dateData" />
|
||||
</div>
|
||||
<div class="bottom" style="display: flex;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<coreBottomBar :lineData="productData.line" :dateData="dateData" />
|
||||
|
||||
</div>
|
||||
<template v-if="activeTab === 'product'">
|
||||
<div class="top" style="display: flex; width: 100%;">
|
||||
<top-item :rawItemList='productData' :dateData="dateData" />
|
||||
</div>
|
||||
<div class="bottom" style="display: flex;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<!-- <top-item /> -->
|
||||
<coreBottomBar :lineData="product.line" :dateData="dateData" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="activeTab === 'heat'">
|
||||
<div class="bottom" style="background-color: rgba(249, 252, 255, 1);">
|
||||
<div class="bottom" style="margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
|
||||
<div style='text-align: center;margin: 5px;font-size: 18px;font-weight: 400;color: #000;'>1200t/d</div>
|
||||
<heatBar :lineData="heatData['1200t']" :xData="['桐城','洛阳','江苏','秦皇岛']" :dateData="dateData" />
|
||||
</div>
|
||||
<div style='text-align: center;margin: 5px;font-size: 18px;font-weight: 400;color: #000;'>650t/d</div>
|
||||
<heatBar :lineData="heatData['650t']" :xData="['宜兴','自贡','漳州']" :dateData="dateData" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Container from './container.vue'
|
||||
import Container from './keyProductContainer.vue'
|
||||
// import * as echarts from 'echarts'
|
||||
import topItem from './top-product-item.vue'
|
||||
import coreBottomBar from './productBottomBar.vue'
|
||||
import heatBar from './heatBarChart.vue'
|
||||
|
||||
export default {
|
||||
name: 'ProductionStatus',
|
||||
components: { Container, topItem, coreBottomBar },
|
||||
components: { Container, topItem, coreBottomBar, heatBar },
|
||||
// mixins: [resize],
|
||||
props: {
|
||||
productData: {
|
||||
product: {
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
heat: {
|
||||
type: Object,
|
||||
default: () => {} // 默认空数组,避免报错
|
||||
},
|
||||
@@ -35,13 +51,45 @@ export default {
|
||||
type: Object,
|
||||
default: () => { } // 默认空数组,避免报错
|
||||
},
|
||||
heat: {
|
||||
type: Object,
|
||||
default: () => { }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
chart: null,
|
||||
activeTab: 'product',
|
||||
currentTap: 'month',
|
||||
productData:{},
|
||||
heatData:{}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 切换标签时更新图表
|
||||
activeTab(newVal) {
|
||||
// this.$nextTick(() => this.updateChart())
|
||||
},
|
||||
product: {
|
||||
handler() {
|
||||
if(this.currentTap === 'month') {
|
||||
this.productData = this.product.mon
|
||||
}else{
|
||||
this.productData = this.product.total
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
heat: {
|
||||
handler() {
|
||||
if(this.currentTap === 'month') {
|
||||
this.heatData = this.heat.mon
|
||||
}else{
|
||||
this.heatData = this.heat.total
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 初始化图表(若需展示图表,需在模板中添加对应 DOM)
|
||||
@@ -49,6 +97,22 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 处理标题点击切换标签
|
||||
handleTabSwitch(tabType) {
|
||||
this.activeTab = tabType // tabType由Container组件传递:'purchase'或'inventory'
|
||||
this.showChart = true // 切换时默认显示图表(可根据需求调整)
|
||||
},
|
||||
tabChange(val) {
|
||||
if(val === 'month') {
|
||||
this.currentTap = 'month'
|
||||
this.productData = this.product.mon
|
||||
this.heatData = this.heat.mon
|
||||
}else{
|
||||
this.currentTap = 'total'
|
||||
this.productData = this.product.total
|
||||
this.heatData = this.heat.total
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
">
|
||||
<coreSalesKPIs :sale="sale" :dateData="dateData" />
|
||||
<financeCosts :finance="finance" :dateData="dateData" :cost="cost" />
|
||||
<keyProductionIndicators :productData="productData" :dateData="dateData" />
|
||||
<keyProductionIndicators :product="productData" :heat="heat" :dateData="dateData" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="top" style="display: flex;gap: 16px;margin-top: 6px;">
|
||||
@@ -152,6 +152,7 @@ export default {
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
this.productData = res.data.product
|
||||
this.heat = res.data.heat
|
||||
this.purchase = res.data.purchase
|
||||
this.inventory = res.data.inventory
|
||||
this.importantWork = res.data.importantWork
|
||||
@@ -160,11 +161,6 @@ export default {
|
||||
this.sale = res.data.sale
|
||||
this.orderOutput = res.data.orderOutput
|
||||
this.baseOrder = res.data.baseOrder
|
||||
// this.saleData = res.data.SaleData
|
||||
// this.premiumProduct = res.data.premiumProduct
|
||||
// this.salesTrendMap = res.data.salesTrendMap
|
||||
// this.grossMarginTrendMap = res.data.grossMarginTrendMap
|
||||
// this.salesProportion = res.data.salesProportion ? res.data.salesProportion : {}
|
||||
})
|
||||
},
|
||||
handleTimeChange(obj) {
|
||||
|
||||
@@ -167,8 +167,8 @@ export default {
|
||||
mobileCodeTimer: 0,
|
||||
loginForm: {
|
||||
loginType: 'uname',
|
||||
username: 'admin',
|
||||
password: 'admin123',
|
||||
username: '',//admin
|
||||
password: '',//admin123
|
||||
captchaVerification: '',
|
||||
mobile: '',
|
||||
mobileCode: '',
|
||||
|
||||
Reference in New Issue
Block a user