fix: 登录页签残留、表单校验脱节与仓库入口合并
All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m30s
All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m30s
排除登录路由进页签;FormPro 内部 model 优先;侧栏仓库选择与地址弹窗合并并可切换。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
3642d650db
commit
3bfe6c6a26
@ -223,6 +223,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-form
|
<a-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
|
v-bind="$attrs"
|
||||||
v-model="formValue"
|
v-model="formValue"
|
||||||
:label-col-props="labelColProps"
|
:label-col-props="labelColProps"
|
||||||
:layout="formLayout"
|
:layout="formLayout"
|
||||||
@ -230,7 +231,6 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:wrapper-col-props="wrapperColProps"
|
:wrapper-col-props="wrapperColProps"
|
||||||
class="form-pro-surface"
|
class="form-pro-surface"
|
||||||
v-bind="$attrs"
|
|
||||||
>
|
>
|
||||||
<a-row
|
<a-row
|
||||||
v-if="layout === 'default' || formLayout === 'vertical'"
|
v-if="layout === 'default' || formLayout === 'vertical'"
|
||||||
|
|||||||
@ -120,6 +120,30 @@ async function loadCustomerCode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncWarehouseOptions(rows: WarehouseDto[]) {
|
||||||
|
warehouseStore.setOptions(
|
||||||
|
rows
|
||||||
|
.filter((row) => row.id != null)
|
||||||
|
.map((row) => ({
|
||||||
|
label: row.warehouseName || String(row.id),
|
||||||
|
value: Number(row.id),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectWarehouse(item: WarehouseDto) {
|
||||||
|
const id = Number(item.id)
|
||||||
|
if (!id) return
|
||||||
|
if (id === currentId.value) return
|
||||||
|
syncWarehouseOptions(list.value)
|
||||||
|
warehouseStore.setWarehouseId(id)
|
||||||
|
Message.success(
|
||||||
|
t('nav.warehousePeek.switched', {
|
||||||
|
name: item.warehouseName || String(id),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async function loadList() {
|
async function loadList() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@ -128,6 +152,7 @@ async function loadList() {
|
|||||||
loadCustomerCode(),
|
loadCustomerCode(),
|
||||||
])
|
])
|
||||||
list.value = (res.items ?? []).map(normalize).sort(compareWarehouseSort)
|
list.value = (res.items ?? []).map(normalize).sort(compareWarehouseSort)
|
||||||
|
syncWarehouseOptions(list.value)
|
||||||
} catch {
|
} catch {
|
||||||
list.value = []
|
list.value = []
|
||||||
} finally {
|
} finally {
|
||||||
@ -171,6 +196,7 @@ watch(
|
|||||||
{{ t('nav.warehousePeek.tip') }}
|
{{ t('nav.warehousePeek.tip') }}
|
||||||
<span v-if="customerCode" class="peek-tip__code">{{ customerCode }}</span>
|
<span v-if="customerCode" class="peek-tip__code">{{ customerCode }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
<p class="peek-hint">{{ t('nav.warehousePeek.selectHint') }}</p>
|
||||||
|
|
||||||
<a-spin :loading="loading" class="peek-spin">
|
<a-spin :loading="loading" class="peek-spin">
|
||||||
<div v-if="!orderedList.length && !loading" class="peek-empty">
|
<div v-if="!orderedList.length && !loading" class="peek-empty">
|
||||||
@ -182,6 +208,12 @@ watch(
|
|||||||
:key="String(item.id)"
|
:key="String(item.id)"
|
||||||
class="peek-card"
|
class="peek-card"
|
||||||
:class="{ 'is-current': Number(item.id) === currentId }"
|
:class="{ 'is-current': Number(item.id) === currentId }"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
:aria-pressed="Number(item.id) === currentId"
|
||||||
|
@click="selectWarehouse(item)"
|
||||||
|
@keydown.enter.prevent="selectWarehouse(item)"
|
||||||
|
@keydown.space.prevent="selectWarehouse(item)"
|
||||||
>
|
>
|
||||||
<header class="peek-card__head">
|
<header class="peek-card__head">
|
||||||
<div class="peek-card__title">
|
<div class="peek-card__title">
|
||||||
@ -191,43 +223,53 @@ watch(
|
|||||||
{{ t('nav.warehousePeek.current') }}
|
{{ t('nav.warehousePeek.current') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<div class="peek-card__actions" @click.stop>
|
||||||
type="button"
|
<button
|
||||||
class="peek-card__copy-all"
|
v-if="Number(item.id) !== currentId"
|
||||||
:title="t('system.warehouseAddress.actions.copy')"
|
type="button"
|
||||||
@click="copyItem(item)"
|
class="peek-card__use"
|
||||||
>
|
@click="selectWarehouse(item)"
|
||||||
<icon-copy />
|
>
|
||||||
<span>{{ t('nav.warehousePeek.copyAll') }}</span>
|
{{ t('nav.warehousePeek.useThis') }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="peek-card__copy-all"
|
||||||
|
:title="t('system.warehouseAddress.actions.copy')"
|
||||||
|
@click="copyItem(item)"
|
||||||
|
>
|
||||||
|
<icon-copy />
|
||||||
|
<span>{{ t('nav.warehousePeek.copyAll') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="peek-card__rows">
|
<div class="peek-card__rows">
|
||||||
<div class="peek-row">
|
<div class="peek-row">
|
||||||
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.recipient') }}</span>
|
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.recipient') }}</span>
|
||||||
<span class="peek-row__value" :title="displayRecipient(item)">{{ displayRecipient(item) }}</span>
|
<span class="peek-row__value" :title="displayRecipient(item)">{{ displayRecipient(item) }}</span>
|
||||||
<button type="button" class="peek-row__copy" @click="copyField(displayRecipient(item))">
|
<button type="button" class="peek-row__copy" @click.stop="copyField(displayRecipient(item))">
|
||||||
<icon-copy />
|
<icon-copy />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="peek-row">
|
<div class="peek-row">
|
||||||
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.contactPhone') }}</span>
|
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.contactPhone') }}</span>
|
||||||
<span class="peek-row__value" :title="item.contactPhone || '-'">{{ item.contactPhone || '-' }}</span>
|
<span class="peek-row__value" :title="item.contactPhone || '-'">{{ item.contactPhone || '-' }}</span>
|
||||||
<button type="button" class="peek-row__copy" @click="copyField(item.contactPhone || '')">
|
<button type="button" class="peek-row__copy" @click.stop="copyField(item.contactPhone || '')">
|
||||||
<icon-copy />
|
<icon-copy />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="peek-row peek-row--address">
|
<div class="peek-row peek-row--address">
|
||||||
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.shippingAddress') }}</span>
|
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.shippingAddress') }}</span>
|
||||||
<span class="peek-row__value" :title="displayAddress(item)">{{ displayAddress(item) }}</span>
|
<span class="peek-row__value" :title="displayAddress(item)">{{ displayAddress(item) }}</span>
|
||||||
<button type="button" class="peek-row__copy" @click="copyField(displayAddress(item))">
|
<button type="button" class="peek-row__copy" @click.stop="copyField(displayAddress(item))">
|
||||||
<icon-copy />
|
<icon-copy />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="item.remark" class="peek-row">
|
<div v-if="item.remark" class="peek-row">
|
||||||
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.remark') }}</span>
|
<span class="peek-row__label">{{ t('system.warehouseAddress.fields.remark') }}</span>
|
||||||
<span class="peek-row__value" :title="item.remark">{{ item.remark }}</span>
|
<span class="peek-row__value" :title="item.remark">{{ item.remark }}</span>
|
||||||
<button type="button" class="peek-row__copy" @click="copyField(item.remark || '')">
|
<button type="button" class="peek-row__copy" @click.stop="copyField(item.remark || '')">
|
||||||
<icon-copy />
|
<icon-copy />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -239,7 +281,7 @@ watch(
|
|||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.peek-tip {
|
.peek-tip {
|
||||||
margin: 0 0 12px;
|
margin: 0 0 6px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: color-mix(in srgb, var(--app-primary-color, #1d7281) 8%, #fff);
|
background: color-mix(in srgb, var(--app-primary-color, #1d7281) 8%, #fff);
|
||||||
@ -256,6 +298,13 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.peek-hint {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
.peek-spin {
|
.peek-spin {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -276,15 +325,22 @@ watch(
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--color-border-2, #e5e6eb);
|
border: 1px solid var(--color-border-2, #e5e6eb);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.14s ease, background-color 0.14s ease, box-shadow 0.14s ease;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: color-mix(in srgb, var(--app-primary-color, #1d7281) 28%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
&.is-current {
|
&.is-current {
|
||||||
border-color: color-mix(in srgb, var(--app-primary-color, #1d7281) 35%, transparent);
|
border-color: color-mix(in srgb, var(--app-primary-color, #1d7281) 35%, transparent);
|
||||||
background: color-mix(in srgb, var(--app-primary-color, #1d7281) 5%, #fff);
|
background: color-mix(in srgb, var(--app-primary-color, #1d7281) 5%, #fff);
|
||||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--app-primary-color, #1d7281) 12%, transparent);
|
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--app-primary-color, #1d7281) 12%, transparent);
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__head {
|
&__head {
|
||||||
@ -295,6 +351,29 @@ watch(
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__use {
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 8px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--app-primary-color, #1d7281);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -129,26 +129,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="customer-side-rail__footer-row">
|
<div class="customer-side-rail__footer-row">
|
||||||
<div class="customer-side-rail__warehouse-group">
|
<button
|
||||||
<div class="customer-side-rail__warehouse">
|
type="button"
|
||||||
<ContactService
|
class="customer-side-rail__warehouse-btn"
|
||||||
variant="sider"
|
:class="{ 'is-compact': compactLayout }"
|
||||||
dropdown-position="tl"
|
:title="warehouseDockTitle"
|
||||||
trigger="click"
|
:aria-label="warehouseDockTitle"
|
||||||
content-class="csr-dock-dropdown"
|
@click="openWarehouseAddressPeek"
|
||||||
:compact="compactLayout"
|
>
|
||||||
/>
|
<span class="customer-side-rail__warehouse-btn-text">{{ warehouseDockLabel }}</span>
|
||||||
</div>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="customer-side-rail__tool customer-side-rail__warehouse-view"
|
|
||||||
:title="t('nav.warehousePeek.title')"
|
|
||||||
:aria-label="t('nav.warehousePeek.title')"
|
|
||||||
@click="openWarehouseAddressPeek"
|
|
||||||
>
|
|
||||||
<icon-eye />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="customer-side-rail__toggle"
|
class="customer-side-rail__toggle"
|
||||||
@ -175,10 +165,11 @@ import { Message } from '@arco-design/web-vue'
|
|||||||
import {
|
import {
|
||||||
IconDoubleLeft,
|
IconDoubleLeft,
|
||||||
IconDoubleRight,
|
IconDoubleRight,
|
||||||
IconEye,
|
|
||||||
IconPrinter,
|
IconPrinter,
|
||||||
} from '@arco-design/web-vue/es/icon'
|
} from '@arco-design/web-vue/es/icon'
|
||||||
|
import { useCurrentWarehouseStore } from '@/store'
|
||||||
import { useRbacUserStore } from '@/store/modules/rbac-user'
|
import { useRbacUserStore } from '@/store/modules/rbac-user'
|
||||||
|
import { getWarehouseList } from '@/api/system/warehouse'
|
||||||
import { getSubMenuIcon } from '@/utils/menu-icon'
|
import { getSubMenuIcon } from '@/utils/menu-icon'
|
||||||
import { MENU_NAME_TO_LOCALE_KEY, translateMenuTitle } from '@/utils/menu-i18n'
|
import { MENU_NAME_TO_LOCALE_KEY, translateMenuTitle } from '@/utils/menu-i18n'
|
||||||
import { openWindow, regexUrl } from '@/utils'
|
import { openWindow, regexUrl } from '@/utils'
|
||||||
@ -209,7 +200,6 @@ import {
|
|||||||
} from '@/utils/customer-portal-nav'
|
} from '@/utils/customer-portal-nav'
|
||||||
import { resolvePortalDrawerPanel } from '@/views/user/portal-drawer/types'
|
import { resolvePortalDrawerPanel } from '@/views/user/portal-drawer/types'
|
||||||
import { openPortalSideDrawer } from '@/views/user/portal-drawer/usePortalSideDrawer'
|
import { openPortalSideDrawer } from '@/views/user/portal-drawer/usePortalSideDrawer'
|
||||||
import ContactService from '@/components/ContactService/index.vue'
|
|
||||||
import RightTopMessage from '@/components/top-nav/RightTopMessage.vue'
|
import RightTopMessage from '@/components/top-nav/RightTopMessage.vue'
|
||||||
import RightTopUser from '@/components/top-nav/RightTopUser.vue'
|
import RightTopUser from '@/components/top-nav/RightTopUser.vue'
|
||||||
import WarehouseAddressPeekModal from '@/components/customer-side-rail/WarehouseAddressPeekModal.vue'
|
import WarehouseAddressPeekModal from '@/components/customer-side-rail/WarehouseAddressPeekModal.vue'
|
||||||
@ -218,6 +208,7 @@ const { t, te, locale } = useI18n()
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const rbacUserStore = useRbacUserStore()
|
const rbacUserStore = useRbacUserStore()
|
||||||
|
const warehouseStore = useCurrentWarehouseStore()
|
||||||
const printConnectionState = useHiprintClientState()
|
const printConnectionState = useHiprintClientState()
|
||||||
|
|
||||||
/** 与 CSS transition 时长一致(Linear 风格约 300ms) */
|
/** 与 CSS transition 时长一致(Linear 风格约 300ms) */
|
||||||
@ -319,10 +310,41 @@ function handleCustomerBalanceChanged() {
|
|||||||
|
|
||||||
const warehousePeekVisible = ref(false)
|
const warehousePeekVisible = ref(false)
|
||||||
|
|
||||||
|
const warehouseDockName = computed(
|
||||||
|
() =>
|
||||||
|
warehouseStore.warehouseName ||
|
||||||
|
t('nav.contactService.selectWarehouse'),
|
||||||
|
)
|
||||||
|
|
||||||
|
const warehouseDockLabel = computed(() => {
|
||||||
|
const name = warehouseDockName.value
|
||||||
|
if (compactLayout.value) return name.slice(0, 1) || '仓'
|
||||||
|
return name
|
||||||
|
})
|
||||||
|
|
||||||
|
const warehouseDockTitle = computed(
|
||||||
|
() => `${t('nav.warehousePeek.title')}: ${warehouseDockName.value}`,
|
||||||
|
)
|
||||||
|
|
||||||
function openWarehouseAddressPeek() {
|
function openWarehouseAddressPeek() {
|
||||||
warehousePeekVisible.value = true
|
warehousePeekVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ensureWarehouseOptions() {
|
||||||
|
try {
|
||||||
|
const { items } = await getWarehouseList({ page: 1, pageSize: 100 })
|
||||||
|
const list = (items ?? [])
|
||||||
|
.filter((row) => row.id != null)
|
||||||
|
.map((row) => ({
|
||||||
|
label: row.warehouseName || String(row.id),
|
||||||
|
value: Number(row.id),
|
||||||
|
}))
|
||||||
|
warehouseStore.setOptions(list)
|
||||||
|
} catch {
|
||||||
|
/* 侧栏仅展示已缓存仓名,打开弹窗时再拉地址 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function groupLabel(group: CustomerNavGroup) {
|
function groupLabel(group: CustomerNavGroup) {
|
||||||
if (te(group.labelKey)) return t(group.labelKey)
|
if (te(group.labelKey)) return t(group.labelKey)
|
||||||
return group.labelFallback
|
return group.labelFallback
|
||||||
@ -469,6 +491,7 @@ watch(activePath, () => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initHiprintClientState().catch(() => undefined)
|
initHiprintClientState().catch(() => undefined)
|
||||||
|
void ensureWarehouseOptions()
|
||||||
window.addEventListener(CUSTOMER_BALANCE_CHANGED_EVENT, handleCustomerBalanceChanged)
|
window.addEventListener(CUSTOMER_BALANCE_CHANGED_EVENT, handleCustomerBalanceChanged)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -561,41 +584,46 @@ onUnmounted(() => {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__warehouse-group {
|
&__warehouse-btn {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
|
||||||
|
|
||||||
&__warehouse {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
:deep(.contact-service--sider) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.contact-service--sider .contact-service__label) {
|
|
||||||
padding-right: 2px;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__warehouse-view {
|
|
||||||
width: 26px;
|
|
||||||
height: 30px;
|
height: 30px;
|
||||||
margin-left: -1px;
|
padding: 0 8px;
|
||||||
border-radius: 0 6px 6px 0;
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
background: rgba(255, 255, 255, 0.55);
|
background: rgba(255, 255, 255, 0.55);
|
||||||
color: var(--color-text-3);
|
color: var(--color-text-2);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: color 0.14s ease, background-color 0.14s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--app-primary-color);
|
color: var(--app-primary-color, #1d7281);
|
||||||
background: rgba(255, 255, 255, 0.92);
|
background: rgba(255, 255, 255, 0.92);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-compact {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 36px;
|
||||||
|
min-width: 36px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warehouse-btn-text {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__message-slot {
|
&__message-slot {
|
||||||
@ -1031,41 +1059,14 @@ onUnmounted(() => {
|
|||||||
gap: 2px;
|
gap: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-side-rail__warehouse-group {
|
.customer-side-rail__warehouse-btn {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
|
min-width: 36px;
|
||||||
|
height: 28px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
flex-direction: column;
|
padding: 0;
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
gap: 0;
|
text-align: center;
|
||||||
}
|
|
||||||
|
|
||||||
.customer-side-rail__warehouse {
|
|
||||||
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;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -352,9 +352,8 @@
|
|||||||
if (nextRoute.query?._refresh || nextRoute.query?._r) {
|
if (nextRoute.query?._refresh || nextRoute.query?._r) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!nextRoute.meta.noAffix) {
|
// 登录/官网等独立壳不进页签;具体过滤在 updateTabList
|
||||||
tabBarStore.updateTabList(nextRoute);
|
tabBarStore.updateTabList(nextRoute);
|
||||||
}
|
|
||||||
nextTick(() => scrollActiveTagIntoView());
|
nextTick(() => scrollActiveTagIntoView());
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
|||||||
@ -1699,6 +1699,9 @@ export default {
|
|||||||
'nav.userMenu.switchLanguage': 'Switch to {lang}',
|
'nav.userMenu.switchLanguage': 'Switch to {lang}',
|
||||||
'nav.warehousePeek.title': 'Warehouse address',
|
'nav.warehousePeek.title': 'Warehouse address',
|
||||||
'nav.warehousePeek.tip': 'Put your customer code on the parcel for warehouse intake',
|
'nav.warehousePeek.tip': 'Put your customer code on the parcel for warehouse intake',
|
||||||
|
'nav.warehousePeek.selectHint': 'Click a card to switch the shipping warehouse',
|
||||||
|
'nav.warehousePeek.useThis': 'Use',
|
||||||
|
'nav.warehousePeek.switched': 'Switched to {name}',
|
||||||
'nav.warehousePeek.current': 'Current',
|
'nav.warehousePeek.current': 'Current',
|
||||||
'nav.warehousePeek.copyAll': 'Copy',
|
'nav.warehousePeek.copyAll': 'Copy',
|
||||||
'nav.warehousePeek.empty': 'No warehouse address yet',
|
'nav.warehousePeek.empty': 'No warehouse address yet',
|
||||||
|
|||||||
@ -1455,6 +1455,9 @@ export default {
|
|||||||
'nav.userMenu.switchLanguage': '切换到{lang}',
|
'nav.userMenu.switchLanguage': '切换到{lang}',
|
||||||
'nav.warehousePeek.title': '仓库地址',
|
'nav.warehousePeek.title': '仓库地址',
|
||||||
'nav.warehousePeek.tip': '寄件请备注客户编号,便于仓库认领',
|
'nav.warehousePeek.tip': '寄件请备注客户编号,便于仓库认领',
|
||||||
|
'nav.warehousePeek.selectHint': '点击卡片可切换当前发货仓库',
|
||||||
|
'nav.warehousePeek.useThis': '选用',
|
||||||
|
'nav.warehousePeek.switched': '已切换为 {name}',
|
||||||
'nav.warehousePeek.current': '当前',
|
'nav.warehousePeek.current': '当前',
|
||||||
'nav.warehousePeek.copyAll': '复制',
|
'nav.warehousePeek.copyAll': '复制',
|
||||||
'nav.warehousePeek.empty': '暂无仓库地址',
|
'nav.warehousePeek.empty': '暂无仓库地址',
|
||||||
|
|||||||
@ -33,6 +33,8 @@ const router = createRouter({
|
|||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
loginPortal: 'customer',
|
loginPortal: 'customer',
|
||||||
|
noAffix: true,
|
||||||
|
hideInMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -42,6 +44,8 @@ const router = createRouter({
|
|||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
loginPortal: 'admin',
|
loginPortal: 'admin',
|
||||||
|
noAffix: true,
|
||||||
|
hideInMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -61,6 +65,7 @@ const router = createRouter({
|
|||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
|
noAffix: true,
|
||||||
customerPortalOnly: true,
|
customerPortalOnly: true,
|
||||||
locale: 'h5.login.title',
|
locale: 'h5.login.title',
|
||||||
},
|
},
|
||||||
@ -73,6 +78,7 @@ const router = createRouter({
|
|||||||
meta: {
|
meta: {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
|
noAffix: true,
|
||||||
adminPortalOnly: true,
|
adminPortalOnly: true,
|
||||||
locale: 'h5Admin.login.title',
|
locale: 'h5Admin.login.title',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { isString } from '@/utils/is';
|
|||||||
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
||||||
import { buildMenuTitleMaps, resolveMenuTitleByPath, resolveMenuTitleFromTree } from '@/utils/menu-display';
|
import { buildMenuTitleMaps, resolveMenuTitleByPath, resolveMenuTitleFromTree } from '@/utils/menu-display';
|
||||||
import { translateMenuTitleByPath } from '@/utils/menu-i18n';
|
import { translateMenuTitleByPath } from '@/utils/menu-i18n';
|
||||||
|
import { isLoginRouteName } from '@/utils/login-portal';
|
||||||
import i18n from '@/locale';
|
import i18n from '@/locale';
|
||||||
import type { MenuTreeDto } from '@/api/rbac';
|
import type { MenuTreeDto } from '@/api/rbac';
|
||||||
import { TabBarState, TagProps } from './types';
|
import { TabBarState, TagProps } from './types';
|
||||||
@ -56,7 +57,19 @@ const formatTag = (route: RouteLocationNormalized): TagProps => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const BAN_LIST = [REDIRECT_ROUTE_NAME];
|
const BAN_LIST = [REDIRECT_ROUTE_NAME, 'H5Login', 'H5AdminLogin', 'siteRoot'];
|
||||||
|
|
||||||
|
function shouldSkipTabRoute(route: RouteLocationNormalized): boolean {
|
||||||
|
const name = route.name != null ? String(route.name) : '';
|
||||||
|
if (!name) return true;
|
||||||
|
if (BAN_LIST.includes(name)) return true;
|
||||||
|
if (isLoginRouteName(name)) return true;
|
||||||
|
if (route.meta?.noAffix) return true;
|
||||||
|
if (route.meta?.requiresAuth === false && (route.path === '/login' || route.path === '/admin')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const useTabBarStore = defineStore('tabBar', {
|
const useTabBarStore = defineStore('tabBar', {
|
||||||
state: (): TabBarState => ({
|
state: (): TabBarState => ({
|
||||||
@ -104,7 +117,11 @@ const useTabBarStore = defineStore('tabBar', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateTabList(route: RouteLocationNormalized) {
|
updateTabList(route: RouteLocationNormalized) {
|
||||||
if (BAN_LIST.includes(route.name as string)) return;
|
if (shouldSkipTabRoute(route)) return;
|
||||||
|
// 登录页曾被误写入时清掉,避免登录成功后仍挂着「login」页签
|
||||||
|
this.tagList = this.tagList.filter(
|
||||||
|
(tag) => !isLoginRouteName(tag.name) && tag.name !== 'H5Login' && tag.name !== 'H5AdminLogin',
|
||||||
|
);
|
||||||
const next = formatTag(route);
|
const next = formatTag(route);
|
||||||
const idx = this.tagList.findIndex(
|
const idx = this.tagList.findIndex(
|
||||||
(tag) =>
|
(tag) =>
|
||||||
@ -131,6 +148,13 @@ const useTabBarStore = defineStore('tabBar', {
|
|||||||
/** 菜单/标签点击立刻高亮,不等路由 afterEach */
|
/** 菜单/标签点击立刻高亮,不等路由 afterEach */
|
||||||
previewNavigate(tag: TagProps) {
|
previewNavigate(tag: TagProps) {
|
||||||
if (!tag?.fullPath && !tag?.name) return;
|
if (!tag?.fullPath && !tag?.name) return;
|
||||||
|
if (
|
||||||
|
isLoginRouteName(tag.name) ||
|
||||||
|
tag.name === 'H5Login' ||
|
||||||
|
tag.name === 'H5AdminLogin'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.previewingPath = tag.fullPath || '';
|
this.previewingPath = tag.fullPath || '';
|
||||||
this.previewingName = tag.name || '';
|
this.previewingName = tag.name || '';
|
||||||
const exists = this.tagList.some(
|
const exists = this.tagList.some(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { RouteLocationRaw } from 'vue-router';
|
import type { RouteLocationRaw } from 'vue-router';
|
||||||
import { isNavigationFailure } from 'vue-router';
|
import { isNavigationFailure } from 'vue-router';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { useUserStore } from '@/store';
|
import { useUserStore, useTabBarStore } from '@/store';
|
||||||
import { ensureDynamicRouterReady } from '@/router/ensure-dynamic-router';
|
import { ensureDynamicRouterReady } from '@/router/ensure-dynamic-router';
|
||||||
import { ensureHeavyPlugins } from '@/plugins/heavy-plugins';
|
import { ensureHeavyPlugins } from '@/plugins/heavy-plugins';
|
||||||
import { ensureFullLocaleMessages } from '@/locale';
|
import { ensureFullLocaleMessages } from '@/locale';
|
||||||
@ -63,6 +63,8 @@ export async function enterSystemAfterLogin(options: {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
skipNextPageTransition();
|
skipNextPageTransition();
|
||||||
|
// 登录壳不进业务页签;进系统前清空,避免残留「login」标签
|
||||||
|
useTabBarStore().resetTabList();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await router.replace({ ...options.target } as RouteLocationRaw);
|
await router.replace({ ...options.target } as RouteLocationRaw);
|
||||||
|
|||||||
@ -168,9 +168,9 @@ async function handleSubmit() {
|
|||||||
{{ t('order.waybillManage.changeStatus.modalTip') }}
|
{{ t('order.waybillManage.changeStatus.modalTip') }}
|
||||||
</a-alert>
|
</a-alert>
|
||||||
|
|
||||||
|
<!-- 勿传 :model:FormPro 用内部 formValue;attrs 会覆盖导致输入与校验脱节 -->
|
||||||
<FormPro
|
<FormPro
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="formState"
|
|
||||||
:item-list="itemList"
|
:item-list="itemList"
|
||||||
:row-gutter="16"
|
:row-gutter="16"
|
||||||
:default-span="24"
|
:default-span="24"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user