diff --git a/.dockerignore b/.dockerignore index 5674289..332da2e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -25,6 +25,7 @@ xhs-agent-desktop .env* !.env.production +!.env.ideship-test # 本地简易镜像另用 Dockerfile;k3s 构建用 Dockerfile.k3s Dockerfile diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index a1cc650..0906f6e 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -2,6 +2,7 @@ # push master → prod;push test → test # 构建始终用 prod kubeconfig(Kaniko);仅 deploy 切集群 # 前端镜像:test 分支须传 FRONT_BUILD_SCRIPT=build:test,否则会打进正式 API 域名 +# 构建变慢时:确认宿主机 ci-publish-front.sh 已给 Kaniko --cache=true(lock 不变时应跳过 pnpm install) name: Build and Deploy Front on: diff --git a/Dockerfile.k3s b/Dockerfile.k3s index 88fa85e..97e3896 100644 --- a/Dockerfile.k3s +++ b/Dockerfile.k3s @@ -5,23 +5,39 @@ # prod 默认 build(.env.production → api.ideship.com) # test 传 build:test(.env.ideship-test → api.test.ideship.com) # 即使误用 production 构建,运行时也会按 hostname 纠正到测试 API。 +# +# 构建耗时提示(宿主机 ci-publish-front.sh): +# 务必给 Kaniko 开 --cache=true(及 cache-repo / cache-ttl), +# 否则每次都会全量 pnpm install + vite build(本地 vite alone 约 3~4 分钟)。 + +# ---------- 运行时 serve:与源码无关,Kaniko 有 cache 时几乎秒过 ---------- +FROM docker.m.daocloud.io/library/node:20-alpine AS serve-runtime +RUN npm config set registry https://registry.npmmirror.com \ + && npm install -g serve@14.2.4 + +# ---------- 前端构建 ---------- FROM docker.m.daocloud.io/library/node:20-alpine AS build WORKDIR /app ARG FRONT_BUILD_SCRIPT=build -RUN npm config set registry https://registry.npmmirror.com \ - && npm install -g pnpm@9.15.9 + +# corepack 启用内置 pnpm,避免每次 npm install -g +RUN corepack enable \ + && corepack prepare pnpm@9.15.9 --activate \ + && pnpm config set registry https://registry.npmmirror.com + COPY package.json pnpm-lock.yaml ./ # --ignore-scripts:跳过 gifsicle/optipng 等原生编译(需 autoreconf;vite 未启用 imagemin) -RUN pnpm config set registry https://registry.npmmirror.com \ - && pnpm install --frozen-lockfile --ignore-scripts +# 先只装依赖,lock 不变时可命中 Kaniko 层缓存(最大收益) +RUN pnpm install --frozen-lockfile --ignore-scripts + COPY . . RUN pnpm run ${FRONT_BUILD_SCRIPT} -# 仅静态托管 + SPA fallback;不再内置 nginx(网关层 Traefik 负责 HTTPS) +# ---------- 最终镜像:只拷 serve + dist ---------- FROM docker.m.daocloud.io/library/node:20-alpine WORKDIR /app -RUN npm config set registry https://registry.npmmirror.com \ - && npm install -g serve@14.2.4 +COPY --from=serve-runtime /usr/local/lib/node_modules/serve /usr/local/lib/node_modules/serve +COPY --from=serve-runtime /usr/local/bin/serve /usr/local/bin/serve COPY --from=build /app/dist ./dist EXPOSE 80 CMD ["serve", "-s", "dist", "-l", "80"] diff --git a/src/App.vue b/src/App.vue index 2d6db0f..075f1e1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,7 +2,7 @@
portalTransition.value === 'oms-page-flip'); + const isPageIris = computed(() => portalTransition.value === 'oms-iris'); + + const IRIS_MS = 1000; + const IRIS_EASE = 'cubic-bezier(0.22, 1, 0.36, 1)'; function clearPortalTransition() { portalTransition.value = ''; @@ -57,9 +60,7 @@ node.style.width = '100%'; node.style.minHeight = '100%'; node.style.zIndex = String(z); - node.style.transformStyle = 'preserve-3d'; - node.style.backfaceVisibility = 'hidden'; - node.style.willChange = 'transform, opacity, filter'; + node.style.willChange = 'clip-path, opacity, filter'; } function clearSheetStyles(node: HTMLElement) { @@ -68,122 +69,68 @@ node.style.width = ''; node.style.minHeight = ''; node.style.zIndex = ''; - node.style.transformStyle = ''; - node.style.backfaceVisibility = ''; node.style.willChange = ''; node.style.transform = ''; - node.style.transformOrigin = ''; node.style.filter = ''; node.style.opacity = ''; - node.style.boxShadow = ''; + node.style.clipPath = ''; + node.style.removeProperty('-webkit-clip-path'); } /** - * 真翻书:书脊在左固定,右上角往左掀。 - * 多段缓动;新页在底下同步亮起。 + * 中心虹膜:旧页留底层轻压暗;新页 clip-path 从中心向外展开。 */ - function playBookFlipLeave(node: HTMLElement, done: () => void) { - prepareSheet(node, 4); - node.style.transformOrigin = '0% 22%'; - node.style.boxShadow = '-8px 12px 28px rgba(8, 18, 28, 0.16)'; - - const shade = document.createElement('div'); - shade.className = 'oms-flip-shade'; - node.appendChild(shade); - + function playIrisLeave(node: HTMLElement, done: () => void) { + prepareSheet(node, 1); const anim = node.animate( [ - { - transform: 'rotateY(0deg)', - filter: 'brightness(1)', - opacity: 1, - boxShadow: '-6px 10px 24px rgba(8, 18, 28, 0.12)', - offset: 0, - }, - { - transform: 'rotateY(-8deg)', - filter: 'brightness(0.99)', - opacity: 1, - boxShadow: '-10px 12px 28px rgba(8, 18, 28, 0.16)', - offset: 0.18, - }, - { - transform: 'rotateY(-28deg)', - filter: 'brightness(0.97)', - opacity: 1, - boxShadow: '-16px 14px 34px rgba(8, 18, 28, 0.2)', - offset: 0.36, - }, - { - transform: 'rotateY(-58deg)', - filter: 'brightness(0.94)', - opacity: 1, - boxShadow: '-22px 12px 40px rgba(8, 18, 28, 0.24)', - offset: 0.55, - }, - { - transform: 'rotateY(-98deg)', - filter: 'brightness(0.9)', - opacity: 0.92, - boxShadow: '-26px 10px 44px rgba(8, 18, 28, 0.26)', - offset: 0.74, - }, - { - transform: 'rotateY(-138deg)', - filter: 'brightness(0.84)', - opacity: 0.7, - boxShadow: '-18px 8px 32px rgba(8, 18, 28, 0.18)', - offset: 0.9, - }, - { - transform: 'rotateY(-162deg)', - filter: 'brightness(0.78)', - opacity: 0.28, - boxShadow: '-6px 6px 18px rgba(8, 18, 28, 0.1)', - offset: 1, - }, + { opacity: 1, filter: 'brightness(1)', offset: 0 }, + { opacity: 0.92, filter: 'brightness(0.94)', offset: 0.45 }, + { opacity: 0.82, filter: 'brightness(0.88)', offset: 1 }, ], { - duration: 1480, - easing: 'cubic-bezier(0.33, 0.0, 0.2, 1)', + duration: IRIS_MS, + easing: IRIS_EASE, fill: 'forwards', }, ); - const shadeAnim = shade.animate( - [ - { opacity: 0, offset: 0 }, - { opacity: 0.25, offset: 0.18 }, - { opacity: 0.55, offset: 0.45 }, - { opacity: 0.7, offset: 0.7 }, - { opacity: 0.2, offset: 1 }, - ], - { - duration: 1480, - easing: 'cubic-bezier(0.33, 0.0, 0.2, 1)', - fill: 'forwards', - }, - ); - - Promise.all([anim.finished, shadeAnim.finished]) + anim.finished .catch(() => undefined) .finally(() => { - shade.remove(); done(); }); } - function playBookFlipEnter(node: HTMLElement, done: () => void) { - prepareSheet(node, 1); + function playIrisEnter(node: HTMLElement, done: () => void) { + prepareSheet(node, 4); + node.style.clipPath = 'circle(0% at 50% 50%)'; + node.style.setProperty('-webkit-clip-path', 'circle(0% at 50% 50%)'); + const anim = node.animate( [ - { opacity: 0.88, filter: 'brightness(0.9)', offset: 0 }, - { opacity: 0.94, filter: 'brightness(0.94)', offset: 0.45 }, - { opacity: 1, filter: 'brightness(1)', offset: 1 }, + { + clipPath: 'circle(0% at 50% 50%)', + opacity: 1, + filter: 'brightness(0.98)', + offset: 0, + }, + { + clipPath: 'circle(42% at 50% 50%)', + opacity: 1, + filter: 'brightness(1)', + offset: 0.42, + }, + { + clipPath: 'circle(150% at 50% 50%)', + opacity: 1, + filter: 'brightness(1)', + offset: 1, + }, ], { - duration: 1480, - easing: 'cubic-bezier(0.33, 0.0, 0.2, 1)', + duration: IRIS_MS, + easing: IRIS_EASE, fill: 'forwards', }, ); @@ -197,19 +144,19 @@ } function onPortalLeave(el: Element, done: () => void) { - if (portalTransition.value !== 'oms-page-flip') { + if (portalTransition.value !== 'oms-iris') { done(); return; } - playBookFlipLeave(el as HTMLElement, done); + playIrisLeave(el as HTMLElement, done); } function onPortalEnter(el: Element, done: () => void) { - if (portalTransition.value !== 'oms-page-flip') { + if (portalTransition.value !== 'oms-iris') { done(); return; } - playBookFlipEnter(el as HTMLElement, done); + playIrisEnter(el as HTMLElement, done); } function isStandalonePortalPath(path: string) { @@ -252,7 +199,7 @@ const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if ((armed || siteToLogin || loginPortalSwitch) && !reduceMotion) { - portalTransition.value = 'oms-page-flip'; + portalTransition.value = 'oms-iris'; } else { portalTransition.value = ''; } @@ -317,36 +264,12 @@ html body .main-menu .menu-text { min-height: 100%; } -.app-portal-stage--flip { +.app-portal-stage--iris { position: relative; - perspective: 2600px; - perspective-origin: 78% 16%; overflow: hidden; min-height: 100vh; min-height: 100dvh; - background: #0b1620; -} - -.oms-flip-shade { - position: absolute; - inset: 0; - z-index: 20; - pointer-events: none; - background: - linear-gradient( - 90deg, - rgba(0, 0, 0, 0.28) 0%, - rgba(0, 0, 0, 0.1) 14%, - rgba(0, 0, 0, 0.02) 28%, - transparent 46% - ), - linear-gradient( - 215deg, - rgba(255, 255, 255, 0.22) 0%, - rgba(255, 255, 255, 0.05) 18%, - transparent 42% - ); - mix-blend-mode: multiply; - opacity: 0; + /* 与登录页灰底一致,避免虹膜展开时露黑缝 */ + background: var(--app-page-bg, #f0f3f4); } diff --git a/src/api/order/index.ts b/src/api/order/index.ts index 9938966..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 @@ -1742,6 +1761,50 @@ export async function getOrderPriceCheckShippingOptions(): Promise item.products.length > 0) } +/** 查价表单下拉(匿名可读):国家 / 发货仓 / 货物属性 */ +export interface PriceCheckFormOptions { + countries: Array<{ countryCode: string; chineseName: string; englishName: string }> + warehouses: Array<{ id: number; warehouseName: string }> + cargoAttributes: Array<{ dictCode: string; dictName: string }> +} + +/** GET /order/price-check/form-options */ +export async function getOrderPriceCheckFormOptions(): Promise { + const res = await axiosInstance.get(orderUrl('/order/price-check/form-options')) + const body = res.data + assertBusinessOk(body) + const payload = body?.data ?? body?.Data ?? body ?? {} + const countriesRaw = payload?.countries ?? payload?.Countries ?? [] + const warehousesRaw = payload?.warehouses ?? payload?.Warehouses ?? [] + const cargoRaw = payload?.cargoAttributes ?? payload?.CargoAttributes ?? [] + return { + countries: (Array.isArray(countriesRaw) ? countriesRaw : []) + .map((row: any) => { + const countryCode = String(row?.countryCode ?? row?.CountryCode ?? '') + .trim() + .toUpperCase() + const chineseName = String(row?.chineseName ?? row?.ChineseName ?? '').trim() + const englishName = String(row?.englishName ?? row?.EnglishName ?? '').trim() + return { countryCode, chineseName, englishName } + }) + .filter((c) => c.countryCode), + warehouses: (Array.isArray(warehousesRaw) ? warehousesRaw : []) + .map((row: any) => { + const id = Number(row?.id ?? row?.Id) + const warehouseName = String(row?.warehouseName ?? row?.WarehouseName ?? '').trim() + return { id, warehouseName: warehouseName || String(id) } + }) + .filter((w) => Number.isFinite(w.id) && w.id > 0), + cargoAttributes: (Array.isArray(cargoRaw) ? cargoRaw : []) + .map((row: any) => { + const dictCode = String(row?.dictCode ?? row?.DictCode ?? '').trim() + const dictName = String(row?.dictName ?? row?.DictName ?? '').trim() + return { dictCode, dictName: dictName || dictCode } + }) + .filter((c) => c.dictCode), + } +} + export async function postOrderPriceCheck(params: PriceCheckRequest): Promise<{ items: any[] failures: PriceCheckFailure[] diff --git a/src/api/rbac/axios-config.ts b/src/api/rbac/axios-config.ts index 6486337..191b803 100644 --- a/src/api/rbac/axios-config.ts +++ b/src/api/rbac/axios-config.ts @@ -1,9 +1,8 @@ import axios, { AxiosError, AxiosRequestConfig } from 'axios' import { Message } from '@arco-design/web-vue' -import router from '@/router' -import { OpenAPI, clearAuthToken, OmsLoginType } from './index' +import { OpenAPI } from './index' import { useRbacUserStore } from '@/store/modules/rbac-user' -import { resolvePostLogoutLogin } from '@/utils/login-portal' +import { handleSessionExpired } from '@/utils/auth-session-expired' // 创建自定义 axios 实例 // baseURL 为空;远端由 OpenAPI.BASE / omsUrl 按 env 拼接(本地走 Vite 代理到测试环境) @@ -31,35 +30,6 @@ function onTokenRefreshed(token: string) { refreshSubscribers = [] } -function redirectToLoginPage( - userStore: ReturnType, - loginType?: OmsLoginType | null -) { - const current = router.currentRoute.value - const target = resolvePostLogoutLogin({ - loginType: loginType !== undefined ? loginType : userStore.resolveLoginTypeForRedirect(), - path: current.fullPath, - }) - if (target.path) { - router.replace({ - path: target.path, - query: target.query, - }) - return - } - router.replace({ - name: target.name, - query: target.query, - }) -} - -function logoutAndRedirectToLogin(userStore: ReturnType, message: string) { - const loginType = userStore.resolveLoginTypeForRedirect() - userStore.logout() - Message.error(message) - redirectToLoginPage(userStore, loginType) -} - // 请求拦截器 axiosInstance.interceptors.request.use( async (config: AxiosRequestConfig) => { @@ -152,14 +122,18 @@ axiosInstance.interceptors.response.use( // 401:保活窗内(含 F5 从 lastActivity 恢复的 idle 窗)才允许续期 if (status === 401 && !originalRequest._retry) { + // 访客 / 已登出:不走会话过期跳转(避免 H5 可逛页被踢去登录) + if (!userStore.isLoggedIn) { + return Promise.reject(error) + } if (!userStore.isKeepAliveActive()) { - if (!silentError) logoutAndRedirectToLogin(userStore, '登录已过期,请重新登录') + if (!silentError) handleSessionExpired('登录已过期,请重新登录') return Promise.reject(error) } // 如果没有 refreshToken,直接登出 if (!userStore.refreshToken) { - if (!silentError) logoutAndRedirectToLogin(userStore, '未授权,请重新登录') + if (!silentError) handleSessionExpired('未授权,请重新登录') return Promise.reject(error) } @@ -192,11 +166,11 @@ axiosInstance.interceptors.response.use( return axiosInstance(originalRequest) } else { // 刷新失败,登出 - if (!silentError) logoutAndRedirectToLogin(userStore, '登录已过期,请重新登录') + if (!silentError) handleSessionExpired('登录已过期,请重新登录') return Promise.reject(error) } } catch { - if (!silentError) logoutAndRedirectToLogin(userStore, '登录已过期,请重新登录') + if (!silentError) handleSessionExpired('登录已过期,请重新登录') return Promise.reject(error) } finally { isRefreshing = false @@ -210,7 +184,7 @@ axiosInstance.interceptors.response.use( switch (status) { case 401: // 如果走到这里,说明401没有被上面的逻辑处理(可能是没有retry标记的情况) - logoutAndRedirectToLogin(userStore, '未授权,请重新登录') + handleSessionExpired('未授权,请重新登录') break case 403: Message.error('没有权限访问') 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(() => { > - + + + + + + diff --git a/src/views/order/waybillManage/index.vue b/src/views/order/waybillManage/index.vue index 7a13bda..f7a81c5 100644 --- a/src/views/order/waybillManage/index.vue +++ b/src/views/order/waybillManage/index.vue @@ -87,6 +87,7 @@ isOrderDeletable, isOrderNotShipped, getOrderStatusMeta, + getPostShipStatusChangeTargets, buildOrderStatusTagHtml, buildOrderStatusSubTagHtml, buildBooleanTagHtml, @@ -133,6 +134,9 @@ const StockBindModal = defineAsyncComponent( () => import('../myOrders/components/StockBindModal.vue'), ); + const ChangePostShipStatusModal = defineAsyncComponent( + () => import('./components/ChangePostShipStatusModal.vue'), + ); defineOptions({ name: 'WaybillManage', @@ -579,11 +583,20 @@ const showStockBindAction = computed( () => hasPermission('Order.WaybillManage.StockBind'), ); + const showChangeStatusAction = computed( + () => + hasPermission('Order.WaybillManage.ChangeStatus') && + isActiveTab('all', 'pendingOnline', 'shipping', 'deliverySuccess', 'deliveryFailed'), + ); const stockBindModalVisible = ref(false); const stockBindOrderIds = ref([]); /** 本次打开绑定前是否含直发单(用于成功后提示已转代发) */ const stockBindDidConvert = ref(false); + const changeStatusModalVisible = ref(false); + const changeStatusOrderId = ref(null); + const changeStatusCurrent = ref(''); + const changeStatusWaybillLabel = ref(''); const problematicModalVisible = ref(false); const claimedModalVisible = ref(false); const reallocateModalVisible = ref(false); @@ -1636,6 +1649,29 @@ await openDetailWithOptionalFetch(rows[0], { editBasic: true }); }; + const openChangeStatusModal = () => { + const rows = requireSelectedRows(); + if (!rows.length) return; + if (rows.length > 1) { + Message.warning(t('order.waybillManage.changeStatus.singleOnly')); + return; + } + const row = rows[0]; + const id = getOrderRowId(row); + if (id == null) return; + const status = resolveOrderStatusCode(row); + if (!getPostShipStatusChangeTargets(status).length) { + Message.warning(t('order.waybillManage.changeStatus.notAllowed')); + return; + } + const waybillNo = String(row?.waybillNo ?? row?.WaybillNo ?? '').trim(); + const serviceNo = String(row?.serviceNo ?? row?.ServiceNo ?? '').trim(); + changeStatusOrderId.value = Number(id); + changeStatusCurrent.value = status; + changeStatusWaybillLabel.value = waybillNo || serviceNo || `#${id}`; + changeStatusModalVisible.value = true; + }; + const openProblematicModal = () => { const rows = requireSelectedRows(); if (!rows.length) return; @@ -1799,6 +1835,7 @@ if (key === 'export') return handleExport(); if (key === 'importDownstreamWeight') return openDownstreamWeightImport(); if (key === 'modifyOrder') return handleModifyOrder(); + if (key === 'changeStatus') return openChangeStatusModal(); if (key === 'stockBind') return openStockBindModal(); }; @@ -2319,6 +2356,11 @@ :disabled="selectedRowCount === 0" @click="onMoreActionClick('modifyOrder')" >{{ t('order.myOrders.actions.modifyOrder') }} + {{ t('order.myOrders.actions.changeStatus') }}
@@ -2511,6 +2553,14 @@ :order-ids="stockBindOrderIds" @success="handleStockBindSuccess" /> + diff --git a/src/views/register/index.vue b/src/views/register/index.vue index 42102f9..f5d112c 100644 --- a/src/views/register/index.vue +++ b/src/views/register/index.vue @@ -88,6 +88,8 @@ import { } from '@/utils/advisorInviteStorage' import { stashInviteRef } from '@/utils/inviteRefStorage' import { isTimeoutError, withTimeout } from '@/utils/withTimeout' +import { preferH5Portal } from '@/utils/device' +import { isAdminH5Path } from '@/utils/login-portal' defineOptions({ name: 'CustomerRegister' }) @@ -96,6 +98,27 @@ const REFERRER_TIMEOUT_MS = 15000 const { t } = useI18n() const route = useRoute() const router = useRouter() + +function resolvePostRegisterLogin() { + const redirect = typeof route.query.redirect === 'string' ? route.query.redirect.trim() : '' + const wantH5 = + preferH5Portal() || + (redirect.startsWith('/h5') && !isAdminH5Path(redirect)) + if (wantH5) { + return { + path: '/h5/login', + query: redirect ? { redirect } : undefined, + } + } + return { + name: 'login' as const, + query: redirect + ? { redirect } + : inviteCode.value + ? {} + : undefined, + } +} const formRef = ref() const submitting = ref(false) const inviteCode = ref('') @@ -201,7 +224,7 @@ async function handleSubmit() { clearStashedInviteRef() clearStashedAdvisorInvite() Message.success(t('marketing.register.success')) - router.replace({ name: 'login', query: inviteCode.value ? {} : undefined }) + router.replace(resolvePostRegisterLogin()) } catch (e: any) { Message.error(e?.message || t('marketing.register.failed')) } finally { @@ -210,7 +233,7 @@ async function handleSubmit() { } function goLogin() { - router.push({ name: 'login' }) + router.push(resolvePostRegisterLogin()) } onMounted(() => {