Files
yudao-dev/src/views/home/components/indicatorDetails.vue
2026-04-14 13:54:05 +08:00

529 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="flex: 1">
<Container name="指标填报详情" icon="cockpitItemIcon" size="indicatorDetailsBg" topSize="indicatorDetailsTitleBg">
<div class="kpi-content" style="padding: 14px 14px; display: flex;flex-direction: column; width: 100%;">
<!-- 查询表单区域 -->
<div class="bottom"
style="display: flex;gap: 8px; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);height: 64px;padding: 16px 16px;">
<div style="width: 4px;height: 16px;background: #0B58FF;border-radius: 1px;margin-top: 10px;"></div>
<el-form :inline="true" :model="form" class="demo-form-inline">
<el-form-item label="所属层级">
<el-select v-model="form.levelId" placeholder="请选择" @change='handleLevelChange'>
<el-option v-for="item in levelLList" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="填报月份">
<el-date-picker v-model="form.month" type="month" placeholder="选择月" :editable='false' :clearable='false' @change="handleMonthChange">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button style="background-color: #0B58FF;" type="primary" @click="onSubmit">查询</el-button>
<el-button type="primary" plain size="medium" @click='downLoadExcel'>模板下载</el-button>
<el-button type="primary" plain size="medium" @click='importExcel'>导入</el-button>
<el-button type="primary" plain size="medium" @click='exportExcel'>导出</el-button>
</el-form-item>
</el-form>
</div>
<!-- 表格操作区域 + 表格区域 -->
<div class="bottom"
style="display: flex; width: 100%;margin-top: 8px;background-color: rgba(249, 252, 255, 1);height: 772px;padding: 16px 16px;flex-direction: column; gap: 8px;">
<!-- 只读模式显示编辑按钮 -->
<div v-if="!isDetail" style="display: flex;gap: 8px;align-items: center;height: 32px;">
<div style="width: 4px;height: 16px;background: #0B58FF;border-radius: 1px;"></div>
<div style="width: 58px;
height: 16px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(0,0,0,0.85);
line-height: 16px;
text-align: right;
font-style: normal;">指标详情</div>
<el-button style="background-color: #0B58FF;height: 32px;line-height: 10px;" type="primary"
@click="handleEdit">编辑</el-button>
</div>
<!-- 编辑模式显示快捷操作按钮 -->
<div v-if="isDetail" style="display: flex;gap: 8px;align-items: center;height: 32px;">
<div style="width: 4px;height: 16px;background: #0B58FF;border-radius: 1px;"></div>
<div
style="width: 58px;height: 16px;font-family: PingFangSC, PingFang SC;font-weight: 400;font-size: 14px;color: rgba(0,0,0,0.85);line-height: 16px;text-align: right;font-style: normal;">
指标详情</div>
<el-button style="background-color: #0B58FF;height: 32px;line-height: 10px;" type="primary"
@click="handleSave">保存</el-button>
<el-button text style="height: 32px;line-height: 10px;" plain size="medium"
@click="handleCancel">取消</el-button>
</div>
<!-- 表格组件添加key属性强制刷新绑定所有必要属性 -->
<base-table style="height: 700px;" @emitFun="inputChange" class="right-aside" :table-props="tableProps"
:page="form.pageNo" :limit="form.pageSize" :table-data="tableData" ref="baseTable"
:key="`base-table-${isDetail}`" :maxHeight="700"
></base-table>
</div>
</div>
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body @close="handleImportDialogClose">
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" action="#" :disabled="upload.httpUploading"
:on-change="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<span>仅允许导入xlsxlsx格式文件</span>
</div>
<div class="el-upload__tip" slot="tip">
</div>
</el-upload>
<div v-if="upload.httpUploading" class="upload-progress-wrap">
<div class="upload-progress-track">
<div class="upload-progress-bar"></div>
</div>
<span class="upload-progress-text">正在上传</span>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" :loading="upload.httpUploading" :disabled="upload.httpUploading" @click="submitFileForm"> </el-button>
<el-button :disabled="upload.httpUploading" @click="cancelBtn"> </el-button>
</div>
</el-dialog>
</Container>
</div>
</template>
<script>
import Container from './container.vue'
import { getLevelStruc, getRealMonthPage, updateRealMonthData, getDictListData,importTemplateZB } from '@/api/cockpit'
import inputArea from './inputArea.vue' // 导入输入组件
import {getAccessToken, getTenantId} from '@/utils/auth'
import axios from 'axios';
import moment from 'moment'
export default {
name: 'ProductionStatus',
components: {
Container,
inputArea // 注册输入组件
},
props: {
// 可保留原有props配置若无需求可忽略
},
data() {
return {
form: {
levelId: undefined,
pageNo: 1,
pageSize: 100,
month: undefined,
startTime: undefined, // 当月起始时间戳1号00:00:00
endTime: undefined // 当月结束时间戳月末23:59:59
},
dictData:[],
isDetail: false, // 编辑状态标识false=只读true=编辑
tableData: [], // 表格数据
levelLList: [], // 所属层级下拉数据
tableProps: [], // 表格列配置
upload: {
// 是否显示弹出层
open: false,
// 弹出层标题
title: "指标填报导入",
fileList:[],
currentFile:null,
// HTTP 上传中(点击确定后 axios 上传,展示不确定进度条)
httpUploading: false
}
}
},
watch: {
// 可选监听isDetail变化确保配置同步双重保障
isDetail(newVal) {
this.initTableProps(newVal);
}
},
mounted() {
// 1. 初始化当前月份到日期选择器
const currentDate = new Date();
this.form.month = currentDate;
// 2. 计算当月起止时间戳
this.setMonthTimeStamp();
// 3. 先初始化表格配置(优先于数据请求,避免表格空配置渲染)
this.initTableProps(this.isDetail);
// 4. 等待配置就绪后,再请求数据,避免异步冲突
this.getDictData()
this.$nextTick(() => {
this.getData();
});
},
methods: {
getDictData() {
getDictListData({ pageNo: 1, pageSize: 100, dictType: 'lb_dw' }).then((res) => {
this.dictData = res.data.list
})
},
// 表格单元格数据变更回调
inputChange(val) {
console.log('修改的数据:', val);
// 安全修改:判断索引是否存在,避免数组越界
if (this.tableData[val._pageIndex - 1]) {
this.tableData[val._pageIndex - 1][val.prop] = val[val.prop];
// 标记数据为已修改状态
this.tableData[val._pageIndex - 1].status = 1;
}
},
// 初始化表格列配置核心精准控制inputArea挂载
initTableProps(isEdit) {
console.log('当前编辑状态:', isEdit);
// 基础表格列配置(只读模式使用)
const baseTableProps = [
{ prop: 'type', label: '指标类型', align: 'center' },
{ prop: 'name', label: '指标名称', align: 'center' },
{ prop: 'unitLabel', label: '单位', align: 'center' },
{ prop: 'value', label: '实际值', align: 'center' },
];
if (isEdit) {
// 编辑模式仅给「预估值」列添加inputArea精准挂载避免无效配置
this.tableProps = baseTableProps.map(item => {
if (item.prop === 'value') { // 只给需要编辑的列添加子组件
return {
...item,
subcomponent: inputArea // 挂载输入组件
};
}
return item; // 其他列保持原有配置
});
} else {
// 只读模式:深拷贝基础配置,避免引用污染
this.tableProps = JSON.parse(JSON.stringify(baseTableProps));
}
console.log('表格配置:', this.tableProps);
},
// 切换到编辑模式
handleEdit() {
this.isDetail = true;
// 先更新表格配置,再强制表格刷新(双重保障)
this.initTableProps(this.isDetail);
this.$nextTick(() => {
if (this.$refs.baseTable) {
this.$refs.baseTable.$forceUpdate(); // 强制表格组件刷新
}
});
},
handleSave() {
this.$modal.confirm('是否确认保存数据?').then(() => {
return updateRealMonthData(this.tableData)
}).then(() => {
this.isDetail = false;
// 重置表格配置
this.initTableProps(this.isDetail);
// 清空并重新请求数据,恢复原始状态
this.$nextTick(() => {
this.tableData = [];
this.getDataPage();
});
this.$modal.msgSuccess("保存成功");
this.$emit('updateLeft')
}).catch(() => { });
},
// 取消编辑,恢复只读模式
handleCancel() {
this.isDetail = false;
// 重置表格配置
this.initTableProps(this.isDetail);
// 清空并重新请求数据,恢复原始状态
this.$nextTick(() => {
this.tableData = [];
this.getDataPage();
});
console.log('已取消编辑,恢复原始数据');
},
handleClear() {
this.isDetail = false;
// 重置表格配置
this.initTableProps(this.isDetail);
// 清空并重新请求数据,恢复原始状态
this.$nextTick(() => {
this.tableData = [];
this.getDataPage();
});
},
// 通用方法:计算指定月份(默认当前月)的起止时间戳
setMonthTimeStamp(selectDate) {
let targetDate;
if (selectDate) {
targetDate = new Date(selectDate); // 传入选中日期,计算对应月份
} else {
targetDate = new Date(); // 未传入,使用当前系统日期
}
const year = targetDate.getFullYear();
const month = targetDate.getMonth(); // 月份0-11
// 计算当月1号 00:00:00 时间戳
const startDate = new Date(year, month, 1, 0, 0, 0);
this.form.startTime = startDate.getTime();
// 计算当月最后一天 23:59:59 时间戳(精准版)
const lastDay = new Date(year, month + 1, 0).getDate();
const endDatePrecise = new Date(year, month, lastDay, 23, 59, 59);
this.form.endTime = endDatePrecise.getTime();
},
// 日期选择器变更事件:更新对应月份的起止时间戳
handleMonthChange(val) {
if (!val) {
// 清空选择时,重置时间戳
this.form.startTime = undefined;
this.form.endTime = undefined;
return;
}
// 计算选中月份的起止时间戳
this.setMonthTimeStamp(val);
// 重新请求数据
this.getDataPage();
},
getUnitLabel(unitCode) {
// 若字典为空或无匹配编码,返回原编码或空字符串
if (!this.dictData || this.dictData.length === 0) {
return unitCode || '';
}
// 查找匹配的字典项
const matchItem = this.dictData.find(item => item.value == unitCode);
// 返回匹配的label无匹配则返回原unit编码
return matchItem ? matchItem.label : (unitCode || '');
},
// 请求下拉数据和表格数据
getData() {
// 1. 请求所属层级下拉数据
getLevelStruc().then((res) => {
this.levelLList = res.data || [];
this.form.levelId = this.levelLList[0].id;
this.$emit('updateLevel', this.levelLList[0].id)
this.getDataPage()
}).catch(err => {
console.error('获取所属层级失败:', err);
this.levelLList = [];
});
this.$emit('updateLeft')
},
getDataPage() {
// 2. 请求表格分页数据
getRealMonthPage({
levelId: this.form.levelId,
startTime: this.form.startTime,
endTime: this.form.endTime,
pageSize: this.form.pageSize,
pageNo: this.form.pageNo
}).then((res) => {
console.log('表格数据:', res);
this.tableData = res.data.map(item => {
// 新增unitLabel字段存储匹配后的显示名称
return {
...item,
unitLabel: this.getUnitLabel(item.unit)
};
});
}).catch(err => {
console.error('获取表格数据失败:', err);
this.tableData = [];
});
},
handleLevelChange(id) {
this.form.levelId = id;
this.getDataPage()
this.$emit('updateLevel', id)
},
// 查询按钮点击事件(可根据需求扩展逻辑)
onSubmit() {
// 清空原有表格数据,重新请求
this.tableData = [];
this.$nextTick(() => {
this.getDataPage();
});
},
// 导入
importExcel() {
this.upload.httpUploading = false
this.upload.open = true
},
downLoadExcel() {
importTemplateZB({
levelId:this.form.levelId,
startTime: this.form.startTime,
endTime: this.form.endTime,
pageSize: 1000,
template:1,
pageNo: this.form.pageNo
}).then(response => {
let factoryName = this.levelLList.filter(item => item.id == this.form.levelId)[0].name;
let fileName = factoryName+'_指标导入模板.xls';
this.$download.excel(response, fileName);
});
},
exportExcel() {
importTemplateZB({
levelId:this.form.levelId,
startTime: this.form.startTime,
endTime: this.form.endTime,
pageSize: 1000,
template:0,
pageNo: this.form.pageNo
}).then(response => {
let mon = moment(this.form.endTime).format('YYYYMM');
let factoryName = this.levelLList.filter(item => item.id == this.form.levelId)[0].name;
let fileName = mon + factoryName + '月度指标填报表.xls';
this.$download.excel(response, fileName);
});
},
// 文件上传中处理
handleFileUploadProgress(file, fileList) {
console.log('文件上传中:',file, fileList)
this.upload.fileList = fileList;
this.upload.currentFile = file.raw;
},
handleFileSuccess() {},
importTemplate() {},
// 提交上传文件
async submitFileForm() {
if (!this.upload.currentFile) {
return this.$message.error('请先选择要上传的文件!')
}
if (this.upload.httpUploading) {
return
}
this.upload.httpUploading = true
try {
const formData = new FormData()
formData.append('file', this.upload.currentFile) // 文件字段
formData.append('reportDate', this.form.endTime) // 时间维度字段
formData.append('levelId', this.form.levelId) // 层级维度字段
const response = await axios({
url: process.env.VUE_APP_BASE_API + '/admin-api/lb/index-real-month/actualIndicatorImport',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': "Bearer " + getAccessToken(),
'tenant-id': getTenantId(),
},
timeout: 300000
})
// 4. 处理响应结果
if (response.data.code === 0) {
this.$message.success('文件上传成功!')
// 重置表单
this.upload.fileList = []
this.upload.currentFile = null
this.upload.open = false
this.$refs.upload.clearFiles();
this.getDataPage();
this.$emit('updateLeft')
} else {
this.$message.error(`上传失败:${response.data.msg || '未知错误'}`)
}
} catch (error) {
// 5. 异常处理
console.error('文件上传出错:', error)
this.$message.error('上传失败!')
} finally {
this.upload.httpUploading = false
}
},
cancelBtn() {
if (this.upload.httpUploading) {
return
}
this.upload.open = false
},
handleImportDialogClose() {
this.upload.currentFile = null
this.upload.fileList = []
this.$nextTick(() => {
if (this.$refs.upload) {
this.$refs.upload.clearFiles()
}
})
}
}
}
</script>
<style lang='scss' scoped>
.upload-progress-wrap {
margin-top: 12px;
}
.upload-progress-track {
height: 8px;
border-radius: 4px;
background: rgba(11, 88, 255, 0.12);
overflow: hidden;
position: relative;
}
.upload-progress-bar {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 38%;
border-radius: 4px;
background: linear-gradient(90deg, #0b58ff, #5b9aff, #0b58ff);
animation: upload-indeterminate-slide 1.35s ease-in-out infinite;
}
@keyframes upload-indeterminate-slide {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(320%);
}
}
.upload-progress-text {
display: block;
margin-top: 8px;
font-size: 12px;
color: rgba(0, 0, 0, 0.55);
text-align: center;
}
// 月份列表容器样式(保留原有配置,若无使用可忽略)
.month-list {
// 内联样式已优化行间距,此处可留空或补充
}
// 基础月份样式(保留原有配置,若无使用可忽略)
.monthItem {
width: 164px;
height: 42px;
border-radius: 4px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 20px;
color: rgba(0, 0, 0, 0.85);
line-height: 42px;
text-align: center;
font-style: normal;
cursor: pointer;
transition: all 0.2s ease;
border: 2px solid transparent;
margin: 0;
}
.monthItem.has-data {
background-color: #D1E8FF;
}
.monthItem:not(.has-data) {
background-color: #EFF3F8;
}
.monthItem.active {
border: 2px solid #0B58FF !important;
}
</style>