<template>
|
<div class="app-container">
|
<div class="search_form">
|
<div class="search-row">
|
<div class="search-item">
|
<span class="search_title">工单编号:</span>
|
<el-input v-model="searchForm.workOrderNo"
|
style="width: 240px"
|
placeholder="请输入"
|
@change="handleQuery"
|
clearable
|
prefix-icon="Search"/>
|
</div>
|
<div class="search-item">
|
<el-button type="primary"
|
@click="handleQuery">搜索
|
</el-button>
|
</div>
|
</div>
|
</div>
|
<div class="table_list">
|
<PIMTable rowKey="id"
|
:column="tableColumn"
|
:tableData="tableData"
|
:page="page"
|
:tableLoading="tableLoading"
|
@pagination="pagination">
|
<template #completionStatus="{ row }">
|
<el-progress :percentage="toProgressPercentage(row?.completionStatus)"
|
:color="progressColor(toProgressPercentage(row?.completionStatus))"
|
:status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"/>
|
</template>
|
</PIMTable>
|
</div>
|
<el-dialog v-model="editDialogVisible"
|
title="编辑时间"
|
width="500px">
|
<el-form :model="editrow"
|
label-width="120px">
|
<el-form-item label="计划开始时间">
|
<el-date-picker v-model="editrow.planStartTime"
|
type="date"
|
placeholder="请选择"
|
value-format="YYYY-MM-DD"
|
style="width: 300px"/>
|
</el-form-item>
|
<el-form-item label="计划结束时间">
|
<el-date-picker v-model="editrow.planEndTime"
|
type="date"
|
placeholder="请选择"
|
value-format="YYYY-MM-DD"
|
style="width: 300px"/>
|
</el-form-item>
|
<el-form-item label="实际开始时间">
|
<el-date-picker v-model="editrow.actualStartTime"
|
type="date"
|
placeholder="请选择"
|
value-format="YYYY-MM-DD"
|
style="width: 300px"/>
|
</el-form-item>
|
<el-form-item label="实际结束时间">
|
<el-date-picker v-model="editrow.actualEndTime"
|
type="date"
|
placeholder="请选择"
|
value-format="YYYY-MM-DD"
|
style="width: 300px"/>
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary"
|
@click="handleUpdate">确定</el-button>
|
<el-button @click="editDialogVisible = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<el-dialog v-model="transferCardVisible"
|
title="流转卡"
|
width="1000px">
|
<div class="transfer-card-title">工单流转卡</div>
|
<div class="transfer-card-container">
|
<div class="transfer-card-info">
|
<div class="info-group">
|
<div class="info-item">
|
<span class="info-label">工单编号</span>
|
<span class="info-value">{{ transferCardRowData.workOrderNo }}</span>
|
</div>
|
<!-- <div class="info-item">
|
<span class="info-label">产品编号</span>
|
<span class="info-value">{{ transferCardRowData.productNo }}</span>
|
</div> -->
|
<div class="info-item">
|
<span class="info-label">产品名称</span>
|
<span class="info-value">{{ transferCardRowData.productName }}</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">产品规格</span>
|
<span class="info-value">{{ transferCardRowData.model }}</span>
|
</div>
|
<!-- <div class="info-item">
|
<span class="info-label">工单状态</span>
|
<span class="info-value">{{
|
transferCardRowData.status === 1 ? '待确认' :
|
transferCardRowData.status === 2 ? '待生产' :
|
transferCardRowData.status === 3 ? '生产中' :
|
transferCardRowData.status === 4 ? '已生产' :
|
transferCardRowData.status
|
}}</span>
|
</div> -->
|
<div class="info-item">
|
<span class="info-label">计划开始时间</span>
|
<span class="info-value">{{ transferCardRowData.planStartTime }}</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">计划结束时间</span>
|
<span class="info-value">{{ transferCardRowData.planEndTime }}</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">备注</span>
|
<span class="info-value">{{ transferCardRowData.remark }}</span>
|
</div>
|
</div>
|
<div class="info-group">
|
<div class="info-item">
|
<span class="info-label">需求数量</span>
|
<span class="info-value">{{ transferCardRowData.planQuantity }}</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">完成数量</span>
|
<span class="info-value">{{ transferCardRowData.completeQuantity }}</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">良品数量</span>
|
<span class="info-value">0</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">不良品数</span>
|
<span class="info-value">0</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">实际开始时间</span>
|
<span class="info-value">{{ transferCardRowData.actualStartTime }}</span>
|
</div>
|
<div class="info-item">
|
<span class="info-label">实际结束时间</span>
|
<span class="info-value">{{ transferCardRowData.actualEndTime }}</span>
|
</div>
|
</div>
|
</div>
|
<div class="transfer-card-qr">
|
<div class="qr-container">
|
<img :src="transferCardQrUrl"
|
alt="流转卡二维码"
|
style="width: 200px; height: 200px;"/>
|
<!-- <div class="qr-tip"
|
style="margin-top: 10px; text-align: center;">流转卡二维码</div> -->
|
</div>
|
</div>
|
</div>
|
<div class="print-button-container"
|
style=" text-align: center;
|
margin-bottom: 40px;">
|
<el-button type="primary"
|
style="margin-top: 20px;"
|
@click="printTransferCard">打印流转卡
|
</el-button>
|
</div>
|
</el-dialog>
|
<el-dialog v-model="reportDialogVisible"
|
:title="`报工(工单编号:${currentReportRowData?.workOrderNo || '-'})`"
|
width="1000px">
|
<el-form
|
ref="reportFormRef"
|
:model="reportForm"
|
:rules="reportFormRules"
|
label-width="120px"
|
>
|
<el-row :gutter="20">
|
|
<el-col :span="12">
|
<el-form-item label="待生产数量">
|
<el-input v-model="reportForm.planQuantity" readonly disabled/>
|
</el-form-item>
|
</el-col>
|
|
<el-col :span="12">
|
<el-form-item label="本次生产数量" prop="quantity">
|
<el-input
|
v-model.number="reportForm.quantity"
|
type="number"
|
min="1"
|
step="1"
|
placeholder="请输入本次生产数量"
|
style="width: 100%"
|
@input="handleQuantityInput"
|
/>
|
</el-form-item>
|
</el-col>
|
|
<el-col :span="12">
|
<el-form-item label="补产数量" prop="replenishQty">
|
<el-input
|
v-model.number="reportForm.replenishQty"
|
type="number"
|
min="0"
|
step="1"
|
placeholder="请输入补产数量"
|
/>
|
</el-form-item>
|
</el-col>
|
|
<el-col :span="12">
|
<el-form-item label="报废数量" prop="scrapQty">
|
<el-input
|
v-model.number="reportForm.scrapQty"
|
type="number"
|
min="0"
|
step="1"
|
placeholder="请输入报废数量"
|
@input="handleScrapQtyInput"
|
/>
|
</el-form-item>
|
</el-col>
|
|
<el-col :span="12">
|
<el-form-item label="班组信息" prop="teamList">
|
<el-select
|
v-model="reportForm.teamList"
|
multiple
|
filterable
|
clearable
|
collapse-tags
|
value-key="userId"
|
placeholder="请选择班组成员"
|
>
|
<el-option
|
v-for="user in reportForm.userIdsList"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="{ userId: user.userId, userName: user.nickName }"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
|
<!-- <el-col :span="12">-->
|
<!-- <el-form-item label="机台" prop="deviceId">-->
|
<!-- <el-select-->
|
<!-- v-model="reportForm.deviceId"-->
|
<!-- placeholder="请选择机台"-->
|
<!-- filterable-->
|
<!-- clearable-->
|
<!-- @change="(val) => handleDeviceChange(val)"-->
|
<!-- :disabled="isDetail"-->
|
<!-- >-->
|
<!-- <el-option-->
|
<!-- v-for="item in deviceOptions"-->
|
<!-- :key="item.id"-->
|
<!-- :label="item.deviceName"-->
|
<!-- :value="item.id"-->
|
<!-- />-->
|
<!-- </el-select>-->
|
<!-- </el-form-item>-->
|
<!-- </el-col>-->
|
|
<el-col :span="12">
|
<el-form-item label="审核人" prop="auditUserId">
|
<el-select
|
v-model="reportForm.auditUserId"
|
placeholder="请选择审核人"
|
clearable
|
filterable
|
@change="handleReviewerIdChange"
|
>
|
<el-option
|
v-for="user in userOptions"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="user.userId"
|
/>
|
</el-select>
|
</el-form-item>
|
</el-col>
|
|
</el-row>
|
</el-form>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary"
|
@click="handleReport">确定</el-button>
|
<el-button @click="reportDialogVisible = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<el-dialog
|
v-model="auditDialogVisible"
|
title="审核"
|
width="1000px"
|
:close-on-click-modal="false"
|
>
|
<el-table :data="auditTableData" border style="width: 100%" v-loading="auditLoading">
|
<el-table-column label="产品名称" prop="productName" min-width="140" show-overflow-tooltip/>
|
<el-table-column label="规格" prop="model" min-width="120" show-overflow-tooltip/>
|
<el-table-column label="单位" prop="unit" width="80"/>
|
<el-table-column label="工序名称" prop="processName" min-width="120" show-overflow-tooltip/>
|
<el-table-column label="需求数量" prop="planQuantity" width="110"/>
|
<el-table-column label="完成数量" prop="completeQuantity" width="110"/>
|
<el-table-column label="完成进度" prop="completionStatus" width="140">
|
<template #default="{ row }">
|
<el-progress
|
:percentage="toProgressPercentage(row?.completionStatus)"
|
:color="progressColor(toProgressPercentage(row?.completionStatus))"
|
:status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column label="计划开始时间" prop="planStartTime" width="140"/>
|
<el-table-column label="计划结束时间" prop="planEndTime" width="140"/>
|
</el-table>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary" :loading="auditLoading" @click="submitAudit(1)">通过</el-button>
|
<el-button type="danger" :loading="auditLoading" @click="submitAudit(2)">不通过</el-button>
|
<el-button :disabled="auditLoading" @click="auditDialogVisible = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<el-dialog v-model="scheduleDialogVisible"
|
:title="`生产排产(工单编号:${currentReportRowData?.workOrderNo || '-'})`"
|
width="1000px"
|
:close-on-click-modal="false">
|
<div class="schedule-panel">
|
<el-row style="margin-bottom: 12px;">
|
<el-col>
|
<el-button type="primary" plain :disabled="scheduleLoading || scheduleSaving" @click="addScheduleRow">
|
新增一行
|
</el-button>
|
</el-col>
|
</el-row>
|
<el-table :data="scheduleRows" border style="width: 100%" v-loading="scheduleLoading">
|
<el-table-column type="index" label="序号" width="70" align="center" :index="indexMethod" />
|
<el-table-column label="本次上机机台" min-width="220">
|
<template #default="{ row }">
|
<el-select
|
v-model="row.deviceId"
|
placeholder="请选择机台"
|
filterable
|
clearable
|
style="width: 100%"
|
:disabled="scheduleSaving"
|
@change="val => handleScheduleDeviceChange(val, row)"
|
>
|
<el-option
|
v-for="item in deviceOptions"
|
:key="item.id"
|
:label="item.deviceName"
|
:value="String(item.id)"
|
/>
|
</el-select>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="本次上机人" min-width="220">
|
<template #default="{ row }">
|
<el-select
|
v-model="row.userIds"
|
placeholder="请选择上机人"
|
filterable
|
multiple
|
clearable
|
collapse-tags
|
style="width: 100%"
|
:disabled="scheduleSaving"
|
@change="val => handleScheduleUserChange(val, row)"
|
>
|
<el-option
|
v-for="user in row.userOptions"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="String(user.userId)"
|
/>
|
</el-select>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="本次上机时间" min-width="240">
|
<template #default="{ row }">
|
<el-date-picker
|
v-model="row.startTime"
|
type="datetime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
placeholder="请选择上机时间"
|
style="width: 100%"
|
:disabled="scheduleSaving"
|
/>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="操作" width="110" align="center">
|
<template #default="{ row }">
|
<el-button
|
link
|
type="danger"
|
:loading="row.deleting"
|
:disabled="scheduleSaving"
|
@click="removeScheduleRow(row)"
|
>
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<Pagination
|
v-show="schedulePage.total > 0"
|
style="margin-top: 12px"
|
:total="schedulePage.total"
|
:page="schedulePage.current"
|
:limit="schedulePage.size"
|
@pagination="handleSchedulePagination"
|
/>
|
</div>
|
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button type="primary" :loading="scheduleSaving" @click="handleSaveSchedule">保存排产</el-button>
|
<el-button :disabled="scheduleSaving" @click="scheduleDialogVisible = false">取消</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<FilesDia ref="workOrderFilesRef"/>
|
</div>
|
</template>
|
|
<script setup>
|
import {onMounted, ref, nextTick, computed} from "vue";
|
import {deepClone} from "@/utils/index.js"
|
import {ElMessageBox, ElMessage} from "element-plus";
|
import Pagination from "@/components/PIMTable/Pagination.vue";
|
import dayjs from "dayjs";
|
import { processList } from '@/api/productionManagement/productionProcess.js'
|
import {
|
productWorkOrderPage,
|
updateProductWorkOrder,
|
addProductMain,
|
downProductWorkOrder,
|
addProductionMachineRecord,
|
productionMachineRecordListPage,
|
deleteProductionMachineRecord
|
} from "@/api/productionManagement/workOrder.js";
|
import {getUserProfile, userListNoPageByTenantId} from "@/api/system/user.js";
|
import QRCode from "qrcode";
|
import {getCurrentInstance, reactive, toRefs} from "vue";
|
import FilesDia from "./components/filesDia.vue";
|
import {getDeviceLedger} from "@/api/equipmentManagement/ledger.js";
|
|
const {proxy} = getCurrentInstance();
|
|
const currentUserId = ref("");
|
const deviceOptions = ref([])
|
const currentUserName = ref("");
|
|
const ensureCurrentUser = async () => {
|
if (currentUserId.value) return;
|
try {
|
const res = await getUserProfile();
|
if (res?.code === 200) {
|
currentUserId.value = String(res?.data?.userId ?? "");
|
currentUserName.value = String(res?.data?.nickName ?? "");
|
}
|
} catch (err) {
|
console.error("获取用户信息失败", err);
|
}
|
};
|
|
// 机台获取
|
const getDeviceList = () => {
|
getDeviceLedger().then(res => {
|
deviceOptions.value = Array.isArray(res?.data) ? res.data : []
|
})
|
}
|
|
const handleDeviceChange = (val) => {
|
const device = deviceOptions.value.find(item => item.id === val)
|
reportForm.deviceName = device?.deviceName || ""
|
reportForm.deviceId = val || ""
|
}
|
|
const normalizeArray = (val) => {
|
if (val === null || val === undefined) return [];
|
if (Array.isArray(val)) return val;
|
if (typeof val === "string") {
|
return val
|
.split(/[,,;;\s]+/g)
|
.map((s) => s.trim())
|
.filter(Boolean);
|
}
|
return [val];
|
};
|
|
const isCurrentUserInUserIds = (row) => {
|
const uid = String(currentUserId.value || "");
|
if (!uid) return false;
|
|
const ids = normalizeArray(row?.userIds)
|
.map(id => String(id))
|
.filter(Boolean);
|
|
return ids.includes(uid);
|
};
|
|
const canOperateByReportWorker = computed(() => {
|
return (row) => isCurrentUserReportWorker(row);
|
});
|
|
const isRowScheduled = (row) => {
|
const ids = normalizeArray(row?.userIds)
|
.map((val) => String(val))
|
.filter(Boolean);
|
if (!ids.length) return false;
|
return ids.some((val) => val !== "0");
|
};
|
|
const buildBaseScheduleUsersByRow = (row) => {
|
if (!row) return [];
|
|
if (Array.isArray(row?.reportWorkerList) && row.reportWorkerList.length > 0) {
|
const mapped = row.reportWorkerList
|
.map((item) => {
|
const userId = String(item?.userId ?? item?.id ?? "").trim();
|
const nickName = String(item?.userName ?? item?.nickName ?? "").trim();
|
return { userId, nickName: nickName || userId };
|
})
|
.filter((item) => item.userId);
|
const uniq = new Map();
|
mapped.forEach((item) => uniq.set(String(item.userId), item));
|
return Array.from(uniq.values());
|
}
|
|
const configuredIds = [
|
row.reportUserIds,
|
row.reportWorkerIds,
|
row.userIdList,
|
row.reportUserId,
|
row.userId,
|
]
|
.flatMap((v) => normalizeArray(v))
|
.map((v) => String(v).trim())
|
.filter(Boolean);
|
|
if (configuredIds.length > 0) {
|
const uniqIds = Array.from(new Set(configuredIds));
|
return uniqIds.map((id) => {
|
const user = userTeamOptions.value.find((u) => String(u.userId) === String(id));
|
return { userId: String(id), nickName: user?.nickName || String(id) };
|
});
|
}
|
|
return userTeamOptions.value.map((u) => ({
|
userId: String(u.userId),
|
nickName: u.nickName,
|
}));
|
};
|
|
const resolveScheduleUserName = (userId) => {
|
const uid = String(userId ?? "").trim();
|
if (!uid) return "";
|
const inBase = baseScheduleUsers.value.find((u) => String(u.userId) === uid);
|
if (inBase?.nickName) return inBase.nickName;
|
const inTeam = userTeamOptions.value.find((u) => String(u.userId) === uid);
|
return inTeam?.nickName || uid;
|
};
|
|
const buildScheduleUserOptionsByDeviceId = (deviceId) => {
|
const device = deviceOptions.value.find((item) => String(item.id) === String(deviceId));
|
|
const operatorIds = device?.operatorId
|
? String(device.operatorId)
|
.split(/[,,;;\s]+/g)
|
.map((id) => id.trim())
|
.filter(Boolean)
|
: [];
|
|
if (!operatorIds.length) {
|
return [...baseScheduleUsers.value];
|
}
|
|
return baseScheduleUsers.value.filter((user) =>
|
operatorIds.includes(String(user.userId))
|
);
|
};
|
|
const createScheduleRow = (preset = {}) => {
|
const deviceId =
|
preset?.deviceId === null || preset?.deviceId === undefined
|
? ""
|
: String(preset.deviceId);
|
|
const userOptions = deviceId
|
? buildScheduleUserOptionsByDeviceId(deviceId)
|
: [...baseScheduleUsers.value];
|
|
const userIds = normalizeArray(preset?.userIds)
|
.map((val) => String(val))
|
.filter(Boolean)
|
.filter((uid) => userOptions.some((user) => String(user.userId) === String(uid)));
|
|
return {
|
id: preset?.id ?? "",
|
deviceId,
|
deviceName: preset?.deviceName ?? "",
|
userIds,
|
userOptions,
|
startTime: preset?.startTime ?? dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
deleting: false,
|
};
|
};
|
|
const addScheduleRow = (preset) => {
|
if (preset) {
|
scheduleRows.value.push(createScheduleRow(preset));
|
return;
|
}
|
|
const first = scheduleRows.value[0] || {};
|
const deviceId = first.deviceId || currentReportRowData.value?.deviceId || "";
|
const userOptions = deviceId ? buildScheduleUserOptionsByDeviceId(deviceId) : [...baseScheduleUsers.value];
|
const defaultUserId = userOptions[0]?.userId ? String(userOptions[0].userId) : "";
|
|
scheduleRows.value.push(
|
createScheduleRow({
|
id: "",
|
deviceId,
|
deviceName: first.deviceName || currentReportRowData.value?.deviceName || "",
|
userIds: defaultUserId ? [defaultUserId] : [],
|
startTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
|
})
|
);
|
};
|
|
const refreshScheduleRows = async () => {
|
const workOrderRow = currentReportRowData.value;
|
|
if (!workOrderRow?.id) {
|
schedulePage.current = 1;
|
schedulePage.total = 0;
|
scheduleRows.value = [createScheduleRow({})];
|
return;
|
}
|
|
scheduleLoading.value = true;
|
try {
|
const res = await productionMachineRecordListPage({
|
workOrderId: workOrderRow.id,
|
current: schedulePage.current,
|
size: schedulePage.size,
|
});
|
|
const records = Array.isArray(res?.data?.records) ? res.data.records : [];
|
const apiTotal = Number(res?.data?.total);
|
schedulePage.total =
|
Number.isFinite(apiTotal) && apiTotal > 0
|
? apiTotal
|
: records.length;
|
|
const lastPage = Math.max(1, Math.ceil((schedulePage.total || 0) / schedulePage.size));
|
if (schedulePage.current > lastPage) {
|
schedulePage.current = lastPage;
|
await refreshScheduleRows();
|
return;
|
}
|
|
const rows = records.map(record => mapMachineRecordToScheduleRow(record));
|
scheduleRows.value = rows.length > 0 ? rows : [createScheduleRow({})];
|
} catch (error) {
|
console.error("获取排产记录失败", error);
|
schedulePage.total = 0;
|
scheduleRows.value = [createScheduleRow({})];
|
ElMessage.error("获取排产记录失败");
|
} finally {
|
scheduleLoading.value = false;
|
}
|
};
|
|
const removeScheduleRow = async (row) => {
|
if (!row) return;
|
|
if (!row.id) {
|
scheduleRows.value = scheduleRows.value.filter((item) => item !== row);
|
if (!scheduleRows.value.length) {
|
scheduleRows.value = [createScheduleRow({})];
|
}
|
return;
|
}
|
|
try {
|
await ElMessageBox.confirm("确定删除这条排产记录吗?", "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
});
|
} catch {
|
return;
|
}
|
|
row.deleting = true;
|
try {
|
const res = await deleteProductionMachineRecord([row.id]);
|
if (res?.code !== undefined && res.code !== 200) {
|
ElMessage.error(res?.msg || "删除失败");
|
return;
|
}
|
|
ElMessage.success("删除成功");
|
await refreshScheduleRows();
|
getList();
|
} catch (error) {
|
console.error("删除排产记录失败", error);
|
ElMessage.error("删除失败,请重试");
|
} finally {
|
row.deleting = false;
|
}
|
};
|
|
const handleScheduleUserChange = (userIds, row) => {
|
row.userIds = normalizeArray(userIds).map((val) => String(val)).filter(Boolean);
|
};
|
|
const handleScheduleDeviceChange = (deviceId, row) => {
|
const device = deviceOptions.value.find((item) => String(item.id) === String(deviceId));
|
|
row.deviceId = deviceId === null || deviceId === undefined ? "" : String(deviceId);
|
row.deviceName = device?.deviceName || "";
|
row.userOptions = row.deviceId ? buildScheduleUserOptionsByDeviceId(row.deviceId) : [...baseScheduleUsers.value];
|
|
row.userIds = normalizeArray(row.userIds)
|
.map((uid) => String(uid))
|
.filter((uid) => row.userOptions.some((user) => String(user.userId) === String(uid)));
|
};
|
|
const handleSchedulePagination = ({page, limit}) => {
|
schedulePage.current = page;
|
schedulePage.size = limit;
|
refreshScheduleRows();
|
};
|
|
const validateScheduleRows = () => {
|
if (!scheduleRows.value.length) {
|
ElMessage.warning("请至少添加一条上机信息");
|
return false;
|
}
|
|
for (let index = 0; index < scheduleRows.value.length; index += 1) {
|
const row = scheduleRows.value[index];
|
|
if (!row.deviceId) {
|
ElMessage.warning(`第${index + 1}行请选择机台`);
|
return false;
|
}
|
|
if (!Array.isArray(row.userIds) || row.userIds.length === 0) {
|
ElMessage.warning(`第${index + 1}行请选择上机人`);
|
return false;
|
}
|
|
if (!row.startTime) {
|
ElMessage.warning(`第${index + 1}行请选择上机时间`);
|
return false;
|
}
|
|
if (!dayjs(row.startTime).isValid()) {
|
ElMessage.warning(`第${index + 1}行上机时间格式不正确`);
|
return false;
|
}
|
}
|
|
|
return true;
|
};
|
|
const buildMachineRecordPayload = (workOrderRow, scheduleRow, sortIndex = 0) => {
|
const processId =
|
workOrderRow?.processId ??
|
workOrderRow?.productProcessRouteItemId ??
|
reportForm.productProcessRouteItemId;
|
|
const operatorIds = normalizeArray(scheduleRow?.userIds)
|
.map((val) => String(val).trim())
|
.filter(Boolean)
|
.join(",");
|
|
const nickName = normalizeArray(scheduleRow?.userIds)
|
.map((uid) => resolveScheduleUserName(uid))
|
.filter(Boolean)
|
.join(",");
|
|
const payload = {
|
workOrderId: workOrderRow?.id,
|
processId,
|
machineId: scheduleRow.deviceId ? Number(scheduleRow.deviceId) : undefined,
|
deviceName: scheduleRow.deviceName,
|
operatorId: operatorIds || undefined,
|
nickName: nickName || "",
|
machineStartTime: scheduleRow.startTime,
|
reportStatus: false,
|
remark: `排产序号:${sortIndex + 1}`,
|
};
|
|
if (scheduleRow.id) {
|
payload.id = scheduleRow.id;
|
}
|
|
return payload;
|
};
|
|
const indexMethod = (index) => {
|
return (schedulePage.current - 1) * schedulePage.size + index + 1;
|
};
|
|
const mapMachineRecordToScheduleRow = (record) => {
|
const id = record?.id ?? "";
|
const deviceId = record?.machineId ?? record?.deviceId ?? "";
|
const deviceName = record?.deviceName ?? record?.machineName ?? "";
|
const startTime = record?.machineStartTime ?? record?.startTime ?? "";
|
const userIds = normalizeArray(record?.operatorId ?? record?.operatorIds ?? record?.userId)
|
.map((val) => String(val))
|
.filter(Boolean);
|
|
return createScheduleRow({
|
id,
|
deviceId: deviceId === null || deviceId === undefined ? "" : String(deviceId),
|
deviceName,
|
userIds,
|
startTime,
|
});
|
};
|
|
const buildScheduleRowsFromRecords = (records) => {
|
const list = Array.isArray(records) ? records : [];
|
const grouped = new Map();
|
|
list.forEach((record) => {
|
const row = mapMachineRecordToScheduleRow(record);
|
const key = `${row.deviceId}__${row.startTime}__${row.deviceName}`;
|
|
if (!grouped.has(key)) {
|
grouped.set(key, row);
|
return;
|
}
|
|
const existing = grouped.get(key);
|
existing.ids = Array.from(new Set([existing.id, row.id].filter(Boolean)));
|
existing.userIds = Array.from(
|
new Set([...(existing?.userIds || []), ...(row?.userIds || [])].map((v) => String(v)))
|
).filter(Boolean);
|
|
if (!existing.deviceName && row.deviceName) existing.deviceName = row.deviceName;
|
});
|
|
return Array.from(grouped.values()).sort(
|
(a, b) => dayjs(a.startTime).valueOf() - dayjs(b.startTime).valueOf()
|
);
|
};
|
|
const openScheduleDialog = async (row) => {
|
currentReportRowData.value = row;
|
baseScheduleUsers.value = buildBaseScheduleUsersByRow(row);
|
userTemp.value = [...baseScheduleUsers.value];
|
schedulePage.current = 1;
|
scheduleRows.value = [];
|
scheduleDialogVisible.value = true;
|
|
await refreshScheduleRows();
|
};
|
|
const handleSaveSchedule = async () => {
|
if (scheduleSaving.value) return;
|
if (!validateScheduleRows()) return;
|
|
const workOrderRow = currentReportRowData.value;
|
if (!workOrderRow?.id) {
|
ElMessage.warning("缺少工单信息,无法保存排产");
|
return;
|
}
|
|
const sortedRows = [...scheduleRows.value].sort((a, b) => dayjs(a.startTime).valueOf() - dayjs(b.startTime).valueOf());
|
|
scheduleSaving.value = true;
|
try {
|
const productionMachineRecord = sortedRows.map((scheduleRow, index) =>
|
buildMachineRecordPayload(workOrderRow, scheduleRow, index)
|
);
|
|
const res = await addProductionMachineRecord({productionMachineRecord});
|
if (res?.code !== undefined && res.code !== 200) {
|
ElMessage.error(res?.msg || "保存排产失败");
|
return;
|
}
|
|
proxy.$modal.msgSuccess("排产已保存");
|
await refreshScheduleRows();
|
getList();
|
} catch (error) {
|
console.error("保存排产失败", error);
|
ElMessage.error("保存排产失败,请重试");
|
} finally {
|
scheduleSaving.value = false;
|
}
|
};
|
|
const tableColumn = ref([
|
{
|
label: "工单类型",
|
prop: "workOrderType",
|
width: "80",
|
},
|
{
|
label: "工单编号",
|
prop: "workOrderNo",
|
width: "140",
|
},
|
{
|
label: "生产订单号",
|
prop: "productOrderNpsNo",
|
width: "140",
|
},
|
{
|
label: "产品名称",
|
prop: "productName",
|
width: "140",
|
},
|
{
|
label: "规格",
|
prop: "model",
|
},
|
{
|
label: "单位",
|
prop: "unit",
|
},
|
{
|
label: "工序名称",
|
prop: "processName",
|
},
|
{
|
label: "需求数量",
|
prop: "planQuantity",
|
width: "140",
|
},
|
{
|
label: "完成数量",
|
prop: "completeQuantity",
|
width: "140",
|
},
|
{
|
label: "完成进度",
|
prop: "completionStatus",
|
dataType: "slot",
|
slot: "completionStatus",
|
width: "140",
|
},
|
{
|
label: "计划开始时间",
|
prop: "planStartTime",
|
width: "140",
|
},
|
{
|
label: "计划结束时间",
|
prop: "planEndTime",
|
width: "140",
|
},
|
{
|
label: "实际开始时间",
|
prop: "actualStartTime",
|
width: "140",
|
},
|
{
|
label: "实际结束时间",
|
prop: "actualEndTime",
|
width: "140",
|
},
|
{
|
label: "操作",
|
width: "200",
|
align: "center",
|
dataType: "action",
|
fixed: "right",
|
operation: [
|
{
|
name: "编辑",
|
clickFun: row => {
|
handleEdit(row);
|
},
|
},
|
{
|
name: "流转卡",
|
clickFun: row => {
|
downloadAndPrintWorkOrder(row);
|
},
|
},
|
{
|
name: "附件",
|
clickFun: row => {
|
openWorkOrderFiles(row);
|
},
|
},
|
{
|
name: "报工",
|
clickFun: row => {
|
showReportDialog(row);
|
},
|
// 用户当前id
|
disabled: row => row.completeQuantity !==0 ||
|
!isCurrentUserInUserIds(row)
|
},
|
{
|
name: "生产排产",
|
clickFun: row => {
|
if (!row.canSchedule) {
|
ElMessage.warning("当前用户不在该工序人员中,不能生产排产");
|
return;
|
}
|
openScheduleDialog(row);
|
},
|
disabled: row => !row.canSchedule
|
}
|
// {
|
// name:"审核",
|
// color: "#f56c6c",
|
// clickFun: row => {
|
// handleAudit(row);
|
// },
|
// disabled: row => Number(row?.auditStatus) === 1,
|
// }
|
],
|
},
|
]);
|
const tableData = ref([]);
|
const tableLoading = ref(false);
|
const scheduleRows = ref([]);
|
const scheduleLoading = ref(false);
|
const scheduleSaving = ref(false);
|
const schedulePage = reactive({
|
current: 1,
|
size: 10,
|
total: 0,
|
});
|
const editDialogVisible = ref(false);
|
const transferCardVisible = ref(false);
|
const transferCardQrUrl = ref("");
|
const scheduleDialogVisible = ref(false);
|
const transferCardRowData = ref(null);
|
const baseScheduleUsers = ref([]);
|
const userTemp = ref([]);
|
const reportDialogVisible = ref(false);
|
const auditDialogVisible = ref(false);
|
const auditRowData = ref(null);
|
const auditTableData = ref([]);
|
const auditLoading = ref(false);
|
const workOrderFilesRef = ref(null);
|
const reportFormRef = ref(null);
|
const userOptions = ref([]);
|
const userTeamOptions = ref([]);
|
const reportForm = reactive({
|
planQuantity: 0,
|
quantity: null,
|
scrapQty: null,
|
startTime: "",
|
endTime: "",
|
userName: "",
|
workOrderId: "",
|
reportWork: "",
|
productProcessRouteItemId: "",
|
userId: "",
|
productMainId: null,
|
teamList: [],
|
deviceId: null,
|
});
|
|
// 本次生产数量验证规则
|
const validateQuantity = (rule, value, callback) => {
|
if (value === null || value === undefined || value === "") {
|
callback(new Error("请输入本次生产数量"));
|
return;
|
}
|
const num = Number(value);
|
// 整数且大于等于1
|
if (isNaN(num) || !Number.isInteger(num) || num < 1) {
|
callback(new Error("本次生产数量必须大于等于1"));
|
return;
|
}
|
callback();
|
};
|
|
// 报废数量验证规则
|
const validateScrapQty = (rule, value, callback) => {
|
if (value === null || value === undefined || value === "") {
|
callback();
|
return;
|
}
|
const num = Number(value);
|
// 整数且大于等于0
|
if (isNaN(num) || !Number.isInteger(num) || num < 0) {
|
callback(new Error("报废数量必须大于等于0"));
|
return;
|
}
|
callback();
|
};
|
|
// 审核
|
const handleAudit = (row) => {
|
if (Number(row?.auditStatus) === 1) {
|
ElMessage.warning("该工单已审核");
|
return;
|
}
|
auditRowData.value = row;
|
const workOrderNo = row?.workOrderNo;
|
const related = workOrderNo
|
? tableData.value.filter(r => r?.workOrderNo === workOrderNo)
|
: [];
|
auditTableData.value = related.length > 0 ? related : [row];
|
auditDialogVisible.value = true;
|
};
|
|
const submitAudit = async (result) => {
|
const current = auditRowData.value;
|
if (!current) return;
|
if (auditLoading.value) return;
|
|
const confirmText = result === 1 ? "确定审核通过吗?" : "确定审核不通过吗?";
|
try {
|
await ElMessageBox.confirm(confirmText, "提示", {
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
});
|
} catch {
|
return;
|
}
|
|
auditLoading.value = true;
|
try {
|
const updates = auditTableData.value.map(item => {
|
const id = item?.id;
|
if (!id) return Promise.resolve();
|
return updateProductWorkOrder({id, auditStatus: result});
|
});
|
await Promise.all(updates);
|
ElMessage.success("审核成功");
|
auditDialogVisible.value = false;
|
getList();
|
} finally {
|
auditLoading.value = false;
|
}
|
};
|
|
// 查看详情
|
const handleView = (row) => {
|
const {workOrderId} = row;
|
router.push({
|
path: "/productionManagement/workOrderDetail",
|
query: {workOrderId},
|
});
|
}
|
|
// 验证规则
|
const reportFormRules = {
|
quantity: [{required: true, validator: validateQuantity, trigger: "blur"}],
|
scrapQty: [{validator: validateScrapQty, trigger: "blur"}],
|
startTime: [{required: true, message: "请选择开始时间", trigger: "change"}],
|
endTime: [{required: true, message: "请选择结束时间", trigger: "change"}],
|
auditUserId: [{required: true, message: "请选择审核人", trigger: "change"}],
|
teamList: [{required: true, message: "请选择班组", trigger: "change"}],
|
deviceId: [{required: true, message: "请选择设备", trigger: "change"}],
|
};
|
|
// 处理本次生产数量输入,限制必须大于等于1
|
const handleQuantityInput = value => {
|
if (value === "" || value === null || value === undefined) {
|
reportForm.quantity = null;
|
return;
|
}
|
const num = Number(value);
|
if (isNaN(num)) {
|
return;
|
}
|
// 如果小于1,清除
|
if (num < 1) {
|
reportForm.quantity = null;
|
return;
|
}
|
// 如果是小数取整数部分
|
if (!Number.isInteger(num)) {
|
const intValue = Math.floor(num);
|
// 如果取整后小于1,清除
|
if (intValue < 1) {
|
reportForm.quantity = null;
|
return;
|
}
|
reportForm.quantity = intValue;
|
return;
|
}
|
reportForm.quantity = num;
|
};
|
|
// 处理报废数量
|
const handleScrapQtyInput = value => {
|
if (value === "" || value === null || value === undefined) {
|
reportForm.scrapQty = null;
|
return;
|
}
|
const num = Number(value);
|
// 如果是NaN,保持原值
|
if (isNaN(num)) {
|
return;
|
}
|
// 如果是负数,清除输入
|
if (num < 0) {
|
reportForm.scrapQty = null;
|
return;
|
}
|
// 如果是小数,取整数部分
|
if (!Number.isInteger(num)) {
|
reportForm.scrapQty = Math.floor(num);
|
return;
|
}
|
// 有效的非负整数(包括0)
|
reportForm.scrapQty = num;
|
};
|
const currentReportRowData = ref(null);
|
const page = reactive({
|
current: 1,
|
size: 100,
|
total: 0,
|
});
|
|
const data = reactive({
|
searchForm: {
|
workOrderNo: "",
|
},
|
});
|
const {searchForm} = toRefs(data);
|
const toProgressPercentage = val => {
|
const n = Number(val);
|
if (!Number.isFinite(n)) return 0;
|
if (n <= 0) return 0;
|
if (n >= 100) return 100;
|
return Math.round(n);
|
};
|
const progressColor = percentage => {
|
const p = toProgressPercentage(percentage);
|
if (p < 30) return "#f56c6c";
|
if (p < 50) return "#e6a23c";
|
if (p < 80) return "#409eff";
|
return "#67c23a";
|
};
|
let editrow = ref(null);
|
|
// 查询列表
|
/** 搜索按钮操作 */
|
const handleQuery = () => {
|
page.current = 1;
|
getList();
|
};
|
const pagination = obj => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList();
|
};
|
const getList = async () => {
|
tableLoading.value = true;
|
|
try {
|
await ensureCurrentUser();
|
await processLists();
|
|
const params = { ...searchForm.value, ...page };
|
const res = await productWorkOrderPage(params);
|
|
const records = Array.isArray(res?.data?.records) ? res.data.records : [];
|
|
tableData.value = records.map(row => ({
|
...row,
|
canSchedule: canScheduleByWorkOrderNo(row)
|
}));
|
|
page.total = res?.data?.total || 0;
|
} finally {
|
tableLoading.value = false;
|
}
|
};
|
|
// 下载并打印工单流转卡(文件流)
|
const downloadAndPrintWorkOrder = async row => {
|
if (!row || !row.id) {
|
proxy.$modal.msgError("缺少工单ID,无法下载流转卡");
|
return;
|
}
|
const fileName = row.workOrderNo
|
? `工单流转卡_${row.workOrderNo}.xlsx`
|
: "工单流转卡.xlsx";
|
try {
|
// 调用接口,以 responseType: 'blob' 获取文件流
|
const blob = await downProductWorkOrder(row.id);
|
|
if (!blob) {
|
proxy.$modal.msgError("未获取到流转卡文件");
|
return;
|
}
|
|
// 创建 Blob URL
|
const fileBlob =
|
blob instanceof Blob
|
? blob
|
: new Blob([blob], {type: blob.type || "application/octet-stream"});
|
const url = window.URL.createObjectURL(fileBlob);
|
|
// 创建隐藏 iframe,用于触发浏览器打印
|
const iframe = document.createElement("iframe");
|
iframe.style.position = "fixed";
|
iframe.style.right = "0";
|
iframe.style.bottom = "0";
|
iframe.style.width = "0";
|
iframe.style.height = "0";
|
iframe.style.border = "0";
|
iframe.src = url;
|
document.body.appendChild(iframe);
|
|
iframe.onload = () => {
|
try {
|
iframe.contentWindow?.focus();
|
iframe.contentWindow?.print();
|
} catch (e) {
|
console.error("自动调用打印失败", e);
|
// 退而求其次,打开新窗口由用户手动打印
|
window.open(url);
|
}
|
};
|
} catch (e) {
|
console.error("下载工单流转卡失败", e);
|
proxy.$modal.msgError("下载工单流转卡失败");
|
}
|
};
|
|
const showTransferCard = async row => {
|
transferCardRowData.value = row;
|
const qrContent = String(row.id);
|
|
transferCardQrUrl.value = await QRCode.toDataURL(qrContent);
|
transferCardVisible.value = true;
|
};
|
|
const printTransferCard = () => {
|
window.print();
|
};
|
|
const handleEdit = row => {
|
// if (!isCurrentUserReportWorker(row)) {
|
// ElMessage.warning("当前用户不是该工单的报工人,无法编辑");
|
// return;
|
// }
|
editrow.value = JSON.parse(JSON.stringify(row));
|
editDialogVisible.value = true;
|
};
|
|
const handleUpdate = () => {
|
updateProductWorkOrder(editrow.value)
|
.then(res => {
|
proxy.$modal.msgSuccess("提交成功");
|
editDialogVisible.value = false;
|
getList();
|
})
|
.catch(() => {
|
ElMessageBox.alert("修改失败", "提示", {
|
confirmButtonText: "确定",
|
});
|
});
|
};
|
|
const showReportDialog = row => {
|
currentReportRowData.value = row;
|
reportForm.planQuantity = row.planQuantity - row.completeQuantity;
|
reportForm.quantity = row.quantity !== undefined && row.quantity !== null ? row.quantity : null;
|
reportForm.productProcessRouteItemId = row.productProcessRouteItemId;
|
reportForm.workOrderId = row.id;
|
reportForm.reportWork = row.reportWork;
|
reportForm.productMainId = row.productMainId;
|
reportForm.startTime = "";
|
reportForm.endTime = "";
|
reportForm.replenishQty = 0;
|
reportForm.teamList = [];
|
reportForm.scrapQty = 0;
|
reportForm.userIds = row.userIds || [];
|
|
const ids = (row.userIds || "")
|
.split(",")
|
.map(id => id.trim())
|
.filter(Boolean);
|
|
reportForm.userIdsList = userTeamOptions.value
|
.filter(item => ids.includes(String(item.userId)))
|
.map(item => ({
|
userId: item.userId,
|
nickName: item.nickName
|
}));
|
|
|
nextTick(() => {
|
reportFormRef.value?.clearValidate();
|
});
|
ensureCurrentUser().then(() => {
|
reportForm.userId = currentUserId.value;
|
reportForm.userName = currentUserName.value;
|
});
|
|
reportDialogVisible.value = true;
|
};
|
|
const openWorkOrderFiles = row => {
|
workOrderFilesRef.value?.openDialog(row);
|
};
|
|
const handleReport = () => {
|
reportFormRef.value?.validate(valid => {
|
if (!valid) {
|
return false;
|
}
|
|
if (reportForm.planQuantity <= 0) {
|
ElMessageBox.alert("待生产数量为0,无法报工", "提示", {
|
confirmButtonText: "确定",
|
});
|
return;
|
}
|
|
// 验证本次生产数量
|
if (
|
reportForm.quantity === null ||
|
reportForm.quantity === undefined ||
|
reportForm.quantity === ""
|
) {
|
ElMessageBox.alert("请输入本次生产数量", "提示", {
|
confirmButtonText: "确定",
|
});
|
return;
|
}
|
|
const quantity = Number(reportForm.quantity);
|
const scrapQty =
|
reportForm.scrapQty === null ||
|
reportForm.scrapQty === undefined ||
|
reportForm.scrapQty === ""
|
? 0
|
: Number(reportForm.scrapQty);
|
|
// 本次生产数量
|
if (isNaN(quantity) || !Number.isInteger(quantity) || quantity < 1) {
|
ElMessageBox.alert("本次生产数量必须大于等于1", "提示", {
|
confirmButtonText: "确定",
|
});
|
return;
|
}
|
|
// 报废数量必须是整数且大于等于0
|
if (isNaN(scrapQty) || !Number.isInteger(scrapQty) || scrapQty < 0) {
|
ElMessageBox.alert("报废数量必须大于等于0", "提示", {
|
confirmButtonText: "确定",
|
});
|
return;
|
}
|
|
if (quantity > reportForm.planQuantity) {
|
ElMessageBox.alert("本次生产数量不能超过待生产数量", "提示", {
|
confirmButtonText: "确定",
|
});
|
return;
|
}
|
|
const submitData = {
|
...reportForm,
|
quantity: quantity,
|
scrapQty: scrapQty,
|
};
|
addProductMain(submitData).then(res => {
|
if (res.code === 200) {
|
proxy.$modal.msgSuccess("报工成功");
|
reportDialogVisible.value = false;
|
getList();
|
} else {
|
ElMessageBox.alert(res.msg || "报工失败", "提示", {
|
confirmButtonText: "确定",
|
});
|
}
|
});
|
});
|
};
|
|
// 获取用户列表
|
const getUserList = () => {
|
userListNoPageByTenantId()
|
.then(res => {
|
if (res.code === 200) {
|
const list = Array.isArray(res.data) ? res.data : []
|
userOptions.value = [
|
{nickName: "任意用户", userId: "-1"},
|
...deepClone(list)
|
]
|
userTeamOptions.value = deepClone(list)
|
}
|
})
|
.catch(err => {
|
console.error("获取用户列表失败", err);
|
});
|
};
|
|
// 用户选择变化时更新 userName
|
const handleUserChange = userId => {
|
if (userId) {
|
const selectedUser = userOptions.value.find(user => user.userId === userId);
|
if (selectedUser) {
|
reportForm.userName = selectedUser.nickName;
|
}
|
} else {
|
reportForm.userName = "";
|
}
|
};
|
// 审核人
|
const handleReviewerIdChange = userId => {
|
if (userId) {
|
const selectedUser = userOptions.value.find(user => user.userId === userId);
|
if (selectedUser) {
|
reportForm.auditUserName = selectedUser.nickName;
|
}
|
} else {
|
reportForm.auditUserName = "";
|
}
|
}
|
|
const processData = ref([]);
|
|
// 查询所有工序
|
const processLists = async () => {
|
console.log(processData.value)
|
if (processData.value.length > 0) {
|
return processData.value;
|
}
|
const res = await processList();
|
|
processData.value = Array.isArray(res?.data) ? res.data : [];
|
return processData.value;
|
};
|
|
// 判断当前用户是否能排产
|
const canScheduleByWorkOrderNo = (row) => {
|
if (!row) return false;
|
|
const uid = String(currentUserId.value || "");
|
if (!uid) return false;
|
|
const currentProcess = processData.value.find(item =>
|
String(item.id) === String(row.processId)
|
);
|
|
if (!currentProcess) return false;
|
|
const ids = normalizeArray(currentProcess.userIds)
|
.map(id => String(id).trim())
|
.filter(Boolean);
|
|
return ids.includes(uid);
|
};
|
|
onMounted(async () => {
|
await ensureCurrentUser();
|
await processLists();
|
await getList();
|
getUserList();
|
getDeviceList();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.search_form {
|
margin-bottom: 20px;
|
|
.search-row {
|
display: flex;
|
gap: 20px;
|
align-items: center;
|
|
.search-item {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
}
|
}
|
}
|
|
.transfer-card-title {
|
font-size: 24px;
|
font-weight: bold;
|
text-align: center;
|
margin-bottom: 20px;
|
}
|
|
.transfer-card-container {
|
display: flex;
|
gap: 20px;
|
height: 350px;
|
|
.transfer-card-info {
|
flex: 1;
|
overflow: auto;
|
|
.info-group {
|
width: 50%;
|
float: left;
|
}
|
|
.info-item {
|
display: flex;
|
margin-bottom: 15px;
|
|
.info-label {
|
width: 120px;
|
font-weight: bold;
|
margin-right: 20px;
|
}
|
|
.info-value {
|
flex: 1;
|
}
|
}
|
}
|
|
.transfer-card-qr {
|
width: 240px;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: flex-start;
|
}
|
}
|
</style>
|
|
<style lang="scss">
|
@media print {
|
@page {
|
size: landscape;
|
}
|
body * {
|
visibility: hidden;
|
}
|
.el-dialog__wrapper,
|
.el-dialog,
|
.el-dialog__body,
|
.transfer-card-title,
|
.transfer-card-container,
|
.transfer-card-container *,
|
.info-item,
|
.info-label,
|
.info-value {
|
visibility: visible;
|
}
|
.print-button-container {
|
visibility: hidden;
|
}
|
.el-dialog__wrapper {
|
position: absolute;
|
top: 0;
|
left: 0;
|
right: 0;
|
margin: 0;
|
}
|
.el-dialog {
|
width: 100% !important;
|
max-width: 800px;
|
margin: 0 auto !important;
|
}
|
.el-dialog__header,
|
.el-dialog__footer {
|
display: none;
|
}
|
.el-dialog__body {
|
padding: 20px;
|
}
|
.transfer-card-container {
|
height: auto;
|
display: flex;
|
gap: 20px;
|
}
|
.transfer-card-info {
|
flex: 1;
|
|
.info-group {
|
width: 100%;
|
float: none;
|
margin-bottom: 20px;
|
}
|
|
.info-item {
|
display: flex;
|
margin-bottom: 10px;
|
|
.info-label {
|
width: 100px;
|
font-weight: bold;
|
margin-right: 15px;
|
white-space: nowrap;
|
}
|
|
.info-value {
|
flex: 1;
|
word-break: break-word;
|
}
|
}
|
}
|
.transfer-card-qr {
|
width: 160px;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: flex-start;
|
}
|
.qr-container img {
|
width: 140px !important;
|
height: 140px !important;
|
}
|
}
|
</style>
|