feat: 查价页优化,销售产品可改代码,分区导入跳过重复
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0d5ebdb591
commit
63c7e4ab6f
@ -117,6 +117,7 @@ export async function postForewarningServiceKpiAreaGetKpiAreaInfos(
|
||||
export type KpiAreaInfoImportResult = {
|
||||
successCount: number
|
||||
failCount: number
|
||||
skipCount: number
|
||||
failures: Array<{ rowNumber: number; reason: string }>
|
||||
}
|
||||
|
||||
@ -261,6 +262,7 @@ export async function importKpiAreaInfo(
|
||||
return {
|
||||
successCount: Number(d.successCount ?? d.SuccessCount ?? 0),
|
||||
failCount: Number(d.failCount ?? d.FailCount ?? 0),
|
||||
skipCount: Number(d.skipCount ?? d.SkipCount ?? 0),
|
||||
failures,
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +62,11 @@ export async function updateSalesProduct(body: Record<string, any> & { id: numbe
|
||||
assertBusinessOk(res.data)
|
||||
}
|
||||
|
||||
export async function updateSalesProductCode(body: { id: number | string; productCode: string }) {
|
||||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/product-code/update'), body)
|
||||
assertBusinessOk(res.data)
|
||||
}
|
||||
|
||||
export async function deleteSalesProduct(id: number | string) {
|
||||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/delete'), { id })
|
||||
assertBusinessOk(res.data)
|
||||
|
||||
@ -656,6 +656,7 @@ const CORE_GROUPS: PageButtonGroup[] = [
|
||||
{ code: 'AddProduct', name: '新增产品' },
|
||||
{ code: 'Create', name: '新增' },
|
||||
{ code: 'Update', name: '修改' },
|
||||
{ code: 'UpdateCode', name: '修改产品代码' },
|
||||
{ code: 'Delete', name: '删除' },
|
||||
{ code: 'EditPrice', name: '编辑' },
|
||||
{ code: 'Save', name: '保存' },
|
||||
|
||||
@ -2368,7 +2368,7 @@ export default {
|
||||
'order.priceCheck.form.destinationCountry.placeholder': 'Please select',
|
||||
'order.priceCheck.form.shippingMethod': 'Shipping method',
|
||||
'order.priceCheck.form.shippingMethod.placeholder': 'Please select',
|
||||
'order.priceCheck.form.channel': 'Channel',
|
||||
'order.priceCheck.form.channel': 'Route',
|
||||
'order.priceCheck.form.channel.placeholder': 'All',
|
||||
'order.priceCheck.form.cargoWeight': 'Cargo weight',
|
||||
'order.priceCheck.form.cargoWeight.placeholder': 'Please enter weight (KG)',
|
||||
@ -2389,7 +2389,7 @@ export default {
|
||||
'order.priceCheck.form.postalCode': 'Postal code',
|
||||
'order.priceCheck.form.postalCode.tip': 'more accurate',
|
||||
'order.priceCheck.form.postalCode.placeholder': 'Optional — improves zoning',
|
||||
'order.priceCheck.form.moreFilters': 'More filters',
|
||||
'order.priceCheck.form.moreFilters': 'More',
|
||||
'order.priceCheck.form.activeFilters': '{count} selected',
|
||||
'order.priceCheck.form.clearFilters': 'Clear',
|
||||
'order.priceCheck.form.removeFilter': 'Remove filter',
|
||||
@ -2465,9 +2465,9 @@ export default {
|
||||
'order.priceCheck.table.viewDetail': 'Details',
|
||||
'order.priceCheck.table.viewGuide': 'Channel guide',
|
||||
'order.priceCheck.table.viewGuideHint': 'Click to view route guide',
|
||||
'order.priceCheck.table.customsOrderNow': 'Place order',
|
||||
'order.priceCheck.table.orderNow': 'Place order',
|
||||
'order.priceCheck.table.askAi': 'Ask AI',
|
||||
'order.priceCheck.table.customsOrderNow': 'Go to order',
|
||||
'order.priceCheck.table.orderNow': 'Go to order',
|
||||
'order.priceCheck.table.askAi': 'Ask advisor',
|
||||
'order.priceCheck.table.askAiCanShip': 'Can this ship?',
|
||||
'order.priceCheck.table.askAiCheaper': 'Anything cheaper?',
|
||||
'order.priceCheck.table.empty': 'No matching quotes',
|
||||
@ -5171,7 +5171,13 @@ export default {
|
||||
'channel.salesProducts.placeholder.productDescription': 'Customer-facing description (optional)',
|
||||
'channel.salesProducts.placeholder.preStorageScript': 'Script before order persistence (optional)',
|
||||
'channel.salesProducts.tip.productCode':
|
||||
'Rule: region/country code + 01 line/02 express/03 postal/04 packet + other.\nExample: US04-5DAY\nNot editable after create; uppercase letters, digits, and - only',
|
||||
'Rule: region/country code + 01 line/02 express/03 postal/04 packet + other.\nExample: US04-5DAY\nUppercase letters, digits, and - only; can be changed later via the edit action',
|
||||
'channel.salesProducts.form.editProductCode': 'Edit',
|
||||
'channel.salesProducts.modal.editProductCodeTitle': 'Edit product code',
|
||||
'channel.salesProducts.modal.editProductCodeHint':
|
||||
'Updates the sales-product master code only. Historical orders keep the old code; quoting and place-order use the new one.',
|
||||
'channel.salesProducts.messages.productCodeRequired': 'Enter a product code',
|
||||
'channel.salesProducts.messages.productCodeUpdated': 'Product code updated',
|
||||
'channel.salesProducts.tip.productName':
|
||||
'Rule: region/country + product type description + other.\nExample: US air packet-5 days',
|
||||
'channel.salesProducts.tip.group': 'Used for website/admin grouping, e.g. express parcel',
|
||||
@ -5558,7 +5564,7 @@ export default {
|
||||
'channel.area.message.deleteFailed': 'Delete failed. Please try again later.',
|
||||
'channel.area.import.modalTitle': 'Import area details',
|
||||
'channel.area.import.tip':
|
||||
'Download the template first. Country code is required (country name is filled from the code). Put match dimension/mode codes from the Enum sheet. One row, one dimension: postcode rules if filled; state only = state match; state+city = city match; empty = country fallback.',
|
||||
'Download the template first. Import is incremental: exact duplicate rules in this area are skipped, not overwritten. Country code is required (country name is filled from the code). Put match dimension/mode codes from the Enum sheet. One row, one dimension: postcode rules if filled; state only = state match; state+city = city match; empty = country fallback.',
|
||||
'channel.area.import.downloadTemplate': 'Download template',
|
||||
'channel.area.import.submit': 'Import',
|
||||
'channel.area.import.downloadSuccess': 'Template downloaded',
|
||||
@ -5569,7 +5575,7 @@ export default {
|
||||
'channel.area.import.failed': 'Import failed',
|
||||
'channel.area.import.partialSuccess': 'Some rows failed. See details below.',
|
||||
'channel.area.import.allFailed': 'Import failed. Please check the file.',
|
||||
'channel.area.import.resultSummary': '{success} succeeded, {fail} failed',
|
||||
'channel.area.import.resultSummary': '{success} succeeded, {skip} duplicate skipped, {fail} failed',
|
||||
'channel.area.import.failRow': 'Row {row}: {reason}',
|
||||
'channel.area.export.success': 'Exported {count} row(s)',
|
||||
'channel.area.export.failed': 'Export failed',
|
||||
|
||||
@ -2254,7 +2254,7 @@ export default {
|
||||
'order.priceCheck.form.destinationCountry.placeholder': '请选择',
|
||||
'order.priceCheck.form.shippingMethod': '运输方式',
|
||||
'order.priceCheck.form.shippingMethod.placeholder': '请选择',
|
||||
'order.priceCheck.form.channel': '渠道',
|
||||
'order.priceCheck.form.channel': '线路',
|
||||
'order.priceCheck.form.channel.placeholder': '全部',
|
||||
'order.priceCheck.form.cargoWeight': '货物重量',
|
||||
'order.priceCheck.form.cargoWeight.placeholder': '请输入重量(KG)',
|
||||
@ -2275,7 +2275,7 @@ export default {
|
||||
'order.priceCheck.form.postalCode': '邮编',
|
||||
'order.priceCheck.form.postalCode.tip': '更准',
|
||||
'order.priceCheck.form.postalCode.placeholder': '选填,补全更准确',
|
||||
'order.priceCheck.form.moreFilters': '更多条件',
|
||||
'order.priceCheck.form.moreFilters': '更多',
|
||||
'order.priceCheck.form.activeFilters': '已选 {count} 项',
|
||||
'order.priceCheck.form.clearFilters': '清空',
|
||||
'order.priceCheck.form.removeFilter': '移除条件',
|
||||
@ -2351,9 +2351,9 @@ export default {
|
||||
'order.priceCheck.table.viewDetail': '详情',
|
||||
'order.priceCheck.table.viewGuide': '渠道说明',
|
||||
'order.priceCheck.table.viewGuideHint': '点击查看线路说明',
|
||||
'order.priceCheck.table.customsOrderNow': '立刻下单',
|
||||
'order.priceCheck.table.orderNow': '立刻下单',
|
||||
'order.priceCheck.table.askAi': '问 AI',
|
||||
'order.priceCheck.table.customsOrderNow': '去下单',
|
||||
'order.priceCheck.table.orderNow': '去下单',
|
||||
'order.priceCheck.table.askAi': '问顾问',
|
||||
'order.priceCheck.table.askAiCanShip': '这个能不能发?',
|
||||
'order.priceCheck.table.askAiCheaper': '有没有更便宜?',
|
||||
'order.priceCheck.table.empty': '暂无匹配报价',
|
||||
@ -5206,7 +5206,13 @@ export default {
|
||||
'channel.salesProducts.placeholder.productDescription': '面向客户的产品说明(选填)',
|
||||
'channel.salesProducts.placeholder.preStorageScript': '订单落库前执行的脚本(选填)',
|
||||
'channel.salesProducts.tip.productCode':
|
||||
'命名规则:地区/国家代码 + 01专线/02快递/03邮政/04小包 + 其他。\n示例:US04-5DAY\n创建后不可修改;仅支持大写字母、数字和 -',
|
||||
'命名规则:地区/国家代码 + 01专线/02快递/03邮政/04小包 + 其他。\n示例:US04-5DAY\n仅支持大写字母、数字和 -;创建后可通过修改入口单独改代码',
|
||||
'channel.salesProducts.form.editProductCode': '修改',
|
||||
'channel.salesProducts.modal.editProductCodeTitle': '修改产品代码',
|
||||
'channel.salesProducts.modal.editProductCodeHint':
|
||||
'仅改销售产品主数据代码。历史订单仍保留原代码;下单、查价将使用新代码。',
|
||||
'channel.salesProducts.messages.productCodeRequired': '请输入产品代码',
|
||||
'channel.salesProducts.messages.productCodeUpdated': '产品代码已更新',
|
||||
'channel.salesProducts.tip.productName':
|
||||
'命名规则:地区/国家 + 产品类型描述 + 其他。\n示例:美国空运小包-5日达',
|
||||
'channel.salesProducts.tip.group': '用于官网/后台分组展示,如专线小包',
|
||||
@ -5569,7 +5575,7 @@ export default {
|
||||
'channel.area.search.button.query': '查询',
|
||||
'channel.area.search.button.reset': '重置',
|
||||
'channel.area.import.modalTitle': '导入分区明细',
|
||||
'channel.area.import.tip': '请先下载模板。国家代码必填(无需填国家名称,导入时按代码自动带出)。匹配维度、匹配方式请填 Sheet「枚举值」中的编码。一行一个维度:填邮编则为邮编规则(只填起始=前缀;起止相同=精确;起止不同=闭区间);不填邮编只填州=州匹配;州+城市=城市匹配;都空=仅国家兜底。',
|
||||
'channel.area.import.tip': '请先下载模板。导入为增量写入:与本分区已有规则完全相同的行会跳过,不会覆盖。国家代码必填(无需填国家名称,导入时按代码自动带出)。匹配维度、匹配方式请填 Sheet「枚举值」中的编码。一行一个维度:填邮编则为邮编规则(只填起始=前缀;起止相同=精确;起止不同=闭区间);不填邮编只填州=州匹配;州+城市=城市匹配;都空=仅国家兜底。',
|
||||
'channel.area.import.downloadTemplate': '下载模板',
|
||||
'channel.area.import.submit': '开始导入',
|
||||
'channel.area.import.downloadSuccess': '模板下载成功',
|
||||
@ -5580,7 +5586,7 @@ export default {
|
||||
'channel.area.import.failed': '导入失败',
|
||||
'channel.area.import.partialSuccess': '部分行导入失败,请查看失败明细',
|
||||
'channel.area.import.allFailed': '导入失败,请检查文件内容',
|
||||
'channel.area.import.resultSummary': '成功 {success} 条,失败 {fail} 条',
|
||||
'channel.area.import.resultSummary': '成功 {success} 条,跳过重复 {skip} 条,失败 {fail} 条',
|
||||
'channel.area.import.failRow': '第 {row} 行:{reason}',
|
||||
'channel.area.export.success': '已导出 {count} 条',
|
||||
'channel.area.export.failed': '导出失败',
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
{{
|
||||
t('channel.area.import.resultSummary', {
|
||||
success: importResult.successCount,
|
||||
skip: importResult.skipCount,
|
||||
fail: importResult.failCount,
|
||||
})
|
||||
}}
|
||||
@ -117,7 +118,7 @@
|
||||
try {
|
||||
const result = await importKpiAreaInfo(areaId.value, selectedFile.value);
|
||||
importResult.value = result;
|
||||
if (result.successCount > 0) {
|
||||
if (result.successCount > 0 || result.skipCount > 0) {
|
||||
emit('success');
|
||||
}
|
||||
if (result.failCount === 0) {
|
||||
@ -125,7 +126,7 @@
|
||||
visible.value = false;
|
||||
return true;
|
||||
}
|
||||
if (result.successCount > 0) {
|
||||
if (result.successCount > 0 || result.skipCount > 0) {
|
||||
Message.warning(t('channel.area.import.partialSuccess'));
|
||||
} else {
|
||||
Message.error(t('channel.area.import.allFailed'));
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
saveSalesProductCustomerGuide,
|
||||
toggleSalesProductPriceStatus,
|
||||
updateSalesProduct,
|
||||
updateSalesProductCode,
|
||||
updateSalesProductPrice,
|
||||
} from '@/api/channel/salesProduct';
|
||||
import { getLabelTemplateList } from '@/api/channel/labelTemplate';
|
||||
@ -154,6 +155,9 @@
|
||||
const customerGuideSaving = ref(false);
|
||||
const customerGuidePanelRef = ref<InstanceType<typeof MarkdownDocPanel> | null>(null);
|
||||
const savingConfig = ref(false);
|
||||
const productCodeModalVisible = ref(false);
|
||||
const productCodeDraft = ref('');
|
||||
const savingProductCode = ref(false);
|
||||
|
||||
function parseAdditionalServiceCodes(raw: unknown): string[] {
|
||||
if (Array.isArray(raw)) {
|
||||
@ -252,6 +256,42 @@
|
||||
.replace(/[^A-Z0-9-]/g, '');
|
||||
}
|
||||
|
||||
function openProductCodeModal() {
|
||||
if (selectedProduct.value == null) return;
|
||||
productCodeDraft.value = normalizeBizCode(formData.productCode);
|
||||
productCodeModalVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleSaveProductCode() {
|
||||
const pid = selectedProduct.value;
|
||||
if (pid == null) return;
|
||||
const next = normalizeBizCode(productCodeDraft.value);
|
||||
if (!next) {
|
||||
Message.warning(t('channel.salesProducts.messages.productCodeRequired'));
|
||||
return Promise.reject();
|
||||
}
|
||||
savingProductCode.value = true;
|
||||
try {
|
||||
await updateSalesProductCode({ id: pid, productCode: next });
|
||||
formData.productCode = next;
|
||||
const hit = productList.value.find((p: any) => p.id === pid);
|
||||
if (hit?.raw) {
|
||||
hit.raw.productCode = next;
|
||||
hit.raw.ProductCode = next;
|
||||
}
|
||||
if (configFormBaseline.value) {
|
||||
configFormBaseline.value = { ...configFormBaseline.value, productCode: next };
|
||||
}
|
||||
productCodeModalVisible.value = false;
|
||||
Message.success(t('channel.salesProducts.messages.productCodeUpdated'));
|
||||
} catch (e: any) {
|
||||
Message.error(e?.message || '修改产品代码失败');
|
||||
return Promise.reject(e);
|
||||
} finally {
|
||||
savingProductCode.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const selectedProductContext = computed(() => {
|
||||
const pid = selectedProduct.value;
|
||||
if (pid == null) return null;
|
||||
@ -4014,10 +4054,18 @@
|
||||
</template>
|
||||
<icon-question-circle class="tip-icon" />
|
||||
</a-tooltip>
|
||||
<button
|
||||
type="button"
|
||||
class="product-code-edit-icon"
|
||||
:aria-label="t('channel.salesProducts.form.editProductCode')"
|
||||
@click.stop="openProductCodeModal"
|
||||
>
|
||||
<icon-edit />
|
||||
</button>
|
||||
</label>
|
||||
<a-input
|
||||
v-model="formData.productCode"
|
||||
:placeholder="t('channel.salesProducts.form.placeholder.input')"
|
||||
:placeholder="t('channel.salesProducts.placeholder.productCode')"
|
||||
class="form-input"
|
||||
disabled
|
||||
/>
|
||||
@ -5487,6 +5535,25 @@
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="productCodeModalVisible"
|
||||
:title="t('channel.salesProducts.modal.editProductCodeTitle')"
|
||||
width="420px"
|
||||
:mask-closable="false"
|
||||
:ok-loading="savingProductCode"
|
||||
@ok="handleSaveProductCode"
|
||||
@cancel="productCodeModalVisible = false"
|
||||
>
|
||||
<p class="product-code-edit-hint">{{ t('channel.salesProducts.modal.editProductCodeHint') }}</p>
|
||||
<a-input
|
||||
:model-value="productCodeDraft"
|
||||
:placeholder="t('channel.salesProducts.placeholder.productCode')"
|
||||
allow-clear
|
||||
@update:model-value="(v) => (productCodeDraft = normalizeBizCode(v))"
|
||||
@press-enter="handleSaveProductCode"
|
||||
/>
|
||||
</a-modal>
|
||||
|
||||
<ChannelReferencePreviewModal ref="referencePreviewRef" :dict-label-fn="dictLabelByType" />
|
||||
</div>
|
||||
</template>
|
||||
@ -5691,6 +5758,31 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.product-code-edit-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text-3);
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--app-primary-color, #007979);
|
||||
}
|
||||
}
|
||||
|
||||
.product-code-edit-hint {
|
||||
margin: 0 0 12px;
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select,
|
||||
.form-textarea {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { IconDown } from '@arco-design/web-vue/es/icon'
|
||||
import IconTabService from '@/assets/icons/h5/tab-service.svg'
|
||||
|
||||
defineOptions({ name: 'PriceCheckQuoteResultCard' })
|
||||
|
||||
@ -328,13 +328,13 @@
|
||||
</div>
|
||||
|
||||
<div class="quote-card__actions" @click.stop>
|
||||
<button type="button" class="ops-btn ops-btn--primary" @click="emit('order')">
|
||||
{{ t('order.priceCheck.table.orderNow') }}
|
||||
</button>
|
||||
<a-dropdown trigger="click" position="br">
|
||||
<button type="button" class="ops-btn ops-btn--ghost">
|
||||
{{ t('order.priceCheck.table.askAi') }}
|
||||
<icon-down style="margin-left: 2px" />
|
||||
<a-dropdown trigger="click" position="bl">
|
||||
<button
|
||||
type="button"
|
||||
class="advisor-icon-btn"
|
||||
:aria-label="t('order.priceCheck.table.askAi')"
|
||||
>
|
||||
<IconTabService class="advisor-icon-btn__icon" />
|
||||
</button>
|
||||
<template #content>
|
||||
<a-doption @click="emit('ask', 'canShip')">
|
||||
@ -345,6 +345,9 @@
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<button type="button" class="ops-btn ops-btn--primary" @click="emit('order')">
|
||||
{{ t('order.priceCheck.table.orderNow') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
@ -666,25 +669,18 @@
|
||||
|
||||
.quote-card__actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
min-width: 88px;
|
||||
|
||||
:deep(.arco-dropdown-open),
|
||||
:deep(.arco-trigger),
|
||||
> :deep(.arco-dropdown-trigger),
|
||||
> .arco-dropdown-open {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
> :deep(span),
|
||||
> :deep(div) {
|
||||
width: 100%;
|
||||
display: block;
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,36 +688,54 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&--ghost {
|
||||
background: transparent;
|
||||
border-color: var(--color-border-2);
|
||||
color: var(--color-text-2);
|
||||
|
||||
&:hover {
|
||||
color: var(--app-primary-color, #007979);
|
||||
border-color: color-mix(in srgb, var(--app-primary-color, #007979) 35%, var(--color-border-2));
|
||||
background: color-mix(in srgb, var(--app-primary-color, #007979) 4%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
&--primary {
|
||||
min-width: 88px;
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
border-radius: 6px;
|
||||
background: var(--app-primary-color, #007979);
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 2px color-mix(in srgb, var(--app-primary-color, #007979) 28%, transparent);
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.04);
|
||||
filter: brightness(1.06);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.advisor-icon-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
background: transparent;
|
||||
color: var(--app-primary-color, #007979);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:hover {
|
||||
background: color-mix(in srgb, var(--app-primary-color, #007979) 8%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.advisor-icon-btn__icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.quote-card__fee-pop {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -799,7 +813,7 @@
|
||||
|
||||
.quote-card__actions {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@ -810,13 +824,10 @@
|
||||
}
|
||||
|
||||
.quote-card__actions {
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ops-btn--primary {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { IconDown, IconSearch } from '@arco-design/web-vue/es/icon'
|
||||
import { IconClose, IconDown, IconSearch } from '@arco-design/web-vue/es/icon'
|
||||
import SlidingTabs from '@/components/SlidingTabs/index.vue'
|
||||
import type { PriceCheckShippingGroup } from '@/api/order'
|
||||
|
||||
@ -132,7 +132,7 @@ function clearSelection(e: Event) {
|
||||
aria-label="clear"
|
||||
@click="clearSelection"
|
||||
>
|
||||
×
|
||||
<icon-close />
|
||||
</button>
|
||||
<icon-down class="trigger-icon" />
|
||||
</div>
|
||||
@ -233,20 +233,23 @@ function clearSelection(e: Event) {
|
||||
}
|
||||
|
||||
.trigger-clear {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 4px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: var(--color-fill-4);
|
||||
color: #fff;
|
||||
background: transparent;
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-text-3);
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
const formData = reactive({
|
||||
destinationCountry: '',
|
||||
shippingMethod: '',
|
||||
cargoWeight: '',
|
||||
cargoWeight: '1',
|
||||
length: '',
|
||||
width: '',
|
||||
height: '',
|
||||
@ -92,18 +92,6 @@
|
||||
const couponInstanceId = ref<number | undefined>(undefined);
|
||||
const availableCoupons = ref<CouponAvailableOutput[]>([]);
|
||||
|
||||
const advancedActiveCount = computed(() => {
|
||||
let n = 0;
|
||||
if (formData.shippingMethod) n += 1;
|
||||
if (formData.cargoType) n += 1;
|
||||
if (formData.warehouseId != null) n += 1;
|
||||
if (hasFullDimensions() || hasPartialDimensions()) n += 1;
|
||||
if (String(formData.destinationProvince || '').trim()) n += 1;
|
||||
if (String(formData.destinationCity || '').trim()) n += 1;
|
||||
if (couponInstanceId.value) n += 1;
|
||||
return n;
|
||||
});
|
||||
|
||||
const needPostalHint = computed(() => {
|
||||
if (!hasQueried.value) return false;
|
||||
if (String(formData.postalCode || '').trim()) return false;
|
||||
@ -125,7 +113,6 @@
|
||||
| 'destinationCity'
|
||||
| 'cargoType'
|
||||
| 'warehouse'
|
||||
| 'shipping'
|
||||
| 'coupon';
|
||||
value?: string | number;
|
||||
};
|
||||
@ -181,17 +168,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
if (formData.shippingMethod) {
|
||||
const flat = (shippingMethodGroups.value || []).flatMap((g) => g.products || []);
|
||||
const hit = flat.find((it) => it.productCode === formData.shippingMethod);
|
||||
chips.push({
|
||||
key: 'shipping',
|
||||
kind: 'shipping',
|
||||
value: formData.shippingMethod,
|
||||
label: hit?.label || formData.shippingMethod,
|
||||
});
|
||||
}
|
||||
|
||||
if (couponInstanceId.value) {
|
||||
const c = availableCoupons.value.find((x) => (x.id ?? x.Id) === couponInstanceId.value);
|
||||
const name = String(c?.templateName ?? c?.TemplateName ?? '').trim();
|
||||
@ -229,10 +205,6 @@
|
||||
formData.warehouseId = undefined;
|
||||
return;
|
||||
}
|
||||
if (chip.kind === 'shipping') {
|
||||
formData.shippingMethod = '';
|
||||
return;
|
||||
}
|
||||
if (chip.kind === 'coupon') {
|
||||
couponInstanceId.value = undefined;
|
||||
}
|
||||
@ -246,7 +218,6 @@
|
||||
formData.destinationCity = '';
|
||||
formData.cargoType = '';
|
||||
formData.warehouseId = undefined;
|
||||
formData.shippingMethod = '';
|
||||
couponInstanceId.value = undefined;
|
||||
}
|
||||
|
||||
@ -621,14 +592,15 @@
|
||||
availableCoupons.value = coupons ?? [];
|
||||
hintBannerDismissed.value = false;
|
||||
queryResults.value = processQueryResults(items || []);
|
||||
selectedGuideRecord.value = null;
|
||||
resetRecommendState();
|
||||
sortMode.value = 'recommend';
|
||||
currentPage.value = 1;
|
||||
recalcLoadingKeys.value = {};
|
||||
if (!queryResults.value.length) {
|
||||
selectedGuideRecord.value = null;
|
||||
applyPriceCheckFailures(failures ?? []);
|
||||
} else {
|
||||
selectedGuideRecord.value = sortedResults.value[0] ?? queryResults.value[0];
|
||||
clearPriceCheckFailures();
|
||||
Message.success(t('order.priceCheck.message.querySuccess', { count: queryResults.value.length }));
|
||||
}
|
||||
@ -648,7 +620,7 @@
|
||||
Object.assign(formData, {
|
||||
destinationCountry: '',
|
||||
shippingMethod: '',
|
||||
cargoWeight: '',
|
||||
cargoWeight: '1',
|
||||
length: '',
|
||||
width: '',
|
||||
height: '',
|
||||
@ -1001,6 +973,17 @@
|
||||
</div>
|
||||
|
||||
<div class="form-row form-row-primary">
|
||||
<div class="form-item form-item-channel">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.channel') }}</label>
|
||||
<ShippingMethodTabSelect
|
||||
v-model="formData.shippingMethod"
|
||||
:groups="shippingMethodGroups"
|
||||
:loading="optionsLoading"
|
||||
:placeholder="$t('order.priceCheck.form.channel.placeholder')"
|
||||
class="form-select"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item form-item-country">
|
||||
<label class="form-label required">{{ $t('order.priceCheck.form.destinationCountry') }}</label>
|
||||
<a-select
|
||||
@ -1035,21 +1018,21 @@
|
||||
</a-select>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label class="form-label required">{{ $t('order.priceCheck.form.cargoWeight') }}</label>
|
||||
<div ref="postalInputRef" class="form-item form-item-compact">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.postalCode') }}</label>
|
||||
<a-input
|
||||
v-model="formData.cargoWeight"
|
||||
:placeholder="$t('order.priceCheck.form.cargoWeight.placeholder')"
|
||||
v-model="formData.postalCode"
|
||||
:placeholder="$t('order.priceCheck.form.postalCode.placeholder')"
|
||||
class="form-input"
|
||||
@press-enter="handleQuery"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div ref="postalInputRef" class="form-item">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.postalCode') }}</label>
|
||||
<div class="form-item form-item-compact">
|
||||
<label class="form-label required">{{ $t('order.priceCheck.form.cargoWeight') }}</label>
|
||||
<a-input
|
||||
v-model="formData.postalCode"
|
||||
:placeholder="$t('order.priceCheck.form.postalCode.placeholder')"
|
||||
v-model="formData.cargoWeight"
|
||||
:placeholder="$t('order.priceCheck.form.cargoWeight.placeholder')"
|
||||
class="form-input"
|
||||
@press-enter="handleQuery"
|
||||
/>
|
||||
@ -1071,10 +1054,7 @@
|
||||
{{ $t('order.priceCheck.actions.reset') }}
|
||||
</a-button>
|
||||
<button type="button" class="advanced-toggle-btn" @click="showAdvanced = !showAdvanced">
|
||||
<span>
|
||||
{{ $t('order.priceCheck.form.moreFilters') }}
|
||||
<span v-if="advancedActiveCount" class="advanced-count">{{ advancedActiveCount }}</span>
|
||||
</span>
|
||||
<span>{{ $t('order.priceCheck.form.moreFilters') }}</span>
|
||||
<icon-up v-if="showAdvanced" />
|
||||
<icon-down v-else />
|
||||
</button>
|
||||
@ -1110,10 +1090,10 @@
|
||||
<icon-close />
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" class="filter-summary-bar__clear" @click="clearAdvancedConditions">
|
||||
{{ $t('order.priceCheck.form.clearFilters') }}
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="filter-summary-bar__clear" @click="clearAdvancedConditions">
|
||||
{{ $t('order.priceCheck.form.clearFilters') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-show="showAdvanced" class="form-row form-row-advanced">
|
||||
@ -1183,17 +1163,6 @@
|
||||
</a-select>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.channel') }}</label>
|
||||
<ShippingMethodTabSelect
|
||||
v-model="formData.shippingMethod"
|
||||
:groups="shippingMethodGroups"
|
||||
:loading="optionsLoading"
|
||||
:placeholder="$t('order.priceCheck.form.channel.placeholder')"
|
||||
class="form-select"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.cargoType') }}</label>
|
||||
<a-select
|
||||
@ -1365,8 +1334,8 @@
|
||||
background-color: var(--app-page-bg);
|
||||
}
|
||||
.price-check-layout { display: flex; gap: 12px; flex: 1; min-height: 0; min-width: 0; }
|
||||
.price-check-main { flex: 7 1 0; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 12px; overflow: hidden; }
|
||||
.price-check-aside { flex: 3 1 0; min-width: 300px; min-height: 0; display: flex; flex-direction: column; > * { flex: 1; min-height: 0; } }
|
||||
.price-check-main { flex: 5 1 0; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 12px; overflow: hidden; }
|
||||
.price-check-aside { flex: 3 1 0; min-width: 340px; min-height: 0; display: flex; flex-direction: column; > * { flex: 1; min-height: 0; } }
|
||||
.liquid-panel {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
@ -1439,8 +1408,9 @@
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.form-row { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
|
||||
.form-row { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
|
||||
.form-row-primary, .form-row-advanced { overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; }
|
||||
.form-row-primary { flex-wrap: nowrap; }
|
||||
.form-row-advanced {
|
||||
margin-top: 12px;
|
||||
padding-top: 16px;
|
||||
@ -1543,23 +1513,9 @@
|
||||
white-space: nowrap;
|
||||
&:hover { color: var(--app-primary-color, #007979); }
|
||||
}
|
||||
.advanced-count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 4px;
|
||||
padding: 0 4px;
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 121, 121, 0.1);
|
||||
color: var(--app-primary-color, #007979);
|
||||
font-size: 11px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.form-hint { margin: 0; color: var(--color-text-3); font-size: 12px; line-height: 1.3; }
|
||||
.form-item {
|
||||
display: flex; flex-direction: column; gap: 8px; flex: 0 0 auto; width: 168px; min-width: 140px;
|
||||
display: flex; flex-direction: column; gap: 8px; flex: 0 0 auto; width: 148px; min-width: 132px;
|
||||
&.dimensions-group { width: 260px; min-width: 220px; }
|
||||
&.button-group { width: auto; min-width: 0; flex-shrink: 0; }
|
||||
.button-group-row {
|
||||
@ -1570,7 +1526,9 @@
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
&.form-item-country { width: 280px; min-width: 240px; flex-shrink: 0; }
|
||||
&.form-item-country { width: 240px; min-width: 200px; flex-shrink: 0; }
|
||||
&.form-item-channel { width: 220px; min-width: 180px; flex-shrink: 0; }
|
||||
&.form-item-compact { width: 112px; min-width: 100px; }
|
||||
}
|
||||
.country-shortcuts {
|
||||
display: flex;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user