Compare commits
2 Commits
ffadbcd92f
...
25bc5efd30
| Author | SHA1 | Date | |
|---|---|---|---|
| 25bc5efd30 | |||
| 0791ea968a |
@ -106,13 +106,19 @@ const useTabBarStore = defineStore('tabBar', {
|
||||
}
|
||||
},
|
||||
|
||||
/** LRU:访问时挪到末尾,并裁剪超量缓存 */
|
||||
/**
|
||||
* LRU:访问时挪到末尾,并裁剪超量缓存。
|
||||
* 必须一次性重赋值 Set,禁止先 delete 再 add:
|
||||
* keep-alive 会监听 include,名字从列表里消失会立刻 prune 掉缓存实例,
|
||||
* 表现为「切换标签就整页刷新」。
|
||||
*/
|
||||
touchCache(name: string) {
|
||||
if (!isString(name) || name === '') return;
|
||||
if (this.cacheTabList.has(name)) {
|
||||
this.cacheTabList.delete(name);
|
||||
}
|
||||
this.cacheTabList.add(name);
|
||||
const next = new Set(
|
||||
[...this.cacheTabList].filter((item) => item !== name),
|
||||
);
|
||||
next.add(name);
|
||||
this.cacheTabList = next;
|
||||
this.pruneCache(name);
|
||||
},
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@ function escapeAttr(text: string): string {
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
/** Vue / a-tag 内联样式(软底 + 左侧色条,无硬框) */
|
||||
/** Vue / a-tag 内联样式(软底圆角,无硬框、无左侧色条) */
|
||||
export function getStatusTagStyle(
|
||||
toneOrColors: StatusTagTone | StatusTagColors | null | undefined,
|
||||
variant: StatusTagVariant = 'solid',
|
||||
@ -102,11 +102,10 @@ export function getStatusTagStyle(
|
||||
background: colors.tagBgColor,
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
boxShadow: `inset 3px 0 0 ${colors.tagColor}`,
|
||||
fontWeight: '500',
|
||||
fontSize: '12px',
|
||||
lineHeight: '22px',
|
||||
padding: '0 10px 0 12px',
|
||||
padding: '0 10px',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
letterSpacing: '0.02em',
|
||||
|
||||
@ -10,7 +10,8 @@ import AdminWorkplace from './components/AdminWorkplace.vue'
|
||||
import CustomerWorkplace from './components/CustomerWorkplace.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'Home',
|
||||
// 必须与路由 name `Workplace` 一致,否则 keep-alive include 对不上,切标签会整页重挂
|
||||
name: 'Workplace',
|
||||
})
|
||||
|
||||
const rbacUserStore = useRbacUserStore()
|
||||
|
||||
@ -5,6 +5,50 @@ export interface OrderDetailFieldDef {
|
||||
labelKey: string;
|
||||
}
|
||||
|
||||
/** 订单详情基本信息中按「是/否」展示的布尔字段 */
|
||||
export const ORDER_DETAIL_BOOLEAN_FIELD_KEYS = [
|
||||
'isCod',
|
||||
'isIntercept',
|
||||
'isRemote',
|
||||
'isPaid',
|
||||
] as const;
|
||||
|
||||
export type OrderDetailBooleanFieldKey = (typeof ORDER_DETAIL_BOOLEAN_FIELD_KEYS)[number];
|
||||
|
||||
export function isOrderDetailBooleanField(key: string): boolean {
|
||||
return (ORDER_DETAIL_BOOLEAN_FIELD_KEYS as readonly string[]).includes(key);
|
||||
}
|
||||
|
||||
/** 兼容 true/false、0/1、是/否 等后端写法 */
|
||||
export function coerceOrderDetailBoolean(raw: unknown): boolean | null {
|
||||
if (raw === true || raw === 1 || raw === '1') return true;
|
||||
if (raw === false || raw === 0 || raw === '0') return false;
|
||||
if (raw == null || raw === '') return null;
|
||||
const s = String(raw).trim().toLowerCase();
|
||||
if (!s || s === '--') return null;
|
||||
if (['true', 'yes', 'y', '是'].includes(s)) return true;
|
||||
if (['false', 'no', 'n', '否'].includes(s)) return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function formatOrderDetailBooleanLabel(
|
||||
raw: unknown,
|
||||
t: (key: string, ...args: any[]) => string,
|
||||
): string {
|
||||
const flag = coerceOrderDetailBoolean(raw);
|
||||
// 缺省按「否」,避免详情里出现裸文本/「--」与同组 tag 不一致
|
||||
return flag ? t('order.myOrders.drawer.yes') : t('order.myOrders.drawer.no');
|
||||
}
|
||||
|
||||
export function isOrderDetailYesNoLabel(
|
||||
text: string,
|
||||
t: (key: string, ...args: any[]) => string,
|
||||
): boolean {
|
||||
return (
|
||||
text === t('order.myOrders.drawer.yes') || text === t('order.myOrders.drawer.no')
|
||||
);
|
||||
}
|
||||
|
||||
export const ORDER_DETAIL_EXTRA_BASIC_FIELDS: OrderDetailFieldDef[] = [
|
||||
{ key: 'quantity', labelKey: 'order.myOrders.drawer.quantity' },
|
||||
{ key: 'exportCustomsMode', labelKey: 'order.myOrders.drawer.exportCustomsMode' },
|
||||
@ -34,8 +78,30 @@ export const ORDER_DETAIL_VALUE_FIELD_MAP: Record<string, string[]> = {
|
||||
additionalServices: ['additionalServices', 'AdditionalServices'],
|
||||
downstreamSupplierName: ['downstreamSupplierName', 'DownstreamSupplierName'],
|
||||
downstreamSupplierCode: ['downstreamSupplierCode', 'DownstreamSupplierCode'],
|
||||
isPaid: ['isPaid', 'IsPaid'],
|
||||
isCod: ['isCod', 'IsCod'],
|
||||
isIntercept: ['isIntercept', 'IsIntercept'],
|
||||
isRemote: ['isRemote', 'IsRemote'],
|
||||
};
|
||||
|
||||
/** 读取布尔字段原始值(保留 false/0,不因 falsy 被跳过) */
|
||||
export function pickOrderDetailBooleanRaw(
|
||||
order: Record<string, any> | null | undefined,
|
||||
key: string,
|
||||
): unknown {
|
||||
if (!order) return undefined;
|
||||
const keys = ORDER_DETAIL_VALUE_FIELD_MAP[key] || [key];
|
||||
for (const k of keys) {
|
||||
if (!Object.prototype.hasOwnProperty.call(order, k) && order[k] === undefined) {
|
||||
continue;
|
||||
}
|
||||
const v = order[k];
|
||||
if (v === undefined) continue;
|
||||
return v;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export interface OrderDetailFieldGroup {
|
||||
key: string;
|
||||
labelKey: string;
|
||||
@ -97,6 +163,80 @@ export function pickOrderDetailRawValue(order: Record<string, any> | null | unde
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** 兼容 camelCase / PascalCase 嵌套字段(列表轻量映射会留下空 items/weightList) */
|
||||
export function pickOrderDetailNested<T = unknown>(
|
||||
order: Record<string, any> | null | undefined,
|
||||
...keys: string[]
|
||||
): T | undefined {
|
||||
if (!order) return undefined;
|
||||
for (const key of keys) {
|
||||
if (order[key] !== undefined && order[key] !== null) {
|
||||
return order[key] as T;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** 多个候选数组里取「有内容」的那个,避免列表空数组盖住详情 */
|
||||
export function pickOrderDetailArray<T = any>(
|
||||
order: Record<string, any> | null | undefined,
|
||||
...keys: string[]
|
||||
): T[] {
|
||||
if (!order) return [];
|
||||
let fallback: T[] = [];
|
||||
for (const key of keys) {
|
||||
const raw = order[key];
|
||||
if (!Array.isArray(raw)) continue;
|
||||
if (raw.length > 0) return raw as T[];
|
||||
fallback = raw as T[];
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开详情时合并列表行 + /order/detail:
|
||||
* 列表故意 Items/WeightList/FeeList=[],不能让空数组盖住详情里的完整嵌套数据。
|
||||
*/
|
||||
export function mergeOrderListRowWithDetail(listRow: any, detail: any) {
|
||||
const base = listRow && typeof listRow === 'object' ? listRow : {};
|
||||
const d = detail && typeof detail === 'object' ? detail : {};
|
||||
const merged = { ...base, ...d };
|
||||
|
||||
const sender = pickOrderDetailNested(d, 'sender', 'Sender')
|
||||
?? pickOrderDetailNested(base, 'sender', 'Sender');
|
||||
const receiver = pickOrderDetailNested(d, 'receiver', 'Receiver')
|
||||
?? pickOrderDetailNested(base, 'receiver', 'Receiver');
|
||||
const items = pickOrderDetailArray(d, 'items', 'Items');
|
||||
const itemsResolved = items.length
|
||||
? items
|
||||
: pickOrderDetailArray(base, 'items', 'Items');
|
||||
const weightList = pickOrderDetailArray(d, 'weightList', 'WeightList');
|
||||
const weightResolved = weightList.length
|
||||
? weightList
|
||||
: pickOrderDetailArray(base, 'weightList', 'WeightList');
|
||||
const feeList = pickOrderDetailArray(
|
||||
d,
|
||||
'feeList',
|
||||
'FeeList',
|
||||
'orderFees',
|
||||
'fees',
|
||||
);
|
||||
const feeResolved = feeList.length
|
||||
? feeList
|
||||
: pickOrderDetailArray(base, 'feeList', 'FeeList', 'orderFees', 'fees');
|
||||
|
||||
if (sender && typeof sender === 'object') {
|
||||
merged.sender = sender;
|
||||
}
|
||||
if (receiver && typeof receiver === 'object') {
|
||||
merged.receiver = receiver;
|
||||
}
|
||||
merged.items = itemsResolved;
|
||||
merged.weightList = weightResolved;
|
||||
merged.feeList = feeResolved;
|
||||
return merged;
|
||||
}
|
||||
|
||||
export interface DictRowLike {
|
||||
dictCode?: string | null;
|
||||
dictName?: string | null;
|
||||
|
||||
@ -216,10 +216,10 @@
|
||||
{{ getOrderValue(fieldKey) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="isOrderBooleanField(fieldKey)">
|
||||
<template v-else-if="shouldRenderOrderBooleanTag(fieldKey)">
|
||||
<a-tag
|
||||
size="small"
|
||||
:style="getOrderBooleanTagStyle(fieldKey)"
|
||||
:style="getBooleanTagStyle(getOrderValue(fieldKey) === $t('order.myOrders.drawer.yes'))"
|
||||
>
|
||||
{{ getOrderValue(fieldKey) }}
|
||||
</a-tag>
|
||||
@ -761,10 +761,16 @@ import {
|
||||
ORDER_DETAIL_VALUE_FIELD_MAP,
|
||||
buildDeclarationPayloadFromRows,
|
||||
cloneDeclarationItemRows,
|
||||
formatOrderDetailBooleanLabel,
|
||||
formatOrderDetailExtendedValue,
|
||||
isOrderDetailBooleanField,
|
||||
isOrderDetailYesNoLabel,
|
||||
mergeOrderBasicFields,
|
||||
parseAdditionalServiceCodes,
|
||||
patchOrderBasicFieldGroups,
|
||||
pickOrderDetailArray,
|
||||
pickOrderDetailBooleanRaw,
|
||||
pickOrderDetailNested,
|
||||
} from '../../components/orderDetailFieldConfig';
|
||||
import {
|
||||
filterMyOrdersColumns,
|
||||
@ -1120,14 +1126,9 @@ function getOrderFieldLabelKey(fieldKey: string) {
|
||||
return orderBasicFields.find((field) => field.key === fieldKey)?.labelKey ?? fieldKey;
|
||||
}
|
||||
|
||||
function isOrderBooleanField(fieldKey: string) {
|
||||
return ['isCod', 'isIntercept', 'isRemote', 'isPaid'].includes(fieldKey);
|
||||
}
|
||||
|
||||
function getOrderBooleanTagStyle(fieldKey: string) {
|
||||
const val = getOrderValue(fieldKey);
|
||||
const yes = t('order.myOrders.drawer.yes');
|
||||
return getBooleanTagStyle(val === yes);
|
||||
function shouldRenderOrderBooleanTag(fieldKey: string) {
|
||||
if (isOrderDetailBooleanField(fieldKey)) return true;
|
||||
return isOrderDetailYesNoLabel(getOrderValue(fieldKey), t);
|
||||
}
|
||||
|
||||
const declarationSkuPreviewVisible = ref(false);
|
||||
@ -1586,18 +1587,15 @@ function getOrderValue(key: string) {
|
||||
shipTime: ['shipTime'],
|
||||
payTime: ['payTime'],
|
||||
signTime: ['signTime'],
|
||||
isPaid: ['isPaid', 'IsPaid'],
|
||||
isCod: ['isCod'],
|
||||
codCurrency: ['codCurrency'],
|
||||
codSum: ['codSum'],
|
||||
isIntercept: ['isIntercept'],
|
||||
isRemote: ['isRemote'],
|
||||
createTime: ['createTime'],
|
||||
createBy: ['createBy'],
|
||||
updateTime: ['updateTime'],
|
||||
updateBy: ['updateBy'],
|
||||
...ORDER_DETAIL_VALUE_FIELD_MAP,
|
||||
};
|
||||
if (isOrderDetailBooleanField(key)) {
|
||||
return formatOrderDetailBooleanLabel(pickOrderDetailBooleanRaw(o, key), t);
|
||||
}
|
||||
const keys = fieldMap[key] || [key];
|
||||
let val: any;
|
||||
for (const k of keys) {
|
||||
@ -1607,9 +1605,6 @@ function getOrderValue(key: string) {
|
||||
}
|
||||
}
|
||||
if (val == null || val === '') return '--';
|
||||
if (key === 'isCod' || key === 'isIntercept' || key === 'isRemote' || key === 'isPaid') {
|
||||
return val ? t('order.myOrders.drawer.yes') : t('order.myOrders.drawer.no');
|
||||
}
|
||||
if (key === 'orderStatus') {
|
||||
const code = resolveOrderStatusCode(o);
|
||||
const meta = getOrderStatusMeta(code);
|
||||
@ -1634,7 +1629,7 @@ function getSenderValue(key: string) {
|
||||
}
|
||||
const val = sender.value[key];
|
||||
if (key === 'isDefault') {
|
||||
return val == null ? '--' : (val ? t('order.myOrders.drawer.yes') : t('order.myOrders.drawer.no'));
|
||||
return formatOrderDetailBooleanLabel(val, t);
|
||||
}
|
||||
return val ?? '--';
|
||||
}
|
||||
@ -1967,21 +1962,27 @@ async function loadTrackTimeline() {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载详情数据(优先使用 list 接口返回的 sender/receiver/items/weightList,与 parcel/create 对应)
|
||||
// 加载详情数据(兼容 camel/Pascal;列表空 items/weightList 不能盖住详情)
|
||||
function loadDetailData() {
|
||||
const o = detail.value;
|
||||
if (!o) return;
|
||||
|
||||
// 发件人
|
||||
if (o.sender && typeof o.sender === 'object') {
|
||||
sender.value = { ...o.sender, country: o.sender.country ?? o.sender.countryCode };
|
||||
const senderRaw = pickOrderDetailNested<Record<string, any>>(o, 'sender', 'Sender');
|
||||
if (senderRaw && typeof senderRaw === 'object') {
|
||||
sender.value = {
|
||||
...senderRaw,
|
||||
country: senderRaw.country ?? senderRaw.countryCode ?? senderRaw.CountryCode,
|
||||
};
|
||||
} else {
|
||||
sender.value = {};
|
||||
}
|
||||
|
||||
// 收件人:优先使用 list 返回的 receiver
|
||||
if (o.receiver && typeof o.receiver === 'object') {
|
||||
recipient.value = { ...o.receiver, country: o.receiver.country ?? o.receiver.countryCode };
|
||||
const receiverRaw = pickOrderDetailNested<Record<string, any>>(o, 'receiver', 'Receiver');
|
||||
if (receiverRaw && typeof receiverRaw === 'object') {
|
||||
recipient.value = {
|
||||
...receiverRaw,
|
||||
country: receiverRaw.country ?? receiverRaw.countryCode ?? receiverRaw.CountryCode,
|
||||
};
|
||||
} else {
|
||||
recipient.value = {
|
||||
recipientsName: o.consignee || '',
|
||||
@ -2006,17 +2007,12 @@ function loadDetailData() {
|
||||
};
|
||||
}
|
||||
|
||||
// 申报信息
|
||||
declarationList.value = Array.isArray(o.items) && o.items.length > 0
|
||||
? cloneDeclarationItemRows(o.items)
|
||||
: [];
|
||||
const itemsRaw = pickOrderDetailArray(o, 'items', 'Items');
|
||||
declarationList.value = itemsRaw.length > 0 ? cloneDeclarationItemRows(itemsRaw) : [];
|
||||
|
||||
// 件重尺
|
||||
weightList.value = Array.isArray(o.weightList) && o.weightList.length > 0 ? o.weightList : [];
|
||||
weightList.value = pickOrderDetailArray(o, 'weightList', 'WeightList');
|
||||
|
||||
// 费用:接口字段可为 feeList / orderFees / FeeList
|
||||
const fees = o.feeList ?? o.FeeList ?? o.orderFees ?? o.fees;
|
||||
feeList.value = Array.isArray(fees) ? fees : [];
|
||||
feeList.value = pickOrderDetailArray(o, 'feeList', 'FeeList', 'orderFees', 'fees');
|
||||
}
|
||||
|
||||
function applyDetailFromApi(raw: any) {
|
||||
|
||||
@ -62,6 +62,7 @@
|
||||
type ColumnScheme,
|
||||
} from './columnConfig';
|
||||
import { filterMyOrdersColumns, filterMyOrdersFieldKeys } from './customerFieldMask';
|
||||
import { mergeOrderListRowWithDetail } from '../components/orderDetailFieldConfig';
|
||||
import { useHiprintClientState } from '@/utils/hiprint/hiprintClientState';
|
||||
import { fetchPrintUrlsForOrders, getCachedPrintUrl } from '@/utils/printSessionCache';
|
||||
|
||||
@ -274,7 +275,7 @@
|
||||
if (id != null && id !== '') {
|
||||
try {
|
||||
const detail = await getOrderDetail(id);
|
||||
nextRow = normalizeOrderRow({ ...row, ...detail });
|
||||
nextRow = normalizeOrderRow(mergeOrderListRowWithDetail(row, detail));
|
||||
} catch {
|
||||
/* 保留列表行数*/
|
||||
}
|
||||
@ -1528,7 +1529,7 @@
|
||||
if (rows.length === 1 && id != null) {
|
||||
try {
|
||||
const detail = await getOrderDetail(id);
|
||||
ctxRow = normalizeOrderRow({ ...ctxRow, ...detail });
|
||||
ctxRow = normalizeOrderRow(mergeOrderListRowWithDetail(ctxRow, detail));
|
||||
} catch {
|
||||
/* keep list row */
|
||||
}
|
||||
|
||||
@ -207,10 +207,10 @@
|
||||
{{ getOrderValue(fieldKey) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="isOrderBooleanField(fieldKey)">
|
||||
<template v-else-if="shouldRenderOrderBooleanTag(fieldKey)">
|
||||
<a-tag
|
||||
size="small"
|
||||
:style="getOrderBooleanTagStyle(fieldKey)"
|
||||
:style="getBooleanTagStyle(getOrderValue(fieldKey) === $t('order.myOrders.drawer.yes'))"
|
||||
>
|
||||
{{ getOrderValue(fieldKey) }}
|
||||
</a-tag>
|
||||
@ -1037,10 +1037,16 @@ import {
|
||||
ORDER_DETAIL_VALUE_FIELD_MAP,
|
||||
buildDeclarationPayloadFromRows,
|
||||
cloneDeclarationItemRows,
|
||||
formatOrderDetailBooleanLabel,
|
||||
formatOrderDetailExtendedValue,
|
||||
isOrderDetailBooleanField,
|
||||
isOrderDetailYesNoLabel,
|
||||
mergeOrderBasicFields,
|
||||
parseAdditionalServiceCodes,
|
||||
patchOrderBasicFieldGroups,
|
||||
pickOrderDetailArray,
|
||||
pickOrderDetailBooleanRaw,
|
||||
pickOrderDetailNested,
|
||||
} from '../../components/orderDetailFieldConfig';
|
||||
import { openAskAdvisor } from '@/composables/useAskAdvisor';
|
||||
import { buildFailGuideFromReason } from '@/utils/parcelCreateFailGuide';
|
||||
@ -1393,14 +1399,9 @@ function getOrderFieldLabelKey(fieldKey: string) {
|
||||
return orderBasicFields.find((field) => field.key === fieldKey)?.labelKey ?? fieldKey;
|
||||
}
|
||||
|
||||
function isOrderBooleanField(fieldKey: string) {
|
||||
return ['isCod', 'isIntercept', 'isRemote', 'isPaid'].includes(fieldKey);
|
||||
}
|
||||
|
||||
function getOrderBooleanTagStyle(fieldKey: string) {
|
||||
const val = getOrderValue(fieldKey);
|
||||
const yes = t('order.myOrders.drawer.yes');
|
||||
return getBooleanTagStyle(val === yes);
|
||||
function shouldRenderOrderBooleanTag(fieldKey: string) {
|
||||
if (isOrderDetailBooleanField(fieldKey)) return true;
|
||||
return isOrderDetailYesNoLabel(getOrderValue(fieldKey), t);
|
||||
}
|
||||
|
||||
const declarationSkuPreviewVisible = ref(false);
|
||||
@ -1819,18 +1820,15 @@ function getOrderValue(key: string) {
|
||||
shipTime: ['shipTime'],
|
||||
payTime: ['payTime'],
|
||||
signTime: ['signTime'],
|
||||
isPaid: ['isPaid', 'IsPaid'],
|
||||
isCod: ['isCod'],
|
||||
codCurrency: ['codCurrency'],
|
||||
codSum: ['codSum'],
|
||||
isIntercept: ['isIntercept'],
|
||||
isRemote: ['isRemote'],
|
||||
createTime: ['createTime'],
|
||||
createBy: ['createBy'],
|
||||
updateTime: ['updateTime'],
|
||||
updateBy: ['updateBy'],
|
||||
...ORDER_DETAIL_VALUE_FIELD_MAP,
|
||||
};
|
||||
if (isOrderDetailBooleanField(key)) {
|
||||
return formatOrderDetailBooleanLabel(pickOrderDetailBooleanRaw(o, key), t);
|
||||
}
|
||||
const keys = fieldMap[key] || [key];
|
||||
let val: any;
|
||||
for (const k of keys) {
|
||||
@ -1840,9 +1838,6 @@ function getOrderValue(key: string) {
|
||||
}
|
||||
}
|
||||
if (val == null || val === '') return '--';
|
||||
if (key === 'isCod' || key === 'isIntercept' || key === 'isRemote' || key === 'isPaid') {
|
||||
return val ? t('order.myOrders.drawer.yes') : t('order.myOrders.drawer.no');
|
||||
}
|
||||
if (key === 'orderStatus') {
|
||||
const code = resolveOrderStatusCode(o);
|
||||
const meta = getOrderStatusMeta(code);
|
||||
@ -1867,7 +1862,7 @@ function getSenderValue(key: string) {
|
||||
}
|
||||
const val = sender.value[key];
|
||||
if (key === 'isDefault') {
|
||||
return val == null ? '--' : (val ? t('order.myOrders.drawer.yes') : t('order.myOrders.drawer.no'));
|
||||
return formatOrderDetailBooleanLabel(val, t);
|
||||
}
|
||||
return val ?? '--';
|
||||
}
|
||||
@ -2462,16 +2457,22 @@ function loadDetailData() {
|
||||
const o = detail.value;
|
||||
if (!o) return;
|
||||
|
||||
// 发件人
|
||||
if (o.sender && typeof o.sender === 'object') {
|
||||
sender.value = { ...o.sender, country: o.sender.country ?? o.sender.countryCode };
|
||||
const senderRaw = pickOrderDetailNested<Record<string, any>>(o, 'sender', 'Sender');
|
||||
if (senderRaw && typeof senderRaw === 'object') {
|
||||
sender.value = {
|
||||
...senderRaw,
|
||||
country: senderRaw.country ?? senderRaw.countryCode ?? senderRaw.CountryCode,
|
||||
};
|
||||
} else {
|
||||
sender.value = {};
|
||||
}
|
||||
|
||||
// 收件人:优先使用 list 返回的 receiver
|
||||
if (o.receiver && typeof o.receiver === 'object') {
|
||||
recipient.value = { ...o.receiver, country: o.receiver.country ?? o.receiver.countryCode };
|
||||
const receiverRaw = pickOrderDetailNested<Record<string, any>>(o, 'receiver', 'Receiver');
|
||||
if (receiverRaw && typeof receiverRaw === 'object') {
|
||||
recipient.value = {
|
||||
...receiverRaw,
|
||||
country: receiverRaw.country ?? receiverRaw.countryCode ?? receiverRaw.CountryCode,
|
||||
};
|
||||
} else {
|
||||
recipient.value = {
|
||||
recipientsName: o.consignee || '',
|
||||
@ -2496,17 +2497,12 @@ function loadDetailData() {
|
||||
};
|
||||
}
|
||||
|
||||
// 申报信息
|
||||
declarationList.value = Array.isArray(o.items) && o.items.length > 0
|
||||
? cloneDeclarationItemRows(o.items)
|
||||
: [];
|
||||
const itemsRaw = pickOrderDetailArray(o, 'items', 'Items');
|
||||
declarationList.value = itemsRaw.length > 0 ? cloneDeclarationItemRows(itemsRaw) : [];
|
||||
|
||||
// 件重尺
|
||||
weightList.value = Array.isArray(o.weightList) && o.weightList.length > 0 ? o.weightList : [];
|
||||
weightList.value = pickOrderDetailArray(o, 'weightList', 'WeightList');
|
||||
|
||||
// 费用:接口字段可为 feeList / orderFees / FeeList
|
||||
const fees = o.feeList ?? o.FeeList ?? o.orderFees ?? o.fees;
|
||||
feeList.value = Array.isArray(fees) ? fees : [];
|
||||
feeList.value = pickOrderDetailArray(o, 'feeList', 'FeeList', 'orderFees', 'fees');
|
||||
}
|
||||
|
||||
function applyDetailFromApi(raw: any) {
|
||||
|
||||
@ -74,6 +74,7 @@
|
||||
WAYBILL_MANAGE_DEFAULT_KEYS,
|
||||
getWaybillBaseCatalog,
|
||||
} from './columnConfig';
|
||||
import { mergeOrderListRowWithDetail } from '../components/orderDetailFieldConfig';
|
||||
import {
|
||||
fetchPrintUrlForOrder,
|
||||
fetchPrintUrlsForOrders,
|
||||
@ -209,11 +210,17 @@
|
||||
return map;
|
||||
}
|
||||
|
||||
/** 列表行与表格列对齐:只保留标量/展平字段,不保留嵌套树(Items/FeeList 等) */
|
||||
/**
|
||||
* 列表行与表格列对齐:展平寄收件人等列字段;
|
||||
* 同时保留 sender/receiver/items/weightList/feeList,供详情抽屉使用
|
||||
*(勿再整棵丢掉,否则 /order/detail 嵌套数据会被 normalize 冲掉)。
|
||||
*/
|
||||
function normalizeOrderRow(raw: any, customerLabelMap?: Map<number, string>) {
|
||||
const r = raw?.receiver || raw?.Receiver || {};
|
||||
const s = raw?.sender || raw?.Sender || {};
|
||||
const wl = raw?.weightList || raw?.WeightList;
|
||||
const itemsRaw = raw?.items || raw?.Items;
|
||||
const feeRaw = raw?.feeList || raw?.FeeList || raw?.orderFees || raw?.fees;
|
||||
const lastW =
|
||||
Array.isArray(wl) && wl.length ? wl[wl.length - 1] : undefined;
|
||||
|
||||
@ -466,6 +473,12 @@
|
||||
consigneeCountry && consigneeCountryName
|
||||
? `${consigneeCountry} ${consigneeCountryName}`
|
||||
: consigneeCountryName || consigneeCountry || null,
|
||||
// 详情抽屉依赖嵌套对象;列表接口 items/weightList 常为空数组,有内容时以 raw 为准
|
||||
sender: s && typeof s === 'object' && Object.keys(s).length ? s : undefined,
|
||||
receiver: r && typeof r === 'object' && Object.keys(r).length ? r : undefined,
|
||||
items: Array.isArray(itemsRaw) ? itemsRaw : [],
|
||||
weightList: Array.isArray(wl) ? wl : [],
|
||||
feeList: Array.isArray(feeRaw) ? feeRaw : [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -478,7 +491,10 @@
|
||||
if (id != null && id !== '') {
|
||||
try {
|
||||
const detail = await getOrderDetail(id);
|
||||
nextRow = normalizeOrderRow({ ...row, ...detail }, buildCustomerLabelMap());
|
||||
nextRow = normalizeOrderRow(
|
||||
mergeOrderListRowWithDetail(row, detail),
|
||||
buildCustomerLabelMap(),
|
||||
);
|
||||
} catch {
|
||||
/* 保留列表行数据 */
|
||||
}
|
||||
@ -1735,7 +1751,10 @@
|
||||
if (rows.length === 1 && id != null) {
|
||||
try {
|
||||
const detail = await getOrderDetail(id);
|
||||
ctxRow = normalizeOrderRow({ ...ctxRow, ...detail });
|
||||
ctxRow = normalizeOrderRow(
|
||||
mergeOrderListRowWithDetail(ctxRow, detail),
|
||||
buildCustomerLabelMap(),
|
||||
);
|
||||
} catch {
|
||||
/* keep list row */
|
||||
}
|
||||
|
||||
@ -68,6 +68,11 @@ import {
|
||||
} from './providerCatalog';
|
||||
import { buildAiViewTree, type AiViewKey } from './viewsCatalog';
|
||||
|
||||
defineOptions({
|
||||
// 与路由 name 对齐;index.vue 默认推断为 index,会导致切标签无法缓存
|
||||
name: 'SystemAiAssistant',
|
||||
});
|
||||
|
||||
const DEFAULT_VIEW: AiViewKey = 'providers';
|
||||
|
||||
const selectedViewKeys = ref<string[]>([DEFAULT_VIEW]);
|
||||
|
||||
@ -147,6 +147,11 @@ import {
|
||||
type AiProviderItem,
|
||||
} from '@/views/system/aiAssistant/providerCatalog';
|
||||
|
||||
defineOptions({
|
||||
// 与路由 name 对齐;index.vue 默认推断为 index,会导致切标签无法缓存
|
||||
name: 'SystemMcpTools',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user