| | |
| | | lower-threshold="80" |
| | | @scrolltolower="loadMore" |
| | | > |
| | | <view v-for="(item, index) in tableData" :key="item.id || index" class="ledger-item"> |
| | | <view v-for="(item, index) in tableData" :key="item.id || index" class="ledger-item" @click="showTransferCard(item)"> |
| | | <view class="item-header"> |
| | | <view class="item-left"> |
| | | <view class="document-icon"> |
| | | <up-icon name="file-text" size="16" color="#ffffff"></up-icon> |
| | | </view> |
| | | <text class="item-id">{{ item.workOrderNo }}</text> |
| | | <text class="item-id">{{ item.workOrderNo || '无' }}</text> |
| | | </view> |
| | | <view class="item-right"> |
| | | <text class="item-tag tag-type">{{ item.workOrderType }}</text> |
| | | <text class="item-tag tag-type">{{ item.workOrderType || '无' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <view class="item-details"> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">产品名称</text> |
| | | <text class="detail-value">{{ item.productName }}</text> |
| | | <text class="detail-value">{{ item.productName || '无' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">规格型号</text> |
| | | <text class="detail-value">{{ item.model }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">机台名称</text> |
| | | <text class="detail-value">{{ item.deviceName }}</text> |
| | | <text class="detail-value">{{ item.model || '无' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">工序名称 / 计量单位</text> |
| | | <text class="detail-value">{{ item.processName }} / {{ item.unit }}</text> |
| | | <text class="detail-value">{{ item.processName || '无' }} / {{ item.unit || '无' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">需求/完成数量</text> |
| | | <text class="detail-value">{{ item.planQuantity }} / {{ item.completeQuantity }}</text> |
| | | <text class="detail-value">{{ item.planQuantity || '无' }} / {{ item.completeQuantity || '无' }}</text> |
| | | </view> |
| | | |
| | | <view class="progress-section"> |
| | |
| | | ></up-line-progress> |
| | | </view> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">实际开始</text> |
| | | <text class="detail-value">{{ item.startProductTime || '-' }}</text> |
| | | </view> |
| | | <view class="detail-row"> |
| | | <text class="detail-label">实际结束</text> |
| | | <text class="detail-value">{{ item.endProductTime || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="item-actions" v-if="!item.endProductTime"> |
| | | <view class="item-actions" v-if="!isEnded(item)"> |
| | | <up-button |
| | | text="开始报工" |
| | | size="mini" |
| | | type="primary" |
| | | :disabled="!canStartProduction(item) || startSubmittingId === item.id" |
| | | @click="handleStartProduction(item)" |
| | | /> |
| | | <up-button |
| | | text="结束报工" |
| | | text="报工" |
| | | size="mini" |
| | | type="success" |
| | | :disabled="!canEndProduction(item)" |
| | | @click="openEndReport(item)" |
| | | @click.stop="openEndReport(item)" |
| | | /> |
| | | </view> |
| | | </view> |
| | |
| | | <!-- 流转卡弹窗 --> |
| | | <up-popup :show="transferCardVisible" mode="center" @close="transferCardVisible = false" round="10"> |
| | | <view class="qr-popup"> |
| | | <text class="qr-title">工单流转卡二维码</text> |
| | | <view class="qr-box"> |
| | | <geek-qrcode |
| | | v-if="transferCardRowData" |
| | | :val="String(transferCardRowData.id)" |
| | | :size="200" |
| | | /> |
| | | <text class="qr-info" v-if="transferCardRowData">{{ transferCardRowData.workOrderNo || '-' }}</text> |
| | | <view class="transfer-records"> |
| | | <view class="transfer-records-header"> |
| | | <text class="transfer-records-title">报工审核记录</text> |
| | | <text class="transfer-records-count">共 {{ transferCardRecords.length }} 条</text> |
| | | </view> |
| | | <view v-if="transferCardLoading" class="transfer-records-loading">加载中...</view> |
| | | <view v-else-if="transferCardRecords.length > 0" class="transfer-records-list"> |
| | | <view |
| | | v-for="(record, index) in transferCardRecords" |
| | | :key="record.id || index" |
| | | class="transfer-record-item" |
| | | > |
| | | <view class="transfer-record-top"> |
| | | <view class="transfer-record-top-main"> |
| | | <text class="transfer-record-no">{{ record.productNo || `报工记录${index + 1}` }}</text> |
| | | <text class="transfer-record-sub"> |
| | | {{ record.process || '-' }} / {{ record.deviceName || '-' }} |
| | | </text> |
| | | </view> |
| | | <text class="audit-status-tag" :class="`audit-status-${getAuditStatusType(record.auditStatus)}`"> |
| | | {{ getAuditStatusLabel(record.auditStatus) }} |
| | | </text> |
| | | </view> |
| | | <view class="transfer-record-grid"> |
| | | <view |
| | | v-for="field in transferRecordFields" |
| | | :key="field.prop" |
| | | class="transfer-record-cell" |
| | | :class="{ 'is-wide': field.wide }" |
| | | > |
| | | <text class="transfer-record-label">{{ field.label }}</text> |
| | | <view v-if="field.prop === 'teamNames'" class="transfer-record-content"> |
| | | <view v-if="getTeamNameList(record.teamNames).length > 0" class="transfer-tag-list"> |
| | | <text |
| | | v-for="name in getTeamNameList(record.teamNames)" |
| | | :key="name" |
| | | class="transfer-name-tag" |
| | | > |
| | | {{ name }} |
| | | </text> |
| | | </view> |
| | | <text v-else class="transfer-record-value">-</text> |
| | | </view> |
| | | <view v-else-if="field.prop === 'auditStatus'" class="transfer-record-content"> |
| | | <text class="audit-status-tag" :class="`audit-status-${getAuditStatusType(record.auditStatus)}`"> |
| | | {{ getAuditStatusLabel(record.auditStatus) }} |
| | | </text> |
| | | </view> |
| | | <text v-else class="transfer-record-value"> |
| | | {{ formatTransferRecordValue(record, field.prop) }} |
| | | </text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="transfer-records-empty">暂无报工审核记录</view> |
| | | </view> |
| | | <text class="qr-info" v-if="transferCardRowData">{{ transferCardRowData.workOrderNo }}</text> |
| | | <up-button text="关闭" @click="transferCardVisible = false" style="margin-top: 20px;"></up-button> |
| | | </view> |
| | | </up-popup> |
| | | |
| | | <!-- 结束报工弹窗 --> |
| | | <!-- 报工弹窗 --> |
| | | <up-popup |
| | | v-model:show="endReportVisible" |
| | | mode="bottom" |
| | |
| | | <view class="report-modal"> |
| | | <view class="modal-header"> |
| | | <view class="modal-header-left"> |
| | | <text class="modal-title">结束报工</text> |
| | | <text class="modal-title">报工</text> |
| | | <text class="modal-subtitle" v-if="endReportRow"> |
| | | {{ endReportRow.workOrderNo || '-' }} · {{ endReportRow.deviceName || '-' }} |
| | | </text> |
| | |
| | | </view> |
| | | |
| | | <scroll-view class="modal-content" scroll-y> |
| | | <view class="report-summary" v-if="endReportRow"> |
| | | <!-- <view class="report-summary" v-if="endReportRow"> |
| | | <view class="summary-left"> |
| | | <text class="summary-title">{{ endReportRow.productName || '-' }}</text> |
| | | <text class="summary-sub">{{ endReportRow.processName || '-' }} · {{ endReportRow.model || '-' }}</text> |
| | |
| | | <text class="summary-num">{{ endReportForm.planQuantity || '0' }}</text> |
| | | <text class="summary-label">待生产</text> |
| | | </view> |
| | | </view> |
| | | </view> --> |
| | | |
| | | <up-form :model="endReportForm" labelWidth="120"> |
| | | <view class="form-section"> |
| | |
| | | <up-input v-model="endReportForm.planQuantity" disabled /> |
| | | </up-form-item> |
| | | <up-form-item label="本次生产数量" required> |
| | | <up-input v-model="endReportForm.quantity" disabled /> |
| | | <up-input v-model="endReportForm.quantity" /> |
| | | </up-form-item> |
| | | <up-form-item label="补产数量"> |
| | | <up-input v-model="endReportForm.replenishQty" type="number" placeholder="请输入" /> |
| | |
| | | <up-form-item label="报废数量"> |
| | | <up-input v-model="endReportForm.scrapQty" type="number" placeholder="请输入" /> |
| | | </up-form-item> |
| | | <up-form-item label="开始时间" required> |
| | | <up-input |
| | | v-model="endReportForm.startTime" |
| | | readonly |
| | | placeholder="请选择开始时间" |
| | | @click="startTimePickerVisible = true" |
| | | /> |
| | | </up-form-item> |
| | | |
| | | <up-form-item label="结束时间" required> |
| | | <up-input |
| | | v-model="endReportForm.endTime" |
| | | readonly |
| | | placeholder="请选择结束时间" |
| | | @click="endTimePickerVisible = true" |
| | | /> |
| | | </up-form-item> |
| | | </view> |
| | | |
| | | <view class="form-section"> |
| | | <text class="section-title">班组信息</text> |
| | | <up-form-item label="机台" required> |
| | | <up-input |
| | | v-model="endReportForm.deviceName" |
| | | readonly |
| | | placeholder="请选择" |
| | | @click="openDevicePicker" |
| | | suffixIcon="arrow-down" |
| | | /> |
| | | </up-form-item> |
| | | <up-form-item label="班组人员"> |
| | | <up-input |
| | | v-model="teamDisplayText" |
| | |
| | | suffixIcon="arrow-down" |
| | | /> |
| | | </up-form-item> |
| | | </view> |
| | | |
| | | <view class="form-section"> |
| | | <text class="section-title">审核信息</text> |
| | | <up-form-item label="审核人" required> |
| | | <up-input |
| | | v-model="endReportForm.auditUserName" |
| | |
| | | @select="onAuditSelect" |
| | | @close="auditPickerVisible = false" |
| | | /> |
| | | <up-action-sheet |
| | | :show="devicePickerVisible" |
| | | :actions="deviceActions" |
| | | title="选择机台" |
| | | @select="onDeviceSelect" |
| | | @close="devicePickerVisible = false" |
| | | /> |
| | | |
| | | <!-- 时间选择器 --> |
| | | <up-datetime-picker |
| | |
| | | <script setup> |
| | | import { ref, reactive, toRefs, computed, getCurrentInstance } from "vue"; |
| | | import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app"; |
| | | import { productWorkOrderPage, addProductMain, startProduction, getProductWorkOrderById } from "@/api/productionManagement/productionReporting.js"; |
| | | import { productWorkOrderPage, addProductMain, startProduction, getProductWorkOrderById,getProductionProductMain } from "@/api/productionManagement/productionReporting.js"; |
| | | import { getDeviceLedger } from "@/api/equipmentManagement/ledger"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | import FilesDia from "./components/filesDia.vue"; |
| | |
| | | const loadStatus = ref('loadmore'); |
| | | const transferCardVisible = ref(false); |
| | | const transferCardRowData = ref(null); |
| | | const transferCardLoading = ref(false); |
| | | const transferCardRecords = ref([]); |
| | | const auditStatusOptions = ref([ |
| | | { label: "未审核", value: 0 }, |
| | | { label: "通过", value: 1 }, |
| | | { label: "不通过", value: 2 }, |
| | | ]); |
| | | const transferRecordFields = [ |
| | | { label: "报工人员", prop: "teamNames", wide: true }, |
| | | { label: "审核人", prop: "auditUserName" }, |
| | | { label: "最终审核人", prop: "sureAuditUserName" }, |
| | | { label: "工单编号", prop: "workOrderNo" }, |
| | | { label: "订单编号", prop: "salesContractNo" }, |
| | | { label: "产品名称", prop: "productName" }, |
| | | { label: "产品规格型号", prop: "productModelName" }, |
| | | { label: "产出数量", prop: "quantity" }, |
| | | { label: "报废数量", prop: "scrapQty" }, |
| | | { label: "单位", prop: "unit" }, |
| | | { label: "审核状态", prop: "auditStatus" }, |
| | | { label: "备注信息", prop: "auditOpinion", wide: true }, |
| | | { label: "创建时间", prop: "createTime", wide: true }, |
| | | ]; |
| | | const workOrderFilesRef = ref(null); |
| | | const startSubmittingId = ref(null); |
| | | |
| | |
| | | quantity: "", |
| | | replenishQty: "0", |
| | | scrapQty: "0", |
| | | deviceLedgerId: "", |
| | | deviceName: "", |
| | | teamList: [], |
| | | startTime: "", |
| | | endTime: "", |
| | |
| | | const userOptions = ref([]); |
| | | const auditPickerVisible = ref(false); |
| | | const auditActions = computed(() => userOptions.value.map(u => ({ name: u.name, value: u.value }))); |
| | | |
| | | const deviceOptions = ref([]); |
| | | const devicePickerVisible = ref(false); |
| | | const deviceActions = computed(() => deviceOptions.value.map(d => ({ name: d?.deviceName || "", value: d?.id }))); |
| | | |
| | | const teamPickerVisible = ref(false); |
| | | const teamCheckedIds = ref([]); |
| | |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | workOrderNo: "", |
| | | workOrderId: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | |
| | | } |
| | | }; |
| | | |
| | | const ensureDeviceOptions = async () => { |
| | | if (deviceOptions.value.length > 0) return; |
| | | try { |
| | | const res = await getDeviceLedger(); |
| | | deviceOptions.value = res?.data || []; |
| | | } catch { |
| | | deviceOptions.value = []; |
| | | } |
| | | }; |
| | | |
| | | const getList = () => { |
| | | console.log(searchForm.value); |
| | | if (loading.value) return; |
| | | loading.value = true; |
| | | |
| | |
| | | return Math.round(n); |
| | | }; |
| | | |
| | | const showTransferCard = (row) => { |
| | | transferCardRowData.value = row; |
| | | transferCardVisible.value = true; |
| | | const getAuditStatusLabel = (val) => { |
| | | const target = auditStatusOptions.value.find(item => Number(item.value) === Number(val)); |
| | | return target?.label || "未知"; |
| | | }; |
| | | |
| | | const openWorkOrderFiles = (row) => { |
| | | workOrderFilesRef.value?.openDialog(row); |
| | | const getAuditStatusType = (val) => { |
| | | const typeMap = { 0: "info", 1: "success", 2: "danger" }; |
| | | return typeMap[Number(val)] || "default"; |
| | | }; |
| | | |
| | | const getTeamNameList = (val) => { |
| | | if (!val) return []; |
| | | return String(val) |
| | | .split(",") |
| | | .map(item => item.trim()) |
| | | .filter(Boolean); |
| | | }; |
| | | |
| | | const formatTransferRecordValue = (record, prop) => { |
| | | if (prop === "auditStatus") { |
| | | return getAuditStatusLabel(record?.[prop]); |
| | | } |
| | | |
| | | const value = record?.[prop]; |
| | | if (value === null || value === undefined || value === "") { |
| | | return "-"; |
| | | } |
| | | |
| | | return String(value); |
| | | }; |
| | | |
| | | const getTransferCardParams = (row) => { |
| | | const params = { |
| | | current: 1, |
| | | size: 100, |
| | | }; |
| | | |
| | | if (row?.id != null) { |
| | | params.workOrderId = row.id; |
| | | } |
| | | if (row?.workOrderNo) { |
| | | params.workOrderNo = row.workOrderNo; |
| | | } |
| | | if (row?.productMainId != null) { |
| | | params.productMainId = row.productMainId; |
| | | } |
| | | |
| | | return params; |
| | | }; |
| | | |
| | | const showTransferCard = async (row) => { |
| | | transferCardRowData.value = row; |
| | | transferCardVisible.value = true; |
| | | transferCardRecords.value = []; |
| | | transferCardLoading.value = true; |
| | | |
| | | try { |
| | | const res = await getProductionProductMain(getTransferCardParams(row)); |
| | | transferCardRecords.value = res?.data?.records || res?.records || []; |
| | | } catch { |
| | | transferCardRecords.value = []; |
| | | showToast("加载报工审核记录失败"); |
| | | } finally { |
| | | transferCardLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const getPendingQty = (row) => { |
| | | const plan = Number(row?.planQuantity) || 0; |
| | | const complete = Number(row?.completeQuantity) || 0; |
| | | return plan - complete; |
| | | return Math.max(plan - complete, 0); |
| | | }; |
| | | |
| | | const isStarted = (row) => { |
| | | if (row?.startProductTime && !row?.endProductTime) return true; |
| | | if (String(row?.reportWork) === "1" && !row?.endProductTime) return true; |
| | | return false; |
| | | if (!row?.id) return false; |
| | | if (Number(row?.completeQuantity) > 0) return true; |
| | | if (row?.startProductTime) return true; |
| | | return String(row?.reportWork) === "1"; |
| | | }; |
| | | |
| | | const isEnded = (row) => { |
| | | return Boolean(row?.endProductTime); |
| | | return getPendingQty(row) <= 0; |
| | | }; |
| | | |
| | | const canEndProduction = (row) => { |
| | | if (!row?.id) return false; |
| | | if (getPendingQty(row) <= 0) return false; |
| | | if (isEnded(row)) return false; |
| | | if (!isStarted(row)) return false; |
| | | return true; |
| | | }; |
| | | |
| | |
| | | if (getPendingQty(row) <= 0) return false; |
| | | if (isEnded(row)) return false; |
| | | if (isStarted(row)) return false; |
| | | if (!canStartProductionByUserIds(userStore.id, row)) return false; |
| | | return true; |
| | | return canStartProductionByUserIds(userStore.id, row); |
| | | |
| | | }; |
| | | |
| | | // 根据userIds判断是否有用户可以报工 |
| | | const canStartProductionByUserIds = (userId, row) => { |
| | | const team = row?.userIds || ""; |
| | | return team.includes(userId); |
| | | if (!userId) return true; |
| | | if (!team) return true; |
| | | return String(team).includes(String(userId)); |
| | | }; |
| | | |
| | | const handleStartProduction = (row) => { |
| | |
| | | endReportRow.value = row; |
| | | await ensureUserInfo(); |
| | | await ensureUserOptions(); |
| | | await ensureDeviceOptions(); |
| | | |
| | | endReportForm.planQuantity = String(getPendingQty(row)); |
| | | endReportForm.quantity = String(getPendingQty(row)); |
| | | endReportForm.replenishQty = "0"; |
| | | endReportForm.scrapQty = "0"; |
| | | endReportForm.deviceLedgerId = ""; |
| | | endReportForm.deviceName = ""; |
| | | endReportForm.teamList = []; |
| | | teamCheckedIds.value = []; |
| | | |
| | |
| | | endTimeValue.value = Date.now(); |
| | | endReportForm.endTime = formatDateTime(endTimeValue.value); |
| | | |
| | | if (row?.deviceName) { |
| | | const matched = deviceOptions.value.find(d => String(d?.deviceName || "") === String(row.deviceName)); |
| | | if (matched) { |
| | | endReportForm.deviceLedgerId = String(matched.id ?? ""); |
| | | endReportForm.deviceName = String(matched.deviceName ?? ""); |
| | | } |
| | | } |
| | | |
| | | endReportVisible.value = true; |
| | | }; |
| | | |
| | |
| | | endReportForm.auditUserId = String(e?.value ?? ""); |
| | | endReportForm.auditUserName = String(e?.name ?? ""); |
| | | auditPickerVisible.value = false; |
| | | }; |
| | | |
| | | const openDevicePicker = async () => { |
| | | await ensureDeviceOptions(); |
| | | devicePickerVisible.value = true; |
| | | }; |
| | | |
| | | const onDeviceSelect = (e) => { |
| | | endReportForm.deviceLedgerId = String(e?.value ?? ""); |
| | | endReportForm.deviceName = String(e?.name ?? ""); |
| | | devicePickerVisible.value = false; |
| | | }; |
| | | |
| | | const openTeamPicker = async () => { |
| | |
| | | }; |
| | | |
| | | const onStartTimeConfirm = (e) => { |
| | | endReportForm.startTime = formatDateTime(e.value); |
| | | const start = e.value; |
| | | endReportForm.startTime = formatDateTime(start); |
| | | startTimePickerVisible.value = false; |
| | | |
| | | // 👉 自动给结束时间 +1小时 |
| | | const end = start + 60 * 60 * 1000; |
| | | endTimeValue.value = end; |
| | | endReportForm.endTime = formatDateTime(end); |
| | | }; |
| | | |
| | | const onEndTimeConfirm = (e) => { |
| | | endReportForm.endTime = formatDateTime(e.value); |
| | | const end = e.value; |
| | | |
| | | if (end <= startTimeValue.value) { |
| | | showToast("结束时间必须大于开始时间"); |
| | | return; |
| | | } |
| | | |
| | | endReportForm.endTime = formatDateTime(end); |
| | | endTimePickerVisible.value = false; |
| | | }; |
| | | |
| | |
| | | showToast("报废数量必须为大于等于0的整数"); |
| | | return; |
| | | } |
| | | if (!endReportForm.startTime) { |
| | | showToast("请选择开始时间"); |
| | | return; |
| | | } |
| | | |
| | | if (!endReportForm.endTime) { |
| | | showToast("请选择结束时间"); |
| | | return; |
| | | } |
| | | |
| | | const start = new Date(endReportForm.startTime).getTime(); |
| | | const end = new Date(endReportForm.endTime).getTime(); |
| | | |
| | | if (end <= start) { |
| | | showToast("结束时间必须大于开始时间"); |
| | | return; |
| | | } |
| | | |
| | | if (!endReportForm.auditUserId) { |
| | | showToast("请选择审核人"); |
| | | return; |
| | | } |
| | | |
| | | if (!endReportForm.deviceLedgerId) { |
| | | showToast("请选择机台"); |
| | | return; |
| | | } |
| | | |
| | |
| | | userName: endReportForm.userName || userStore.nickName, |
| | | auditUserId: endReportForm.auditUserId, |
| | | auditUserName: endReportForm.auditUserName, |
| | | deviceLedgerId: endReportForm.deviceLedgerId, |
| | | deviceName: endReportForm.deviceName, |
| | | }; |
| | | const res = await addProductMain(submitData); |
| | | if (res?.code === 200) { |
| | | showToast("结束报工成功"); |
| | | showToast("报工成功"); |
| | | closeEndReport(); |
| | | handleQuery(); |
| | | } else { |
| | | showToast(res?.msg || "结束报工失败"); |
| | | showToast(res?.msg || "报工失败"); |
| | | } |
| | | } catch { |
| | | showToast("结束报工失败"); |
| | | showToast("报工失败"); |
| | | } finally { |
| | | uni.hideLoading(); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | .qr-popup { |
| | | width: 90vw; |
| | | max-width: 900rpx; |
| | | max-height: 85vh; // 原来 80 → 加大 |
| | | padding: 32rpx 28rpx; |
| | | background: #fff; |
| | | border-radius: 20rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | box-sizing: border-box; |
| | | } |
| | | .qr-title { |
| | | font-size: 30rpx; |
| | | font-weight: 600; |
| | | color: #222; |
| | | } |
| | | .qr-box { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 100%; |
| | | margin-top: 24rpx; |
| | | } |
| | | .qr-info { |
| | | margin-top: 16rpx; |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | } |
| | | .transfer-records { |
| | | width: 100%; |
| | | margin-top: 28rpx; |
| | | } |
| | | .transfer-records-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | margin-bottom: 16rpx; |
| | | } |
| | | .transfer-records-title { |
| | | font-size: 26rpx; |
| | | font-weight: 600; |
| | | color: #222; |
| | | } |
| | | .transfer-records-count { |
| | | font-size: 22rpx; |
| | | color: #8a8a8a; |
| | | } |
| | | .transfer-records-loading, |
| | | .transfer-records-empty { |
| | | padding: 28rpx 0; |
| | | font-size: 24rpx; |
| | | color: #8a8a8a; |
| | | text-align: center; |
| | | } |
| | | .transfer-records-list { |
| | | max-height: 500rpx; |
| | | overflow: auto; |
| | | } |
| | | .transfer-record-item { |
| | | padding: 20rpx 22rpx; |
| | | border-radius: 16rpx; |
| | | background: #f7f8fa; |
| | | } |
| | | |
| | | .transfer-record-item + .transfer-record-item { |
| | | margin-top: 16rpx; |
| | | } |
| | | .transfer-record-top { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | gap: 24rpx; |
| | | } |
| | | .transfer-record-top-main { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8rpx; |
| | | min-width: 0; |
| | | } |
| | | .transfer-record-no { |
| | | font-size: 28rpx; |
| | | font-weight: 600; |
| | | color: #222; |
| | | word-break: break-all; |
| | | } |
| | | .transfer-record-sub { |
| | | font-size: 22rpx; |
| | | color: #8a8a8a; |
| | | word-break: break-all; |
| | | } |
| | | .transfer-record-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16rpx; |
| | | margin-top: 18rpx; |
| | | } |
| | | .transfer-record-cell { |
| | | width: 100%; // 原来一行两个 → 改成一行一个 |
| | | } |
| | | .transfer-record-cell.is-wide { |
| | | width: 100%; |
| | | } |
| | | .transfer-record-label { |
| | | display: block; |
| | | margin-bottom: 8rpx; |
| | | font-size: 22rpx; |
| | | color: #8a8a8a; |
| | | } |
| | | .transfer-record-content { |
| | | min-height: 36rpx; |
| | | } |
| | | .transfer-record-value { |
| | | display: block; |
| | | font-size: 24rpx; |
| | | color: #222; |
| | | word-break: break-all; |
| | | } |
| | | .transfer-tag-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10rpx; |
| | | } |
| | | .transfer-name-tag { |
| | | padding: 6rpx 14rpx; |
| | | border-radius: 999rpx; |
| | | background: #e9f2ff; |
| | | color: #2f6bff; |
| | | font-size: 22rpx; |
| | | } |
| | | .audit-status-tag { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | min-height: 40rpx; |
| | | padding: 0 16rpx; |
| | | border-radius: 999rpx; |
| | | font-size: 22rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | .audit-status-info { |
| | | background: #edf1f5; |
| | | color: #6b7280; |
| | | } |
| | | .audit-status-success { |
| | | background: #e9f9ef; |
| | | color: #16a34a; |
| | | } |
| | | .audit-status-danger { |
| | | background: #feeeee; |
| | | color: #dc2626; |
| | | } |
| | | .audit-status-default { |
| | | background: #f4f4f5; |
| | | color: #606266; |
| | | } |
| | | |
| | | .report-modal { |
| | | background: #fff; |
| | | max-height: 85vh; |