| | |
| | | |
| | | <view class="scan-container"> |
| | | |
| | | <PageHeader title="扫码出库" |
| | | <PageHeader title="扫码发货" |
| | | |
| | | @back="goBack" /> |
| | | |
| | |
| | | |
| | | <text class="module-label">合格出库</text> |
| | | |
| | | <text class="module-desc">扫描合格品进行领用出库</text> |
| | | <text class="module-desc">记录合格品的出库流向,销售二维码扫描发货</text> |
| | | |
| | | </view> |
| | | |
| | |
| | | |
| | | </view> |
| | | |
| | | <view |
| | | |
| | | <view v-if="!isFullyOutbound(item)" |
| | | class="stocked-qty-block"> |
| | | |
| | | <view class="kv-row stocked-qty-row"> |
| | | |
| | | <text class="kv-label">出库数量</text> |
| | | <text class="kv-label">{{ needScanShipFlow ? "本次发货数量" : "出库数量" }}</text> |
| | | |
| | | <view class="kv-value stocked-qty-input-wrap"> |
| | | |
| | | <up-input :key="'stocked-' + idx" |
| | | <text v-if="needScanShipFlow" |
| | | |
| | | class="scan-ship-qty-readonly">{{ resolveScanShipLineQuantity(item) }}</text> |
| | | |
| | | <up-input v-else |
| | | |
| | | :key="'stocked-' + idx" |
| | | |
| | | v-model="item.operateQuantity" |
| | | |
| | | type="number" |
| | | |
| | | placeholder="请输入出库数量" |
| | | :clearable="!isSalesQualifiedOutboundQtyLocked" |
| | | |
| | | clearable |
| | | :disabled="isSalesQualifiedOutboundQtyLocked" |
| | | |
| | | |
| | | border="surround" |
| | | |
| | |
| | | |
| | | </view> |
| | | |
| | | <text v-if="needScanShipFlow" |
| | | |
| | | class="scan-ship-qty-hint">整单一次性发货,数量以订单为准,不可修改</text> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <view v-if="needScanShipFlow" |
| | | |
| | | class="scan-ship-card"> |
| | | |
| | | <view class="scan-ship-title">发货信息</view> |
| | | |
| | | <u-form label-width="220rpx"> |
| | | |
| | | <u-form-item label="发货方式" |
| | | |
| | | class="scan-ship-type-form-item" |
| | | |
| | | required> |
| | | |
| | | <view class="scan-ship-type-trigger" |
| | | |
| | | @tap.stop="openScanShipTypeSheet"> |
| | | |
| | | <u-input v-model="scanShipTypeLabel" |
| | | |
| | | readonly |
| | | |
| | | placeholder="请选择" /> |
| | | |
| | | <u-icon name="arrow-right" |
| | | |
| | | color="#c0c4cc" |
| | | |
| | | size="18" /> |
| | | |
| | | </view> |
| | | |
| | | </u-form-item> |
| | | |
| | | <u-form-item v-if="scanShipTypeValue === '货车'" |
| | | |
| | | label="车牌号" |
| | | |
| | | required> |
| | | |
| | | <u-input v-model="scanShipCarNumber" |
| | | |
| | | placeholder="请输入车牌号" |
| | | |
| | | clearable /> |
| | | |
| | | </u-form-item> |
| | | |
| | | <u-form-item v-if="scanShipTypeValue === '快递'" |
| | | |
| | | label="快递单号" |
| | | |
| | | required> |
| | | |
| | | <u-input v-model="scanShipExpress" |
| | | |
| | | placeholder="请输入快递单号" |
| | | |
| | | clearable /> |
| | | |
| | | </u-form-item> |
| | | |
| | | </u-form> |
| | | |
| | | <view class="approval-process"> |
| | | |
| | | <view class="approval-header"> |
| | | |
| | | <text class="approval-title">审批流程</text> |
| | | |
| | | <text class="approval-desc">每个步骤只能选择一个审批人</text> |
| | | |
| | | </view> |
| | | |
| | | <view class="approval-steps"> |
| | | |
| | | <view v-for="(step, stepIndex) in scanShipApproverNodes" |
| | | |
| | | :key="step.id" |
| | | |
| | | class="approval-step"> |
| | | |
| | | <view class="step-title"> |
| | | |
| | | <text>审批人</text> |
| | | |
| | | </view> |
| | | |
| | | <view class="approver-container"> |
| | | |
| | | <view v-if="step.nickName" |
| | | |
| | | class="approver-item"> |
| | | |
| | | <view class="approver-avatar"> |
| | | |
| | | <text class="avatar-text">{{ step.nickName.charAt(0) }}</text> |
| | | |
| | | </view> |
| | | |
| | | <view class="approver-info"> |
| | | |
| | | <text class="approver-name">{{ step.nickName }}</text> |
| | | |
| | | </view> |
| | | |
| | | <view class="delete-approver-btn" |
| | | |
| | | @tap.stop="removeScanShipApproverUser(stepIndex)">×</view> |
| | | |
| | | </view> |
| | | |
| | | <view v-else |
| | | |
| | | class="add-approver-btn" |
| | | |
| | | @tap.stop="openScanShipContactSelect(stepIndex)"> |
| | | |
| | | <view class="add-circle">+</view> |
| | | |
| | | <text class="add-label">选择审批人</text> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <view class="delete-step-btn" |
| | | |
| | | v-if="scanShipApproverNodes.length > 1" |
| | | |
| | | @tap.stop="removeScanShipApproverNode(stepIndex)">删除节点</view> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <view class="add-step-btn"> |
| | | |
| | | <u-button icon="plus" |
| | | |
| | | plain |
| | | |
| | | type="primary" |
| | | |
| | | style="width: 100%" |
| | | |
| | | @click="addScanShipApproverNode">新增节点</u-button> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <view class="scan-ship-files"> |
| | | |
| | | <text class="scan-ship-subtitle">发货附件(选填,最多10张)</text> |
| | | |
| | | <u-button class="scan-ship-add-img-btn" |
| | | |
| | | size="small" |
| | | |
| | | type="primary" |
| | | |
| | | :loading="scanShipUploading" |
| | | |
| | | :disabled="scanShipFiles.length >= 10" |
| | | |
| | | @click="chooseScanShipImage">添加图片</u-button> |
| | | |
| | | <view v-if="scanShipFiles.length" |
| | | |
| | | class="scan-ship-file-grid"> |
| | | |
| | | <view v-for="(f, fi) in scanShipFiles" |
| | | |
| | | :key="fi" |
| | | |
| | | class="scan-ship-thumb-wrap"> |
| | | |
| | | <image :src="scanShipFileUrl(f)" |
| | | |
| | | class="scan-ship-thumb" |
| | | |
| | | mode="aspectFill" |
| | | |
| | | @click="previewScanShipImage(fi)" /> |
| | | |
| | | <text class="scan-ship-thumb-del" |
| | | |
| | | @click.stop="removeScanShipFile(fi)">×</text> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <up-action-sheet :show="scanShipTypeSheetShow" |
| | | |
| | | :actions="scanShipTypeActions" |
| | | |
| | | title="发货方式" |
| | | |
| | | @select="onScanShipTypeSelect" |
| | | |
| | | @close="scanShipTypeSheetShow = false" /> |
| | | |
| | | <view class="footer-btns"> |
| | | |
| | |
| | | |
| | | @click="cancelForm">返回</u-button> |
| | | |
| | | <u-button class="footer-confirm-btn" |
| | | <u-button v-if="needScanShipFlow" |
| | | |
| | | class="footer-confirm-btn" |
| | | |
| | | :loading="submitLoading" |
| | | |
| | | @click="confirmScanShipApply">提交发货审批</u-button> |
| | | |
| | | <u-button v-else |
| | | |
| | | class="footer-confirm-btn" |
| | | |
| | | :loading="submitLoading" |
| | | |
| | |
| | | |
| | | <script setup> |
| | | |
| | | import { ref, computed } from "vue"; |
| | | import { ref, computed, onMounted, onUnmounted } from "vue"; |
| | | |
| | | import PageHeader from "@/components/PageHeader.vue"; |
| | | |
| | | import { productList as salesProductList } from "@/api/salesManagement/salesLedger"; |
| | | import { productList as salesProductList, scanShipApply } from "@/api/salesManagement/salesLedger"; |
| | | |
| | | import config from "@/config"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import { getLedgerShippingLabel } from "@/utils/salesLedgerShip"; |
| | | |
| | | import modal from "@/plugins/modal"; |
| | | |
| | |
| | | resolveListTypeForDetail, |
| | | resolveContractNo, |
| | | buildSalesLedgerProductList, |
| | | buildScanShipProductList, |
| | | resolveScanShipLineQuantity, |
| | | hasAnyPositiveStockedQty, |
| | | resolveSubmitSceneKey, |
| | | } from "./scanOut.logic"; |
| | |
| | | |
| | | const submitLoading = ref(false); |
| | | |
| | | /** 销售 + 合格:走「提交发货审批」,不再直接出库或跳转原发货页 */ |
| | | const needScanShipFlow = computed( |
| | | () => |
| | | showForm.value && |
| | | type.value === QUALITY_TYPE.qualified && |
| | | contractKind.value === CONTRACT_KIND.sales |
| | | ); |
| | | |
| | | const scanShipTypeValue = ref("货车"); |
| | | const scanShipTypeLabel = computed(() => scanShipTypeValue.value); |
| | | const scanShipTypeSheetShow = ref(false); |
| | | const scanShipTypeActions = ref([ |
| | | { name: "货车", value: "货车" }, |
| | | { name: "快递", value: "快递" }, |
| | | ]); |
| | | const scanShipCarNumber = ref(""); |
| | | const scanShipExpress = ref(""); |
| | | let nextScanShipApproverNodeId = 2; |
| | | const scanShipApproverNodes = ref([{ id: 1, userId: null, nickName: "" }]); |
| | | const scanShipFiles = ref([]); |
| | | const scanShipUploading = ref(false); |
| | | |
| | | const scanShipUploadConfig = { |
| | | action: "/file/upload", |
| | | limit: 10, |
| | | fileType: ["jpg", "jpeg", "png", "gif", "webp"], |
| | | formType: 10, |
| | | }; |
| | | |
| | | const uploadScanShipFileUrl = computed( |
| | | () => (config.baseUrl || "").replace(/\/$/, "") + scanShipUploadConfig.action |
| | | ); |
| | | |
| | | const onScanShipTypeSelect = item => { |
| | | scanShipTypeValue.value = item.name || item.value || "货车"; |
| | | scanShipTypeSheetShow.value = false; |
| | | if (scanShipTypeValue.value === "货车") scanShipExpress.value = ""; |
| | | else scanShipCarNumber.value = ""; |
| | | }; |
| | | |
| | | const openScanShipTypeSheet = () => { |
| | | scanShipTypeSheetShow.value = true; |
| | | }; |
| | | |
| | | const addScanShipApproverNode = () => { |
| | | scanShipApproverNodes.value.push({ |
| | | id: nextScanShipApproverNodeId++, |
| | | userId: null, |
| | | nickName: "", |
| | | }); |
| | | }; |
| | | |
| | | const removeScanShipApproverNode = stepIndex => { |
| | | if (scanShipApproverNodes.value.length <= 1) return; |
| | | scanShipApproverNodes.value.splice(stepIndex, 1); |
| | | }; |
| | | |
| | | const removeScanShipApproverUser = stepIndex => { |
| | | const step = scanShipApproverNodes.value[stepIndex]; |
| | | if (!step) return; |
| | | step.userId = null; |
| | | step.nickName = ""; |
| | | }; |
| | | |
| | | const openScanShipContactSelect = stepIndex => { |
| | | uni.setStorageSync("stepIndex", stepIndex); |
| | | uni.navigateTo({ |
| | | url: "/pages/cooperativeOffice/collaborativeApproval/contactSelect?approveType=7&source=scanShip", |
| | | }); |
| | | }; |
| | | |
| | | const handleScanShipSelectContact = data => { |
| | | if (data?.source !== "scanShip") return; |
| | | if (!needScanShipFlow.value) return; |
| | | const c = data?.contact; |
| | | if (!c) return; |
| | | const idx = Number(data.stepIndex); |
| | | const i = Number.isNaN(idx) ? 0 : idx; |
| | | if (i < 0 || i >= scanShipApproverNodes.value.length) return; |
| | | const row = scanShipApproverNodes.value[i]; |
| | | row.userId = c.userId; |
| | | row.nickName = c.nickName || c.userName || ""; |
| | | }; |
| | | |
| | | const scanShipFileUrl = file => { |
| | | const base = config.fileUrl || ""; |
| | | const link = file?.link || file?.url || ""; |
| | | if (link && String(link).startsWith("http")) return link; |
| | | if (link && link.startsWith("/")) return base + link; |
| | | return file?.tempFilePath || file?.path || ""; |
| | | }; |
| | | |
| | | const chooseScanShipImage = () => { |
| | | if (scanShipFiles.value.length >= scanShipUploadConfig.limit) { |
| | | modal.msgError(`最多${scanShipUploadConfig.limit}张`); |
| | | return; |
| | | } |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ["compressed"], |
| | | sourceType: ["album", "camera"], |
| | | success: res => { |
| | | const path = res?.tempFilePaths?.[0]; |
| | | const tf = res?.tempFiles?.[0] || {}; |
| | | if (!path) return; |
| | | const token = getToken(); |
| | | if (!token) { |
| | | modal.msgError("未登录"); |
| | | return; |
| | | } |
| | | scanShipUploading.value = true; |
| | | uni.uploadFile({ |
| | | url: uploadScanShipFileUrl.value, |
| | | filePath: path, |
| | | name: "file", |
| | | formData: { type: scanShipUploadConfig.formType }, |
| | | header: { Authorization: `Bearer ${token}` }, |
| | | success: up => { |
| | | try { |
| | | const body = JSON.parse(up.data || "{}"); |
| | | if (body.code === 200 && body.data) { |
| | | const d = body.data; |
| | | scanShipFiles.value.push({ |
| | | tempId: d.tempId ?? d.tempFileId ?? d.id, |
| | | link: d.link || d.url, |
| | | url: d.url, |
| | | name: d.originalFilename || d.originalName || "图片", |
| | | tempFilePath: path, |
| | | }); |
| | | modal.msgSuccess("上传成功"); |
| | | } else { |
| | | modal.msgError(body.msg || "上传失败"); |
| | | } |
| | | } catch (e) { |
| | | modal.msgError("上传解析失败"); |
| | | } |
| | | }, |
| | | fail: () => modal.msgError("上传失败"), |
| | | complete: () => { |
| | | scanShipUploading.value = false; |
| | | }, |
| | | }); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | const removeScanShipFile = idx => { |
| | | scanShipFiles.value.splice(idx, 1); |
| | | }; |
| | | |
| | | const previewScanShipImage = idx => { |
| | | const urls = scanShipFiles.value.map(f => scanShipFileUrl(f)).filter(Boolean); |
| | | const cur = urls[idx]; |
| | | if (urls.length && cur) uni.previewImage({ urls, current: cur }); |
| | | }; |
| | | |
| | | const getScanShipTempFileIds = () => |
| | | scanShipFiles.value.map(f => f.tempId).filter(id => id != null && id !== ""); |
| | | |
| | | const confirmScanShipApply = async () => { |
| | | if (scanLedgerId.value == null || scanLedgerId.value === "") { |
| | | modal.msgError("缺少订单信息,请重新扫码"); |
| | | return; |
| | | } |
| | | if (!hasEditableOutboundItems.value) { |
| | | modal.msgError("该产品已经全部出库"); |
| | | return; |
| | | } |
| | | const salesLedgerProductList = buildScanShipProductList(recordList.value); |
| | | if (!hasAnyPositiveStockedQty(salesLedgerProductList)) { |
| | | modal.msgError("当前订单无可发货数量"); |
| | | return; |
| | | } |
| | | if (scanShipTypeValue.value === "货车" && !String(scanShipCarNumber.value || "").trim()) { |
| | | modal.msgError("请输入车牌号"); |
| | | return; |
| | | } |
| | | if (scanShipTypeValue.value === "快递" && !String(scanShipExpress.value || "").trim()) { |
| | | modal.msgError("请输入快递单号"); |
| | | return; |
| | | } |
| | | const hasEmptyStep = scanShipApproverNodes.value.some(s => !s?.userId); |
| | | if (hasEmptyStep) { |
| | | modal.msgError("请为每个审批步骤选择审批人"); |
| | | return; |
| | | } |
| | | if (scanShipUploading.value) { |
| | | modal.msgError("附件上传中,请稍候"); |
| | | return; |
| | | } |
| | | const approveUserIds = scanShipApproverNodes.value |
| | | .map(s => s.userId) |
| | | .filter(id => id != null && id !== "") |
| | | .join(","); |
| | | const payload = { |
| | | salesLedgerId: scanLedgerId.value, |
| | | salesLedgerProductList, |
| | | approveUserIds, |
| | | shipType: scanShipTypeValue.value, |
| | | shippingCarNumber: |
| | | scanShipTypeValue.value === "货车" ? String(scanShipCarNumber.value || "").trim() : "", |
| | | expressNumber: |
| | | scanShipTypeValue.value === "快递" ? String(scanShipExpress.value || "").trim() : "", |
| | | tempFileIds: getScanShipTempFileIds(), |
| | | }; |
| | | try { |
| | | submitLoading.value = true; |
| | | modal.loading("提交中..."); |
| | | const res = await scanShipApply(payload); |
| | | modal.closeLoading(); |
| | | if (res.code === 200) { |
| | | modal.msgSuccess(res.msg || "发货审批已发起"); |
| | | resetDetailView(); |
| | | } else { |
| | | modal.msgError(res.msg || "提交失败"); |
| | | } |
| | | } catch (e) { |
| | | modal.closeLoading(); |
| | | console.error(e); |
| | | } finally { |
| | | submitLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | uni.$on("selectContact", handleScanShipSelectContact); |
| | | }); |
| | | onUnmounted(() => { |
| | | uni.$off("selectContact", handleScanShipSelectContact); |
| | | }); |
| | | const isSalesQualifiedOutboundQtyLocked = computed( |
| | | () => |
| | | type.value === QUALITY_TYPE.qualified && |
| | | contractKind.value === CONTRACT_KIND.sales |
| | | ); |
| | | const submitConfigByScene = createSubmitConfig(scanLedgerId); |
| | | |
| | | const cardTitleMain = computed(() => { |
| | |
| | | |
| | | }; |
| | | |
| | | const parseUnqualifiedInboundQty = item => { |
| | | return ( |
| | | parseOptionalNumber( |
| | | item?.unqualifiedStockedQuantity ?? |
| | | item?.unQualifiedStockedQuantity ?? |
| | | item?.unqualifiedStockedQty ?? |
| | | item?.unqualifiedInboundQuantity |
| | | ) ?? 0 |
| | | ); |
| | | }; |
| | | |
| | | const parseUnqualifiedOutboundQty = item => { |
| | | return ( |
| | | parseOptionalNumber( |
| | | item?.unqualifiedShippedQuantity ?? |
| | | item?.unQualifiedShippedQuantity ?? |
| | | item?.unqualifiedShippedQty ?? |
| | | item?.unqualifiedOutboundQuantity |
| | | ) ?? 0 |
| | | ); |
| | | }; |
| | | |
| | | const isFullyOutbound = item => { |
| | | if (type.value === QUALITY_TYPE.unqualified) { |
| | | return parseUnqualifiedInboundQty(item) - parseUnqualifiedOutboundQty(item) <= 0; |
| | | } |
| | | const remaining = parseOptionalNumber(item?.remainingShippedQuantity); |
| | | if (remaining !== null) return remaining <= 0; |
| | | const fallback = parseOptionalNumber(defaultStockedQuantityFromRow(item, "outbound")); |
| | | return fallback !== null ? fallback <= 0 : false; |
| | | }; |
| | | |
| | | const hasEditableOutboundItems = computed(() => { |
| | | if (!recordList.value?.length) return false; |
| | | return recordList.value.some(item => !isFullyOutbound(item)); |
| | | }); |
| | | |
| | | |
| | | |
| | | const formatCell = (item, row, idx) => { |
| | |
| | | if (row.key === "productStockStatus") |
| | | |
| | | return formatProductStockStatus(item.productStockStatus); |
| | | |
| | | if (row.key === "ledgerShippingStatus") return getLedgerShippingLabel(item); |
| | | |
| | | if (row.key === "heavyBox") return formatHeavyBox(item.heavyBox); |
| | | |
| | |
| | | |
| | | recordList.value = []; |
| | | |
| | | scanShipTypeValue.value = "货车"; |
| | | scanShipCarNumber.value = ""; |
| | | scanShipExpress.value = ""; |
| | | nextScanShipApproverNodeId = 2; |
| | | scanShipApproverNodes.value = [{ id: 1, userId: null, nickName: "" }]; |
| | | scanShipFiles.value = []; |
| | | scanShipUploading.value = false; |
| | | scanShipTypeSheetShow.value = false; |
| | | |
| | | }; |
| | | |
| | | |
| | |
| | | |
| | | return; |
| | | |
| | | } |
| | | if (!hasEditableOutboundItems.value) { |
| | | modal.msgError("该产品已经全部出库"); |
| | | return; |
| | | } |
| | | |
| | | const salesLedgerProductList = buildSalesLedgerProductList(recordList.value); |
| | |
| | | } |
| | | |
| | | }; |
| | | |
| | | |
| | | |
| | | const goBack = () => { |
| | | |
| | |
| | | |
| | | height: 140rpx; |
| | | |
| | | min-width: 140rpx; |
| | | |
| | | min-height: 140rpx; |
| | | |
| | | flex-shrink: 0; |
| | | |
| | | border-radius: 32rpx; |
| | | |
| | | display: flex; |
| | |
| | | display: flex; |
| | | |
| | | flex-direction: column; |
| | | |
| | | flex: 1; |
| | | |
| | | min-width: 0; |
| | | |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | |
| | | /* 发货信息:与扫码入库审批区、台账发货页表单风格一致 */ |
| | | |
| | | .scan-ship-card { |
| | | |
| | | background-color: #fff; |
| | | |
| | | margin: 20rpx; |
| | | |
| | | padding: 0 0 24rpx; |
| | | |
| | | border-radius: 16rpx; |
| | | |
| | | box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06); |
| | | |
| | | overflow: hidden; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-title { |
| | | |
| | | font-size: 30rpx; |
| | | |
| | | font-weight: 600; |
| | | |
| | | color: #333; |
| | | |
| | | padding: 28rpx 28rpx 20rpx; |
| | | |
| | | border-bottom: 1rpx solid #eee; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-form) { |
| | | |
| | | background: transparent; |
| | | |
| | | margin: 0; |
| | | |
| | | padding: 0; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-form-item) { |
| | | |
| | | background: #fff; |
| | | |
| | | padding: 0 28rpx; |
| | | |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-form-item:last-child) { |
| | | |
| | | border-bottom: none; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-form-item__body) { |
| | | |
| | | padding: 24rpx 0; |
| | | |
| | | min-height: 88rpx; |
| | | |
| | | display: flex; |
| | | |
| | | align-items: center; |
| | | |
| | | border: none; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-form-item__label) { |
| | | |
| | | font-size: 28rpx; |
| | | |
| | | color: #888; |
| | | |
| | | font-weight: 400; |
| | | |
| | | flex-shrink: 0; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-form-item__content) { |
| | | |
| | | flex: 1; |
| | | |
| | | min-width: 0; |
| | | |
| | | display: flex; |
| | | |
| | | justify-content: flex-end; |
| | | |
| | | align-items: center; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-input), |
| | | |
| | | .scan-ship-card :deep(.u-input input), |
| | | |
| | | .scan-ship-card :deep(.u-input__content__field-wrapper__field), |
| | | |
| | | .scan-ship-card :deep(.u-input__input) { |
| | | |
| | | border: none !important; |
| | | |
| | | box-shadow: none !important; |
| | | |
| | | background: transparent !important; |
| | | |
| | | font-size: 28rpx; |
| | | |
| | | color: #1a1a1a; |
| | | |
| | | text-align: right; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-input input[readonly]), |
| | | |
| | | .scan-ship-card :deep(.u-input__input[readonly]) { |
| | | |
| | | color: #666; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.scan-ship-type-form-item .u-form-item__content) { |
| | | |
| | | justify-content: flex-start; |
| | | |
| | | align-items: center; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-type-trigger { |
| | | |
| | | display: flex; |
| | | |
| | | flex-direction: row; |
| | | |
| | | align-items: center; |
| | | |
| | | width: 100%; |
| | | |
| | | min-height: 72rpx; |
| | | |
| | | padding: 8rpx 0; |
| | | |
| | | box-sizing: border-box; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-type-trigger :deep(.u-input) { |
| | | |
| | | flex: 1; |
| | | |
| | | min-width: 0; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-type-trigger :deep(input), |
| | | |
| | | .scan-ship-type-trigger :deep(.u-input__input) { |
| | | |
| | | pointer-events: none; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.u-input input::placeholder), |
| | | |
| | | .scan-ship-card :deep(.u-input__input::placeholder) { |
| | | |
| | | color: #c0c4cc; |
| | | |
| | | font-size: 28rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card :deep(.scan-ship-type-form-item .u-input input), |
| | | |
| | | .scan-ship-card :deep(.scan-ship-type-form-item .u-input__input) { |
| | | |
| | | text-align: left; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approval-process { |
| | | |
| | | background: transparent; |
| | | |
| | | margin: 0; |
| | | |
| | | padding: 24rpx 28rpx 8rpx; |
| | | |
| | | border-radius: 0; |
| | | |
| | | box-shadow: none; |
| | | |
| | | border-top: 1rpx solid #f0f0f0; |
| | | |
| | | margin-top: 8rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approval-header { |
| | | |
| | | margin-bottom: 16rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approval-title { |
| | | |
| | | font-size: 30rpx; |
| | | |
| | | font-weight: 600; |
| | | |
| | | color: #333; |
| | | |
| | | display: block; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approval-desc { |
| | | |
| | | font-size: 24rpx; |
| | | |
| | | color: #999; |
| | | |
| | | margin-top: 6rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approval-step { |
| | | |
| | | margin-bottom: 18rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .step-title text { |
| | | |
| | | font-size: 24rpx; |
| | | |
| | | color: #666; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approver-container { |
| | | |
| | | display: flex; |
| | | |
| | | align-items: center; |
| | | |
| | | margin-top: 10rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approver-item { |
| | | |
| | | width: 100%; |
| | | |
| | | display: flex; |
| | | |
| | | align-items: center; |
| | | |
| | | gap: 12rpx; |
| | | |
| | | padding: 12rpx 0; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approver-avatar { |
| | | |
| | | width: 64rpx; |
| | | |
| | | height: 64rpx; |
| | | |
| | | border-radius: 50%; |
| | | |
| | | background: #f3f4f6; |
| | | |
| | | border: 2rpx solid #e5e7eb; |
| | | |
| | | display: flex; |
| | | |
| | | align-items: center; |
| | | |
| | | justify-content: center; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .avatar-text { |
| | | |
| | | font-size: 24rpx; |
| | | |
| | | color: #374151; |
| | | |
| | | font-weight: 600; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approver-info { |
| | | |
| | | flex: 1; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .approver-name { |
| | | |
| | | font-size: 28rpx; |
| | | |
| | | color: #333; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .delete-approver-btn { |
| | | |
| | | font-size: 32rpx; |
| | | |
| | | color: #ff4d4f; |
| | | |
| | | padding: 0 8rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .add-approver-btn { |
| | | |
| | | display: flex; |
| | | |
| | | align-items: center; |
| | | |
| | | gap: 10rpx; |
| | | |
| | | color: #3b82f6; |
| | | |
| | | padding: 10rpx 0; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .add-circle { |
| | | |
| | | width: 52rpx; |
| | | |
| | | height: 52rpx; |
| | | |
| | | border: 2rpx dashed #a0aec0; |
| | | |
| | | border-radius: 50%; |
| | | |
| | | color: #6b7280; |
| | | |
| | | display: flex; |
| | | |
| | | align-items: center; |
| | | |
| | | justify-content: center; |
| | | |
| | | font-size: 34rpx; |
| | | |
| | | line-height: 1; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .add-label { |
| | | |
| | | font-size: 26rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .delete-step-btn { |
| | | |
| | | color: #ff4d4f; |
| | | |
| | | font-size: 24rpx; |
| | | |
| | | margin-top: 8rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-card .add-step-btn { |
| | | |
| | | margin-top: 8rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-subtitle { |
| | | |
| | | display: block; |
| | | |
| | | font-size: 24rpx; |
| | | |
| | | color: #666; |
| | | |
| | | margin-bottom: 16rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-files { |
| | | |
| | | padding: 28rpx 28rpx 0; |
| | | |
| | | border-top: 1rpx solid #f0f0f0; |
| | | |
| | | margin-top: 16rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-files :deep(.scan-ship-add-img-btn) { |
| | | |
| | | width: 100% !important; |
| | | |
| | | margin-top: 8rpx; |
| | | |
| | | border-radius: 16rpx !important; |
| | | |
| | | min-height: 88rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-file-grid { |
| | | |
| | | display: flex; |
| | | |
| | | flex-wrap: wrap; |
| | | |
| | | gap: 24rpx; |
| | | |
| | | margin-top: 24rpx; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-thumb-wrap { |
| | | |
| | | position: relative; |
| | | |
| | | width: calc(50% - 12rpx); |
| | | |
| | | min-width: 200rpx; |
| | | |
| | | padding: 16rpx; |
| | | |
| | | box-sizing: border-box; |
| | | |
| | | background: #fff; |
| | | |
| | | border-radius: 16rpx; |
| | | |
| | | border: 1rpx solid #e9ecef; |
| | | |
| | | box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.05); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-thumb { |
| | | |
| | | width: 100%; |
| | | |
| | | height: 200rpx; |
| | | |
| | | border-radius: 12rpx; |
| | | |
| | | display: block; |
| | | |
| | | border: 2rpx solid #f0f0f0; |
| | | |
| | | background: #f5f5f5; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-thumb-del { |
| | | |
| | | position: absolute; |
| | | |
| | | top: 8rpx; |
| | | |
| | | right: 8rpx; |
| | | |
| | | width: 44rpx; |
| | | |
| | | height: 44rpx; |
| | | |
| | | background: #ff4757; |
| | | |
| | | color: #fff; |
| | | |
| | | font-size: 28rpx; |
| | | |
| | | line-height: 44rpx; |
| | | |
| | | text-align: center; |
| | | |
| | | border-radius: 50%; |
| | | |
| | | box-shadow: 0 4rpx 8rpx rgba(255, 71, 87, 0.35); |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .scan-ship-qty-readonly { |
| | | |
| | | font-size: 30rpx; |
| | | |
| | | font-weight: 600; |
| | | |
| | | color: #1a1a1a; |
| | | |
| | | } |
| | | |
| | | .scan-ship-qty-hint { |
| | | |
| | | display: block; |
| | | |
| | | font-size: 24rpx; |
| | | |
| | | color: #999; |
| | | |
| | | padding: 8rpx 0 0; |
| | | |
| | | line-height: 1.4; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | .footer-btns { |
| | | |
| | | display: flex; |