Merge remote-tracking branch 'origin/0.8.X' into 0.8.X

This commit is contained in:
2026-03-23 15:33:46 +08:00
53 changed files with 2809 additions and 114 deletions

View File

@@ -8,3 +8,10 @@ export function getAcidTypingPrefill(currentCoilNo) {
})
}
export function getGalvanize1TypingPrefill(params) {
return request({
url: '/pocket/galvanize1/crmPdoExcoil/list',
method: 'get',
params
})
}

View File

@@ -54,7 +54,7 @@
<!-- 第四行材质 + 表面质量 -->
<div class="info-grid-item label-cell">材质</div>
<div class="info-grid-item value-cell">
<div class="nob">{{ content.materialWithManufacturer || '' }}</div>
<div class="nob">{{ materialWithManufacturer || '' }}</div>
</div>
<div class="info-grid-item label-cell">表面状态</div>
<div class="info-grid-item value-cell">
@@ -434,10 +434,14 @@ export default {
.nob {
width: 100%;
height: 100%;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
border: none;
outline: none;
background: transparent;
text-align: center;
font-size: 1.2em;
}
/* 打印样式 - 强制单页适配180mm x 100mm纸张保持原有样式不变 */

View File

@@ -54,7 +54,7 @@
<!-- 第四行材质 + 表面质量 -->
<div class="info-grid-item label-cell">材质</div>
<div class="info-grid-item value-cell">
<div class="nob">{{ content.materialWithManufacturer || '' }}</div>
<div class="nob">{{ materialWithManufacturer || '' }}</div>
</div>
<div class="info-grid-item label-cell">镀层种类</div>
<div class="info-grid-item value-cell">
@@ -102,7 +102,7 @@
<!-- 第八行生产日期跨3列 -->
<div class="info-grid-item label-cell">生产日期</div>
<div class="info-grid-item value-cell">
<div>{{ parseTime(content.updateTime, '{y}-{m}-{d}') }}</div>
<div class="nob">{{ parseTime(content.updateTime, '{y}-{m}-{d}') }}</div>
<!-- <input type="text" class="nob" :value=" /> -->
</div>
</div>
@@ -437,8 +437,12 @@ export default {
height: 100%;
border: none;
outline: none;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
background: transparent;
text-align: center;
font-size: 1.2em;
}
/* 打印样式 - 强制单页适配180mm x 100mm纸张保持原有样式不变 */

View File

@@ -333,6 +333,9 @@ export default {
.nob {
width: 100%;
height: 100%;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
border: none;
outline: none;
background: transparent;

View File

@@ -60,7 +60,7 @@
<!-- 第四行材质 + 表面质量 -->
<div class="info-grid-item label-cell">材质</div>
<div class="info-grid-item value-cell">
<div class="nob">{{ content.materialWithManufacturer || '' }}</div>
<div class="nob">{{ materialWithManufacturer || '' }}</div>
<!-- <input type="text" class="nob" :value="content.materialWithManufacturer || ''" /> -->
</div>
<div class="info-grid-item label-cell">质量要求</div>
@@ -392,7 +392,7 @@ export default {
.info-grid-item {
border: 1px solid #000;
padding: 0.1em;
font-size: 1.05em;
font-size: 1.2em;
height: 2em;
text-align: center;
vertical-align: middle;
@@ -443,8 +443,12 @@ export default {
height: 100%;
border: none;
outline: none;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
background: transparent;
text-align: center;
font-size: 1.2em;
}
/* 打印样式 - 强制单页适配180mm x 100mm纸张保持原有样式不变 */

View File

@@ -253,6 +253,10 @@ export default {
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
font-size: 1.2em;
}
/* 打印样式 */

View File

@@ -246,6 +246,10 @@ export default {
word-break: break-all;
overflow-wrap: break-word;
white-space: normal;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
font-size: 1.2em;
}
/* 打印样式 - 强制单页适配100mm x 80mm纸张保持原有样式不变 */

View File

@@ -244,6 +244,7 @@ export default {
.nob {
width: 100%;
height: 100%;
font-size: 1.2em;
border: none;
outline: none;
background: transparent;
@@ -253,6 +254,10 @@ export default {
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #000;
font-family: '黑体', serif;
font-size: 1.2em;
}
/* 打印样式 */

View File

@@ -454,7 +454,7 @@
<el-dialog title="分步加工" :visible.sync="stepSpilt.visible" width="1400px" append-to-body>
<step-split @print="handlePrintLabel" @complete="handleComposeSplit" :actionId="stepSpilt.actionId"
:coilId="stepSpilt.coilId" :actionStatus="stepSpilt.actionStatus" />
:coilId="stepSpilt.coilId" :actionStatus="stepSpilt.actionStatus" :actionType="stepSpilt.actionType" />
</el-dialog>
<label-render ref="labelRender" v-show="false" :content="labelRender.data" :labelType="labelRender.type" />
@@ -577,7 +577,8 @@ export default {
visible: false,
coilId: null,
actionId: null,
actionStatus: null
actionStatus: null,
actionType: null,
}
}
},
@@ -642,6 +643,10 @@ export default {
// 如果待操作列表为空,则加载数据
if (this.pendingActionList.length === 0) {
this.getPendingAction()
console.log(this.useSpecialSplit)
if (this.useSpecialSplit) {
this.getStepSplitList()
}
}
}
},
@@ -673,15 +678,16 @@ export default {
// 立即加载物料列表(不依赖字典)
// this.getMaterialCoil()
// 尝试加载待操作列表(如果字典已加载)
this.$nextTick(() => {
if (this.acidRollingActionType) {
this.actionQueryParams.actionType = this.acidRollingActionType
this.getPendingAction()
if (this.useSpecialSplit) {
this.getStepSplitList()
}
}
})
// this.$nextTick(() => {
// if (this.acidRollingActionType) {
// this.actionQueryParams.actionType = this.acidRollingActionType
// this.getPendingAction()
// console.log(this.useSpecialSplit)
// if (this.useSpecialSplit) {
// this.getStepSplitList()
// }
// }
// })
},
mounted() {
// 确保在mounted时也尝试加载字典数据可能此时才加载完成
@@ -689,6 +695,10 @@ export default {
if (this.acidRollingActionType && !this.actionQueryParams.actionType) {
this.actionQueryParams.actionType = this.acidRollingActionType
this.getPendingAction()
console.log(this.useSpecialSplit)
if (this.useSpecialSplit) {
this.getStepSplitList()
}
}
})
},
@@ -1008,6 +1018,7 @@ export default {
this.stepSpilt.coilId = row.coilId
this.stepSpilt.actionId = row.actionId
this.stepSpilt.actionStatus = row.actionStatus
this.stepSpilt.actionType = row.actionType
this.stepSpilt.visible = true
// this.buttonLoading = true
// this.getPendingAction()

View File

@@ -67,8 +67,13 @@
</el-table-column>
</el-table>
<!-- <el-descriptions :column="1" border title="二级数据" v-if="showSplitForm"></el-descriptions>
<el-table v-if="showSplitForm"></el-table> -->
<el-descriptions :column="1" border title="镀锌二级数据" v-if="actionType == 501 && showSplitForm"></el-descriptions>
<el-table v-if="actionType == 501 && showSplitForm" v-loading="zincLoading" :data="zincList" border stripe @row-click="handleZincItemClick">
<el-table-column prop="enterCoilNo" label="入场钢卷号" />
<el-table-column prop="createTime" label="生产开始时间" />
<el-table-column prop="endTime" label="生产结束时间" />
<el-table-column prop="shiftNo" label="班组" />
</el-table>
</div>
</el-col>
@@ -197,6 +202,7 @@
</el-form-item>
<el-form-item>
<el-button type="info" @click="copyFromSourceCoil" icon="el-icon-document-copy">复制源卷信息</el-button>
<el-button :loading="buttonLoading" type="primary" @click="addSplit">提交分条</el-button>
<el-button :loading="buttonLoading" @click="resetSplitForm">重置</el-button>
</el-form-item>
@@ -268,6 +274,7 @@
<script>
import { getMaterialCoil, listMaterialCoil, createSpecialChild, completeSpecialSplit, updateMaterialCoilSimple, checkCoilNo, delMaterialCoil } from '@/api/wms/coil'
import { completeAction, getPendingAction, updatePendingAction } from '@/api/wms/pendingAction'
import { getGalvanize1TypingPrefill } from '@/api/pocket/acidTyping';
import ProductSelect from "@/components/KLPService/ProductSelect";
import RawMaterialSelect from "@/components/KLPService/RawMaterialSelect";
import WarehouseSelect from "@/components/KLPService/WarehouseSelect";
@@ -280,7 +287,7 @@ export default {
name: 'StepSplit',
props: {
actionId: {
type: String,
type: [String, Number],
required: true,
},
coilId: {
@@ -291,6 +298,10 @@ export default {
type: Number,
default: 0,
},
actionType: {
type: Number,
required: true,
},
},
components: {
ProductSelect,
@@ -398,7 +409,10 @@ export default {
defectCode: null,
degree: null,
remark: null
}
},
zincList: [],
zincLoading: false,
}
},
computed: {
@@ -427,6 +441,16 @@ export default {
// 若actionId变化需要重新加载数据可在此补充逻辑
},
},
actionType: {
immediate: true,
handler(val) {
console.log('actionType', val)
if (val == 501) {
// 获取镀锌线二级系统数据
this.getZincList()
}
}
}
},
methods: {
// 查询待分条的钢卷信息
@@ -443,6 +467,16 @@ export default {
}
},
async getZincList() {
this.zincLoading = true
const res = await getGalvanize1TypingPrefill({
pageSize: 10,
pageNum: 1,
})
this.zincList = res.rows || []
this.zincLoading = false
},
async handlePrint(row) {
this.$emit('print', row)
},
@@ -557,6 +591,20 @@ export default {
this.showSplitForm = false
},
handleZincItemClick(row) {
this.splitForm = {
...this.splitForm,
team: row.shiftNo,
// enterCoilNo: row.enterCoilNo,
productionStartTime: row.createTime,
productionEndTime: row.endTime,
itemType: 'product',
materialType: '成品',
length: row.exitLength,
netWeight: row.exitNetWeight,
}
},
// 编辑分条项
async handleEditSplit(row) {
this.showSplitForm = true
@@ -766,6 +814,52 @@ export default {
if (!dict) return code;
const item = dict.find(item => item.value === code);
return item ? item.label : code;
},
// 复制源卷信息到分条表单
copyFromSourceCoil() {
// 复制除了指定字段之外的其他字段
const excludeFields = ['enterCoilNo', 'currentCoilNo', 'coilId', 'createTime', 'createBy'];
// 构建要复制的字段
const copiedFields = {
supplierCoilNo: this.coilInfo.supplierCoilNo,
warehouseId: this.coilInfo.warehouseId,
actualWarehouseId: this.coilInfo.actualWarehouseId,
team: this.coilInfo.team,
materialType: this.coilInfo.materialType,
itemType: this.coilInfo.itemType,
itemId: this.coilInfo.itemId,
qualityStatus: this.coilInfo.qualityStatus,
trimmingRequirement: this.coilInfo.trimmingRequirement,
packingStatus: this.coilInfo.packingStatus,
packagingRequirement: this.coilInfo.packagingRequirement,
grossWeight: parseFloat(this.coilInfo.grossWeight) || null,
netWeight: parseFloat(this.coilInfo.netWeight) || null,
length: parseFloat(this.coilInfo.length) || null,
actualLength: parseFloat(this.coilInfo.actualLength) || null,
actualWidth: parseFloat(this.coilInfo.actualWidth) || null,
temperGrade: this.coilInfo.temperGrade,
coatingType: this.coilInfo.coatingType,
remark: this.coilInfo.remark,
productionStartTime: this.coilInfo.productionStartTime,
productionEndTime: this.coilInfo.productionEndTime,
productionDuration: this.coilInfo.productionDuration,
formattedDuration: this.coilInfo.productionDuration ? this.formatDuration(this.coilInfo.productionDuration * 60 * 1000) : ''
};
// 合并到分条表单
this.splitForm = {
...this.splitForm,
...copiedFields
};
// 同步材料类型和长度显示状态
if (this.splitForm.materialType) {
this.handleMaterialTypeChange(this.splitForm.materialType);
}
this.$message.success('已复制源卷信息,请根据需要修改');
}
},
}

View File

@@ -85,9 +85,9 @@
<el-card class="form-card">
<div slot="header" class="card-header">
<span><i class="el-icon-edit-outline"></i> {{ '更新信息' }}</span>
<!-- <el-button type="text" size="mini" @click="copyFromCurrent" icon="el-icon-document-copy">
复制当前信息
</el-button> -->
<el-button type="text" size="mini" @click="copyFromCurrent" icon="el-icon-document-copy">
复制源卷信息
</el-button>
</div>
<el-form ref="updateForm" :model="updateForm" :rules="rules" label-width="80px" size="small">
@@ -820,23 +820,43 @@ export default {
// 复制当前信息到更新表单
copyFromCurrent() {
const itemType = this.currentInfo.materialType === '原料' ? 'raw_material' : 'product';
this.updateForm = {
currentCoilNo: this.currentInfo.currentCoilNo,
// 复制除了指定字段之外的其他字段
const excludeFields = ['enterCoilNo', 'currentCoilNo', 'coilId', 'createTime', 'createBy'];
// 构建要复制的字段
const copiedFields = {
team: this.currentInfo.team,
materialType: this.currentInfo.materialType,
// 不复制 itemType 和 itemId让它们由 materialType 自动决定
itemType,
itemType: this.currentInfo.itemType,
itemId: this.currentInfo.itemId,
grossWeight: parseFloat(this.currentInfo.grossWeight) || null,
netWeight: parseFloat(this.currentInfo.netWeight) || null,
warehouseId: this.currentInfo.warehouseId,
actualWarehouseId: this.currentInfo.actualWarehouseId,
length: parseFloat(this.currentInfo.length) || null,
remark: this.currentInfo.remark
actualLength: parseFloat(this.currentInfo.actualLength) || null,
actualWidth: parseFloat(this.currentInfo.actualWidth) || null,
temperGrade: this.currentInfo.temperGrade,
coatingType: this.currentInfo.coatingType,
qualityStatus: this.currentInfo.qualityStatus,
packagingRequirement: this.currentInfo.packagingRequirement,
packingStatus: this.currentInfo.packingStatus,
trimmingRequirement: this.currentInfo.trimmingRequirement,
remark: this.currentInfo.remark,
productionStartTime: this.currentInfo.productionStartTime,
productionEndTime: this.currentInfo.productionEndTime,
productionDuration: this.currentInfo.productionDuration,
formattedDuration: this.currentInfo.productionDuration ? this.formatDuration(this.currentInfo.productionDuration * 60 * 1000) : ''
};
// 合并到更新表单
this.updateForm = {
...this.updateForm,
...copiedFields
};
// materialType 会触发 watch自动设置 itemType 并加载物品列表
this.$message.success('已复制当前信息,包含' + this.currentInfo.materialType + '类型的相关信息, 请根据需要修改');
this.$message.success('已复制源卷信息,请根据需要修改');
},
// 保存更新

View File

@@ -1,10 +1,16 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="入职时间" prop="changeTime">
<el-date-picker clearable v-model="queryParams.changeTime" type="date" value-format="yyyy-MM-dd"
placeholder="请选择入职时间">
</el-date-picker>
<el-form-item label="入职时间">
<el-date-picker
v-model="queryParams.changeTimeRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 240px"
/>
</el-form-item>
<!-- <el-form-item label="异动原因" prop="changeReason">
<el-input
@@ -280,6 +286,9 @@ export default {
infoId: undefined,
changeType: 0,
changeTime: undefined,
changeStartTime: undefined,
changeEndTime: undefined,
changeTimeRange: [],
changeReason: undefined,
changeHandler: undefined,
attachment: undefined,
@@ -387,12 +396,23 @@ export default {
},
/** 搜索按钮操作 */
handleQuery() {
// 处理时间范围
if (this.queryParams.changeTimeRange && this.queryParams.changeTimeRange.length === 2) {
this.queryParams.changeStartTime = this.queryParams.changeTimeRange[0];
this.queryParams.changeEndTime = this.queryParams.changeTimeRange[1];
} else {
this.queryParams.changeStartTime = undefined;
this.queryParams.changeEndTime = undefined;
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.changeTimeRange = [];
this.queryParams.changeStartTime = undefined;
this.queryParams.changeEndTime = undefined;
this.handleQuery();
},
// 多选框选中数据

View File

@@ -33,6 +33,17 @@
<el-option label="已转正" value="1" />
<el-option label="未转正" value="0" />
</el-select>
</el-form-item>
<el-form-item label="转正时间">
<el-date-picker
v-model="queryParams.regularTimeRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="在职情况" prop="isLeave">
<el-select v-model="queryParams.isLeave" placeholder="请选择在职情况" clearable @change="handleQuery">
@@ -106,6 +117,7 @@
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
<el-button v-if="scope.row.isLeave === 0 && scope.row.isRegular === 0" size="mini" type="text" icon="el-icon-check"
@click="handleResignation(scope.row)">转正</el-button>
<el-button v-if="scope.row.isRegular === 1" size="mini" type="text" icon="el-icon-view" @click="handleViewRegular(scope.row)">转正记录</el-button>
</template>
</el-table-column>
</el-table>
@@ -232,22 +244,6 @@
</el-col>
</el-row>
</el-card>
<!-- 备注和附件 -->
<el-card class="mb-4" shadow="hover">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-col>
<el-col :span="24" v-if="!form.infoId">
<el-form-item label="附件" prop="attachment">
<file-upload v-model="attachment"></file-upload>
</el-form-item>
</el-col>
</el-row>
</el-card>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
@@ -286,13 +282,34 @@
<el-button @click="cancelResignation"> </el-button>
</div>
</el-dialog>
<!-- 查看转正记录对话框 -->
<el-dialog title="转正记录" :visible.sync="regularRecordOpen" width="500px" append-to-body>
<el-descriptions :column="1" border>
<el-descriptions-item label="转正时间">
{{ regularRecord.changeTime }}
</el-descriptions-item>
<el-descriptions-item label="转正原因">
{{ regularRecord.changeReason }}
</el-descriptions-item>
<el-descriptions-item label="负责人">
{{ regularRecord.changeHandler }}
</el-descriptions-item>
<el-descriptions-item label="附件">
<file-upload v-model="regularRecord.attachment"></file-upload>
</el-descriptions-item>
<el-descriptions-item label="备注">
{{ regularRecord.remark }}
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script>
import { listEmployeeInfo, getEmployeeInfo, delEmployeeInfo, updateEmployeeInfo } from "@/api/wms/employeeInfo";
import { listDept } from "@/api/wms/dept";
import { employeeEntry, employeeLeave, employeeRegular } from '@/api/wms/employeeChange'
import { employeeEntry, employeeRegular, listEmployeeChange } from '@/api/wms/employeeChange'
export default {
name: "EmployeeInfo",
@@ -329,6 +346,11 @@ export default {
age: undefined,
gender: undefined,
education: undefined,
isRegular: undefined,
isLeave: undefined,
regularStartTime: undefined,
regularEndTime: undefined,
regularTimeRange: [],
},
// 表单参数
form: {},
@@ -360,7 +382,17 @@ export default {
}
],
},
resignationAttachment: undefined
resignationAttachment: undefined,
// 转正记录相关
regularRecordOpen: false,
regularRecord: {
name: undefined,
changeTime: undefined,
changeReason: undefined,
changeHandler: undefined,
attachment: undefined,
remark: undefined
}
};
},
dicts: ['hrm_employee_education'],
@@ -383,6 +415,17 @@ export default {
this.deptList = response.data;
});
},
/** 查询转正记录 */
getRegularRecords() {
listEmployeeChange({
infoId: this.form.infoId,
changeType: "2"
}).then(response => {
this.regularRecord = response.rows[0];
});
},
// 取消按钮
cancel() {
this.open = false;
@@ -417,12 +460,23 @@ export default {
},
/** 搜索按钮操作 */
handleQuery() {
// 处理时间范围
if (this.queryParams.regularTimeRange && this.queryParams.regularTimeRange.length === 2) {
this.queryParams.regularStartTime = this.queryParams.regularTimeRange[0];
this.queryParams.regularEndTime = this.queryParams.regularTimeRange[1];
} else {
this.queryParams.regularStartTime = undefined;
this.queryParams.regularEndTime = undefined;
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.regularTimeRange = [];
this.queryParams.regularStartTime = undefined;
this.queryParams.regularEndTime = undefined;
this.handleQuery();
},
// 多选框选中数据
@@ -462,20 +516,6 @@ export default {
}).finally(() => {
this.buttonLoading = false;
});
} else {
employeeEntry({
changeType: 0,
changeTime: this.form.entryTime,
changeHandler: this.$store.getters.nickName,
attachment: this.attachment,
...this.form
}).then(response => {
this.$modal.msgSuccess("员工入职成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
@@ -581,7 +621,7 @@ export default {
type = 'info';
} else if (diffDays >= 90) {
// 3个月以上未转正
type = 'warning';
type = 'danger';
}
} else {
// 已转正
@@ -589,6 +629,20 @@ export default {
}
return { days: diffDays, type, status };
},
/** 查看转正记录 */
handleViewRegular(row) {
this.form = row;
this.getRegularRecords();
this.regularRecordOpen = true;
},
/** 下载附件 */
downloadAttachment(attachment) {
if (attachment) {
window.open(attachment);
}
}
}
};

View File

@@ -33,6 +33,17 @@
<el-option label="已转正" value="1" />
<el-option label="未转正" value="0" />
</el-select>
</el-form-item>
<el-form-item label="入职时间">
<el-date-picker
v-model="queryParams.entryTimeRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="在职情况" prop="isLeave">
<el-select v-model="queryParams.isLeave" placeholder="请选择在职情况" clearable @change="handleQuery">
@@ -328,6 +339,11 @@ export default {
age: undefined,
gender: undefined,
education: undefined,
isRegular: undefined,
isLeave: undefined,
entryStartTime: undefined,
entryEndTime: undefined,
entryTimeRange: [],
},
// 表单参数
form: {},
@@ -423,12 +439,23 @@ export default {
},
/** 搜索按钮操作 */
handleQuery() {
// 处理时间范围
if (this.queryParams.entryTimeRange && this.queryParams.entryTimeRange.length === 2) {
this.queryParams.entryStartTime = this.queryParams.entryTimeRange[0];
this.queryParams.entryEndTime = this.queryParams.entryTimeRange[1];
} else {
this.queryParams.entryStartTime = undefined;
this.queryParams.entryEndTime = undefined;
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.entryTimeRange = [];
this.queryParams.entryStartTime = undefined;
this.queryParams.entryEndTime = undefined;
this.handleQuery();
},
// 多选框选中数据
@@ -587,7 +614,7 @@ export default {
type = 'info';
} else if (diffDays >= 90) {
// 3个月以上未转正
type = 'warning';
type = 'danger';
}
} else {
// 已转正

View File

@@ -1,10 +1,16 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="离职时间" prop="changeTime">
<el-date-picker clearable v-model="queryParams.changeTime" type="date" value-format="yyyy-MM-dd"
placeholder="请选择离职时间">
</el-date-picker>
<el-form-item label="离职时间">
<el-date-picker
v-model="queryParams.changeTimeRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 240px"
/>
</el-form-item>
<!-- <el-form-item label="异动原因" prop="changeReason">
<el-input
@@ -138,6 +144,9 @@ export default {
infoId: undefined,
changeType: 1,
changeTime: undefined,
changeStartTime: undefined,
changeEndTime: undefined,
changeTimeRange: [],
changeReason: undefined,
changeHandler: undefined,
attachment: undefined,
@@ -245,12 +254,23 @@ export default {
},
/** 搜索按钮操作 */
handleQuery() {
// 处理时间范围
if (this.queryParams.changeTimeRange && this.queryParams.changeTimeRange.length === 2) {
this.queryParams.changeStartTime = this.queryParams.changeTimeRange[0];
this.queryParams.changeEndTime = this.queryParams.changeTimeRange[1];
} else {
this.queryParams.changeStartTime = undefined;
this.queryParams.changeEndTime = undefined;
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.changeTimeRange = [];
this.queryParams.changeStartTime = undefined;
this.queryParams.changeEndTime = undefined;
this.handleQuery();
},
// 多选框选中数据

View File

@@ -10,6 +10,17 @@
<el-form-item label="新部门" prop="newDept">
<el-input v-model="queryParams.newDept" placeholder="请输入新部门" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="转岗时间">
<el-date-picker
v-model="queryParams.transferTimeRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="转岗经办人" prop="transferHandler">
<el-input v-model="queryParams.transferHandler" placeholder="请输入转岗经办人" clearable
@keyup.enter.native="handleQuery" />
@@ -146,6 +157,9 @@ export default {
newDept: undefined,
newJobType: undefined,
transferHandler: undefined,
transferStartTime: undefined,
transferEndTime: undefined,
transferTimeRange: [],
},
// 部门列表
deptList: [],
@@ -233,12 +247,23 @@ export default {
},
/** 搜索按钮操作 */
handleQuery() {
// 处理时间范围
if (this.queryParams.transferTimeRange && this.queryParams.transferTimeRange.length === 2) {
this.queryParams.transferStartTime = this.queryParams.transferTimeRange[0];
this.queryParams.transferEndTime = this.queryParams.transferTimeRange[1];
} else {
this.queryParams.transferStartTime = undefined;
this.queryParams.transferEndTime = undefined;
}
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.transferTimeRange = [];
this.queryParams.transferStartTime = undefined;
this.queryParams.transferEndTime = undefined;
this.handleQuery();
},
// 多选框选中数据

View File

@@ -0,0 +1,730 @@
<template>
<div class="employee-report" v-loading="loading">
<!-- 第一行时间段筛选 -->
<div class="filter-section">
<el-form :inline="true" class="demo-form-inline">
<el-form-item label="调动时间">
<el-date-picker
v-model="dateRange"
type="datetimerange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd hh:mm:ss"
value-format="yyyy-MM-dd hh:mm:ss"
/>
</el-form-item>
<el-form-item label="入职时间">
<el-date-picker
v-model="employeeDateRange"
type="datetimerange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd hh:mm:ss"
value-format="yyyy-MM-dd hh:mm:ss"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 第二行员工情况统计信息和图表 -->
<div class="stats-section">
<div class="stats-cards">
<el-card shadow="hover" class="stat-card">
<div class="stat-item">
<div class="stat-value">{{ stats.totalEmployees }}</div>
<div class="stat-label">总人数</div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="stat-item">
<div class="stat-value">{{ stats.entryCount }}</div>
<div class="stat-label">入职人数</div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="stat-item">
<div class="stat-value">{{ stats.leaveCount }}</div>
<div class="stat-label">离职人数</div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="stat-item">
<div class="stat-value">{{ stats.regularCount }}</div>
<div class="stat-label">转正人数</div>
</div>
</el-card>
<el-card shadow="hover" class="stat-card">
<div class="stat-item">
<div class="stat-value">{{ stats.transferCount }}</div>
<div class="stat-label">调岗次数</div>
</div>
</el-card>
</div>
</div>
<!-- 第三行互动筛选图表区 -->
<div class="interactive-charts-section">
<el-card shadow="hover" class="chart-card">
<!-- <div slot="header" class="clearfix">
<span>学历分布</span>
</div> -->
<div id="educationChart" style="width: 100%; height: 300px;"></div>
</el-card>
<el-card shadow="hover" class="chart-card">
<!-- <div slot="header" class="clearfix">
<span>年龄段分布</span>
</div> -->
<div id="ageChart" style="width: 100%; height: 300px;"></div>
</el-card>
<el-card shadow="hover" class="chart-card">
<!-- <div slot="header" class="clearfix">
<span>性别分布</span>
</div> -->
<div id="genderChart" style="width: 100%; height: 300px;"></div>
</el-card>
</div>
<!-- 第四行多轴折线图 -->
<div class="trend-section">
<!-- <el-card shadow="hover"> -->
<!-- <div slot="header" class="clearfix">
<span>员工变动趋势</span>
</div> -->
<div id="trendChart" style="width: 100%; height: 400px;"></div>
<!-- </el-card> -->
</div>
<!-- 第五行明细数据表 -->
<div class="details-section">
<el-tabs v-model="activeTab">
<el-tab-pane label="当前员工" name="current">
<el-table :data="currentEmployees" style="width: 100%" height="400">
<el-table-column prop="name" label="员工姓名" />
<el-table-column prop="dept" label="部门" />
<el-table-column prop="jobType" label="职位" />
<el-table-column prop="entryTime" label="入职日期" />
<!-- <el-table-column prop="status" label="状态" /> -->
<el-table-column prop="education" label="学历" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="gender" label="性别" />
</el-table>
</el-tab-pane>
<el-tab-pane label="离职记录" name="leave">
<el-table :data="leaveRecords" style="width: 100%" height="400">
<el-table-column prop="wmsEmployeeInfo.name" label="员工姓名" />
<el-table-column prop="wmsEmployeeInfo.dept" label="部门" />
<el-table-column prop="wmsEmployeeInfo.jobType" label="职位" />
<el-table-column prop="changeTime" label="离职日期" />
<el-table-column prop="changeReason" label="离职原因" />
</el-table>
</el-tab-pane>
<el-tab-pane label="入职记录" name="entry">
<el-table :data="entryRecords" style="width: 100%" height="400">
<el-table-column prop="wmsEmployeeInfo.name" label="员工姓名" />
<el-table-column prop="wmsEmployeeInfo.dept" label="部门" />
<el-table-column prop="wmsEmployeeInfo.jobType" label="职位" />
<el-table-column prop="entryDate" label="入职日期" />
<el-table-column prop="wmsEmployeeInfo.education" label="学历" />
<el-table-column prop="wmsEmployeeInfo.gender" label="性别" />
</el-table>
</el-tab-pane>
<el-tab-pane label="转正记录" name="regular">
<el-table :data="regularRecords" style="width: 100%" height="400">
<el-table-column prop="wmsEmployeeInfo.name" label="员工姓名" />
<el-table-column prop="wmsEmployeeInfo.dept" label="部门" />
<el-table-column prop="wmsEmployeeInfo.jobType" label="职位" />
<el-table-column prop="regularDate" label="转正日期" />
</el-table>
</el-tab-pane>
<el-tab-pane label="调岗记录" name="transfer">
<el-table :data="transferRecords" style="width: 100%" height="400">
<el-table-column prop="wmsEmployeeInfo.name" label="员工姓名" />
<el-table-column prop="transferTime" label="调岗日期" />
<el-table-column prop="oldDept" label="原部门" />
<el-table-column prop="oldJobType" label="原职位" />
<el-table-column prop="newDept" label="新部门" />
<el-table-column prop="newJobType" label="新职位" />
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { listEmployeeInfo } from '@/api/wms/employeeInfo'
import { listEmployeeChange } from '@/api/wms/employeeChange'
import { listEmployeeTransfer } from '@/api/wms/employeeTransfer'
export default {
name: 'EmployeeReport',
data() {
return {
dateRange: this.getMonthRange(), // 默认选中本月
employeeDateRange: ['', ''],
shortcuts: [
{
text: '本月',
value: () => {
return this.getMonthRange()
}
},
{
text: '上个月',
value: () => {
const date = new Date()
const firstDay = new Date(date.getFullYear(), date.getMonth() - 1, 1, 0, 0, 0)
const lastDay = new Date(date.getFullYear(), date.getMonth(), 0, 23, 59, 59)
// 格式化日期为yyyy-MM-dd HH:mm:ss格式
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
return [formatDate(firstDay), formatDate(lastDay)]
}
},
{
text: '近三个月',
value: () => {
const date = new Date()
const lastDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
const firstDay = new Date(date.getFullYear(), date.getMonth() - 2, 1, 0, 0, 0)
// 格式化日期为yyyy-MM-dd HH:mm:ss格式
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
return [formatDate(firstDay), formatDate(lastDay)]
}
},
{
text: '本年',
value: () => {
const date = new Date()
const firstDay = new Date(date.getFullYear(), 0, 1, 0, 0, 0)
const lastDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
// 格式化日期为yyyy-MM-dd HH:mm:ss格式
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
return [formatDate(firstDay), formatDate(lastDay)]
}
}
],
stats: {
totalEmployees: 0,
regularEmployees: 0,
probationEmployees: 0,
entryCount: 0,
leaveCount: 0,
regularCount: 0,
transferCount: 0
},
currentEmployees: [],
leaveRecords: [],
entryRecords: [],
regularRecords: [],
transferRecords: [],
activeTab: 'current',
charts: {},
loading: false,
}
},
mounted() {
// 默认设置为当前月
this.dateRange = this.getMonthRange()
this.initCharts()
this.handleQuery()
},
methods: {
getMonthRange() {
const date = new Date()
// 当月第一天00:00:00
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0)
// 当月最后一天23:59:59
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59)
// 格式化日期为yyyy-MM-dd HH:mm:ss格式
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
return [formatDate(firstDay), formatDate(lastDay)]
},
initCharts() {
this.charts.educationChart = echarts.init(document.getElementById('educationChart'))
this.charts.ageChart = echarts.init(document.getElementById('ageChart'))
this.charts.genderChart = echarts.init(document.getElementById('genderChart'))
this.charts.trendChart = echarts.init(document.getElementById('trendChart'))
// 监听窗口大小变化,自适应图表
window.addEventListener('resize', () => {
Object.values(this.charts).forEach(chart => chart.resize())
})
},
handleQuery() {
const queryParams = {
startTime: this.dateRange ? this.dateRange[0] : '',
endTime: this.dateRange ? this.dateRange[1] : '',
entryStartTime: this.employeeDateRange ? this.employeeDateRange[0] : '',
entryEndTime: this.employeeDateRange ? this.employeeDateRange[1] : '',
}
this.loadData(queryParams)
},
async loadData(queryParams) {
this.loading = true
try {
await Promise.all([
this.loadEmployeeData(queryParams),
this.loadChangeData(queryParams),
this.loadTransferData(queryParams)
])
} finally {
this.loading = false
}
},
resetQuery() {
this.dateRange = this.getMonthRange()
this.handleQuery()
},
loadEmployeeData(queryParams) {
console.log(queryParams)
return new Promise((resolve) => {
listEmployeeInfo(queryParams).then(response => {
const data = response.rows
this.currentEmployees = data
// 统计当前员工信息
this.stats.totalEmployees = data.length
this.stats.regularEmployees = data.filter(item => item.status === '已转正').length
this.stats.probationEmployees = data.filter(item => item.status === '试用期').length
// 生成学历分布图表
this.updateEducationChart()
// 生成年龄段分布图表
this.updateAgeChart()
// 生成性别分布图表
this.updateGenderChart()
resolve()
})
})
},
loadChangeData(queryParams) {
return new Promise((resolve) => {
listEmployeeChange({
changeStartTime: queryParams.startTime,
changeEndTime: queryParams.endTime
}).then(response => {
const data = response.rows
// 分类处理异动记录
this.entryRecords = data.filter(item => item.changeType == 0)
this.leaveRecords = data.filter(item => item.changeType == 1)
this.regularRecords = data.filter(item => item.changeType == 2)
// 统计异动数据
this.stats.entryCount = this.entryRecords.length
this.stats.leaveCount = this.leaveRecords.length
this.stats.regularCount = this.regularRecords.length
// 更新趋势图表
this.updateTrendChart()
resolve()
})
})
},
loadTransferData(queryParams) {
return new Promise((resolve) => {
listEmployeeTransfer({
transferStartTime: queryParams.startTime,
transferEndTime: queryParams.endTime
}).then(response => {
const data = response.rows
console.log(data, '调岗记录')
this.transferRecords = data
this.stats.transferCount = data.length
// 更新趋势图表
this.updateTrendChart()
resolve()
})
})
},
updateEducationChart() {
// 统计学历分布
const educationData = {}
this.currentEmployees.forEach(emp => {
const edu = emp.education || '未知'
educationData[edu] = (educationData[edu] || 0) + 1
})
const option = {
title: {
text: '学历分布',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '学历',
type: 'pie',
radius: '50%',
data: Object.entries(educationData).map(([name, value]) => ({ name, value })),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
this.charts.educationChart.setOption(option)
},
updateAgeChart() {
// 统计年龄段分布
const ageGroups = {
'20岁以下': 0,
'20-30岁': 0,
'30-40岁': 0,
'40-50岁': 0,
'50岁以上': 0
}
this.currentEmployees.forEach(emp => {
const age = parseInt(emp.age) || 0
if (age < 20) ageGroups['20岁以下']++
else if (age < 30) ageGroups['20-30岁']++
else if (age < 40) ageGroups['30-40岁']++
else if (age < 50) ageGroups['40-50岁']++
else ageGroups['50岁以上']++
})
const option = {
title: {
text: '年龄段分布',
left: 'center'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: Object.keys(ageGroups)
},
yAxis: {
type: 'value'
},
series: [
{
name: '人数',
type: 'bar',
data: Object.values(ageGroups),
itemStyle: {
color: '#409EFF'
}
}
]
}
this.charts.ageChart.setOption(option)
},
updateGenderChart() {
// 统计性别分布
const genderData = {
'男': 0,
'女': 0
}
this.currentEmployees.forEach(emp => {
if (emp.gender === '男' || emp.gender === '女') {
genderData[emp.gender]++
}
})
const option = {
title: {
text: '性别分布',
left: 'center'
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left'
},
series: [
{
name: '性别',
type: 'pie',
radius: '50%',
data: Object.entries(genderData).map(([name, value]) => ({ name, value })),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
this.charts.genderChart.setOption(option)
},
updateTrendChart() {
// 生成时间序列数据
const timeRange = this.generateTimeRange()
const trendData = {
entry: new Array(timeRange.length).fill(0),
leave: new Array(timeRange.length).fill(0),
regular: new Array(timeRange.length).fill(0),
transfer: new Array(timeRange.length).fill(0)
}
// 提取日期部分的辅助函数
const getDatePart = (datetime) => {
if (!datetime) return ''
return datetime.split(' ')[0]
}
// 填充入职数据
this.entryRecords.forEach(record => {
const datePart = getDatePart(record.changeTime)
const index = timeRange.indexOf(datePart)
if (index !== -1) trendData.entry[index]++
})
// 填充离职数据
this.leaveRecords.forEach(record => {
const datePart = getDatePart(record.changeTime)
const index = timeRange.indexOf(datePart)
if (index !== -1) trendData.leave[index]++
})
// 填充转正数据
this.regularRecords.forEach(record => {
const datePart = getDatePart(record.changeTime)
const index = timeRange.indexOf(datePart)
if (index !== -1) trendData.regular[index]++
})
// 填充调岗数据
this.transferRecords.forEach(record => {
const datePart = getDatePart(record.transferTime)
const index = timeRange.indexOf(datePart)
if (index !== -1) trendData.transfer[index]++
})
const option = {
title: {
text: '员工变动趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['入职', '离职', '转正', '调岗'],
bottom: 0
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: timeRange
},
yAxis: {
type: 'value'
},
series: [
{
name: '入职',
type: 'line',
stack: 'Total',
data: trendData.entry,
itemStyle: {
color: '#67C23A'
}
},
{
name: '离职',
type: 'line',
stack: 'Total',
data: trendData.leave,
itemStyle: {
color: '#F56C6C'
}
},
{
name: '转正',
type: 'line',
stack: 'Total',
data: trendData.regular,
itemStyle: {
color: '#409EFF'
}
},
{
name: '调岗',
type: 'line',
stack: 'Total',
data: trendData.transfer,
itemStyle: {
color: '#E6A23C'
}
}
]
}
this.charts.trendChart.setOption(option)
},
generateTimeRange() {
// 生成时间范围内的日期数组
const start = this.dateRange && this.dateRange[0] ? new Date(this.dateRange[0]) : new Date()
const end = this.dateRange && this.dateRange[1] ? new Date(this.dateRange[1]) : new Date()
const timeRange = []
// 如果没有设置时间范围默认显示最近30天
if (!this.dateRange || !this.dateRange[0] || !this.dateRange[1]) {
const thirtyDaysAgo = new Date()
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
start.setTime(thirtyDaysAgo.getTime())
}
const current = new Date(start)
while (current <= end) {
const dateStr = current.toISOString().split('T')[0]
timeRange.push(dateStr)
current.setDate(current.getDate() + 1)
}
return timeRange
}
}
}
</script>
<style scoped>
.employee-report {
padding: 20px;
background-color: #f5f7fa;
}
.filter-section {
margin-bottom: 20px;
background-color: #fff;
padding: 20px;
border-radius: 4px;
}
.stats-section {
margin-bottom: 20px;
}
.stats-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
text-align: center;
}
.stat-item {
padding: 10px;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #409EFF;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #606266;
}
.stats-chart {
background-color: #fff;
border-radius: 4px;
padding: 20px;
}
.interactive-charts-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.chart-card {
background-color: #fff;
border-radius: 4px;
padding: 20px;
}
.trend-section {
margin-bottom: 20px;
background-color: #fff;
border-radius: 4px;
padding: 20px;
}
.details-section {
background-color: #fff;
border-radius: 4px;
padding: 20px;
}
</style>

View File

@@ -1,8 +1,34 @@
<template>
<div class="coil-table">
<!-- 其他props -->
<div class="table-controls">
<div class="filter-section">
<el-input v-model="filterKeyword" placeholder="输入关键词筛选" clearable @input="handleFilterChange"
style="width: 200px; margin-right: 10px" />
<el-select v-model="filterColumn" placeholder="选择筛选字段" @change="handleFilterChange"
style="width: 200px; margin-right: 10px"
multiple
collapse-tags>
<el-option v-for="column in columns" :key="column.prop" :label="column.title" :value="column.prop" />
</el-select>
</div>
<div class="sort-section">
<el-select v-model="sortField" placeholder="选择排序字段" @change="handleSortChange"
style="width: 150px; margin-right: 10px">
<el-option v-for="column in columns" :key="column.prop" :label="column.title" :value="column.prop" />
</el-select>
<el-select v-model="sortDirection" placeholder="选择排序方向" @change="handleSortChange" style="width: 100px">
<el-option label="升序" value="asc" />
<el-option label="降序" value="desc" />
</el-select>
</div>
<el-pagination layout="total, sizes, prev, pager, next, jumper" :total="total" :page-size.sync="pageSize"
:page-sizes="[10, 20, 50, 100, 200, 500, 1000]" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
<el-table :data="tableData" style="width: 100%" height="calc(100vh - 320px)" border>
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.title" :width="column.width" :align="column.align">
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.title"
:width="column.width" :align="column.align">
<template slot-scope="scope">
<!-- 特殊 prop 渲染逻辑 -->
<template v-if="column.prop === 'enterCoilNo'">
@@ -18,6 +44,10 @@
<template v-else-if="column.prop === 'status'">
{{ scope.row.status === 0 ? '在库' : '已出库' }}
</template>
<!-- 生产耗时单位分钟渲染为xx天xx小时xx分钟 -->
<template v-else-if="column.prop === 'productionDuration'">
{{ formatProductionDuration(scope.row.productionDuration) }}
</template>
<!-- 默认渲染 -->
<template v-else>
{{ scope.row[column.prop] }}
@@ -25,14 +55,6 @@
</template>
</el-table-column>
</el-table>
<el-pagination
v-if="showPagination"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:page-size.sync="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
@@ -62,20 +84,80 @@ export default {
return {
pageNum: 1,
pageSize: 1000,
// 排序相关
sortField: '',
sortDirection: 'asc',
// 筛选相关
filterKeyword: '',
filterColumn: [],
}
},
mounted() {
// 默认选中所有列
this.filterColumn = this.columns.map(column => column.prop)
},
computed: {
// 处理排序和筛选后的数据
processedData() {
let result = [...this.data]
// 筛选逻辑
if (this.filterColumn.length > 0 && this.filterKeyword) {
const keyword = this.filterKeyword.toLowerCase()
result = result.filter(item => {
// 只要有一个字段匹配,就保留该记录
return this.filterColumn.some(column => {
const value = item[column]
if (value === null || value === undefined) return false
return String(value).toLowerCase().includes(keyword)
})
})
}
// 排序逻辑
if (this.sortField) {
result.sort((a, b) => {
const aValue = a[this.sortField]
const bValue = b[this.sortField]
// 处理null和undefined
if (aValue === null || aValue === undefined) return 1
if (bValue === null || bValue === undefined) return -1
// 字符串比较
if (typeof aValue === 'string' && typeof bValue === 'string') {
return this.sortDirection === 'asc'
? aValue.localeCompare(bValue)
: bValue.localeCompare(aValue)
}
// 数字比较
if (typeof aValue === 'number' && typeof bValue === 'number') {
return this.sortDirection === 'asc'
? aValue - bValue
: bValue - aValue
}
// 其他类型比较
return this.sortDirection === 'asc'
? String(aValue).localeCompare(String(bValue))
: String(bValue).localeCompare(String(aValue))
})
}
return result
},
// 内部实现前端分页逻辑
tableData() {
return this.data.slice((this.pageNum - 1) * this.pageSize, this.pageNum * this.pageSize)
return this.processedData.slice((this.pageNum - 1) * this.pageSize, this.pageNum * this.pageSize)
},
// 计算总页数
totalPage() {
return Math.ceil(this.data.length / this.pageSize)
return Math.ceil(this.processedData.length / this.pageSize)
},
// 计算总条数
total() {
return this.data.length
return this.processedData.length
},
// 是否展示分页组件
showPagination() {
@@ -92,6 +174,72 @@ export default {
handleCurrentChange(val) {
this.pageNum = val
},
// 生产耗时单位分钟渲染为xx天xx小时xx分钟
formatProductionDuration(duration) {
if (!duration || isNaN(duration)) {
return '0分钟'
}
const days = Math.floor(duration / 1440)
const hours = Math.floor((duration - days * 1440) / 60)
const minutes = duration - days * 1440 - hours * 60
let result = ''
if (days > 0) {
result += `${days}`
}
if (hours > 0) {
result += `${hours}小时`
}
if (minutes > 0 || (days === 0 && hours === 0)) {
result += `${minutes}分钟`
}
return result
},
// 处理筛选条件变化
handleFilterChange() {
this.pageNum = 1
},
// 处理排序规则变化
handleSortChange() {
this.pageNum = 1
}
}
}
</script>
</script>
<style scoped>
.coil-table {
padding: 10px;
}
.table-controls {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 10px;
}
.filter-section,
.sort-section {
display: flex;
align-items: center;
gap: 10px;
}
.el-pagination {
margin-top: 0px !important;
}
@media screen and (max-width: 768px) {
.table-controls {
flex-direction: column;
align-items: flex-end;
}
.filter-section,
.sort-section {
width: 100%;
}
}
</style>

View File

@@ -136,8 +136,15 @@ export default {
optionalProps: [
{ label: '入场钢卷号', value: 'enterCoilNo' },
{ label: '当前钢卷号', value: 'currentCoilNo' },
{ label: '厂家钢卷号', value: 'supplierCoilNo' },
{ label: '逻辑库区', value: 'warehouseName' },
{ label: '实际库区', value: 'actualWarehouseName' },
{ label: '质量状态', value: 'qualityStatus' },
{ label: '切边要求', value: 'trimmingRequirement' },
{ label: '打包状态', value: 'packingStatus' },
{ label: '包装要求', value: 'packagingRequirement' },
{ label: '产品类型', value: 'itemId' },
{ label: '品名', value: 'itemName' },
{ label: '宽度', value: 'computedWidth' },

View File

@@ -59,11 +59,11 @@ const calcAbSummary = (list) => {
returnWeight: 0,
// 计入技术部的钢卷占比
jishuRate: 0,
jishuRate: 0,
// 计入小钢卷库的钢卷占比
miniRate: 0,
miniRate: 0,
// 计入废品库的钢卷占比
rubbishRate: 0,
rubbishRate: 0,
// 计入退货库的钢卷占比
returnRate: 0,
}
@@ -76,22 +76,28 @@ const calcAbSummary = (list) => {
// 技术部
const coil = list[i];
// 技术部
if (coil.warehouseId == '2019583656787259393') {
if (coil.warehouseId == '2019583656787259393' || coil.qualityStatus == 'O') {
o['jishuCount'] = o['jishuCount'] + 1
o['jishuWeight'] = o['jishuWeight'] + parseFloat(coil.netWeight) || 0
}
// 小刚卷库
if (coil.warehouseId == '2019583325311414274') {
else if (coil.warehouseId == '2019583325311414274') {
o['miniCount'] = o['miniCount'] + 1
o['miniWeight'] = o['miniWeight'] + parseFloat(coil.netWeight) || 0
}
// 废品库
if (coil.warehouseId == '2019583429955104769') {
// 废品库, 或者之状态为D-,DD+,C-CC+其中之一
else if (coil.warehouseId == '2019583429955104769'
|| coil.qualityStatus == 'D-' ||
coil.qualityStatus == 'D' ||
coil.qualityStatus == 'D+' ||
coil.qualityStatus == 'C-' ||
coil.qualityStatus == 'C' ||
coil.qualityStatus == 'C+') {
o['rubbishCount'] = o['rubbishCount'] + 1
o['rubbishWeight'] = o['rubbishWeight'] + parseFloat(coil.netWeight) || 0
}
// 退货库
if (coil.warehouseId == '2019583137616310273') {
else if (coil.warehouseId == '2019583137616310273') {
o['returnCount'] = o['returnCount'] + 1
o['returnWeight'] = o['returnWeight'] + parseFloat(coil.netWeight) || 0
}
@@ -133,35 +139,35 @@ const calcTeamSummary = (list) => {
const calcMSummary = (list, lossList) => {
// 统计需要二外处理M卷也就是钢卷的currentCoilNo中带有M的钢卷在统计产出钢卷的数量和重量时需要忽略并记录并且在统计消耗钢卷的总重量时也需要移除
// 筛选出 M 卷
const mCoils = list.filter(item => item.currentCoilNo && item.currentCoilNo.includes('M'))
// 非 M 卷
const nonMCoils = list.filter(item => !item.currentCoilNo || !item.currentCoilNo.includes('M'))
// 非 M 卷作为产出统计
const outCount = nonMCoils.length
const outTotalWeight = nonMCoils.reduce((acc, cur) => acc + (parseFloat(cur.netWeight) || 0), 0)
const outAvgWeight = outCount > 0 ? (outTotalWeight / outCount)?.toFixed(2) : 0
// 计算产出的 M 卷总重量
const mOutTotalWeight = mCoils.reduce((acc, cur) => acc + (parseFloat(cur.netWeight) || 0), 0)
// 消耗钢卷统计(数量包括所有卷,但总重量减去产出的 M 卷重量)
const lossCount = lossList.length
const lossTotalWeight = lossList.reduce((acc, cur) => acc + (parseFloat(cur.netWeight) || 0), 0) - mOutTotalWeight
const lossAvgWeight = lossCount > 0 ? (lossTotalWeight / lossCount)?.toFixed(2) : 0
// 合计
const totalCount = outCount + lossCount
const totalWeight = parseFloat((outTotalWeight + lossTotalWeight).toFixed(2))
const totalAvgWeight = totalCount > 0 ? (totalWeight / totalCount)?.toFixed(2) : 0
// 成品比率
const passRate = outCount > 0 && lossTotalWeight > 0 ? (outTotalWeight / lossTotalWeight) : 0
// 损失比率
const lossRate = totalCount > 0 ? (1 - passRate) : 0
// 异常率,成品在warehouseId在'2019583656787259393',
// '2019583325311414274',
// '2019583429955104769',
@@ -172,7 +178,7 @@ const calcMSummary = (list, lossList) => {
// 正品率1-异常率)
const passRate2 = totalCount != 0 ? (1 - abRate) : 0
return {
outCount,
outTotalWeight: outTotalWeight.toFixed(2),

View File

@@ -26,6 +26,16 @@ const defaultColumns = {
prop: "productionEndTime",
align: "center",
},
{
title: "生产耗时",
prop: "productionDuration",
align: "center",
},
{
title: '质量状态',
prop: 'qualityStatus',
align: 'center'
},
{
title: "逻辑库区",
prop: "warehouseName",
@@ -92,6 +102,11 @@ const defaultColumns = {
prop: "warehouseName",
align: "center",
},
{
title: '质量状态',
prop: 'qualityStatus',
align: 'center'
},
{
title: "产品类型",
prop: "itemId",
@@ -148,6 +163,11 @@ const defaultColumns = {
prop: "warehouseName",
align: "center",
},
{
title: '质量状态',
prop: 'qualityStatus',
align: 'center'
},
{
title: "产品类型",
prop: "itemId",
@@ -209,6 +229,11 @@ const defaultColumns = {
prop: "warehouseName",
align: "center",
},
{
title: '质量状态',
prop: 'qualityStatus',
align: 'center'
},
{
title: "产品类型",
prop: "itemId",

View File

@@ -3,11 +3,11 @@
<el-row>
<el-form label-width="80px" inline>
<el-form-item label="开始时间" prop="startDate">
<el-date-picker style="width: 200px;" v-model="queryParams.startDate" type="date" value-format="yyyy-MM-dd"
<el-date-picker style="width: 200px;" v-model="queryParams.byCreateTimeStart" type="date" value-format="yyyy-MM-dd"
placeholder="选择开始日期" @change="handleDateChange"></el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endDate">
<el-date-picker style="width: 200px;" v-model="queryParams.endDate" type="date" value-format="yyyy-MM-dd"
<el-date-picker style="width: 200px;" v-model="queryParams.byCreateTimeEnd" type="date" value-format="yyyy-MM-dd"
placeholder="选择结束日期" @change="handleDateChange"></el-date-picker>
</el-form-item>
<el-form-item label="入场钢卷号" prop="enterCoilNo">
@@ -222,7 +222,7 @@ export default {
lossList: [],
queryParams: {
pageNum: 1,
pageSize: 9999,
pageSize: 99999,
date: currentDate, // 绑定日期选择器的默认值(当天)
byCreateTimeStart: start, // 默认当天0点
byCreateTimeEnd: end, // 默认当天23:59:59
@@ -250,6 +250,8 @@ export default {
lossColumns: [],
outputColumns: [],
actionIds: '',
}
},
watch: {
@@ -335,7 +337,7 @@ export default {
actionStatus: 2,
warehouseId: this.queryParams.planId,
actionType,
pageSize: 9999,
pageSize: 99999,
pageNum: 1,
startTime: this.queryParams.byCreateTimeStart,
endTime: this.queryParams.byCreateTimeEnd,
@@ -343,6 +345,7 @@ export default {
})
}))
const actions = resultList.flatMap(item => item.rows)
this.actionIds = actions.map(item => item.actionId).join(',')
const coilIds = actions.map(item => item.coilId).join(',')
console.log(coilIds)
if (!coilIds) {

View File

@@ -197,7 +197,7 @@ export default {
lossList: [],
queryParams: {
pageNum: 1,
pageSize: 9999,
pageSize: 99999,
date: currentDate, // 绑定日期选择器的默认值(当天)
byCreateTimeStart: start, // 默认当天0点
byCreateTimeEnd: end, // 默认当天23:59:59
@@ -301,9 +301,7 @@ export default {
computedWidth: parseFloat(width),
}
})
// this.loading = false
this.getLossList()
// this.loading = false
})
},
async getLossList() {
@@ -313,7 +311,7 @@ export default {
actionStatus: 2,
warehouseId: this.queryParams.planId,
actionType,
pageSize: 9999,
pageSize: 99999,
pageNum: 1,
startTime: this.queryParams.byCreateTimeStart,
endTime: this.queryParams.byCreateTimeEnd,