feat: 接口参数复制带注释 JSON,下单自寄到仓展示仓库地址
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m56s
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m56s
便于调试时一键复制 Body/Response 参数树,并让客户在自寄到仓时快速查看与复制仓库收货信息。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a5ef7e542d
commit
2b3797d548
@ -42,6 +42,8 @@
|
|||||||
} from '@/utils/interfaceParameterModel';
|
} from '@/utils/interfaceParameterModel';
|
||||||
import {
|
import {
|
||||||
applyJsonCommentsToExistingRows,
|
applyJsonCommentsToExistingRows,
|
||||||
|
buildCommentedJsonFromKvRows,
|
||||||
|
buildCommentedJsonFromParameterFlatList,
|
||||||
defaultResolveParameterType,
|
defaultResolveParameterType,
|
||||||
extractCommentsFromJson,
|
extractCommentsFromJson,
|
||||||
flatParameterListToTableRows,
|
flatParameterListToTableRows,
|
||||||
@ -2691,16 +2693,11 @@
|
|||||||
dataList1.value = undefined;
|
dataList1.value = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制 cURL tab 输入框内容到剪贴板
|
async function writeTextToClipboard(content: string) {
|
||||||
async function copyRequestExampleContent() {
|
|
||||||
const content = requestExample.value || '';
|
|
||||||
if (!content.trim()) {
|
|
||||||
Message.warning('暂无内容可复制');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(content);
|
await navigator.clipboard.writeText(content);
|
||||||
Message.success('已复制到剪贴板');
|
Message.success('已复制到剪贴板');
|
||||||
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
const textArea = document.createElement('textarea');
|
const textArea = document.createElement('textarea');
|
||||||
textArea.value = content;
|
textArea.value = content;
|
||||||
@ -2711,12 +2708,66 @@
|
|||||||
try {
|
try {
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
Message.success('已复制到剪贴板');
|
Message.success('已复制到剪贴板');
|
||||||
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
Message.error('复制失败');
|
Message.error('复制失败');
|
||||||
}
|
return false;
|
||||||
|
} finally {
|
||||||
document.body.removeChild(textArea);
|
document.body.removeChild(textArea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制 cURL tab 输入框内容到剪贴板
|
||||||
|
async function copyRequestExampleContent() {
|
||||||
|
const content = requestExample.value || '';
|
||||||
|
if (!content.trim()) {
|
||||||
|
Message.warning('暂无内容可复制');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await writeTextToClipboard(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 复制当前 Body 参数列表为带 // 描述的格式化 JSON */
|
||||||
|
async function copyBodyParamsAsCommentedJson() {
|
||||||
|
if (isNoneBodyContentType.value) {
|
||||||
|
Message.warning('当前请求类型为 none,无 Body 参数可复制');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
if (isBodyKvMode.value) {
|
||||||
|
const rows = getTableFullData(bodyFormTableRef, bodyFormTableProps.data);
|
||||||
|
content = buildCommentedJsonFromKvRows(rows);
|
||||||
|
} else {
|
||||||
|
const rows = getRequestBodyFlatParams();
|
||||||
|
content = buildCommentedJsonFromParameterFlatList(rows, {
|
||||||
|
isRootRow: isBodyRootRow,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const compact = content.replace(/\s+/g, '');
|
||||||
|
if (!compact || compact === '{}' || compact === '[]') {
|
||||||
|
Message.warning('暂无参数可复制');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await writeTextToClipboard(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 复制当前 Response Body 参数列表为带 // 描述的格式化 JSON */
|
||||||
|
async function copyResponseParamsAsCommentedJson() {
|
||||||
|
const fromGrid =
|
||||||
|
responseTableRef.value?.getTableData?.()?.fullData
|
||||||
|
|| responseTableRef.value?.xGrid?.getTableData()?.fullData;
|
||||||
|
const rows = getTreeTableFlatRows(fromGrid, responseTableProps.data);
|
||||||
|
const content = buildCommentedJsonFromParameterFlatList(rows);
|
||||||
|
const compact = content.replace(/\s+/g, '');
|
||||||
|
if (!compact || compact === '{}' || compact === '[]') {
|
||||||
|
Message.warning('暂无参数可复制');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await writeTextToClipboard(content);
|
||||||
|
}
|
||||||
|
|
||||||
// 取消 Request 参数解析
|
// 取消 Request 参数解析
|
||||||
function cancelClick() {
|
function cancelClick() {
|
||||||
@ -3656,6 +3707,14 @@
|
|||||||
aria-label="Request content type"
|
aria-label="Request content type"
|
||||||
@change="updateContentType"
|
@change="updateContentType"
|
||||||
/>
|
/>
|
||||||
|
<a-button
|
||||||
|
size="mini"
|
||||||
|
class="content-type-copy-btn"
|
||||||
|
title="复制当前参数为带描述的 JSON"
|
||||||
|
@click="copyBodyParamsAsCommentedJson"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<!-- Query tab 内容 -->
|
<!-- Query tab 内容 -->
|
||||||
<div v-show="requestTab === 'query'" class="query-tab-pane">
|
<div v-show="requestTab === 'query'" class="query-tab-pane">
|
||||||
@ -4428,6 +4487,14 @@
|
|||||||
aria-label="Response content type"
|
aria-label="Response content type"
|
||||||
@change="updateResponseContentType"
|
@change="updateResponseContentType"
|
||||||
/>
|
/>
|
||||||
|
<a-button
|
||||||
|
size="mini"
|
||||||
|
class="content-type-copy-btn"
|
||||||
|
title="复制当前参数为带描述的 JSON"
|
||||||
|
@click="copyResponseParamsAsCommentedJson"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
<!-- Response body tab 内容 -->
|
<!-- Response body tab 内容 -->
|
||||||
<div v-show="responseTab === 'body'" class="table-content">
|
<div v-show="responseTab === 'body'" class="table-content">
|
||||||
@ -5149,7 +5216,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--app-button-gap, 8px);
|
gap: var(--app-button-gap, 8px);
|
||||||
padding: 0 0 10px var(--vxe-table-cell-padding-left, 10px);
|
padding: 0 var(--vxe-table-cell-padding-left, 10px) 10px;
|
||||||
min-height: 28px;
|
min-height: 28px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@ -5159,6 +5226,14 @@
|
|||||||
.content-type-tabs {
|
.content-type-tabs {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-type-copy-btn {
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apifox 式虚拟根节点(仅 Json/Xml 展示,导出剥离) */
|
/* Apifox 式虚拟根节点(仅 Json/Xml 展示,导出剥离) */
|
||||||
|
|||||||
@ -2425,6 +2425,19 @@ export default {
|
|||||||
'For multiple shipments, select waybills in Waybill Manage to create pickups in bulk',
|
'For multiple shipments, select waybills in Waybill Manage to create pickups in bulk',
|
||||||
'order.placeOrder.single.doorPickupBatchHintCustomer':
|
'order.placeOrder.single.doorPickupBatchHintCustomer':
|
||||||
'For multiple shipments, select orders in My Orders to create pickups in bulk',
|
'For multiple shipments, select orders in My Orders to create pickups in bulk',
|
||||||
|
'order.placeOrder.single.selfShipAddressTitle': 'Ship to warehouse',
|
||||||
|
'order.placeOrder.single.selfShipRecipient': 'Recipient',
|
||||||
|
'order.placeOrder.single.selfShipPhone': 'Phone',
|
||||||
|
'order.placeOrder.single.selfShipAddress': 'Address',
|
||||||
|
'order.placeOrder.single.selfShipClientNo': 'Client No.',
|
||||||
|
'order.placeOrder.single.selfShipCopy': 'Copy',
|
||||||
|
'order.placeOrder.single.selfShipCopySuccess': 'Warehouse shipping info copied',
|
||||||
|
'order.placeOrder.single.selfShipCopyFailed': 'Copy failed',
|
||||||
|
'order.placeOrder.single.selfShipSelectWarehouse': 'Select a warehouse above first',
|
||||||
|
'order.placeOrder.single.selfShipAddressEmpty':
|
||||||
|
'This warehouse has no shipping address yet. Contact support or choose another warehouse.',
|
||||||
|
'order.placeOrder.single.selfShipAddressHint':
|
||||||
|
'Ship to this address yourself. Put the client number on the courier label so the warehouse can match it.',
|
||||||
'order.placeOrder.single.submitHint': 'Complete required fields and accept the agreement to submit',
|
'order.placeOrder.single.submitHint': 'Complete required fields and accept the agreement to submit',
|
||||||
'order.placeOrder.single.submitReady': 'Ready to submit',
|
'order.placeOrder.single.submitReady': 'Ready to submit',
|
||||||
'order.placeOrder.contact.blockTitle': 'Complete your contact info',
|
'order.placeOrder.contact.blockTitle': 'Complete your contact info',
|
||||||
|
|||||||
@ -2318,6 +2318,18 @@ export default {
|
|||||||
'多票上门揽收可在「运单管理」中勾选运单后批量创建揽收单',
|
'多票上门揽收可在「运单管理」中勾选运单后批量创建揽收单',
|
||||||
'order.placeOrder.single.doorPickupBatchHintCustomer':
|
'order.placeOrder.single.doorPickupBatchHintCustomer':
|
||||||
'多票上门揽收可在「我的订单」中勾选运单后批量创建揽收单',
|
'多票上门揽收可在「我的订单」中勾选运单后批量创建揽收单',
|
||||||
|
'order.placeOrder.single.selfShipAddressTitle': '寄往仓库',
|
||||||
|
'order.placeOrder.single.selfShipRecipient': '收件人',
|
||||||
|
'order.placeOrder.single.selfShipPhone': '电话',
|
||||||
|
'order.placeOrder.single.selfShipAddress': '地址',
|
||||||
|
'order.placeOrder.single.selfShipClientNo': '客户单号',
|
||||||
|
'order.placeOrder.single.selfShipCopy': '复制',
|
||||||
|
'order.placeOrder.single.selfShipCopySuccess': '仓库寄件信息已复制',
|
||||||
|
'order.placeOrder.single.selfShipCopyFailed': '复制失败',
|
||||||
|
'order.placeOrder.single.selfShipSelectWarehouse': '请先选择上方仓库',
|
||||||
|
'order.placeOrder.single.selfShipAddressEmpty': '该仓库暂未配置收货地址,请联系客服或更换仓库',
|
||||||
|
'order.placeOrder.single.selfShipAddressHint':
|
||||||
|
'按此地址自行寄出。快递面单建议备注客户单号,方便仓库认领。',
|
||||||
'order.placeOrder.single.submitHint': '请完善必填信息并勾选协议后提交',
|
'order.placeOrder.single.submitHint': '请完善必填信息并勾选协议后提交',
|
||||||
'order.placeOrder.single.submitReady': '信息已完善,可提交下单',
|
'order.placeOrder.single.submitReady': '信息已完善,可提交下单',
|
||||||
'order.placeOrder.contact.blockTitle': '请先完善联系方式',
|
'order.placeOrder.contact.blockTitle': '请先完善联系方式',
|
||||||
|
|||||||
@ -586,3 +586,233 @@ export function reorderJsonBodiesForMapping(
|
|||||||
if (!best) return candidates;
|
if (!best) return candidates;
|
||||||
return [best, ...candidates.filter((c) => c !== best)];
|
return [best, ...candidates.filter((c) => c !== best)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeJsonComment(text: string): string {
|
||||||
|
return String(text || '')
|
||||||
|
.replace(/\r?\n/g, ' ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleLiteralByParameterType(type: unknown): string {
|
||||||
|
const t = Number(type);
|
||||||
|
if (t === ParameterType.Integer || t === ParameterType.Float) return '0';
|
||||||
|
if (t === ParameterType.Boolean) return 'false';
|
||||||
|
if (t === ParameterType.Object) return '{}';
|
||||||
|
if (t === ParameterType.Array) return '[]';
|
||||||
|
return '""';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTopLevelParameterPid(pId: unknown): boolean {
|
||||||
|
return pId == null || pId === '' || pId === 0 || pId === '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildCommentedJsonOptions {
|
||||||
|
isRootRow?: (row: any) => boolean;
|
||||||
|
indentSize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将参数树扁平列表导出为带 // 参数描述的 JSON(JSON5 风格,可再导入注释)。
|
||||||
|
* 叶子用类型占位值;Object/Array 按子节点展开;跳过虚拟根与 obj/val/array 占位名。
|
||||||
|
*/
|
||||||
|
export function buildCommentedJsonFromParameterFlatList(
|
||||||
|
rows: any[],
|
||||||
|
options?: BuildCommentedJsonOptions,
|
||||||
|
): string {
|
||||||
|
const list = (Array.isArray(rows) ? rows : []).filter(Boolean);
|
||||||
|
const isRootRow = options?.isRootRow ?? (() => false);
|
||||||
|
const indentSize = options?.indentSize ?? 2;
|
||||||
|
const pad = (depth: number) => ' '.repeat(Math.max(0, depth) * indentSize);
|
||||||
|
|
||||||
|
const byId = new Map<any, any>();
|
||||||
|
list.forEach((r) => {
|
||||||
|
if (r?.id != null) byId.set(r.id, r);
|
||||||
|
});
|
||||||
|
|
||||||
|
const childrenOf = new Map<any, any[]>();
|
||||||
|
const ensureBucket = (key: any) => {
|
||||||
|
if (!childrenOf.has(key)) childrenOf.set(key, []);
|
||||||
|
return childrenOf.get(key)!;
|
||||||
|
};
|
||||||
|
|
||||||
|
list.forEach((row) => {
|
||||||
|
if (isRootRow(row)) return;
|
||||||
|
const pId = row.pId ?? row.PId;
|
||||||
|
const parent = byId.get(pId);
|
||||||
|
const bucketKey =
|
||||||
|
isTopLevelParameterPid(pId) || (parent && isRootRow(parent)) ? 0 : pId;
|
||||||
|
ensureBucket(bucketKey).push(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getChildren = (id: any) => childrenOf.get(id) || [];
|
||||||
|
|
||||||
|
function formatComment(desc: unknown): string {
|
||||||
|
const text = escapeJsonComment(String(desc ?? ''));
|
||||||
|
return text ? ` // ${text}` : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderObjectProps(nodes: any[], depth: number): string {
|
||||||
|
const props = nodes.filter((n) => {
|
||||||
|
const name = String(n?.name ?? '').trim();
|
||||||
|
return name && !SYNTHETIC_PARAM_NAMES.has(name);
|
||||||
|
});
|
||||||
|
if (!props.length) return `${pad(depth)}{}`;
|
||||||
|
|
||||||
|
const lines: string[] = [`${pad(depth)}{`];
|
||||||
|
props.forEach((node, index) => {
|
||||||
|
const comma = index < props.length - 1 ? ',' : '';
|
||||||
|
lines.push(...renderPropertyLines(node, depth + 1, comma));
|
||||||
|
});
|
||||||
|
lines.push(`${pad(depth)}}`);
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPropertyLines(node: any, depth: number, comma: string): string[] {
|
||||||
|
const name = String(node?.name ?? '').trim();
|
||||||
|
const key = JSON.stringify(name);
|
||||||
|
const comment = formatComment(node?.description);
|
||||||
|
const type = Number(node?.type);
|
||||||
|
const kids = getChildren(node.id);
|
||||||
|
const indent = pad(depth);
|
||||||
|
|
||||||
|
if (type === ParameterType.Object || (kids.length > 0 && type !== ParameterType.Array)) {
|
||||||
|
const innerProps = kids.filter((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim();
|
||||||
|
return n && !SYNTHETIC_PARAM_NAMES.has(n);
|
||||||
|
});
|
||||||
|
if (!innerProps.length && type === ParameterType.Object) {
|
||||||
|
return [`${indent}${key}: {}${comma}${comment}`];
|
||||||
|
}
|
||||||
|
const lines: string[] = [`${indent}${key}: {${comment}`];
|
||||||
|
innerProps.forEach((child, index) => {
|
||||||
|
const childComma = index < innerProps.length - 1 ? ',' : '';
|
||||||
|
lines.push(...renderPropertyLines(child, depth + 1, childComma));
|
||||||
|
});
|
||||||
|
lines.push(`${indent}}${comma}`);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === ParameterType.Array) {
|
||||||
|
return renderArrayPropertyLines(node, depth, comma, key, comment, kids);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [`${indent}${key}: ${sampleLiteralByParameterType(type)}${comma}${comment}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderArrayPropertyLines(
|
||||||
|
_node: any,
|
||||||
|
depth: number,
|
||||||
|
comma: string,
|
||||||
|
key: string,
|
||||||
|
comment: string,
|
||||||
|
kids: any[],
|
||||||
|
): string[] {
|
||||||
|
const indent = pad(depth);
|
||||||
|
const objChild = kids.find((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim().toLowerCase();
|
||||||
|
return n === 'obj' || Number(k?.type) === ParameterType.Object;
|
||||||
|
});
|
||||||
|
const valChild = kids.find((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim().toLowerCase();
|
||||||
|
return n === 'val';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (objChild) {
|
||||||
|
const fields = getChildren(objChild.id).filter((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim();
|
||||||
|
return n && !SYNTHETIC_PARAM_NAMES.has(n);
|
||||||
|
});
|
||||||
|
if (!fields.length) {
|
||||||
|
return [`${indent}${key}: [{}]${comma}${comment}`];
|
||||||
|
}
|
||||||
|
const lines: string[] = [`${indent}${key}: [${comment}`, `${pad(depth + 1)}{`];
|
||||||
|
fields.forEach((child, index) => {
|
||||||
|
const childComma = index < fields.length - 1 ? ',' : '';
|
||||||
|
lines.push(...renderPropertyLines(child, depth + 2, childComma));
|
||||||
|
});
|
||||||
|
lines.push(`${pad(depth + 1)}}`, `${indent}]${comma}`);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valChild) {
|
||||||
|
const sample = sampleLiteralByParameterType(valChild.type);
|
||||||
|
return [`${indent}${key}: [${sample}]${comma}${comment}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
const namedKids = kids.filter((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim();
|
||||||
|
return n && !SYNTHETIC_PARAM_NAMES.has(n);
|
||||||
|
});
|
||||||
|
if (namedKids.length) {
|
||||||
|
const lines: string[] = [`${indent}${key}: [${comment}`, `${pad(depth + 1)}{`];
|
||||||
|
namedKids.forEach((child, index) => {
|
||||||
|
const childComma = index < namedKids.length - 1 ? ',' : '';
|
||||||
|
lines.push(...renderPropertyLines(child, depth + 2, childComma));
|
||||||
|
});
|
||||||
|
lines.push(`${pad(depth + 1)}}`, `${indent}]${comma}`);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [`${indent}${key}: []${comma}${comment}`];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tops = getChildren(0);
|
||||||
|
if (!tops.length) return '{\n}\n';
|
||||||
|
|
||||||
|
if (tops.length === 1 && isSyntheticArrayWrapper(tops[0])) {
|
||||||
|
const wrapper = tops[0];
|
||||||
|
const kids = getChildren(wrapper.id);
|
||||||
|
const objChild = kids.find((k) => String(k?.name ?? '').trim().toLowerCase() === 'obj');
|
||||||
|
const valChild = kids.find((k) => String(k?.name ?? '').trim().toLowerCase() === 'val');
|
||||||
|
|
||||||
|
if (objChild) {
|
||||||
|
const fields = getChildren(objChild.id).filter((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim();
|
||||||
|
return n && !SYNTHETIC_PARAM_NAMES.has(n);
|
||||||
|
});
|
||||||
|
if (!fields.length) return '[{}]\n';
|
||||||
|
return `[\n${renderObjectProps(fields, 1)}\n]\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valChild) {
|
||||||
|
return `[${sampleLiteralByParameterType(valChild.type)}]\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const namedKids = kids.filter((k) => {
|
||||||
|
const n = String(k?.name ?? '').trim();
|
||||||
|
return n && !SYNTHETIC_PARAM_NAMES.has(n);
|
||||||
|
});
|
||||||
|
if (namedKids.length) {
|
||||||
|
return `[\n${renderObjectProps(namedKids, 1)}\n]\n`;
|
||||||
|
}
|
||||||
|
return '[]\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${renderObjectProps(tops, 0)}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** FormUrlEncoded / 扁平 KV 列表 → 带 // 描述的 JSON 对象 */
|
||||||
|
export function buildCommentedJsonFromKvRows(rows: any[]): string {
|
||||||
|
const items = (Array.isArray(rows) ? rows : []).filter((r) => {
|
||||||
|
const name = String(r?.name ?? '').trim();
|
||||||
|
return !!name;
|
||||||
|
});
|
||||||
|
if (!items.length) return '{\n}\n';
|
||||||
|
|
||||||
|
const lines: string[] = ['{'];
|
||||||
|
items.forEach((row, index) => {
|
||||||
|
const name = String(row.name).trim();
|
||||||
|
const rawValue = row.value;
|
||||||
|
const value =
|
||||||
|
rawValue == null || rawValue === ''
|
||||||
|
? '""'
|
||||||
|
: JSON.stringify(String(rawValue));
|
||||||
|
const desc = escapeJsonComment(String(row.description ?? ''));
|
||||||
|
const comma = index < items.length - 1 ? ',' : '';
|
||||||
|
const comment = desc ? ` // ${desc}` : '';
|
||||||
|
lines.push(` ${JSON.stringify(name)}: ${value}${comma}${comment}`);
|
||||||
|
});
|
||||||
|
lines.push('}');
|
||||||
|
return `${lines.join('\n')}\n`;
|
||||||
|
}
|
||||||
|
|||||||
@ -231,6 +231,45 @@
|
|||||||
<p v-if="form.inboundMethod === 'doorPickup'" class="h5-po__hint">
|
<p v-if="form.inboundMethod === 'doorPickup'" class="h5-po__hint">
|
||||||
{{ t('h5.placeOrder.inboundDoorPickupHint') }}
|
{{ t('h5.placeOrder.inboundDoorPickupHint') }}
|
||||||
</p>
|
</p>
|
||||||
|
<div v-if="form.inboundMethod === 'selfShip'" class="h5-po__wh-card">
|
||||||
|
<div class="h5-po__wh-head">
|
||||||
|
<strong>
|
||||||
|
{{ t('h5.placeOrder.selfShipAddressTitle') }}
|
||||||
|
<template v-if="selectedWarehouseOption?.label">
|
||||||
|
· {{ selectedWarehouseOption.label }}
|
||||||
|
</template>
|
||||||
|
</strong>
|
||||||
|
<button
|
||||||
|
v-if="selectedWarehouseHasAddress"
|
||||||
|
type="button"
|
||||||
|
class="h5-po__wh-copy"
|
||||||
|
@click="copyWarehouseShipAddress"
|
||||||
|
>
|
||||||
|
{{ t('h5.placeOrder.selfShipCopy') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p v-if="!selectedWarehouseOption" class="h5-po__hint">
|
||||||
|
{{ t('h5.placeOrder.selfShipSelectWarehouse') }}
|
||||||
|
</p>
|
||||||
|
<template v-else-if="!selectedWarehouseHasAddress">
|
||||||
|
<p class="h5-po__hint">{{ t('h5.placeOrder.selfShipAddressEmpty') }}</p>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p>
|
||||||
|
<span>{{ t('h5.placeOrder.selfShipRecipient') }}</span>
|
||||||
|
{{ selectedWarehouseOption.recipient || '-' }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>{{ t('h5.placeOrder.selfShipPhone') }}</span>
|
||||||
|
{{ selectedWarehouseOption.contactPhone || '-' }}
|
||||||
|
</p>
|
||||||
|
<p class="is-addr">
|
||||||
|
<span>{{ t('h5.placeOrder.selfShipAddress') }}</span>
|
||||||
|
{{ selectedWarehouseOption.shippingAddress || '-' }}
|
||||||
|
</p>
|
||||||
|
<p class="h5-po__hint">{{ t('h5.placeOrder.selfShipAddressHint') }}</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="form.inboundMethod === 'doorPickup'">
|
<template v-if="form.inboundMethod === 'doorPickup'">
|
||||||
@ -1062,7 +1101,15 @@ const steps = computed(() => [
|
|||||||
|
|
||||||
const productOptions = ref<{ value: string; label: string; productId?: number }[]>([]);
|
const productOptions = ref<{ value: string; label: string; productId?: number }[]>([]);
|
||||||
const cargoOptions = ref<{ value: string; label: string }[]>([]);
|
const cargoOptions = ref<{ value: string; label: string }[]>([]);
|
||||||
const warehouseOptions = ref<{ value: string; label: string }[]>([]);
|
const warehouseOptions = ref<
|
||||||
|
{
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
recipient?: string;
|
||||||
|
contactPhone?: string;
|
||||||
|
shippingAddress?: string;
|
||||||
|
}[]
|
||||||
|
>([]);
|
||||||
const countryOptions = ref<{ value: string; label: string }[]>([]);
|
const countryOptions = ref<{ value: string; label: string }[]>([]);
|
||||||
const currencyOptions = ref<{ value: string; label: string }[]>([]);
|
const currencyOptions = ref<{ value: string; label: string }[]>([]);
|
||||||
const senderOptions = ref<{ id: number; label: string; data: Record<string, string> }[]>([]);
|
const senderOptions = ref<{ id: number; label: string; data: Record<string, string> }[]>([]);
|
||||||
@ -1242,6 +1289,36 @@ const form = reactive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedWarehouseOption = computed(() => {
|
||||||
|
const id = String(form.warehouseId ?? '').trim();
|
||||||
|
if (!id) return undefined;
|
||||||
|
return warehouseOptions.value.find((opt) => String(opt.value) === id);
|
||||||
|
});
|
||||||
|
const selectedWarehouseHasAddress = computed(() => {
|
||||||
|
const opt = selectedWarehouseOption.value;
|
||||||
|
if (!opt) return false;
|
||||||
|
return Boolean(opt.recipient || opt.contactPhone || opt.shippingAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function copyWarehouseShipAddress() {
|
||||||
|
const opt = selectedWarehouseOption.value;
|
||||||
|
if (!opt) return;
|
||||||
|
const text = [
|
||||||
|
opt.label || '',
|
||||||
|
`${t('h5.placeOrder.selfShipRecipient')}:${opt.recipient || '-'}`,
|
||||||
|
`${t('h5.placeOrder.selfShipPhone')}:${opt.contactPhone || '-'}`,
|
||||||
|
`${t('h5.placeOrder.selfShipAddress')}:${opt.shippingAddress || '-'}`,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n');
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
Message.success(t('h5.placeOrder.selfShipCopySuccess'));
|
||||||
|
} catch {
|
||||||
|
Message.error(t('h5.orders.copyFailed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function trim(v: unknown) {
|
function trim(v: unknown) {
|
||||||
return String(v ?? '').trim();
|
return String(v ?? '').trim();
|
||||||
}
|
}
|
||||||
@ -2556,7 +2633,20 @@ async function loadOptions(
|
|||||||
.map((row: any) => {
|
.map((row: any) => {
|
||||||
const id = row?.id ?? row?.Id;
|
const id = row?.id ?? row?.Id;
|
||||||
const name = String(row?.warehouseName ?? row?.WarehouseName ?? '').trim();
|
const name = String(row?.warehouseName ?? row?.WarehouseName ?? '').trim();
|
||||||
return { value: String(id ?? ''), label: name || String(id) };
|
const pick = (...keys: string[]) => {
|
||||||
|
for (const k of keys) {
|
||||||
|
const v = row?.[k];
|
||||||
|
if (v !== undefined && v !== null && String(v).trim() !== '') return String(v).trim();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
value: String(id ?? ''),
|
||||||
|
label: name || String(id),
|
||||||
|
recipient: pick('recipient', 'Recipient'),
|
||||||
|
contactPhone: pick('contactPhone', 'ContactPhone'),
|
||||||
|
shippingAddress: pick('shippingAddress', 'ShippingAddress'),
|
||||||
|
};
|
||||||
})
|
})
|
||||||
.filter((x) => x.value);
|
.filter((x) => x.value);
|
||||||
if (!opts?.skipDefaults && warehouseOptions.value.length && !form.warehouseId) {
|
if (!opts?.skipDefaults && warehouseOptions.value.length && !form.warehouseId) {
|
||||||
@ -3030,6 +3120,50 @@ watch(
|
|||||||
color: #86909c;
|
color: #86909c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h5-po__wh-card {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid color-mix(in srgb, var(--app-primary-color) 16%, #e5e6eb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-po__wh-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
strong {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-po__wh-copy {
|
||||||
|
border: none;
|
||||||
|
background: color-mix(in srgb, var(--app-primary-color) 12%, #fff);
|
||||||
|
color: var(--app-primary-color);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-po__wh-card p {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1d2129;
|
||||||
|
line-height: 1.45;
|
||||||
|
span {
|
||||||
|
color: #86909c;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
&.is-addr {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.h5-po__more-panel {
|
.h5-po__more-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -393,6 +393,17 @@ export default {
|
|||||||
'h5.placeOrder.inboundDoorPickup': 'Door pickup',
|
'h5.placeOrder.inboundDoorPickup': 'Door pickup',
|
||||||
'h5.placeOrder.inboundDoorPickupHint':
|
'h5.placeOrder.inboundDoorPickupHint':
|
||||||
'Door pickup is a paid service. Please fill in the pickup address and expected time.',
|
'Door pickup is a paid service. Please fill in the pickup address and expected time.',
|
||||||
|
'h5.placeOrder.selfShipAddressTitle': 'Ship to warehouse',
|
||||||
|
'h5.placeOrder.selfShipRecipient': 'Recipient',
|
||||||
|
'h5.placeOrder.selfShipPhone': 'Phone',
|
||||||
|
'h5.placeOrder.selfShipAddress': 'Address',
|
||||||
|
'h5.placeOrder.selfShipCopy': 'Copy',
|
||||||
|
'h5.placeOrder.selfShipCopySuccess': 'Warehouse shipping info copied',
|
||||||
|
'h5.placeOrder.selfShipSelectWarehouse': 'Select a warehouse first',
|
||||||
|
'h5.placeOrder.selfShipAddressEmpty':
|
||||||
|
'This warehouse has no shipping address yet. Contact support or choose another warehouse.',
|
||||||
|
'h5.placeOrder.selfShipAddressHint':
|
||||||
|
'Ship to this address yourself. Put the client number on the courier label so the warehouse can match it.',
|
||||||
'h5.placeOrder.pickupAddress': 'Pickup address',
|
'h5.placeOrder.pickupAddress': 'Pickup address',
|
||||||
'h5.placeOrder.pickupAddressPh': 'Select a saved sender address',
|
'h5.placeOrder.pickupAddressPh': 'Select a saved sender address',
|
||||||
'h5.placeOrder.expectPickupTime': 'Expected pickup time',
|
'h5.placeOrder.expectPickupTime': 'Expected pickup time',
|
||||||
|
|||||||
@ -391,6 +391,15 @@ export default {
|
|||||||
'h5.placeOrder.inboundSelfShip': '自寄到仓',
|
'h5.placeOrder.inboundSelfShip': '自寄到仓',
|
||||||
'h5.placeOrder.inboundDoorPickup': '上门揽收',
|
'h5.placeOrder.inboundDoorPickup': '上门揽收',
|
||||||
'h5.placeOrder.inboundDoorPickupHint': '上门揽收为付费服务,请填写取件地址与期望揽收时间。',
|
'h5.placeOrder.inboundDoorPickupHint': '上门揽收为付费服务,请填写取件地址与期望揽收时间。',
|
||||||
|
'h5.placeOrder.selfShipAddressTitle': '寄往仓库',
|
||||||
|
'h5.placeOrder.selfShipRecipient': '收件人',
|
||||||
|
'h5.placeOrder.selfShipPhone': '电话',
|
||||||
|
'h5.placeOrder.selfShipAddress': '地址',
|
||||||
|
'h5.placeOrder.selfShipCopy': '复制',
|
||||||
|
'h5.placeOrder.selfShipCopySuccess': '仓库寄件信息已复制',
|
||||||
|
'h5.placeOrder.selfShipSelectWarehouse': '请先选择仓库',
|
||||||
|
'h5.placeOrder.selfShipAddressEmpty': '该仓库暂未配置收货地址,请联系客服或更换仓库',
|
||||||
|
'h5.placeOrder.selfShipAddressHint': '按此地址自行寄出。快递面单建议备注客户单号,方便仓库认领。',
|
||||||
'h5.placeOrder.pickupAddress': '取件地址',
|
'h5.placeOrder.pickupAddress': '取件地址',
|
||||||
'h5.placeOrder.pickupAddressPh': '请选择发件地址簿',
|
'h5.placeOrder.pickupAddressPh': '请选择发件地址簿',
|
||||||
'h5.placeOrder.expectPickupTime': '期望揽收时间',
|
'h5.placeOrder.expectPickupTime': '期望揽收时间',
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
IconEdit,
|
IconEdit,
|
||||||
IconQuestionCircle,
|
IconQuestionCircle,
|
||||||
IconCheck,
|
IconCheck,
|
||||||
|
IconCopy,
|
||||||
} from '@arco-design/web-vue/es/icon';
|
} from '@arco-design/web-vue/es/icon';
|
||||||
import SlidingTabs from '@/components/SlidingTabs/index.vue';
|
import SlidingTabs from '@/components/SlidingTabs/index.vue';
|
||||||
import ExcelFileIcon from '@/components/icons/ExcelFileIcon.vue';
|
import ExcelFileIcon from '@/components/icons/ExcelFileIcon.vue';
|
||||||
@ -311,7 +312,12 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
const singleDomesticPickupProductOptions = ref<Array<{ value: string; label: string }>>([]);
|
const singleDomesticPickupProductOptions = ref<Array<{ value: string; label: string }>>([]);
|
||||||
const singleCargoAttributeOptions = ref<SingleSelectOption[]>([]);
|
const singleCargoAttributeOptions = ref<SingleSelectOption[]>([]);
|
||||||
const singleExportCustomsModeOptions = ref<SingleSelectOption[]>([]);
|
const singleExportCustomsModeOptions = ref<SingleSelectOption[]>([]);
|
||||||
const singleWarehouseOptions = ref<SingleSelectOption[]>([]);
|
type WarehouseSelectOption = SingleSelectOption & {
|
||||||
|
recipient?: string;
|
||||||
|
contactPhone?: string;
|
||||||
|
shippingAddress?: string;
|
||||||
|
};
|
||||||
|
const singleWarehouseOptions = ref<WarehouseSelectOption[]>([]);
|
||||||
const singleCurrencyOptions = ref<SingleSelectOption[]>([]);
|
const singleCurrencyOptions = ref<SingleSelectOption[]>([]);
|
||||||
const singleCountryOptions = ref<SingleSelectOption[]>([]);
|
const singleCountryOptions = ref<SingleSelectOption[]>([]);
|
||||||
|
|
||||||
@ -346,7 +352,20 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
function normalizeWarehouseRow(row: any) {
|
function normalizeWarehouseRow(row: any) {
|
||||||
const id = row?.id ?? row?.Id;
|
const id = row?.id ?? row?.Id;
|
||||||
const name = String(row?.warehouseName ?? row?.WarehouseName ?? '').trim();
|
const name = String(row?.warehouseName ?? row?.WarehouseName ?? '').trim();
|
||||||
return { id: id != null && id !== '' ? String(id) : '', name };
|
const pick = (...keys: string[]) => {
|
||||||
|
for (const k of keys) {
|
||||||
|
const v = row?.[k];
|
||||||
|
if (v !== undefined && v !== null && String(v).trim() !== '') return String(v).trim();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
id: id != null && id !== '' ? String(id) : '',
|
||||||
|
name,
|
||||||
|
recipient: pick('recipient', 'Recipient'),
|
||||||
|
contactPhone: pick('contactPhone', 'ContactPhone'),
|
||||||
|
shippingAddress: pick('shippingAddress', 'ShippingAddress'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeCountryRow(row: any) {
|
function normalizeCountryRow(row: any) {
|
||||||
@ -1999,6 +2018,60 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const selectedWarehouseOption = computed(() => {
|
||||||
|
const id = String(singleForm.warehouseId ?? '').trim();
|
||||||
|
if (!id) return undefined;
|
||||||
|
return singleWarehouseOptions.value.find((opt) => String(opt.value) === id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedWarehouseHasAddress = computed(() => {
|
||||||
|
const opt = selectedWarehouseOption.value;
|
||||||
|
if (!opt) return false;
|
||||||
|
return Boolean(opt.recipient || opt.contactPhone || opt.shippingAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildWarehouseShipCopyText() {
|
||||||
|
const opt = selectedWarehouseOption.value;
|
||||||
|
if (!opt) return '';
|
||||||
|
const lines = [
|
||||||
|
opt.label || '',
|
||||||
|
`收件人:${opt.recipient || '-'}`,
|
||||||
|
`电话:${opt.contactPhone || '-'}`,
|
||||||
|
`地址:${opt.shippingAddress || '-'}`,
|
||||||
|
];
|
||||||
|
const clientNo = String(singleForm.clientNo ?? '').trim();
|
||||||
|
if (clientNo) {
|
||||||
|
lines.push(`客户单号:${clientNo}`);
|
||||||
|
}
|
||||||
|
return lines.filter(Boolean).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyWarehouseShipAddress() {
|
||||||
|
const text = buildWarehouseShipCopyText();
|
||||||
|
if (!text.trim()) {
|
||||||
|
Message.warning('该仓库暂未配置收货地址');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
Message.success('仓库寄件信息已复制');
|
||||||
|
} catch {
|
||||||
|
const area = document.createElement('textarea');
|
||||||
|
area.value = text;
|
||||||
|
area.style.position = 'fixed';
|
||||||
|
area.style.left = '-9999px';
|
||||||
|
document.body.appendChild(area);
|
||||||
|
area.select();
|
||||||
|
try {
|
||||||
|
document.execCommand('copy');
|
||||||
|
Message.success('仓库寄件信息已复制');
|
||||||
|
} catch {
|
||||||
|
Message.error('复制失败');
|
||||||
|
}
|
||||||
|
document.body.removeChild(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function resolveDefaultDeclaredCurrency() {
|
function resolveDefaultDeclaredCurrency() {
|
||||||
const opts = singleCurrencyOptions.value;
|
const opts = singleCurrencyOptions.value;
|
||||||
const usd = opts.find((o) => String(o.value).trim().toUpperCase() === 'USD');
|
const usd = opts.find((o) => String(o.value).trim().toUpperCase() === 'USD');
|
||||||
@ -2247,7 +2320,13 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
const whOpts = (warehouseRes.items ?? [])
|
const whOpts = (warehouseRes.items ?? [])
|
||||||
.map(normalizeWarehouseRow)
|
.map(normalizeWarehouseRow)
|
||||||
.filter((w) => w.id)
|
.filter((w) => w.id)
|
||||||
.map((w) => ({ value: w.id, label: w.name || w.id }));
|
.map((w) => ({
|
||||||
|
value: w.id,
|
||||||
|
label: w.name || w.id,
|
||||||
|
recipient: w.recipient,
|
||||||
|
contactPhone: w.contactPhone,
|
||||||
|
shippingAddress: w.shippingAddress,
|
||||||
|
}));
|
||||||
singleWarehouseOptions.value = whOpts;
|
singleWarehouseOptions.value = whOpts;
|
||||||
if (whOpts.length && !String(singleForm.warehouseId ?? '').trim()) {
|
if (whOpts.length && !String(singleForm.warehouseId ?? '').trim()) {
|
||||||
singleForm.warehouseId = whOpts[0].value;
|
singleForm.warehouseId = whOpts[0].value;
|
||||||
@ -3616,7 +3695,71 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
size="small"
|
size="small"
|
||||||
class="inbound-method-radios"
|
class="inbound-method-radios"
|
||||||
>
|
>
|
||||||
<a-radio value="selfShip">自寄到仓</a-radio>
|
<a-radio value="selfShip">
|
||||||
|
<span class="inbound-method-pickup-label">
|
||||||
|
自寄到仓
|
||||||
|
<a-popover
|
||||||
|
position="bl"
|
||||||
|
trigger="hover"
|
||||||
|
content-class="warehouse-ship-popover"
|
||||||
|
>
|
||||||
|
<IconQuestionCircle
|
||||||
|
class="inbound-method-help"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
<template #content>
|
||||||
|
<div class="warehouse-ship-popover__body">
|
||||||
|
<div class="warehouse-ship-popover__head">
|
||||||
|
<strong>
|
||||||
|
寄往仓库
|
||||||
|
<template v-if="selectedWarehouseOption?.label">
|
||||||
|
· {{ selectedWarehouseOption.label }}
|
||||||
|
</template>
|
||||||
|
</strong>
|
||||||
|
<a-button
|
||||||
|
v-if="selectedWarehouseHasAddress"
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
class="warehouse-ship-popover__copy"
|
||||||
|
@click="copyWarehouseShipAddress"
|
||||||
|
>
|
||||||
|
<template #icon><IconCopy /></template>
|
||||||
|
复制
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<p v-if="!selectedWarehouseOption" class="warehouse-ship-popover__empty">
|
||||||
|
请先选择上方仓库
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
v-else-if="!selectedWarehouseHasAddress"
|
||||||
|
class="warehouse-ship-popover__empty"
|
||||||
|
>
|
||||||
|
该仓库暂未配置收货地址,请联系客服或更换仓库
|
||||||
|
</p>
|
||||||
|
<template v-else>
|
||||||
|
<dl class="warehouse-ship-popover__rows">
|
||||||
|
<div>
|
||||||
|
<dt>收件人</dt>
|
||||||
|
<dd>{{ selectedWarehouseOption.recipient || '-' }}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>电话</dt>
|
||||||
|
<dd>{{ selectedWarehouseOption.contactPhone || '-' }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="is-addr">
|
||||||
|
<dt>地址</dt>
|
||||||
|
<dd>{{ selectedWarehouseOption.shippingAddress || '-' }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
<p class="warehouse-ship-popover__hint">
|
||||||
|
按此地址自行寄出。快递面单建议备注客户单号,方便仓库认领。
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
</span>
|
||||||
|
</a-radio>
|
||||||
<a-radio value="doorPickup">
|
<a-radio value="doorPickup">
|
||||||
<span class="inbound-method-pickup-label">
|
<span class="inbound-method-pickup-label">
|
||||||
上门揽收
|
上门揽收
|
||||||
@ -6836,3 +6979,77 @@ import SelectDeclarationSkuModal from './components/SelectDeclarationSkuModal.vu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.warehouse-ship-popover {
|
||||||
|
max-width: 360px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.arco-popover-content {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warehouse-ship-popover__body {
|
||||||
|
min-width: 240px;
|
||||||
|
max-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warehouse-ship-popover__head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warehouse-ship-popover__copy {
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: var(--app-primary-color);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warehouse-ship-popover__rows {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 48px minmax(0, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 20px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.warehouse-ship-popover__hint,
|
||||||
|
.warehouse-ship-popover__empty {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user