<template>
|
<view class="production-traceability">
|
<PageHeader title="生产追溯"
|
@back="goBack" />
|
<!-- 搜索区域 -->
|
<view class="search-section">
|
<view class="search-bar"
|
@click="openNpsNoSelector">
|
<view class="search-input">
|
<text v-if="!selectedNpsNo"
|
class="placeholder">请选择生产订单号</text>
|
<text v-else
|
class="value">{{ selectedNpsNoLabel }}</text>
|
</view>
|
<view class="search-button">
|
<up-icon name="arrow-down"
|
size="20"
|
color="#999"></up-icon>
|
</view>
|
</view>
|
</view>
|
<!-- 内容区域 -->
|
<view class="content-container"
|
v-if="rowData.productionOrderDto">
|
<!-- 基础信息 -->
|
<view class="info-card">
|
<view class="card-title">基础信息</view>
|
<view class="base-info">
|
<view class="info-row">
|
<text class="label">生产订单号:</text>
|
<text class="value">{{ rowData.productionOrderDto?.npsNo || '-' }}</text>
|
</view>
|
<view class="info-row">
|
<text class="label">产品名称:</text>
|
<text class="value">{{ rowData.productionOrderDto?.productName || '-' }}</text>
|
</view>
|
<view class="info-row">
|
<text class="label">规格型号:</text>
|
<text class="value">{{ rowData.productionOrderDto?.model || '-' }}</text>
|
</view>
|
<view class="info-row">
|
<text class="label">计划数量:</text>
|
<text class="value">{{ rowData.productionOrderDto?.quantity || 0 }} {{ rowData.productionOrderDto?.unit }}</text>
|
</view>
|
<view class="info-row">
|
<text class="label">当前状态:</text>
|
<view class="value">
|
<up-tag :text="getStatusText(rowData.productionOrderDto?.status)"
|
:type="getStatusType(rowData.productionOrderDto?.status)"
|
size="mini" />
|
</view>
|
</view>
|
<view class="info-row">
|
<text class="label">客户名称:</text>
|
<text class="value">{{ rowData.productionOrderDto?.customerName || '-' }}</text>
|
</view>
|
<view class="info-row">
|
<text class="label">开始日期:</text>
|
<text class="value">{{ formatDate(rowData.productionOrderDto?.startTime) }}</text>
|
</view>
|
<view class="info-row">
|
<text class="label">完成进度:</text>
|
<view class="value progress-box">
|
<up-line-progress :percentage="formatProgress(rowData.productionOrderDto?.completionStatus)"
|
:activeColor="progressColor(formatProgress(rowData.productionOrderDto?.completionStatus))"
|
:showText="true" />
|
</view>
|
</view>
|
</view>
|
</view>
|
<!-- 工单信息 -->
|
<view class="work-order-section"
|
v-if="rowData.productionRecords && rowData.productionRecords.length > 0">
|
<view class="section-title">工单信息</view>
|
<view v-for="(item, index) in rowData.productionRecords"
|
:key="index"
|
class="work-order-card">
|
<view class="card-header">
|
<text class="work-order-no">{{ item.workOrder.workOrderNo }}</text>
|
<text class="progress-tag"
|
:style="{ color: progressColor(item.workOrder.completionStatus) }">{{ item.workOrder.completionStatus || 0 }}%</text>
|
</view>
|
<view class="card-content">
|
<view class="content-row">
|
<text class="label">产品/规格:</text>
|
<text class="value">{{ item.workOrder.productName }} / {{ item.workOrder.model }}</text>
|
</view>
|
<view class="content-row">
|
<text class="label">当前工序:</text>
|
<text class="value">{{ item.workOrder.operationName || '-' }}</text>
|
</view>
|
<view class="content-row">
|
<text class="label">需求/完成:</text>
|
<text class="value">{{ item.workOrder.planQuantity }} / {{ item.workOrder.completeQuantity }}</text>
|
</view>
|
<view class="content-row">
|
<text class="label">报废数量:</text>
|
<text class="value error-text">{{ item.workOrder.scrapQty || 0 }}</text>
|
</view>
|
</view>
|
<view class="card-footer">
|
<up-button type="primary"
|
size="small"
|
plain
|
text="报工记录"
|
@click="handleShowReports(item)"></up-button>
|
<up-button type="success"
|
size="small"
|
plain
|
text="质检信息"
|
@click="handleShowQuality(item)"></up-button>
|
</view>
|
</view>
|
</view>
|
<view v-else
|
class="no-data-minor">
|
<up-empty mode="data"
|
text="暂无工单信息"
|
icon-size="40"></up-empty>
|
</view>
|
</view>
|
<view v-else
|
class="no-data">
|
<up-empty mode="search"
|
text="请选择生产订单号查看追溯信息"></up-empty>
|
</view>
|
<!-- 生产订单号选择弹窗 -->
|
<up-popup :show="showNpsNoSelector"
|
mode="bottom"
|
@close="showNpsNoSelector = false"
|
round="10">
|
<view class="selector-popup">
|
<view class="popup-header">
|
<text class="popup-title">选择生产订单号</text>
|
<up-icon name="close"
|
size="20"
|
@click="showNpsNoSelector = false"></up-icon>
|
</view>
|
<view class="search-box">
|
<up-search placeholder="输入关键字搜索"
|
v-model="npsNoQuery"
|
:show-action="false"
|
@change="handleNpsNoSearch"
|
@search="handleNpsNoSearch"
|
:loading="npsNoLoading"></up-search>
|
</view>
|
<scroll-view scroll-y
|
class="options-list">
|
<view v-for="item in npsNoOptions"
|
:key="item.id"
|
class="option-item"
|
@click="onSelectNpsNo(item)">
|
<view class="option-main">
|
<text class="nps-no">{{ item.npsNo }}</text>
|
<text class="product-info">{{ item.productName }} / {{ item.model }}</text>
|
</view>
|
<up-icon v-if="selectedNpsNo === item.id"
|
name="checkbox-mark"
|
color="#3c9cff"
|
size="20"></up-icon>
|
</view>
|
<view v-if="npsNoOptions.length === 0"
|
class="no-options">
|
<text>{{ npsNoLoading ? '加载中...' : '暂无选项' }}</text>
|
</view>
|
</scroll-view>
|
</view>
|
</up-popup>
|
<!-- 报工详情弹窗 -->
|
<up-popup :show="reportPopupVisible"
|
mode="bottom"
|
@close="reportPopupVisible = false"
|
round="10">
|
<view class="popup-content">
|
<view class="popup-header">
|
<text class="popup-title">生产报工详情</text>
|
<up-icon name="close"
|
size="20"
|
@click="reportPopupVisible = false"></up-icon>
|
</view>
|
<scroll-view scroll-y
|
class="popup-scroll">
|
<view class="detail-info">
|
<view class="info-row"><text class="label">工单号:</text><text class="value">{{ detailData.workOrder.workOrderNo }}</text></view>
|
<view class="info-row"><text class="label">计划/完成:</text><text class="value">{{ detailData.workOrder.planQuantity }} / {{ detailData.workOrder.completeQuantity }}</text></view>
|
<view class="info-row"><text class="label">实际时间:</text><text class="value">{{ formatDate(detailData.workOrder.actualStartTime) }} 至 {{ formatDate(detailData.workOrder.actualEndTime) }}</text></view>
|
</view>
|
<view class="list-title">报工明细</view>
|
<view v-for="(report, idx) in detailData.reports"
|
:key="idx"
|
class="detail-item">
|
<view class="item-main">
|
<view class="item-row"><text class="label">报工单号:</text><text class="value">{{ report.productNo }}</text></view>
|
<view class="item-row"><text class="label">产出数量:</text><text class="value">{{ report.quantity || 0 }}</text></view>
|
<view class="item-row"><text class="label">报废数量:</text><text class="value error-text">{{ report.scrapQty || 0 }}</text></view>
|
<view class="item-row"><text class="label">工时(h):</text><text class="value">{{ report.workHour || 0 }}</text></view>
|
<view class="item-row"><text class="label">创建人:</text><text class="value">{{ report.userName }}</text></view>
|
<view class="item-row"><text class="label">创建时间:</text><text class="value">{{ formatDate(report.createTime, '{y}-{m}-{d} {h}:{i}') }}</text></view>
|
</view>
|
<view class="item-actions">
|
<text class="action-link"
|
@click="showParams(report.productionOperationParamList)">参数详情</text>
|
<text class="action-link green"
|
@click="handleShowInput(report.id)">投入详情</text>
|
</view>
|
</view>
|
<view v-if="!detailData.reports || detailData.reports.length === 0"
|
class="no-data-minor">暂无报工明细</view>
|
</scroll-view>
|
</view>
|
</up-popup>
|
<!-- 投入详情弹窗 -->
|
<up-popup :show="inputPopupVisible"
|
mode="bottom"
|
@close="inputPopupVisible = false"
|
round="10">
|
<view class="popup-content">
|
<view class="popup-header">
|
<text class="popup-title">投入信息详情</text>
|
<up-icon name="close"
|
size="20"
|
@click="inputPopupVisible = false"></up-icon>
|
</view>
|
<scroll-view scroll-y
|
class="popup-scroll">
|
<view class="input-list-popup">
|
<view v-for="(item, idx) in inputListData"
|
:key="idx"
|
class="quality-record">
|
<view class="record-title">投入记录 {{ idx + 1 }}</view>
|
<view class="info-grid">
|
<view class="info-item"><text class="label">报工单号</text><text class="value">{{ item.productNo || '-' }}</text></view>
|
<view class="info-item"><text class="label">投入数量</text><text class="value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text></view>
|
<view class="info-item full-width"><text class="label">投入产品名称</text><text class="value">{{ item.productName || '-' }}</text></view>
|
<view class="info-item full-width"><text class="label">投入产品型号</text><text class="value">{{ item.model || '-' }}</text></view>
|
</view>
|
</view>
|
<view v-if="!inputListData || inputListData.length === 0"
|
class="no-data-minor">{{ inputLoading ? '加载中...' : '暂无投入记录' }}</view>
|
</view>
|
</scroll-view>
|
</view>
|
</up-popup>
|
<!-- 质检详情弹窗 -->
|
<up-popup :show="qualityPopupVisible"
|
mode="bottom"
|
@close="qualityPopupVisible = false"
|
round="10">
|
<view class="popup-content">
|
<view class="popup-header">
|
<text class="popup-title">质检详情</text>
|
<up-icon name="close"
|
size="20"
|
@click="qualityPopupVisible = false"></up-icon>
|
</view>
|
<scroll-view scroll-y
|
class="popup-scroll">
|
<view v-for="(record, idx) in qualityRecords"
|
:key="idx"
|
class="quality-record">
|
<view class="record-title">检测记录 {{ idx + 1 }}</view>
|
<view class="info-grid">
|
<view class="info-item"><text class="label">检测日期</text><text class="value">{{ formatDate(record.createTime) }}</text></view>
|
<view class="info-item"><text class="label">检测结果</text><up-tag style="width:100rpx"
|
:text="record.checkResult || '待检测'"
|
:type="record.checkResult === '合格' ? 'success' : 'error'"
|
size="mini" /></view>
|
<view class="info-item"><text class="label">检验员</text><text class="value">{{ record.userName }}</text></view>
|
<view class="info-item"><text class="label">数量</text><text class="value">{{ record.quantity }} {{ record.unit }}</text></view>
|
<view class="info-item"><text class="label">报工单号</text><text class="value">{{ record.reportNo || '-' }}</text></view>
|
<view class="info-item"><text class="label">产品名称</text><text class="value">{{ record.productName || '-' }}</text></view>
|
<view class="info-item"><text class="label">规格型号</text><text class="value">{{ record.model || '-' }}</text></view>
|
<view class="info-item"><text class="label">检测单位</text><text class="value">{{ record.checkCompany || '-' }}</text></view>
|
</view>
|
<view class="params-table">
|
<view class="table-header">
|
<text class="col">指标</text>
|
<text class="col">单位</text>
|
<text class="col">标准值</text>
|
<text class="col">内控值</text>
|
<text class="col">实际值</text>
|
</view>
|
<view v-for="(param, pIdx) in record.inspectParamList"
|
:key="pIdx"
|
class="table-row">
|
<text class="col">{{ param.parameterItem }}</text>
|
<text class="col">{{ param.unit || '-' }}</text>
|
<text class="col">{{ param.standardValue }}</text>
|
<text class="col">{{ param.controlValue || '-' }}</text>
|
<text class="col"
|
:class="{ 'error-text': param.testValue != param.standardValue }">{{ param.testValue }}</text>
|
</view>
|
</view>
|
</view>
|
<view v-if="!qualityRecords || qualityRecords.length === 0"
|
class="no-data-minor">暂无质检记录</view>
|
</scroll-view>
|
</view>
|
</up-popup>
|
<!-- 参数详情弹窗 -->
|
<up-modal :show="paramModalVisible"
|
title="参数详情"
|
@confirm="paramModalVisible = false">
|
<view class="modal-content">
|
<view v-for="(param, idx) in currentParams"
|
:key="idx"
|
class="param-row">
|
<text class="label">{{ param.paramName }}:</text>
|
<text class="value">{{ param.inputValue }} {{ param.unit && param.unit !== '/' ? param.unit : '' }}</text>
|
</view>
|
<view v-if="!currentParams || currentParams.length === 0"
|
class="no-data-minor">暂无参数数据</view>
|
</view>
|
</up-modal>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed } from "vue";
|
import { onLoad } from "@dcloudio/uni-app";
|
import {
|
getOrderDetail,
|
productOrderListPage,
|
} from "@/api/productionManagement/productionOrder";
|
import { productionProductInputListPage } from "@/api/productionManagement/productionProductMain";
|
import PageHeader from "@/components/PageHeader.vue";
|
import { parseTime } from "@/utils/ruoyi";
|
|
// 选择器相关
|
const showNpsNoSelector = ref(false);
|
const npsNoQuery = ref("");
|
const npsNoOptions = ref([]);
|
const npsNoLoading = ref(false);
|
const selectedNpsNo = ref(null);
|
const selectedNpsNoLabel = ref("");
|
|
const rowData = reactive({
|
productionOrderDto: null,
|
productionRecords: [],
|
});
|
|
// 报工详情
|
const reportPopupVisible = ref(false);
|
const detailData = ref({ workOrder: {}, reports: [] });
|
|
// 投入详情
|
const inputPopupVisible = ref(false);
|
const inputListData = ref([]);
|
const inputLoading = ref(false);
|
|
// 质检详情
|
const qualityPopupVisible = ref(false);
|
const qualityRecords = ref([]);
|
|
// 参数详情
|
const paramModalVisible = ref(false);
|
const currentParams = ref([]);
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
const openNpsNoSelector = () => {
|
showNpsNoSelector.value = true;
|
if (npsNoOptions.value.length === 0) {
|
handleNpsNoSearch();
|
}
|
};
|
|
const handleNpsNoSearch = async () => {
|
npsNoLoading.value = true;
|
try {
|
const res = await productOrderListPage({
|
npsNo: npsNoQuery.value || "",
|
pageNum: 1,
|
pageSize: 50,
|
});
|
npsNoOptions.value = res.data?.records || res.rows || [];
|
} catch (error) {
|
console.error(error);
|
} finally {
|
npsNoLoading.value = false;
|
}
|
};
|
|
const onSelectNpsNo = async item => {
|
selectedNpsNo.value = item.id;
|
selectedNpsNoLabel.value = item.npsNo;
|
showNpsNoSelector.value = false;
|
|
uni.showLoading({ title: "加载中..." });
|
try {
|
const res = await getOrderDetail(item.npsNo);
|
if (res.code === 200 && res.data) {
|
const { productionOrder, workOrderList } = res.data;
|
rowData.productionOrderDto = productionOrder || item;
|
rowData.productionRecords = workOrderList || [];
|
} else {
|
rowData.productionOrderDto = item;
|
rowData.productionRecords = [];
|
}
|
} catch (error) {
|
console.error(error);
|
rowData.productionOrderDto = item;
|
rowData.productionRecords = [];
|
uni.showToast({ title: "获取详情失败", icon: "none" });
|
} finally {
|
uni.hideLoading();
|
}
|
};
|
|
onLoad(async options => {
|
if (options.npsNo) {
|
uni.showLoading({ title: "加载中..." });
|
try {
|
const res = await productOrderListPage({
|
npsNo: options.npsNo,
|
pageNum: 1,
|
pageSize: 10,
|
});
|
const records = res.data?.records || res.rows || [];
|
if (records.length > 0) {
|
onSelectNpsNo(records[0]);
|
} else {
|
uni.showToast({ title: "未找到相关订单", icon: "none" });
|
}
|
} catch (error) {
|
console.error(error);
|
} finally {
|
uni.hideLoading();
|
}
|
}
|
});
|
|
const getStatusText = status => {
|
const statusMap = { 1: "待开始", 2: "进行中", 3: "已完成", 5: "已结束" };
|
return statusMap[status] || "已取消";
|
};
|
|
const getStatusType = status => {
|
const typeMap = { 1: "primary", 2: "warning", 3: "success", 5: "error" };
|
return typeMap[status] || "info";
|
};
|
|
const formatDate = (date, pattern = "{y}-{m}-{d}") => {
|
return parseTime(date, pattern) || "-";
|
};
|
|
const formatProgress = val => {
|
const p = parseFloat(val || 0);
|
return p >= 100 ? 100 : p;
|
};
|
|
const progressColor = percentage => {
|
if (percentage < 30) return "#f56c6c";
|
if (percentage < 70) return "#e6a23c";
|
return "#67c23a";
|
};
|
|
const handleShowReports = row => {
|
detailData.value = {
|
workOrder: row.workOrder || {},
|
reports: (row.reportList || []).map(r => ({
|
...r.reportMain,
|
...(r.reportOutputList ? r.reportOutputList[0] : {}),
|
id: r.reportMain.id,
|
productionOperationParamList: r.reportParamList || [],
|
})),
|
};
|
reportPopupVisible.value = true;
|
};
|
|
const handleShowInput = async reportId => {
|
inputPopupVisible.value = true;
|
inputLoading.value = true;
|
inputListData.value = [];
|
try {
|
const res = await productionProductInputListPage({
|
productMainId: reportId,
|
pageNum: 1,
|
pageSize: 100,
|
});
|
inputListData.value = res.data?.records || res.rows || [];
|
} catch (error) {
|
console.error(error);
|
uni.showToast({ title: "获取投入信息失败", icon: "none" });
|
} finally {
|
inputLoading.value = false;
|
}
|
};
|
|
const handleShowQuality = row => {
|
const inspects = row.inspectList || [];
|
qualityRecords.value = inspects.map(i => ({
|
...i.inspect,
|
reportNo: i.reportNo,
|
productName: row.workOrder?.productName || "-",
|
model: row.workOrder?.model || "-",
|
userName: i.reportMain?.userName || "-",
|
inspectParamList: i.inspectParamList || [],
|
}));
|
qualityPopupVisible.value = true;
|
};
|
|
const showParams = params => {
|
currentParams.value = params || [];
|
paramModalVisible.value = true;
|
};
|
</script>
|
|
<style scoped lang="scss">
|
@import "@/styles/procurement-common.scss";
|
|
.production-traceability {
|
min-height: 100vh;
|
background-color: #f5f7fa;
|
}
|
|
.search-section {
|
background-color: #fff;
|
padding: 20rpx 24rpx;
|
margin-bottom: 20rpx;
|
}
|
|
.search-bar {
|
display: flex;
|
align-items: center;
|
background-color: #f2f2f2;
|
border-radius: 8rpx;
|
padding: 0 20rpx;
|
height: 80rpx;
|
|
.search-input {
|
flex: 1;
|
display: flex;
|
align-items: center;
|
|
.placeholder {
|
font-size: 28rpx;
|
color: #999;
|
}
|
|
.value {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: 500;
|
}
|
}
|
|
.search-button {
|
padding: 0 10rpx;
|
}
|
}
|
|
.selector-popup {
|
background: #fff;
|
padding: 30rpx;
|
max-height: 70vh;
|
display: flex;
|
flex-direction: column;
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 24rpx;
|
|
.popup-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
}
|
}
|
|
.search-box {
|
margin-bottom: 20rpx;
|
}
|
|
.options-list {
|
flex: 1;
|
overflow: hidden;
|
}
|
|
.option-item {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 24rpx 0;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
.option-main {
|
flex: 1;
|
.nps-no {
|
font-size: 28rpx;
|
font-weight: bold;
|
color: #333;
|
display: block;
|
margin-bottom: 4rpx;
|
}
|
.product-info {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
|
.no-options {
|
text-align: center;
|
padding: 40rpx;
|
color: #999;
|
font-size: 26rpx;
|
}
|
}
|
|
.content-container {
|
padding: 0 24rpx 40rpx;
|
}
|
|
.info-card {
|
background: #fff;
|
border-radius: 16rpx;
|
padding: 24rpx;
|
margin-bottom: 24rpx;
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
.card-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #333;
|
margin-bottom: 24rpx;
|
padding-left: 16rpx;
|
border-left: 8rpx solid #3c9cff;
|
}
|
}
|
|
.info-grid {
|
display: flex;
|
flex-wrap: wrap;
|
|
.info-item {
|
width: 50%;
|
margin-bottom: 20rpx;
|
display: flex;
|
flex-direction: column;
|
|
&.full-width {
|
width: 100%;
|
}
|
|
.label {
|
font-size: 24rpx;
|
color: #999;
|
margin-bottom: 8rpx;
|
}
|
|
.value {
|
font-size: 28rpx;
|
color: #333;
|
word-break: break-all;
|
}
|
}
|
}
|
|
.progress-container {
|
display: flex;
|
align-items: center;
|
gap: 20rpx;
|
|
up-line-progress {
|
flex: 1;
|
}
|
|
.progress-text {
|
font-size: 24rpx;
|
color: #666;
|
min-width: 60rpx;
|
}
|
}
|
|
.section-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #333;
|
margin: 32rpx 0 20rpx;
|
}
|
|
.work-order-card {
|
background: #fff;
|
border-radius: 16rpx;
|
padding: 24rpx;
|
margin-bottom: 20rpx;
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20rpx;
|
padding-bottom: 16rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
.work-order-no {
|
font-size: 28rpx;
|
font-weight: bold;
|
color: #3c9cff;
|
}
|
|
.progress-tag {
|
font-size: 28rpx;
|
font-weight: bold;
|
}
|
}
|
|
.card-content {
|
.content-row {
|
margin-bottom: 12rpx;
|
font-size: 26rpx;
|
|
.label {
|
color: #999;
|
}
|
|
.value {
|
color: #333;
|
}
|
}
|
}
|
|
.card-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 20rpx;
|
margin-top: 20rpx;
|
}
|
}
|
|
.base-info {
|
background: #fff;
|
padding: 24rpx;
|
border-radius: 16rpx;
|
margin-bottom: 30rpx;
|
|
.info-row {
|
margin-bottom: 16rpx;
|
font-size: 28rpx;
|
display: flex;
|
align-items: center;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.label {
|
color: #999;
|
min-width: 180rpx;
|
}
|
.value {
|
color: #333;
|
flex: 1;
|
font-weight: 500;
|
|
&.progress-box {
|
flex: 1;
|
}
|
}
|
}
|
}
|
|
.info-grid {
|
display: flex;
|
flex-wrap: wrap;
|
padding: 10rpx 0;
|
|
.info-item {
|
width: 50%;
|
margin-bottom: 20rpx;
|
display: flex;
|
flex-direction: column;
|
|
&.full-width {
|
width: 100%;
|
}
|
|
.label {
|
font-size: 24rpx;
|
color: #999;
|
margin-bottom: 4rpx;
|
}
|
.value {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: 500;
|
}
|
}
|
}
|
|
.popup-content {
|
background: #fff;
|
padding: 30rpx;
|
max-height: 80vh;
|
display: flex;
|
flex-direction: column;
|
border-radius: 20rpx 20rpx 0 0;
|
|
.popup-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 30rpx;
|
|
.popup-title {
|
font-size: 34rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
}
|
|
.popup-scroll {
|
flex: 1;
|
overflow: hidden;
|
}
|
}
|
|
.detail-info {
|
background: #f8f9fa;
|
padding: 24rpx;
|
border-radius: 16rpx;
|
margin-bottom: 30rpx;
|
flex-direction: column;
|
|
.info-row {
|
margin-bottom: 12rpx;
|
font-size: 28rpx;
|
display: flex;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.label {
|
color: #999;
|
min-width: 140rpx;
|
}
|
.value {
|
color: #333;
|
flex: 1;
|
font-weight: 500;
|
}
|
}
|
}
|
|
.list-title {
|
font-size: 30rpx;
|
font-weight: bold;
|
margin-bottom: 20rpx;
|
color: #333;
|
display: flex;
|
align-items: center;
|
|
&::before {
|
content: "";
|
width: 6rpx;
|
height: 28rpx;
|
background: #3c9cff;
|
margin-right: 12rpx;
|
border-radius: 4rpx;
|
}
|
}
|
|
.detail-item {
|
background: #fff;
|
border: 1rpx solid #f0f0f0;
|
border-radius: 12rpx;
|
padding: 24rpx;
|
margin-bottom: 20rpx;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
|
.item-main {
|
flex: 1;
|
.item-row {
|
font-size: 26rpx;
|
margin-bottom: 8rpx;
|
display: flex;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.label {
|
color: #999;
|
min-width: 130rpx;
|
}
|
.value {
|
color: #333;
|
flex: 1;
|
}
|
}
|
}
|
|
.item-actions {
|
display: flex;
|
flex-direction: column;
|
gap: 16rpx;
|
padding-left: 20rpx;
|
border-left: 1rpx solid #f0f0f0;
|
|
.action-link {
|
font-size: 26rpx;
|
color: #3c9cff;
|
white-space: nowrap;
|
|
&.green {
|
color: #52c41a;
|
}
|
}
|
}
|
}
|
|
.quality-record {
|
background: #fff;
|
border: 1rpx solid #f0f0f0;
|
border-radius: 16rpx;
|
padding: 24rpx;
|
margin-bottom: 30rpx;
|
|
.record-title {
|
font-size: 30rpx;
|
font-weight: bold;
|
color: #3c9cff;
|
margin-bottom: 24rpx;
|
display: flex;
|
justify-content: space-between;
|
}
|
}
|
|
.params-table {
|
margin-top: 24rpx;
|
border: 1rpx solid #f0f0f0;
|
border-radius: 12rpx;
|
overflow: hidden;
|
|
.table-header {
|
display: flex;
|
background: #f8f9fa;
|
padding: 20rpx 16rpx;
|
font-size: 26rpx;
|
font-weight: bold;
|
color: #666;
|
}
|
|
.table-row {
|
display: flex;
|
padding: 20rpx 16rpx;
|
font-size: 26rpx;
|
border-top: 1rpx solid #f0f0f0;
|
color: #333;
|
|
&:nth-child(even) {
|
background: #fafafa;
|
}
|
}
|
|
.col {
|
flex: 1;
|
text-align: center;
|
word-break: break-all;
|
}
|
}
|
|
.modal-content {
|
padding: 30rpx;
|
.param-row {
|
margin-bottom: 20rpx;
|
font-size: 28rpx;
|
display: flex;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.label {
|
color: #666;
|
min-width: 160rpx;
|
}
|
.value {
|
color: #333;
|
font-weight: 500;
|
flex: 1;
|
}
|
}
|
}
|
|
.input-list-popup {
|
.input-item {
|
background: #fff;
|
border: 1rpx solid #f0f0f0;
|
border-radius: 12rpx;
|
padding: 20rpx;
|
margin-bottom: 20rpx;
|
|
.input-row {
|
display: flex;
|
font-size: 26rpx;
|
margin-bottom: 8rpx;
|
&:last-child {
|
margin-bottom: 0;
|
}
|
.label {
|
color: #999;
|
min-width: 160rpx;
|
}
|
.value {
|
color: #333;
|
flex: 1;
|
}
|
}
|
}
|
}
|
|
.error-text {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
|
.no-data-minor {
|
text-align: center;
|
padding: 60rpx 40rpx;
|
color: #999;
|
font-size: 28rpx;
|
}
|
</style>
|