From 407a0f7be6f0fe77a11f2ed2269be74ba480ca59 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Thu, 30 Jul 2026 20:09:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Handsontable=20=E6=B7=B7=E5=90=88?= =?UTF-8?q?=E8=A1=A8=E5=A4=B4=E4=B8=8E=E8=AF=B7=E6=B1=82=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=94=B9=E9=80=A0=EF=BC=8C=E5=B9=B6=E5=AE=8C=E5=96=84=20H5=20?= =?UTF-8?q?=E4=B8=8B=E5=8D=95/=E7=99=BB=E5=BD=95=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一 HandsontablePro 液态筛选表头、批量输入与自动列增强;请求日志改用公共列表组件;同步收件地址解析与 H5/PC 下单相关改动。 Co-authored-by: Cursor --- docs/前端页面设计规范.md | 2 + src/api/order/index.ts | 57 + src/components.d.ts | 2 + .../HandsontablePro/ActiveFilterBar.vue | 124 ++ .../HandsontablePro/HeaderFilterPopover.vue | 1134 ++++++++++++++++ src/components/HandsontablePro/filterValue.ts | 104 ++ .../HandsontablePro/hybridHeader.ts | 96 ++ src/components/HandsontablePro/index.vue | 1206 ++++++++++++----- src/components/HandsontablePro/types.ts | 28 + src/router/guard/userLoginInfo.ts | 8 +- src/router/index.ts | 6 +- src/utils/login-portal.ts | 4 +- src/views/api/requestLog/index.vue | 912 +++++-------- .../components/ChannelConfigHotTable.vue | 1 + .../components/H5PlaceOrderSheet.vue | 238 +++- .../components/WechatOAuthLoginButton.vue | 44 +- src/views/h5/customer-center/locale/en-US.ts | 24 +- src/views/h5/customer-center/locale/zh-CN.ts | 23 +- src/views/h5/customer-center/login/index.vue | 298 ++-- src/views/h5/customer-center/me/index.vue | 303 +---- src/views/order/placeOrder/index.vue | 416 ++++-- src/views/system/announcement/index.vue | 1 - src/views/track/trackContent/index.vue | 3 +- src/views/track/trackFlag/index.vue | 3 +- .../components/TrackOrderDetailDrawer.vue | 20 + .../composables/useTrackOrderData.ts | 40 +- .../track/trackOrder/config/tableColumns.ts | 78 +- src/views/track/trackState/index.vue | 3 +- 28 files changed, 3668 insertions(+), 1510 deletions(-) create mode 100644 src/components/HandsontablePro/ActiveFilterBar.vue create mode 100644 src/components/HandsontablePro/HeaderFilterPopover.vue create mode 100644 src/components/HandsontablePro/filterValue.ts create mode 100644 src/components/HandsontablePro/hybridHeader.ts diff --git a/docs/前端页面设计规范.md b/docs/前端页面设计规范.md index 5a75b62..d6cca5b 100644 --- a/docs/前端页面设计规范.md +++ b/docs/前端页面设计规范.md @@ -320,6 +320,8 @@ itemList: [ | `key` | `string` | 对应行数据字段名(如 `jobId`、`statusLabel`) | | `label` | `string` | 表头文案(支持 `t('...')`) | | `width` | `number` | 列宽(像素) | +| `headerMode` | `'inline' \| 'icon' \| 'none'` | 混合表头:内嵌搜索 / 仅图标 / 纯展示;未设时由 `searchable` + `filterType` 推导 | +| `searchable` / `searchField` / `multiValue` | — | 表头搜索;`multiValue` 显示「多」批量入口 | **正确示例:** diff --git a/src/api/order/index.ts b/src/api/order/index.ts index 5b8c9cf..de05512 100644 --- a/src/api/order/index.ts +++ b/src/api/order/index.ts @@ -1650,6 +1650,63 @@ function throwParcelCreateBusinessError(body: any, fallbackMsg: string): never { }) } +export interface OrderReceiverParseAddressResult { + receiver: { + recipientsName?: string + companyName?: string + phone?: string + telePhone?: string + email?: string + countryCode?: string + province?: string + city?: string + streetNumber?: string + streetAddress1?: string + streetAddress2?: string + streetAddress3?: string + zipCode?: string + } + isComplete: boolean + missingRequired: string[] + missingRecommended: string[] + warnings: string[] +} + +/** POST /order/receiver/parse-address — 粘贴地址 AI 解析 */ +export async function postOrderReceiverParseAddress(body: { + rawText: string + providerKey?: string + model?: string +}): Promise { + const res = await axiosInstance.post(orderUrl('/order/receiver/parse-address'), body) + assertBusinessOk(res.data) + const data = unwrapApiData(res.data) + const receiver = data?.receiver ?? data?.Receiver ?? {} + const pick = (a: string, b: string) => + String(receiver?.[a] ?? receiver?.[b] ?? '').trim() || undefined + return { + receiver: { + recipientsName: pick('recipientsName', 'RecipientsName'), + companyName: pick('companyName', 'CompanyName'), + phone: pick('phone', 'Phone'), + telePhone: pick('telePhone', 'TelePhone'), + email: pick('email', 'Email'), + countryCode: pick('countryCode', 'CountryCode'), + province: pick('province', 'Province'), + city: pick('city', 'City'), + streetNumber: pick('streetNumber', 'StreetNumber'), + streetAddress1: pick('streetAddress1', 'StreetAddress1'), + streetAddress2: pick('streetAddress2', 'StreetAddress2'), + streetAddress3: pick('streetAddress3', 'StreetAddress3'), + zipCode: pick('zipCode', 'ZipCode'), + }, + isComplete: Boolean(data?.isComplete ?? data?.IsComplete), + missingRequired: (data?.missingRequired ?? data?.MissingRequired ?? []) as string[], + missingRecommended: (data?.missingRecommended ?? data?.MissingRecommended ?? []) as string[], + warnings: (data?.warnings ?? data?.Warnings ?? []) as string[], + } +} + /** POST /order/parcel/create */ export async function postOrderParcelCreate(body: Record): Promise { try { diff --git a/src/components.d.ts b/src/components.d.ts index 2851f29..45cefb7 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -8,6 +8,7 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + ActiveFilterBar: typeof import('./components/HandsontablePro/ActiveFilterBar.vue')['default'] AiAdvisorShell: typeof import('./components/NanaAssistant/AiAdvisorShell.vue')['default'] ApiTest: typeof import('./components/ApiTest/index.vue')['default'] BasicLayoutLeftRight: typeof import('./components/BasicLayoutLeftRight/index.vue')['default'] @@ -27,6 +28,7 @@ declare module 'vue' { FormWrapper: typeof import('./components/global-setting/form-wrapper.vue')['default'] GlobalSetting: typeof import('./components/global-setting/index.vue')['default'] HandsontablePro: typeof import('./components/HandsontablePro/index.vue')['default'] + HeaderFilterPopover: typeof import('./components/HandsontablePro/HeaderFilterPopover.vue')['default'] InterfaceDetail: typeof import('./components/InterfaceDetail/index.vue')['default'] InvitePoster: typeof import('./components/InvitePoster.vue')['default'] InviteQrImage: typeof import('./components/InviteQrImage.vue')['default'] diff --git a/src/components/HandsontablePro/ActiveFilterBar.vue b/src/components/HandsontablePro/ActiveFilterBar.vue new file mode 100644 index 0000000..84bde9a --- /dev/null +++ b/src/components/HandsontablePro/ActiveFilterBar.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/components/HandsontablePro/HeaderFilterPopover.vue b/src/components/HandsontablePro/HeaderFilterPopover.vue new file mode 100644 index 0000000..d10d83e --- /dev/null +++ b/src/components/HandsontablePro/HeaderFilterPopover.vue @@ -0,0 +1,1134 @@ + + +