From 3642d650dbcbae7d3060acef9be432340899c712 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Mon, 26 Oct 2026 11:46:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=A2=E6=88=B7=E4=BE=A7=E6=A0=8F?= =?UTF-8?q?=E8=B4=A6=E6=88=B7=E5=9D=9E=E4=B8=8E=E8=BF=90=E5=8D=95=E5=8F=91?= =?UTF-8?q?=E8=B4=A7=E5=90=8E=E7=8A=B6=E6=80=81=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 侧栏改为 Cursor 式账户面板(仓库眼标预览、语言切换、假邮箱过滤),运单「更多」支持发货后四态变更且妥投成功仅可进入。 Co-authored-by: Cursor --- src/api/order/index.ts | 19 + src/components.d.ts | 1 + src/components/ContactService/index.vue | 104 ++++- .../WarehouseAddressPeekModal.vue | 404 ++++++++++++++++ src/components/customer-side-rail/index.vue | 434 ++++++++---------- src/components/top-nav/RightTopMessage.vue | 52 ++- src/components/top-nav/RightTopUser.vue | 425 ++++++++++++++++- src/constants/orderStatus.ts | 21 + src/constants/pageButtonCatalog.ts | 2 + src/locale/en-US.ts | 28 ++ src/locale/zh-CN.ts | 29 +- src/utils/customer-portal-nav.ts | 9 + src/utils/menu-i18n.ts | 19 +- .../components/ChangePostShipStatusModal.vue | 217 +++++++++ src/views/order/waybillManage/index.vue | 50 ++ 15 files changed, 1540 insertions(+), 274 deletions(-) create mode 100644 src/components/customer-side-rail/WarehouseAddressPeekModal.vue create mode 100644 src/views/order/waybillManage/components/ChangePostShipStatusModal.vue diff --git a/src/api/order/index.ts b/src/api/order/index.ts index e5a68a9..393521a 100644 --- a/src/api/order/index.ts +++ b/src/api/order/index.ts @@ -396,6 +396,25 @@ export function postOrderDelete(body: OrderIdRequest) { return postOrderById('/order/delete', body) } +export interface OrderChangePostShipStatusRequest extends OrderIdRequest { + /** pendingOnline / shipping / deliverySuccess / deliveryFailed */ + targetStatus: string + /** 必填备注 */ + remark: string +} + +/** POST /order/change-post-ship-status — 出库后四态校正(派送成功只进不出) */ +export async function postOrderChangePostShipStatus( + body: OrderChangePostShipStatusRequest, +): Promise { + const res = await axiosInstance.post(orderUrl('/order/change-post-ship-status'), { + id: Number(body.id), + targetStatus: String(body.targetStatus || '').trim(), + remark: String(body.remark || '').trim(), + }) + assertBusinessOk(res.data) +} + export interface OrderTrackUpsertResult { skipped?: boolean skipReason?: string diff --git a/src/components.d.ts b/src/components.d.ts index 705769d..468d0f1 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -70,5 +70,6 @@ declare module 'vue' { TopNav: typeof import('./components/top-nav/index.vue')['default'] UserEditDrawer: typeof import('./components/UserEditDrawer/index.vue')['default'] UserProfileFields: typeof import('./components/UserProfileFields/index.vue')['default'] + WarehouseAddressPeekModal: typeof import('./components/customer-side-rail/WarehouseAddressPeekModal.vue')['default'] } } diff --git a/src/components/ContactService/index.vue b/src/components/ContactService/index.vue index aa34834..289b898 100644 --- a/src/components/ContactService/index.vue +++ b/src/components/ContactService/index.vue @@ -11,11 +11,19 @@ const props = withDefaults( dropdownPosition?: 'br' | 'bl' | 'tr' | 'tl' | 'rt' | 'rb' /** 侧栏收起:窄宽展示缩略文案(无图标) */ compact?: boolean + /** 下拉触发;侧栏默认 click,顶栏 hover */ + trigger?: 'hover' | 'click' + /** 侧栏展开:显示「发货仓库」语义前缀 */ + showCaption?: boolean + contentClass?: string }>(), { variant: 'navbar', dropdownPosition: 'br', compact: false, + trigger: undefined, + showCaption: false, + contentClass: '', }, ) @@ -39,6 +47,22 @@ const labelText = computed(() => { const useDropdown = computed(() => props.variant === 'navbar' || props.variant === 'sider') +const dropdownTrigger = computed(() => { + if (props.trigger) return props.trigger + return props.variant === 'sider' ? 'click' : 'hover' +}) + +const showSiderCaption = computed( + () => props.showCaption && props.variant === 'sider' && !props.compact, +) + +const contentClass = computed(() => props.contentClass || undefined) + +const popupTranslate = computed<[number, number] | undefined>(() => { + const pos = props.dropdownPosition + return pos === 'tl' || pos === 'tr' ? [0, -6] : undefined +}) + async function loadServiceDataList() { try { const { items } = await getWarehouseList({ @@ -77,13 +101,23 @@ onMounted(() => { > - + @@ -176,6 +175,7 @@ import { Message } from '@arco-design/web-vue' import { IconDoubleLeft, IconDoubleRight, + IconEye, IconPrinter, } from '@arco-design/web-vue/es/icon' import { useRbacUserStore } from '@/store/modules/rbac-user' @@ -212,8 +212,9 @@ import { openPortalSideDrawer } from '@/views/user/portal-drawer/usePortalSideDr import ContactService from '@/components/ContactService/index.vue' import RightTopMessage from '@/components/top-nav/RightTopMessage.vue' import RightTopUser from '@/components/top-nav/RightTopUser.vue' +import WarehouseAddressPeekModal from '@/components/customer-side-rail/WarehouseAddressPeekModal.vue' -const { t, te } = useI18n() +const { t, te, locale } = useI18n() const route = useRoute() const router = useRouter() const rbacUserStore = useRbacUserStore() @@ -275,7 +276,6 @@ const printConnectionTitle = computed(() => { }) const customerBalance = ref(null) -const balanceRefreshing = ref(false) let balanceInflight: Promise | null = null const balanceDisplay = computed(() => { @@ -311,38 +311,18 @@ function loadCustomerBalance() { return balanceInflight } -async function onRefreshBalance() { - if (balanceRefreshing.value) return - balanceRefreshing.value = true - const previous = customerBalance.value - try { - await loadCustomerBalance() - Message.success(t('nav.balance.refreshed')) - } catch (error) { - console.error('Failed to load customer balance', error) - customerBalance.value = previous - Message.error(t('nav.balance.refreshFailed')) - } finally { - balanceRefreshing.value = false - } -} - -function onBalanceMenuClick(key: string) { - if (key === 'recharge') { - router.push({ name: 'VipRechargeDeposit' }) - } else if (key === 'rechargeRecord') { - router.push({ name: 'FinanceRechargeRecord' }) - } else if (key === 'accountFlow') { - router.push({ name: 'FinanceAccountFlow' }) - } -} - function handleCustomerBalanceChanged() { void loadCustomerBalance().catch((error) => { console.error('Failed to load customer balance', error) }) } +const warehousePeekVisible = ref(false) + +function openWarehouseAddressPeek() { + warehousePeekVisible.value = true +} + function groupLabel(group: CustomerNavGroup) { if (te(group.labelKey)) return t(group.labelKey) return group.labelFallback @@ -353,6 +333,7 @@ function navKey(item: CustomerNavItem) { } function formatTitle(item: CustomerNavItem) { + void locale.value return translateMenuTitle(t, { name: item.name, route: item.route || item.path, @@ -576,7 +557,15 @@ onUnmounted(() => { &__footer-row { display: flex; align-items: center; - gap: 6px; + gap: 2px; + min-width: 0; + } + + &__warehouse-group { + flex: 1; + display: flex; + align-items: center; + gap: 0; min-width: 0; } @@ -589,10 +578,50 @@ onUnmounted(() => { } :deep(.contact-service--sider .contact-service__label) { - min-height: 32px; - padding: 0 8px; - font-size: 12px; - justify-content: flex-start; + padding-right: 2px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + } + + &__warehouse-view { + width: 26px; + height: 30px; + margin-left: -1px; + border-radius: 0 6px 6px 0; + background: rgba(255, 255, 255, 0.55); + color: var(--color-text-3); + + &:hover { + color: var(--app-primary-color); + background: rgba(255, 255, 255, 0.92); + } + } + + &__message-slot { + flex: 1; + min-width: 0; + display: flex; + justify-content: center; + + :deep(.message-dropdown-trigger) { + width: 100%; + max-width: 56px; + height: 30px; + justify-content: center; + padding: 0 4px; + border-radius: 6px; + color: var(--color-text-2); + gap: 2px; + } + + :deep(.message-dropdown-trigger:hover) { + color: var(--color-text-1); + background: rgba(255, 255, 255, 0.85); + } + + :deep(.message-label) { + display: none; } } @@ -751,18 +780,19 @@ onUnmounted(() => { flex-shrink: 0; display: flex; flex-direction: column; - gap: 8px; - padding: 10px var(--csr-pad-x) 12px; + gap: 4px; + padding: 8px var(--csr-pad-x) 8px; border-top: 1px solid var(--color-border-2, rgba(0, 0, 0, 0.06)); } - &__user-row { + /* 工具条:头像 + 消息 + 打印 */ + &__toolbar { display: flex; align-items: center; - gap: 4px; + gap: 2px; min-width: 0; padding: 2px; - border-radius: 10px; + border-radius: 8px; background: rgba(255, 255, 255, 0.45); transition: background-color 0.14s ease, @@ -778,85 +808,26 @@ onUnmounted(() => { } } - &__user-balance { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: flex-end; - max-width: 72px; - min-width: 0; - height: 36px; - padding-right: 4px; - border-radius: 8px; - - &.is-balance-active { - background: color-mix(in srgb, var(--app-primary-color, #1d7281) 12%, #fff); - box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--app-primary-color, #1d7281) 18%, transparent); - } - } - - &__balance-trigger { - display: flex; - align-items: center; - justify-content: flex-end; - max-width: 100%; - min-width: 0; - height: 32px; - padding: 0 4px; - border-radius: 6px; - cursor: pointer; - color: var(--app-primary-color); - font-weight: 600; - font-size: 12px; - font-variant-numeric: tabular-nums; - letter-spacing: -0.02em; - - &:hover { - background: rgba(255, 255, 255, 0.85); - } - } - - &__balance-currency { - flex-shrink: 0; - margin-right: 1px; - opacity: 0.85; - font-weight: 600; - } - - &__balance-value { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - /* 工具:打印(含下载)/ 消息 */ - &__utils { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 2px; - padding: 2px; - border-radius: 10px; - background: rgba(255, 255, 255, 0.5); - } - &__tool { position: relative; display: inline-flex; align-items: center; justify-content: center; - width: 100%; - height: 32px; + flex-shrink: 0; + width: 30px; + height: 30px; border: none; - border-radius: 8px; + border-radius: 6px; background: transparent; color: var(--color-text-3); cursor: pointer; text-decoration: none; - font-size: 16px; + font-size: 14px; + transition: color 0.14s ease, background-color 0.14s ease; &:hover { color: var(--color-text-1); - background: rgba(255, 255, 255, 0.95); + background: rgba(255, 255, 255, 0.85); } &.is-enabled { @@ -867,12 +838,16 @@ onUnmounted(() => { opacity: 0.6; pointer-events: none; } + + &--print { + opacity: 0.9; + } } &__tool-dot { position: absolute; top: 5px; - right: 7px; + right: 5px; width: 5px; height: 5px; border-radius: 50%; @@ -883,60 +858,24 @@ onUnmounted(() => { background: rgb(var(--green-6)); } - &__tool-slot { - display: flex; - align-items: center; - justify-content: center; - height: 32px; - border-radius: 8px; - color: var(--color-text-3); - - :deep(.dropDown), - :deep(.lang-icon), - :deep(.arco-badge) { - color: inherit; - } - - :deep(.lang-icon) { - font-size: 16px; - } - - :deep(.message-dropdown-trigger) { - height: 32px; - min-width: 28px; - padding: 0 4px; - } - } - &__user { - flex: 1; + flex: 0 0 auto; display: flex; align-items: center; - min-width: 0; - :deep(.user-dropdown-trigger) { - width: 100%; - max-width: none; - height: 36px; - padding: 0 8px; + :deep(.user-dropdown-trigger), + :deep(.user-dropdown-trigger.is-compact) { + width: 30px; + height: 30px; + padding: 0; + margin: 0; + justify-content: center; color: var(--color-text-1); } :deep(.user-dropdown-trigger:hover) { background: rgba(255, 255, 255, 0.85); } - - :deep(.user-dropdown-trigger.is-compact) { - width: 36px; - height: 36px; - padding: 0; - margin: 0; - } - - :deep(.user-name) { - max-width: none; - font-size: 13px; - } } &__toggle { @@ -944,23 +883,24 @@ onUnmounted(() => { align-items: center; justify-content: center; flex-shrink: 0; - width: 32px; - height: 32px; + width: 28px; + height: 28px; border: none; - border-radius: 8px; + border-radius: 6px; background: transparent; color: var(--color-text-3); cursor: pointer; - font-size: 15px; + font-size: 13px; &:hover { color: var(--app-primary-color); - background: rgba(255, 255, 255, 0.8); + background: rgba(255, 255, 255, 0.65); } } &__toggle-icon { display: inline-flex; + font-size: 13px; } /* @@ -1038,15 +978,15 @@ onUnmounted(() => { .customer-side-rail__dock { align-items: center; - gap: 6px; - padding: 8px 0 10px; + gap: 4px; + padding: 6px 0 8px; } - .customer-side-rail__user-row { + .customer-side-rail__toolbar { width: 40px; flex-direction: column; align-items: center; - justify-content: center; + gap: 2px; padding: 2px; background: transparent; box-shadow: none; @@ -1058,57 +998,81 @@ onUnmounted(() => { } .customer-side-rail__user { - width: 36px; + width: 30px; + flex: 0 0 auto; + } + + .customer-side-rail__message-slot { + width: 30px; flex: 0 0 auto; - :deep(.user-dropdown-trigger), - :deep(.user-dropdown-trigger.is-compact) { - width: 36px; - height: 36px; - margin: 0; + :deep(.message-dropdown-trigger) { + width: 30px; + max-width: none; + height: 28px; padding: 0; - justify-content: center; + background: transparent; + } + + :deep(.message-count) { + display: none; } } - .customer-side-rail__user-balance { - display: none; - } - - .customer-side-rail__utils { - width: 40px; - grid-template-columns: 1fr; - gap: 2px; - justify-items: center; - padding: 2px 0; - background: transparent; - } - - .customer-side-rail__tool, - .customer-side-rail__tool-slot { - width: 36px; - height: 32px; - } - - .customer-side-rail__tool-slot :deep(.message-count) { - display: none; + .customer-side-rail__tool { + width: 30px; + height: 28px; } .customer-side-rail__footer-row { width: 40px; flex-direction: column; align-items: center; - gap: 4px; + gap: 2px; + } + + .customer-side-rail__warehouse-group { + width: 36px; + flex: 0 0 auto; + flex-direction: column; + align-items: center; + gap: 0; } .customer-side-rail__warehouse { - width: 32px; + width: 36px; flex: 0 0 auto; + + :deep(.contact-service--sider .contact-service__label) { + width: 36px; + min-width: 36px; + height: 28px; + min-height: 28px; + padding: 0; + border-radius: 6px; + justify-content: center; + text-align: center; + } + + :deep(.contact-service--sider .contact-service__text) { + flex: none; + width: 100%; + text-align: center; + } + } + + .customer-side-rail__warehouse-view { + width: 28px; + height: 24px; + margin-left: 0; + border-radius: 6px; + background: transparent; } .customer-side-rail__toggle { - width: 32px; - height: 32px; + width: 28px; + height: 28px; + padding: 0; } } } diff --git a/src/components/top-nav/RightTopMessage.vue b/src/components/top-nav/RightTopMessage.vue index b004487..b0c6028 100644 --- a/src/components/top-nav/RightTopMessage.vue +++ b/src/components/top-nav/RightTopMessage.vue @@ -1,12 +1,19 @@ @@ -2511,6 +2553,14 @@ :order-ids="stockBindOrderIds" @success="handleStockBindSuccess" /> +