fix:编码乱码
Some checks failed
Build and Deploy Front / deploy (push) Failing after 2m17s

This commit is contained in:
xiaolipro 2026-09-07 14:36:45 +08:00
parent b347745a1e
commit a0dfdffa14
25 changed files with 156 additions and 152 deletions

View File

@ -1228,8 +1228,8 @@ export default {
'nanaAssistant.hint': 'Type a question, or pick a capability above.',
'nanaAssistant.hintLive': 'Smart advisor is live — price, channel guide, tracking.',
'nanaAssistant.inputPlaceholder': 'Ask a question…',
'nanaAssistant.inputPlaceholderLive': '',
'nanaAssistant.inputPlaceholderTrack': '',
'nanaAssistant.inputPlaceholderLive': 'Ask about price, channels, or tracking…',
'nanaAssistant.inputPlaceholderTrack': 'Enter waybill / tracking no.…',
'nanaAssistant.mcp.sectionLabel': 'Capabilities',
'nanaAssistant.mcp.required': 'Required',
'nanaAssistant.mcp.optional': 'Optional',
@ -1276,10 +1276,10 @@ export default {
'nanaAssistant.replyPrice': 'For a quote, destination destCountry + actual weight (kg) is enough — e.g. US 0.5kg. Postal code/dims help; origin city is not required.',
'nanaAssistant.replyQuoteSpeed': 'With destCountry + weight we can return an indicative quote right away; final billing follows inbound weight and product rules.',
'nanaAssistant.trackHint': 'Just send the waybill number to track, e.g. IDE2607281746002926',
'nanaAssistant.replyVisibility': 'You get milestone updates from pickup to delivery — pickup, export, line-haul, customs, and final delivery. We proactively flag delays before they escalate.',
'nanaAssistant.replyAfterSales': "Your dedicated advisor handles exceptions, claims, and after-sales follow-up — you won't be left chasing tickets alone. Describe the issue and tracking number if you have one.",
'nanaAssistant.replyHuman': 'Reach us via the contact page — phone 18123959245 or email bd01@1st56.com. Mention your route and cargo so we assign the right advisor.',
'nanaAssistant.fallback': 'Tell me destination destCountry + weight to quote, or paste a waybill number to track — or talk to a human advisor.',
'nanaAssistant.replyVisibility': 'You get milestone updates from pickup to delivery — pickup, export, line-haul, customs, and final delivery. We proactively flag delays before they escalate.',
'nanaAssistant.replyAfterSales': "Your dedicated advisor handles exceptions, claims, and after-sales follow-up — you won't be left chasing tickets alone. Describe the issue and tracking number if you have one.",
'nanaAssistant.replyHuman': 'Reach us via the contact page — phone 18123959245 or email bd01@1st56.com. Mention your route and cargo so we assign the right advisor.',
'nanaAssistant.fallback': 'Tell me destination destCountry + weight to quote, or paste a waybill number to track — or talk to a human advisor.',
'nanaAssistant.emptyReply': 'No useful reply came back. Please try again with destination + weight, or a waybill number.',
'nanaAssistant.aiUnavailable': 'AI advisor is not configured yet. Please contact a human advisor or try later.',
'nanaAssistant.providerLoading': 'Connecting to AI advisor… please send again in a moment.',
@ -1288,7 +1288,7 @@ export default {
'nanaAssistant.wechatId': 'WeChat ID',
'nanaAssistant.noAdvisor': 'No exclusive advisor assigned yet',
'nanaAssistant.noWechatQr': 'WeChat QR code not uploaded',
'nanaAssistant.greeting': "Hi, I'm Nana, your AI assistant.\n\nAsk me about tracking, shipping times, declared values, customs clearance, and more. How can I help you today?",
'nanaAssistant.greeting': "Hi, I'm Nana, your AI assistant.\n\nAsk me about tracking, shipping times, declared values, customs clearance, and more. How can I help?",
'nanaAssistant.send': 'Send',
'nanaAssistant.close': 'Close',
'nanaAssistant.back': 'Back',
@ -2805,7 +2805,6 @@ export default {
'system.user.form.wechatQrCode': 'WeChat QR Code',
'system.user.form.wechatQrUpload': 'Upload QR code',
'system.user.table.employeeNo': 'Employee No.',
'system.user.table.phone': 'Phone',
'system.user.message.wechatQrTooLarge': 'WeChat QR image must be under 2MB',
'system.user.search.organizationPlaceholder': 'Filter by organization',
'system.user.search.accountOrNamePlaceholder': 'Search username or account',

View File

@ -2728,7 +2728,6 @@ export default {
'system.user.form.wechatQrCode': '微信二维码',
'system.user.form.wechatQrUpload': '上传二维码',
'system.user.table.employeeNo': '工号',
'system.user.table.phone': '电话',
'system.user.message.wechatQrTooLarge': '微信二维码图片不能超过 2MB',
'system.user.search.organizationPlaceholder': '选择组织筛选',
'system.user.search.accountOrNamePlaceholder': '搜索用户名或账号',

View File

@ -128,7 +128,7 @@ function formatNotifyMethods(methods: string[]) {
const found = NOTIFY_METHOD_OPTIONS.find((item) => item.value === code)
return found ? t(found.labelKey) : code
})
.join('�)
.join('')
}
function mapRow(r: any): IssueTemplateRow {
@ -443,7 +443,7 @@ const { tableContainerRef, tableHeight } = useTableContainerHeight()
const handsontableHeight = computed(() => Math.max(tableHeight.value || 0, 400))
function customCellRenderer(_row: number, col: number, _value: any, rowData: IssueTemplateRow): string | null {
// 固定列åº<EFBFBD>:æ“<EFBFBD>ä½?+ åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ä»?col 2 èµ?
// 固定列序:操?+ 序号 ?数据?col 2 )
const dataColIndex = col - 2
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
const colKey = hotColumns.value[dataColIndex].key
@ -466,7 +466,7 @@ function customCellRenderer(_row: number, col: number, _value: any, rowData: Iss
? t('channel.issueTemplate.handlerType.customer')
: t('channel.issueTemplate.handlerType.user')
const detail = rowData.handlerType === 'user' && rowData.handlerUserName
? `ï¼?{rowData.handlerUserName}`
? `【${rowData.handlerUserName}`
: ''
return `<span>${typeLabel}${detail}</span>`
}

View File

@ -480,7 +480,7 @@
]);
function hotCellRenderer(_row: number, col: number, _value: any, rowData: any): string | null {
// ChannelConfigHotTable:æ“<EFBFBD>ä½?+ åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ä»?col 2 èµ?
// ChannelConfigHotTable:操?+ 序号 ?数据?col 2 )
const dataColIndex = col - 2;
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null;
const colKey = hotColumns.value[dataColIndex].key;

View File

@ -220,7 +220,7 @@ function statusTone(status: number): StatusTagTone {
}
function customCellRenderer(_row: number, col: number, _value: unknown, rowData: WithdrawRow): string | null {
// 固定列åº<EFBFBD>:æ“<EFBFBD>ä½?+ åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ä»?col 2 èµ?
// 固定列序:操?+ 序号 ?数据?col 2 )
const dataColIndex = col - 2
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
const colKey = hotColumns.value[dataColIndex].key

View File

@ -1,7 +1,7 @@
<template>
<div class="pl-page">
<div class="pl-col pl-col--main">
<!-- å<EFBFBD>¡ç‰‡1:扫ç <EFBFBD>打å<EFBFBD>?-->
<!-- 卡片1:扫码打印 -->
<section class="pl-card pl-card--scan">
<div class="pl-card__body pl-scan-bar">
<div class="pl-scan-bar__row pl-scan-bar__row--scan">
@ -62,7 +62,7 @@
</div>
</section>
<!-- å<EFBFBD>¡ç‰‡2:核对信æ<EFBFBD>?-->
<!-- 卡片2:核对信息 -->
<section class="pl-card pl-card--verify">
<div class="pl-card__body pl-body">
<a-spin :loading="lookingUp" class="pl-body-spin">
@ -88,7 +88,7 @@
</div>
</div>
<!-- 已命�-->
<!-- 已命中 -->
<div v-else-if="lookup?.matched" class="pl-panel pl-panel--hit">
<div class="pl-hero">
<div class="pl-hero__nos">
@ -187,7 +187,7 @@
</div>
</div>
<!-- 空�-->
<!-- 空态 -->
<div v-else class="pl-empty">
<p class="pl-empty__title">{{ $t('operation.printLabel.preview.empty') }}</p>
<p class="pl-empty__tip">{{ $t('operation.printLabel.tip') }}</p>
@ -197,7 +197,7 @@
</section>
</div>
<!-- 最近打å<EFBFBD>?-->
<!-- 最近打印 -->
<aside class="pl-card pl-card--recent-wide">
<div class="pl-list-toolbar">
<div />

View File

@ -120,7 +120,7 @@
return undefined;
}
/** 列表行与表格列对é½<EFBFBD>:接å<EFBFBD>£ç”?serviceNo/clientNo/trackNo,表格用è¿<C3A8>å<EFBFBD>•å<E280A2>?客户å<C2B7>•å<E280A2>·/跟踪å<C2AA>?*/
/** 列表行与表格列对齐:接口用 serviceNo/clientNo/trackNo,表格用运单号/客户单号/跟踪号 */
function normalizeOrderRow(raw: any) {
const r = raw?.receiver || raw?.Receiver || {};
const wl = raw?.weightList || raw?.WeightList;
@ -212,7 +212,7 @@
const detail = await getOrderDetail(id);
nextRow = normalizeOrderRow({ ...row, ...detail });
} catch {
/* ä¿<EFBFBD>留列表行数æ<EFBFBD>?*/
/* 保留列表行数*/
}
}
currentOrder.value = nextRow;
@ -237,7 +237,8 @@
}
};
// 当å‰<EFBFBD>激活的标签页(我的订å<EFBFBD>•=订å<EFBFBD>•çŠæ€<EFBFBD>;代å<EFBFBD>‘订å<EFBFBD>•=代å<EFBFBD>‘进度ï¼? const validStatusSet = new Set(ORDER_STATUS_TABS.map((meta) => meta.value));
// 当前激活的标签页(我的订单=订单状态;代发订单=代发进度)
const validStatusSet = new Set(ORDER_STATUS_TABS.map((meta) => meta.value));
function resolveActiveTabFromQuery(): string {
if (isFulfillList.value) {
@ -283,7 +284,7 @@
return isFulfillList.value && keys.includes(activeTab.value);
}
/** å<EFBFBD>?Tab 下动作优先级(越é<C5A0> å‰<C3A5>越优先外露,最å¤?4 个) */
/** Tab 下动作优先级(越靠前越优先外露,最4 个) */
const FULFILL_TAB_TOOLBAR_ACTION_ORDER: Record<string, ToolbarActionKey[]> = {
all: ['stockBind', 'pay', 'predeclare', 'batchPrint', 'modifyOrder', 'delete', 'export'],
pendingBind: ['stockBind', 'modifyOrder', 'predeclare', 'delete', 'pay', 'export'],
@ -292,7 +293,7 @@
outboundDone: ['batchPrint', 'export'],
};
/** 按当å‰<EFBFBD>状æ€<EFBFBD>收敛工具æ <EFBFBD>,é<EFBFBD>¿å…<EFBFBD>客户在无关 Tab 看到一排无效按é’?*/
/** 按当前状态收敛工具栏,避免客户在无关 Tab 看到一排无效按*/
const showPayAction = computed(() =>
isFulfillList.value
? isFulfillProgressTab('all', 'pendingBind')
@ -368,7 +369,7 @@
modifyOrder: 'order.myOrders.actions.modifyOrder',
};
/** å<EFBFBD>?Tab 下动作优先级(越é<C5A0> å‰<C3A5>越优先外露,最å¤?4 个) */
/** Tab 下动作优先级(越靠前越优先外露,最4 个) */
const TAB_TOOLBAR_ACTION_ORDER: Record<string, ToolbarActionKey[]> = {
all: [
'pay',
@ -661,7 +662,7 @@
tabCounts.value = next;
handleCounts.value = handleCountsRaw ?? {};
} catch {
/* 角标失败ä¸<EFBFBD>å½±å“<EFBFBD>列è¡?*/
/* 角标失败不影响列*/
}
}
@ -674,12 +675,12 @@
const pageSize = ref(50);
const totalCount = ref(0);
/** 列表筛选:å<EFBFBD>•å<EFBFBD>·å…³é”®è¯?+ 创建时间(默认近 90 天) */
/** 列表筛选:单号关键+ 创建时间(默认近 90 天) */
const listKeyword = ref('');
const createTimeRange = ref<(Date | string)[] | undefined>([
...buildDefaultCreateTimeRange(),
]);
/** 用户明确放宽时间窗(清空)å<EFBFBD>Žï¼Œä¸<EFBFBD>å†<EFBFBD>自动填回默è®?*/
/** 用户明确放宽时间窗(清空)后,不再自动填回默*/
const timeWindowRelaxed = ref(false);
const dateFilterExpanded = ref(false);
const headerSearchParams = reactive<Record<string, string>>({});
@ -763,7 +764,7 @@
const timeWindowHelp = computed(() => {
if (timeWindowRelaxed.value && !createTimeRange.value?.length) {
return `å·²å<EFBFBD>–消时间é™<EFBFBD>制,全é‡<EFBFBD>查询å<EFBFBD>¯èƒ½è¾ƒæ…¢ã€‚建议按å<EFBFBD>•å<EFBFBD>·ç²¾ç¡®æŸ¥è¯¢ï¼Œæˆ–æ<EFBFBD>¢å¤<EFBFBD>è¿?${ORDER_LIST_DEFAULT_DAYS} 天。`;
return '';
}
return `默认仅展示近 ${ORDER_LIST_DEFAULT_DAYS} 天订单;按单号精确查询时可清空时间。`;
});
@ -794,10 +795,10 @@
return;
}
Modal.confirm({
title: '清空æ—间范围ï¼?,
title: '清空时间范围?',
content: `未指定单号时查询全部历史可能较慢。建议保留近 ${ORDER_LIST_DEFAULT_DAYS} 天,或先输入单号再清空时间。`,
okText: '仍要清空',
cancelText: 'ä¿<EFBFBD>ç•™è¿?0å¤?,
cancelText: '保留近90天',
onOk: () => {
timeWindowRelaxed.value = true;
createTimeRange.value = undefined;
@ -837,9 +838,9 @@
}
const selectedRowCount = ref(0);
/** 表格选中行(用于批é‡<EFBFBD>æ“<EFBFBD>作ï¼?*/
/** 表格选中行(用于批量操作*/
const selectedOrderRows = ref<any[]>([]);
/** 当å‰<EFBFBD>正在执行çš?toolbar æ“<C3A6>作(仅对应按钮显示 loading,é<C592>¿å…<C3A5>全部按钮è<C2AE>”动) */
/** 当前正在执行的 toolbar 操作(仅对应按钮显示 loading,避免全部按钮联动) */
const actionLoadingKey = ref<string | null>(null);
function isActionLoading(key: string) {
@ -1011,7 +1012,8 @@
// 当前页数据(HandsontablePro 用)
const processedTableData = computed(() => orderList.value);
// 标签页切æ<EFBFBD>? const handleTabChange = (key: string) => {
// 标签页切)
const handleTabChange = (key: string) => {
if (activeTab.value === key) {
return;
}
@ -1036,7 +1038,8 @@
loadOrderList();
};
// æ“<EFBFBD>作按钮处ç<EFBFBD>†ï¼ˆä¾<EFBFBD>赖表格多选行;导出按当å‰<EFBFBD> tab æ<EFBFBD>¡ä»ï¼Œæ— éœ€é€‰è¡Œï¼? function openPayConfirm(rows: any[]) {
// 操作按钮处理(依赖表格多选行;导出按当前 tab 条件,无需选行)
function openPayConfirm(rows: any[]) {
payConfirmOrders.value = rows
.filter((row) => getOrderRowId(row) != null)
.map((row) => ({
@ -1102,12 +1105,12 @@
action: 'pre-declare',
rows,
loadingKey: 'predeclare',
successMessage: '预报已æ<EFBFBD><EFBFBD>äº?,
successMessage: '预报已提交',
syncRunner: async () => {
for (const row of rows) {
await postOrderPreDeclare({ id: getOrderRowId(row)! });
}
Message.success('预报已æ<EFBFBD><EFBFBD>äº?);
Message.success('预报已提交');
await refreshListAndTabCounts();
},
});
@ -1212,7 +1215,7 @@
}
Modal.confirm({
title: '确认删除',
content: `确定删除选中çš?${rows.length} æ<>¡è®¢å<C2A2>•å<E280A2>—?`,
content: `确定删除选中的 ${rows.length} 条订单吗?`,
onOk: async () => {
try {
await withOrderActionLoading('delete', async () => {
@ -1220,12 +1223,12 @@
action: 'delete',
rows,
loadingKey: 'delete',
successMessage: '已删�,
successMessage: '已删除',
syncRunner: async () => {
for (const row of rows) {
await postOrderDelete({ id: getOrderRowId(row)! });
}
Message.success('已删�);
Message.success('已删除');
await refreshListAndTabCounts();
},
});
@ -1249,7 +1252,7 @@
function isUsablePrintUrl(raw: unknown) {
const value = String(raw ?? '').trim();
if (!value) return false;
if (value.includes('获å<EFBFBD>–ä¸?)) return false;
if (value.includes('获取中')) return false;
return true;
}
@ -1261,7 +1264,7 @@
return;
}
if (!printConnectionState.enabled || !printConnectionState.connected) {
Message.warning('请先在é¡éƒ¨æ‰“å¼€å¹è¿žæŽ¥æ‰“å<EFBFBD>°å¼€å…?);
Message.warning('请先在顶部打开并连接打印开关');
return;
}
try {
@ -1288,7 +1291,7 @@
const rows = requireSelectedRows();
if (!rows.length) return;
if (!printConnectionState.enabled || !printConnectionState.connected) {
Message.warning('请先在é¡éƒ¨æ‰“å¼€å¹è¿žæŽ¥æ‰“å<EFBFBD>°å¼€å…?);
Message.warning('请先在顶部打开并连接打印开关');
return;
}
try {
@ -1375,12 +1378,12 @@
sortOrder,
});
if (downloadUrl) window.open(downloadUrl, '_blank');
Message.success(downloadUrl ? '导出链接已打开' : '导出已æ<EFBFBD><EFBFBD>äº?);
Message.success(downloadUrl ? '导出链接已打开' : '导出已提交');
});
} catch (e: any) {
if (e?.message !== 'busy') {
Message.error(
e?.message || `导出失败(å<EFBFBD>•次最å¤?${ORDER_EXPORT_MAX_ROWS.toLocaleString()} æ<EFBFBD>¡ï¼‰`,
e?.message || `导出失败(单次最多 ${ORDER_EXPORT_MAX_ROWS.toLocaleString()} 条)`,
);
}
}
@ -1536,7 +1539,7 @@
loadOrderList();
};
// HandsontablePro 列定义(支æŒ<EFBFBD>命å<EFBFBD><EFBFBD>方案é…<EFBFBD>置,最å¤?30 列)
// HandsontablePro 列定义(支持命名方案配置,最多 30 列)
const initialIsFulfillList = route.meta.listMode === 'fulfill';
const columnConfigState = ref(
loadColumnConfigState(
@ -1598,8 +1601,8 @@
filterType: 'enum' as const,
headerMode: 'icon' as const,
filterOptions: [
{ label: '已支�, value: '1' },
{ label: '未支�, value: '0' },
{ label: '已支付', value: '1' },
{ label: '已支付', value: '1' },
],
};
}
@ -1772,7 +1775,8 @@
const qty = Number(rowData?.stockBindQtyTotal ?? value ?? 0);
return qty > 0 ? String(qty) : '--';
}
// 首列添加 data-row-status 用于行背景样� if (colKey === 'waybillNo') {
// 首列添加 data-row-status 用于行背景样)
if (colKey === 'waybillNo') {
const statusCode = resolveOrderStatusCode(rowData);
const escaped = escapeHtml(String(value || '--'));
return `<span data-row-status="${statusCode}">${escaped}</span>`;
@ -1858,7 +1862,7 @@
<template>
<div class="my-orders-container">
<!-- å<EFBFBD>•一工作é<EFBFBD>¢ï¼šTabs/ç­›é€?+ å­<EFBFBD>çŠæ€?+ 工具æ ?+ 表格 -->
<!-- 单一工作面:Tabs/筛+ 子状+ 工具+ 表格 -->
<div class="table-section">
<div class="tabs-actions-section">
<div class="tabs-slot">
@ -1879,7 +1883,7 @@
@press-enter="handleSearch"
/>
<span class="list-date-filter">
<a-tooltip v-if="!dateFilterExpanded" content="日期筛�>
<a-tooltip v-if="!dateFilterExpanded" content="日期筛选">
<a-button class="list-date-toggle" @click="dateFilterExpanded = true">
<template #icon>
<IconCalendar />
@ -1910,7 +1914,8 @@
class="list-date-restore"
@click="restoreDefaultTimeWindow(); handleSearch()"
>
æ<EFBFBD>¢å¤<EFBFBD>è¿‘{{ ORDER_LIST_DEFAULT_DAYS }}å¤? </a-button>
恢复近{{ ORDER_LIST_DEFAULT_DAYS }}天
</a-button>
<a-button
v-if="dateFilterExpanded"
type="text"
@ -1955,7 +1960,7 @@
</div>
</div>
<!-- 表格æ“<EFBFBD>作按钮和分页控åˆ?-->
<!-- 表格操作按钮和分页控-->
<div class="table-toolbar">
<div class="actions-right">
<a-button
@ -2483,7 +2488,7 @@
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
border: none !important;
// 覆盖 Handsontable 默认�table 边框
// 覆盖 Handsontable 默认的 table 边框
table {
border: none !important;
}
@ -2590,7 +2595,8 @@
}
}
// å“<EFBFBD>应å¼<EFBFBD>设è®? @media (max-width: 768px) {
// 响应式设计
@media (max-width: 768px) {
.actions-right {
flex-wrap: wrap;
gap: 4px;

View File

@ -323,7 +323,7 @@ async function loadStatusCounts() {
}
statusCounts.value = next
} catch {
/* 角标失败ä¸<EFBFBD>å½±å“<EFBFBD>列è¡?*/
/* 角标失败不影响列?*/
}
}
@ -571,8 +571,8 @@ async function doBatchPay() {
.filter((x: any) => !(x?.success ?? x?.Success))
.slice(0, 5)
.map((x: any) => `${x?.pickupNo || x?.PickupNo || x?.id || x?.Id}: ${x?.message || x?.Message || ''}`)
.join('ï¼?)
$baseMessage(failItems ? `${message}�{failItems}` : message, 'warning')
.join('')
$baseMessage(failItems ? `${message}(${failItems}` : message, 'warning')
} else {
$baseMessage(message, 'success')
}
@ -1348,7 +1348,7 @@ onMounted(async () => {
</a-tag>
</div>
<div class="pk-detail-sub">
{{ t('order.pickupOrder.table.createTime') }}ï¼?
{{ t('order.pickupOrder.table.createTime') }}?
{{ formatDateTime(detail.createTime || detail.CreateTime) }}
</div>
</div>

View File

@ -53,12 +53,12 @@ const slidingPlatformTabs = computed(() =>
const lazadaSiteOptions = computed(() => [
{ value: 'cb', label: t('order.platformStore.site.crossBorder') },
{ value: 'sg', label: '新加å<EFBFBD>? },
{ value: 'sg', label: '新加坡' },
{ value: 'my', label: '马来西亚' },
{ value: 'th', label: '泰国' },
{ value: 'vn', label: '越南' },
{ value: 'ph', label: 'è<EFBFBD>²å¾‹å®? },
{ value: 'id', label: 'å<EFBFBD>°åº¦å°¼è¥¿äº? },
{ value: 'ph', label: '菲律宾' },
{ value: 'id', label: '印度尼西亚' },
])
const [registerFormModal, { openModal, closeModal }] = useModal() as any

View File

@ -556,7 +556,7 @@ async function handleSubmit(): Promise<boolean> {
const msg = failItems
.slice(0, 3)
.map((x: any) => `${x.platformOrderNo || x.PlatformOrderNo || x.id}: ${x.message || x.Message}`)
.join('ï¼?)
.join('')
$baseMessage(
t('order.platformStoreOrder.createLogistics.partial', { success, fail, detail: msg }),
'warning',
@ -752,7 +752,7 @@ watch(leftTab, () => {
</aside>
<section v-if="activeDraft" class="psc-detail">
<!-- 仅物æµ<EFBFBD>:æ”ä»äº?+ 商å“<EFBFBD>纵å<EFBFBD>‘校对 -->
<!-- 仅物流:收件?+ 商品纵向校对 -->
<template v-if="!isFulfill">
<div class="psc-section">
<h4>{{ t('order.platformStoreOrder.createLogistics.receiverTitle') }}</h4>
@ -789,7 +789,7 @@ watch(leftTab, () => {
</div>
</template>
<!-- 物æµ<EFBFBD>&代å<EFBFBD>‘:订å<EFBFBD>•商å“?â†?已绑资æº<EFBFBD> 对照校对 -->
<!-- 物流&代发:订单商已绑资源 对照校对 -->
<template v-else>
<div class="psc-ship-strip" :class="{ missing: activeDraft.receiverMissing }">
<strong>{{ activeDraft.platformOrderNo }}</strong>
@ -1258,7 +1258,7 @@ watch(leftTab, () => {
}
}
/* ── 代å<C2A3>‘:收件摘è¦?── */
/* ── 代发:收件摘?── */
.psc-ship-strip {
flex-shrink: 0;
display: flex;
@ -1361,7 +1361,7 @@ watch(leftTab, () => {
}
}
/* ── 代å<C2A3>‘:订å<C2A2>•商å“?â†?已绑 对照 ── */
/* ── 代发:订单商已绑 对照 ── */
.psc-compare {
flex: 1.2;
min-height: 220px;
@ -1505,7 +1505,7 @@ watch(leftTab, () => {
flex-shrink: 0;
}
/* ── 代å<C2A3>‘:底部挑选资æº?── */
/* ── 代发:底部挑选资?── */
.psc-pick {
flex: 0.9;
min-height: 180px;

View File

@ -50,7 +50,7 @@ const {
ensureMutationCustomerId,
} = useWarehouseAdminCustomer()
/** Tab active-key;全部用 'all',请求时再转成不<EFBFBD><EFBFBD>?fulfillStatus / platformCode */
/** Tab active-key;全部用 'all',请求时再转成不?fulfillStatus / platformCode */
const STATUS_TAB_ALL = 'all'
const PLATFORM_TAB_ALL = 'all'
const ALLOWED_FULFILL_STATUS = new Set(['pending', 'toShip', 'shipped', 'canceled', 'completed'])

View File

@ -482,7 +482,7 @@ function handlePageSizeChange(size: number) {
function formatReceiverOptionLabel(opt: NoticeReceiverOption) {
if (!opt.extra) return opt.label
return `${opt.label}�{opt.extra})`
return `${opt.label}(${opt.extra})`
}
function mergeReceiverOptions(options: NoticeReceiverOption[]) {

View File

@ -30,14 +30,14 @@
</a-table-column>
<a-table-column title="目标 ID" data-index="targetId" ellipsis />
<a-table-column title="动作" data-index="action" :width="100" />
<a-table-column title="æ“<EFBFBD>作äº?mapId" data-index="operatorMapId" :width="110" />
<a-table-column title="操作人 mapId" data-index="operatorMapId" :width="110" />
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="access" title="数据访问日志">
<a-alert type="info" style="margin-bottom: 12px">
P1 å<EFBFBD>Žç«¯æš‚ä¸<EFBFBD>自动写入访问日志;本页为预留查询界é<EFBFBD>¢ï¼Œå¾…内审需求å<EFBFBD>¯ç”¨å<EFBFBD>Žå†<EFBFBD>对接ã€?
P1 后端暂不自动写入访问日志;本页为预留查询界面,待内审需求启用后再对接?
</a-alert>
<div class="table-toolbar">
<a-space wrap>

View File

@ -345,7 +345,7 @@ function actionRenderer(_row: DictDto, _rowIndex: number): string {
])
}
/** 固定列åº<EFBFBD>:æ“<EFBFBD>ä½?+ åº<C3A5>å<EFBFBD>· â†?æ•°æ<C2B0>®ä»?col 2 èµ?*/
/** 固定列序:操?+ 序号 ?数据?col 2 ?*/
function customCellRenderer(_row: number, col: number, _value: unknown, rowData: DictDto): string | null {
const dataColIndex = col - 2
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
@ -391,7 +391,7 @@ function handleTypeNodeClick(key: string) {
}
function handleTypeSearch() {
// filteredTypeList 客户端过�
// filteredTypeList 客户端过?
}
function handleSearch() {

View File

@ -432,7 +432,7 @@ function handleOrgSearch() {
}
function customCellRenderer(_row: number, col: number, _value: any, rowData: UserDto): string | null {
// HandsontablePro 列åº<EFBFBD>:æ“<EFBFBD>ä½?â†?åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ï¼›showActionColumn æ—?dataColStart=2
// HandsontablePro 列序:操序号 ?数据;showActionColumn ?dataColStart=2
const dataColIndex = col - 2
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
const key = hotColumns.value[dataColIndex].key

View File

@ -33,10 +33,10 @@
const listData = ref<API.GetTrackPullConfigPageOutputVO[]>([]);
// 蠎皮畑蛻苓。ィ譏<EFBFBD>蟆<EFBFBD>シ<EFBFBD>ppKey -> name<EFBFBD>?
// 应用列表映射(appKey -> name)
const appMap = ref<Record<string, string>>({});
// 蜉<EFBFBD>霓ス蠎皮畑蛻苓。ィ蟷カ蛻帛サコ譏<EFBFBD>蟆?
// 加载应用列表并创建映)
const loadAppMap = async () => {
try {
const res = await getAppGetList({
@ -56,7 +56,7 @@
}
};
// 扈<EFBFBD>サカ謖りスス譌カ蜉<EFBFBD>霓ス蠎皮畑譏<EFBFBD>蟆?
// 组件挂载时加载应用映?
onMounted(() => {
loadAppMap();
});
@ -208,19 +208,19 @@
}
}
/* 菫ョ謾ケ貊壼勘譚。霓ィ驕楢レ譎ッ鬚懆<EFBFBD>?*/
/* 修改滚动条轨道背景颜?*/
.scrollbar::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.08);
border-radius: 8px;
}
/* 菫ョ謾ケ貊壼勘譚。貊大摎閭梧勹鬚懆<EFBFBD>?*/
/* 修改滚动条滑块背景颜?*/
.scrollbar::-webkit-scrollbar-thumb {
background: rgba(86, 144, 236, 0.65);
border-radius: 8px;
}
/* 菫ョ謾ケ貊壼勘譚。貊大摎蜑肴勹鬚懆<EFBFBD>?*/
/* 修改滚动条滑块前景颜?*/
.scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(59, 97, 148, 0.85);
}

View File

@ -32,10 +32,10 @@
});
const listData = ref<API.GetTrackPushConfigPageOutputVO[]>([]);
// 蠎皮畑蛻苓。ィ譏<EFBFBD>蟆<EFBFBD>シ<EFBFBD>ppKey -> name<EFBFBD>?
// 应用列表映射(appKey -> name)
const appMap = ref<Record<string, string>>({});
// 蜉<EFBFBD>霓ス蠎皮畑蛻苓。ィ蟷カ蛻帛サコ譏<EFBFBD>蟆?
// 加载应用列表并创建映)
const loadAppMap = async () => {
try {
const res = await getAppGetList({
@ -55,7 +55,7 @@
}
};
// 扈<EFBFBD>サカ謖りスス譌カ蜉<EFBFBD>霓ス蠎皮畑譏<EFBFBD>蟆?
// 组件挂载时加载应用映?
onMounted(() => {
loadAppMap();
});
@ -205,19 +205,19 @@
}
}
/* 菫ョ謾ケ貊壼勘譚。霓ィ驕楢レ譎ッ鬚懆<EFBFBD>?*/
/* 修改滚动条轨道背景颜?*/
.scrollbar::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.08);
border-radius: 8px;
}
/* 菫ョ謾ケ貊壼勘譚。貊大摎閭梧勹鬚懆<EFBFBD>?*/
/* 修改滚动条滑块背景颜?*/
.scrollbar::-webkit-scrollbar-thumb {
background: rgba(86, 144, 236, 0.65);
border-radius: 8px;
}
/* 菫ョ謾ケ貊壼勘譚。貊大摎蜑肴勹鬚懆<EFBFBD>?*/
/* 修改滚动条滑块前景颜?*/
.scrollbar::-webkit-scrollbar-thumb:hover {
background: rgba(59, 97, 148, 0.85);
}

View File

@ -304,7 +304,7 @@ async function loadMainCustomerOptions() {
const id = extractCustomerId(c as Record<string, unknown>)!
return {
value: id,
label: `${c.customerName || ''}�{c.customerCode || id})`,
label: `${c.customerName || ''}(${c.customerCode || id})`,
}
})
} catch {
@ -379,8 +379,8 @@ const formRules = computed(() => ({
password: [{ required: true, message: t('user.subAccount.rules.passwordRequired') }],
}))
// æ<EFBFBD>ƒé™<EFBFBD>树(å­<EFBFBD>è´¦å<EFBFBD>·æ<EFBFBD>ƒé™<EFBFBD>é¡» âŠ?当å‰<EFBFBD>登录用户 permissionsï¼?
const permissionTreeLoading = ref(false)
// 权限树(子账号权限须 ?当前登录用户 permissions)
const permissionTreeLoading = ref(false)
const permissionTree = ref<PermissionGroupDto[]>([])
const permissionExpandedKeys = ref<string[]>([])
const allowedPermissionSet = computed(

View File

@ -78,7 +78,7 @@ const {
} = useWarehouseAdminCustomer()
const currentWarehouseStore = useCurrentWarehouseStore()
/** Tab active-key;全部用 'all',请求时å†<C3A5>转æˆ<C3A6>ä¸<C3A4>ä¼?receiptStatus */
/** Tab active-key;全部用 'all',请求时再转成后端 receiptStatus */
const STATUS_TAB_ALL = 'all'
const receiptStatus = ref<string>(STATUS_TAB_ALL)
const summary = ref({ forecasted: 0, received: 0, completed: 0 })
@ -699,7 +699,7 @@ onMounted(async () => {
<div v-for="box in receiveBoxes" :key="box.boxId" class="receive-box-block">
<div class="box-title">
{{ t('warehouse.inbound.box') }} {{ box.boxSeqNo }}
<span v-if="box.markBoxNo">({{ box.markBoxNo }}�/span>
<span v-if="box.markBoxNo">({{ box.markBoxNo }})</span>
</div>
<a-table :data="box.lines" :pagination="false" size="small" :bordered="{ cell: true }">
<template #columns>
@ -759,7 +759,7 @@ onMounted(async () => {
|| warehouseOptions.find((o) => o.value === formModel.warehouseId)?.label
|| currentWarehouseStore.warehouseName
|| formModel.warehouseId
|| '�
|| '-'
}}
</span>
</a-form-item>

View File

@ -180,7 +180,7 @@ async function loadSourceBinOptions(skuId?: number, whId?: number) {
const code = pickField(normalized, 'binCode', 'BinCode') ?? '-'
const name = pickField(normalized, 'binName', 'BinName')
return {
label: `${code}${name ? ` - ${name}` : ''}�{qty})`,
label: `${code}${name ? ` - ${name}` : ''}(${qty})`,
value: Number(pickField(normalized, 'binLocationId', 'BinLocationId') ?? 0),
qty,
}

View File

@ -63,7 +63,7 @@ const {
} = useWarehouseAdminCustomer()
const currentWarehouseStore = useCurrentWarehouseStore()
/** Tab active-key;全部用 'all',请求时å†<C3A5>转æˆ<C3A6>ä¸<C3A4>ä¼?lifecycleStatus */
/** Tab active-key;全部用 'all',请求时再转成后端 lifecycleStatus */
const STATUS_TAB_ALL = 'all'
const lifecycleStatus = ref<string>(STATUS_TAB_ALL)
const keyword = ref('')
@ -156,13 +156,13 @@ function isMultiItemRow(row: any): boolean {
return getRowItemCount(row) > 1
}
/** 快递å<EFBFBD>•å<EFBFBD>·ï¼šçº¯æ–‡æœ?*/
/** 快递单号:纯文本 */
function renderTrackingNoCell(_row: any, value: any) {
const text = value == null || value === '' ? '-' : String(value)
return escapeHtml(text)
}
/** 多商å“<EFBFBD>标识列:仅多商å“<EFBFBD>显示红色「多ã€?*/
/** 多商品标识列:仅多商品显示红色「多」 */
function renderMultiBadgeCell(row: any) {
if (!isMultiItemRow(row)) {
return ''
@ -172,11 +172,11 @@ function renderMultiBadgeCell(row: any) {
`<span class="parcel-multi-badge" title="${escapeHtml(tip)}" ` +
`style="display:inline-flex;align-items:center;justify-content:center;` +
`min-width:16px;height:16px;padding:0 3px;border-radius:3px;background:#f53f3f;color:#fff;` +
`font-size:10px;font-weight:700;line-height:1;letter-spacing:0;">�/span>`
`font-size:10px;font-weight:700;line-height:1;letter-spacing:0;">多</span>`
)
}
/** 商å“<EFBFBD>/属æ€?æ•°é‡<C3A9>:仅多商å“<C3A5>å<EFBFBD>¯ç‚¹ã€<C3A3>主色;å<E280BA>•商å“<C3A5>普通文æœ?*/
/** 商品/属性数量:仅多商品可点、主色;单商品普通文本 */
function renderDetailCell(row: any, value: any) {
const text = value == null || value === '' ? '-' : String(value)
if (!isMultiItemRow(row)) {
@ -191,7 +191,7 @@ function resolveItemImageSrc(url?: string | null) {
return resolveOmsAssetUrl(url) || String(url || '').trim()
}
/** 列表无图 / 加载失败时的缺çœ<C3A7>图片 icon(data URI)â€?用中性ç<C2A7>°ï¼Œæ—¥å¤œé—´éƒ½å<C2BD>¯è¯?*/
/** 列表无图 / 加载失败时的缺省图片 icon(data URI):用中性灰,日夜间都可 */
const EMPTY_IMAGE_PLACEHOLDER =
'data:image/svg+xml,' +
encodeURIComponent(
@ -203,7 +203,7 @@ const EMPTY_IMAGE_PLACEHOLDER =
`</svg>`,
)
/** 图片列:独立缩略图,点击放大;无图显示缺�icon */
/** 图片列:独立缩略图,点击放大;无图显示缺省 icon */
function renderImageCell(row: any) {
const src = resolveItemImageSrc(row?.imageThumbUrl)
if (!src) {
@ -791,7 +791,7 @@ onMounted(async () => {
warehouseOptions.find((o) => o.value === formModel.warehouseId)?.label
|| currentWarehouseStore.warehouseName
|| formModel.warehouseId
|| '�
|| '-'
}}
</span>
</a-form-item>

View File

@ -253,7 +253,7 @@ async function handleSubmit() {
const serviceNos = linked
.map((o: any) => o.serviceNo || o.ServiceNo)
.filter(Boolean)
.join('�)
.join('')
if (serviceNos) {
showSuccess(t('warehouse.parcelArrival.message.arriveSuccessWithOrders', { orders: serviceNos }))
if (hasPermission('Operation.FulfillPack')) {

View File

@ -439,7 +439,7 @@ onMounted(async () => {
:wrapper-col-props="{ span: 16 }"
>
<a-form-item :label="t('warehouse.sku.form.currentWarehouse')">
<span class="wh-form-readonly">{{ currentWarehouseLabel || '� }}</span>
<span class="wh-form-readonly">{{ currentWarehouseLabel || '-' }}</span>
</a-form-item>
<a-form-item :label="t('warehouse.sku.form.skuCode')" required>
<a-input
@ -469,7 +469,7 @@ onMounted(async () => {
<div class="wh-form-sku-preview">
<div class="wh-form-sku-preview__image">
<img v-if="skuImagePreview" :src="skuImagePreview" alt="sku" />
<span v-else class="wh-form-hint">�/span>
<span v-else class="wh-form-hint">-</span>
</div>
<div class="wh-form-sku-preview__fields">
<a-upload

View File

@ -4,12 +4,12 @@
<div class="toolbar-left">
<div class="toolbar-info">
<div class="xhs-panel-title">发布记录</div>
<div class="xhs-panel-desc">å<EFBFBD>«å®šæ—å¾…å<EFBFBD>‘布与已å<EFBFBD>‘布/失败记录;到期将自动å°<EFBFBD>试å<EFBFBD>‘布ã€?/div>
<div class="xhs-panel-desc">含定时待发布与已发布/失败记录;到期将自动尝试发布。</div>
</div>
</div>
<div class="toolbar-right">
<a-button :disabled="loading" @click="emit('refresh')">
{{ loading ? '刷新中� : '刷新' }}
{{ loading ? '刷新中…' : '刷新' }}
</a-button>
<a-pagination
v-if="showPagination"

View File

@ -5,7 +5,7 @@
<div class="toolbar-left">
<span class="xhs-panel-title">启动服务</span>
<span class="xhs-panel-desc">
<EFBFBD>祆㦤 Agent + 瘚讛<EFBFBD><EFBFBD>典<EFBFBD><EFBFBD>𠬍<EFBFBD><EFBFBD>曉笆<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝔踴<EFBFBD><EFBFBD><EFBFBD>蝚磰扇<EFBFBD><EFBFBD><EFBFBD><EFBFBD>函<EFBFBD><EFBFBD>?
本机 Agent + 浏览器引擎:找对标、写稿、发笔记、互动留痕
</span>
</div>
<div class="toolbar-right">
@ -31,7 +31,7 @@
v-if="offlineCount > 0 && agents.length > 0"
type="warning"
style="margin-bottom: 12px"
:title="`<EFBFBD>?${offlineCount} <20>?Agent 蝳餌瑪嚗諹窈璉<E7AA88><E79289>交𧋦<E4BAA4>?Agent <20>臬炏<E887AC>刻<EFBFBD>銵䈣"
:title="`${offlineCount} 个 Agent 离线,请检查本机 Agent 是否在运行`"
closable
/>
@ -39,26 +39,26 @@
v-if="!loading && agents.length === 0"
type="info"
style="margin-bottom: 12px"
title="撠𡁏𧊋餈墧𦻖 Agent嚗諹窈餈鞱<E9A488> xhs-agent-desktop 撟嗉<E6929F><E59789>?OMS"
title="尚未连接 Agent,请运行 xhs-agent-desktop 并连接 OMS"
closable
/>
<div class="service-cards-row">
<div class="service-card xhs-inner-card">
<div class="card-header">
<span class="card-title">撠讐滯銋?繚 瘚讛<EFBFBD><EFBFBD>典<EFBFBD><EFBFBD>?/span>
<span class="card-title">小红书 · 浏览器引擎</span>
<XhsStatusBadge :kind="engineBadge.kind" :text="engineBadge.text" />
</div>
<p class="card-hint">{{ bridgeUrlText }}</p>
<p class="card-hint card-note">
<EFBFBD><EFBFBD><EFBFBD> xiaohongshu-skills嚗鋳ridge Server + Chrome <EFBFBD>拙<EFBFBD>嚗<EFBFBD>銁<EFBFBD>祆㦤<EFBFBD>笔<EFBFBD>瘚讛<EFBFBD><EFBFBD>其葉<EFBFBD>滢<EFBFBD>撠讐滯銋艾<EFBFBD>?
内嵌 xiaohongshu-skills:Bridge Server + Chrome 扩展,在本机真实浏览器中操作小红书。
</p>
<div class="card-btn-row">
<a-button type="primary" :disabled="!canControl" @click="control('engine_start')">
启动引擎
</a-button>
<a-button :disabled="!canControl" @click="control('engine_stop')">停止引擎</a-button>
<a-button @click="loadAgents"><EFBFBD>瑟鰵<EFBFBD>嗆<EFBFBD>?/a-button>
<a-button @click="loadAgents">刷新状态</a-button>
</div>
</div>
@ -69,9 +69,9 @@
</div>
<p class="card-hint card-note">{{ loginGuideText }}</p>
<div class="card-btn-row">
<a-button :disabled="!canControl" @click="control('clear_cookies')"><EFBFBD><EFBFBD><EFBFBD>箇蒈敶?/a-button>
<a-button :disabled="!canControl" @click="control('clear_cookies')">退出登录</a-button>
<a-button type="primary" :disabled="!canControl" @click="control('browser_login')">
瘚讛<EFBFBD><EFBFBD>函蒈敶?
浏览器登录
</a-button>
<a-button :disabled="!canControl" @click="control('check_login')">登录校验</a-button>
</div>
@ -83,7 +83,7 @@
<XhsStatusBadge kind="idle" text="即将支持" />
</div>
<p class="card-hint card-note">
<EFBFBD>㚚𨺗<EFBFBD>?Chrome <EFBFBD>拙<EFBFBD>銝舘䌊<EFBFBD>典<EFBFBD>撘閙<EFBFBD>甇<EFBFBD>銁<EFBFBD>亙<EFBFBD>銝准<EFBFBD><EFBFBD><EFBFBD>蝥踹<EFBFBD><EFBFBD>臭<EFBFBD><EFBFBD>祉<EFBFBD>銝<EFBFBD><EFBFBD>瑚蝙<EFBFBD>典<EFBFBD><EFBFBD>僐<EFBFBD><EFBFBD><EFBFBD>霈箔<EFBFBD><EFBFBD>𥕢<EFBFBD><EFBFBD>賢<EFBFBD><EFBFBD>?
抖音 / Chrome 扩展与自动化引擎正在接入中。上线后可与本站一样使用分析、评论与创作能力。
</p>
<div class="card-btn-row">
<a-button disabled>敬请期待</a-button>
@ -94,7 +94,7 @@
<div v-if="agents.length > 0" class="agent-table-section">
<div class="table-toolbar">
<div class="toolbar-left">
<span class="section-title">撌脰<EFBFBD><EFBFBD>亥挽憭?/span>
<span class="section-title">已连接设备</span>
<a-button @click="loadAgents">
<template #icon><icon-refresh /></template>
刷新
@ -129,7 +129,7 @@
{{ record.deviceName || record.deviceId }}
</template>
</a-table-column>
<a-table-column title="<EFBFBD>嗆<EFBFBD>? :width="90">
<a-table-column title="状态" :width="90">
<template #cell="{ record }">
<XhsStatusBadge
:kind="record.status === 'online' ? 'ok' : 'idle'"
@ -141,7 +141,7 @@
<template #cell="{ record }">
<XhsStatusBadge
:kind="record.engineRunning ? 'ok' : 'err'"
:text="record.engineRunning ? '餈鞱<EFBFBD>銝? : '<27>芸鍳<E88AB8>?"
:text="record.engineRunning ? '运行中' : '未启动'"
/>
</template>
</a-table-column>
@ -149,20 +149,20 @@
<template #cell="{ record }">
<XhsStatusBadge
:kind="record.extensionConnected ? 'ok' : 'warn'"
:text="record.extensionConnected ? '撌脰<EFBFBD><EFBFBD>? : '<27>芾<EFBFBD><E88ABE>?"
:text="record.extensionConnected ? '已连接' : '未连接'"
/>
</template>
</a-table-column>
<a-table-column title="撠讐滯銋? :width="90">
<a-table-column title="小红书" :width="90">
<template #cell="{ record }">
<XhsStatusBadge
:kind="record.xhsLoggedIn ? 'ok' : 'warn'"
:text="record.xhsLoggedIn ? '撌脩蒈敶? : '<27>芰蒈敶?"
:text="record.xhsLoggedIn ? '已登录' : '未登录'"
/>
</template>
</a-table-column>
<a-table-column title="版本" data-index="agentVersion" :width="100" />
<a-table-column title="<EFBFBD><EFBFBD><EFBFBD>𤾸<EFBFBD>頝? :width="170">
<a-table-column title="最后心跳" :width="170">
<template #cell="{ record }">
{{ formatTime(record.lastHeartbeatTime) }}
</template>
@ -180,26 +180,26 @@
:footer="false"
>
<a-steps :current="0" direction="vertical" size="small" style="margin-bottom: 20px">
<a-step description="餈鞱<EFBFBD> xhs-agent-desktop/run.ps1 <EFBFBD>枏<EFBFBD>獢屸𢒰蝡?>
<EFBFBD>枏<EFBFBD> Agent 獢屸𢒰蝡?
<a-step description="运行 xhs-agent-desktop/run.ps1 打开桌面端">
打开 Agent 桌面端
</a-step>
<a-step description="獢屸𢒰蝡舐<EFBFBD><EFBFBD>颯<EFBFBD>諹<EFBFBD><EFBFBD>?OMS<4D>㵪<EFBFBD>靽脲<E99DBD><E884B2>祇△<E7A587>枏<EFBFBD><E69E8F>喳虾<E596B3>芸𢆡<E88AB8><F0A286A1><EFBFBD>">
銝<EFBFBD><EFBFBD>株<EFBFBD><EFBFBD>?
<a-step description="桌面端点击「连接 OMS」;保持本页打开即可自动授权">
一键连接
</a-step>
<a-step description="启动引擎,Chrome 加载内置扩展并登录小红书">
<EFBFBD>祆㦤撘閙<EFBFBD>銝擧<EFBFBD>撅?
本机引擎与扩展
</a-step>
<a-step description="在此页远程启停引擎、扫码登录小红书">
Web 远程控制
</a-step>
</a-steps>
<a-alert type="info" style="margin-bottom: 12px" title="<EFBFBD>𣳇<EFBFBD>憛怠<EFBFBD> API <20>?Token">
霂瑕<EFBFBD><EFBFBD>冽<EFBFBD>閫<EFBFBD>膥<EFBFBD>餃<EFBFBD> OMS嚗<EFBFBD><EFBFBD><EFBFBD>枏<EFBFBD>獢屸𢒰蝡舐<EFBFBD><EFBFBD>颯<EFBFBD>諹<EFBFBD><EFBFBD>?OMS<EFBFBD>溻<EFBFBD><EFBFBD>𥅾<EFBFBD>祇△撌脫<EFBFBD>撘<EFBFBD>嚗峕<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>芸𢆡摰峕<EFBFBD><EFBFBD>?
<a-alert type="info" style="margin-bottom: 12px" title="无需填写 API / Token">
请先在浏览器登录 OMS,再打开桌面端点击「连接 OMS」。若本页已打开,授权会自动完成。
</a-alert>
<div class="cmd-block">
<div class="cmd-label">獢屸𢒰蝡?/div>
<div class="cmd-label">桌面端</div>
<pre>{{ desktopAgentHint }}</pre>
</div>
</a-drawer>
@ -257,41 +257,41 @@ const bridgeUrlText = computed(() => {
const engineBadge = computed((): { kind: XhsBadgeKind; text: string } => {
const agent = selectedAgent.value;
if (!agent || agent.status !== 'online') return { kind: 'idle', text: '敺<EFBFBD><EFBFBD><EFBFBD>? };
if (agent.engineRunning) return { kind: 'ok', text: '餈鞱<EFBFBD>銝? };
if (agent.extensionConnected) return { kind: 'warn', text: '敺<EFBFBD><EFBFBD>撅? };
return { kind: 'err', text: '<EFBFBD>芸鍳<EFBFBD>? };
if (!agent || agent.status !== 'online') return { kind: 'idle', text: '待刷新' };
if (agent.engineRunning) return { kind: 'ok', text: '运行中' };
if (agent.extensionConnected) return { kind: 'warn', text: '待扩展' };
return { kind: 'err', text: '未启动' };
});
const loginBadge = computed((): { kind: XhsBadgeKind; text: string } => {
const agent = selectedAgent.value;
if (!agent || agent.status !== 'online') return { kind: 'idle', text: '敺<EFBFBD><EFBFBD><EFBFBD>? };
if (agent.xhsLoggedIn) return { kind: 'ok', text: '撌脩蒈敶? };
if (!agent || agent.status !== 'online') return { kind: 'idle', text: '待刷新' };
if (agent.xhsLoggedIn) return { kind: 'ok', text: '已登录' };
if (agent.engineRunning && agent.extensionConnected) return { kind: 'ok', text: '扩展就绪' };
if (agent.engineRunning) return { kind: 'warn', text: '<EFBFBD>芰蒈敶? };
return { kind: 'warn', text: '敺<EFBFBD>鍳<EFBFBD>? };
if (agent.engineRunning) return { kind: 'warn', text: '未登录' };
return { kind: 'warn', text: '待启动' };
});
const loginGuideText = computed(() => {
const agent = selectedAgent.value;
if (!agent) {
return '<EFBFBD>芷<EFBFBD>嚗朞<EFBFBD><EFBFBD>?Agent <EFBFBD>?<EFBFBD>臬𢆡撘閙<EFBFBD> <EFBFBD>?<EFBFBD>?Chrome <EFBFBD>㰘蝸<EFBFBD>拙<EFBFBD> <EFBFBD>?<EFBFBD>枏<EFBFBD>撠讐滯銋血僎<EFBFBD>餃<EFBFBD><EFBFBD>?;
return '只需:连接 Agent → 启动引擎 → 在 Chrome 加载扩展 → 打开小红书并登录。';
}
if (agent.xhsLoggedIn) {
return '<EFBFBD>⊿<EFBFBD><EFBFBD>朞<EFBFBD>嚗𡁜虾<EFBFBD>具<EFBFBD><EFBFBD><EFBFBD>摰孵<EFBFBD><EFBFBD>僐<EFBFBD>齿<EFBFBD><EFBFBD>𣬚<EFBFBD>甈曉<EFBFBD>雿栶<EFBFBD>漤△雿輻鍂撌亙<EFBFBD><EFBFBD>?;
return '校验通过:可在「内容分析」或「爆款创作」页使用工具。';
}
if (agent.engineRunning && agent.extensionConnected) {
return '撘閙<EFBFBD>銝擧<EFBFBD>撅訫歇撠梁貌<EFBFBD><EFBFBD>窈<EFBFBD>?Chrome 銝剜<EFBFBD>撘<EFBFBD>撠讐滯銋血僎<EFBFBD>餃<EFBFBD>嚗𥕦虾<EFBFBD>孵稬<EFBFBD>拙<EFBFBD><EFBFBD>暹<EFBFBD><EFBFBD>亦<EFBFBD>摰墧𧒄<EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>?;
return '引擎与扩展已就绪。请在 Chrome 中打开小红书并登录;可点击扩展图标查看实时状态。';
}
return '<EFBFBD>芷<EFBFBD>嚗𡁜鍳<EFBFBD>典<EFBFBD><EFBFBD>?<EFBFBD>?<EFBFBD>?Chrome <EFBFBD>㰘蝸<EFBFBD>拙<EFBFBD> <EFBFBD>?<EFBFBD>枏<EFBFBD>撠讐滯銋血僎<EFBFBD>餃<EFBFBD><EFBFBD><EFBFBD><EFBFBD>撅訫㦛<EFBFBD><EFBFBD>虾<EFBFBD>亦<EFBFBD> Bridge 銝𡒊蒈敶閧𠶖<EFBFBD><EFBFBD><EFBFBD>銝𧢲䲮<EFBFBD>厰僼<EFBFBD>其<EFBFBD>敹急㭘<EFBFBD>滢<EFBFBD><EFBFBD>?;
return '只需:启动引擎 → 在 Chrome 加载扩展 → 打开小红书并登录。扩展图标可查看 Bridge 与登录状态;下方按钮用于快捷操作。';
});
const desktopAgentHint = computed(() =>
[
'1. 运行 oms0414/xhs-agent-desktop/run.ps1',
'2. 獢屸𢒰蝡舐<EFBFBD><EFBFBD>颯<EFBFBD>諹<EFBFBD><EFBFBD>?OMS<EFBFBD>㵪<EFBFBD><EFBFBD>𣳇<EFBFBD>憛?API / Token嚗?,
'3. 靽脲<EFBFBD><EFBFBD>祇△<EFBFBD>枏<EFBFBD> <EFBFBD>?<EFBFBD>芸𢆡<EFBFBD><EFBFBD><EFBFBD>嚗𥟇<EFBFBD><EFBFBD>㗇<EFBFBD>蝷箸<EFBFBD>撘<EFBFBD><EFBFBD><EFBFBD><EFBFBD>憿?,
'4. <EFBFBD>臬𢆡撘閙<EFBFBD> <EFBFBD>?Chrome <EFBFBD>㰘蝸<EFBFBD>拙<EFBFBD><EFBFBD>桀<EFBFBD> <EFBFBD>?<EFBFBD>餃<EFBFBD>撠讐滯銋?,
'2. 桌面端点击「连接 OMS」(无需填 API / Token)',
'3. 保持本页打开 → 自动授权;或按提示打开授权页',
'4. 启动引擎 → Chrome 加载扩展目录 → 登录小红书',
].join('\n'),
);
@ -333,7 +333,7 @@ async function tryAutoPair() {
const ok = await pushCredentialsToAgent(token);
if (ok) {
pairPushed = true;
Message.success('撌脣<EFBFBD>獢屸𢒰 Agent <EFBFBD>煾<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?);
Message.success('已向桌面 Agent 发送授权');
setTimeout(loadAgents, 1500);
}
}
@ -370,13 +370,13 @@ async function control(action: string) {
return;
}
if (agent.status !== 'online') {
Message.warning('<EFBFBD><EFBFBD><EFBFBD>?Agent 蝳餌瑪嚗諹窈璉<E7AA88><E79289>交𧋦<E4BAA4>箸糓<E7AEB8>血銁餈鞱<E9A488>');
Message.warning('所有 Agent 离线,请检查本机是否在运行');
return;
}
if (action === 'clear_cookies') {
Modal.confirm({
title: '<EFBFBD><EFBFBD><EFBFBD>箇蒈敶?,
content: '撠<EFBFBD>銁瘚讛<EFBFBD><EFBFBD>其葉<EFBFBD><EFBFBD><EFBFBD>箏<EFBFBD>蝥V髡<EFBFBD>餃<EFBFBD><EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>蝖桀<EFBFBD>蝏抒賒嚗?,
title: '退出登录',
content: '将在浏览器中退出小红书登录状态,确定继续?',
onOk: () => sendControl(agent.id, action),
});
return;
@ -387,7 +387,7 @@ async function control(action: string) {
async function sendControl(agentId: number, action: string) {
try {
const res = await controlXhsAgent(agentId, action);
Message.success(`撌脣<EFBFBD>撱箔遙<EFBFBD>?#${res.taskId}`);
Message.success(`已创建任务 #${res.taskId}`);
setTimeout(loadAgents, 2000);
} catch (e: any) {
Message.error(e?.message || '操作失败');