Compare commits

...

5 Commits

Author SHA1 Message Date
05fe91618c 运营驾驶舱修改部分 2026-03-27 11:12:13 +08:00
b85ceb2542 merge B 2026-03-27 10:06:27 +08:00
9b0a768216 指标和预算填报删除指标类型 2026-03-27 08:36:02 +08:00
ee4fdbd45b 修改icon提示 2026-03-20 10:25:15 +08:00
5465a43bcc 去掉默认的账密 2026-03-19 09:13:27 +08:00
19 changed files with 680 additions and 52 deletions

View File

@@ -10,9 +10,9 @@ VUE_APP_TITLE = 洛玻集团驾驶舱
# VUE_APP_BASE_API = 'http://172.16.33.83:7070' # 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' # VUE_APP_BASE_API = 'http://192.168.0.35:8080'

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <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="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="01-运营驾驶舱" transform="translate(-1864.000000, -12.000000)" fill="#0B58FF"> <g id="01-运营驾驶舱" transform="translate(-1864.000000, -12.000000)" fill="#0B58FF">
<g id="icon/可视化/全屏" transform="translate(1864.000000, 12.000000)"> <g id="icon/可视化/全屏" transform="translate(1864.000000, 12.000000)">

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <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="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="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> <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> <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> </g>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,6 +1,6 @@
<!--?xml version="1.0" encoding="UTF-8"?--> <!--?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"> <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 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)" fill="#0B58FF" id="icon/可视化/退出全屏">
<g transform="translate(1802.000000, 40.000000)"> <g transform="translate(1802.000000, 40.000000)">

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,5 +1,5 @@
<template> <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-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="重点工作" prop="name"> <el-form-item label="重点工作" prop="name">
@@ -15,11 +15,12 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="所属年份" prop="daySpan"> <el-form-item label="所属年份" prop="daySpan1">
<el-date-picker <el-date-picker
v-model="value3" v-model="value3"
type="year" type="year"
placeholder="选择年"> placeholder="选择年"
style="width: 100%;">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
</el-col> </el-col>

View File

@@ -37,7 +37,7 @@ export default {
isEdit: false, //是否是编辑 isEdit: false, //是否是编辑
rules: { rules: {
name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }], name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入产品编码', trigger: 'blur' }], code: [{ required: true, message: '请输入产品规格', trigger: 'blur' }],
daySpan: [{ required: true, message: '请选择工艺', trigger: 'change' }], daySpan: [{ required: true, message: '请选择工艺', trigger: 'change' }],
} }
} }

View File

