feat: 查价按统一地理分区规则匹配,并补全州/市入参
Some checks failed
Build and Deploy Front / deploy (push) Failing after 1m33s
Some checks failed
Build and Deploy Front / deploy (push) Failing after 1m33s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
07f241ab64
commit
c8a52cf87e
@ -1267,6 +1267,7 @@ export interface PriceCheckRequest {
|
||||
/** 仓库主键(多选;为空则遍历全部仓库) */
|
||||
warehouseIds?: number[]
|
||||
destinationProvince?: string
|
||||
destinationCity?: string
|
||||
postalCode?: string
|
||||
/** 选用的优惠券实例 Id */
|
||||
couponInstanceId?: number
|
||||
|
||||
@ -312,6 +312,8 @@ export default {
|
||||
'warehouse.parcel.drawer.frozenQty': 'Frozen',
|
||||
'warehouse.parcel.drawer.itemCount': '{n} line(s)',
|
||||
'warehouse.parcel.tip': 'Available = ready to bind; Frozen = reserved by orders. Use status tabs to filter.',
|
||||
'warehouse.parcel.pluginHint': 'Don’t want to fill parcel details? Use the one-click add-parcel plugin.',
|
||||
'warehouse.parcel.pluginHintDownload': 'Download it from the top-right icon',
|
||||
'warehouse.parcel.drawer.edit': 'Edit parcel',
|
||||
'warehouse.parcel.drawer.empty': 'No items',
|
||||
'warehouse.parcel.table.arrivalTime': 'Arrival Time',
|
||||
@ -1439,43 +1441,49 @@ export default {
|
||||
'nanaAssistant.tabAi': 'AI Nana',
|
||||
'nanaAssistant.tabAdvisor': 'Exclusive Advisor',
|
||||
'nanaAssistant.badge': 'Logistics Advisor',
|
||||
'nanaAssistant.welcomeTitle': 'What do you need help shipping?',
|
||||
'nanaAssistant.welcomeSub': 'Price, cargo eligibility, tracking — pick a capability below.',
|
||||
'nanaAssistant.welcomeTitle': 'What do you want to check?',
|
||||
'nanaAssistant.welcomeHello': '{greet}, Xiao Ai at your service',
|
||||
'nanaAssistant.welcomeSub': 'What do you want to check? Tap below, or just type.',
|
||||
'nanaAssistant.greet.morning': 'Good morning',
|
||||
'nanaAssistant.greet.noon': 'Good afternoon',
|
||||
'nanaAssistant.greet.afternoon': 'Good afternoon',
|
||||
'nanaAssistant.greet.evening': 'Good evening',
|
||||
'nanaAssistant.greet.night': 'Still up',
|
||||
'nanaAssistant.quickLabel': 'Common questions',
|
||||
'nanaAssistant.welcomeMsg': "Hi — I'm your IDE logistics advisor. Pick a question below or type your own.",
|
||||
'nanaAssistant.welcomeMsg': "Hi — I'm your IDE logistics advisor. Tap below or type your own question.",
|
||||
'nanaAssistant.historyTitle': 'Chat history',
|
||||
'nanaAssistant.newChat': 'New chat',
|
||||
'nanaAssistant.emptyHistory': 'No conversations yet',
|
||||
'nanaAssistant.searchHistory': 'Search chats or waybill no.',
|
||||
'nanaAssistant.noSearchResults': 'No matching conversations',
|
||||
'nanaAssistant.deleteThread': 'Delete conversation',
|
||||
'nanaAssistant.hint': 'Type a question, or pick a capability above.',
|
||||
'nanaAssistant.hintLive': 'Smart advisor is live — price, channel guide, tracking.',
|
||||
'nanaAssistant.hint': 'Type a question, or tap an option above.',
|
||||
'nanaAssistant.hintLive': 'Ask about rates, whether it can ship, or where a parcel is.',
|
||||
'nanaAssistant.inputPlaceholder': 'Ask a question…',
|
||||
'nanaAssistant.inputPlaceholderLive': 'Ask about price, channels, or tracking…',
|
||||
'nanaAssistant.inputPlaceholderTrack': 'Enter waybill / tracking no.…',
|
||||
'nanaAssistant.mcp.sectionLabel': 'Capabilities',
|
||||
'nanaAssistant.mcp.exampleHint': 'e.g. {sample}',
|
||||
'nanaAssistant.mcp.required': 'Required',
|
||||
'nanaAssistant.mcp.optional': 'Optional',
|
||||
'nanaAssistant.mcp.missingAsk': 'Please also provide: {fields}. Then I can look it up.',
|
||||
'nanaAssistant.mcp.priceTitle': 'Price check',
|
||||
'nanaAssistant.mcp.priceDesc': 'Estimate freight by destCountry & weight',
|
||||
'nanaAssistant.mcp.priceTitle': 'Check rates',
|
||||
'nanaAssistant.mcp.priceDesc': 'Estimate freight by country and weight',
|
||||
'nanaAssistant.mcp.priceCoach':
|
||||
'To quote I need:\n1) Destination destCountry (e.g. US)\n2) Actual weight in kg\n\nOptional: postal code, L×W×H (cm), cargo type.\nExample: US 0.5kg zip 10001',
|
||||
'nanaAssistant.mcp.priceExample': 'e.g. US 0.5kg zip 10001',
|
||||
'To quote I need destination country and actual weight (kg). Postal code and size help accuracy.\nExample: US 0.5kg',
|
||||
'nanaAssistant.mcp.priceExample': 'Check US 0.5kg rates for me',
|
||||
'nanaAssistant.mcp.priceDraft':
|
||||
'Country:\nWeight (kg):\nPostal code (optional):\nDims LWH cm (optional):',
|
||||
'nanaAssistant.mcp.guideTitle': 'Can this cargo ship?',
|
||||
'nanaAssistant.mcp.guideDesc': 'Check against the channel customer guide',
|
||||
'nanaAssistant.mcp.guideDesc': 'See if this item can go on this service',
|
||||
'nanaAssistant.mcp.guideCoach':
|
||||
'I need:\n1) Sales product code or name\n2) Cargo / product name\n\nExample: product EX_ASP_HKDHL, cargo lipstick — can it ship?',
|
||||
'nanaAssistant.mcp.guideExample': 'e.g. product XX, cargo lipstick OK?',
|
||||
'I need the sales product (service) and the cargo name.\nExample: product XX, cargo lipstick — can it ship?',
|
||||
'nanaAssistant.mcp.guideExample': 'Can lipstick ship?',
|
||||
'nanaAssistant.mcp.guideDraft': 'Sales product code/name:\nCargo name:',
|
||||
'nanaAssistant.mcp.trackTitle': 'Track shipment',
|
||||
'nanaAssistant.mcp.trackDesc': 'Timeline by waybill number',
|
||||
'nanaAssistant.mcp.trackTitle': 'Where is my parcel?',
|
||||
'nanaAssistant.mcp.trackDesc': 'See where the parcel is now',
|
||||
'nanaAssistant.mcp.trackCoach':
|
||||
'Please provide a waybill / tracking / customer order number.\nIf a waybill is already loaded, tap “Track”.',
|
||||
'nanaAssistant.mcp.trackExample': 'e.g. Track waybill NFXXXX',
|
||||
'Send a waybill number. If one is already loaded, tap “Track this shipment”.',
|
||||
'nanaAssistant.mcp.trackExample': 'Where is waybill NFXXXX',
|
||||
'nanaAssistant.mcp.trackDraft': 'Waybill No.:',
|
||||
'nanaAssistant.mcp.field.destCountry': 'Destination destCountry',
|
||||
'nanaAssistant.mcp.field.weight': 'Weight (kg)',
|
||||
@ -1528,6 +1536,10 @@ export default {
|
||||
'nanaAssistant.viewWaybill': 'View waybill →',
|
||||
'nanaAssistant.queryTrack': 'Track shipment',
|
||||
'nanaAssistant.endWaybillConsult': 'End waybill consult',
|
||||
'nanaAssistant.waybillWelcomeTitle': 'How can I help with this shipment?',
|
||||
'nanaAssistant.waybillWelcomeSub': 'Waybill {no} is loaded — track it or ask about fees.',
|
||||
'nanaAssistant.waybillAskTrack': 'Track this shipment',
|
||||
'nanaAssistant.waybillAskFee': 'Ask about fees',
|
||||
'nanaAssistant.waybillAutoAsk': 'Please track waybill {no} for me',
|
||||
'nanaAssistant.trackWaybillDemo': 'Got your request for waybill {no}. When the smart service is configured, we prefer the live API; this is a reference reply: milestones usually include pickup, export, line-haul, customs, and delivery. Your advisor will follow up on exceptions.',
|
||||
'nanaAssistant.copyWechat': 'Copy WeChat ID',
|
||||
@ -2255,7 +2267,9 @@ export default {
|
||||
'order.priceCheck.form.warehouse': 'Warehouse',
|
||||
'order.priceCheck.form.warehouse.placeholder': 'All',
|
||||
'order.priceCheck.form.destinationProvince': 'State / Province',
|
||||
'order.priceCheck.form.destinationProvince.placeholder': 'Please select',
|
||||
'order.priceCheck.form.destinationProvince.placeholder': 'Used when postal code is empty',
|
||||
'order.priceCheck.form.destinationCity': 'City',
|
||||
'order.priceCheck.form.destinationCity.placeholder': 'Used when postal code is empty',
|
||||
'order.priceCheck.form.postalCode': 'Postal code',
|
||||
'order.priceCheck.form.postalCode.tip': 'more accurate',
|
||||
'order.priceCheck.form.postalCode.placeholder': 'Optional — improves zoning',
|
||||
@ -2568,7 +2582,7 @@ export default {
|
||||
'order.placeOrder.single.exportQualificationManage': 'Manage',
|
||||
'order.placeOrder.single.exportQualificationLoadFailed': 'Failed to load export qualifications',
|
||||
'order.placeOrder.single.platformCode': 'E-commerce platform',
|
||||
'order.placeOrder.single.platformCodePlaceholder': 'e.g. Amazon / eBay / Shopify',
|
||||
'order.placeOrder.single.platformCodePlaceholder': 'Select ecommerce platform',
|
||||
'order.placeOrder.single.showExtraCols': 'More fields',
|
||||
'order.placeOrder.single.hideExtraCols': 'Less',
|
||||
'order.placeOrder.single.showRecipientOptional': 'More fields',
|
||||
@ -5330,6 +5344,15 @@ export default {
|
||||
'channel.area.form.city': 'City',
|
||||
'channel.area.form.leftPostCode': 'Start postcode',
|
||||
'channel.area.form.rightPostCode': 'End postcode',
|
||||
'channel.area.form.matchDim': 'Match dimension',
|
||||
'channel.area.form.matchMode': 'Match mode',
|
||||
'channel.area.matchDim.country': 'Country only',
|
||||
'channel.area.matchDim.zip': 'Postcode',
|
||||
'channel.area.matchDim.state': 'State/Province',
|
||||
'channel.area.matchDim.city': 'City',
|
||||
'channel.area.matchMode.prefix': 'Prefix',
|
||||
'channel.area.matchMode.exact': 'Exact',
|
||||
'channel.area.matchMode.range': 'Range',
|
||||
'channel.area.form.placeholder.keyword': 'Enter keyword',
|
||||
'channel.area.form.placeholder.areaName': 'Enter area name',
|
||||
'channel.area.form.placeholder.input': 'Please enter',
|
||||
@ -5337,17 +5360,28 @@ export default {
|
||||
'channel.area.form.rules.countryRequired': 'Please select country/region',
|
||||
'channel.area.form.rules.destCountryRequired': 'Please select country/region',
|
||||
'channel.area.section.match': 'Match scope',
|
||||
'channel.area.section.postcode': 'Postcode range',
|
||||
'channel.area.tip.country': 'Required; country only matches all postcodes in that country',
|
||||
'channel.area.tip.province': 'Optional; blank means any state/province',
|
||||
'channel.area.tip.city': 'Optional; blank means any city',
|
||||
'channel.area.tip.leftPostCode': 'Pair with end postcode; if one side is filled the other is required',
|
||||
'channel.area.tip.rightPostCode': 'Must be greater than or equal to start postcode',
|
||||
'channel.area.section.postcode': 'Postcode match',
|
||||
'channel.area.tip.country': 'Required; Country-only is the lowest-priority fallback for that country',
|
||||
'channel.area.tip.province': 'Required for state or city dimension',
|
||||
'channel.area.tip.city': 'Required for city dimension; state is also required',
|
||||
'channel.area.tip.leftPostCode': 'Prefix: leading digits; Exact: full postcode; Range: start postcode',
|
||||
'channel.area.tip.rightPostCode': 'Range end postcode, closed interval (inclusive)',
|
||||
'channel.area.tip.matchDim': 'One row, one dimension. Postcode beats state/city; country-only is last fallback',
|
||||
'channel.area.tip.matchMode': 'Prefix matches leading chars; exact is equality; range is inclusive',
|
||||
'channel.area.form.rules.maxLength128': 'Cannot exceed 128 characters',
|
||||
'channel.area.form.rules.maxLength30': 'Cannot exceed 30 characters',
|
||||
'channel.area.form.rules.noChinese': 'Cannot contain Chinese characters',
|
||||
'channel.area.form.rules.noSpacesOnly': 'Start postcode cannot be spaces only',
|
||||
'channel.area.form.rules.noSpacesOnlyEnd': 'End postcode cannot be spaces only',
|
||||
'channel.area.form.rules.matchDimRequired': 'Select a match dimension',
|
||||
'channel.area.form.rules.matchModeRequired': 'Select a match mode',
|
||||
'channel.area.form.rules.provinceRequired': 'Enter state/province',
|
||||
'channel.area.form.rules.cityRequired': 'Enter city',
|
||||
'channel.area.form.rules.cityRequiresState': 'City match requires state/province',
|
||||
'channel.area.form.rules.postcodeRequired': 'Enter postcode',
|
||||
'channel.area.form.rules.postcodeRangeRequired': 'Range match requires end postcode',
|
||||
'channel.area.form.placeholder.provinceRequired': 'Enter state/province',
|
||||
'channel.area.form.placeholder.cityRequired': 'Enter city',
|
||||
'channel.area.modal.addArea': 'Add area',
|
||||
'channel.area.modal.editArea': 'Edit area',
|
||||
'channel.area.modal.addDetail': 'Add area detail',
|
||||
@ -5376,12 +5410,12 @@ export default {
|
||||
'channel.config.detailCount': '{count} detail record(s)',
|
||||
'channel.area.tip.addArea': 'Areas group detail records. Use a clear name such as "US West Coast" or "EU Remote".',
|
||||
'channel.area.tip.editArea': 'Renaming an area does not change existing details. Avoid duplicate names.',
|
||||
'channel.area.tip.addDetail': 'Country/region is required; state/province and city are optional. When using postcodes, start must not exceed end. Country only matches all postcodes in that destCountry.',
|
||||
'channel.area.tip.editDetail': 'Changes apply immediately to channel matching. Verify destCountry and postcode range.',
|
||||
'channel.area.tip.addDetail': 'One row, one dimension. Postcode (prefix/exact/closed range) wins; then state; then city (needs state); country-only is last fallback.',
|
||||
'channel.area.tip.editDetail': 'Changes apply immediately to quote/billing matching. Verify dimension and postcode rules.',
|
||||
'channel.area.form.placeholder.provinceOptional': 'Optional; leave blank for any state/province',
|
||||
'channel.area.form.placeholder.cityOptional': 'Optional; leave blank for any city',
|
||||
'channel.area.form.placeholder.leftPostCode': 'Optional; pair with end postcode',
|
||||
'channel.area.form.placeholder.rightPostCode': 'Optional; must be greater than or equal to start',
|
||||
'channel.area.form.placeholder.leftPostCode': 'Prefix, exact postcode, or range start',
|
||||
'channel.area.form.placeholder.rightPostCode': 'Range end (inclusive)',
|
||||
'channel.area.form.rules.maxLength50': 'Cannot exceed 50 characters',
|
||||
'channel.area.form.rules.postcodeRangeInvalid': 'Start postcode cannot be greater than end postcode',
|
||||
'channel.area.modal.deleteConfirmWithName': 'Delete area "{name}"? All details under it will be removed.',
|
||||
|
||||
@ -313,6 +313,8 @@ export default {
|
||||
'warehouse.parcel.drawer.frozenQty': '冻结',
|
||||
'warehouse.parcel.drawer.itemCount': '共 {n} 项',
|
||||
'warehouse.parcel.tip': '可用=可绑定发货;冻结=已被订单占用。点状态 Tab 快速筛选在途/到仓/已上架。',
|
||||
'warehouse.parcel.pluginHint': '懒得填写包裹信息?那就使用一键添加包裹插件吧。',
|
||||
'warehouse.parcel.pluginHintDownload': '去右上角下载插件',
|
||||
'warehouse.parcel.drawer.edit': '编辑包裹',
|
||||
'warehouse.parcel.drawer.empty': '暂无商品明细',
|
||||
'warehouse.parcel.table.arrivalTime': '到仓时间',
|
||||
@ -1222,42 +1224,48 @@ export default {
|
||||
'nanaAssistant.tabAi': 'AI Nana',
|
||||
'nanaAssistant.tabAdvisor': '专属顾问',
|
||||
'nanaAssistant.badge': '物流顾问',
|
||||
'nanaAssistant.welcomeTitle': '这次发货,需要什么帮助?',
|
||||
'nanaAssistant.welcomeSub': '查价格、品名能否发、查轨迹 — 点选下方能力后补充信息即可。',
|
||||
'nanaAssistant.welcomeTitle': '今天想查点什么?',
|
||||
'nanaAssistant.welcomeHello': '{greet},小艾为您服务',
|
||||
'nanaAssistant.welcomeSub': '今天想查点什么?点下面,或自己打字问我。',
|
||||
'nanaAssistant.greet.morning': '早上好',
|
||||
'nanaAssistant.greet.noon': '中午好',
|
||||
'nanaAssistant.greet.afternoon': '下午好',
|
||||
'nanaAssistant.greet.evening': '晚上好',
|
||||
'nanaAssistant.greet.night': '夜深了',
|
||||
'nanaAssistant.quickLabel': '常见问题',
|
||||
'nanaAssistant.welcomeMsg': '你好,我是 IDE 物流顾问。可以点选下方问题,或直接输入。',
|
||||
'nanaAssistant.welcomeMsg': '你好,我是 IDE 物流顾问。可以直接问,也可以点下面的入口。',
|
||||
'nanaAssistant.historyTitle': '历史记录',
|
||||
'nanaAssistant.newChat': '新会话',
|
||||
'nanaAssistant.emptyHistory': '暂无会话',
|
||||
'nanaAssistant.searchHistory': '搜索会话或运单号',
|
||||
'nanaAssistant.noSearchResults': '未找到匹配的会话',
|
||||
'nanaAssistant.deleteThread': '删除会话',
|
||||
'nanaAssistant.hint': '直接提问,或点选上方能力。',
|
||||
'nanaAssistant.hintLive': '已接入智能顾问,可查价格、对客说明、轨迹。',
|
||||
'nanaAssistant.hint': '直接提问,或点上面的入口。',
|
||||
'nanaAssistant.hintLive': '可以问运费、能不能寄、包裹到哪了。',
|
||||
'nanaAssistant.inputPlaceholder': '输入您的问题…',
|
||||
'nanaAssistant.inputPlaceholderLive': '',
|
||||
'nanaAssistant.inputPlaceholderTrack': '',
|
||||
'nanaAssistant.mcp.sectionLabel': '可查能力',
|
||||
'nanaAssistant.mcp.exampleHint': '例:{sample}',
|
||||
'nanaAssistant.mcp.required': '必填',
|
||||
'nanaAssistant.mcp.optional': '选填',
|
||||
'nanaAssistant.mcp.missingAsk': '还需要补充:{fields}。补全后我再帮你查。',
|
||||
'nanaAssistant.mcp.priceTitle': '查价格',
|
||||
'nanaAssistant.mcp.priceDesc': '按目的国与重量试算运费',
|
||||
'nanaAssistant.mcp.priceTitle': '查运费',
|
||||
'nanaAssistant.mcp.priceDesc': '按目的国和重量估算运费',
|
||||
'nanaAssistant.mcp.priceCoach':
|
||||
'查价格需要你提供:\n① 目的国家(如美国 / US)\n② 实重 kg\n\n选填:邮编、长宽高(cm)、货物属性(普货/敏感)。\n补全后发送即可,例如:美国 0.5kg 邮编10001',
|
||||
'nanaAssistant.mcp.priceExample': '例:美国 0.5kg 邮编10001',
|
||||
'查运费需要:目的国家和实重(kg)。有邮编、尺寸会更准。\n补全后发送即可,例如:美国 0.5kg',
|
||||
'nanaAssistant.mcp.priceExample': '帮我查下美国0.5kg价格',
|
||||
'nanaAssistant.mcp.priceDraft': '目的国:\n实重(kg):\n邮编(选填):\n尺寸长宽高cm(选填):',
|
||||
'nanaAssistant.mcp.guideTitle': '品名能否发',
|
||||
'nanaAssistant.mcp.guideDesc': '对照渠道对客说明判断可否承运',
|
||||
'nanaAssistant.mcp.guideTitle': '这个货能不能寄',
|
||||
'nanaAssistant.mcp.guideDesc': '看这个品名能不能走这条渠道',
|
||||
'nanaAssistant.mcp.guideCoach':
|
||||
'判断品名能否承运需要:\n① 销售产品代码或名称(运输方式)\n② 客户品名(英文/中文均可)\n\n例如:产品 EX_ASP_HKDHL,品名 lipstick,能不能发?',
|
||||
'nanaAssistant.mcp.guideExample': '例:产品XX,品名 lipstick 能发吗',
|
||||
'判断能不能寄需要:销售产品(运输方式)和品名。\n例如:产品 XX,品名 lipstick,能发吗?',
|
||||
'nanaAssistant.mcp.guideExample': 'lipstick能寄吗',
|
||||
'nanaAssistant.mcp.guideDraft': '销售产品代码/名称:\n客户品名:',
|
||||
'nanaAssistant.mcp.trackTitle': '查轨迹',
|
||||
'nanaAssistant.mcp.trackDesc': '按运单号查看物流节点',
|
||||
'nanaAssistant.mcp.trackTitle': '包裹到哪了',
|
||||
'nanaAssistant.mcp.trackDesc': '看包裹现在到哪了',
|
||||
'nanaAssistant.mcp.trackCoach':
|
||||
'查轨迹请提供运单号 / 跟踪号 / 客户单号(任选其一)。\n若已从订单带入运单,可直接点「查轨迹」。',
|
||||
'nanaAssistant.mcp.trackExample': '例:请查运单 NFXXXX 的轨迹',
|
||||
'查包裹位置请发运单号。如果上面已经带入运单,直接点「查这票轨迹」即可。',
|
||||
'nanaAssistant.mcp.trackExample': '帮我查下运单NFXXXX到哪了',
|
||||
'nanaAssistant.mcp.trackDraft': '运单号:',
|
||||
'nanaAssistant.mcp.field.destCountry': '目的国家',
|
||||
'nanaAssistant.mcp.field.weight': '实重(kg)',
|
||||
@ -1310,6 +1318,10 @@ export default {
|
||||
'nanaAssistant.viewWaybill': '查看运单 →',
|
||||
'nanaAssistant.queryTrack': '查轨迹',
|
||||
'nanaAssistant.endWaybillConsult': '结束运单咨询',
|
||||
'nanaAssistant.waybillWelcomeTitle': '这票需要什么帮助?',
|
||||
'nanaAssistant.waybillWelcomeSub': '已带入运单 {no},可以直接查轨迹或问费用。',
|
||||
'nanaAssistant.waybillAskTrack': '查这票轨迹',
|
||||
'nanaAssistant.waybillAskFee': '问这票费用',
|
||||
'nanaAssistant.waybillAutoAsk': '请帮我查询运单 {no} 的物流轨迹',
|
||||
'nanaAssistant.trackWaybillDemo': '已收到运单 {no} 的查询。若已配置智能服务,将优先走真实接口;当前为参考回复:一般可在揽收、出口、干线、清关、妥投等节点看到更新。如有异常,专属顾问会主动跟进。',
|
||||
'nanaAssistant.copyWechat': '复制微信号',
|
||||
@ -2141,7 +2153,9 @@ export default {
|
||||
'order.priceCheck.form.warehouse': '仓库',
|
||||
'order.priceCheck.form.warehouse.placeholder': '全部',
|
||||
'order.priceCheck.form.destinationProvince': '目的州省',
|
||||
'order.priceCheck.form.destinationProvince.placeholder': '请选择',
|
||||
'order.priceCheck.form.destinationProvince.placeholder': '无邮编时按州匹配',
|
||||
'order.priceCheck.form.destinationCity': '目的城市',
|
||||
'order.priceCheck.form.destinationCity.placeholder': '无邮编时按城市匹配',
|
||||
'order.priceCheck.form.postalCode': '邮编',
|
||||
'order.priceCheck.form.postalCode.tip': '更准',
|
||||
'order.priceCheck.form.postalCode.placeholder': '选填,补全更准确',
|
||||
@ -2451,7 +2465,7 @@ export default {
|
||||
'order.placeOrder.single.exportQualificationManage': '管理备案',
|
||||
'order.placeOrder.single.exportQualificationLoadFailed': '加载出口资质备案失败',
|
||||
'order.placeOrder.single.platformCode': '电商平台',
|
||||
'order.placeOrder.single.platformCodePlaceholder': '如 Amazon / eBay / Shopify',
|
||||
'order.placeOrder.single.platformCodePlaceholder': '请选择电商平台',
|
||||
'order.placeOrder.single.showExtraCols': '更多字段',
|
||||
'order.placeOrder.single.hideExtraCols': '收起',
|
||||
'order.placeOrder.single.declarationPickSku': '从 SKU 选择',
|
||||
@ -5367,6 +5381,15 @@ export default {
|
||||
'channel.area.form.city': '城市',
|
||||
'channel.area.form.leftPostCode': '起始邮编',
|
||||
'channel.area.form.rightPostCode': '截止邮编',
|
||||
'channel.area.form.matchDim': '匹配维度',
|
||||
'channel.area.form.matchMode': '匹配方式',
|
||||
'channel.area.matchDim.country': '仅国家',
|
||||
'channel.area.matchDim.zip': '邮编',
|
||||
'channel.area.matchDim.state': '州/省',
|
||||
'channel.area.matchDim.city': '城市',
|
||||
'channel.area.matchMode.prefix': '前缀',
|
||||
'channel.area.matchMode.exact': '精确',
|
||||
'channel.area.matchMode.range': '区间',
|
||||
'channel.area.form.placeholder.keyword': '请输入关键字',
|
||||
'channel.area.form.placeholder.areaName': '请输入分区名称',
|
||||
'channel.area.form.placeholder.input': '请输入',
|
||||
@ -5374,17 +5397,28 @@ export default {
|
||||
'channel.area.form.rules.countryRequired': '请选择国家/地区',
|
||||
'channel.area.form.rules.destCountryRequired': '请选择国家/地区',
|
||||
'channel.area.section.match': '匹配范围',
|
||||
'channel.area.section.postcode': '邮编区间',
|
||||
'channel.area.tip.country': '必填;仅填国家表示匹配该国全部邮编',
|
||||
'channel.area.tip.province': '选填,留空表示不限州/省',
|
||||
'channel.area.tip.city': '选填,留空表示不限城市',
|
||||
'channel.area.tip.leftPostCode': '与截止邮编成对填写;可只填一侧时另一侧必填',
|
||||
'channel.area.tip.rightPostCode': '须大于等于起始邮编',
|
||||
'channel.area.section.postcode': '邮编匹配',
|
||||
'channel.area.tip.country': '必填;选择「仅国家」表示该国最低优先级兜底',
|
||||
'channel.area.tip.province': '州/省或城市维度时必填',
|
||||
'channel.area.tip.city': '城市维度时必填,且必须同时填写州/省',
|
||||
'channel.area.tip.leftPostCode': '前缀填邮编前几位;精确填完整邮编;区间填起始邮编',
|
||||
'channel.area.tip.rightPostCode': '区间截止邮编,闭区间(含起止)',
|
||||
'channel.area.tip.matchDim': '一行只表达一个维度:邮编优先于州/省、城市;仅国家为最低兜底',
|
||||
'channel.area.tip.matchMode': '前缀按左起位数匹配;精确全等;区间为闭区间',
|
||||
'channel.area.form.rules.maxLength128': '限制不可超过128个字符',
|
||||
'channel.area.form.rules.maxLength30': '限制不可超过30个字符',
|
||||
'channel.area.form.rules.noChinese': '不能存在中文字符',
|
||||
'channel.area.form.rules.noSpacesOnly': '起始邮编不可只输入空格',
|
||||
'channel.area.form.rules.noSpacesOnlyEnd': '截止邮编不可只输入空格',
|
||||
'channel.area.form.rules.matchDimRequired': '请选择匹配维度',
|
||||
'channel.area.form.rules.matchModeRequired': '请选择匹配方式',
|
||||
'channel.area.form.rules.provinceRequired': '请填写州/省',
|
||||
'channel.area.form.rules.cityRequired': '请填写城市',
|
||||
'channel.area.form.rules.cityRequiresState': '城市匹配必须同时填写州/省',
|
||||
'channel.area.form.rules.postcodeRequired': '请填写邮编',
|
||||
'channel.area.form.rules.postcodeRangeRequired': '区间匹配请填写截止邮编',
|
||||
'channel.area.form.placeholder.provinceRequired': '请输入州/省',
|
||||
'channel.area.form.placeholder.cityRequired': '请输入城市',
|
||||
'channel.area.modal.addArea': '新增分区',
|
||||
'channel.area.modal.editArea': '修改分区',
|
||||
'channel.area.modal.addDetail': '新增分区明细',
|
||||
@ -5405,7 +5439,7 @@ export default {
|
||||
'channel.area.search.button.query': '查询',
|
||||
'channel.area.search.button.reset': '重置',
|
||||
'channel.area.import.modalTitle': '导入分区明细',
|
||||
'channel.area.import.tip': '请先下载模板,按列填写国家/邮编等信息后上传 Excel(.xlsx/.xls)。国家代码必填;仅填国家代码时将自动匹配国家名称。',
|
||||
'channel.area.import.tip': '请先下载模板。国家代码必填。一行一个维度:填邮编则为邮编规则(只填起始=前缀;起止相同=精确;起止不同=闭区间);不填邮编只填州=州匹配;州+城市=城市匹配;都空=仅国家兜底。',
|
||||
'channel.area.import.downloadTemplate': '下载模板',
|
||||
'channel.area.import.submit': '开始导入',
|
||||
'channel.area.import.downloadSuccess': '模板下载成功',
|
||||
@ -5428,12 +5462,12 @@ export default {
|
||||
'channel.config.detailCount': '共 {count} 条明细',
|
||||
'channel.area.tip.addArea': '分区用于归类管理明细。名称建议简洁明确,例如「美国西海岸」或「欧盟偏远」。',
|
||||
'channel.area.tip.editArea': '修改分区名称不会影响已有明细,但请避免与现有分区重名造成混淆。',
|
||||
'channel.area.tip.addDetail': '国家/地区必填;州/省、城市可选。填写邮编区间时,起始邮编不能大于截止邮编;仅填国家表示匹配该国全部邮编。',
|
||||
'channel.area.tip.editDetail': '修改后将立即影响渠道匹配逻辑,请确认国家与邮编区间准确无误。',
|
||||
'channel.area.tip.addDetail': '一行只选一个维度。有邮编时只按邮编匹配(前缀/精确/闭区间);无邮编才按州、再按城市;仅国家为最低兜底。',
|
||||
'channel.area.tip.editDetail': '修改后立即影响查价/计费匹配,请确认维度与邮编规则。',
|
||||
'channel.area.form.placeholder.provinceOptional': '选填,留空表示不限州/省',
|
||||
'channel.area.form.placeholder.cityOptional': '选填,留空表示不限城市',
|
||||
'channel.area.form.placeholder.leftPostCode': '选填,与截止邮编成对填写',
|
||||
'channel.area.form.placeholder.rightPostCode': '选填,需大于等于起始邮编',
|
||||
'channel.area.form.placeholder.leftPostCode': '前缀/精确邮编,或区间起始',
|
||||
'channel.area.form.placeholder.rightPostCode': '区间截止(含)',
|
||||
'channel.area.form.rules.maxLength50': '限制不可超过50个字符',
|
||||
'channel.area.form.rules.postcodeRangeInvalid': '起始邮编不能大于截止邮编',
|
||||
'channel.area.modal.deleteConfirmWithName': '确定删除分区「{name}」吗?其下明细将一并删除。',
|
||||
|
||||
@ -43,6 +43,74 @@ function trimStr(v: unknown): string {
|
||||
return String(v ?? '').trim();
|
||||
}
|
||||
|
||||
function inferGeoMatch(data: Record<string, any>) {
|
||||
const dim = String(data.matchDim || '').trim();
|
||||
const mode = String(data.matchMode || '').trim();
|
||||
if (dim) {
|
||||
return {
|
||||
matchDim: dim,
|
||||
matchMode: mode || (dim === 'zip' ? 'range' : dim === 'country' ? 'all' : 'exact'),
|
||||
};
|
||||
}
|
||||
const left = trimStr(data.leftPostCode || data.leftPostcode);
|
||||
const right = trimStr(data.rightPostCode || data.rightPostcode);
|
||||
if (left || right) {
|
||||
let matchMode = 'range';
|
||||
if (left && !right) matchMode = 'prefix';
|
||||
else if (left && right && left === right) matchMode = 'exact';
|
||||
return { matchDim: 'zip', matchMode };
|
||||
}
|
||||
if (trimStr(data.city)) return { matchDim: 'city', matchMode: 'exact' };
|
||||
if (trimStr(data.province)) return { matchDim: 'state', matchMode: 'exact' };
|
||||
return { matchDim: 'country', matchMode: 'all' };
|
||||
}
|
||||
|
||||
function applyMatchDimension(data: Record<string, any>) {
|
||||
const rest = normalizePostcodeFields({ ...data });
|
||||
const { matchDim, matchMode } = inferGeoMatch(rest);
|
||||
rest.matchDim = matchDim;
|
||||
rest.matchMode = matchMode;
|
||||
|
||||
if (matchDim === 'country') {
|
||||
rest.province = undefined;
|
||||
rest.city = undefined;
|
||||
rest.leftPostCode = undefined;
|
||||
rest.rightPostCode = undefined;
|
||||
rest.leftPostcode = undefined;
|
||||
rest.rightPostcode = undefined;
|
||||
} else if (matchDim === 'state') {
|
||||
rest.city = undefined;
|
||||
rest.leftPostCode = undefined;
|
||||
rest.rightPostCode = undefined;
|
||||
rest.leftPostcode = undefined;
|
||||
rest.rightPostcode = undefined;
|
||||
} else if (matchDim === 'city') {
|
||||
rest.leftPostCode = undefined;
|
||||
rest.rightPostCode = undefined;
|
||||
rest.leftPostcode = undefined;
|
||||
rest.rightPostcode = undefined;
|
||||
} else if (matchDim === 'zip') {
|
||||
rest.province = undefined;
|
||||
rest.city = undefined;
|
||||
const left = trimStr(rest.leftPostCode);
|
||||
if (matchMode === 'prefix') {
|
||||
rest.rightPostCode = undefined;
|
||||
rest.rightPostcode = undefined;
|
||||
rest.leftPostCode = left || undefined;
|
||||
rest.leftPostcode = left || undefined;
|
||||
} else if (matchMode === 'exact') {
|
||||
rest.leftPostCode = left || undefined;
|
||||
rest.leftPostcode = left || undefined;
|
||||
rest.rightPostCode = left || undefined;
|
||||
rest.rightPostcode = left || undefined;
|
||||
} else {
|
||||
rest.leftPostcode = rest.leftPostCode;
|
||||
rest.rightPostcode = rest.rightPostCode;
|
||||
}
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
|
||||
function normalizePostcodeFields(data: Record<string, any>) {
|
||||
const leftPostCode = trimStr(data.leftPostCode);
|
||||
const rightPostCode = trimStr(data.rightPostCode);
|
||||
@ -91,20 +159,48 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
}
|
||||
|
||||
function beforeSubmitRightAreaInfo(data: Record<string, any>) {
|
||||
const rest = normalizePostcodeFields({ ...data });
|
||||
const rest = applyMatchDimension({ ...data });
|
||||
delete rest._countryQuickTags;
|
||||
delete rest.sectionMatch;
|
||||
delete rest.sectionPostcode;
|
||||
|
||||
const left = rest.leftPostCode;
|
||||
const right = rest.rightPostCode;
|
||||
const { matchDim, matchMode } = inferGeoMatch(rest);
|
||||
if (matchDim === 'state' && !trimStr(rest.province)) {
|
||||
$baseMessage(t('channel.area.form.rules.provinceRequired'), 'warning');
|
||||
throw new Error('province-required');
|
||||
}
|
||||
if (matchDim === 'city') {
|
||||
if (!trimStr(rest.province) || !trimStr(rest.city)) {
|
||||
$baseMessage(t('channel.area.form.rules.cityRequiresState'), 'warning');
|
||||
throw new Error('city-requires-state');
|
||||
}
|
||||
}
|
||||
if (matchDim === 'zip') {
|
||||
const left = trimStr(rest.leftPostCode);
|
||||
const right = trimStr(rest.rightPostCode);
|
||||
if (!left) {
|
||||
$baseMessage(t('channel.area.form.rules.postcodeRequired'), 'warning');
|
||||
throw new Error('postcode-required');
|
||||
}
|
||||
if (matchMode === 'range') {
|
||||
if (!right) {
|
||||
$baseMessage(t('channel.area.form.rules.postcodeRangeRequired'), 'warning');
|
||||
throw new Error('postcode-range-required');
|
||||
}
|
||||
if (isPostcodeRangeInvalid(left, right)) {
|
||||
$baseMessage(t('channel.area.form.rules.postcodeRangeInvalid'), 'warning');
|
||||
throw new Error('postcode-range-invalid');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const code = rest?.countryCode;
|
||||
const hit = countryRows.value.find((r) => r.countryCode === code);
|
||||
return {
|
||||
...rest,
|
||||
matchDim,
|
||||
matchMode,
|
||||
prefixLen: matchDim === 'zip' && matchMode === 'prefix' ? trimStr(rest.leftPostCode).length : rest.prefixLen,
|
||||
countryName: hit?.chineseName ?? rest?.countryName ?? '',
|
||||
};
|
||||
}
|
||||
@ -219,17 +315,60 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
label: '',
|
||||
span: 24,
|
||||
},
|
||||
{
|
||||
label: t('channel.area.form.matchDim'),
|
||||
field: 'matchDim',
|
||||
comp: 'a-select',
|
||||
tooltip: t('channel.area.tip.matchDim'),
|
||||
compProps: {
|
||||
options: [
|
||||
{ label: t('channel.area.matchDim.country'), value: 'country' },
|
||||
{ label: t('channel.area.matchDim.zip'), value: 'zip' },
|
||||
{ label: t('channel.area.matchDim.state'), value: 'state' },
|
||||
{ label: t('channel.area.matchDim.city'), value: 'city' },
|
||||
],
|
||||
placeholder: t('common.selectPlaceholder'),
|
||||
allowClear: false,
|
||||
},
|
||||
rules: [{ required: true, message: t('channel.area.form.rules.matchDimRequired') }],
|
||||
span: 12,
|
||||
},
|
||||
{
|
||||
label: t('channel.area.form.matchMode'),
|
||||
field: 'matchMode',
|
||||
comp: 'a-select',
|
||||
tooltip: t('channel.area.tip.matchMode'),
|
||||
ifShow: (model: Record<string, any>) => model.matchDim === 'zip',
|
||||
compProps: {
|
||||
options: [
|
||||
{ label: t('channel.area.matchMode.prefix'), value: 'prefix' },
|
||||
{ label: t('channel.area.matchMode.exact'), value: 'exact' },
|
||||
{ label: t('channel.area.matchMode.range'), value: 'range' },
|
||||
],
|
||||
placeholder: t('common.selectPlaceholder'),
|
||||
allowClear: false,
|
||||
},
|
||||
rules: [{ required: true, message: t('channel.area.form.rules.matchModeRequired') }],
|
||||
span: 12,
|
||||
},
|
||||
{
|
||||
label: t('channel.area.form.province'),
|
||||
field: 'province',
|
||||
comp: 'a-input',
|
||||
tooltip: t('channel.area.tip.province'),
|
||||
ifShow: (model: Record<string, any>) =>
|
||||
model.matchDim === 'state' || model.matchDim === 'city',
|
||||
compProps: {
|
||||
placeholder: t('channel.area.form.placeholder.provinceOptional'),
|
||||
placeholder: t('channel.area.form.placeholder.provinceRequired'),
|
||||
clearable: true,
|
||||
allowClear: true,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: t('channel.area.form.rules.provinceRequired'),
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
max: 128,
|
||||
message: t('channel.area.form.rules.maxLength128'),
|
||||
@ -243,12 +382,18 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
field: 'city',
|
||||
comp: 'a-input',
|
||||
tooltip: t('channel.area.tip.city'),
|
||||
ifShow: (model: Record<string, any>) => model.matchDim === 'city',
|
||||
compProps: {
|
||||
placeholder: t('channel.area.form.placeholder.cityOptional'),
|
||||
placeholder: t('channel.area.form.placeholder.cityRequired'),
|
||||
clearable: true,
|
||||
allowClear: true,
|
||||
},
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: t('channel.area.form.rules.cityRequired'),
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
max: 128,
|
||||
message: t('channel.area.form.rules.maxLength128'),
|
||||
@ -264,12 +409,14 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
hideColon: true,
|
||||
comp: 'slot',
|
||||
span: 24,
|
||||
ifShow: (model: Record<string, any>) => model.matchDim === 'zip',
|
||||
},
|
||||
{
|
||||
label: t('channel.area.form.leftPostCode'),
|
||||
field: 'leftPostCode',
|
||||
comp: 'a-input',
|
||||
tooltip: t('channel.area.tip.leftPostCode'),
|
||||
ifShow: (model: Record<string, any>) => model.matchDim === 'zip',
|
||||
compProps: {
|
||||
placeholder: t('channel.area.form.placeholder.leftPostCode'),
|
||||
clearable: true,
|
||||
@ -277,21 +424,11 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
},
|
||||
dynamicRules: ({ model }: { model: Record<string, any> }) => [
|
||||
{
|
||||
required: !!model.rightPostCode,
|
||||
required: model.matchDim === 'zip',
|
||||
pattern: /^((?![\u4E00-\u9FA5]).)*$/,
|
||||
message: t('channel.area.form.rules.noChinese'),
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (_rule: any, value: any, callback: any) => {
|
||||
if (value && !String(value).trim()) {
|
||||
callback(new Error(t('channel.area.form.rules.noSpacesOnly')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
max: 30,
|
||||
message: t('channel.area.form.rules.maxLength30'),
|
||||
@ -305,6 +442,8 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
field: 'rightPostCode',
|
||||
comp: 'a-input',
|
||||
tooltip: t('channel.area.tip.rightPostCode'),
|
||||
ifShow: (model: Record<string, any>) =>
|
||||
model.matchDim === 'zip' && model.matchMode === 'range',
|
||||
compProps: {
|
||||
placeholder: t('channel.area.form.placeholder.rightPostCode'),
|
||||
clearable: true,
|
||||
@ -312,16 +451,15 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
},
|
||||
dynamicRules: ({ model }: { model: Record<string, any> }) => [
|
||||
{
|
||||
required: !!model.leftPostCode,
|
||||
required: model.matchDim === 'zip' && model.matchMode === 'range',
|
||||
pattern: /^((?![\u4E00-\u9FA5]).)*$/,
|
||||
message: t('channel.area.form.rules.noChinese'),
|
||||
trigger: 'change',
|
||||
},
|
||||
{
|
||||
validator: (_rule: any, value: any, callback: any) => {
|
||||
if (value && !String(value).trim()) {
|
||||
callback(new Error(t('channel.area.form.rules.noSpacesOnlyEnd')));
|
||||
} else if (
|
||||
if (
|
||||
model.matchMode === 'range' &&
|
||||
model.leftPostCode &&
|
||||
value &&
|
||||
isPostcodeRangeInvalid(String(model.leftPostCode).trim(), String(value).trim())
|
||||
@ -415,6 +553,8 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
|
||||
const hotColumns = computed<ChannelHotColumn[]>(() => [
|
||||
{ key: 'countryName', label: t('channel.area.form.country'), width: 120 },
|
||||
{ key: 'matchDim', label: t('channel.area.form.matchDim'), width: 100 },
|
||||
{ key: 'matchMode', label: t('channel.area.form.matchMode'), width: 100 },
|
||||
{ key: 'province', label: t('channel.area.form.province'), width: 120 },
|
||||
{ key: 'city', label: t('channel.area.form.city'), width: 120 },
|
||||
{ key: 'leftPostcode', label: t('channel.area.table.leftPostcode'), width: 120 },
|
||||
@ -505,6 +645,11 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
(r) => r.chineseName === row.countryName || r.countryCode === row.countryName
|
||||
)?.countryCode ||
|
||||
'',
|
||||
...inferGeoMatch({
|
||||
...row,
|
||||
leftPostCode: row.leftPostcode ?? row.leftPostCode,
|
||||
rightPostCode: row.rightPostcode ?? row.rightPostCode,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -513,6 +658,8 @@ export function useKpiAreaPage(areaType: KpiAreaType) {
|
||||
title: titleObj[type],
|
||||
formData: {
|
||||
areaId: currentId.value?.id,
|
||||
matchDim: 'zip',
|
||||
matchMode: 'range',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -23,7 +23,11 @@
|
||||
|
||||
<template v-else>
|
||||
<div class="route-row">
|
||||
<button type="button" class="route-cell" @click="openPicker('warehouse')">
|
||||
<button
|
||||
type="button"
|
||||
class="route-cell route-cell--origin"
|
||||
@click="openPicker('warehouse')"
|
||||
>
|
||||
<span class="route-cell__label">{{ t('h5.price.origin') }}</span>
|
||||
<span class="route-cell__value">
|
||||
<span>{{ warehouseLabel || t('h5.price.originAny') }}</span>
|
||||
@ -32,28 +36,14 @@
|
||||
</button>
|
||||
<div class="route-mid" aria-hidden="true">
|
||||
<span class="route-mid__icon">
|
||||
<svg
|
||||
class="route-mid__arrow"
|
||||
viewBox="0 0 40 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 10h24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-dasharray="2.5 3.5"
|
||||
opacity="0.55"
|
||||
/>
|
||||
<path
|
||||
d="M26 4.2 36 10l-10 5.8V4.2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<IconRoutePackage class="route-mid__package" />
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" class="route-cell" @click="openPicker('country')">
|
||||
<button
|
||||
type="button"
|
||||
class="route-cell route-cell--dest"
|
||||
@click="openPicker('country')"
|
||||
>
|
||||
<span class="route-cell__label">{{ t('h5.price.destination') }}</span>
|
||||
<span class="route-cell__value" :class="{ 'is-placeholder': !destinationCode }">
|
||||
<span class="route-cell__dest">
|
||||
@ -79,6 +69,24 @@
|
||||
autocomplete="postal-code"
|
||||
/>
|
||||
</div>
|
||||
<div class="line-field">
|
||||
<span class="line-field__label">{{ t('order.priceCheck.form.destinationProvince') }}</span>
|
||||
<input
|
||||
v-model="form.destinationProvince"
|
||||
class="line-field__input"
|
||||
type="text"
|
||||
:placeholder="t('order.priceCheck.form.destinationProvince.placeholder')"
|
||||
/>
|
||||
</div>
|
||||
<div class="line-field">
|
||||
<span class="line-field__label">{{ t('order.priceCheck.form.destinationCity') }}</span>
|
||||
<input
|
||||
v-model="form.destinationCity"
|
||||
class="line-field__input"
|
||||
type="text"
|
||||
:placeholder="t('order.priceCheck.form.destinationCity.placeholder')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="pkg-row">
|
||||
<label class="pkg-cell">
|
||||
@ -518,6 +526,7 @@ import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { IconDown } from '@arco-design/web-vue/es/icon';
|
||||
import IconRoutePackage from '@/assets/icons/h5/route-package.svg';
|
||||
import type { CustomerProfileCompleteness } from '@/api/customer';
|
||||
import { postOrderPriceCheck, type PriceCheckQueryHint } from '@/api/order';
|
||||
import { getCountryList } from '@/api/system/country';
|
||||
@ -556,6 +565,8 @@ const form = reactive({
|
||||
destinationCountry: '',
|
||||
cargoWeight: '1',
|
||||
postalCode: '',
|
||||
destinationProvince: '',
|
||||
destinationCity: '',
|
||||
cargoType: '',
|
||||
warehouseId: undefined as number | undefined,
|
||||
length: '',
|
||||
@ -1007,7 +1018,8 @@ async function handleQuery() {
|
||||
...(length != null && width != null && height != null
|
||||
? { length: String(length), width: String(width), height: String(height) }
|
||||
: {}),
|
||||
destinationProvince: '',
|
||||
destinationProvince: form.destinationProvince?.trim() || '',
|
||||
destinationCity: form.destinationCity?.trim() || '',
|
||||
postalCode: form.postalCode.trim() || '',
|
||||
cargoTypes: form.cargoType ? [form.cargoType] : undefined,
|
||||
warehouseIds: form.warehouseId ? [form.warehouseId] : undefined,
|
||||
@ -1316,10 +1328,10 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.route-row {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 56px minmax(0, 1fr);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
align-items: center;
|
||||
column-gap: 2px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #eef0f3;
|
||||
}
|
||||
@ -1327,41 +1339,77 @@ onMounted(() => {
|
||||
.route-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
padding: 2px 4px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
&:active .route-cell__value {
|
||||
opacity: 0.72;
|
||||
}
|
||||
|
||||
/* 发货仓:贴左边 */
|
||||
&--origin {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
padding: 6px 56px 6px 0;
|
||||
|
||||
.route-cell__label {
|
||||
text-align: left;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.route-cell__value {
|
||||
justify-content: flex-start;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #4e5969;
|
||||
min-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 目的地:贴右边,略加重 */
|
||||
&--dest {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
padding: 6px 0 6px 56px;
|
||||
|
||||
.route-cell__label {
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #4e5969;
|
||||
}
|
||||
|
||||
.route-cell__value {
|
||||
justify-content: flex-end;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1d2129;
|
||||
min-height: 26px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.route-cell__label {
|
||||
font-size: 12px;
|
||||
color: #86909c;
|
||||
line-height: 1.2;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.route-cell__value {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
max-width: 100%;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1d2129;
|
||||
line-height: 1.25;
|
||||
min-height: 26px;
|
||||
|
||||
> span {
|
||||
min-width: 0;
|
||||
@ -1371,7 +1419,7 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
&.is-placeholder > span {
|
||||
color: #c9cdd4;
|
||||
color: color-mix(in srgb, var(--app-primary-color) 55%, #86909c);
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
@ -1386,7 +1434,7 @@ onMounted(() => {
|
||||
.route-cell__dest {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
gap: 5px;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
@ -1415,23 +1463,27 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.route-mid {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-self: center;
|
||||
min-width: 56px;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.route-mid__icon {
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--app-primary-color);
|
||||
}
|
||||
|
||||
.route-mid__arrow {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
.route-mid__package {
|
||||
width: 48px;
|
||||
height: 36px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
@ -44,6 +44,8 @@
|
||||
/** 仓库(单选;空表示全部) */
|
||||
warehouseId: undefined as number | undefined,
|
||||
postalCode: '',
|
||||
destinationProvince: '',
|
||||
destinationCity: '',
|
||||
});
|
||||
|
||||
/** 是否已发起过查询(用于空态 / 结果区切换) */
|
||||
@ -576,7 +578,8 @@
|
||||
: {}),
|
||||
cargoTypes: formData.cargoType ? [formData.cargoType] : undefined,
|
||||
warehouseIds: formData.warehouseId != null ? [formData.warehouseId] : undefined,
|
||||
destinationProvince: '',
|
||||
destinationProvince: formData.destinationProvince?.trim() || '',
|
||||
destinationCity: formData.destinationCity?.trim() || '',
|
||||
postalCode: formData.postalCode?.trim() || '',
|
||||
couponInstanceId: couponInstanceId.value,
|
||||
});
|
||||
@ -617,6 +620,8 @@
|
||||
cargoType: '',
|
||||
warehouseId: undefined as number | undefined,
|
||||
postalCode: '',
|
||||
destinationProvince: '',
|
||||
destinationCity: '',
|
||||
});
|
||||
applyDefaultFilters();
|
||||
hasQueried.value = false;
|
||||
@ -668,7 +673,8 @@
|
||||
: {}),
|
||||
cargoTypes: [payload.cargoType],
|
||||
warehouseIds: [payload.warehouseId],
|
||||
destinationProvince: '',
|
||||
destinationProvince: formData.destinationProvince?.trim() || '',
|
||||
destinationCity: formData.destinationCity?.trim() || '',
|
||||
postalCode: formData.postalCode?.trim() || '',
|
||||
couponInstanceId: couponInstanceId.value,
|
||||
});
|
||||
@ -1014,6 +1020,26 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.destinationProvince') }}</label>
|
||||
<a-input
|
||||
v-model="formData.destinationProvince"
|
||||
:placeholder="$t('order.priceCheck.form.destinationProvince.placeholder')"
|
||||
class="form-input"
|
||||
@press-enter="handleQuery"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item">
|
||||
<label class="form-label">{{ $t('order.priceCheck.form.destinationCity') }}</label>
|
||||
<a-input
|
||||
v-model="formData.destinationCity"
|
||||
:placeholder="$t('order.priceCheck.form.destinationCity.placeholder')"
|
||||
class="form-input"
|
||||
@press-enter="handleQuery"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-item button-group">
|
||||
<label class="form-label form-label-spacer" aria-hidden="true"> </label>
|
||||
<div class="button-group-row">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user