Merge branch 'master' of https://github.com/xiaoliutral/itfx-front
This commit is contained in:
commit
152e281cdd
@ -31,7 +31,7 @@ alwaysApply: true
|
||||
- HandsontablePro 列定义用 `key` + `label`,勿用 `data` + `title`
|
||||
- HandsontablePro 固定列序:操作(可选,内容自适应宽)→ 序号(50px)→ 复选框(可选,60px)→ 数据;`stretch-columns` 只拉宽数据列,勿再传 `action-column-position` / `action-column-width`
|
||||
- 列表页高度:`flex: 1; min-height: 0`,`.list-wrap` 勿写死 min-height
|
||||
- 分页 10/30/50/100,默认 30
|
||||
- 分页 10/30/50/100,默认 50
|
||||
- 新页面 path 与 API 前缀冲突时须配置 nginx SPA 回退(见规范 §十五)
|
||||
|
||||
### 权限
|
||||
|
||||
@ -4,6 +4,13 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>9610跨境电商出口方案选择指南 | IDE国际物流</title>
|
||||
<meta name="description" content="IDE 9610跨境电商出口指南:报关、退税与专线物流怎么选,一看就懂。">
|
||||
<meta property="og:title" content="9610跨境电商出口方案选择指南 | IDE国际物流">
|
||||
<meta property="og:description" content="IDE 9610跨境电商出口指南:报关、退税与专线物流怎么选,一看就懂。">
|
||||
<meta property="og:image" content="https://ideship.com/site/assets/img/og-cover.png">
|
||||
<meta property="og:url" content="https://ideship.com/9610.html">
|
||||
<meta property="og:type" content="website">
|
||||
<script src="assets/js/og-meta.js"></script>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
|
||||
BIN
IDE官网/assets/img/og-cover.png
Normal file
BIN
IDE官网/assets/img/og-cover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@ -712,6 +712,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (window.IDEAdvisor) {
|
||||
window.IDEAdvisor.setLanguage(isChinese ? 'zh' : 'en');
|
||||
}
|
||||
|
||||
if (window.IDEOpenGraph && typeof window.IDEOpenGraph.refresh === 'function') {
|
||||
window.IDEOpenGraph.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSavedLanguage() {
|
||||
|
||||
78
IDE官网/assets/js/og-meta.js
Normal file
78
IDE官网/assets/js/og-meta.js
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Open Graph:复用 <title> / meta[name=description],补齐绝对 og:url / og:image。
|
||||
* 爬虫可读 head 内静态 meta;本脚本在浏览器中按当前域名校正。
|
||||
*/
|
||||
(function () {
|
||||
var PROD_ORIGIN = 'https://ideship.com';
|
||||
var OG_IMAGE_PATH = '/site/assets/img/og-cover.png';
|
||||
var OG_IMAGE_REL = 'assets/img/og-cover.png';
|
||||
|
||||
function truncate(text, max) {
|
||||
var value = String(text || '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
if (value.length <= max) return value;
|
||||
return value.slice(0, max);
|
||||
}
|
||||
|
||||
function ensureMeta(property, content) {
|
||||
if (!content) return;
|
||||
var node = document.querySelector('meta[property="' + property + '"]');
|
||||
if (!node) {
|
||||
node = document.createElement('meta');
|
||||
node.setAttribute('property', property);
|
||||
document.head.appendChild(node);
|
||||
}
|
||||
node.setAttribute('content', content);
|
||||
}
|
||||
|
||||
function resolveOrigin() {
|
||||
if (location.protocol === 'http:' || location.protocol === 'https:') {
|
||||
return location.origin;
|
||||
}
|
||||
return PROD_ORIGIN;
|
||||
}
|
||||
|
||||
function resolvePageUrl(origin) {
|
||||
if (location.protocol === 'file:') {
|
||||
return /9610\.html$/i.test(location.pathname || '')
|
||||
? PROD_ORIGIN + '/9610.html'
|
||||
: PROD_ORIGIN + '/';
|
||||
}
|
||||
var path = location.pathname || '/';
|
||||
if (/\/index\.html$/i.test(path)) {
|
||||
path = path.replace(/\/index\.html$/i, '/') || '/';
|
||||
}
|
||||
if (path === '/site' || path === '/site/' || path === '/site/index.html') {
|
||||
path = '/';
|
||||
}
|
||||
return origin + path + (location.search || '');
|
||||
}
|
||||
|
||||
function resolveImageUrl(origin) {
|
||||
if (origin === PROD_ORIGIN || /(^|\.)ideship\.com$/i.test(location.hostname || '')) {
|
||||
return origin + OG_IMAGE_PATH;
|
||||
}
|
||||
try {
|
||||
return new URL(OG_IMAGE_REL, location.href).href;
|
||||
} catch (e) {
|
||||
return origin + '/assets/img/og-cover.png';
|
||||
}
|
||||
}
|
||||
|
||||
function applyOpenGraph() {
|
||||
var title = truncate(document.title, 30);
|
||||
var descNode = document.querySelector('meta[name="description"]');
|
||||
var description = truncate(descNode && descNode.getAttribute('content'), 60);
|
||||
var origin = resolveOrigin();
|
||||
|
||||
ensureMeta('og:title', title);
|
||||
if (description) ensureMeta('og:description', description);
|
||||
ensureMeta('og:image', resolveImageUrl(origin));
|
||||
ensureMeta('og:url', resolvePageUrl(origin));
|
||||
ensureMeta('og:type', 'website');
|
||||
}
|
||||
|
||||
applyOpenGraph();
|
||||
window.IDEOpenGraph = { refresh: applyOpenGraph };
|
||||
})();
|
||||
@ -9,6 +9,14 @@
|
||||
<meta content="IDE 面向中小卖家的跨境物流。全球数字化一站式跨境物流服务商。注册登记后即可询价,一口价不砍,专属顾问跟到签收。" name="description">
|
||||
<meta content="IDE, 跨境物流, 中小卖家, 9610, 疑难单, 出口退税, 欧美专线, 东南亚专线" name="keywords">
|
||||
|
||||
<!-- Open Graph(标题/描述复用上方 title、description;图片为 478×478) -->
|
||||
<meta property="og:title" content="IDE - 跨境物流,全球即递">
|
||||
<meta property="og:description" content="IDE 面向中小卖家的跨境物流。全球数字化一站式服务,一口价不砍。">
|
||||
<meta property="og:image" content="https://ideship.com/site/assets/img/og-cover.png">
|
||||
<meta property="og:url" content="https://ideship.com/">
|
||||
<meta property="og:type" content="website">
|
||||
<script src="assets/js/og-meta.js"></script>
|
||||
|
||||
<!-- Favicons(logo1 已入库;favicon.png 由 postbuild 同步) -->
|
||||
<link href="assets/img/logo1.png" rel="icon" type="image/png">
|
||||
<link href="assets/img/apple-touch-icon.png" rel="apple-touch-icon">
|
||||
|
||||
@ -237,7 +237,7 @@ flowchart TB
|
||||
| `sku` | `WarehouseSku` | `views/warehouse/sku/index.vue` | `Warehouse.Sku.Query` |
|
||||
| `inboundNotice` | `WarehouseInboundNotice` | `views/warehouse/inboundNotice/index.vue` | `Warehouse.InboundNotice.Query` |
|
||||
|
||||
列表页布局遵循 `docs/前端页面设计规范.md`:`xxx-container`、`table-toolbar`、分页右上角、`PaginationPro` 默认 30 条。
|
||||
列表页布局遵循 `docs/前端页面设计规范.md`:`xxx-container`、`table-toolbar`、分页右上角、`PaginationPro` 默认 50 条。
|
||||
|
||||
### 2.3 RBAC 权限码
|
||||
|
||||
|
||||
@ -302,7 +302,7 @@ itemList: [
|
||||
|
||||
- **位置(必选)**:所有分页组件(`PaginationPro`、Arco `a-pagination`、项目内其它分页封装等)须置于**列表的右上角**——即放在**紧邻列表上方**的工具栏**右侧**(`table-toolbar` / `.list-toolbar` 的 `.toolbar-right`,与 `toolbar-left` 同一行),与列表左右对齐、视觉上位于列表区域上方靠右。禁止将分页放在表格**下方**、列表**底部**、或单独居中另起一行(与「二、2.5.3」「2.5.4」工具栏约定一致)。
|
||||
- 可选 pageSize:`10 / 30 / 50 / 100`(按需裁剪)
|
||||
- 默认 pageSize:`30`
|
||||
- 默认 pageSize:`50`
|
||||
- 接口参数:`pageIndex`、`pageSize`;返回:`totalCount`、`items` 或 `data.list`
|
||||
|
||||
### 7.4 操作列
|
||||
@ -547,7 +547,7 @@ const { t } = useI18n();
|
||||
- [ ] 表单使用 FormPro 或统一 label/span
|
||||
- [ ] 表格优先 TablePro / CustomList / HandsontablePro
|
||||
- [ ] **HandsontablePro** 列定义使用 **`key` + `label`**,勿用 `data` + `title`(**§7.5**)
|
||||
- [ ] 分页组件置于**列表上方工具栏右侧**(列表右上角),**§7.3**;分页 10/30/50/100,默认 30
|
||||
- [ ] 分页组件置于**列表上方工具栏右侧**(列表右上角),**§7.3**;分页 10/30/50/100,默认 50
|
||||
- [ ] 操作按钮按权限使用 `hasPermission`
|
||||
- [ ] 路由 meta 配置 `permission`
|
||||
- [ ] **新增前端页面路径**若落在 `/order/`、`/channel/`、`/system/` 等与 Admin API 同源前缀下,须在 **`nginx-default.conf` / `nginx-docker.conf`** 增加 `^~` 的 `try_files /index.html =200` 规则(**§十五**),避免刷新/直链 404
|
||||
|
||||
@ -632,3 +632,10 @@ div.main-menu[data-v-eb552580] {
|
||||
font-size: 12px !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
/* 工具栏已选条数:数字默认红色 */
|
||||
.selected-count .count-number {
|
||||
color: #f53f3f;
|
||||
font-weight: 600;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
@ -23,6 +23,36 @@
|
||||
appStore.theme === 'dark' ? 'ht-theme-classic-dark' : 'ht-theme-classic',
|
||||
);
|
||||
|
||||
/** Vite 异步 CSS 分包时,等 --ht-line-height 就绪再 new Handsontable,避免主题告警 */
|
||||
function waitForHotThemeStyles(themeName: string, timeoutMs = 3000): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const started = Date.now();
|
||||
const probe = document.createElement('div');
|
||||
probe.className = themeName;
|
||||
probe.style.cssText = 'position:absolute;left:-99999px;top:0;visibility:hidden;pointer-events:none;';
|
||||
document.body.appendChild(probe);
|
||||
|
||||
const finish = (ok: boolean) => {
|
||||
probe.remove();
|
||||
resolve(ok);
|
||||
};
|
||||
|
||||
const tick = () => {
|
||||
const lineHeight = getComputedStyle(probe).getPropertyValue('--ht-line-height').trim();
|
||||
if (lineHeight) {
|
||||
finish(true);
|
||||
return;
|
||||
}
|
||||
if (Date.now() - started >= timeoutMs) {
|
||||
finish(false);
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(tick);
|
||||
};
|
||||
tick();
|
||||
});
|
||||
}
|
||||
|
||||
// 注册所有 Handsontable 模块
|
||||
registerAllModules();
|
||||
|
||||
@ -749,12 +779,11 @@
|
||||
}
|
||||
|
||||
function applyLiquidFieldStyle(el: HTMLElement, focused = false) {
|
||||
const dark = appStore.theme === 'dark';
|
||||
// 内联 !important 压过浏览器 UA / Handsontable 默认边框(截图里那圈硬黑框)
|
||||
// 与工作台卡片同款磨砂玻璃 token(内联 !important 压过 UA / HOT 默认边框)
|
||||
el.style.setProperty('width', '100%', 'important');
|
||||
el.style.setProperty('min-width', '0', 'important');
|
||||
el.style.setProperty('max-width', '100%', 'important');
|
||||
el.style.setProperty('height', '24px', 'important');
|
||||
el.style.setProperty('height', '26px', 'important');
|
||||
el.style.setProperty('padding', '0 10px', 'important');
|
||||
el.style.setProperty('margin', '0', 'important');
|
||||
el.style.setProperty('border-width', '1px', 'important');
|
||||
@ -762,37 +791,35 @@
|
||||
el.style.setProperty(
|
||||
'border-color',
|
||||
focused
|
||||
? 'color-mix(in srgb, var(--app-primary-color) 36%, transparent)'
|
||||
: dark
|
||||
? 'var(--color-border-2)'
|
||||
: 'rgba(255, 255, 255, 0.85)',
|
||||
? 'color-mix(in srgb, var(--app-primary-color) 22%, var(--app-acrylic-border-strong, rgba(255,255,255,0.92)))'
|
||||
: 'var(--app-acrylic-border, rgba(255, 255, 255, 0.72))',
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty('border-radius', '999px', 'important');
|
||||
el.style.setProperty('border-radius', '12px', 'important');
|
||||
el.style.setProperty('outline', 'none', 'important');
|
||||
el.style.setProperty('box-sizing', 'border-box', 'important');
|
||||
el.style.setProperty(
|
||||
'background',
|
||||
dark
|
||||
? focused
|
||||
? 'var(--color-bg-3)'
|
||||
: 'var(--color-fill-2)'
|
||||
: focused
|
||||
? 'linear-gradient(165deg, rgba(255,255,255,0.98) 0%, rgba(255,255,255,0.72) 55%, rgba(240,248,248,0.65) 100%)'
|
||||
: 'linear-gradient(165deg, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.5) 55%, rgba(245,247,248,0.42) 100%)',
|
||||
focused
|
||||
? 'var(--app-acrylic-hover-bg, rgba(255, 255, 255, 0.44))'
|
||||
: 'var(--app-acrylic-bg, rgba(255, 255, 255, 0.32))',
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty(
|
||||
'backdrop-filter',
|
||||
'var(--app-acrylic-blur, blur(28px) saturate(160%))',
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty(
|
||||
'-webkit-backdrop-filter',
|
||||
'var(--app-acrylic-blur, blur(28px) saturate(160%))',
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty('backdrop-filter', dark ? 'none' : 'blur(12px) saturate(1.2)', 'important');
|
||||
el.style.setProperty('-webkit-backdrop-filter', dark ? 'none' : 'blur(12px) saturate(1.2)', 'important');
|
||||
el.style.setProperty(
|
||||
'box-shadow',
|
||||
dark
|
||||
? focused
|
||||
? '0 0 0 2px color-mix(in srgb, var(--app-primary-color) 28%, transparent)'
|
||||
: 'none'
|
||||
: focused
|
||||
? 'inset 0 1px 0 rgba(255,255,255,1), inset 0 -1px 0 rgba(0,121,121,0.06), 0 0 0 3px rgba(0,121,121,0.12), 0 3px 10px rgba(15,23,42,0.07)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.98), inset 0 -1px 1px rgba(15,23,42,0.03), 0 1px 2px rgba(15,23,42,0.04), 0 2px 6px rgba(15,23,42,0.03)',
|
||||
focused
|
||||
? '0 0 0 2px color-mix(in srgb, var(--app-primary-color) 16%, transparent)'
|
||||
: 'none',
|
||||
'important'
|
||||
);
|
||||
el.style.setProperty(
|
||||
@ -802,11 +829,12 @@
|
||||
);
|
||||
el.style.setProperty('font-size', '12px', 'important');
|
||||
el.style.setProperty('font-weight', '500', 'important');
|
||||
el.style.setProperty('line-height', '22px', 'important');
|
||||
el.style.setProperty('line-height', '24px', 'important');
|
||||
el.style.setProperty('font-family', 'inherit', 'important');
|
||||
el.style.setProperty('caret-color', 'var(--app-primary-color, #054f74)', 'important');
|
||||
el.style.setProperty('-webkit-appearance', 'none', 'important');
|
||||
el.style.setProperty('appearance', 'none', 'important');
|
||||
el.style.setProperty('transition', 'border-color 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease', 'important');
|
||||
if (el.tagName === 'BUTTON') {
|
||||
el.style.setProperty('cursor', 'pointer', 'important');
|
||||
el.style.setProperty('text-align', 'left', 'important');
|
||||
@ -858,6 +886,15 @@
|
||||
input.value = raw;
|
||||
input.title = '双击打开筛选 · 回车搜索';
|
||||
});
|
||||
input.addEventListener('mouseenter', () => {
|
||||
if (document.activeElement === input) return;
|
||||
input.style.setProperty('background', 'var(--app-acrylic-hover-bg, rgba(255, 255, 255, 0.44))', 'important');
|
||||
input.style.setProperty('border-color', 'var(--app-acrylic-border-strong, rgba(255, 255, 255, 0.92))', 'important');
|
||||
});
|
||||
input.addEventListener('mouseleave', () => {
|
||||
if (document.activeElement === input) return;
|
||||
applyLiquidFieldStyle(input, false);
|
||||
});
|
||||
input.addEventListener('paste', (e: ClipboardEvent) => {
|
||||
e.stopPropagation();
|
||||
const clipboardData = e.clipboardData;
|
||||
@ -1311,7 +1348,8 @@
|
||||
width: '100%',
|
||||
height: tableHeight,
|
||||
licenseKey: 'non-commercial-and-evaluation',
|
||||
className: hotThemeClass.value,
|
||||
// HOT 16:主题必须用 themeName(勿塞进 className),否则会报 stylesheets missing
|
||||
themeName: hotThemeClass.value,
|
||||
language: 'zh-CN',
|
||||
readOnly: props.readOnly === true,
|
||||
// 可编辑:回车/选中后输入即可开始编辑
|
||||
@ -1806,6 +1844,8 @@
|
||||
removeFilterIconFromColumns();
|
||||
syncHybridHeaders();
|
||||
remasureActionColumnFromDom();
|
||||
// remasure/updateSettings 后 HOT 会按原生滚动条再次缩短 clone,需再对齐一次
|
||||
scheduleCloneOverlaySync();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1830,24 +1870,92 @@
|
||||
holder.style.setProperty('-ms-overflow-style', 'none', 'important');
|
||||
|
||||
const hider = holder.querySelector(':scope > .wtHider') as HTMLElement | null;
|
||||
if (hider) {
|
||||
hider.style.setProperty('min-width', 'calc(100% + 1px)', 'important');
|
||||
}
|
||||
const spreader = holder.querySelector(':scope > .wtHider > .wtSpreader') as HTMLElement | null;
|
||||
// 切勿给 wtSpreader 设 min-width:它带横向虚拟滚动 left 偏移,强行拉宽会在滚到最右出现空白并导致表头/内容错位
|
||||
if (spreader) {
|
||||
spreader.style.setProperty('min-width', 'calc(100% + 1px)', 'important');
|
||||
spreader.style.removeProperty('min-width');
|
||||
}
|
||||
if (hider) {
|
||||
hider.style.removeProperty('min-width');
|
||||
// 先去掉强制宽度再测:仅在 stretch 铺满、无真实横向溢出时补 1px,稳住自绘横条
|
||||
const needsFakeOverflow = props.stretchColumns && holder.scrollWidth <= holder.clientWidth;
|
||||
if (needsFakeOverflow) {
|
||||
hider.style.setProperty('min-width', 'calc(100% + 1px)', 'important');
|
||||
}
|
||||
}
|
||||
|
||||
bindHorizontalScrollbar(holder);
|
||||
scheduleCloneOverlaySync(holder);
|
||||
syncHorizontalScrollbarMetrics(holder);
|
||||
};
|
||||
|
||||
const syncCloneOverlaysFromMaster = (holder?: HTMLElement | null) => {
|
||||
const currentHolder = holder || masterHolderEl;
|
||||
if (!currentHolder || !hotContainer.value) return;
|
||||
const top = hotContainer.value.querySelector('.ht_clone_top .wtHolder') as HTMLElement | null;
|
||||
const bottom = hotContainer.value.querySelector('.ht_clone_bottom .wtHolder') as HTMLElement | null;
|
||||
const left = hotContainer.value.querySelector('.ht_clone_left .wtHolder') as HTMLElement | null;
|
||||
const corner = hotContainer.value.querySelector('.ht_clone_top_left_corner .wtHolder') as HTMLElement | null;
|
||||
if (top && top.scrollLeft !== currentHolder.scrollLeft) top.scrollLeft = currentHolder.scrollLeft;
|
||||
if (bottom && bottom.scrollLeft !== currentHolder.scrollLeft) bottom.scrollLeft = currentHolder.scrollLeft;
|
||||
if (left && left.scrollTop !== currentHolder.scrollTop) left.scrollTop = currentHolder.scrollTop;
|
||||
if (corner) {
|
||||
if (corner.scrollLeft !== currentHolder.scrollLeft) corner.scrollLeft = currentHolder.scrollLeft;
|
||||
if (corner.scrollTop !== currentHolder.scrollTop) corner.scrollTop = currentHolder.scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
/** 隐藏原生纵条后 HOT 仍按滚动条宽度缩短 clone_top,导致表头比内容窄一截 */
|
||||
const syncCloneOverlayWidths = (holder?: HTMLElement | null) => {
|
||||
const currentHolder = holder || masterHolderEl;
|
||||
if (!currentHolder || !hotContainer.value) return;
|
||||
const w = currentHolder.clientWidth;
|
||||
if (w <= 0) return;
|
||||
const widthPx = `${w}px`;
|
||||
// 只校准顶部冻结表头;切勿拉宽 ht_clone_bottom,否则会盖住主表导致滚轮/拖动失效
|
||||
const cloneTop = hotContainer.value.querySelector('.ht_clone_top') as HTMLElement | null;
|
||||
const cloneTopHolder = hotContainer.value.querySelector('.ht_clone_top .wtHolder') as HTMLElement | null;
|
||||
if (cloneTop) {
|
||||
cloneTop.style.setProperty('width', widthPx, 'important');
|
||||
cloneTop.style.setProperty('right', '0px', 'important');
|
||||
cloneTop.style.setProperty('z-index', '180', 'important');
|
||||
}
|
||||
if (cloneTopHolder) cloneTopHolder.style.setProperty('width', widthPx, 'important');
|
||||
|
||||
// 无底部冻结行时,底 clone 应为不可点的空层
|
||||
const cloneBottom = hotContainer.value.querySelector('.ht_clone_bottom') as HTMLElement | null;
|
||||
if (cloneBottom) {
|
||||
cloneBottom.style.removeProperty('width');
|
||||
cloneBottom.style.setProperty('pointer-events', 'none', 'important');
|
||||
cloneBottom.style.setProperty('height', '0px', 'important');
|
||||
const bottomHolder = cloneBottom.querySelector('.wtHolder') as HTMLElement | null;
|
||||
if (bottomHolder) {
|
||||
bottomHolder.style.removeProperty('width');
|
||||
bottomHolder.style.setProperty('pointer-events', 'none', 'important');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** HOT 布局可能在 afterRender 之后才写回 clone 宽度,延迟再对齐一次 */
|
||||
const scheduleCloneOverlaySync = (holder?: HTMLElement | null) => {
|
||||
syncCloneOverlayWidths(holder);
|
||||
syncCloneOverlaysFromMaster(holder);
|
||||
requestAnimationFrame(() => {
|
||||
syncCloneOverlayWidths(holder);
|
||||
syncCloneOverlaysFromMaster(holder);
|
||||
});
|
||||
};
|
||||
|
||||
const onMasterHolderScroll = () => {
|
||||
// 滚动时 HOT 会把 clone_top 宽度改回“预留原生纵条”的窄值,需持续拉齐,否则右侧纵条会顶到表头空隙里
|
||||
syncCloneOverlaysFromMaster();
|
||||
syncCloneOverlayWidths();
|
||||
if (scrollSyncRaf != null) return;
|
||||
scrollSyncRaf = window.requestAnimationFrame(() => {
|
||||
scrollSyncRaf = null;
|
||||
syncCloneOverlayWidths();
|
||||
syncHorizontalThumbFromScroll();
|
||||
syncVerticalThumbFromScroll();
|
||||
syncVerticalScrollbarMetrics();
|
||||
});
|
||||
};
|
||||
|
||||
@ -1885,28 +1993,38 @@
|
||||
const trackEl = verticalScrollbarTrackRef.value;
|
||||
if (!currentHolder || !trackEl) return;
|
||||
|
||||
// 纵向滚动条从“主数据区(wtHolder)顶部”开始,避免任何表头样式差异带来的重叠
|
||||
const holderHost = hotContainer.value?.querySelector('.ht_master .wtHolder') as HTMLElement | null;
|
||||
// 纵条从冻结表头下沿开始,避免盖到表头;高度对齐主数据可视区剩余高度
|
||||
const containerEl = containerRef.value;
|
||||
let nextTop = 40;
|
||||
if (holderHost && containerEl) {
|
||||
const holderRect = holderHost.getBoundingClientRect();
|
||||
const cloneTop = hotContainer.value?.querySelector('.ht_clone_top') as HTMLElement | null;
|
||||
const hScroll = horizontalScrollbarTrackRef.value;
|
||||
let nextTop = 0;
|
||||
let trackHeight = Math.max(currentHolder.clientHeight, 0);
|
||||
|
||||
if (containerEl) {
|
||||
const containerRect = containerEl.getBoundingClientRect();
|
||||
// +1 作为边界安全余量,避免 1px 级别重叠
|
||||
nextTop = Math.max(Math.ceil(holderRect.top - containerRect.top) + 1, 0);
|
||||
} else if (holderHost) {
|
||||
nextTop = Math.max(Math.ceil(holderHost.offsetTop) + 1, 0);
|
||||
const holderRect = currentHolder.getBoundingClientRect();
|
||||
if (cloneTop) {
|
||||
const cloneRect = cloneTop.getBoundingClientRect();
|
||||
// +2:与冻结表头留出缝,避免贴边看起来像纵条顶进表头
|
||||
nextTop = Math.max(Math.ceil(cloneRect.bottom - containerRect.top) + 2, 0);
|
||||
} else {
|
||||
nextTop = Math.max(Math.ceil(holderRect.top - containerRect.top), 0);
|
||||
}
|
||||
const bottomLimit = hScroll && customScrollbar.visible
|
||||
? Math.floor(hScroll.getBoundingClientRect().top - containerRect.top)
|
||||
: Math.floor(holderRect.bottom - containerRect.top);
|
||||
trackHeight = Math.max(bottomLimit - nextTop, 0);
|
||||
}
|
||||
|
||||
if (customVerticalScrollbar.trackTop !== nextTop) {
|
||||
customVerticalScrollbar.trackTop = nextTop;
|
||||
}
|
||||
|
||||
const trackHeight = Math.max(currentHolder.clientHeight, 0);
|
||||
customVerticalScrollbar.trackVisibleHeight = trackHeight;
|
||||
const scrollHeight = Math.max(currentHolder.scrollHeight, currentHolder.clientHeight + 1);
|
||||
const maxScrollTop = Math.max(scrollHeight - currentHolder.clientHeight, 0);
|
||||
|
||||
customVerticalScrollbar.visible = maxScrollTop > 0;
|
||||
customVerticalScrollbar.visible = maxScrollTop > 0 && trackHeight > 0;
|
||||
customVerticalScrollbar.trackHeight = trackHeight;
|
||||
|
||||
const thumbHeight = maxScrollTop > 0
|
||||
@ -2121,18 +2239,15 @@
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 日夜切换:同步 HOT 主题类并刷新表头液态输入样式
|
||||
// 日夜切换:走 HOT 官方 useTheme,确保 CSS 变量与内部主题状态一致
|
||||
watch(hotThemeClass, (cls) => {
|
||||
const root = hotContainer.value;
|
||||
if (root) {
|
||||
root.classList.remove('ht-theme-classic', 'ht-theme-classic-dark');
|
||||
root.classList.add(cls);
|
||||
}
|
||||
// Handsontable 会把 className 写到表格根节点
|
||||
const hotRoot = hotInstance?.rootElement as HTMLElement | undefined;
|
||||
if (hotRoot) {
|
||||
hotRoot.classList.remove('ht-theme-classic', 'ht-theme-classic-dark');
|
||||
hotRoot.classList.add(cls);
|
||||
if (hotInstance) {
|
||||
hotInstance.useTheme(cls);
|
||||
}
|
||||
nextTick(() => {
|
||||
syncHybridHeaders();
|
||||
@ -2151,6 +2266,21 @@
|
||||
}
|
||||
);
|
||||
|
||||
/** 主题 CSS 就绪后再创建实例(首屏 / 数据晚到补初始化共用) */
|
||||
async function initHandsontableWhenReady(options?: { finalize?: boolean }) {
|
||||
if (hotInstance) return;
|
||||
const themeReady = await waitForHotThemeStyles(hotThemeClass.value);
|
||||
if (!themeReady) {
|
||||
console.warn('[HandsontablePro] theme CSS not ready, init anyway:', hotThemeClass.value);
|
||||
}
|
||||
if (hotInstance) return;
|
||||
initHandsontable();
|
||||
if (options?.finalize !== false) {
|
||||
finalizeInit();
|
||||
setupResizeObserver();
|
||||
}
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.data, () => {
|
||||
if (ignoreDataWatch) return;
|
||||
@ -2158,9 +2288,8 @@
|
||||
updateData();
|
||||
} else {
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
initHandsontable();
|
||||
}, 200);
|
||||
// 仅补创建;finalize/resize 留给 onMounted,避免重复观察
|
||||
void initHandsontableWhenReady({ finalize: false });
|
||||
});
|
||||
}
|
||||
}, { deep: true });
|
||||
@ -2245,6 +2374,10 @@
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
const setupResizeObserver = () => {
|
||||
if (!containerRef.value) return;
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
resizeObserver = null;
|
||||
}
|
||||
resizeObserver = new ResizeObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry || !hotInstance) return;
|
||||
@ -2280,12 +2413,7 @@
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
// 确保样式已加载后再初始化
|
||||
setTimeout(() => {
|
||||
initHandsontable();
|
||||
finalizeInit();
|
||||
setupResizeObserver();
|
||||
}, 200);
|
||||
void initHandsontableWhenReady();
|
||||
});
|
||||
|
||||
// 添加窗口大小变化监听器
|
||||
@ -2443,6 +2571,41 @@
|
||||
.ht_clone_corner {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
// 隐藏原生纵条后 HOT 仍按 ~15px 缩短冻结表头;拉满宽度,避免右侧纵条从 freeze 表头旁“冒出来”
|
||||
.ht_clone_top {
|
||||
width: 100% !important;
|
||||
right: 0 !important;
|
||||
left: 0 !important;
|
||||
z-index: 180 !important; // 高于自绘纵条,右上角由表头盖住
|
||||
background: var(--color-fill-2) !important;
|
||||
box-sizing: border-box !important;
|
||||
|
||||
.wtHolder {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
// 表头底边压住纵条顶,视觉上纵条绝不进冻结区
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 1px;
|
||||
background: var(--color-border-2);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 无底部冻结行时,底 clone 常被撑成整表透明层,挡住滚轮与纵条拖动
|
||||
.ht_clone_bottom {
|
||||
pointer-events: none !important;
|
||||
height: 0 !important;
|
||||
max-height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.wtHolder {
|
||||
border: none !important;
|
||||
@ -2463,10 +2626,8 @@
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
// 主表 wtHider:stretchH 拉满后 HOT 判定无横向滚动,刷新后横条会消失;略宽于视口以稳定显示横条
|
||||
> .wtHider {
|
||||
min-width: calc(100% + 1px) !important;
|
||||
}
|
||||
// wtHider 的 1px 溢出由 ensureHorizontalScrollbarVisible 按需内联设置;
|
||||
// 勿写死 min-width,否则宽表滚到最右会出现空白列、表头与内容错位
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0 !important;
|
||||
@ -2742,19 +2903,20 @@
|
||||
width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
max-width: 100%;
|
||||
height: 24px !important;
|
||||
height: 26px !important;
|
||||
padding: 0 10px !important;
|
||||
border-radius: 999px !important;
|
||||
/* 工作台卡片同款:磨砂玻璃 + 亮边 */
|
||||
border-radius: 12px !important;
|
||||
border-width: 1px !important;
|
||||
border-style: solid !important;
|
||||
border-color: var(--color-border-2) !important;
|
||||
background: var(--color-bg-2) !important;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
border-color: var(--app-acrylic-border, rgba(255, 255, 255, 0.72)) !important;
|
||||
background: var(--app-acrylic-bg, rgba(255, 255, 255, 0.32)) !important;
|
||||
backdrop-filter: var(--app-acrylic-blur, blur(28px) saturate(160%));
|
||||
-webkit-backdrop-filter: var(--app-acrylic-blur, blur(28px) saturate(160%));
|
||||
box-shadow: none !important;
|
||||
font-size: 12px !important;
|
||||
font-weight: 500 !important;
|
||||
line-height: 22px !important;
|
||||
line-height: 24px !important;
|
||||
color: var(--color-text-2) !important;
|
||||
outline: none !important;
|
||||
box-sizing: border-box !important;
|
||||
@ -2762,20 +2924,30 @@
|
||||
caret-color: var(--app-primary-color, #054f74);
|
||||
-webkit-appearance: none !important;
|
||||
appearance: none !important;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
background 0.2s ease,
|
||||
color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.ht-header-hybrid-input) {
|
||||
&:hover {
|
||||
background: var(--color-fill-2) !important;
|
||||
border-color: var(--app-acrylic-border-strong, rgba(255, 255, 255, 0.92)) !important;
|
||||
background: var(--app-acrylic-hover-bg, rgba(255, 255, 255, 0.44)) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
color: var(--color-text-1) !important;
|
||||
border-color: color-mix(in srgb, var(--app-primary-color) 36%, transparent) !important;
|
||||
background: var(--color-bg-2) !important;
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--app-primary-color) 22%, transparent) !important;
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--app-primary-color) 22%,
|
||||
var(--app-acrylic-border-strong, rgba(255, 255, 255, 0.92))
|
||||
) !important;
|
||||
background: var(--app-acrylic-hover-bg, rgba(255, 255, 255, 0.44)) !important;
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--app-primary-color) 16%, transparent) !important;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
@ -2798,6 +2970,8 @@
|
||||
|
||||
&:hover {
|
||||
color: var(--color-text-2) !important;
|
||||
border-color: var(--app-acrylic-border-strong, rgba(255, 255, 255, 0.92)) !important;
|
||||
background: var(--app-acrylic-hover-bg, rgba(255, 255, 255, 0.44)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2938,7 +3112,8 @@
|
||||
border-left: 0.5px solid #f0f0f0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
z-index: 3;
|
||||
// 高于 ht_clone_bottom(130),低于冻结表头(180),避免滚到表头右上角时纵条压住表头
|
||||
z-index: 150;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
},
|
||||
defaultPageSize: {
|
||||
type: Number,
|
||||
default: null, // null 表示使用默认值 300
|
||||
default: null, // null 表示使用统一默认值 50
|
||||
},
|
||||
});
|
||||
const attrs = useAttrs();
|
||||
@ -129,16 +129,16 @@
|
||||
return baseSizes;
|
||||
};
|
||||
|
||||
// 确定默认 pageSize(按统一分页规则,默认 30)
|
||||
// 确定默认 pageSize(按统一分页规则,默认 50)
|
||||
const getDefaultPageSize = () => {
|
||||
if (props.defaultPageSize) {
|
||||
return props.defaultPageSize;
|
||||
}
|
||||
if (props.maxPageSize) {
|
||||
// 有最大限制时,默认取 30,但不超过 maxPageSize
|
||||
return Math.min(30, props.maxPageSize);
|
||||
// 有最大限制时,默认取 50,但不超过 maxPageSize
|
||||
return Math.min(50, props.maxPageSize);
|
||||
}
|
||||
return 30;
|
||||
return 50;
|
||||
};
|
||||
|
||||
const tablePage = reactive<VxeGridPropTypes.PagerConfig>({
|
||||
|
||||
@ -5046,6 +5046,7 @@ export default {
|
||||
'track.order.drawer.tabs.small': 'Small package',
|
||||
'track.order.drawer.tabs.main': 'Main order',
|
||||
'track.order.table.orderType': 'Order type',
|
||||
'track.order.table.orderNo': 'Order number',
|
||||
'track.order.table.customsOrderNo': 'Customer order number',
|
||||
'track.order.table.customerShortName': 'Customer short name',
|
||||
'track.order.table.thirdPartyOrderNo': 'Last mile order number',
|
||||
@ -5065,6 +5066,7 @@ export default {
|
||||
'track.order.table.product': 'Sales product',
|
||||
'track.order.table.channel': 'Service channel',
|
||||
'track.order.table.apiCode': 'API service provider',
|
||||
'track.order.table.countryCode': 'Country',
|
||||
'track.order.table.destCountryCode': 'Country',
|
||||
'track.order.table.province': 'Province/State',
|
||||
'track.order.table.city': 'City',
|
||||
|
||||
@ -5116,6 +5116,7 @@ export default {
|
||||
'track.order.drawer.tabs.package': '大包',
|
||||
'track.order.drawer.tabs.main': '主单',
|
||||
'track.order.table.orderType': '订单类型',
|
||||
'track.order.table.orderNo': '订单号',
|
||||
'track.order.table.customsOrderNo': '客户单号',
|
||||
'track.order.table.customerShortName': '客户简称',
|
||||
'track.order.table.thirdPartyOrderNo': '尾程单号',
|
||||
@ -5135,6 +5136,7 @@ export default {
|
||||
'track.order.table.product': '销售产品',
|
||||
'track.order.table.channel': '服务渠道',
|
||||
'track.order.table.apiCode': 'API服务商',
|
||||
'track.order.table.countryCode': '国家',
|
||||
'track.order.table.destCountryCode': '国家',
|
||||
'track.order.table.province': '州省',
|
||||
'track.order.table.city': '城市',
|
||||
|
||||
@ -51,8 +51,7 @@ function isH5LikePath(path: string) {
|
||||
normalized === '/h5' ||
|
||||
normalized.startsWith('/h5/') ||
|
||||
normalized.startsWith('/wechat/') ||
|
||||
normalized === '/track/query' ||
|
||||
normalized.startsWith('/track/')
|
||||
normalized === '/track/query'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -50,7 +50,7 @@ import { computed } from 'vue';
|
||||
{
|
||||
totalCount: 0,
|
||||
currentPage: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
serialNoHeader: '',
|
||||
actionColumnHeader: '',
|
||||
customCellRenderer: undefined,
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
showPagination: true,
|
||||
totalCount: 0,
|
||||
currentPage: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
pageSizes: () => [10, 30, 50, 100],
|
||||
}
|
||||
);
|
||||
|
||||
@ -359,7 +359,7 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
|
||||
const totalCount = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
const tableLoading = ref(false);
|
||||
const leftLoading = ref(false);
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ type IssueTemplateRow = {
|
||||
|
||||
const tableData = ref<IssueTemplateRow[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const listLoading = ref(false)
|
||||
const organizationOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
@ -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 {
|
||||
// 固定列序:操作 + 序号 → 数据从 col 2 起
|
||||
// 固定列åº<EFBFBD>:æ“<EFBFBD>ä½?+ åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ä»?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>`
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
const tableData = ref<Record<string, unknown>[]>([]);
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
total: 0,
|
||||
showTotal: true,
|
||||
showPageSize: true,
|
||||
|
||||
@ -382,7 +382,7 @@
|
||||
const searchModel = reactive<any>({ keyword: '' });
|
||||
const totalCount = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
const tableLoading = ref(false);
|
||||
const leftLoading = ref(false);
|
||||
|
||||
|
||||
@ -445,7 +445,7 @@
|
||||
const searchModel = reactive<any>({ keyword: '' });
|
||||
const totalCount = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
const tableLoading = ref(false);
|
||||
const leftLoading = ref(false);
|
||||
|
||||
@ -480,7 +480,7 @@
|
||||
]);
|
||||
|
||||
function hotCellRenderer(_row: number, col: number, _value: any, rowData: any): string | null {
|
||||
// ChannelConfigHotTable:操作 + 序号 → 数据从 col 2 起
|
||||
// ChannelConfigHotTable:æ“<EFBFBD>ä½?+ åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ä»?col 2 èµ?
|
||||
const dataColIndex = col - 2;
|
||||
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null;
|
||||
const colKey = hotColumns.value[dataColIndex].key;
|
||||
|
||||
@ -206,7 +206,7 @@ const viewMode = ref<ViewMode>('detail')
|
||||
const dateRange = ref<any[]>([])
|
||||
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const feeItemDictRows = ref<DictDto[]>([])
|
||||
|
||||
const columnConfigState = ref<ColumnConfigState>(
|
||||
|
||||
@ -271,7 +271,7 @@ const categoryFilter = ref<number | undefined>()
|
||||
const statusFilter = ref<string | undefined>()
|
||||
const audienceFilter = ref<string | undefined>()
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const modalVisible = ref(false)
|
||||
const modalLoading = ref(false)
|
||||
|
||||
@ -172,7 +172,7 @@ const listLoading = ref(false)
|
||||
const orderNo = ref('')
|
||||
const statusFilter = ref<number | undefined>(undefined)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const adjustVisible = ref(false)
|
||||
const adjustLoading = ref(false)
|
||||
|
||||
@ -166,7 +166,7 @@ const customerCode = ref('')
|
||||
const statusFilter = ref<number | undefined>(1)
|
||||
const withdrawType = ref<number | undefined>(undefined)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const rejectVisible = ref(false)
|
||||
const rejectLoading = ref(false)
|
||||
@ -220,7 +220,7 @@ function statusTone(status: number): StatusTagTone {
|
||||
}
|
||||
|
||||
function customCellRenderer(_row: number, col: number, _value: unknown, rowData: WithdrawRow): string | null {
|
||||
// 固定列序:操作 + 序号 → 数据从 col 2 起
|
||||
// 固定列åº<EFBFBD>:æ“<EFBFBD>ä½?+ åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ä»?col 2 èµ?
|
||||
const dataColIndex = col - 2
|
||||
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
|
||||
const colKey = hotColumns.value[dataColIndex].key
|
||||
|
||||
@ -169,7 +169,7 @@ const listLoading = ref(false)
|
||||
const customerCode = ref('')
|
||||
const changeType = ref<number | undefined>(undefined)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const adjustVisible = ref(false)
|
||||
const adjustLoading = ref(false)
|
||||
|
||||
@ -158,7 +158,7 @@ const listLoading = ref(false)
|
||||
const keyword = ref('')
|
||||
const statusFilter = ref<number | undefined>(undefined)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const modalVisible = ref(false)
|
||||
const modalLoading = ref(false)
|
||||
|
||||
@ -329,7 +329,7 @@ const totalCount = ref(0)
|
||||
const listLoading = ref(false)
|
||||
const keyword = ref('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const drawerVisible = ref(false)
|
||||
const drawerLoading = ref(false)
|
||||
|
||||
@ -34,7 +34,7 @@ const listLoading = ref(false)
|
||||
const confirmLoading = ref(false)
|
||||
const queueList = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const selectedOrderId = ref<number | null>(null)
|
||||
const selectedQueueRows = ref<any[]>([])
|
||||
|
||||
@ -195,7 +195,7 @@ const submitting = ref(false)
|
||||
const lookingUp = ref(false)
|
||||
const listLoading = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const headerSearchParams = reactive<Record<string, string>>({})
|
||||
const NUMBER_FILTER_KEYS = ['waybillNumber'] as const
|
||||
let softPollTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
@ -179,7 +179,7 @@ const submitting = ref(false)
|
||||
const lookingUp = ref(false)
|
||||
const listLoading = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const headerSearchParams = reactive<Record<string, string>>({})
|
||||
const NUMBER_FILTER_KEYS = ['waybillNumber'] as const
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="pl-page">
|
||||
<div class="pl-col pl-col--main">
|
||||
<!-- 卡片1:扫码打印 -->
|
||||
<!-- å<EFBFBD>¡ç‰‡1:扫ç <EFBFBD>打å<EFBFBD>?-->
|
||||
<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>
|
||||
|
||||
<!-- 卡片2:核对信息 -->
|
||||
<!-- å<EFBFBD>¡ç‰‡2ï¼šæ ¸å¯¹ä¿¡æ<EFBFBD>?-->
|
||||
<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 />
|
||||
@ -307,7 +307,7 @@ const lookingUp = ref(false)
|
||||
const printing = ref(false)
|
||||
const listLoading = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const headerSearchParams = reactive<Record<string, string>>({})
|
||||
const NUMBER_FILTER_KEYS = ['waybillNumber', 'customerOrderNumber'] as const
|
||||
const lookup = ref<PrintLabelLookupResult | null>(null)
|
||||
|
||||
@ -31,7 +31,7 @@ const { tableContainerRef, tableHeight: handsontableHeight } = useTableContainer
|
||||
|
||||
const listLoading = ref(false)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const tableRows = ref<any[]>([])
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ const keyword = ref('')
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const selectedIds = ref<number[]>([])
|
||||
const statusCounts = ref({ total: 0, pending: 0, declared: 0 })
|
||||
@ -597,7 +597,11 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="selected-count">
|
||||
{{ t('order.myOrders.selectedCount', { count: selectedRowCount }) }}
|
||||
<i18n-t keypath="order.myOrders.selectedCount" tag="span">
|
||||
<template #count>
|
||||
<span class="count-number">{{ selectedRowCount }}</span>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<div class="pagination-controls">
|
||||
<PaginationPro
|
||||
|
||||
@ -120,7 +120,7 @@
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** 列表行与表格列对齐:接口用 serviceNo/clientNo/trackNo,表格用运单号/客户单号/跟踪号 */
|
||||
/** åˆ—è¡¨è¡Œä¸Žè¡¨æ ¼åˆ—å¯¹é½<EFBFBD>:接å<EFBFBD>£ç”?serviceNo/clientNo/trackNoï¼Œè¡¨æ ¼ç”¨è¿<C3A8>å<EFBFBD>•å<E280A2>?客户å<C2B7>•å<E280A2>·/跟踪å<C2AA>?*/
|
||||
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,8 +237,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 当前激活的标签页(我的订单=订单状态;代发订单=代发进度)
|
||||
const validStatusSet = new Set(ORDER_STATUS_TABS.map((meta) => meta.value));
|
||||
// 当å‰<EFBFBD>æ¿€æ´»çš„æ ‡ç¾é¡µï¼ˆæˆ‘的订å<EFBFBD>•=订å<EFBFBD>•状æ€<EFBFBD>;代å<EFBFBD>‘订å<EFBFBD>•=代å<EFBFBD>‘进度ï¼? const validStatusSet = new Set(ORDER_STATUS_TABS.map((meta) => meta.value));
|
||||
|
||||
function resolveActiveTabFromQuery(): string {
|
||||
if (isFulfillList.value) {
|
||||
@ -284,7 +283,7 @@
|
||||
return isFulfillList.value && keys.includes(activeTab.value);
|
||||
}
|
||||
|
||||
/** 各 Tab 下动作优先级(越靠前越优先外露,最多 4 个) */
|
||||
/** å<EFBFBD>?Tab 下动作优先级(越é<C5A0> å‰<C3A5>越优先外露,最å¤?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'],
|
||||
@ -293,7 +292,7 @@
|
||||
outboundDone: ['batchPrint', 'export'],
|
||||
};
|
||||
|
||||
/** 按当前状态收敛工具栏,避免客户在无关 Tab 看到一排无效按钮 */
|
||||
/** 按当å‰<EFBFBD>状æ€<EFBFBD>收敛工具æ <EFBFBD>,é<EFBFBD>¿å…<EFBFBD>å®¢æˆ·åœ¨æ— å…³ Tab çœ‹åˆ°ä¸€æŽ’æ— æ•ˆæŒ‰é’?*/
|
||||
const showPayAction = computed(() =>
|
||||
isFulfillList.value
|
||||
? isFulfillProgressTab('all', 'pendingBind')
|
||||
@ -369,7 +368,7 @@
|
||||
modifyOrder: 'order.myOrders.actions.modifyOrder',
|
||||
};
|
||||
|
||||
/** 各 Tab 下动作优先级(越靠前越优先外露,最多 4 个) */
|
||||
/** å<EFBFBD>?Tab 下动作优先级(越é<C5A0> å‰<C3A5>越优先外露,最å¤?4 个) */
|
||||
const TAB_TOOLBAR_ACTION_ORDER: Record<string, ToolbarActionKey[]> = {
|
||||
all: [
|
||||
'pay',
|
||||
@ -662,7 +661,7 @@
|
||||
tabCounts.value = next;
|
||||
handleCounts.value = handleCountsRaw ?? {};
|
||||
} catch {
|
||||
/* 角标失败不影响列表 */
|
||||
/* è§’æ ‡å¤±è´¥ä¸<EFBFBD>å½±å“<EFBFBD>列è¡?*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -672,15 +671,15 @@
|
||||
|
||||
// 分页相关数据
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
const totalCount = ref(0);
|
||||
|
||||
/** 列表筛选:单号关键词 + 创建时间(默认近 90 天) */
|
||||
/** 列表ç›é€‰ï¼šå<EFBFBD>•å<EFBFBD>·å…³é”®è¯?+ 创建时间(默认近 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>>({});
|
||||
@ -764,7 +763,7 @@
|
||||
|
||||
const timeWindowHelp = computed(() => {
|
||||
if (timeWindowRelaxed.value && !createTimeRange.value?.length) {
|
||||
return `已取消时间限制,全量查询可能较慢。建议按单号精确查询,或恢复近 ${ORDER_LIST_DEFAULT_DAYS} 天。`;
|
||||
return `å·²å<EFBFBD>–消时间é™<EFBFBD>制,全é‡<EFBFBD>查询å<EFBFBD>¯èƒ½è¾ƒæ…¢ã€‚建议按å<EFBFBD>•å<EFBFBD>·ç²¾ç¡®æŸ¥è¯¢ï¼Œæˆ–æ<EFBFBD>¢å¤<EFBFBD>è¿?${ORDER_LIST_DEFAULT_DAYS} 天。`;
|
||||
}
|
||||
return `默认仅展示近 ${ORDER_LIST_DEFAULT_DAYS} 天订单;按单号精确查询时可清空时间。`;
|
||||
});
|
||||
@ -795,10 +794,10 @@
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '清空时间范围?',
|
||||
title: '清空时间范围�,
|
||||
content: `未指定单号时查询全部历史可能较慢。建议保留近 ${ORDER_LIST_DEFAULT_DAYS} 天,或先输入单号再清空时间。`,
|
||||
okText: '仍要清空',
|
||||
cancelText: '保留近90天',
|
||||
cancelText: 'ä¿<EFBFBD>ç•™è¿?0å¤?,
|
||||
onOk: () => {
|
||||
timeWindowRelaxed.value = true;
|
||||
createTimeRange.value = undefined;
|
||||
@ -838,9 +837,9 @@
|
||||
}
|
||||
|
||||
const selectedRowCount = ref(0);
|
||||
/** 表格选中行(用于批量操作) */
|
||||
/** è¡¨æ ¼é€‰ä¸è¡Œï¼ˆç”¨äºŽæ‰¹é‡<EFBFBD>æ“<EFBFBD>作ï¼?*/
|
||||
const selectedOrderRows = ref<any[]>([]);
|
||||
/** 当前正在执行的 toolbar 操作(仅对应按钮显示 loading,避免全部按钮联动) */
|
||||
/** 当å‰<EFBFBD>æ£åœ¨æ‰§è¡Œçš?toolbar æ“<C3A6>作(仅对应按钮显示 loading,é<C592>¿å…<C3A5>全部按钮è<C2AE>”动) */
|
||||
const actionLoadingKey = ref<string | null>(null);
|
||||
|
||||
function isActionLoading(key: string) {
|
||||
@ -1012,8 +1011,7 @@
|
||||
// 当前页数据(HandsontablePro 用)
|
||||
const processedTableData = computed(() => orderList.value);
|
||||
|
||||
// 标签页切换
|
||||
const handleTabChange = (key: string) => {
|
||||
// æ ‡ç¾é¡µåˆ‡æ<EFBFBD>? const handleTabChange = (key: string) => {
|
||||
if (activeTab.value === key) {
|
||||
return;
|
||||
}
|
||||
@ -1038,8 +1036,7 @@
|
||||
loadOrderList();
|
||||
};
|
||||
|
||||
// 操作按钮处理(依赖表格多选行;导出按当前 tab 条件,无需选行)
|
||||
function openPayConfirm(rows: any[]) {
|
||||
// æ“<EFBFBD>作按钮处ç<EFBFBD>†ï¼ˆä¾<EFBFBD>èµ–è¡¨æ ¼å¤šé€‰è¡Œï¼›å¯¼å‡ºæŒ‰å½“å‰<EFBFBD> tab æ<EFBFBD>¡ä»¶ï¼Œæ— 需选行ï¼? function openPayConfirm(rows: any[]) {
|
||||
payConfirmOrders.value = rows
|
||||
.filter((row) => getOrderRowId(row) != null)
|
||||
.map((row) => ({
|
||||
@ -1105,12 +1102,12 @@
|
||||
action: 'pre-declare',
|
||||
rows,
|
||||
loadingKey: 'predeclare',
|
||||
successMessage: '预报已提交',
|
||||
successMessage: '预报已æ<EFBFBD><EFBFBD>äº?,
|
||||
syncRunner: async () => {
|
||||
for (const row of rows) {
|
||||
await postOrderPreDeclare({ id: getOrderRowId(row)! });
|
||||
}
|
||||
Message.success('预报已提交');
|
||||
Message.success('预报已æ<EFBFBD><EFBFBD>äº?);
|
||||
await refreshListAndTabCounts();
|
||||
},
|
||||
});
|
||||
@ -1215,7 +1212,7 @@
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定删除选中的 ${rows.length} 条订单吗?`,
|
||||
content: `ç¡®å®šåˆ é™¤é€‰ä¸çš?${rows.length} æ<>¡è®¢å<C2A2>•å<E280A2>—?`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await withOrderActionLoading('delete', async () => {
|
||||
@ -1223,12 +1220,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();
|
||||
},
|
||||
});
|
||||
@ -1252,7 +1249,7 @@
|
||||
function isUsablePrintUrl(raw: unknown) {
|
||||
const value = String(raw ?? '').trim();
|
||||
if (!value) return false;
|
||||
if (value.includes('获取中')) return false;
|
||||
if (value.includes('获å<EFBFBD>–ä¸?)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1264,7 +1261,7 @@
|
||||
return;
|
||||
}
|
||||
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
||||
Message.warning('请先在顶部打开并连接打印开关');
|
||||
Message.warning('请先在顶部打开并连接打å<EFBFBD>°å¼€å…?);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -1291,7 +1288,7 @@
|
||||
const rows = requireSelectedRows();
|
||||
if (!rows.length) return;
|
||||
if (!printConnectionState.enabled || !printConnectionState.connected) {
|
||||
Message.warning('请先在顶部打开并连接打印开关');
|
||||
Message.warning('请先在顶部打开并连接打å<EFBFBD>°å¼€å…?);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -1378,12 +1375,12 @@
|
||||
sortOrder,
|
||||
});
|
||||
if (downloadUrl) window.open(downloadUrl, '_blank');
|
||||
Message.success(downloadUrl ? '导出链接已打开' : '导出已提交');
|
||||
Message.success(downloadUrl ? '导出链接已打开' : '导出已æ<EFBFBD><EFBFBD>äº?);
|
||||
});
|
||||
} catch (e: any) {
|
||||
if (e?.message !== 'busy') {
|
||||
Message.error(
|
||||
e?.message || `导出失败(单次最多 ${ORDER_EXPORT_MAX_ROWS.toLocaleString()} 条)`,
|
||||
e?.message || `导出失败(å<EFBFBD>•次最å¤?${ORDER_EXPORT_MAX_ROWS.toLocaleString()} æ<EFBFBD>¡ï¼‰`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1539,7 +1536,7 @@
|
||||
loadOrderList();
|
||||
};
|
||||
|
||||
// HandsontablePro 列定义(支持命名方案配置,最多 30 列)
|
||||
// HandsontablePro 列定义(支æŒ<EFBFBD>命å<EFBFBD><EFBFBD>方案é…<EFBFBD>置,最å¤?30 列)
|
||||
const initialIsFulfillList = route.meta.listMode === 'fulfill';
|
||||
const columnConfigState = ref(
|
||||
loadColumnConfigState(
|
||||
@ -1601,8 +1598,8 @@
|
||||
filterType: 'enum' as const,
|
||||
headerMode: 'icon' as const,
|
||||
filterOptions: [
|
||||
{ label: '已支付', value: '1' },
|
||||
{ label: '未支付', value: '0' },
|
||||
{ label: '已支�, value: '1' },
|
||||
{ label: '未支�, value: '0' },
|
||||
],
|
||||
};
|
||||
}
|
||||
@ -1775,8 +1772,7 @@
|
||||
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>`;
|
||||
@ -1862,7 +1858,7 @@
|
||||
|
||||
<template>
|
||||
<div class="my-orders-container">
|
||||
<!-- 单一工作面:Tabs/筛选 + 子状态 + 工具栏 + 表格 -->
|
||||
<!-- å<EFBFBD>•一工作é<EFBFBD>¢ï¼šTabs/ç›é€?+ å<EFBFBD>状æ€?+ 工具æ ?+ è¡¨æ ¼ -->
|
||||
<div class="table-section">
|
||||
<div class="tabs-actions-section">
|
||||
<div class="tabs-slot">
|
||||
@ -1883,7 +1879,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 />
|
||||
@ -1914,8 +1910,7 @@
|
||||
class="list-date-restore"
|
||||
@click="restoreDefaultTimeWindow(); handleSearch()"
|
||||
>
|
||||
恢复近{{ ORDER_LIST_DEFAULT_DAYS }}天
|
||||
</a-button>
|
||||
æ<EFBFBD>¢å¤<EFBFBD>è¿‘{{ ORDER_LIST_DEFAULT_DAYS }}å¤? </a-button>
|
||||
<a-button
|
||||
v-if="dateFilterExpanded"
|
||||
type="text"
|
||||
@ -1960,7 +1955,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格操作按钮和分页控制 -->
|
||||
<!-- è¡¨æ ¼æ“<EFBFBD>作按钮和分页控åˆ?-->
|
||||
<div class="table-toolbar">
|
||||
<div class="actions-right">
|
||||
<a-button
|
||||
@ -1994,7 +1989,11 @@
|
||||
|
||||
<div class="toolbar-right">
|
||||
<span class="selected-count">
|
||||
{{ t('order.myOrders.selectedCount', { count: selectedRowCount }) }}
|
||||
<i18n-t keypath="order.myOrders.selectedCount" tag="span">
|
||||
<template #count>
|
||||
<span class="count-number">{{ selectedRowCount }}</span>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<div class="column-config-controls">
|
||||
<a-select
|
||||
@ -2484,7 +2483,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;
|
||||
}
|
||||
@ -2591,8 +2590,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式设计
|
||||
@media (max-width: 768px) {
|
||||
// å“<EFBFBD>应å¼<EFBFBD>设è®? @media (max-width: 768px) {
|
||||
.actions-right {
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
|
||||
@ -70,7 +70,7 @@ const STATUS_TAB_ALL = 'all'
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const statusCounts = ref<Record<string, number>>({
|
||||
[STATUS_TAB_ALL]: 0,
|
||||
@ -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')
|
||||
}
|
||||
@ -1292,7 +1292,11 @@ onMounted(async () => {
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="selected-count">
|
||||
{{ t('order.pickupOrder.selectedCount', { count: selectedRowCount }) }}
|
||||
<i18n-t keypath="order.pickupOrder.selectedCount" tag="span">
|
||||
<template #count>
|
||||
<span class="count-number">{{ selectedRowCount }}</span>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<PaginationPro
|
||||
:total-count="totalCount"
|
||||
@ -1344,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>
|
||||
|
||||
@ -32,7 +32,7 @@ const keyword = ref('')
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
|
||||
const platformDefs = [
|
||||
@ -53,12 +53,12 @@ const slidingPlatformTabs = computed(() =>
|
||||
|
||||
const lazadaSiteOptions = computed(() => [
|
||||
{ value: 'cb', label: t('order.platformStore.site.crossBorder') },
|
||||
{ value: 'sg', label: '新加坡' },
|
||||
{ value: 'sg', label: 'æ–°åŠ å<EFBFBD>? },
|
||||
{ value: 'my', label: '马来西亚' },
|
||||
{ value: 'th', label: '泰国' },
|
||||
{ value: 'vn', label: '越南' },
|
||||
{ value: 'ph', label: '菲律宾' },
|
||||
{ value: 'id', label: '印度尼西亚' },
|
||||
{ value: 'ph', label: 'è<EFBFBD>²å¾‹å®? },
|
||||
{ value: 'id', label: 'å<EFBFBD>°åº¦å°¼è¥¿äº? },
|
||||
])
|
||||
|
||||
const [registerFormModal, { openModal, closeModal }] = useModal() as any
|
||||
|
||||
@ -249,7 +249,7 @@ async function loadCandidates() {
|
||||
customerId: props.customerId,
|
||||
keyword: keyword.value || undefined,
|
||||
page: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
}
|
||||
if (leftTab.value === 'inventory') {
|
||||
const res = await postOrderStockBindInventoryCandidates(body)
|
||||
@ -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;
|
||||
|
||||
@ -50,7 +50,7 @@ const {
|
||||
ensureMutationCustomerId,
|
||||
} = useWarehouseAdminCustomer()
|
||||
|
||||
/** Tab active-key;全部用 'all',请求时再转成不传 fulfillStatus / platformCode */
|
||||
/** Tab active-key;全部用 'all',请求时å†<C3A5>转æˆ<C3A6>ä¸<C3A4>ä¼?fulfillStatus / platformCode */
|
||||
const STATUS_TAB_ALL = 'all'
|
||||
const PLATFORM_TAB_ALL = 'all'
|
||||
const ALLOWED_FULFILL_STATUS = new Set(['pending', 'toShip', 'shipped', 'canceled', 'completed'])
|
||||
@ -87,7 +87,7 @@ const selectedRows = ref<any[]>([])
|
||||
const selectedRowCount = computed(() => selectedRows.value.length)
|
||||
const hasSelectedRows = computed(() => selectedRowCount.value > 0)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const pullVisible = ref(false)
|
||||
const actionLoading = ref(false)
|
||||
@ -758,7 +758,11 @@ watch(
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<span class="selected-count">
|
||||
{{ t('order.myOrders.selectedCount', { count: selectedRowCount }) }}
|
||||
<i18n-t keypath="order.myOrders.selectedCount" tag="span">
|
||||
<template #count>
|
||||
<span class="count-number">{{ selectedRowCount }}</span>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<PaginationPro
|
||||
:total-count="totalCount"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, nextTick, watch, reactive } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { IconCalendar, IconDown, IconQuestionCircleFill } from '@arco-design/web-vue/es/icon';
|
||||
import { IconCalendar, IconDown, IconQuestionCircleFill, IconSettings } from '@arco-design/web-vue/es/icon';
|
||||
import { Message, Modal } from '@arco-design/web-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import HandsontablePro from '@/components/HandsontablePro/index.vue';
|
||||
@ -14,11 +14,13 @@
|
||||
withSortableColumns,
|
||||
} from '@/utils/headerFilterAdapter';
|
||||
import {
|
||||
MAX_HEADER_BATCH_VALUES,
|
||||
ORDER_EXPORT_MAX_ROWS,
|
||||
ORDER_LIST_DEFAULT_DAYS,
|
||||
buildDefaultCreateTimeRange,
|
||||
formatCreateTimeRangeParam,
|
||||
isDefaultCreateTimeRange,
|
||||
normalizeBatchInputText,
|
||||
} from '@/utils/headerFilterLimits';
|
||||
import {
|
||||
postOrderList,
|
||||
@ -450,7 +452,7 @@
|
||||
|
||||
// 分页相关数据
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
const totalCount = ref(0);
|
||||
|
||||
/** 列表筛选:单号关键词 + 创建时间(默认近 90 天) */
|
||||
@ -532,12 +534,30 @@
|
||||
createTimeEnd = e.format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
}
|
||||
const keyword = (adapted.keyword || listKeyword.value.trim() || '').trim();
|
||||
|
||||
const toolbarBatch = normalizeBatchInputText(listKeyword.value);
|
||||
let filters = adapted.filters.length ? [...adapted.filters] : [];
|
||||
let keyword = '';
|
||||
|
||||
// 工具栏批量(多个单号)→ 运单号 IN;单值仍走 keyword 模糊(运单号/客户单号/跟踪号)
|
||||
if (toolbarBatch.keptCount > 1) {
|
||||
filters = filters.filter((f) => f.field !== 'serviceNo');
|
||||
const values = toolbarBatch.value.split(',').map((v) => v.trim()).filter(Boolean);
|
||||
filters.push({
|
||||
field: 'serviceNo',
|
||||
op: 'in',
|
||||
value: values.join(','),
|
||||
values,
|
||||
});
|
||||
} else {
|
||||
keyword = (adapted.keyword || toolbarBatch.value || listKeyword.value.trim() || '').trim();
|
||||
}
|
||||
|
||||
return {
|
||||
keyword,
|
||||
createTimeStart,
|
||||
createTimeEnd,
|
||||
filters: adapted.filters.length ? adapted.filters : undefined,
|
||||
filters: filters.length ? filters : undefined,
|
||||
sortField: mapSortFieldToApi(listSortField.value),
|
||||
sortOrder: listSortOrder.value || undefined,
|
||||
};
|
||||
@ -914,20 +934,64 @@
|
||||
loadTabCounts();
|
||||
};
|
||||
|
||||
const handleResetSearch = () => {
|
||||
listKeyword.value = '';
|
||||
restoreDefaultTimeWindow();
|
||||
Object.keys(headerSearchParams).forEach((k) => {
|
||||
headerSearchParams[k] = '';
|
||||
});
|
||||
listSortField.value = null;
|
||||
listSortOrder.value = null;
|
||||
currentPage.value = 1;
|
||||
selectedOrderRows.value = [];
|
||||
selectedRowCount.value = 0;
|
||||
loadOrderList();
|
||||
loadTabCounts();
|
||||
};
|
||||
const keywordBatchVisible = ref(false);
|
||||
const keywordBatchInput = ref('');
|
||||
|
||||
const keywordBatchCount = computed(() =>
|
||||
keywordBatchInput.value
|
||||
.split(/[\n\r]+/)
|
||||
.flatMap((line) => line.split(/[,,\t;;]/))
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean).length,
|
||||
);
|
||||
|
||||
function openKeywordBatchModal() {
|
||||
const current = listKeyword.value.trim();
|
||||
keywordBatchInput.value = current
|
||||
? current
|
||||
.split(/[,,\n\r\t;;]+/)
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean)
|
||||
.join('\n')
|
||||
: '';
|
||||
keywordBatchVisible.value = true;
|
||||
}
|
||||
|
||||
function saveKeywordBatch() {
|
||||
const { value, truncated, originalCount, keptCount } = normalizeBatchInputText(
|
||||
keywordBatchInput.value,
|
||||
);
|
||||
listKeyword.value = value;
|
||||
keywordBatchVisible.value = false;
|
||||
handleSearch();
|
||||
if (truncated) {
|
||||
Message.warning(
|
||||
`最多 ${MAX_HEADER_BATCH_VALUES} 个,已保留前 ${keptCount} 个(原 ${originalCount} 个)`,
|
||||
);
|
||||
} else if (keptCount > 0) {
|
||||
Message.success(
|
||||
keptCount > 1
|
||||
? `已批量输入 ${keptCount} 个运单号`
|
||||
: `已输入 ${keptCount} 个值`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeywordBatchPaste(event: ClipboardEvent) {
|
||||
const pastedText = event.clipboardData?.getData('text') || '';
|
||||
if (!pastedText.includes(',') && !pastedText.includes(',')) return;
|
||||
event.preventDefault();
|
||||
const lines = pastedText
|
||||
.split(/[,,\n\r]+/)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
const textarea = event.target as HTMLTextAreaElement;
|
||||
const start = textarea.selectionStart || 0;
|
||||
const end = textarea.selectionEnd || 0;
|
||||
const text = keywordBatchInput.value;
|
||||
keywordBatchInput.value =
|
||||
text.substring(0, start) + lines.join('\n') + text.substring(end);
|
||||
}
|
||||
|
||||
// 当前页数据(HandsontablePro 用)
|
||||
const processedTableData = computed(() => orderList.value);
|
||||
@ -1423,10 +1487,6 @@
|
||||
);
|
||||
const columnConfigModalRef = ref<InstanceType<typeof ColumnConfigModal>>();
|
||||
|
||||
const columnSchemeOptions = computed(() =>
|
||||
columnConfigState.value.schemes.map((s) => ({ label: s.name, value: s.id })),
|
||||
);
|
||||
|
||||
const activeColumnKeys = computed(() => getActiveScheme(columnConfigState.value).keys);
|
||||
|
||||
const hotColumns = computed(() => {
|
||||
@ -1487,15 +1547,6 @@
|
||||
columnCatalog.value = mergeDiscoveredColumns(getWaybillBaseCatalog(), rows);
|
||||
}
|
||||
|
||||
function handleColumnSchemeChange(id: string) {
|
||||
if (!columnConfigState.value.schemes.some((s) => s.id === id)) return;
|
||||
columnConfigState.value = {
|
||||
...columnConfigState.value,
|
||||
activeId: id,
|
||||
};
|
||||
saveColumnConfigState(WAYBILL_MANAGE_COLUMN_STORAGE_KEY, columnConfigState.value);
|
||||
}
|
||||
|
||||
function openColumnConfig() {
|
||||
columnConfigModalRef.value?.open();
|
||||
}
|
||||
@ -1745,7 +1796,9 @@
|
||||
v-model="listKeyword"
|
||||
allow-clear
|
||||
:placeholder="t('order.myOrders.search.keywordPlaceholder')"
|
||||
title="双击支持批量输入"
|
||||
style="width: 240px"
|
||||
@dblclick="openKeywordBatchModal"
|
||||
@press-enter="handleSearch"
|
||||
/>
|
||||
<span class="list-date-filter">
|
||||
@ -1795,9 +1848,6 @@
|
||||
<a-button class="app-btn-search" @click="handleSearch">
|
||||
{{ t('order.myOrders.search.actions.search') }}
|
||||
</a-button>
|
||||
<a-button @click="handleResetSearch" class="app-btn-reset">
|
||||
{{ t('order.myOrders.search.actions.reset') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1917,20 +1967,12 @@
|
||||
|
||||
<div class="toolbar-right">
|
||||
<span class="selected-count">
|
||||
{{ t('order.myOrders.selectedCount', { count: selectedRowCount }) }}
|
||||
<i18n-t keypath="order.myOrders.selectedCount" tag="span">
|
||||
<template #count>
|
||||
<span class="count-number">{{ selectedRowCount }}</span>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<div class="column-config-controls">
|
||||
<a-select
|
||||
:model-value="columnConfigState.activeId"
|
||||
:options="columnSchemeOptions"
|
||||
:placeholder="t('order.myOrders.columnConfig.scheme')"
|
||||
style="width: 140px"
|
||||
@change="handleColumnSchemeChange"
|
||||
/>
|
||||
<a-button @click="openColumnConfig" class="app-btn-toolbar">
|
||||
{{ t('order.myOrders.columnConfig.open') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="pagination-controls">
|
||||
<PaginationPro
|
||||
:total-count="totalCount"
|
||||
@ -1939,6 +1981,16 @@
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
/>
|
||||
<a-tooltip content="表头配置">
|
||||
<a-button
|
||||
class="app-btn-toolbar column-config-icon-btn"
|
||||
@click="openColumnConfig"
|
||||
>
|
||||
<template #icon>
|
||||
<IconSettings />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1994,6 +2046,33 @@
|
||||
<OrderPrintModal v-model:visible="singlePrintVisible" mode="single" :items="singlePrintItems" />
|
||||
<OrderPrintModal v-model:visible="batchPrintVisible" mode="batch" :items="batchPrintItems" />
|
||||
|
||||
<a-modal
|
||||
v-model:visible="keywordBatchVisible"
|
||||
title="批量输入单号"
|
||||
:width="600"
|
||||
unmount-on-close
|
||||
@ok="saveKeywordBatch"
|
||||
>
|
||||
<div class="keyword-batch-content">
|
||||
<p class="keyword-batch-tips">
|
||||
每行一个;支持粘贴逗号分隔。多个单号按运单号精确匹配(最多
|
||||
{{ MAX_HEADER_BATCH_VALUES }} 个);单个仍支持运单号 / 客户单号 / 跟踪号模糊搜索。
|
||||
</p>
|
||||
<a-textarea
|
||||
v-model="keywordBatchInput"
|
||||
placeholder="请输入,每行一个,或粘贴逗号分隔的数据"
|
||||
:auto-size="{ minRows: 10, maxRows: 20 }"
|
||||
@paste="handleKeywordBatchPaste"
|
||||
/>
|
||||
<div v-if="keywordBatchCount" class="keyword-batch-count">
|
||||
已输入 {{ keywordBatchCount }} 个
|
||||
<span v-if="keywordBatchCount > MAX_HEADER_BATCH_VALUES" class="is-warn">
|
||||
(超过上限将截断)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="trackUpsertModalVisible"
|
||||
title="轨迹同步结果"
|
||||
@ -2343,15 +2422,15 @@
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.column-config-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.column-config-icon-btn {
|
||||
width: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2550,4 +2629,23 @@
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.keyword-batch-content {
|
||||
.keyword-batch-tips {
|
||||
margin: 0 0 12px;
|
||||
color: var(--color-text-3);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.keyword-batch-count {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--color-text-3);
|
||||
|
||||
.is-warn {
|
||||
color: rgb(var(--warning-6));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -249,7 +249,7 @@ const dataSource = ref<NoticeDto[]>([])
|
||||
const activeTab = ref<'notice' | 'message'>('notice')
|
||||
const keyword = ref('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
|
||||
const modalVisible = ref(false)
|
||||
@ -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[]) {
|
||||
|
||||
@ -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="操作人 mapId" data-index="operatorMapId" :width="110" />
|
||||
<a-table-column title="æ“<EFBFBD>作äº?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 后端暂不自动写入访问日志;本页为预留查询界面,待内审需求启用后再对接。
|
||||
P1 å<EFBFBD>Žç«¯æš‚ä¸<EFBFBD>自动写入访问日志;本页为预留查询界é<EFBFBD>¢ï¼Œå¾…内审需求å<EFBFBD>¯ç”¨å<EFBFBD>Žå†<EFBFBD>对接ã€?
|
||||
</a-alert>
|
||||
<div class="table-toolbar">
|
||||
<a-space wrap>
|
||||
@ -94,7 +94,7 @@ const changeQuery = reactive({
|
||||
targetId: '',
|
||||
timeRange: [] as string[],
|
||||
})
|
||||
const changePagination = reactive({ current: 1, pageSize: 30, total: 0 })
|
||||
const changePagination = reactive({ current: 1, pageSize: 50, total: 0 })
|
||||
|
||||
const accessLoading = ref(false)
|
||||
const accessLogs = ref<AccessLogItem[]>([])
|
||||
@ -103,7 +103,7 @@ const accessQuery = reactive({
|
||||
dataResourceCode: '',
|
||||
timeRange: [] as string[],
|
||||
})
|
||||
const accessPagination = reactive({ current: 1, pageSize: 30, total: 0 })
|
||||
const accessPagination = reactive({ current: 1, pageSize: 50, total: 0 })
|
||||
|
||||
function buildTimeParams(range: string[]) {
|
||||
if (!range?.length || range.length < 2) return {}
|
||||
|
||||
@ -242,7 +242,7 @@ const query = reactive({
|
||||
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
@ -345,7 +345,7 @@ function actionRenderer(_row: DictDto, _rowIndex: number): string {
|
||||
])
|
||||
}
|
||||
|
||||
/** 固定列序:操作 + 序号 → 数据从 col 2 起 */
|
||||
/** 固定列åº<EFBFBD>:æ“<EFBFBD>ä½?+ åº<C3A5>å<EFBFBD>· â†?æ•°æ<C2B0>®ä»?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() {
|
||||
|
||||
@ -287,7 +287,7 @@ const selectedOrganization = ref<OrganizationTreeDto | null>(null)
|
||||
const userSearchKeyword = ref('')
|
||||
const userLoading = ref(false)
|
||||
const userDataSource = ref<UserDto[]>([])
|
||||
const userPagination = reactive({ current: 1, pageSize: 30, total: 0 })
|
||||
const userPagination = reactive({ current: 1, pageSize: 50, total: 0 })
|
||||
|
||||
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
||||
const handsontableHeight = computed(() => Math.max(tableHeight.value || 0, 400))
|
||||
@ -432,7 +432,7 @@ function handleOrgSearch() {
|
||||
}
|
||||
|
||||
function customCellRenderer(_row: number, col: number, _value: any, rowData: UserDto): string | null {
|
||||
// HandsontablePro 列序:操作 → 序号 → 数据;showActionColumn 时 dataColStart=2
|
||||
// HandsontablePro 列åº<EFBFBD>:æ“<EFBFBD>ä½?â†?åº<EFBFBD>å<EFBFBD>· â†?æ•°æ<EFBFBD>®ï¼›showActionColumn æ—?dataColStart=2
|
||||
const dataColIndex = col - 2
|
||||
if (dataColIndex < 0 || dataColIndex >= hotColumns.value.length) return null
|
||||
const key = hotColumns.value[dataColIndex].key
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
|
||||
/** 搜索条件 */
|
||||
const searchModel = reactive<any>({});
|
||||
/** 搜索关键<EFBFBD>?*/
|
||||
/** æ<EFBFBD>œç´¢å…³é”®ï¿?*/
|
||||
const searchKeyword = ref('');
|
||||
|
||||
const pullListData = ref<any[]>([]);
|
||||
@ -123,8 +123,8 @@
|
||||
children: list.map((item: any) => {
|
||||
const time = item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD HH:mm') : '';
|
||||
const app = item.appName || (item.appKey != null ? (appMap.value?.[item.appKey] ?? '') : '') || item.appKey || '-';
|
||||
const appStr = app ? `【${app}】` : '';
|
||||
const customerStr = item.customer ? `【${item.customer}】` : '';
|
||||
const appStr = app ? `�{app}】` : '';
|
||||
const customerStr = item.customer ? `�{item.customer}】` : '';
|
||||
const codeLine = [time, appStr, customerStr].filter(Boolean).join(' ');
|
||||
return {
|
||||
key: `item-${item.id}`,
|
||||
@ -167,7 +167,7 @@
|
||||
const activeTab = ref<'detail' | 'priority'>('detail');
|
||||
const slidingTabItems = computed(() => [
|
||||
{ key: 'detail', label: '明细' },
|
||||
{ key: 'priority', label: '优先级' },
|
||||
{ key: 'priority', label: '优先� },
|
||||
]);
|
||||
const priorityModalRef = ref();
|
||||
const priorityModalParams = reactive<any>({
|
||||
@ -176,15 +176,15 @@
|
||||
trackPullConfigId: undefined,
|
||||
});
|
||||
|
||||
// 明细表分页
|
||||
// 明细表分�
|
||||
const totalCount = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
|
||||
// 优先级 tab 分页
|
||||
// 优先�tab 分页
|
||||
const priorityTotalCount = ref(0);
|
||||
const priorityCurrentPage = ref(1);
|
||||
const priorityPageSize = ref(30);
|
||||
const prioritypageSize = ref(50);
|
||||
|
||||
const detailColumns = computed(() => [
|
||||
{ key: 'standardTrack', label: t('track.pull.table.standardTrack'), width: 280 },
|
||||
@ -196,9 +196,9 @@
|
||||
{ key: 'customer', label: '客户', width: 140 },
|
||||
{ key: 'orderNoTypeText', label: '拉取单号类型', width: 140 },
|
||||
{ key: 'pullTimesPerDay', label: '每天拉取次数', width: 120 },
|
||||
{ key: 'priority', label: '优先级', width: 100 },
|
||||
{ key: 'priority', label: '优先�, width: 100 },
|
||||
{ key: 'updateTime', label: '修改时间', width: 160 },
|
||||
{ key: 'updateUser', label: '修改人', width: 120 },
|
||||
{ key: 'updateUser', label: '修改�, width: 120 },
|
||||
]);
|
||||
|
||||
function formatOrderNoType(cellValue: any) {
|
||||
@ -293,7 +293,7 @@
|
||||
label: '每天拉取次数',
|
||||
comp: 'AInputNumber',
|
||||
compProps: {
|
||||
placeholder: '请输入次数',
|
||||
placeholder: '请输入次�,
|
||||
min: 0,
|
||||
max: 12,
|
||||
precision: 0,
|
||||
@ -303,7 +303,7 @@
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入每天拉取次数',
|
||||
message: '请输入æ¯<EFBFBD>天拉å<EFBFBD>–次æ•?,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
@ -315,7 +315,7 @@
|
||||
},
|
||||
{
|
||||
field: 'priority',
|
||||
label: '优先级',
|
||||
label: '优先�,
|
||||
comp: 'AInputNumber',
|
||||
compProps: {
|
||||
placeholder: '请输入优先级',
|
||||
@ -370,7 +370,7 @@
|
||||
span: 8,
|
||||
},
|
||||
{
|
||||
label: '服务商',
|
||||
label: 'æœ<EFBFBD>务å•?,
|
||||
field: 'serviceProviderCode',
|
||||
comp: 'SelectPro',
|
||||
compProps: (): any => {
|
||||
@ -379,7 +379,7 @@
|
||||
clearable: true,
|
||||
labelKeys: ['name'],
|
||||
titleStrs: ['name'],
|
||||
placeholder: '请选择服务商',
|
||||
placeholder: '请选择æœ<EFBFBD>务å•?,
|
||||
style: { width: '100%', height: '40px' },
|
||||
idKey: 'code',
|
||||
isPage: false,
|
||||
@ -394,17 +394,17 @@
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择服务商',
|
||||
message: '请选择æœ<EFBFBD>务å•?,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '开始轨迹',
|
||||
label: '开始轨�,
|
||||
field: 'startTrackCode',
|
||||
comp: 'SelectPro',
|
||||
compProps: (): any => {
|
||||
return {
|
||||
placeholder: '请选择开始轨迹',
|
||||
placeholder: '请选择开始轨�,
|
||||
isPage: true,
|
||||
clearable: true,
|
||||
immediate: true,
|
||||
@ -420,11 +420,11 @@
|
||||
span: 8,
|
||||
},
|
||||
{
|
||||
label: '开始睡眠多少小时',
|
||||
label: '开始ç<EFBFBD>¡çœ 多少å°<EFBFBD>æ—?,
|
||||
field: 'silenceBeforeStart',
|
||||
comp: 'AInputNumber',
|
||||
compProps: {
|
||||
placeholder: '请输入1-48的整数',
|
||||
placeholder: '请输�-48的整�,
|
||||
style: { width: '100%', height: '40px' },
|
||||
min: 1,
|
||||
max: 14,
|
||||
@ -437,7 +437,7 @@
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 48,
|
||||
message: '请输入1-48之间的整数',
|
||||
message: '请输�-48之间的整�,
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
@ -464,11 +464,11 @@
|
||||
span: 8,
|
||||
},
|
||||
{
|
||||
label: '结束继续拉取多久/天',
|
||||
label: '结æ<EFBFBD>Ÿç»§ç»æ‹‰å<EFBFBD>–多久/å¤?,
|
||||
field: 'silenceAfterEnd',
|
||||
comp: 'AInputNumber',
|
||||
compProps: {
|
||||
placeholder: '请输入1-14的整数',
|
||||
placeholder: '请输�-14的整�,
|
||||
style: { width: '100%', height: '40px' },
|
||||
min: 1,
|
||||
max: 14,
|
||||
@ -481,7 +481,7 @@
|
||||
type: 'number',
|
||||
min: 1,
|
||||
max: 14,
|
||||
message: '请输入1-14之间的整数',
|
||||
message: '请输�-14之间的整�,
|
||||
trigger: 'change',
|
||||
},
|
||||
],
|
||||
@ -553,7 +553,7 @@
|
||||
};
|
||||
const data: any[] = [
|
||||
{
|
||||
label: '拉取轨迹关键字',
|
||||
label: '拉å<EFBFBD>–轨迹关键å?,
|
||||
field: 'trackKeywords',
|
||||
comp: 'TagsAdd',
|
||||
compProps: ({ formModel }: { formModel: any }) => {
|
||||
@ -565,7 +565,7 @@
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: '拉取轨迹关键字(过滤)',
|
||||
label: '拉å<EFBFBD>–轨迹关键å—(过滤ï¼?,
|
||||
field: 'trackKeywordReverses',
|
||||
comp: 'TagsAdd',
|
||||
compProps: ({ formModel }: { formModel: any }) => {
|
||||
@ -607,7 +607,7 @@
|
||||
// 单击行获取行数据
|
||||
function cellClick(par: any) {
|
||||
currentId.value = par;
|
||||
// 点击行数据处<EFBFBD>?
|
||||
// 点击行数æ<EFBFBD>®å¤„ï¿?
|
||||
}
|
||||
|
||||
// 点击新增 编辑打开左侧弹窗
|
||||
@ -714,10 +714,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 删除优先级数<EFBFBD>?
|
||||
// åˆ é™¤ä¼˜å…ˆçº§æ•°ï¿?
|
||||
function handleDelPriorityData(row: any) {
|
||||
if (!row.customer) {
|
||||
$baseMessage('客户字段为必填项,无法删除', 'warning');
|
||||
$baseMessage('å®¢æˆ·å—æ®µä¸ºå¿…å¡«é¡¹ï¼Œæ— æ³•åˆ é™?, 'warning');
|
||||
return;
|
||||
}
|
||||
$baseConfirm({
|
||||
@ -768,9 +768,9 @@
|
||||
priorityTotalCount.value = 0;
|
||||
}
|
||||
|
||||
// 提交前数据处<EFBFBD>?
|
||||
// æ<EFBFBD><EFBFBD>交å‰<EFBFBD>æ•°æ<EFBFBD>®å¤„ï¿?
|
||||
function beforeSubmitLeft(formData: any) {
|
||||
// 返参配置字段已废弃,提交时确保不带这些字段(兼容旧数据/缓存)
|
||||
// è¿”å<EFBFBD>‚é…<EFBFBD>ç½®å—æ®µå·²åºŸå¼ƒï¼Œæ<EFBFBD><EFBFBD>交时确ä¿<EFBFBD>ä¸<EFBFBD>å¸¦è¿™äº›å—æ®µï¼ˆå…¼å®¹æ—§æ•°æ<EFBFBD>?缓å˜ï¼?
|
||||
const deprecatedKeys = [
|
||||
'parseMethod',
|
||||
'orderPath',
|
||||
@ -792,15 +792,15 @@
|
||||
return formData;
|
||||
}
|
||||
|
||||
// 右侧弹窗提交前处理:接口不再需要兜底轨迹发生地等字段
|
||||
// å<EFBFBD>³ä¾§å¼¹çª—æ<EFBFBD><EFBFBD>交å‰<EFBFBD>处ç<EFBFBD>†ï¼šæŽ¥å<EFBFBD>£ä¸<EFBFBD>å†<EFBFBD>需è¦<EFBFBD>兜底轨迹å<EFBFBD>‘生地ç‰å—æ®?
|
||||
function beforeSubmitRight(formData: any) {
|
||||
if ('trackPlaceValue' in formData) delete formData.trackPlaceValue;
|
||||
return formData;
|
||||
}
|
||||
|
||||
// 从API获取数据后处<EFBFBD>?
|
||||
// 从API获å<EFBFBD>–æ•°æ<EFBFBD>®å<EFBFBD>Žå¤„ï¿?
|
||||
function onGetInfoLeft(data: any) {
|
||||
// 返参配置已废弃:不再做默认值处理
|
||||
// è¿”å<EFBFBD>‚é…<EFBFBD>置已废弃:ä¸<EFBFBD>å†<EFBFBD>å<EFBFBD>šé»˜è®¤å€¼å¤„ç<EFBFBD>?
|
||||
}
|
||||
|
||||
// 左侧弹窗保存
|
||||
@ -825,16 +825,16 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 优先级弹<EFBFBD>?- 新增/编辑
|
||||
// 优先级弹�- 新增/编辑
|
||||
function handleOpenPriorityModal(type: IDialogConfig['type'], row?: any) {
|
||||
if (!currentId.value || !currentId.value.id) {
|
||||
$baseMessage('请先选择左侧一条拉取配置', 'warning');
|
||||
$baseMessage('请先选择左侧一æ<EFBFBD>¡æ‹‰å<EFBFBD>–é…<EFBFBD>ç½?, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const titleObj: { [x in IDialogConfig['type']]: string } = {
|
||||
add: '新增优先级',
|
||||
edit: '编辑优先级',
|
||||
add: '新增优先�,
|
||||
edit: '编辑优先�,
|
||||
};
|
||||
|
||||
priorityModalParams.trackPullConfigId = currentId.value.id;
|
||||
@ -854,8 +854,8 @@
|
||||
formData = {
|
||||
customer: row.customer,
|
||||
orderNoType: row.orderNoType,
|
||||
// 24 / 每天拉取次数 * 60 = 接口<EFBFBD>?
|
||||
// 每天拉取次数 = 24 * 60 / 接口<EFBFBD>?= 1440 / 接口<EFBFBD>?
|
||||
// 24 / æ¯<EFBFBD>天拉å<EFBFBD>–次数 * 60 = 接å<EFBFBD>£ï¿?
|
||||
// æ¯<EFBFBD>天拉å<EFBFBD>–次数 = 24 * 60 / 接å<EFBFBD>£ï¿?= 1440 / 接å<EFBFBD>£ï¿?
|
||||
pullInterval: row.pullInterval && row.pullInterval !== 0 ? 1440 / row.pullInterval : undefined,
|
||||
priority: row.priority,
|
||||
};
|
||||
@ -868,7 +868,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 优先级弹窗提<EFBFBD>?
|
||||
// 优先级弹窗æ<EFBFBD><EFBFBD>ï¿?
|
||||
async function onSubmitPriority(formData: any) {
|
||||
if (!currentId.value || !currentId.value.id) return;
|
||||
|
||||
@ -879,8 +879,8 @@
|
||||
customer: formData.customer,
|
||||
orderNoType: formData.orderNoType,
|
||||
priority: formData.priority,
|
||||
// 24 / 每天拉取次数 * 60 = 接口<EFBFBD>?
|
||||
// 接口<EFBFBD>?= (24 / 每天拉取次数) * 60 = 1440 / 每天拉取次数
|
||||
// 24 / æ¯<EFBFBD>天拉å<EFBFBD>–次数 * 60 = 接å<EFBFBD>£ï¿?
|
||||
// 接å<EFBFBD>£ï¿?= (24 / æ¯<EFBFBD>天拉å<EFBFBD>–次数) * 60 = 1440 / æ¯<EFBFBD>天拉å<EFBFBD>–次数
|
||||
pullInterval: formData.pullInterval && formData.pullInterval !== 0 ? 1440 / formData.pullInterval : undefined,
|
||||
};
|
||||
|
||||
@ -1012,7 +1012,7 @@
|
||||
// 确认串联拉取
|
||||
function handleConfirmChainPull() {
|
||||
if (!selectedServiceProviderId.value) {
|
||||
$baseMessage('请选择服务商', 'warning');
|
||||
$baseMessage('请选择æœ<EFBFBD>务å•?, 'warning');
|
||||
return;
|
||||
}
|
||||
// TODO: 这里可以添加串联拉取的具体逻辑
|
||||
@ -1043,7 +1043,7 @@
|
||||
search-placeholder="请输入服务商名称或appkey"
|
||||
:refresh-title="$t('track.push.actions.refresh')"
|
||||
add-title="新增拉取"
|
||||
loading-text="加载中..."
|
||||
loading-text="åŠ è½½ä¸?.."
|
||||
delete-label="删除"
|
||||
:show-method="false"
|
||||
:show-map-count="false"
|
||||
@ -1162,7 +1162,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优先级 Tab -->
|
||||
<!-- 优先�Tab -->
|
||||
<div v-else class="detail-tab-wrapper">
|
||||
<div class="table-toolbar">
|
||||
<div class="toolbar-left">
|
||||
@ -1223,7 +1223,7 @@
|
||||
>
|
||||
<div class="chain-pull-modal-content">
|
||||
<a-form :model="{ serviceProviderId: selectedServiceProviderId }" layout="vertical">
|
||||
<a-form-item label="服务商" required>
|
||||
<a-form-item label="æœ<EFBFBD>务å•? required>
|
||||
<SelectPro
|
||||
v-model="selectedServiceProviderId"
|
||||
:api="getServiceProviderGetServiceProviderDropdownList"
|
||||
@ -1231,7 +1231,7 @@
|
||||
:label-keys="['name']"
|
||||
id-key="id"
|
||||
:is-page="false"
|
||||
placeholder="请选择服务商"
|
||||
placeholder="请选择æœ<EFBFBD>务å•?
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
@ -1239,7 +1239,7 @@
|
||||
</div>
|
||||
</ModalPro>
|
||||
|
||||
<!-- 轨迹拉取优先级弹<EFBFBD>?-->
|
||||
<!-- 轨迹拉å<EFBFBD>–优先级弹ï¿?-->
|
||||
<ModalForm
|
||||
ref="priorityModalRef"
|
||||
:edit-api="postEditPriority"
|
||||
@ -1323,7 +1323,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 列表容器区域 - 与上游接口的interface-tree保持一<E68C81>?*/
|
||||
/* 列表容器区域 - 与上游接å<C2A5>£çš„interface-treeä¿<C3A4>æŒ<C3A6>一ï¿?*/
|
||||
:deep(.scrollbarplus) {
|
||||
padding: 8px 0 0;
|
||||
padding-right: 4px;
|
||||
@ -1382,7 +1382,7 @@
|
||||
border-radius: 4px;
|
||||
|
||||
&::before {
|
||||
content: '↻';
|
||||
content: '�;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
}
|
||||
@ -1484,7 +1484,7 @@
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
// 让明细页工具栏(含“串联拉取”按钮)与左侧列表的左边距保持一致
|
||||
// 让明细页工具æ <EFBFBD>(å<EFBFBD>«â€œä¸²è<EFBFBD>”拉å<EFBFBD>–â€<EFBFBD>按钮)与左侧列表的左边è·<EFBFBD>ä¿<EFBFBD>æŒ<EFBFBD>一è‡?
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
|
||||
@ -1535,7 +1535,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保trb-transform-left的padding与上游接口一<E58FA3>?*/
|
||||
/* ç¡®ä¿<EFBFBD>trb-transform-leftçš„padding与上游接å<C2A5>£ä¸€ï¿?*/
|
||||
:deep(.trb-transform-left) {
|
||||
padding: 0 !important;
|
||||
margin-right: 16px !important;
|
||||
@ -1547,7 +1547,7 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 输入框 / Select 焦点 - 符合设计规范 */
|
||||
/* 输入æ¡?/ Select 焦点 - 符å<C2A6>ˆè®¾è®¡è§„范 */
|
||||
.track-layout :deep(.arco-input:focus-within),
|
||||
.track-layout :deep(.arco-input-number:focus-within),
|
||||
.track-layout :deep(.arco-select-view-open) {
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
|
||||
/** 搜索条件 */
|
||||
const searchModel = reactive<any>({});
|
||||
/** 搜索关键字 */
|
||||
/** æ<EFBFBD>œç´¢å…³é”®å?*/
|
||||
const searchKeyword = ref('');
|
||||
|
||||
const pushListData = ref<any[]>([]);
|
||||
@ -104,7 +104,7 @@
|
||||
const items = res?.data?.items ?? res?.items ?? res?.data ?? [];
|
||||
pushListData.value = Array.isArray(items) ? items : [];
|
||||
} catch (e) {
|
||||
$baseMessage('加载推送配置列表失败', 'error');
|
||||
$baseMessage('åŠ è½½æŽ¨é€<EFBFBD>é…<EFBFBD>置列表失è´?, 'error');
|
||||
pushListData.value = [];
|
||||
} finally {
|
||||
pushLoading.value = false;
|
||||
@ -117,12 +117,12 @@
|
||||
return [
|
||||
{
|
||||
key: 'category-1',
|
||||
title: '推送配置',
|
||||
title: '推é€<EFBFBD>é…<EFBFBD>ç½?,
|
||||
children: list.map((item: any) => {
|
||||
const time = item.createTime ? dayjs(item.createTime).format('YYYY-MM-DD HH:mm') : '';
|
||||
const app = item.appName || (item.appKey != null ? (appMap.value?.[item.appKey] ?? '') : '') || item.appKey || '-';
|
||||
const appStr = app ? `【${app}】` : '';
|
||||
const customerStr = item.customer ? `【${item.customer}】` : '';
|
||||
const appStr = app ? `�{app}】` : '';
|
||||
const customerStr = item.customer ? `�{item.customer}】` : '';
|
||||
const codeLine = [time, appStr, customerStr].filter(Boolean).join(' ');
|
||||
return {
|
||||
key: `item-${item.id}`,
|
||||
@ -167,7 +167,7 @@
|
||||
// 分页
|
||||
const totalCount = ref(0);
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(30);
|
||||
const pageSize = ref(50);
|
||||
|
||||
const detailColumns = computed(() => {
|
||||
const fixed = [
|
||||
@ -221,7 +221,7 @@
|
||||
function addDraftColumn() {
|
||||
const name = newColumnName.value?.trim();
|
||||
if (!name) {
|
||||
$baseMessage('请输入列名', 'warning');
|
||||
$baseMessage('请输入列å<EFBFBD>?, 'warning');
|
||||
return;
|
||||
}
|
||||
if (draftColumns.value.some((c) => c.title === name)) {
|
||||
@ -251,7 +251,7 @@
|
||||
function saveDraftColumnTitle(col: CustomColumnItem, newTitle: string) {
|
||||
const title = newTitle?.trim();
|
||||
if (!title) {
|
||||
$baseMessage('请输入列名', 'warning');
|
||||
$baseMessage('请输入列å<EFBFBD>?, 'warning');
|
||||
return;
|
||||
}
|
||||
if (draftColumns.value.some((c) => c !== col && c.title === title)) {
|
||||
@ -499,7 +499,7 @@
|
||||
field: col.field,
|
||||
comp: 'AInput',
|
||||
compProps: {
|
||||
placeholder: '请输入',
|
||||
placeholder: '请输�,
|
||||
},
|
||||
span: 12,
|
||||
}));
|
||||
@ -868,7 +868,7 @@
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
});
|
||||
// 根据接口返回的 extraConfig 完全替换自定义表头
|
||||
// æ ¹æ<EFBFBD>®æŽ¥å<EFBFBD>£è¿”回çš?extraConfig 完全替æ<EFBFBD>¢è‡ªå®šä¹‰è¡¨å¤?
|
||||
customColumns.value = Array.from(extraKeys).map((key) => ({ field: key, title: key }));
|
||||
items.forEach((r: any) => {
|
||||
r.standardTrack = formatStandardTrack(r);
|
||||
@ -919,7 +919,7 @@
|
||||
:search-placeholder="$t('track.push.search.placeholder')"
|
||||
:refresh-title="$t('track.push.actions.refresh')"
|
||||
:add-title="$t('track.push.actions.addPush')"
|
||||
loading-text="加载中..."
|
||||
loading-text="åŠ è½½ä¸?.."
|
||||
:delete-label="$t('track.push.actions.delete')"
|
||||
:show-method="false"
|
||||
:show-map-count="false"
|
||||
@ -1043,7 +1043,7 @@
|
||||
<a-button @click="closeColumnModal">取消</a-button>
|
||||
<a-button type="primary" @click="saveDraftColumns">保存</a-button>
|
||||
</template>
|
||||
<div class="column-modal-tip">请填写上游接口-推送轨迹的字段名,用于传递入参</div>
|
||||
<div class="column-modal-tip">请填写上游接å<EFBFBD>?推é€<EFBFBD>è½¨è¿¹çš„å—æ®µå<EFBFBD><EFBFBD>ï¼Œç”¨äºŽä¼ é€’å…¥å<EFBFBD>?/div>
|
||||
<div class="column-management-section">
|
||||
<div class="column-list">
|
||||
<div v-for="column in draftColumns" :key="column.field" class="column-item">
|
||||
@ -1051,7 +1051,7 @@
|
||||
<a-input
|
||||
v-model="column.title"
|
||||
size="small"
|
||||
placeholder="请输入列名"
|
||||
placeholder="请输入列å<EFBFBD>?
|
||||
@press-enter="saveDraftColumnTitle(column, column.title)"
|
||||
@blur="saveDraftColumnTitle(column, column.title)"
|
||||
@keyup.esc="cancelEditDraft(column)"
|
||||
@ -1059,7 +1059,7 @@
|
||||
</div>
|
||||
<div v-else class="column-display">
|
||||
<span class="column-name" @dblclick="startEditDraft(column)">{{ column.title }}</span>
|
||||
<a-button type="text" size="mini" class="column-delete-btn" title="删除列" @click="deleteDraftColumn(column)">
|
||||
<a-button type="text" size="mini" class="column-delete-btn" title="åˆ é™¤åˆ? @click="deleteDraftColumn(column)">
|
||||
×
|
||||
</a-button>
|
||||
</div>
|
||||
@ -1098,7 +1098,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 左侧区域 - 符合设计规范:圆角 8px */
|
||||
/* 左侧区域 - 符å<C2A6>ˆè®¾è®¡è§„范:圆è§?8px */
|
||||
:deep(.trb-transform-left) {
|
||||
background: var(--app-sidebar-bg) !important;
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
@ -1344,7 +1344,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 新增、保存按钮间距 */
|
||||
/* 新增ã€<EFBFBD>ä¿<EFBFBD>å˜æŒ‰é’®é—´è·?*/
|
||||
.toolbar-add-save-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -1494,7 +1494,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 左侧面板与轨迹拉取一致 */
|
||||
/* 左侧é<EFBFBD>¢æ<EFBFBD>¿ä¸Žè½¨è¿¹æ‹‰å<EFBFBD>–一è‡?*/
|
||||
:deep(.trb-transform-left) {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
@ -1530,7 +1530,7 @@
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 输入框 / Select 焦点 - 符合设计规范 */
|
||||
/* 输入æ¡?/ Select 焦点 - 符å<C2A6>ˆè®¾è®¡è§„范 */
|
||||
.track-layout :deep(.arco-input:focus-within),
|
||||
.track-layout :deep(.arco-input-number:focus-within),
|
||||
.track-layout :deep(.arco-select-view-open) {
|
||||
|
||||
@ -28,15 +28,15 @@
|
||||
|
||||
const page = reactive<any>({
|
||||
pageIndex: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
});
|
||||
|
||||
const listData = ref<API.GetTrackPullConfigPageOutputVO[]>([]);
|
||||
|
||||
// 应用列表映射(appKey -> name)
|
||||
// 蠎皮畑蛻苓。ィ譏<EFBFBD>蟆<EFBFBD>シ<EFBFBD>ppKey -> name<EFBFBD>?
|
||||
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);
|
||||
}
|
||||
|
||||
@ -28,14 +28,14 @@
|
||||
|
||||
const page = reactive<any>({
|
||||
pageIndex: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
});
|
||||
const listData = ref<API.GetTrackPushConfigPageOutputVO[]>([]);
|
||||
|
||||
// 应用列表映射(appKey -> name)
|
||||
// 蠎皮畑蛻苓。ィ譏<EFBFBD>蟆<EFBFBD>シ<EFBFBD>ppKey -> name<EFBFBD>?
|
||||
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);
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ const keyword = ref('')
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const saving = ref(false)
|
||||
|
||||
|
||||
@ -175,7 +175,7 @@ const mainCustomerOptions = ref<{ label: string; value: number }[]>([])
|
||||
const tableData = ref<CustomerDto[]>([])
|
||||
const totalCount = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
|
||||
const hotColumns = computed(() => [
|
||||
{ key: 'customerCode', label: t('user.subAccount.fields.customerCode'), width: 110 },
|
||||
@ -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,7 +379,7 @@ const formRules = computed(() => ({
|
||||
password: [{ required: true, message: t('user.subAccount.rules.passwordRequired') }],
|
||||
}))
|
||||
|
||||
// 权限树(子账号权限须 ⊆ 当前登录用户 permissions)
|
||||
// æ<EFBFBD>ƒé™<EFBFBD>æ ‘ï¼ˆå<EFBFBD>è´¦å<EFBFBD>·æ<EFBFBD>ƒé™<EFBFBD>é¡» âŠ?当å‰<EFBFBD>登录用户 permissionsï¼?
|
||||
const permissionTreeLoading = ref(false)
|
||||
const permissionTree = ref<PermissionGroupDto[]>([])
|
||||
const permissionExpandedKeys = ref<string[]>([])
|
||||
|
||||
@ -37,7 +37,7 @@ const tableRows = ref<any[]>([])
|
||||
const keyword = ref('')
|
||||
const enabledFilter = ref<boolean | undefined>(undefined)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
|
||||
const [registerFormModal, { openModal, closeModal }] = useModal() as any
|
||||
|
||||
@ -78,7 +78,7 @@ const {
|
||||
} = useWarehouseAdminCustomer()
|
||||
const currentWarehouseStore = useCurrentWarehouseStore()
|
||||
|
||||
/** Tab active-key;全部用 'all',请求时再转成不传 receiptStatus */
|
||||
/** Tab active-key;全部用 'all',请求时å†<C3A5>转æˆ<C3A6>ä¸<C3A4>ä¼?receiptStatus */
|
||||
const STATUS_TAB_ALL = 'all'
|
||||
const receiptStatus = ref<string>(STATUS_TAB_ALL)
|
||||
const summary = ref({ forecasted: 0, received: 0, completed: 0 })
|
||||
@ -90,7 +90,7 @@ const skuOptions = ref<Array<{ label: string; value: number; code: string }>>([]
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const selectedBoxKeys = ref<string[]>([])
|
||||
const activeBoxKey = ref('')
|
||||
@ -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>
|
||||
|
||||
@ -46,7 +46,7 @@ const {
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const keyword = ref('')
|
||||
const headerSearchParams = reactive<Record<string, string>>({})
|
||||
@ -69,7 +69,7 @@ const historyLoading = ref(false)
|
||||
const historyTab = ref<'all' | 'inbound' | 'deduct'>('all')
|
||||
const historyRows = ref<any[]>([])
|
||||
const historyPage = ref(1)
|
||||
const historyPageSize = ref(30)
|
||||
const historypageSize = ref(50)
|
||||
const historyTotal = ref(0)
|
||||
|
||||
const [registerRelocateModal, { openModal: openRelocate, closeModal: closeRelocate }] = useModal() as any
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ const {
|
||||
} = useWarehouseAdminCustomer()
|
||||
const currentWarehouseStore = useCurrentWarehouseStore()
|
||||
|
||||
/** Tab active-key;全部用 'all',请求时再转成不传 lifecycleStatus */
|
||||
/** Tab active-key;全部用 'all',请求时å†<C3A5>转æˆ<C3A6>ä¸<C3A4>ä¼?lifecycleStatus */
|
||||
const STATUS_TAB_ALL = 'all'
|
||||
const lifecycleStatus = ref<string>(STATUS_TAB_ALL)
|
||||
const keyword = ref('')
|
||||
@ -81,7 +81,7 @@ const warehouseNameMap = computed(() => buildWarehouseNameMap(warehouseOptions.v
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
|
||||
const statusTabs = [
|
||||
@ -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()
|
||||
}
|
||||
|
||||
/** 列表无图 / 加载失败时的缺省图片 icon(data URI)— 用中性灰,日夜间都可读 */
|
||||
/** åˆ—è¡¨æ— å›¾ / åŠ è½½å¤±è´¥æ—¶çš„ç¼ºçœ<C3A7>图片 icon(data URI)â€?ç”¨ä¸æ€§ç<C2A7>°ï¼Œæ—¥å¤œé—´éƒ½å<C2BD>¯è¯?*/
|
||||
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>
|
||||
|
||||
@ -149,7 +149,7 @@ const headerSearchParams = reactive<Record<string, string>>({})
|
||||
const ARRIVAL_SERVER_FILTER_KEYS = ['trackingNo'] as const
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
|
||||
const formData = reactive({
|
||||
@ -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')) {
|
||||
|
||||
@ -47,7 +47,7 @@ const currentWarehouseStore = useCurrentWarehouseStore()
|
||||
const listLoading = ref(false)
|
||||
const tableRows = ref<any[]>([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(30)
|
||||
const pageSize = ref(50)
|
||||
const totalCount = ref(0)
|
||||
const keyword = ref('')
|
||||
const headerSearchParams = reactive<Record<string, string>>({})
|
||||
@ -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
|
||||
|
||||
@ -4,12 +4,12 @@
|
||||
<div class="toolbar-left">
|
||||
<div class="toolbar-info">
|
||||
<div class="xhs-panel-title">发布记录</div>
|
||||
<div class="xhs-panel-desc">含定时待发布与已发布/失败记录;到期将自动尝试发布。</div>
|
||||
<div class="xhs-panel-desc">å<EFBFBD>«å®šæ—¶å¾…å<EFBFBD>‘布与已å<EFBFBD>‘布/失败记录;到期将自动å°<EFBFBD>试å<EFBFBD>‘布ã€?/div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<a-button :disabled="loading" @click="emit('refresh')">
|
||||
{{ loading ? '刷新中…' : '刷新' }}
|
||||
{{ loading ? '刷新ä¸â€? : '刷新' }}
|
||||
</a-button>
|
||||
<a-pagination
|
||||
v-if="showPagination"
|
||||
@ -45,7 +45,7 @@ withDefaults(
|
||||
}>(),
|
||||
{
|
||||
showPagination: false,
|
||||
pagination: () => ({ current: 1, pageSize: 30, total: 0 }),
|
||||
pagination: () => ({ current: 1, pageSize: 50, total: 0 }),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="toolbar-left">
|
||||
<span class="xhs-panel-title">启动服务</span>
|
||||
<span class="xhs-panel-desc">
|
||||
本机 Agent + 浏览器引擎:找对标、写稿、发笔记、互动留痕
|
||||
<EFBFBD>祆㦤 Agent + 瘚讛<EFBFBD><EFBFBD>典<EFBFBD><EFBFBD>𠬍<EFBFBD><EFBFBD>曉笆<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝔踴<EFBFBD><EFBFBD><EFBFBD>蝚磰扇<EFBFBD><EFBFBD><EFBFBD><EFBFBD>函<EFBFBD><EFBFBD>?
|
||||
</span>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
@ -31,7 +31,7 @@
|
||||
v-if="offlineCount > 0 && agents.length > 0"
|
||||
type="warning"
|
||||
style="margin-bottom: 12px"
|
||||
:title="`有 ${offlineCount} 台 Agent 离线,请检查本机 Agent 是否在运行`"
|
||||
:title="`<EFBFBD>?${offlineCount} <20>?Agent 蝳餌瑪嚗諹窈璉<E7AA88><E79289>交𧋦<E4BAA4>?Agent <20>臬炏<E887AC>刻<EFBFBD>銵䈣"
|
||||
closable
|
||||
/>
|
||||
|
||||
@ -39,26 +39,26 @@
|
||||
v-if="!loading && agents.length === 0"
|
||||
type="info"
|
||||
style="margin-bottom: 12px"
|
||||
title="尚未连接 Agent,请运行 xhs-agent-desktop 并连接 OMS"
|
||||
title="撠𡁏𧊋餈墧𦻖 Agent嚗諹窈餈鞱<E9A488> xhs-agent-desktop 撟嗉<E6929F><E59789>?OMS"
|
||||
closable
|
||||
/>
|
||||
|
||||
<div class="service-cards-row">
|
||||
<div class="service-card xhs-inner-card">
|
||||
<div class="card-header">
|
||||
<span class="card-title">小红书 · 浏览器引擎</span>
|
||||
<span class="card-title">撠讐滯銋?繚 瘚讛<EFBFBD><EFBFBD>典<EFBFBD><EFBFBD>?/span>
|
||||
<XhsStatusBadge :kind="engineBadge.kind" :text="engineBadge.text" />
|
||||
</div>
|
||||
<p class="card-hint">{{ bridgeUrlText }}</p>
|
||||
<p class="card-hint card-note">
|
||||
内嵌 xiaohongshu-skills:Bridge Server + Chrome 扩展,在本机真实浏览器中操作小红书。
|
||||
<EFBFBD><EFBFBD><EFBFBD> xiaohongshu-skills嚗鋳ridge Server + Chrome <EFBFBD>拙<EFBFBD>嚗<EFBFBD>銁<EFBFBD>祆㦤<EFBFBD>笔<EFBFBD>瘚讛<EFBFBD><EFBFBD>其葉<EFBFBD>滢<EFBFBD>撠讐滯銋艾<EFBFBD>?
|
||||
</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">刷新状态</a-button>
|
||||
<a-button @click="loadAgents"><EFBFBD>瑟鰵<EFBFBD>嗆<EFBFBD>?/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')">退出登录</a-button>
|
||||
<a-button :disabled="!canControl" @click="control('clear_cookies')"><EFBFBD><EFBFBD><EFBFBD>箇蒈敶?/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">
|
||||
抖音版 Chrome 扩展与自动化引擎正在接入中。上线后可与本站一样使用分析、评论与创作能力。
|
||||
<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>?
|
||||
</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">已连接设备</span>
|
||||
<span class="section-title">撌脰<EFBFBD><EFBFBD>亥挽憭?/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="状态" :width="90">
|
||||
<a-table-column title="<EFBFBD>嗆<EFBFBD>? :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 ? '运行中' : '未启动'"
|
||||
:text="record.engineRunning ? '餈鞱<EFBFBD>銝? : '<27>芸鍳<E88AB8>?"
|
||||
/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
@ -149,20 +149,20 @@
|
||||
<template #cell="{ record }">
|
||||
<XhsStatusBadge
|
||||
:kind="record.extensionConnected ? 'ok' : 'warn'"
|
||||
:text="record.extensionConnected ? '已连接' : '未连接'"
|
||||
:text="record.extensionConnected ? '撌脰<EFBFBD><EFBFBD>? : '<27>芾<EFBFBD><E88ABE>?"
|
||||
/>
|
||||
</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 ? '已登录' : '未登录'"
|
||||
:text="record.xhsLoggedIn ? '撌脩蒈敶? : '<27>芰蒈敶?"
|
||||
/>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="版本" data-index="agentVersion" :width="100" />
|
||||
<a-table-column title="最后心跳" :width="170">
|
||||
<a-table-column title="<EFBFBD><EFBFBD><EFBFBD>𤾸<EFBFBD>頝? :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="运行 xhs-agent-desktop/run.ps1 打开桌面端">
|
||||
打开 Agent 桌面端
|
||||
<a-step description="餈鞱<EFBFBD> xhs-agent-desktop/run.ps1 <EFBFBD>枏<EFBFBD>獢屸𢒰蝡?>
|
||||
<EFBFBD>枏<EFBFBD> Agent 獢屸𢒰蝡?
|
||||
</a-step>
|
||||
<a-step description="桌面端点击「连接 OMS」;保持本页打开即可自动授权">
|
||||
一键连接
|
||||
<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>
|
||||
<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="无需填写 API 或 Token">
|
||||
请先在浏览器登录 OMS,再打开桌面端点击「连接 OMS」。若本页已打开,授权会自动完成。
|
||||
<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>
|
||||
|
||||
<div class="cmd-block">
|
||||
<div class="cmd-label">桌面端</div>
|
||||
<div class="cmd-label">獢屸𢒰蝡?/div>
|
||||
<pre>{{ desktopAgentHint }}</pre>
|
||||
</div>
|
||||
</a-drawer>
|
||||
@ -228,7 +228,7 @@ const selectedAgentId = ref<number | undefined>();
|
||||
const showSetupDrawer = ref(false);
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 30,
|
||||
pageSize: 50,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
@ -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: '待刷新' };
|
||||
if (agent.engineRunning) return { kind: 'ok', text: '运行中' };
|
||||
if (agent.extensionConnected) return { kind: 'warn', text: '待扩展' };
|
||||
return { kind: 'err', text: '未启动' };
|
||||
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>? };
|
||||
});
|
||||
|
||||
const loginBadge = computed((): { kind: XhsBadgeKind; text: string } => {
|
||||
const agent = selectedAgent.value;
|
||||
if (!agent || agent.status !== 'online') return { kind: 'idle', text: '待刷新' };
|
||||
if (agent.xhsLoggedIn) return { kind: 'ok', text: '已登录' };
|
||||
if (!agent || agent.status !== 'online') return { kind: 'idle', text: '敺<EFBFBD><EFBFBD><EFBFBD>? };
|
||||
if (agent.xhsLoggedIn) return { kind: 'ok', text: '撌脩蒈敶? };
|
||||
if (agent.engineRunning && agent.extensionConnected) return { kind: 'ok', text: '扩展就绪' };
|
||||
if (agent.engineRunning) return { kind: 'warn', text: '未登录' };
|
||||
return { kind: 'warn', text: '待启动' };
|
||||
if (agent.engineRunning) return { kind: 'warn', text: '<EFBFBD>芰蒈敶? };
|
||||
return { kind: 'warn', text: '敺<EFBFBD>鍳<EFBFBD>? };
|
||||
});
|
||||
|
||||
const loginGuideText = computed(() => {
|
||||
const agent = selectedAgent.value;
|
||||
if (!agent) {
|
||||
return '只需:连接 Agent → 启动引擎 → 在 Chrome 加载扩展 → 打开小红书并登录。';
|
||||
return '<EFBFBD>芷<EFBFBD>嚗朞<EFBFBD><EFBFBD>?Agent <EFBFBD>?<EFBFBD>臬𢆡撘閙<EFBFBD> <EFBFBD>?<EFBFBD>?Chrome <EFBFBD>㰘蝸<EFBFBD>拙<EFBFBD> <EFBFBD>?<EFBFBD>枏<EFBFBD>撠讐滯銋血僎<EFBFBD>餃<EFBFBD><EFBFBD>?;
|
||||
}
|
||||
if (agent.xhsLoggedIn) {
|
||||
return '校验通过:可在「内容分析」或「爆款创作」页使用工具。';
|
||||
return '<EFBFBD>⊿<EFBFBD><EFBFBD>朞<EFBFBD>嚗𡁜虾<EFBFBD>具<EFBFBD><EFBFBD><EFBFBD>摰孵<EFBFBD><EFBFBD>僐<EFBFBD>齿<EFBFBD><EFBFBD>𣬚<EFBFBD>甈曉<EFBFBD>雿栶<EFBFBD>漤△雿輻鍂撌亙<EFBFBD><EFBFBD>?;
|
||||
}
|
||||
if (agent.engineRunning && agent.extensionConnected) {
|
||||
return '引擎与扩展已就绪。请在 Chrome 中打开小红书并登录;可点击扩展图标查看实时状态。';
|
||||
return '撘閙<EFBFBD>銝擧<EFBFBD>撅訫歇撠梁貌<EFBFBD><EFBFBD>窈<EFBFBD>?Chrome 銝剜<EFBFBD>撘<EFBFBD>撠讐滯銋血僎<EFBFBD>餃<EFBFBD>嚗𥕦虾<EFBFBD>孵稬<EFBFBD>拙<EFBFBD><EFBFBD>暹<EFBFBD><EFBFBD>亦<EFBFBD>摰墧𧒄<EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>?;
|
||||
}
|
||||
return '只需:启动引擎 → 在 Chrome 加载扩展 → 打开小红书并登录。扩展图标可查看 Bridge 与登录状态;下方按钮用于快捷操作。';
|
||||
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>?;
|
||||
});
|
||||
|
||||
const desktopAgentHint = computed(() =>
|
||||
[
|
||||
'1. 运行 oms0414/xhs-agent-desktop/run.ps1',
|
||||
'2. 桌面端点击「连接 OMS」(无需填 API / Token)',
|
||||
'3. 保持本页打开 → 自动授权;或按提示打开授权页',
|
||||
'4. 启动引擎 → Chrome 加载扩展目录 → 登录小红书',
|
||||
'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>撠讐滯銋?,
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
@ -333,7 +333,7 @@ async function tryAutoPair() {
|
||||
const ok = await pushCredentialsToAgent(token);
|
||||
if (ok) {
|
||||
pairPushed = true;
|
||||
Message.success('已向桌面 Agent 发送授权');
|
||||
Message.success('撌脣<EFBFBD>獢屸𢒰 Agent <EFBFBD>煾<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?);
|
||||
setTimeout(loadAgents, 1500);
|
||||
}
|
||||
}
|
||||
@ -370,13 +370,13 @@ async function control(action: string) {
|
||||
return;
|
||||
}
|
||||
if (agent.status !== 'online') {
|
||||
Message.warning('所选 Agent 离线,请检查本机是否在运行');
|
||||
Message.warning('<EFBFBD><EFBFBD><EFBFBD>?Agent 蝳餌瑪嚗諹窈璉<E7AA88><E79289>交𧋦<E4BAA4>箸糓<E7AEB8>血銁餈鞱<E9A488>');
|
||||
return;
|
||||
}
|
||||
if (action === 'clear_cookies') {
|
||||
Modal.confirm({
|
||||
title: '退出登录',
|
||||
content: '将在浏览器中退出小红书登录状态,确定继续?',
|
||||
title: '<EFBFBD><EFBFBD><EFBFBD>箇蒈敶?,
|
||||
content: '撠<EFBFBD>銁瘚讛<EFBFBD><EFBFBD>其葉<EFBFBD><EFBFBD><EFBFBD>箏<EFBFBD>蝥V髡<EFBFBD>餃<EFBFBD><EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>蝖桀<EFBFBD>蝏抒賒嚗?,
|
||||
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(`已创建任务 #${res.taskId}`);
|
||||
Message.success(`撌脣<EFBFBD>撱箔遙<EFBFBD>?#${res.taskId}`);
|
||||
setTimeout(loadAgents, 2000);
|
||||
} catch (e: any) {
|
||||
Message.error(e?.message || '操作失败');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user