| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- <script setup>
- import { computed, ref } from 'vue';
- import { onLoad } from '@dcloudio/uni-app';
- import { useUserStore } from '@/stores/user';
- import { request, parseResponseJson } from '@/utils/request';
- import { formatDateTime } from '@/utils/date';
- import { API_BASE, TOKEN_KEY } from '@/config/api';
- import PhoneDialInline from '@/components/phone-dial-inline.vue';
- import { DEFAULT_PLACEHOLDER_IMAGE_URL, staticAssetUrl } from '@/utils/staticAsset';
- import ClientImageLoader from '@/components/client/client-image-loader.vue';
- import SqCopyBtn from '@/components/sq-copy-btn.vue';
- const userStore = useUserStore();
- const ICON_SECTION_PROOF = staticAssetUrl('funct.svg');
- const ICON_SECTION_DELIVER = staticAssetUrl('trant.svg');
- const ICON_SECTION_GOODS = staticAssetUrl('product-icon.svg');
- const taskId = ref('');
- const loading = ref(true);
- const loadError = ref('');
- const taskNo = ref('');
- const status = ref('');
- const supplierPrepareStatus = ref('');
- const effectiveTime = ref('');
- const order = ref(null);
- const deliver = ref(null);
- const items = ref([]);
- /** 备货明细报价小计之和(与列表卡片 supplierQuoteTotal 口径一致) */
- const supplierQuoteTotal = computed(() => {
- let sum = 0;
- for (const row of items.value) {
- const v = row.supplierQuoteLine;
- if (v != null && !Number.isNaN(Number(v))) {
- sum += Number(v);
- }
- }
- return Math.round(sum * 100) / 100;
- });
- /** 备货明细总数量(用于供货合计前置展示) */
- const totalQuantityText = computed(() => {
- let sum = 0;
- for (const row of items.value) {
- const n = Number(row.quantity ?? 0);
- if (Number.isFinite(n))
- sum += n;
- }
- return Number.isInteger(sum) ? String(sum) : String(sum).replace(/\.?0+$/, '');
- });
- const statusText = {
- pending: '待分配',
- assigned: '待备货',
- supplier_stocking: '备货中',
- supplier_ready: '备货完成(待取货)',
- picking: '取货/配送中',
- picked: '待取货',
- delivering: '配送中',
- completed: '已完成',
- cancelled: '已取消'
- };
- const prepareStatusText = {
- pending_prepare: '待备货',
- stocking: '备货中',
- prepared: '备货完成(待取货)',
- picked_up: '已取货'
- };
- function statusLabel(s) {
- if (!s)
- return '-';
- return statusText[String(s).toLowerCase()] || s;
- }
- function headlinePrepareLabel() {
- const ps = String(supplierPrepareStatus.value || '').toLowerCase();
- if (ps && prepareStatusText[ps])
- return prepareStatusText[ps];
- return statusLabel(status.value);
- }
- function fmtMoney(n) {
- if (n == null || Number.isNaN(Number(n)))
- return '-';
- return `¥${Number(n).toFixed(2)}`;
- }
- /** 与询价单商品列表规格展示一致 */
- function formatMeasureText(measureValue, unitName) {
- const unitText = (unitName || '').trim();
- if (measureValue == null) {
- return unitText ? `- ${unitText}` : '-';
- }
- const measureNum = Number(measureValue);
- if (!Number.isNaN(measureNum)) {
- const measureText = String(measureNum).replace(/\.?0+$/, '');
- return unitText ? `${measureText} ${unitText}` : measureText;
- }
- const measureText = String(measureValue).trim();
- return unitText ? `${measureText} ${unitText}` : measureText || '-';
- }
- function lineSpecText(row) {
- const u = row.productUnitName || row.unitName || '';
- return formatMeasureText(row.measureValue ?? null, u);
- }
- /** 商品行右侧数量角标:X1、X2(大写 X + 数字) */
- function qtyXText(q) {
- const n = q == null ? NaN : Number(q);
- if (!Number.isFinite(n))
- return 'X0';
- const s = Number.isInteger(n) ? String(n) : String(n).replace(/\.?0+$/, '');
- return `X${s}`;
- }
- const acting = ref(false);
- const preparedImages = ref([]);
- const preparedImageFailed = ref({});
- function normalizeImageUrl(u) {
- const s = String(u || '').trim();
- if (!s)
- return '';
- if (/^https?:\/\//i.test(s) || s.startsWith('//'))
- return s;
- const origin = String(API_BASE || '').replace(/\/$/, '');
- if (!origin)
- return s;
- const path = s.startsWith('/') ? s : `/${s}`;
- return `${origin}${path}`;
- }
- const canEditPreparedEvidence = computed(() => {
- const ps = String(supplierPrepareStatus.value || '').toLowerCase();
- return ps === 'prepared';
- });
- const isPickedUpReadonly = computed(() => {
- const ps = String(supplierPrepareStatus.value || '').toLowerCase();
- return ps === 'picked_up';
- });
- function canStartStockDetail() {
- const ps = String(supplierPrepareStatus.value || '').toLowerCase();
- if (ps === 'pending_prepare')
- return true;
- return !supplierPrepareStatus.value && String(status.value || '').toLowerCase() === 'assigned';
- }
- function canFinishStockDetail() {
- const ps = String(supplierPrepareStatus.value || '').toLowerCase();
- if (ps === 'stocking')
- return true;
- return !supplierPrepareStatus.value && String(status.value || '').toLowerCase() === 'supplier_stocking';
- }
- async function onStartStockDetail() {
- if (!taskId.value || acting.value)
- return;
- acting.value = true;
- try {
- const res = await request({
- url: `/mini/supplier/tasks/${taskId.value}/start-stock`,
- method: 'POST',
- data: {}
- });
- const body = parseResponseJson(res);
- if (body.code === 200) {
- uni.showToast({ title: '已开始备货', icon: 'success' });
- await loadDetail();
- }
- else {
- uni.showToast({ title: body.message || '操作失败', icon: 'none' });
- }
- }
- catch {
- uni.showToast({ title: '网络异常', icon: 'none' });
- }
- finally {
- acting.value = false;
- }
- }
- async function onFinishStockDetail() {
- if (!taskId.value || acting.value)
- return;
- if (!preparedImages.value.length) {
- uni.showToast({ title: '请先上传备货完成凭证照片', icon: 'none' });
- return;
- }
- acting.value = true;
- try {
- const res = await request({
- url: `/mini/supplier/tasks/${taskId.value}/finish-stock`,
- method: 'POST',
- data: { imageUrls: preparedImages.value, preparedRemark: '备货完成' }
- });
- const body = parseResponseJson(res);
- if (body.code === 200) {
- uni.showToast({
- title: canEditPreparedEvidence.value ? '备货凭证已更新' : '已确认备货完成',
- icon: 'success'
- });
- await loadDetail();
- }
- else {
- uni.showToast({ title: body.message || '操作失败', icon: 'none' });
- }
- }
- catch {
- uni.showToast({ title: '网络异常', icon: 'none' });
- }
- finally {
- acting.value = false;
- }
- }
- function parseUploadResult(raw) {
- try {
- return JSON.parse(raw);
- }
- catch {
- return {};
- }
- }
- async function uploadPreparedImages() {
- const remain = 6 - preparedImages.value.length;
- if (remain <= 0) {
- uni.showToast({ title: '最多上传6张', icon: 'none' });
- return;
- }
- const choose = await new Promise((resolve) => {
- uni.chooseImage({
- count: remain,
- sizeType: ['compressed'],
- sourceType: ['album', 'camera'],
- success: (res) => resolve(res),
- fail: () => resolve(null)
- });
- });
- if (!choose?.tempFilePaths?.length)
- return;
- const rawToken = uni.getStorageSync(TOKEN_KEY);
- const token = typeof rawToken === 'string' ? rawToken.trim() : '';
- uni.showLoading({ title: '上传中...' });
- try {
- for (const filePath of choose.tempFilePaths) {
- const uploadRes = await new Promise((resolve, reject) => {
- uni.uploadFile({
- url: `${API_BASE}/upload/image`,
- filePath,
- name: 'file',
- header: {
- Authorization: token ? `Bearer ${token}` : ''
- },
- formData: { folder: 'delivery-evidence' },
- success: resolve,
- fail: reject
- });
- });
- const body = parseUploadResult(typeof uploadRes.data === 'string' ? uploadRes.data : String(uploadRes.data));
- if (body.code !== 200 || !body.data?.url) {
- uni.showToast({ title: body.message || '上传失败', icon: 'none' });
- return;
- }
- preparedImages.value.push(normalizeImageUrl(body.data.url));
- }
- }
- finally {
- uni.hideLoading();
- }
- }
- function removePreparedImage(idx) {
- const removed = preparedImages.value[idx];
- if (removed) {
- const key = String(removed);
- if (preparedImageFailed.value[key]) {
- delete preparedImageFailed.value[key];
- }
- }
- preparedImages.value.splice(idx, 1);
- }
- function previewPreparedImage(idx) {
- if (!preparedImages.value.length)
- return;
- uni.previewImage({
- current: preparedImages.value[idx],
- urls: preparedImages.value
- });
- }
- function onPreparedImageError(url) {
- const key = String(url || '').trim();
- if (!key)
- return;
- preparedImageFailed.value[key] = true;
- }
- function onPreparedImageLoad(url) {
- const key = String(url || '').trim();
- if (!key)
- return;
- if (preparedImageFailed.value[key]) {
- delete preparedImageFailed.value[key];
- }
- }
- function goProductDetail(productId) {
- if (!productId) {
- uni.showToast({ title: '暂无商品信息', icon: 'none' });
- return;
- }
- uni.navigateTo({
- url: `/pages/supplier/product/detail?productId=${encodeURIComponent(productId)}&viewOnly=1`
- });
- }
- /** 订单侧与平台(商家结算)结算状态,与列表卡片口径一致 */
- function supplierSettlementSettled() {
- return String(order.value?.supplierSettlementStatus || '').toLowerCase() === 'settled';
- }
- function supplierSettlementStatusText() {
- return supplierSettlementSettled() ? '已与平台结算' : '待平台结算';
- }
- function supplierSettlementTimeText() {
- if (!supplierSettlementSettled())
- return '—';
- const t = order.value?.supplierSettlementAt;
- if (t == null || String(t).trim() === '')
- return '—';
- return formatDateTime(String(t));
- }
- async function loadDetail() {
- const id = taskId.value;
- if (!id) {
- loadError.value = '缺少任务ID';
- loading.value = false;
- return;
- }
- loading.value = true;
- loadError.value = '';
- try {
- const res = await request({
- url: `/mini/supplier/tasks/${id}`,
- method: 'GET'
- });
- const body = parseResponseJson(res);
- if (body.code !== 200 || !body.data) {
- loadError.value = body.message || '加载失败';
- return;
- }
- const d = body.data;
- taskNo.value = d.taskNo || '';
- status.value = d.status || '';
- supplierPrepareStatus.value = d.supplierPrepareStatus || '';
- effectiveTime.value = d.effectiveTime || '';
- order.value = d.order || null;
- deliver.value = d.deliver || null;
- items.value = Array.isArray(d.items) ? d.items : [];
- preparedImages.value = Array.isArray(d.preparedImages)
- ? d.preparedImages
- .map((x) => normalizeImageUrl(x))
- .filter((x) => !!x)
- : [];
- preparedImageFailed.value = {};
- }
- catch {
- loadError.value = '网络异常,请稍后重试';
- }
- finally {
- loading.value = false;
- }
- }
- onLoad((query) => {
- taskId.value = query.id || '';
- if (!userStore.isLoggedIn) {
- uni.reLaunch({ url: '/pages/login/login' });
- return;
- }
- if (!userStore.isSupplier) {
- uni.showToast({ title: '仅供应商可访问', icon: 'none' });
- setTimeout(() => userStore.navigateToHome(), 1200);
- return;
- }
- void loadDetail();
- });
- </script>
- <template>
- <view class="page">
- <view v-if="loading" class="state">
- <ClientSkeleton variant="detail" :rows="3" />
- </view>
- <view v-else-if="loadError" class="state">
- <text>{{ loadError }}</text>
- </view>
- <scroll-view v-else scroll-y class="scroll">
- <view class="hero">
- <view class="hero-title-row">
- <text class="hero-title">{{ taskNo ? `任务号:${taskNo}` : '备货任务' }}</text>
- <SqCopyBtn v-if="taskNo" class="hero-copy" :text="taskNo" label="任务号" icon-only />
- </view>
- <text class="hero-sub">{{ headlinePrepareLabel() }}</text>
- <text v-if="effectiveTime" class="hero-time">送达客户截止时间:{{ effectiveTime }}</text>
- <view v-if="canStartStockDetail() || canFinishStockDetail()" class="hero-actions" @click.stop>
- <view v-if="canFinishStockDetail()" class="evidence-box">
- <text class="evidence-title">请先在下方「备货完成凭证」区域上传照片</text>
- </view>
- <button
- v-if="canStartStockDetail()"
- class="hero-btn hero-btn--outline"
- :disabled="acting"
- @click="onStartStockDetail"
- >
- 开始备货
- </button>
- <button
- v-if="canFinishStockDetail()"
- class="hero-btn hero-btn--solid"
- :disabled="acting"
- @click="onFinishStockDetail"
- >
- 备货完成
- </button>
- </view>
- </view>
- <view class="card">
- <view class="card-title-row">
- <image class="card-title-icon" :src="ICON_SECTION_PROOF" mode="aspectFit" />
- <text class="card-title">备货完成凭证</text>
- </view>
- <text class="card-hint">
- {{ isPickedUpReadonly ? '配送员已取货,凭证已锁定为只读。' : '放在客户订单信息前,便于先上传并核对备货照片。' }}
- </text>
- <view class="evidence-list">
- <view
- v-for="(url, idx) in preparedImages"
- :key="`proof-${idx}`"
- class="evidence-item"
- >
- <ClientImageLoader
- class="evidence-img"
- :src="url"
- mode="aspectFill"
- radius="8rpx"
- @click="previewPreparedImage(idx)"
- @error="onPreparedImageError(url)"
- @load="onPreparedImageLoad(url)"
- />
- <view v-if="preparedImageFailed[url]" class="evidence-img-error">加载失败</view>
- <view
- v-if="canFinishStockDetail() || canEditPreparedEvidence"
- class="evidence-del-btn"
- @click.stop="removePreparedImage(idx)"
- >
- 删除
- </view>
- </view>
- <view
- v-if="canFinishStockDetail() || canEditPreparedEvidence"
- class="evidence-add"
- @click="uploadPreparedImages"
- >
- + 上传
- </view>
- </view>
- <view v-if="canEditPreparedEvidence" class="evidence-actions">
- <button
- class="save-proof-btn"
- :disabled="acting || preparedImages.length === 0"
- @click="onFinishStockDetail"
- >
- 保存凭证
- </button>
- </view>
- <view v-if="order" class="settlement-under-proof">
- <view class="cell">
- <text class="k">平台结算状态</text>
- <view class="v settlement-val">
- <view
- class="settlement-tag"
- :class="supplierSettlementSettled() ? 'settlement-tag--done' : 'settlement-tag--wait'"
- >
- <text class="settlement-tag-text">{{ supplierSettlementStatusText() }}</text>
- </view>
- </view>
- </view>
- <view class="cell">
- <text class="k">结算时间</text>
- <view class="v settlement-val">
- <text class="settlement-time-text">{{ supplierSettlementTimeText() }}</text>
- </view>
- </view>
- </view>
- </view>
- <view v-if="(order && order.orderNo) || (deliver && (deliver.deliverName || deliver.phone))" class="card">
- <view class="card-title-row">
- <image class="card-title-icon" :src="ICON_SECTION_DELIVER" mode="aspectFit" />
- <text class="card-title">配送信息</text>
- </view>
- <view v-if="order && order.orderNo" class="cell">
- <text class="k">客户订单号</text>
- <view class="v copy-v">
- <text class="copy-v-text">{{ order.orderNo }}</text>
- <SqCopyBtn class="copy-btn" :text="order.orderNo" label="客户订单号" icon-only />
- </view>
- </view>
- <view v-if="deliver && (deliver.deliverName || deliver.phone)" class="cell">
- <text class="k">配送员姓名</text>
- <text class="v">{{ deliver.deliverName || '-' }}</text>
- </view>
- <view v-if="deliver && (deliver.deliverName || deliver.phone)" class="cell">
- <text class="k">配送员电话</text>
- <view class="v phone-line">
- <text class="phone-line-text">{{ deliver.phone || '-' }}</text>
- <PhoneDialInline class="dial" :phone="deliver.phone" empty-text="-" icon-only color="#189b65" />
- </view>
- </view>
- </view>
- <view class="goods-panel-head">
- <view class="gdp-head-line">
- <view class="gdp-title-row">
- <image class="gdp-title-icon" :src="ICON_SECTION_GOODS" mode="aspectFit" />
- <text class="gdp-title">备货明细</text>
- </view>
- </view>
- </view>
- <view class="card card--goods-block">
- <view v-if="items.length > 0" class="goods-total-bar goods-total-bar--top">
- <view class="goods-total-inline">
- <text class="goods-total-label">总数量</text>
- <text class="goods-total-qty">{{ totalQuantityText }}</text>
- <text class="goods-total-sep">|</text>
- <text class="goods-total-label">供货合计(元)</text>
- <text class="goods-total-value">{{ fmtMoney(supplierQuoteTotal) }}</text>
- </view>
- </view>
- <view class="goods-pane">
- <view
- v-for="row in items"
- :key="row.id"
- class="goods-card"
- @click="goProductDetail(row.productId)"
- >
- <ClientImageLoader
- class="goods-image"
- :src="row.imageUrl || DEFAULT_PLACEHOLDER_IMAGE_URL"
- mode="aspectFill"
- radius="18rpx"
- />
- <view class="goods-main">
- <view class="goods-name-row">
- <text class="goods-name">{{ row.productName || '—' }}</text>
- <text class="goods-line-tag">备货</text>
- </view>
- <view class="goods-spec-row">
- <text class="goods-spec">{{ lineSpecText(row) }}</text>
- <view class="goods-qty-wrap">
- <text class="goods-qty-label">数量</text>
- <text class="goods-qty-value">{{ qtyXText(row.quantity) }}</text>
- </view>
- </view>
- <view class="goods-line">
- <text v-if="(row.deliveredQuantity ?? 0) > 0" class="goods-delivered-tag">
- 已送达 {{ row.deliveredQuantity }}{{ row.unitName || '' }}
- </text>
- </view>
- <view class="price-block">
- <view class="price-row-inner price-row-inner--value">
- <text class="price-val">{{ fmtMoney(row.supplierQuoteUnit ?? null) }}</text>
- </view>
- <view class="price-row-inner price-row-inner--value strong">
- <text class="price-val">{{ fmtMoney(row.supplierQuoteLine ?? null) }}</text>
- </view>
- </view>
- </view>
- </view>
- <view v-if="items.length === 0" class="empty-lines">
- <text>暂无明细</text>
- </view>
- </view>
- </view>
- </scroll-view>
- </view>
- </template>
- <style lang="scss" scoped>
- .page {
- min-height: 100vh;
- background: #f3f7f5;
- }
- .state {
- padding: 24rpx 20rpx;
- min-height: 0;
- }
- .scroll {
- height: 100vh;
- box-sizing: border-box;
- }
- .hero {
- margin: 20rpx;
- padding: 28rpx 24rpx;
- border-radius: 16rpx;
- background: linear-gradient(145deg, #087f57, #0a9f6d);
- color: #fff;
- }
- .hero-title {
- display: block;
- font-size: 32rpx;
- font-weight: 600;
- margin-bottom: 8rpx;
- }
- .hero-title-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 10px;
- }
- .hero-title-row .hero-title {
- flex: 0 1 auto;
- min-width: 0;
- margin-bottom: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .hero-sub {
- display: block;
- font-size: 26rpx;
- opacity: 0.95;
- }
- .hero-time {
- display: block;
- margin-top: 12rpx;
- font-size: 24rpx;
- opacity: 0.9;
- }
- .hero-actions {
- display: flex;
- flex-wrap: wrap;
- gap: 16rpx;
- margin-top: 20rpx;
- }
- .hero-btn {
- flex: 1;
- min-width: 200rpx;
- height: 72rpx;
- line-height: 72rpx;
- font-size: 28rpx;
- border-radius: 12rpx;
- padding: 0;
- margin: 0;
- border: none;
- }
- .hero-btn::after {
- border: none;
- }
- .hero-btn--outline {
- background: rgba(255, 255, 255, 0.95);
- color: #087f57;
- font-weight: 600;
- }
- .hero-btn--solid {
- background: #fff;
- color: #087f57;
- font-weight: 600;
- }
- .evidence-box {
- width: 100%;
- background: rgba(255, 255, 255, 0.16);
- border-radius: 12rpx;
- padding: 14rpx;
- }
- .evidence-title {
- display: block;
- font-size: 22rpx;
- margin-bottom: 0;
- }
- .evidence-list {
- display: flex;
- flex-wrap: wrap;
- gap: 10rpx;
- }
- .evidence-item,
- .evidence-img,
- .evidence-add {
- width: 96rpx;
- height: 96rpx;
- border-radius: 8rpx;
- }
- .evidence-item {
- position: relative;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- gap: 8rpx;
- width: 96rpx;
- height: auto;
- }
- .evidence-img {
- width: 100%;
- height: 96rpx;
- border-radius: 8rpx;
- }
- .evidence-del-btn {
- height: 34rpx;
- border-radius: 8rpx;
- background: #dc2626;
- color: #ffffff;
- font-size: 20rpx;
- font-weight: 600;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .evidence-img-error {
- height: 30rpx;
- border-radius: 6rpx;
- background: #fee2e2;
- color: #b91c1c;
- font-size: 20rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .evidence-add {
- background: rgba(255, 255, 255, 0.9);
- color: #087f57;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- }
- .evidence-actions {
- margin-top: 14rpx;
- }
- .save-proof-btn {
- margin: 0;
- height: 64rpx;
- line-height: 64rpx;
- border: none;
- border-radius: 10rpx;
- background: #0a9f6d;
- color: #fff;
- font-size: 26rpx;
- }
- .card {
- margin: 0 20rpx 20rpx;
- padding: 24rpx;
- border-radius: 16rpx;
- background: #fff;
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
- }
- .card--goods {
- padding-bottom: 8rpx;
- }
- .phone-line {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12rpx;
- }
- .phone-line-text {
- flex: 1;
- min-width: 0;
- word-break: break-all;
- }
- .dial :deep(.phone-dial-inline__btn) {
- width: 44rpx;
- height: 44rpx;
- background: transparent;
- border-radius: 0;
- }
- /* 同配送员详情页商品区域:标题头 + 商品块 */
- .goods-panel-head {
- margin: 0 20rpx;
- padding: 8rpx 16rpx 12rpx;
- border-radius: 14rpx 14rpx 0 0;
- background: linear-gradient(180deg, #eefaf5 0%, #f8fcfa 100%);
- border: 1rpx solid #d9e9e1;
- border-bottom: none;
- }
- .gdp-head-line {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12rpx;
- }
- .gdp-title-row {
- display: inline-flex;
- align-items: center;
- gap: 10rpx;
- min-width: 0;
- }
- .gdp-title-icon {
- width: 30rpx;
- height: 30rpx;
- flex-shrink: 0;
- }
- .gdp-title {
- display: block;
- font-size: 28rpx;
- font-weight: 700;
- color: #1f2a26;
- }
- .gdp-tag {
- min-height: 40rpx;
- padding: 0 14rpx;
- border-radius: 999rpx;
- background: #e9f9f2;
- color: #0a9f6d;
- font-size: 20rpx;
- display: inline-flex;
- align-items: center;
- }
- .gdp-hint {
- display: block;
- margin-top: 6rpx;
- font-size: 22rpx;
- color: #6f7e77;
- line-height: 1.45;
- }
- .card--goods-block {
- margin: 0 20rpx 20rpx;
- padding: 0;
- border-radius: 0 0 14rpx 14rpx;
- border: 1rpx solid #d9e9e1;
- border-top: none;
- box-shadow: 0 4rpx 12rpx rgba(10, 159, 109, 0.06);
- }
- .card--goods-block .goods-pane {
- margin: 0;
- background: #fff;
- border-radius: 0 0 14rpx 14rpx;
- overflow: hidden;
- }
- .card--goods-block .goods-card {
- display: flex;
- padding: 20rpx 24rpx;
- border-bottom: 1rpx solid #e6efeb;
- background: #fff;
- }
- .card--goods-block .goods-card:active {
- background: #f4fbf8;
- }
- .card--goods-block .goods-image {
- width: 132rpx;
- height: 132rpx;
- border-radius: 14rpx;
- background: #f1f5f3;
- flex-shrink: 0;
- }
- .card--goods-block .goods-main {
- flex: 1;
- margin-left: 16rpx;
- min-width: 0;
- }
- .goods-name-row {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 12rpx;
- }
- .goods-name {
- display: block;
- flex: 1;
- min-width: 0;
- font-size: 28rpx;
- color: #1f2a26;
- font-weight: 600;
- line-height: 1.35;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .goods-line-tag {
- flex-shrink: 0;
- min-height: 34rpx;
- padding: 0 10rpx;
- border-radius: 999rpx;
- background: #f1f5f3;
- color: #6f7e77;
- font-size: 18rpx;
- display: inline-flex;
- align-items: center;
- }
- .goods-spec {
- display: block;
- margin-top: 0;
- font-size: 22rpx;
- color: #6f7e77;
- flex: 1;
- min-width: 0;
- }
- .goods-spec-row {
- margin-top: 8rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12rpx;
- }
- .goods-line {
- margin-top: 10rpx;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- gap: 16rpx;
- }
- .goods-qty-wrap {
- min-height: 44rpx;
- padding: 0 12rpx;
- border-radius: 22rpx;
- border: 1rpx solid #b9decf;
- display: inline-flex;
- align-items: center;
- gap: 8rpx;
- background: #ffffff;
- }
- .goods-qty-label {
- font-size: 20rpx;
- color: #6f7e77;
- }
- .goods-qty-value {
- font-size: 28rpx;
- font-weight: 700;
- color: #087f57;
- }
- .goods-delivered-tag {
- min-height: 44rpx;
- padding: 0 12rpx;
- border-radius: 22rpx;
- background: #fff4dd;
- color: #cf8a19;
- font-size: 20rpx;
- display: inline-flex;
- align-items: center;
- }
- .goods-total-bar {
- padding: 20rpx 24rpx 24rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- border-top: 1rpx solid #e6efeb;
- background: #f8fcfa;
- }
- .goods-total-label {
- font-size: 28rpx;
- color: #52615b;
- font-weight: 600;
- }
- .goods-total-value {
- font-size: 34rpx;
- color: #087f57;
- font-weight: 700;
- }
- .card-title {
- display: block;
- font-size: 30rpx;
- font-weight: 600;
- color: #303133;
- margin-bottom: 16rpx;
- }
- .card-title-row {
- display: flex;
- align-items: center;
- gap: 10rpx;
- margin-bottom: 16rpx;
- }
- .card-title-icon {
- width: 30rpx;
- height: 30rpx;
- flex-shrink: 0;
- }
- .card-title-row .card-title {
- margin-bottom: 0;
- }
- .card-hint {
- display: block;
- font-size: 22rpx;
- color: #909399;
- margin-bottom: 16rpx;
- line-height: 1.5;
- }
- .settlement-under-proof {
- margin-top: 20rpx;
- padding-top: 8rpx;
- border-top: 1rpx solid #eef5f2;
- }
- .settlement-under-proof .cell:first-child {
- padding-top: 8rpx;
- }
- .settlement-val {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- min-width: 0;
- }
- .settlement-time-text {
- font-size: 26rpx;
- color: #303133;
- text-align: right;
- word-break: break-all;
- }
- .settlement-tag {
- padding: 6rpx 18rpx;
- border-radius: 8rpx;
- flex-shrink: 0;
- max-width: 100%;
- }
- .settlement-tag-text {
- font-size: 22rpx;
- font-weight: 600;
- line-height: 1.3;
- }
- .settlement-tag--done {
- background: #ecfdf5;
- }
- .settlement-tag--done .settlement-tag-text {
- color: #059669;
- }
- .settlement-tag--wait {
- background: #fff7e8;
- }
- .settlement-tag--wait .settlement-tag-text {
- color: #d97706;
- }
- .cell {
- display: flex;
- align-items: flex-start;
- padding: 14rpx 0;
- border-bottom: 1rpx solid #f0f0f0;
- font-size: 26rpx;
- }
- .cell:last-child {
- border-bottom: none;
- }
- .k {
- width: 200rpx;
- flex-shrink: 0;
- color: #909399;
- }
- .v {
- flex: 1;
- color: #303133;
- word-break: break-all;
- }
- .copy-v {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12rpx;
- }
- .copy-v-text {
- flex: 1;
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .copy-btn {
- flex-shrink: 0;
- }
- /* 对齐询价单商品区域:左图右文、底部分割线 */
- .goods-pane {
- margin: 0;
- padding: 16rpx;
- background: #f8fcfa;
- border-radius: 0 0 16rpx 16rpx;
- }
- .goods-card {
- display: flex;
- padding: 20rpx;
- margin-bottom: 14rpx;
- border: 1rpx solid #e2efe9;
- border-radius: 14rpx;
- background: #fff;
- box-shadow: 0 6rpx 14rpx rgba(10, 159, 109, 0.06);
- }
- .goods-card:active {
- background: #f4fbf8;
- border-color: #cfe8dd;
- }
- .goods-card:last-child {
- margin-bottom: 0;
- }
- .goods-image {
- width: 132rpx;
- height: 132rpx;
- border-radius: 14rpx;
- background: #eef4f1;
- border: 1rpx solid #e1ece7;
- flex-shrink: 0;
- }
- .goods-main {
- flex: 1;
- margin-left: 16rpx;
- min-width: 0;
- }
- .goods-name {
- display: block;
- font-size: 28rpx;
- color: #1f2a26;
- font-weight: 600;
- line-height: 1.35;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .goods-spec {
- display: block;
- margin-top: 8rpx;
- font-size: 22rpx;
- color: #6f7e77;
- }
- .task-meta {
- margin-top: 12rpx;
- }
- .task-meta-line {
- display: block;
- font-size: 24rpx;
- color: #606266;
- line-height: 1.45;
- }
- .task-meta-line--qty {
- display: flex;
- flex-wrap: nowrap;
- align-items: center;
- justify-content: space-between;
- gap: 16rpx;
- width: 100%;
- }
- .task-qty-label {
- flex-shrink: 0;
- font-size: 24rpx;
- color: #606266;
- }
- .task-qty-value {
- flex-shrink: 0;
- margin-left: auto;
- font-size: 34rpx;
- font-weight: 700;
- color: #087f57;
- letter-spacing: 0.02em;
- }
- .price-block {
- margin-top: 12rpx;
- padding: 12rpx 14rpx;
- background: linear-gradient(180deg, #f8fcfa 0%, #f3f9f6 100%);
- border: 1rpx solid #e3f0ea;
- border-radius: 12rpx;
- }
- .price-row-inner {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 4rpx 0;
- }
- .price-row-inner--value {
- justify-content: flex-end;
- }
- .price-row-inner.strong .price-val {
- font-weight: 700;
- font-size: 30rpx;
- }
- .price-label {
- font-size: 24rpx;
- color: #909399;
- }
- .price-val {
- font-size: 26rpx;
- color: #0a9f6d;
- }
- .price-val--strong {
- font-weight: 700;
- font-size: 30rpx;
- }
- .empty-lines {
- padding: 44rpx 24rpx 36rpx;
- text-align: center;
- color: #9cb0a6;
- font-size: 26rpx;
- }
- .goods-total-bar {
- margin: 0 -24rpx 0;
- padding: 20rpx 24rpx 24rpx;
- display: flex;
- align-items: center;
- justify-content: flex-start;
- border-top: 1rpx solid #eceff3;
- background: #f8fcfa;
- }
- .goods-total-bar--top {
- margin: 0;
- border-top: none;
- border-bottom: 1rpx solid #eceff3;
- border-radius: 0;
- }
- .goods-total-inline {
- display: inline-flex;
- align-items: baseline;
- gap: 10rpx;
- }
- .goods-total-qty {
- font-size: 30rpx;
- color: #1f2a26;
- font-weight: 700;
- }
- .goods-total-sep {
- font-size: 24rpx;
- color: #b4c3bc;
- padding: 0 4rpx;
- }
- .goods-total-label {
- font-size: 28rpx;
- color: #606266;
- font-weight: 500;
- }
- .goods-total-value {
- font-size: 34rpx;
- color: #087f57;
- font-weight: 700;
- }
- </style>
|