Files
shgx_tz_vue-sync/src/views/paintLabManagement/dataRawMaterial.vue
2025-03-28 17:29:58 +08:00

757 lines
23 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 class="app-container">
<!-- 搜索部分 -->
<el-form :model="searchForm" inline size="mini" label-width="80px">
<el-form-item label="开始日期">
<el-date-picker
:style="{ width: inputWidth }"
v-model="searchForm.startDate"
type="datetime"
placeholder="选择日期时间"
align="right"
:picker-options="datePickerOptions"
>
</el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker
:style="{ width: inputWidth }"
v-model="searchForm.endDate"
type="datetime"
placeholder="选择日期时间"
align="right"
:picker-options="datePickerOptions"
>
</el-date-picker>
</el-form-item>
<el-form-item label="批次号">
<el-input
v-model.trim="searchForm.batchNumber"
placeholder="输入批次号"
:style="{ width: inputWidth }"
></el-input>
</el-form-item>
<el-form-item label="颜色代号">
<el-input
v-model.trim="searchForm.colorCode"
placeholder="颜色代号"
:style="{ width: inputWidth }"
></el-input>
</el-form-item>
<el-form-item label="产品描述">
<el-input
v-model.trim="searchForm.productDescription"
placeholder="输入产品描述"
:style="{ width: inputWidth }"
></el-input>
</el-form-item>
<br />
<el-form-item label=" ">
<el-button
size="mini"
type="primary"
icon="el-icon-search"
@click="fetchData"
>搜索</el-button
>
<el-button
size="mini"
type="primary"
icon="el-icon-plus"
@click="showAddDialog = true"
>增加记录</el-button
>
<el-button
size="mini"
type="success"
icon="el-icon-download"
@click="exportData"
>导出当前搜索数据</el-button
>
</el-form-item>
</el-form>
<div style="width: 100%; display: flex; justify-content: flex-end">
<pagination
:pageSizes="[30, 60, 120, 240]"
:total="pagination.total"
:page.sync="pagination.currentPage"
:limit.sync="pagination.pageSize"
@pagination="fetchData"
/>
</div>
<div style="width: 100%">
<div style="font-size: 20px; text-align: center">
原材料数据记录 {{ realTotal }}
</div>
</div>
<el-button
size="mini"
type="primary"
icon="el-icon-check"
@click="saveAllChanges"
:disabled="Object.keys(modifiedRows).length === 0"
>
保存所有修改已修改 {{ Object.keys(modifiedRows).length }}
</el-button>
<!-- 表格部分 -->
<vxe-table
@menu-click="handleContextMenuClick"
:menu-config="tableMenuConfig"
border
:header-cell-style="getHeaderCellStyle"
ref="dataTable"
:column-config="{ resizable: true }"
:row-config="{ isHover: false, isCurrent: false, height: 30 }"
:loading="isLoading"
:data="qualityStatistics"
:merge-cells="mergedCells"
:cell-style="getCellStyle"
size="mini"
align="center"
height="800px"
:export-config="{}"
:edit-config="{ trigger: 'click', mode: 'cell' }"
@edit-actived="handleEditActived"
@edit-closed="handleEditClosed"
>
<vxe-column
field="code"
title="颜色/代号"
width="140px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-textarea
:autosize="{ minRows: 4 }"
v-model="row.code"
type="text"
></vxe-textarea>
</template>
</vxe-column>
<vxe-column
field="description"
title="产品描述"
width="140px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-textarea
:autosize="{ minRows: 4 }"
v-model="row.description"
@input="handleSearchMaterial(row)"
type="text"
></vxe-textarea>
<!-- 物料清单下拉建议功能 -->
<div class="material-suggestions" v-if="materialList.length">
<div
v-for="material in materialList"
:key="material.key"
@click="handleSelectMaterial(row, material)"
>
{{ material.label }}
</div>
</div>
</template>
</vxe-column>
<vxe-column
field="pci"
title="批次号"
width="140px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-textarea
:autosize="{ minRows: 4 }"
v-model="row.pci"
type="text"
></vxe-textarea>
</template>
</vxe-column>
<vxe-column field="value01" title="角度" width="140px"></vxe-column>
<vxe-column
field="value02"
title="△L"
min-width="80px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input
v-model="row.value02"
type="text"
@blur.native.capture="updateUser(row.value02)"
></vxe-input>
</template>
</vxe-column>
<vxe-column
field="value03"
title="△A"
min-width="80px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value03" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column
field="value04"
title="△B"
min-width="80px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value04" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column
field="value05"
title="△E"
min-width="80px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value05" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column
field="value06"
title="mDE"
min-width="100px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value06" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-colgroup title="膜厚" align="center">
<vxe-column field="value07" title="名称" min-width="80px"></vxe-column>
<vxe-column
field="value08"
title="数值"
min-width="80px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value08" type="text"></vxe-input>
</template>
</vxe-column>
</vxe-colgroup>
<vxe-colgroup title="来料检" align="center">
<vxe-column
field="value09"
title="粘度"
min-width="60px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value09" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column
field="value10"
title="固含量"
min-width="100px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value10" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column
field="value11"
title="电阻值"
min-width="80px"
:edit-render="{ autofocus: '.vxe-input--inner' }"
>
<template #edit="{ row }">
<vxe-input v-model="row.value11" type="text"></vxe-input>
</template>
</vxe-column>
</vxe-colgroup>
</vxe-table>
<el-dialog title="提示" :visible.sync="showAddDialog" width="30%">
<span>确定增加一组数据</span>
<span slot="footer" class="dialog-footer">
<el-button @click="showAddDialog = false"> </el-button>
<el-button type="primary" @click="addRecord"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { downloadStatisticsTableExcel } from "@/api/qualityManagement/qualityStatistics_V2";
import {
GetRawMateriallist,
AddRawMateriallist,
UpdateRawMateriallist,
DelRawMateriallist,
QueryMaterialOptions,
} from "@/api/paintLabManagement/dataRawMaterial";
import { debounce } from "lodash";
export default {
name: "DataRawMaterial",
data() {
return {
modifiedRows: {}, // 改为响应式对象
inputWidth: "180px",
datePickerOptions: {
shortcuts: [
{
text: "今天",
onClick(picker) {
picker.$emit("pick", new Date());
},
},
{
text: "昨天",
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24);
picker.$emit("pick", date);
},
},
{
text: "一周前",
onClick(picker) {
const date = new Date();
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit("pick", date);
},
},
],
},
searchForm: {
startDate: new Date(
new Date(new Date().toLocaleDateString()).getTime()
),
endDate: new Date(
new Date(new Date().toLocaleDateString()).getTime() +
24 * 60 * 60 * 1000 -
1
),
batchNumber: null,
colorCode: null,
reportType: 0,
productDescription: "",
},
reportTypes: [{ label: "原材料记录", value: 0 }],
pagination: { total: 0, currentPage: 1, pageSize: 30 },
realTotal: 0,
isLoading: false,
qualityStatistics: [],
mergedCells: [],
hiddenColumns: [],
tableMenuConfig: {
header: {
options: [
[
{ code: "hideColumn", name: "隐藏列" },
{ code: "showAllColumns", name: "取消所有隐藏列" },
],
],
},
body: {
options: [
[
{ code: "removeRow", name: "删除" },
{ code: "updateRow", name: "修改" },
],
],
},
visibleMethod: this.isMenuItemVisible,
},
showAddDialog: false,
materialList: [], // 存储搜索结果
searchDebounce: null, // 防抖实例
};
},
mounted() {
this.fetchData();
},
methods: {
handleEditActived({ row }) {
if (!this.modifiedRows[row.id]) {
// 深拷贝原始数据
this.$set(this.modifiedRows, row.id, JSON.parse(JSON.stringify(row)));
}
},
handleEditClosed({ row }) {
const original = this.modifiedRows[row.id];
if (original) {
// 深度比较对象差异
const changed = Object.keys(original).some((key) => {
// 特殊处理日期类型
if (key === "dt") {
return (
this.$dayjs(original[key]).format() !==
this.$dayjs(row[key]).format()
);
}
return original[key] !== row[key];
});
if (!changed) {
this.$delete(this.modifiedRows, row.id);
}
}
},
async saveAllChanges() {
if (Object.keys(this.modifiedRows).length === 0) {
this.$message.warning("没有需要保存的修改");
return;
}
try {
// 创建分组映射
const groupMap = new Map();
// 遍历所有修改的行
Object.values(this.modifiedRows).forEach((row) => {
// 找到当前行在表格中的索引
const rowIndex = this.qualityStatistics.findIndex(
(r) => r.id === row.id
);
// 计算所属分组的起始索引每6行一组
const groupIndex = Math.floor(rowIndex / 6) * 6;
// 如果该组还未创建,初始化分组
if (!groupMap.has(groupIndex)) {
groupMap.set(groupIndex, {
baseRow: this.qualityStatistics[groupIndex],
modifiedRows: new Set(),
});
}
// 将修改行添加到对应分组
const group = groupMap.get(groupIndex);
group.modifiedRows.add(rowIndex % 6); // 记录组内修改的位置
});
// 构建最终更新数据
const updateList = [];
groupMap.forEach((group, groupIndex) => {
const groupRows = this.qualityStatistics.slice(
groupIndex,
groupIndex + 6
);
// 生成6条更新记录即使部分未修改
Array.from({ length: 6 }).forEach((_, i) => {
const row = groupRows[i] || {};
if (group.baseRow == undefined) {
throw new Error("需要保存的数据不在当前页");
}
updateList.push({
id: row.id,
plIdGroup: row.idGroup,
description: group.baseRow.description,
plCode: group.baseRow.code,
plPci: group.baseRow.pci,
plValue01: row.value01,
plValue02: row.value02,
plValue03: row.value03,
plValue04: row.value04,
plValue05: row.value05,
plValue06: group.baseRow.value06,
plValue07: group.baseRow.value07,
plValue08: group.baseRow.value08,
plValue09: row.value09,
plValue10: row.value10,
plValue11: row.value11,
plCreatedBy: row.createdBy,
plCreatedTime: row.createdTime,
plUpdatedBy: row.updatedBy,
plUpdatedTime: row.updatedTime,
});
});
});
const { code, data } = await UpdateRawMateriallist(updateList);
if (code === 200) {
this.$message.success(`成功保存${data}条记录`);
this.modifiedRows = {};
await this.fetchData();
// 保持合并单元格状态
this.mergeTableRows(6);
}
} catch (error) {
console.error("保存失败:", error);
this.$message.error(
"保存失败: " + (error.response?.data?.message || error.message)
);
}
},
updateUser(value) {
console.log(value);
},
addRecord() {
this.showAddDialog = false;
AddRawMateriallist({ num: 1 }).then((res) => {
if (res.code === 200) this.fetchData();
});
},
async fetchData() {
try {
this.isLoading = true;
const query = {
startDate: this.$dayjs(this.searchForm.startDate).format(
"YYYY-MM-DDTHH:mm:ss"
),
endDate: this.$dayjs(this.searchForm.endDate).format(
"YYYY-MM-DDTHH:mm:ss"
),
batchNumber: this.searchForm.batchNumber || undefined,
colorCode: this.searchForm.colorCode || undefined,
productDescription: this.searchForm.productDescription || undefined,
pageNum: this.pagination.currentPage,
pageSize: this.pagination.pageSize,
};
const { code, data } = await GetRawMateriallist(query);
if (code === 200) {
this.qualityStatistics = data.item1.map((item) => ({
...item,
dt: this.$dayjs(item.dt).isValid()
? this.$dayjs(item.dt).format("YYYY-MM-DD HH:mm:ss")
: null,
}));
this.pagination.total = data.item2;
this.realTotal = Math.ceil(data.item2 / 5); // 根据实际分组调整
this.$nextTick(() => this.mergeTableRows(5)); // 根据实际分组调整
}
} catch (error) {
console.error("数据加载失败:", error);
this.$message.error(
"数据加载失败: " + (error.response?.data?.message || error.message)
);
} finally {
this.isLoading = false;
}
},
// fetchData() {
// this.isLoading = true;
// setTimeout(() => (this.isLoading = false), 30000);
// const query = { ...this.searchForm, ...this.pagination };
// ["batchNumber", "colorCode", "productDescription"].forEach(
// (key) => delete query[key]
// );
// GetRawMateriallist(query).then((res) => {
// if (res.code === 200) {
// this.qualityStatistics = res.data.item1;
// this.pagination.total = res.data.item2;
// this.realTotal = Math.ceil(this.pagination.total / 5);
// this.mergeTableRows(5);
// this.isLoading = false;
// }
// });
// },
getHeaderCellStyle() {
return {
backgroundColor: "#2D3D51",
color: "#ffffff",
border: "1px solid #161823",
fontSize: "18px",
};
},
getCellStyle() {
return {
border: "1px solid #161823",
fontSize: "16px",
fontWeight: "700",
};
},
exportData() {
this.$refs.dataTable.openExport();
// const data = { ...this.searchForm, ...this.pagination, type: this.searchForm.reportType + 1, isShowDetail: isDetailed };
// const fileName = `${this.reportTypes[this.searchForm.reportType].label}${isDetailed ? '-缺陷详情' : '-日生产报表'}.xlsx`;
// downloadStatisticsTableExcel(data).then(res => {
// const blobURL = URL.createObjectURL(res.data);
// const link = document.createElement('a');
// link.href = blobURL;
// link.download = fileName;
// link.style.display = 'none';
// document.body.appendChild(link);
// link.click();
// document.body.removeChild(link);
// URL.revokeObjectURL(blobURL);
// });
},
offsetRate(num) {
return num === null ? "" : parseInt(num);
},
offsetValue(num) {
return num === 0 || num === "0" ? "" : num;
},
handleContextMenuClick({ menu, row }) {
switch (menu.code) {
case "hideColumn":
this.hideColumn(row);
break;
case "showAllColumns":
this.resetColumns();
break;
case "removeRow":
this.removeRow(row);
break;
case "updateRow":
this.updateRow(row);
break;
}
},
hideColumn(column) {
this.$refs.dataTable.hideColumn(column);
this.hiddenColumns.push(column.title);
this.resetMergedCells();
},
resetColumns() {
this.$refs.dataTable.resetColumn();
this.hiddenColumns = [];
this.resetMergedCells();
},
resetMergedCells() {
this.mergeTableRows(this.searchForm.reportType === 3 ? 2 : 3);
},
removeRow(row) {
this.$confirm("此操作将永久删除该组记录, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
DelRawMateriallist({ idGroup: row.idGroup }).then((res) => {
if (res.code === 200) {
this.$message.success("删除成功!");
this.fetchData();
} else {
this.$message.error("删除失败!请检查!");
}
});
});
},
// updateRow(row) {
// const baseIndex = Math.floor(this.qualityStatistics.indexOf(row) / 5) * 5;
// const updatedData = Array.from({ length: 5 }, (_, i) => ({
// id: this.qualityStatistics[baseIndex + i].id,
// plIdGroup: this.qualityStatistics[baseIndex + i].idGroup,
// description: this.qualityStatistics[baseIndex].description,
// plCode: this.qualityStatistics[baseIndex].code,
// plPci: this.qualityStatistics[baseIndex].pci,
// plValue01: this.qualityStatistics[baseIndex + i].value01,
// plValue02: this.qualityStatistics[baseIndex + i].value02,
// plValue03: this.qualityStatistics[baseIndex + i].value03,
// plValue04: this.qualityStatistics[baseIndex + i].value04,
// plValue05: this.qualityStatistics[baseIndex + i].value05,
// plValue06: this.qualityStatistics[baseIndex].value06,
// plValue07: this.qualityStatistics[baseIndex + i].value07,
// plValue08: this.qualityStatistics[baseIndex + i].value08,
// plValue09: this.qualityStatistics[baseIndex].value09,
// plValue10: this.qualityStatistics[baseIndex].value10,
// plValue11: this.qualityStatistics[baseIndex].value11,
// plCreatedBy: this.qualityStatistics[baseIndex + i].createdBy,
// plCreatedTime: this.qualityStatistics[baseIndex + i].createdTime,
// plUpdatedBy: this.qualityStatistics[baseIndex + i].updatedBy,
// plUpdatedTime: this.qualityStatistics[baseIndex + i].updatedTime,
// }));
// UpdateRawMateriallist(updatedData).then((res) => {
// if (res.code === 200) {
// this.$message.success(`更新成功-${res.data}`);
// }
// });
// },
mergeTableRows(rowsPerGroup) {
this.mergedCells = [];
for (let i = 0; i < this.qualityStatistics.length; i += rowsPerGroup) {
this.mergedCells.push(
{ row: i, col: 0, rowspan: rowsPerGroup },
{ row: i, col: 1, rowspan: rowsPerGroup },
{ row: i, col: 2, rowspan: rowsPerGroup },
{ row: i, col: 8, rowspan: rowsPerGroup },
{ row: i, col: 11, rowspan: rowsPerGroup },
{ row: i, col: 12, rowspan: rowsPerGroup },
{ row: i, col: 13, rowspan: rowsPerGroup }
);
}
},
isMenuItemVisible(menu) {
// Implement visibility logic if needed
return true;
},
// 初始化防抖函数
initDebounce() {
this.searchDebounce = debounce(this.doSearchMaterial, 500);
},
// 输入处理
handleSearchMaterial(row) {
if (!this.searchDebounce) {
this.initDebounce();
}
// 清除旧数据
if (!row.description) {
this.materialList = [];
return;
}
this.searchDebounce(row.description);
},
// 实际搜索方法
async doSearchMaterial(query) {
try {
const res = await QueryMaterialOptions({ query });
this.materialList = res.data;
} catch (e) {
console.error("搜索失败", e);
}
},
// 选择物料
handleSelectMaterial(row, material) {
row.description = material.value;
row.code = material.color;
this.materialList = [];
},
},
};
</script>
<style lang="scss" scoped>
/* 建议列表样式 */
.material-suggestions {
position: absolute;
z-index: 999;
background: white;
border: 1px solid #e8eaec;
max-height: 200px;
overflow-y: auto;
}
.material-suggestions div {
padding: 8px;
cursor: pointer;
}
.material-suggestions div:hover {
background-color: #f5f7fa;
}
</style>