1. 配送仓库

This commit is contained in:
李琦
2026-07-18 10:22:36 +08:00
parent 5ff6f0d837
commit 47a829c1b1
17 changed files with 1208 additions and 200 deletions

View File

@@ -3,7 +3,7 @@ import { computed, nextTick, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Button, Descriptions, Image, Tag, Timeline } from 'ant-design-vue';
import { Button, Descriptions, Image, Tabs, Tag, Timeline } from 'ant-design-vue';
import {
expressDetailByOrderId,
@@ -21,9 +21,23 @@ const logisticsAnchorRef = ref<HTMLElement | null>(null);
/** 待发货时由列表传入,点击后关详情并打开发货弹窗 */
const onShipFn = ref<null | (() => void)>(null);
const canShip = computed(
() => Number(data.value.status) === 1 && typeof onShipFn.value === 'function',
);
const canShip = computed(() => {
const packages = Array.isArray(data.value.packages) ? data.value.packages : [];
const mineUnsent = packages.some((p: any) => Number(p.is_send) === 0);
const legacy =
Number(data.value.warehouse_is_send ?? 0) === 0 &&
Number(data.value.status) === 1;
return (mineUnsent || legacy) && typeof onShipFn.value === 'function';
});
const packageTabs = computed(() => {
const packages = Array.isArray(data.value.packages)
? data.value.packages
: Array.isArray(expressDetail.value.packages)
? expressDetail.value.packages
: [];
return packages;
});
const activePkg = ref('0');
const statusMap: Record<number, { text: string; color: string }> = {
0: { text: '待支付', color: 'default' },
@@ -219,8 +233,50 @@ const [Modal, modalApi] = useVbenModal({
<section ref="logisticsAnchorRef">
<h3 class="section-title">物流信息</h3>
<Tabs v-if="packageTabs.length" v-model:active-key="activePkg" type="card">
<Tabs.TabPane
v-for="(pkg, idx) in packageTabs"
:key="String(idx)"
:tab="`包裹${pkg.package_no || idx + 1}(${pkg.warehouse_name || '本仓'})`"
>
<template v-if="Number(pkg.is_send) === 1 && pkg.express">
<Descriptions
:column="{ xxl: 2, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }"
bordered
size="small"
>
<Descriptions.Item label="物流公司">
{{ pkg.express.express_company_name || '-' }}
</Descriptions.Item>
<Descriptions.Item label="运单号">
{{ pkg.express.express_no || '-' }}
</Descriptions.Item>
<Descriptions.Item label="最新状态">
{{ pkg.express.state_txt || '-' }}
</Descriptions.Item>
</Descriptions>
<div
v-if="Array.isArray(pkg.express.detail) && pkg.express.detail.length"
class="mt-4"
>
<Timeline>
<Timeline.Item
v-for="(detail, dIdx) in pkg.express.detail"
:key="dIdx"
>
<Tag :color="getColor(detail.status)">{{ detail.status }}</Tag>
<p>{{ detail.detail_at }}</p>
<p>{{ detail.detail }}</p>
</Timeline.Item>
</Timeline>
</div>
<div v-else class="empty-tip mt-3">暂无物流轨迹</div>
</template>
<div v-else class="empty-tip">本包裹尚未发货</div>
</Tabs.TabPane>
</Tabs>
<template
v-if="
v-else-if="
Number(data.is_send) === 1 ||
Number(data.status) >= 2 ||
expressNoRow().express_no ||
@@ -249,21 +305,17 @@ const [Modal, modalApi] = useVbenModal({
</Descriptions.Item>
</Descriptions>
<div v-if="Array.isArray(expressDetail.detail) && expressDetail.detail.length" class="mt-4">
<h4 class="mb-2 text-sm font-medium">物流追踪</h4>
<Timeline>
<Timeline.Item
v-for="(detail, index) in expressDetail.detail"
:key="index"
>
<Tag :color="getColor(detail.status)">
{{ detail.status }}
</Tag>
<Tag :color="getColor(detail.status)">{{ detail.status }}</Tag>
<p>{{ detail.detail_at }}</p>
<p>{{ detail.detail }}</p>
</Timeline.Item>
</Timeline>
</div>
<div v-else class="empty-tip mt-3">暂无物流轨迹</div>
</template>
<div v-else class="empty-tip">暂无物流</div>
</section>

View File

@@ -120,7 +120,7 @@ function openDetail(row: Record<string, any>, scrollToLogistics = false) {
id: row?.id,
scrollToLogistics,
onShip:
Number(row?.status) === 1
Number(row?.warehouse_is_send ?? 0) === 0 || Number(row?.status) === 1
? () => {
detailModalApi.close();
wareSend(row);
@@ -284,7 +284,7 @@ onMounted(() => {
物流
</Button>
<Button
v-if="Number(row.status) === 1"
v-if="Number(row.warehouse_is_send ?? 0) === 0"
size="small"
type="link"
@click="wareSend(row)"

View File

@@ -31,14 +31,28 @@ const newQuickName = ref('');
const newQuickValue = ref<number | null>(null);
const inputAutoAuditPass = ref(true);
const miniprogramEmptyImage = ref('');
/** 患者端物流是否显示配送仓名(默认否) */
const logisticsShowWhNameUser = ref(false);
/** 诊所端物流是否显示配送仓名(默认是) */
const logisticsShowWhNameClinic = ref(true);
function readStoredTab() {
const stored = localStorage.getItem(TAB_STORAGE_KEY);
if (stored === 'price_adjust' || stored === 'input_audit' || stored === 'miniprogram') {
if (
stored === 'price_adjust' ||
stored === 'input_audit' ||
stored === 'miniprogram' ||
stored === 'logistics'
) {
activeKey.value = stored;
}
}
function parseBoolConfig(val: unknown, defaultVal: boolean): boolean {
if (val === undefined || val === null || val === '') return defaultVal;
return val === '1' || val === 1 || val === true || val === 'true';
}
function handleTabChange(key: string | number) {
activeKey.value = String(key);
localStorage.setItem(TAB_STORAGE_KEY, String(key));
@@ -84,6 +98,12 @@ async function load() {
if (row.config_key === 'miniprogram_empty_image') {
miniprogramEmptyImage.value = String(row.config_value || '');
}
if (row.config_key === 'logistics_show_warehouse_name_user') {
logisticsShowWhNameUser.value = parseBoolConfig(row.config_value, false);
}
if (row.config_key === 'logistics_show_warehouse_name_clinic') {
logisticsShowWhNameClinic.value = parseBoolConfig(row.config_value, true);
}
}
} finally {
loading.value = false;
@@ -125,6 +145,14 @@ async function handleSave() {
config_key: 'miniprogram_empty_image',
config_value: miniprogramEmptyImage.value,
},
{
config_key: 'logistics_show_warehouse_name_user',
config_value: logisticsShowWhNameUser.value ? '1' : '0',
},
{
config_key: 'logistics_show_warehouse_name_clinic',
config_value: logisticsShowWhNameClinic.value ? '1' : '0',
},
]);
message.success('保存成功');
} finally {
@@ -188,6 +216,33 @@ onMounted(() => {
<FormAvatar v-model:value="miniprogramEmptyImage" />
</div>
</Tabs.TabPane>
<Tabs.TabPane key="logistics" tab="物流展示">
<div class="py-4 space-y-6">
<div>
<div class="mb-2 font-medium">患者端:物流多包裹是否显示配送仓名称</div>
<div class="mb-2 text-sm text-gray-500">
关闭后患者端仅显示「包裹1」「包裹2」,不展示仓名
</div>
<Switch
v-model:checked="logisticsShowWhNameUser"
checked-children="显示"
un-checked-children="隐藏"
/>
</div>
<div>
<div class="mb-2 font-medium">诊所端:物流多包裹是否显示配送仓名称</div>
<div class="mb-2 text-sm text-gray-500">
关闭后诊所/商家小程序订单物流仅显示包裹序号;平台后台产品订单不受影响
</div>
<Switch
v-model:checked="logisticsShowWhNameClinic"
checked-children="显示"
un-checked-children="隐藏"
/>
</div>
</div>
</Tabs.TabPane>
</Tabs>
<div class="mt-4">