新增生产-前缫看台记录,并导入车头工/挡车工看台记录历史数据
功能: - 后端新增 KtjlCtgs / KtjlDcgs 两张表、KtjlService 与 api/prod/ktjl 接口 (按日期+车间+班次查询、整组先删后建保存、整组删除、按看台排班带出默认记录、工种下拉) - 挡车数按 F9 口径自动重算(与他人接壤的边界车位各扣 0.5),机组由车位反查机组车台 - 前端新增 /prod/qianxian/ktjl 看台记录页:挡车工/车头工双页签、删除、录入(新增)、录入(指定日期) - 录入弹窗校验对齐 F9:个人时长不超标准、挡车数/看台时长匹配、车头工每机组≥2 人且时长为标准时长整数倍 - 生产-前缫菜单新增「看台记录」,权限 prod:ktjl:list 数据导入: - 新增内嵌资源 biz_prod_qs_ktjl_ctg.csv / biz_prod_qs_ktjl_dcg.csv(约 5 万行,采用紧凑 CSV) - 剔除软删除行、日期异常行、部门无法映射与职工不存在的行;日期由 Excel 序列号转换 - 车头工 12693 行、挡车工 38048 行入库,日期范围 2025-12-16 ~ 2026-09-11 - 表内已有数据则整体跳过,分批 1000 行插入 其他:同步工作进度-13 文档与开发数据库快照
This commit is contained in:
@@ -66,6 +66,7 @@ export const componentMap = {
|
||||
'prod/xdjy/index': () => import('../views/prod/xdjy/index.vue'),
|
||||
'prod/hbjy/index': () => import('../views/prod/hbjy/index.vue'),
|
||||
'prod/ktpb/index': () => import('../views/prod/ktpb/index.vue'),
|
||||
'prod/ktjl/index': () => import('../views/prod/ktjl/index.vue'),
|
||||
'notification/messages/index': () => import('../views/notification/messages/index.vue'),
|
||||
'notification/subscribe/index': () => import('../views/notification/subscribe/index.vue'),
|
||||
'notification/type/index': () => import('../views/notification/type/index.vue'),
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
<script setup>
|
||||
/**
|
||||
* 看台记录录入弹窗(对应 F9 Page_Ktjl_Dcg_Edit / Page_Ktjl_Ctg_Edit)
|
||||
* - 挡车工:可编辑机组/起止车位/挡车数/时长,挡车数按接壤车位自动计算
|
||||
* - 车头工:可编辑机组/时长
|
||||
* - 校验口径与 F9 一致:个人时长不超标准、挡车数/看台时长匹配、车头工每机组≥2 人且时长为标准时长整数倍
|
||||
*/
|
||||
import { ref, reactive, computed, watch } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import request from '../../../utils/request';
|
||||
import EmployeeSelect from '../../../components/select/EmployeeSelect.vue';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: Boolean,
|
||||
type: { type: String, default: 'dcg' }, // dcg | ctg
|
||||
date: { type: String, default: '' },
|
||||
depId: { type: Number, default: 0 },
|
||||
depName: { type: String, default: '' },
|
||||
banci: { type: Number, default: 1 },
|
||||
banciName: { type: String, default: '甲' },
|
||||
/** 预置行(录入(新增) 的排班带出 / 指定日期复制),库中无记录时使用 */
|
||||
presetRows: { type: Array, default: () => [] },
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue', 'saved']);
|
||||
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const rows = ref([]);
|
||||
const hoursStandard = ref(9);
|
||||
const jizuOptions = ref([]);
|
||||
const empPicker = reactive({ visible: false });
|
||||
const selected = ref([]);
|
||||
|
||||
const title = computed(() => (props.type === 'dcg' ? '挡车工看台记录' : '车头工看台记录'));
|
||||
|
||||
watch(() => props.modelValue, async (v) => {
|
||||
visible.value = v;
|
||||
if (v) await load();
|
||||
});
|
||||
watch(visible, (v) => emit('update:modelValue', v));
|
||||
|
||||
async function load() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await request.get(`/prod/ktjl/${props.type}`, {
|
||||
params: { date: props.date, depId: props.depId, banci: props.banci },
|
||||
});
|
||||
const list = (res.data || []).length ? res.data : props.presetRows || [];
|
||||
rows.value = list.map((x) => ({ ...x, selected: false }));
|
||||
const jz = await request.get('/prod/ktpb/jizu-options', { params: { depId: props.depId } });
|
||||
jizuOptions.value = jz.data || [];
|
||||
hoursStandard.value = rows.value.length ? Math.max(...rows.value.map((x) => Number(x.hours) || 0)) : 9;
|
||||
if (props.type === 'dcg') recalcDcg();
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 由车位推导机组(与后端 CalJizu 一致) */
|
||||
function jizuOf(start, end) {
|
||||
const hit = jizuOptions.value.find((j) => j.chetaiStart <= end && j.chetaiEnd >= start);
|
||||
return hit ? hit.jizu : 0;
|
||||
}
|
||||
|
||||
/** 挡车数:车位数 - 与他人在边界车位各分 0.5(与后端 CalDangcheNum 一致) */
|
||||
function recalcDcg() {
|
||||
const list = rows.value;
|
||||
list.forEach((it) => {
|
||||
const s = Number(it.chetaiStart) || 0;
|
||||
const e = Number(it.chetaiEnd) || 0;
|
||||
if (!s || e < s) {
|
||||
it.dangcheNum = 0;
|
||||
return;
|
||||
}
|
||||
const startShared = list.some((x) => x !== it && x.employeeId !== it.employeeId && Number(x.chetaiEnd) === s);
|
||||
const endShared = list.some((x) => x !== it && x.employeeId !== it.employeeId && Number(x.chetaiStart) === e);
|
||||
it.dangcheNum = e + 1 - s - (startShared ? 0.5 : 0) - (endShared ? 0.5 : 0);
|
||||
it.jizu = jizuOf(s, e);
|
||||
});
|
||||
}
|
||||
|
||||
function onChetaiChange() {
|
||||
recalcDcg();
|
||||
}
|
||||
|
||||
/** 添加职工(多选) */
|
||||
async function openEmpPicker() {
|
||||
empPicker.visible = true;
|
||||
}
|
||||
async function onEmployeesPicked(list) {
|
||||
const exist = new Set(rows.value.map((x) => String(x.employeeId)));
|
||||
const picked = Array.isArray(list) ? list : [list];
|
||||
picked.forEach((e) => {
|
||||
if (exist.has(String(e.id))) return;
|
||||
rows.value.push({
|
||||
id: 0,
|
||||
employeeId: e.id,
|
||||
employeeName: e.name,
|
||||
employeeDepId: e.depId,
|
||||
employeeDepName: e.depName,
|
||||
worktypeId: e.worktypeId,
|
||||
worktypeName: e.worktypeName,
|
||||
jizu: props.type === 'dcg' ? 0 : 1,
|
||||
chetaiStart: 0,
|
||||
chetaiEnd: 0,
|
||||
dangcheNum: 0,
|
||||
hours: Number(hoursStandard.value) || 9,
|
||||
noChanliang: false,
|
||||
selected: false,
|
||||
});
|
||||
});
|
||||
if (props.type === 'dcg') recalcDcg();
|
||||
}
|
||||
|
||||
function removeSelected() {
|
||||
const picked = rows.value.filter((x) => x.selected);
|
||||
if (!picked.length) return ElMessage.warning('请选择要删除的记录');
|
||||
rows.value = rows.value.filter((x) => !x.selected);
|
||||
if (props.type === 'dcg') recalcDcg();
|
||||
}
|
||||
|
||||
async function batchHours() {
|
||||
const picked = rows.value.filter((x) => x.selected);
|
||||
if (!picked.length) return ElMessage.warning('请选择要修改的记录');
|
||||
try {
|
||||
const { value } = await ElMessageBox.prompt('批量设置时长(小时)', '设置时长', {
|
||||
inputValue: String(hoursStandard.value),
|
||||
inputPattern: /^\d+(\.\d+)?$/,
|
||||
inputErrorMessage: '请输入数字',
|
||||
});
|
||||
picked.forEach((x) => {
|
||||
x.hours = Number(value);
|
||||
});
|
||||
} catch {
|
||||
/* 取消 */
|
||||
}
|
||||
}
|
||||
|
||||
/** 校验(对齐 F9 ValidCustom),返回提示列表 */
|
||||
function validate() {
|
||||
const warns = [];
|
||||
const std = Number(hoursStandard.value) || 0;
|
||||
|
||||
if (rows.value.some((x) => !x.employeeId)) {
|
||||
return { fatal: '不能有空数据(存在未选择职工的记录)', warns: [] };
|
||||
}
|
||||
|
||||
if (props.type === 'dcg') {
|
||||
const over = [];
|
||||
const byEmp = {};
|
||||
rows.value.forEach((x) => {
|
||||
const key = String(x.employeeId);
|
||||
byEmp[key] = byEmp[key] || { name: x.employeeName, hours: 0 };
|
||||
byEmp[key].hours += Number(x.hours) || 0;
|
||||
});
|
||||
Object.values(byEmp).forEach((v) => {
|
||||
if (std > 0 && v.hours > std) over.push(v.name);
|
||||
});
|
||||
if (over.length) return { fatal: `${over.join('、')} 超过标准时长(${std} 小时)`, warns: [] };
|
||||
|
||||
rows.value.forEach((x) => {
|
||||
const s = Number(x.chetaiStart);
|
||||
const e = Number(x.chetaiEnd);
|
||||
const shared = rows.value.some((y) => y.employeeId !== x.employeeId && (Number(y.chetaiEnd) === s || Number(y.chetaiStart) === e));
|
||||
if (shared && Number(x.dangcheNum) * 10 % 10 === 0) {
|
||||
warns.push(`${x.jizu}组${e}台,多人看台挡车数不匹配`);
|
||||
}
|
||||
if (Number(x.dangcheNum) === 0) warns.push(`${x.jizu}组${e}台,挡车数不能为 0`);
|
||||
});
|
||||
|
||||
// 看台时长校验
|
||||
const jizus = [...new Set(rows.value.map((x) => x.jizu))];
|
||||
jizus.forEach((jz) => {
|
||||
const items = rows.value.filter((x) => x.jizu === jz);
|
||||
if (!items.length) return;
|
||||
const min = Math.min(...items.map((x) => Number(x.chetaiStart)));
|
||||
const max = Math.max(...items.map((x) => Number(x.chetaiEnd)));
|
||||
for (let i = min; i <= max; i++) {
|
||||
const cover = items.filter((x) => Number(x.chetaiStart) <= i && Number(x.chetaiEnd) >= i);
|
||||
if (!cover.length) continue;
|
||||
const hasStart = cover.some((x) => Number(x.chetaiStart) === i);
|
||||
const hasEnd = cover.some((x) => Number(x.chetaiEnd) === i);
|
||||
if (hasStart && hasEnd) {
|
||||
const up = cover.filter((x) => Number(x.chetaiStart) === i).reduce((a, b) => a + Number(b.hours), 0);
|
||||
const down = cover.filter((x) => Number(x.chetaiEnd) === i).reduce((a, b) => a + Number(b.hours), 0);
|
||||
if (up === down) {
|
||||
if (std > 0 && up !== std) warns.push(`${jz}组${i}台,看台时长有误`);
|
||||
} else {
|
||||
warns.push(`${jz}组${i}台,看台时长不一致`);
|
||||
}
|
||||
} else {
|
||||
const sum = cover.reduce((a, b) => a + Number(b.hours), 0);
|
||||
if (std > 0 && sum !== std) warns.push(`${jz}组${i}台,看台时长有误`);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const jizus = [...new Set(rows.value.map((x) => x.jizu))];
|
||||
jizus.forEach((jz) => {
|
||||
const items = rows.value.filter((x) => x.jizu === jz);
|
||||
if (items.length < 2) warns.push(`${jz}组不足两人`);
|
||||
if (items.some((x) => Number(x.hours) === 0)) warns.push(`${jz}组时长有误`);
|
||||
const sum = items.reduce((a, b) => a + Number(b.hours), 0);
|
||||
if (std > 0 && (sum < std * 2 || sum % std !== 0)) warns.push(`${jz}组时长有误`);
|
||||
});
|
||||
}
|
||||
|
||||
return { fatal: '', warns };
|
||||
}
|
||||
|
||||
async function save() {
|
||||
const { fatal, warns } = validate();
|
||||
if (fatal) return ElMessage.warning(fatal);
|
||||
if (warns.length) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`${[...new Set(warns)].join('\n')}\n是否继续保存?`, '验证提示', {
|
||||
type: 'warning',
|
||||
confirmButtonText: '继续保存',
|
||||
cancelButtonText: '返回修改',
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
const res = await request.post(`/prod/ktjl/${props.type}/batch`, {
|
||||
date: props.date,
|
||||
depId: props.depId,
|
||||
banci: props.banci,
|
||||
items: rows.value.map((x) => ({
|
||||
employeeId: x.employeeId,
|
||||
employeeDepId: x.employeeDepId,
|
||||
worktypeId: x.worktypeId,
|
||||
worktypeName: x.worktypeName,
|
||||
jizu: Number(x.jizu) || 0,
|
||||
chetaiStart: Number(x.chetaiStart) || 0,
|
||||
chetaiEnd: Number(x.chetaiEnd) || 0,
|
||||
dangcheNum: Number(x.dangcheNum) || 0,
|
||||
hours: Number(x.hours) || 0,
|
||||
noChanliang: !!x.noChanliang,
|
||||
})),
|
||||
});
|
||||
ElMessage.success(res.message || '保存成功');
|
||||
visible.value = false;
|
||||
emit('saved');
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="title" width="1080px" destroy-on-close append-to-body>
|
||||
<div class="dlg-head">
|
||||
<el-tag type="info">{{ date }}</el-tag>
|
||||
<el-tag type="info">{{ depName }}</el-tag>
|
||||
<el-tag type="info">{{ banciName }}班</el-tag>
|
||||
<span class="lbl">标准时长</span>
|
||||
<el-input-number v-model="hoursStandard" :min="1" :max="24" :step="0.5" :controls="false" style="width: 80px" />
|
||||
<span class="tip">共 {{ rows.length }} 条</span>
|
||||
</div>
|
||||
|
||||
<div class="dlg-toolbar">
|
||||
<el-button type="primary" size="small" @click="openEmpPicker">添加职工</el-button>
|
||||
<el-button size="small" @click="removeSelected">删除选中</el-button>
|
||||
<el-button size="small" @click="batchHours">批量修改时长</el-button>
|
||||
<span class="hint">
|
||||
{{ type === 'dcg'
|
||||
? '挡车数 = 车位数 −(与他人接壤的边界车位各 0.5);机组按车位自动归属'
|
||||
: '每个机组至少 2 人,机组时长合计需为标准时长的整数倍' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="rows" border size="small" height="420" @selection-change="selected = $event">
|
||||
<el-table-column type="selection" width="42" />
|
||||
<el-table-column type="index" label="序号" width="52" />
|
||||
<el-table-column prop="employeeDepName" label="部门" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="worktypeName" label="工种" width="90" />
|
||||
<el-table-column prop="employeeName" label="姓名" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="班次" width="70" align="center">
|
||||
<template #default>{{ banciName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="机组" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.jizu" :min="0" :max="99" :controls="false" size="small" style="width: 60px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-if="type === 'dcg'">
|
||||
<el-table-column label="开始车位" width="96" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.chetaiStart"
|
||||
:min="1"
|
||||
:max="999"
|
||||
:controls="false"
|
||||
size="small"
|
||||
style="width: 76px"
|
||||
@change="onChetaiChange"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束车位" width="96" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.chetaiEnd"
|
||||
:min="1"
|
||||
:max="999"
|
||||
:controls="false"
|
||||
size="small"
|
||||
style="width: 76px"
|
||||
@change="onChetaiChange"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dangcheNum" label="挡车数" width="80" align="right" />
|
||||
</template>
|
||||
<el-table-column label="时长" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number v-model="row.hours" :min="0" :max="24" :step="0.5" :controls="false" size="small" style="width: 70px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="不计产量" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch v-model="row.noChanliang" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
|
||||
<EmployeeSelect v-model="empPicker.visible" multiple :dep-id="depId" @confirm="onEmployeesPicked" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dlg-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
|
||||
.dlg-head .lbl { color: var(--el-text-color-regular); font-size: 13px; margin-left: 6px; }
|
||||
.dlg-head .tip { margin-left: auto; color: var(--el-text-color-secondary); font-size: 13px; }
|
||||
.dlg-toolbar { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
||||
.dlg-toolbar .hint { color: var(--el-text-color-secondary); font-size: 12px; }
|
||||
</style>
|
||||
@@ -0,0 +1,260 @@
|
||||
<script setup>
|
||||
/**
|
||||
* 看台记录(生产-前缫)
|
||||
* 对应 WinForms 端 WinClient\F9.Business.Prod\QS\KTJL\Page_Ktjl
|
||||
* - 两个页签:挡车工 / 车头工
|
||||
* - 过滤:日期 + 车间 + 班次(关键字)
|
||||
* - 按钮:删除(按日期+车间+班次整批)、录入(新增)(按看台排班带出)、录入(指定日期)(复制某日记录)
|
||||
*/
|
||||
import { ref, reactive, computed, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import request from '../../../utils/request';
|
||||
import KtjlEditDialog from './KtjlEditDialog.vue';
|
||||
|
||||
const TABS = [
|
||||
{ name: 'dcg', label: '挡车工' },
|
||||
{ name: 'ctg', label: '车头工' },
|
||||
];
|
||||
const BANCI = [
|
||||
{ value: 1, label: '甲班' },
|
||||
{ value: 2, label: '乙班' },
|
||||
];
|
||||
|
||||
function today() {
|
||||
const d = new Date();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
return `${d.getFullYear()}-${m}-${dd}`;
|
||||
}
|
||||
|
||||
const tab = ref('dcg');
|
||||
const deps = ref([]);
|
||||
const filter = reactive({ date: today(), depId: 0, banci: 1, keyword: '' });
|
||||
const state = reactive({
|
||||
dcg: { rows: [], loading: false },
|
||||
ctg: { rows: [], loading: false },
|
||||
});
|
||||
const cur = computed(() => state[tab.value]);
|
||||
const banciName = computed(() => (filter.banci === 2 ? '乙' : '甲'));
|
||||
const depName = computed(() => deps.value.find((d) => d.id === filter.depId)?.name || '');
|
||||
|
||||
const dlg = reactive({ visible: false, type: 'dcg', presetRows: [] });
|
||||
const copyDlg = reactive({ visible: false, date: '' });
|
||||
|
||||
async function loadDeps() {
|
||||
const res = await request.get('/prod/ktpb/qianxian-deps');
|
||||
deps.value = res.data || [];
|
||||
if (deps.value.length && !filter.depId) filter.depId = deps.value[0].id;
|
||||
}
|
||||
|
||||
async function load() {
|
||||
if (!filter.depId) return;
|
||||
const s = cur.value;
|
||||
s.loading = true;
|
||||
try {
|
||||
const res = await request.get(`/prod/ktjl/${tab.value}`, {
|
||||
params: { date: filter.date, depId: filter.depId, banci: filter.banci, like: filter.keyword },
|
||||
});
|
||||
s.rows = res.data || [];
|
||||
} finally {
|
||||
s.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadDeps();
|
||||
await load();
|
||||
});
|
||||
|
||||
async function onTabChange() {
|
||||
await load();
|
||||
}
|
||||
|
||||
/** 录入(新增):已有记录则继续编辑,否则按看台排班带出默认记录 */
|
||||
async function openNew() {
|
||||
if (!filter.depId) return ElMessage.warning('请先选择车间');
|
||||
const res = await request.get(`/prod/ktjl/${tab.value}`, {
|
||||
params: { date: filter.date, depId: filter.depId, banci: filter.banci },
|
||||
});
|
||||
const exist = res.data || [];
|
||||
if (exist.length) {
|
||||
dlg.presetRows = [];
|
||||
} else {
|
||||
const def = await request.get(`/prod/ktjl/${tab.value}/defaults`, {
|
||||
params: { date: filter.date, depId: filter.depId, banci: filter.banci },
|
||||
});
|
||||
dlg.presetRows = def.data || [];
|
||||
if (!dlg.presetRows.length) {
|
||||
ElMessage.warning('该车间该班次暂无看台排班,请先维护「看台排班」或手工添加职工');
|
||||
}
|
||||
}
|
||||
dlg.type = tab.value;
|
||||
dlg.visible = true;
|
||||
}
|
||||
|
||||
/** 录入(指定日期):把某一天的记录复制到当前日期再编辑 */
|
||||
async function openCopy() {
|
||||
if (!filter.depId) return ElMessage.warning('请先选择车间');
|
||||
copyDlg.date = '';
|
||||
copyDlg.visible = true;
|
||||
}
|
||||
|
||||
async function doCopy() {
|
||||
if (!copyDlg.date) return ElMessage.warning('请选择来源日期');
|
||||
const res = await request.get(`/prod/ktjl/${tab.value}`, {
|
||||
params: { date: copyDlg.date, depId: filter.depId, banci: filter.banci },
|
||||
});
|
||||
const list = (res.data || []).map((x) => ({ ...x, id: 0 }));
|
||||
if (!list.length) return ElMessage.warning('来源日期没有看台记录');
|
||||
copyDlg.visible = false;
|
||||
dlg.presetRows = list;
|
||||
dlg.type = tab.value;
|
||||
dlg.visible = true;
|
||||
}
|
||||
|
||||
async function removeGroup() {
|
||||
if (!filter.depId) return ElMessage.warning('请先选择车间');
|
||||
if (!cur.value.rows.length) return ElMessage.warning('当前没有可删除的记录');
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确认删除 ${filter.date},${depName.value},${banciName.value}班 的全部${tab.value === 'dcg' ? '挡车工' : '车头工'}看台记录?`,
|
||||
'提示',
|
||||
{ type: 'warning' }
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const res = await request.delete(`/prod/ktjl/${tab.value}`, {
|
||||
params: { date: filter.date, depId: filter.depId, banci: filter.banci },
|
||||
});
|
||||
ElMessage.success(res.message || '删除成功');
|
||||
await load();
|
||||
}
|
||||
|
||||
async function onSaved() {
|
||||
await load();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ktjl-page">
|
||||
<el-card shadow="never">
|
||||
<el-tabs v-model="tab" @tab-change="onTabChange">
|
||||
<el-tab-pane v-for="t in TABS" :key="t.name" :label="t.label" :name="t.name" />
|
||||
</el-tabs>
|
||||
|
||||
<div class="toolbar">
|
||||
<span class="lbl">日期</span>
|
||||
<el-date-picker
|
||||
v-model="filter.date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
:clearable="false"
|
||||
style="width: 150px"
|
||||
@change="load"
|
||||
/>
|
||||
<span class="lbl">车间</span>
|
||||
<el-select v-model="filter.depId" placeholder="请选择车间" style="width: 160px" @change="load">
|
||||
<el-option v-for="d in deps" :key="d.id" :label="d.name" :value="d.id" />
|
||||
</el-select>
|
||||
<span class="lbl">班次</span>
|
||||
<el-select v-model="filter.banci" style="width: 110px" @change="load">
|
||||
<el-option v-for="b in BANCI" :key="b.value" :label="b.label" :value="b.value" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="filter.keyword"
|
||||
placeholder="姓名 / 工种 / 机组"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
@keyup.enter="load"
|
||||
@clear="load"
|
||||
/>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<div class="right">
|
||||
<el-button type="danger" plain @click="removeGroup">删除</el-button>
|
||||
<el-button type="primary" @click="openNew">录入(新增)</el-button>
|
||||
<el-button @click="openCopy">录入(指定日期)</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 挡车工 -->
|
||||
<el-table v-if="tab === 'dcg'" v-loading="state.dcg.loading" :data="state.dcg.rows" border size="small">
|
||||
<el-table-column type="index" label="序号" width="55" />
|
||||
<el-table-column prop="depName" label="部门" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="班次" width="70" align="center">
|
||||
<template #default="{ row }">{{ row.banciName }}班</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="employeeName" label="姓名" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="worktypeName" label="工种" width="90" />
|
||||
<el-table-column prop="employeeDepName" label="职工部门" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="jizu" label="机组" width="70" align="center" />
|
||||
<el-table-column prop="chetaiStart" label="开始车位" width="90" align="center" />
|
||||
<el-table-column prop="chetaiEnd" label="结束车位" width="90" align="center" />
|
||||
<el-table-column prop="dangcheNum" label="挡车数" width="80" align="right" />
|
||||
<el-table-column prop="hours" label="时长" width="70" align="right" />
|
||||
<el-table-column label="不计产量" width="90" align="center">
|
||||
<template #default="{ row }">{{ row.noChanliang ? '√' : '' }}</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<span class="empty">当日无记录,可点击「录入(新增)」按看台排班带出</span>
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<!-- 车头工 -->
|
||||
<el-table v-else v-loading="state.ctg.loading" :data="state.ctg.rows" border size="small">
|
||||
<el-table-column type="index" label="序号" width="55" />
|
||||
<el-table-column prop="depName" label="部门" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="班次" width="70" align="center">
|
||||
<template #default="{ row }">{{ row.banciName }}班</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="employeeName" label="姓名" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="worktypeName" label="工种" width="90" />
|
||||
<el-table-column prop="employeeDepName" label="职工部门" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column prop="jizu" label="机组" width="70" align="center" />
|
||||
<el-table-column prop="hours" label="时长" width="70" align="right" />
|
||||
<el-table-column label="不计产量" width="90" align="center">
|
||||
<template #default="{ row }">{{ row.noChanliang ? '√' : '' }}</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<span class="empty">当日无记录,可点击「录入(新增)」按看台排班带出</span>
|
||||
</template>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<KtjlEditDialog
|
||||
v-model="dlg.visible"
|
||||
:type="dlg.type"
|
||||
:date="filter.date"
|
||||
:dep-id="filter.depId"
|
||||
:dep-name="depName"
|
||||
:banci="filter.banci"
|
||||
:banci-name="banciName"
|
||||
:preset-rows="dlg.presetRows"
|
||||
@saved="onSaved"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="copyDlg.visible" title="选择来源日期" width="360px" append-to-body>
|
||||
<el-date-picker
|
||||
v-model="copyDlg.date"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择要复制的日期"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="copy-tip">将把该日期「{{ depName }} · {{ banciName }}班」的{{ tab === 'dcg' ? '挡车工' : '车头工' }}看台记录复制到 {{ filter.date }} 再编辑保存。</div>
|
||||
<template #footer>
|
||||
<el-button @click="copyDlg.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="doCopy">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ktjl-page :deep(.el-card__body) { padding-top: 6px; }
|
||||
.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
|
||||
.toolbar .lbl { color: var(--el-text-color-regular); font-size: 13px; }
|
||||
.toolbar .right { margin-left: auto; }
|
||||
.empty { color: var(--el-text-color-secondary); font-size: 13px; }
|
||||
.copy-tip { margin-top: 10px; color: var(--el-text-color-secondary); font-size: 12px; line-height: 1.6; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user