生产领料、BOM管理、生产工单适配手机端

This commit is contained in:
2026-03-12 13:58:58 +08:00
parent 52f1a1cda2
commit 0432e2430a
6 changed files with 1863 additions and 1120 deletions

View File

@@ -1,146 +1,243 @@
<template>
<Dialog title="查看领料单" v-model="dialogVisible" width="80%">
<Descriptions :schema="schema" :data="detailData" />
<el-drawer
v-model="dialogVisible"
title="查看领料单"
direction="rtl"
size="100%"
:close-on-press-escape="true"
:destroy-on-close="true"
:append-to-body="true"
class="mobile-form-drawer"
>
<div class="mobile-form">
<!-- 基本信息 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">基本信息</div>
<div class="mobile-info-list">
<div class="mobile-info-row" v-for="item in schema" :key="item.field">
<span class="mobile-info-row__label">{{ item.label }}</span>
<span class="mobile-info-row__value">
{{ item.formatter ? item.formatter(detailData[item.field]) : (detailData[item.field] || '-') }}
</span>
</div>
</div>
</div>
<el-divider content-position="left">领料明细</el-divider>
<el-table :data="detailData.items" border style="width: 100%" max-height="480">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="物料信息" min-width="240">
<template #default="{ row }">
<div class="text-13px text-gray-600">编码{{ row.materialCode }}</div>
<div class="text-13px">名称{{ row.materialName }}</div>
</template>
</el-table-column>
<el-table-column label="单位" prop="unit" width="80" />
<el-table-column label="计划数量" prop="planQuantity" width="120" />
<el-table-column label="实际数量" prop="actualQuantity" width="120" />
<el-table-column label="仓库" prop="warehouseName" width="140" />
<el-table-column label="过磅单号" prop="no" width="160" />
<el-table-column label="备注" prop="remark" min-width="160" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="{ row }">
<el-tooltip content="过磅详情" placement="top" v-if="row.purchaseId">
<el-button link type="primary" @click="openWeighDetail(row.purchaseId)">
<Icon icon="ep:document" />
</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<!-- 领料明细 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">领料明细</div>
<div class="mobile-item-list">
<div
v-for="(row, index) in detailData.items"
:key="index"
class="mobile-item-card"
>
<div class="mobile-item-card__header">
<span class="mobile-item-card__index">#{{ index + 1 }}</span>
<span class="mobile-item-card__name">{{ row.materialName || '未知物料' }}</span>
</div>
<div class="mobile-item-card__body">
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">物料编码</span>
<span class="mobile-item-card__info-value">{{ row.materialCode || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">单位</span>
<span class="mobile-item-card__info-value">{{ row.unit || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">计划数量</span>
<span class="mobile-item-card__info-value">{{ row.planQuantity }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">实际数量</span>
<span class="mobile-item-card__info-value">{{ row.actualQuantity || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">仓库</span>
<span class="mobile-item-card__info-value">{{ row.warehouseName || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">过磅单号</span>
<span class="mobile-item-card__info-value">{{ row.no || '-' }}</span>
</div>
<div class="mobile-item-card__info-row" v-if="row.remark">
<span class="mobile-item-card__info-label">备注</span>
<span class="mobile-item-card__info-value">{{ row.remark }}</span>
</div>
</div>
<div class="mobile-item-card__footer" v-if="row.purchaseId">
<el-button size="small" @click="openWeighDetail(row.purchaseId)">查看过磅详情</el-button>
</div>
</div>
<div v-if="detailData.items.length === 0" class="mobile-empty-tip">
暂无领料明细
</div>
</div>
</div>
<template #footer>
<el-button @click="dialogVisible = false">关闭</el-button>
</template>
</Dialog>
<!-- 底部操作按钮 -->
<div class="mobile-form__footer">
<el-button @click="dialogVisible = false"> </el-button>
</div>
</div>
</el-drawer>
<!-- 过磅单详情弹窗 -->
<Dialog title="过磅单详情" v-model="weighDialogVisible" width="50%">
<el-descriptions :column="2" border>
<el-descriptions-item label="过磅单号">{{ weighDetail?.no || '-' }}</el-descriptions-item>
<el-descriptions-item label="入库状态">{{
weighDetail?.inStatus === 1 ? '已入库' : '未入库'
}}</el-descriptions-item>
<el-descriptions-item label="产品">{{
weighDetail?.productName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="车牌">{{
weighDetail?.vehicleNumber || '-'
}}</el-descriptions-item>
<el-descriptions-item label="毛重">{{
weighDetail?.grossWeight != null ? weighDetail.grossWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="皮重">{{
weighDetail?.tareWeight != null ? weighDetail.tareWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="净重">{{
weighDetail?.netWeight != null ? weighDetail.netWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="计划重量">{{
weighDetail?.plannedWeight != null ? weighDetail.plannedWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="杂质率">{{
weighDetail?.impurityRate != null ? (weighDetail.impurityRate * 100).toFixed(2) + '%' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="供应商">{{
weighDetail?.supplierName || '-'
}}</el-descriptions-item>
<el-descriptions-item label="供应商类型">{{
weighDetail?.supplierName2 || '-'
}}</el-descriptions-item>
<el-descriptions-item label="身份证号">{{
weighDetail?.idCardNumber || '-'
}}</el-descriptions-item>
<el-descriptions-item label="司机">{{ weighDetail?.driver || '-' }}</el-descriptions-item>
<el-descriptions-item label="管理员">{{
weighDetail?.administrator || '-'
}}</el-descriptions-item>
<el-descriptions-item label="过磅员">{{ weighDetail?.weigher || '-' }}</el-descriptions-item>
<el-descriptions-item label="产地证编号">{{
weighDetail?.originCertificateNumber || '-'
}}</el-descriptions-item>
<el-descriptions-item label="附件">
<template v-if="weighDetail?.fileUrl">
<a :href="weighDetail.fileUrl" target="_blank">查看附件</a>
</template>
<template v-else>-</template>
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">{{
weighDetail?.remark || '-'
}}</el-descriptions-item>
<el-descriptions-item label="创建时间" :span="2">{{
weighDetail?.createTime ? formatDate(weighDetail.createTime) : '-'
}}</el-descriptions-item>
</el-descriptions>
<el-drawer
v-model="weighDialogVisible"
title="过磅单详情"
direction="rtl"
size="100%"
:close-on-press-escape="true"
:destroy-on-close="true"
:append-to-body="true"
class="mobile-form-drawer"
>
<div class="mobile-form">
<!-- 基本信息 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">基本信息</div>
<div class="mobile-info-list">
<div class="mobile-info-row">
<span class="mobile-info-row__label">过磅单号</span>
<span class="mobile-info-row__value">{{ weighDetail?.no || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">入库状态</span>
<span class="mobile-info-row__value">
<el-tag :type="weighDetail?.inStatus === 1 ? 'success' : 'warning'" size="small">
{{ weighDetail?.inStatus === 1 ? '已入库' : '未入库' }}
</el-tag>
</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">产品</span>
<span class="mobile-info-row__value">{{ weighDetail?.productName || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">车牌</span>
<span class="mobile-info-row__value">{{ weighDetail?.vehicleNumber || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">毛重</span>
<span class="mobile-info-row__value">{{ weighDetail?.grossWeight != null ? weighDetail.grossWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">皮重</span>
<span class="mobile-info-row__value">{{ weighDetail?.tareWeight != null ? weighDetail.tareWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">净重</span>
<span class="mobile-info-row__value">{{ weighDetail?.netWeight != null ? weighDetail.netWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">计划重量</span>
<span class="mobile-info-row__value">{{ weighDetail?.plannedWeight != null ? weighDetail.plannedWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">杂质率</span>
<span class="mobile-info-row__value">{{ weighDetail?.impurityRate != null ? (weighDetail.impurityRate * 100).toFixed(2) + '%' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">供应商</span>
<span class="mobile-info-row__value">{{ weighDetail?.supplierName || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">司机</span>
<span class="mobile-info-row__value">{{ weighDetail?.driver || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">管理员</span>
<span class="mobile-info-row__value">{{ weighDetail?.administrator || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">过磅员</span>
<span class="mobile-info-row__value">{{ weighDetail?.weigher || '-' }}</span>
</div>
<div class="mobile-info-row" v-if="weighDetail?.fileUrl">
<span class="mobile-info-row__label">附件</span>
<span class="mobile-info-row__value"><a :href="weighDetail.fileUrl" target="_blank">查看附件</a></span>
</div>
<div class="mobile-info-row" v-if="weighDetail?.remark">
<span class="mobile-info-row__label">备注</span>
<span class="mobile-info-row__value">{{ weighDetail.remark }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">创建时间</span>
<span class="mobile-info-row__value">{{ weighDetail?.createTime ? formatDate(weighDetail.createTime) : '-' }}</span>
</div>
</div>
</div>
<!-- 检验信息部分 - 仅当有检验数据时显示 -->
<template v-if="hasInspectionData">
<el-divider content-position="left">检验信息</el-divider>
<el-descriptions :column="2" border>
<el-descriptions-item label="取样日期">{{
weighDetail?.sampleDate ? formatDate(weighDetail.sampleDate) : '-'
}}</el-descriptions-item>
<el-descriptions-item label="取样地点">{{
weighDetail?.sampleLocation || '-'
}}</el-descriptions-item>
<el-descriptions-item label="代表重量">{{
weighDetail?.representativeWeight != null ? weighDetail.representativeWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="样品重量">{{
weighDetail?.sampleWeight != null ? weighDetail.sampleWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="杂质重量">{{
weighDetail?.impurityWeight != null ? weighDetail.impurityWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="沙土重量">{{
weighDetail?.sandWeight != null ? weighDetail.sandWeight + ' kg' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="感官">{{
weighDetail?.sensoryEvaluation || '-'
}}</el-descriptions-item>
<el-descriptions-item label="合格率">{{
weighDetail?.qualificationRate ? weighDetail.qualificationRate + '%' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="水分">{{
weighDetail?.moistureContent ? weighDetail.moistureContent + '%' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="含糖">{{
weighDetail?.sugarContent ? weighDetail.sugarContent + '%' : '-'
}}</el-descriptions-item>
<el-descriptions-item label="RA值">{{
weighDetail?.raValue || '-'
}}</el-descriptions-item>
<el-descriptions-item label="STV值">{{
weighDetail?.stvValue || '-'
}}</el-descriptions-item>
<el-descriptions-item label="化验员">{{
weighDetail?.labTechnician || '-'
}}</el-descriptions-item>
<el-descriptions-item label="检验备注" :span="2">{{
weighDetail?.inspectionRemark || '-'
}}</el-descriptions-item>
</el-descriptions>
</template>
</Dialog>
<!-- 检验信息部分 - 仅当有检验数据时显示 -->
<div class="mobile-form__section" v-if="hasInspectionData">
<div class="mobile-form__section-title">检验信息</div>
<div class="mobile-info-list">
<div class="mobile-info-row">
<span class="mobile-info-row__label">取样日期</span>
<span class="mobile-info-row__value">{{ weighDetail?.sampleDate ? formatDate(weighDetail.sampleDate) : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">取样地点</span>
<span class="mobile-info-row__value">{{ weighDetail?.sampleLocation || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">代表重量</span>
<span class="mobile-info-row__value">{{ weighDetail?.representativeWeight != null ? weighDetail.representativeWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">样品重量</span>
<span class="mobile-info-row__value">{{ weighDetail?.sampleWeight != null ? weighDetail.sampleWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">杂质重量</span>
<span class="mobile-info-row__value">{{ weighDetail?.impurityWeight != null ? weighDetail.impurityWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">沙土重量</span>
<span class="mobile-info-row__value">{{ weighDetail?.sandWeight != null ? weighDetail.sandWeight + ' kg' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">感官</span>
<span class="mobile-info-row__value">{{ weighDetail?.sensoryEvaluation || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">合格率</span>
<span class="mobile-info-row__value">{{ weighDetail?.qualificationRate ? weighDetail.qualificationRate + '%' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">水分</span>
<span class="mobile-info-row__value">{{ weighDetail?.moistureContent ? weighDetail.moistureContent + '%' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">含糖</span>
<span class="mobile-info-row__value">{{ weighDetail?.sugarContent ? weighDetail.sugarContent + '%' : '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">RA值</span>
<span class="mobile-info-row__value">{{ weighDetail?.raValue || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">STV值</span>
<span class="mobile-info-row__value">{{ weighDetail?.stvValue || '-' }}</span>
</div>
<div class="mobile-info-row">
<span class="mobile-info-row__label">化验员</span>
<span class="mobile-info-row__value">{{ weighDetail?.labTechnician || '-' }}</span>
</div>
<div class="mobile-info-row" v-if="weighDetail?.inspectionRemark">
<span class="mobile-info-row__label">检验备注</span>
<span class="mobile-info-row__value">{{ weighDetail.inspectionRemark }}</span>
</div>
</div>
</div>
<div class="mobile-form__footer">
<el-button @click="weighDialogVisible = false" style="width: 100%"> </el-button>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
@@ -246,3 +343,115 @@ defineExpose({
open
})
</script>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: center;
z-index: 10;
margin: 0 -4px;
.el-button {
width: 100%;
height: 40px;
font-size: 15px;
}
}
.mobile-info-list {
font-size: 13px;
}
.mobile-info-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid #f5f5f5;
&:last-child { border-bottom: none; }
&__label { color: #909399; flex-shrink: 0; margin-right: 12px; }
&__value { color: #303133; text-align: right; }
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #f9f9fb;
border-radius: 8px;
padding: 12px;
border: 1px solid #ebeef5;
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__index {
font-size: 12px;
color: #909399;
font-weight: 600;
}
&__name {
flex: 1;
font-size: 14px;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
}
&__footer {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #f0f0f0;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 20px 0;
font-size: 14px;
}
</style>

View File

@@ -1,21 +1,29 @@
<template>
<el-dialog
:title="formType === 'create' ? '新增工单' : '编辑工单'"
<el-drawer
v-model="visible"
:width="isMobile ? '100%' : '900px'"
:fullscreen="isMobile"
@close="handleClose"
:close-on-click-modal="false"
:title="formType === 'create' ? '新增工单' : '编辑工单'"
direction="rtl"
size="100%"
:close-on-press-escape="true"
:destroy-on-close="true"
:append-to-body="true"
class="mobile-form-drawer"
>
<el-form :model="form" :rules="rules" ref="formRef" :label-width="isMobile ? '80px' : '120px'" v-loading="formLoading">
<el-tabs v-model="activeTab">
<div class="mobile-form" v-loading="formLoading">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-position="top"
>
<!-- 基本信息 -->
<el-tab-pane label="基本信息" name="basic">
<div class="mobile-form__section">
<div class="mobile-form__section-title">基本信息</div>
<el-form-item label="产品" prop="productId">
<el-select
v-model="form.productId"
placeholder="请选择产品"
:class="isMobile ? '!w-full' : '!w-240px'"
style="width: 100%"
@change="handleProductChange"
>
<el-option
@@ -42,7 +50,7 @@
<el-select
v-model="form.routeId"
placeholder="请选择工序路线"
:class="isMobile ? '!w-full' : '!w-240px'"
style="width: 100%"
@change="handleRouteChange"
>
<el-option
@@ -63,7 +71,8 @@
:precision="2"
:step="0.1"
placeholder="请输入计划数量"
:class="isMobile ? '!w-full' : ''"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
<!-- <el-form-item label="优先级" prop="priority">-->
@@ -78,7 +87,7 @@
<el-form-item label="计划时间" prop="planTime">
<el-date-picker
v-model="form.planTime"
:type="isMobile ? 'datetimerange' : 'datetimerange'"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
:default-time="[
@@ -97,35 +106,52 @@
)
)
]"
:class="isMobile ? '!w-full' : '!w-380px'"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-tab-pane>
</div>
<!-- 工序信息 -->
<el-tab-pane label="工序信息" name="process">
<el-table :data="form.operations" border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="工序编码" prop="operationCode" min-width="120" align="center" />
<el-table-column label="工序名称" prop="operationName" min-width="120" align="center" />
<el-table-column
label="工时(分钟)"
prop="requiredTime"
min-width="120"
align="center"
/>
</el-table>
</el-tab-pane>
</el-tabs>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" :loading="loading" @click="submitForm">确定</el-button>
</template>
</el-dialog>
<div class="mobile-form__section">
<div class="mobile-form__section-title">工序信息</div>
<div class="mobile-item-list">
<div
v-for="(op, index) in form.operations"
:key="index"
class="mobile-item-card"
>
<div class="mobile-item-card__header">
<span class="mobile-item-card__index">#{{ index + 1 }}</span>
<span class="mobile-item-card__name">{{ op.operationName || '未知工序' }}</span>
</div>
<div class="mobile-item-card__body">
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">工序编码</span>
<span class="mobile-item-card__info-value">{{ op.operationCode || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">工时(分钟)</span>
<span class="mobile-item-card__info-value">{{ op.requiredTime || '-' }}</span>
</div>
</div>
</div>
<div v-if="form.operations.length === 0" class="mobile-empty-tip">
请先选择工序路线
</div>
</div>
</div>
</el-form>
<!-- 底部操作按钮 -->
<div class="mobile-form__footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="submitForm"> </el-button>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
@@ -408,8 +434,98 @@ const handleClose = () => {
defineExpose({ open })
</script>
<style scoped>
.el-tabs :deep(.el-tabs__content) {
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-form__footer {
position: sticky;
bottom: 0;
background: #fff;
padding: 12px 16px;
padding-bottom: calc(12px + constant(safe-area-inset-bottom));
padding-bottom: calc(12px + env(safe-area-inset-bottom));
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 12px;
z-index: 10;
margin: 0 -4px;
.el-button {
flex: 1;
height: 40px;
font-size: 15px;
}
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #f9f9fb;
border-radius: 8px;
padding: 12px;
border: 1px solid #ebeef5;
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__index {
font-size: 12px;
color: #909399;
font-weight: 600;
}
&__name {
flex: 1;
font-size: 14px;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 20px 0;
font-size: 14px;
}
</style>

View File

@@ -1,128 +1,126 @@
<template>
<el-dialog title="工序执行情况" v-model="visible" width="1200px" v-loading="loading">
<!-- 工序进度统计 -->
<OperationProgressCard :progressData="progressData" />
<el-drawer
v-model="visible"
title="工序执行情况"
direction="rtl"
size="100%"
:close-on-press-escape="true"
:destroy-on-close="true"
:append-to-body="true"
class="mobile-form-drawer"
>
<div class="mobile-form" v-loading="loading">
<!-- 工序进度统计 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">工序进度</div>
<OperationProgressCard :progressData="progressData" />
</div>
<!-- 工序列表 -->
<el-table :data="operationList" border stripe v-loading="tableLoading">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="工序信息" min-width="180">
<template #default="{ row }">
<div>{{ row.operationName }}</div>
<div class="text-gray-400 text-sm">{{ row.operationCode }}</div>
</template>
</el-table-column>
<el-table-column label="生产数量" min-width="180">
<template #default="{ row }">
<div>计划{{ row.planQuantity }}</div>
<div class="mt-1">完成{{ row.completedQuantity }}</div>
<div class="mt-1">
<span class="text-success">合格{{ row.qualifiedQuantity }}</span>
<span class="text-danger ml-2">不合格{{ row.unqualifiedQuantity }}</span>
<!-- 工序列表 -->
<div class="mobile-form__section">
<div class="mobile-form__section-title">工序列表</div>
<div class="mobile-item-list" v-loading="tableLoading">
<div
v-for="(row, index) in operationList"
:key="row.id"
class="mobile-item-card"
>
<div class="mobile-item-card__header">
<span class="mobile-item-card__index">#{{ index + 1 }}</span>
<span class="mobile-item-card__name">{{ row.operationName || '未知工序' }}</span>
<el-tag :type="getStatusType(row.status)" size="small">{{ getStatusText(row.status) }}</el-tag>
</div>
<div class="mobile-item-card__body">
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">工序编码</span>
<span class="mobile-item-card__info-value">{{ row.operationCode || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">计划数量</span>
<span class="mobile-item-card__info-value">{{ row.planQuantity }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">完成数量</span>
<span class="mobile-item-card__info-value">{{ row.completedQuantity }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">合格/不合格</span>
<span class="mobile-item-card__info-value">
<span style="color: #67c23a">{{ row.qualifiedQuantity }}</span>
<span style="color: #909399"> / </span>
<span style="color: #f56c6c">{{ row.unqualifiedQuantity }}</span>
</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">操作人</span>
<span class="mobile-item-card__info-value">{{ getOperationExecution(row.id)?.workerName || '-' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">开始时间</span>
<span class="mobile-item-card__info-value">{{ getOperationExecution(row.id)?.startTime || '未开始' }}</span>
</div>
<div class="mobile-item-card__info-row">
<span class="mobile-item-card__info-label">结束时间</span>
<span class="mobile-item-card__info-value">{{ getOperationExecution(row.id)?.endTime || '未结束' }}</span>
</div>
</div>
<div class="mobile-item-card__footer">
<el-button
size="small"
type="primary"
@click="handleStart(row)"
v-if="row.status === 0 && [2, 3].includes(workOrderStatus)"
v-hasPermi="['mes:production-order:operations']"
>开始</el-button>
<el-button
size="small"
type="success"
@click="handleComplete(row)"
v-if="row.status === 1"
v-hasPermi="['mes:production-order:operations']"
>完成</el-button>
<el-button
size="small"
type="warning"
@click="handlePause(row)"
v-if="row.status === 1"
v-hasPermi="['mes:production-order:operations']"
>暂停</el-button>
<el-button
size="small"
type="primary"
@click="handleResume(row)"
v-if="row.status === 3"
v-hasPermi="['mes:production-order:operations']"
>恢复</el-button>
<el-button
size="small"
@click="handleViewDetail(row)"
v-if="row.status === 2"
v-hasPermi="['mes:production-order:operations']"
>详情</el-button>
<el-button
size="small"
type="primary"
@click="handleRecordProgress(row)"
v-if="row.status === 1"
v-hasPermi="['mes:production-order:operations']"
>记录进度</el-button>
<el-button
size="small"
@click="handleViewProgressHistory(row)"
v-if="row.status === 1 || row.status === 2"
v-hasPermi="['mes:production-order:operations']"
>查看记录</el-button>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="操作人" min-width="120">
<template #default="{ row }">
<div>{{ getOperationExecution(row.id)?.workerName || '-' }}</div>
</template>
</el-table-column>
<el-table-column label="实际时间" min-width="180">
<template #default="{ row }">
<div>开始{{ getOperationExecution(row.id)?.startTime || '未开始' }}</div>
<div class="mt-1">结束{{ getOperationExecution(row.id)?.endTime || '未结束' }}</div>
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">{{ getStatusText(row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="{ row }">
<el-tooltip
content="开始"
placement="top"
v-if="row.status === 0 && [2, 3].includes(workOrderStatus)"
>
<el-button
link
type="primary"
@click="handleStart(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:video-play" />
</el-button>
</el-tooltip>
<el-tooltip content="完成" placement="top" v-if="row.status === 1">
<el-button
link
type="success"
@click="handleComplete(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:check" />
</el-button>
</el-tooltip>
<el-tooltip content="暂停" placement="top" v-if="row.status === 1">
<el-button
link
type="warning"
@click="handlePause(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:video-pause" />
</el-button>
</el-tooltip>
<el-tooltip content="恢复" placement="top" v-if="row.status === 3">
<el-button
link
type="primary"
@click="handleResume(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:video-play" />
</el-button>
</el-tooltip>
<!-- 查看详情按钮 - 修改显示条件 -->
<el-tooltip content="查看详情" placement="top" v-if="row.status === 2">
<el-button
link
type="info"
@click="handleViewDetail(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:document-copy" />
</el-button>
</el-tooltip>
<el-tooltip content="记录进度" placement="top" v-if="row.status === 1">
<el-button
link
type="primary"
@click="handleRecordProgress(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:edit" />
</el-button>
</el-tooltip>
<el-tooltip
content="查看记录"
placement="top"
v-if="row.status === 1 || row.status === 2"
>
<el-button
link
type="info"
@click="handleViewProgressHistory(row)"
v-hasPermi="['mes:production-order:operations']"
>
<Icon icon="ep:list" />
</el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-dialog>
<div v-if="operationList.length === 0 && !tableLoading" class="mobile-empty-tip">
暂无工序数据
</div>
</div>
</div>
</div>
</el-drawer>
<!-- 开始工序弹窗 -->
<OperationStartDialog
@@ -135,7 +133,7 @@
/>
<!-- 完成工序弹窗 -->
<el-dialog v-model="completeDialogVisible" title="完成工序" width="600px" append-to-body>
<el-dialog v-model="completeDialogVisible" title="完成工序" width="400px" append-to-body>
<el-form ref="completeFormRef" :model="completeForm" :rules="completeRules" label-width="120px">
<!-- 动态工序字段 -->
<template v-if="Object.keys(processFields).length > 0">
@@ -1097,7 +1095,88 @@ onMounted(() => {
defineExpose({ open })
</script>
<style scoped>
<style lang="scss" scoped>
.mobile-form {
padding: 0 4px;
}
.mobile-form__section {
background: #fff;
border-radius: 10px;
padding: 14px;
margin-bottom: 12px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mobile-form__section-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.mobile-item-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-item-card {
background: #f9f9fb;
border-radius: 8px;
padding: 12px;
border: 1px solid #ebeef5;
&__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
&__index {
font-size: 12px;
color: #909399;
font-weight: 600;
}
&__name {
flex: 1;
font-size: 14px;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__body {
font-size: 13px;
}
&__info-row {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
&__info-label {
color: #909399;
flex-shrink: 0;
}
&__info-value {
color: #606266;
text-align: right;
}
&__footer {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #f0f0f0;
}
}
.mobile-empty-tip {
text-align: center;
color: #909399;
padding: 20px 0;
font-size: 14px;
}
.text-lg {
font-size: 16px;
}