@@ -39,7 +39,7 @@ export default {
return { return {
// 图表样式配置项,可以抽离出来方便管理 // 图表样式配置项,可以抽离出来方便管理
chartConfig: { chartConfig: {
manageCost: { '管理费用': {
name: '管理费用', name: '管理费用',
lineColor: 'rgba(11, 88, 255, .5)', lineColor: 'rgba(11, 88, 255, .5)',
itemColor: '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)' }, { offset: 1, color: 'rgba(18, 255, 245, 0)' },
] ]
}, },
saleCost: { '销售费用': {
name: '销售费用', name: '销售费用',
lineColor: 'rgba(54, 181, 138, .5)', lineColor: 'rgba(54, 181, 138, .5)',
itemColor: '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)' }, { offset: 1, color: 'rgba(18, 255, 245, 0)' },
] ]
}, },
financeCost: { '财务费用': {
name: '财务费用', name: '财务费用',
lineColor: 'rgba(255, 132, 0, .5)', lineColor: 'rgba(255, 132, 0, .5)',
itemColor: 'rgba(255, 132, 0, .5)', itemColor: 'rgba(255, 132, 0, .5)',
@@ -78,7 +78,7 @@ export default {
// 从 cost.line 中获取任意一个有数据的键的 keys 作为 X 轴 // 从 cost.line 中获取任意一个有数据的键的 keys 作为 X 轴
const lineData = this.line || {}; const lineData = this.line || {};
const firstKey = Object.keys(lineData)[0]; const firstKey = Object.keys(lineData)[0];
return firstKey ? Object.keys(lineData[firstKey]) : []; return firstKey ? Object.keys(lineData[firstKey].real) : [];
}, },
// 动态生成 series 数据 // 动态生成 series 数据
chartSeries() { chartSeries() {
@@ -94,7 +94,7 @@ export default {
return Object.keys(this.chartConfig).map(key => { return Object.keys(this.chartConfig).map(key => {
const config = this.chartConfig[key]; const config = this.chartConfig[key];
// 确保数据顺序和 X 轴一致 // 确保数据顺序和 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 { return {
name: config.name, name: config.name,

View File

@@ -388,7 +388,7 @@ export default {
console.log('当前编辑状态:', isEdit, '当前时间维度:', this.timeType); console.log('当前编辑状态:', isEdit, '当前时间维度:', this.timeType);
// 基础表格列配置(只读模式使用) // 基础表格列配置(只读模式使用)
const baseTableProps = [ const baseTableProps = [
{ prop: 'type', label: '指标类型', align: 'center' }, // { prop: 'type', label: '指标类型', align: 'center' },
{ prop: 'name', label: '指标名称', align: 'center' }, { prop: 'name', label: '指标名称', align: 'center' },
{ prop: 'unit', label: '单位', align: 'center', filter: publicFormatter('lb_dw') }, { prop: 'unit', label: '单位', align: 'center', filter: publicFormatter('lb_dw') },
{ prop: 'target', label: '预算值', align: 'center' }, { prop: 'target', label: '预算值', align: 'center' },

View File

@@ -1,11 +1,11 @@
<template> <template>
<div style="flex: 1"> <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 的固定高度改为自适应 --> <!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;"> <div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
<!-- 2. .top 保持 flex无需固定高度自动跟随子元素拉伸 --> <!-- 2. .top 保持 flex无需固定高度自动跟随子元素拉伸 -->
<div class="top" style="display: flex; width: 100%;"> <div class="top" style="display: flex; width: 100%;">
<top-item :sale="sale" :dateData="dateData" /> <top-item :sale="saleData" :dateData="dateData" />
</div> </div>
<div class="bottom" <div class="bottom"
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);"> style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
@@ -39,16 +39,39 @@ export default {
}, },
data() { data() {
return { return {
chart: null chart: null,
saleData:{},
currentTap:'month'
} }
}, },
watch: { watch: {
sale: {
handler(newVal) {
if(this.currentTap === 'month') {
this.saleData = this.sale.mon
}else{
this.saleData = this.sale.total
}
},
deep: true
}
}, },
mounted() { mounted() {
// 初始化图表(若需展示图表,需在模板中添加对应 DOM // 初始化图表(若需展示图表,需在模板中添加对应 DOM
// this.$nextTick(() => this.updateChart()) // this.$nextTick(() => this.updateChart())
this.saleData = this.sale.month
}, },
methods: { 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> </script>

View File

@@ -78,7 +78,7 @@ export default {
label: { backgroundColor: '#6a7985' } label: { backgroundColor: '#6a7985' }
} }
}, },
grid: { top: 35, bottom: 20, right: 25, left: 70 }, grid: { top: 35, bottom: 3, right: 15, left: 10, containLabel: true},
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',

View File

@@ -8,7 +8,7 @@
<div class="content-wrapper"> <div class="content-wrapper">
<div class="left"> <div class="left">
<div class="number">{{ item.targetValue }}</div> <div class="number">{{ item.targetValue }}</div>
<div class="title">上月</div> <div class="title">{{currentTap==='month'?'上月':'上年'}}</div>
</div> </div>
<div class="line"></div> <div class="line"></div>
<div class="right"> <div class="right">
@@ -16,7 +16,7 @@
<div class="number" :style="{ color: getColor(item.currentValue, item.targetValue) }"> <div class="number" :style="{ color: getColor(item.currentValue, item.targetValue) }">
{{ item.currentValue }} {{ item.currentValue }}
</div> </div>
<div class="title">本月</div> <div class="title">{{currentTap==='month'?'本月':'本年'}}</div>
</div> </div>
</div> </div>
<div class="line"></div> <div class="line"></div>
@@ -46,6 +46,10 @@ export default {
type: Object, type: Object,
default: () => ({}) default: () => ({})
}, },
currentTap: {
type: String,
default: 'month'
}
}, },
data() { data() {
return { return {

View File

@@ -1,12 +1,12 @@
<template> <template>
<div style="flex: 1"> <div style="flex: 1">
<!-- 传入点击切换的状态到Container组件 --> <!-- 传入点击切换的状态到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;"> <div class="bottom-left-content" style="display: flex;gap: 9px;padding: 14px 16px;flex-direction: column;">
<!-- 根据activeTab状态切换显示采购/存货内容 --> <!-- 根据activeTab状态切换显示采购/存货内容 -->
<template v-if="activeTab === 'purchase'"> <template v-if="activeTab === 'purchase'">
<!-- 采购重点指标对应的内容 --> <!-- 采购重点指标对应的内容 -->
<coreBottomLeftItem :dateData="dateData" :finance="finance"></coreBottomLeftItem> <coreBottomLeftItem :dateData="dateData" :finance="financeData"></coreBottomLeftItem>
<div class="bottom" <div class="bottom"
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);"> style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
<coreBottomBar :dateData="dateData" :line="finance.line"></coreBottomBar> <coreBottomBar :dateData="dateData" :line="finance.line"></coreBottomBar>
@@ -14,7 +14,7 @@
</template> </template>
<template v-else-if="activeTab === 'inventory'"> <template v-else-if="activeTab === 'inventory'">
<!-- 存货重点指标对应的内容 --> <!-- 存货重点指标对应的内容 -->
<costItem :dateData="dateData" :cost="cost"></costItem> <costItem :dateData="dateData" :cost="costData" :currentTap='currentTap'></costItem>
<div class="bottom" <div class="bottom"
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);"> style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
<CostsBottomBar :line="cost.line" :dateData="dateData"> <CostsBottomBar :line="cost.line" :dateData="dateData">
@@ -52,13 +52,16 @@ export default {
dateData: { dateData: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
}, }
}, },
data() { data() {
return { return {
activeTab: 'purchase', // 激活的标签purchase=采购inventory=存货 activeTab: 'purchase', // 激活的标签purchase=采购inventory=存货
showChart: true, // 控制图表是否显示 showChart: true, // 控制图表是否显示
chart: null // 图表实例 chart: null, // 图表实例
financeData:{},
costData:{},
currentTap: 'month'
} }
}, },
watch: { watch: {
@@ -71,7 +74,28 @@ export default {
this.updateChart() this.updateChart()
}, },
deep: true 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() { mounted() {
// 初始化图表 // 初始化图表
@@ -85,6 +109,17 @@ export default {
} }
}, },
methods: { 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) { handleTabSwitch(tabType) {
this.activeTab = tabType // tabType由Container组件传递'purchase'或'inventory' this.activeTab = tabType // tabType由Container组件传递'purchase'或'inventory'

View File

@@ -16,7 +16,17 @@
<span class="title-text">{{ nameTwo }}</span> <span class="title-text">{{ nameTwo }}</span>
</div> </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>
<div class="container-body"> <div class="container-body">
@@ -40,10 +50,18 @@ export default {
return { return {
// 初始状态左侧不透明1右侧透明0.3 // 初始状态左侧不透明1右侧透明0.3
isLeftTransparent: true, // 左侧透明度状态true=1false=0.3 isLeftTransparent: true, // 左侧透明度状态true=1false=0.3
isRightTransparent: false // 右侧透明度状态true=1false=0.3 isRightTransparent: false, // 右侧透明度状态true=1false=0.3
activeTab: 'month', // 默认激活的Tab为月度
}; };
}, },
methods: { methods: {
handleTabClick(tabType) {
this.activeTab = tabType;
// 向父组件派发Tab切换事件传递当前选中的Tab类型
this.$emit('tabChange', tabType);
// 可选:同时传递更详细的信息(如标签名)
// this.$emit('tabChange', { type: tabType, name: tabType === 'month' ? '月度' : '累计' });
},
// 点击左侧标题:左侧保持不透明,右侧变透明,并派发采购标签事件 // 点击左侧标题:左侧保持不透明,右侧变透明,并派发采购标签事件
handleLeftClick() { handleLeftClick() {
this.isLeftTransparent = true; // 左侧不透明 this.isLeftTransparent = true; // 左侧不透明
@@ -151,7 +169,7 @@ export default {
background: linear-gradient(90deg, #FFFFFF 0%, rgba(253, 255, 255, 0) 100%); background: linear-gradient(90deg, #FFFFFF 0%, rgba(253, 255, 255, 0) 100%);
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; left: 240px;
z-index: 10; z-index: 10;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
@@ -258,4 +276,41 @@ export default {
// text-align: left; // text-align: left;
// font-style: normal; // 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> </style>

View 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>

View File

@@ -169,7 +169,7 @@ export default {
// 基础表格列配置(只读模式使用) // 基础表格列配置(只读模式使用)
const baseTableProps = [ const baseTableProps = [
{ prop: 'type', label: '指标类型', align: 'center' }, // { prop: 'type', label: '指标类型', align: 'center' },
{ prop: 'name', label: '指标名称', align: 'center' }, { prop: 'name', label: '指标名称', align: 'center' },
{ prop: 'unitLabel', label: '单位', align: 'center' }, { prop: 'unitLabel', label: '单位', align: 'center' },
{ prop: 'value', label: '实际值', align: 'center' }, { prop: 'value', label: '实际值', align: 'center' },

View 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=1false=0.3
isRightTransparent: false, // 右侧透明度状态true=1false=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>

View File

@@ -1,33 +1,49 @@
<template> <template>
<div style="flex: 1"> <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 的固定高度改为自适应 --> <!-- 1. 移除 .kpi-content 的固定高度改为自适应 -->
<div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;"> <div class="kpi-content" style="padding: 14px 16px; display: flex;flex-direction: column; width: 100%;">
<!-- 2. .top 保持 flex无需固定高度自动跟随子元素拉伸 --> <!-- 2. .top 保持 flex无需固定高度自动跟随子元素拉伸 -->
<div class="top" style="display: flex; width: 100%;"> <template v-if="activeTab === 'product'">
<top-item :rawItemList='productData' :dateData="dateData" /> <div class="top" style="display: flex; width: 100%;">
</div> <top-item :rawItemList='productData' :dateData="dateData" />
<div class="bottom" style="display: flex;margin-top: 8px;background-color: rgba(249, 252, 255, 1);"> </div>
<!-- <top-item /> --> <div class="bottom" style="display: flex;margin-top: 8px;background-color: rgba(249, 252, 255, 1);">
<coreBottomBar :lineData="productData.line" :dateData="dateData" /> <!-- <top-item /> -->
<coreBottomBar :lineData="product.line" :dateData="dateData" />
</div> </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> </div>
</Container> </Container>
</div> </div>
</template> </template>
<script> <script>
import Container from './container.vue' import Container from './keyProductContainer.vue'
// import * as echarts from 'echarts' // import * as echarts from 'echarts'
import topItem from './top-product-item.vue' import topItem from './top-product-item.vue'
import coreBottomBar from './productBottomBar.vue' import coreBottomBar from './productBottomBar.vue'
import heatBar from './heatBarChart.vue'
export default { export default {
name: 'ProductionStatus', name: 'ProductionStatus',
components: { Container, topItem, coreBottomBar }, components: { Container, topItem, coreBottomBar, heatBar },
// mixins: [resize], // mixins: [resize],
props: { props: {
productData: { product: {
type: Object,
default: () => {} // 默认空数组,避免报错
},
heat: {
type: Object, type: Object,
default: () => {} // 默认空数组,避免报错 default: () => {} // 默认空数组,避免报错
}, },
@@ -35,13 +51,45 @@ export default {
type: Object, type: Object,
default: () => { } // 默认空数组,避免报错 default: () => { } // 默认空数组,避免报错
}, },
heat: {
type: Object,
default: () => { }
}
}, },
data() { data() {
return { return {
chart: null chart: null,
activeTab: 'product',
currentTap: 'month',
productData:{},
heatData:{}
} }
}, },
watch: { 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() { mounted() {
// 初始化图表(若需展示图表,需在模板中添加对应 DOM // 初始化图表(若需展示图表,需在模板中添加对应 DOM
@@ -49,6 +97,22 @@ export default {
}, },
methods: { 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> </script>

View File

@@ -14,7 +14,7 @@
"> ">
<coreSalesKPIs :sale="sale" :dateData="dateData" /> <coreSalesKPIs :sale="sale" :dateData="dateData" />
<financeCosts :finance="finance" :dateData="dateData" :cost="cost" /> <financeCosts :finance="finance" :dateData="dateData" :cost="cost" />
<keyProductionIndicators :productData="productData" :dateData="dateData" /> <keyProductionIndicators :product="productData" :heat="heat" :dateData="dateData" />
</div> </div>
</div> </div>
<div class="top" style="display: flex;gap: 16px;margin-top: 6px;"> <div class="top" style="display: flex;gap: 16px;margin-top: 6px;">
@@ -152,6 +152,7 @@ export default {
}).then((res) => { }).then((res) => {
console.log(res) console.log(res)
this.productData = res.data.product this.productData = res.data.product
this.heat = res.data.heat
this.purchase = res.data.purchase this.purchase = res.data.purchase
this.inventory = res.data.inventory this.inventory = res.data.inventory
this.importantWork = res.data.importantWork this.importantWork = res.data.importantWork
@@ -160,11 +161,6 @@ export default {
this.sale = res.data.sale this.sale = res.data.sale
this.orderOutput = res.data.orderOutput this.orderOutput = res.data.orderOutput
this.baseOrder = res.data.baseOrder 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) { handleTimeChange(obj) {

View File

@@ -167,8 +167,8 @@ export default {
mobileCodeTimer: 0, mobileCodeTimer: 0,
loginForm: { loginForm: {
loginType: 'uname', loginType: 'uname',
username: 'admin', username: '',//admin
password: 'admin123', password: '',//admin123
captchaVerification: '', captchaVerification: '',
mobile: '', mobile: '',
mobileCode: '', mobileCode: '',