All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m25s
下单/H5 仓库地址展示与排序备注;映射明细加载优化与发布后保留选中;接口明细加载期不再误报未保存。 Co-authored-by: Cursor <cursoragent@cursor.com>
279 lines
7.1 KiB
Vue
279 lines
7.1 KiB
Vue
<template>
|
|
<a-drawer
|
|
:visible="visible"
|
|
placement="bottom"
|
|
height="auto"
|
|
:footer="false"
|
|
:header="false"
|
|
unmount-on-close
|
|
:drawer-style="drawerStyle"
|
|
popup-container="body"
|
|
class="h5-wh-drawer"
|
|
@cancel="close"
|
|
>
|
|
<div class="h5-wh">
|
|
<div class="h5-wh__handle" aria-hidden="true" />
|
|
<header class="h5-wh__head">
|
|
<h3>{{ t('h5.journey.warehouse.title') }}</h3>
|
|
<button type="button" class="h5-wh__close" @click="close">
|
|
<icon-close />
|
|
</button>
|
|
</header>
|
|
|
|
<p class="h5-wh__lead">{{ t('h5.journey.warehouse.lead') }}</p>
|
|
|
|
<a-spin :loading="loading" class="h5-wh__spin">
|
|
<H5EmptyState
|
|
v-if="!list.length && !loading"
|
|
:icon="IconHome"
|
|
:text="t('h5.journey.warehouse.empty')"
|
|
/>
|
|
<article v-for="item in list" :key="item.id" class="h5-wh__card">
|
|
<div class="h5-wh__card-head">
|
|
<strong>{{ item.warehouseName || '-' }}</strong>
|
|
<button type="button" class="h5-wh__copy" @click="copyItem(item)">
|
|
{{ t('h5.journey.warehouse.copy') }}
|
|
</button>
|
|
</div>
|
|
<p><span>{{ t('h5.journey.warehouse.recipient') }}</span>{{ displayRecipient(item) }}</p>
|
|
<p><span>{{ t('h5.journey.warehouse.phone') }}</span>{{ item.contactPhone || '-' }}</p>
|
|
<p class="is-addr">
|
|
<span>{{ t('h5.journey.warehouse.address') }}</span>
|
|
{{ displayShippingAddress(item) }}
|
|
</p>
|
|
<p v-if="item.remark">
|
|
<span>{{ t('h5.journey.warehouse.remark') }}</span>
|
|
{{ item.remark }}
|
|
</p>
|
|
</article>
|
|
</a-spin>
|
|
</div>
|
|
</a-drawer>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, watch } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { Message } from '@arco-design/web-vue';
|
|
import { IconClose, IconHome } from '@arco-design/web-vue/es/icon';
|
|
import { getWarehouseList, type WarehouseDto } from '@/api/system/warehouse';
|
|
import { resolveCurrentCustomer } from '@/api/customer/resolve';
|
|
import {
|
|
compareWarehouseSort,
|
|
formatWarehouseShipAddress,
|
|
formatWarehouseShipRecipient,
|
|
} from '@/utils/warehouseShipAddress';
|
|
import { getMapIdFromAccessToken } from '@/utils/jwt';
|
|
import { useRbacUserStore } from '@/store/modules/rbac-user';
|
|
import H5EmptyState from './H5EmptyState.vue';
|
|
|
|
defineOptions({ name: 'H5WarehouseAddressSheet' });
|
|
|
|
const props = defineProps<{ visible: boolean }>();
|
|
const emit = defineEmits<{ 'update:visible': [boolean] }>();
|
|
|
|
const { t } = useI18n();
|
|
const rbacUserStore = useRbacUserStore();
|
|
const loading = ref(false);
|
|
const list = ref<WarehouseDto[]>([]);
|
|
const currentCustomerCode = ref('');
|
|
const drawerStyle = {
|
|
borderRadius: '20px 20px 0 0',
|
|
overflow: 'hidden',
|
|
maxHeight: 'min(88vh, 720px)',
|
|
};
|
|
|
|
function close() {
|
|
emit('update:visible', false);
|
|
}
|
|
|
|
function pick(raw: any, ...keys: string[]) {
|
|
for (const k of keys) {
|
|
const v = raw?.[k];
|
|
if (v !== undefined && v !== null && String(v).trim() !== '') return String(v);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function normalize(row: any): WarehouseDto {
|
|
const sortRaw = row?.sort ?? row?.Sort;
|
|
const sortNum = sortRaw == null || sortRaw === '' ? null : Number(sortRaw);
|
|
return {
|
|
id: row?.id ?? row?.Id,
|
|
warehouseName: pick(row, 'warehouseName', 'WarehouseName'),
|
|
recipient: pick(row, 'recipient', 'Recipient'),
|
|
contactPhone: pick(row, 'contactPhone', 'ContactPhone'),
|
|
shippingAddress: pick(row, 'shippingAddress', 'ShippingAddress'),
|
|
sort: sortNum != null && Number.isFinite(sortNum) ? sortNum : null,
|
|
remark: pick(row, 'remark', 'Remark') || undefined,
|
|
};
|
|
}
|
|
|
|
function displayRecipient(item: WarehouseDto) {
|
|
return formatWarehouseShipRecipient(item.recipient, currentCustomerCode.value);
|
|
}
|
|
|
|
function displayShippingAddress(item: WarehouseDto) {
|
|
return formatWarehouseShipAddress(item.shippingAddress, currentCustomerCode.value);
|
|
}
|
|
|
|
async function loadCurrentCustomerCode() {
|
|
try {
|
|
const mapId = getMapIdFromAccessToken(rbacUserStore.accessToken);
|
|
const account = rbacUserStore.userInfo?.account?.trim() || '';
|
|
const data = await resolveCurrentCustomer({ mapId, account });
|
|
currentCustomerCode.value = String(data?.customerCode ?? '').trim();
|
|
} catch {
|
|
currentCustomerCode.value = '';
|
|
}
|
|
}
|
|
|
|
async function loadList() {
|
|
loading.value = true;
|
|
try {
|
|
const [res] = await Promise.all([
|
|
getWarehouseList({ page: 1, pageSize: 100 }),
|
|
loadCurrentCustomerCode(),
|
|
]);
|
|
list.value = (res.items ?? []).map(normalize).sort(compareWarehouseSort);
|
|
} catch {
|
|
list.value = [];
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function copyItem(item: WarehouseDto) {
|
|
const text = [
|
|
item.warehouseName,
|
|
`${t('h5.journey.warehouse.recipient')}${displayRecipient(item)}`,
|
|
`${t('h5.journey.warehouse.phone')}${item.contactPhone || '-'}`,
|
|
`${t('h5.journey.warehouse.address')}${displayShippingAddress(item)}`,
|
|
].join('\n');
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
Message.success(t('h5.me.copySuccess'));
|
|
} catch {
|
|
Message.error(t('h5.orders.copyFailed'));
|
|
}
|
|
}
|
|
|
|
watch(
|
|
() => props.visible,
|
|
(v) => {
|
|
if (v) void loadList();
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
</script>
|
|
|
|
<style lang="less">
|
|
.h5-wh-drawer {
|
|
.arco-drawer-content {
|
|
border-radius: 20px 20px 0 0;
|
|
overflow: hidden;
|
|
max-height: min(88vh, 720px);
|
|
}
|
|
.arco-drawer-body {
|
|
padding: 0;
|
|
max-height: min(88vh, 720px);
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="less" scoped>
|
|
.h5-wh {
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-height: min(88vh, 720px);
|
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
background: #f5f7f8;
|
|
}
|
|
.h5-wh__handle {
|
|
width: 36px;
|
|
height: 4px;
|
|
margin: 10px auto 0;
|
|
border-radius: 999px;
|
|
background: #c9cdd4;
|
|
}
|
|
.h5-wh__head {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 48px;
|
|
h3 {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
.h5-wh__close {
|
|
position: absolute;
|
|
right: 12px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 36px;
|
|
height: 36px;
|
|
border: none;
|
|
background: transparent;
|
|
color: #86909c;
|
|
font-size: 18px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.h5-wh__lead {
|
|
margin: 0 16px 12px;
|
|
font-size: 13px;
|
|
color: #4e5969;
|
|
line-height: 1.5;
|
|
}
|
|
.h5-wh__spin {
|
|
flex: 1;
|
|
overflow: auto;
|
|
padding: 0 16px 16px;
|
|
display: block;
|
|
}
|
|
.h5-wh__card {
|
|
background: #fff;
|
|
border-radius: 16px;
|
|
padding: 14px;
|
|
margin-bottom: 10px;
|
|
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04);
|
|
p {
|
|
margin: 8px 0 0;
|
|
font-size: 13px;
|
|
color: #1d2129;
|
|
line-height: 1.45;
|
|
span {
|
|
color: #86909c;
|
|
margin-right: 6px;
|
|
}
|
|
}
|
|
.is-addr {
|
|
word-break: break-all;
|
|
}
|
|
}
|
|
.h5-wh__card-head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
strong {
|
|
font-size: 15px;
|
|
}
|
|
}
|
|
.h5-wh__copy {
|
|
border: none;
|
|
background: color-mix(in srgb, var(--app-primary-color) 12%, #fff);
|
|
color: var(--app-primary-color);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
height: 28px;
|
|
padding: 0 10px;
|
|
border-radius: 14px;
|
|
cursor: pointer;
|
|
}
|
|
</style>
|