提交
This commit is contained in:
1 parent
88da74d72a
commit
9fcb9270e6
53 files changed
+612846
-3438
No files matched your search
@@ -1,14 +1,34 @@
|
||||
<template>
|
||||
<div class="pl-address-with-library">
|
||||
<div class="pl-address-with-library__row">
|
||||
<AmapAddressPicker
|
||||
v-model="innerAddress"
|
||||
v-model:lng="innerLng"
|
||||
v-model:lat="innerLat"
|
||||
<el-select
|
||||
v-model="selectedId"
|
||||
class="pl-address-with-library__picker"
|
||||
popper-class="pl-address-select-dropdown"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
filterable
|
||||
remote
|
||||
clearable
|
||||
:remote-method="searchAddresses"
|
||||
:loading="loading"
|
||||
:reserve-keyword="false"
|
||||
@focus="handleFocus"
|
||||
@clear="handleClear"
|
||||
@change="handleSelectChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in displayOptions"
|
||||
:key="item.id"
|
||||
:label="optionLabel(item)"
|
||||
:value="item.id"
|
||||
>
|
||||
<div class="pl-address-option">
|
||||
<span class="pl-address-option__name">{{ item.addressName || '—' }}</span>
|
||||
<span class="pl-address-option__full">{{ item.fullAddress || '—' }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button
|
||||
class="pl-address-with-library__library-btn btn-ghost"
|
||||
:disabled="disabled"
|
||||
@@ -23,18 +43,23 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FolderOpened } from '@element-plus/icons-vue'
|
||||
import AmapAddressPicker from '@/components/business/amap-address-picker/index.vue'
|
||||
import PlTransportAddressSelectDialog from '@/components/business/pl-transport-address-select-dialog/index.vue'
|
||||
import type { TransportAddress } from '@/api/pl/index'
|
||||
import { transportAddressApi, type TransportAddress } from '@/api/pl/index'
|
||||
import { pickBxList } from '@/utils/http/bx'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string
|
||||
lng?: number | null
|
||||
lat?: number | null
|
||||
contact?: string
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
}>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
lng?: number | null
|
||||
lat?: number | null
|
||||
contact?: string
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{
|
||||
placeholder: '请选择地址(可按地址名称、详细地址检索)'
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
@@ -43,22 +68,31 @@ const emit = defineEmits<{
|
||||
'update:contact': [value: string]
|
||||
}>()
|
||||
|
||||
/** 下拉默认最多展示条数 */
|
||||
const DROPDOWN_PAGE_SIZE = 10
|
||||
|
||||
const libraryVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const options = ref<TransportAddress[]>([])
|
||||
const selectedId = ref('')
|
||||
/** 编辑态/历史地址:当前值不在地址库时的回显项 */
|
||||
const legacyOption = ref<TransportAddress | null>(null)
|
||||
|
||||
const innerAddress = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value: string) => emit('update:modelValue', value)
|
||||
const displayOptions = computed(() => {
|
||||
const list = options.value
|
||||
const legacy = legacyOption.value
|
||||
if (!legacy) return list
|
||||
if (list.some(item => item.id === legacy.id)) return list
|
||||
// 历史回显优先,仍控制总量不超过默认展示数 + 1(回显项)
|
||||
return [legacy, ...list].slice(0, DROPDOWN_PAGE_SIZE + 1)
|
||||
})
|
||||
|
||||
const innerLng = computed({
|
||||
get: () => props.lng ?? null,
|
||||
set: (value: number | null) => emit('update:lng', value)
|
||||
})
|
||||
|
||||
const innerLat = computed({
|
||||
get: () => props.lat ?? null,
|
||||
set: (value: number | null) => emit('update:lat', value)
|
||||
})
|
||||
function optionLabel(item: TransportAddress): string {
|
||||
const name = (item.addressName || '').trim()
|
||||
const full = (item.fullAddress || '').trim()
|
||||
if (name && full) return `${name}(${full})`
|
||||
return name || full || '—'
|
||||
}
|
||||
|
||||
function formatAddressContact(row: TransportAddress): string {
|
||||
const name = (row.contactName || '').trim()
|
||||
@@ -67,15 +101,124 @@ function formatAddressContact(row: TransportAddress): string {
|
||||
return name || phone
|
||||
}
|
||||
|
||||
function handleLibrarySelect(row: TransportAddress) {
|
||||
innerAddress.value = row.fullAddress
|
||||
innerLng.value = row.lng != null ? Number(row.lng) : null
|
||||
innerLat.value = row.lat != null ? Number(row.lat) : null
|
||||
function applyAddressRow(row: TransportAddress) {
|
||||
selectedId.value = row.id
|
||||
legacyOption.value = null
|
||||
emit('update:modelValue', row.fullAddress || '')
|
||||
emit('update:lng', row.lng != null ? Number(row.lng) : null)
|
||||
emit('update:lat', row.lat != null ? Number(row.lat) : null)
|
||||
const contactText = formatAddressContact(row)
|
||||
if (contactText) {
|
||||
emit('update:contact', contactText)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 与「从地址库选择」弹窗同源(启用中的运输地址库)。
|
||||
* 模糊检索走后端 q:仅匹配地址名称、详细地址(/transport/address/all)。
|
||||
*/
|
||||
async function searchAddresses(keyword: string) {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await transportAddressApi.listAll({
|
||||
q: keyword.trim() || undefined
|
||||
})
|
||||
options.value = pickBxList<TransportAddress>(res).slice(0, DROPDOWN_PAGE_SIZE)
|
||||
syncSelectedFromModel()
|
||||
} catch (error) {
|
||||
console.error('加载运输地址列表失败', error)
|
||||
options.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleFocus() {
|
||||
if (options.value.length === 0) {
|
||||
searchAddresses('')
|
||||
}
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
selectedId.value = ''
|
||||
legacyOption.value = null
|
||||
emit('update:modelValue', '')
|
||||
emit('update:lng', null)
|
||||
emit('update:lat', null)
|
||||
}
|
||||
|
||||
function handleSelectChange(id: string) {
|
||||
if (!id) {
|
||||
handleClear()
|
||||
return
|
||||
}
|
||||
const row = displayOptions.value.find(item => item.id === id)
|
||||
if (!row) return
|
||||
if (row.id.startsWith('legacy-')) {
|
||||
selectedId.value = row.id
|
||||
emit('update:modelValue', row.fullAddress || '')
|
||||
return
|
||||
}
|
||||
applyAddressRow(row)
|
||||
}
|
||||
|
||||
function handleLibrarySelect(row: TransportAddress) {
|
||||
// 弹窗选中后并入当前 options,保证下拉可回显
|
||||
if (!options.value.some(item => item.id === row.id)) {
|
||||
options.value = [row, ...options.value].slice(0, DROPDOWN_PAGE_SIZE)
|
||||
}
|
||||
applyAddressRow(row)
|
||||
}
|
||||
|
||||
function buildLegacyOption(address: string): TransportAddress {
|
||||
return {
|
||||
id: `legacy-${address}`,
|
||||
addressName: address,
|
||||
fullAddress: address,
|
||||
lng: props.lng != null ? Number(props.lng) : 0,
|
||||
lat: props.lat != null ? Number(props.lat) : 0,
|
||||
status: '1',
|
||||
createTime: '',
|
||||
updateTime: ''
|
||||
}
|
||||
}
|
||||
|
||||
function syncSelectedFromModel() {
|
||||
const current = (props.modelValue || '').trim()
|
||||
if (!current) {
|
||||
selectedId.value = ''
|
||||
legacyOption.value = null
|
||||
return
|
||||
}
|
||||
|
||||
const matched =
|
||||
options.value.find(item => item.fullAddress === current) ||
|
||||
(selectedId.value
|
||||
? options.value.find(item => item.id === selectedId.value)
|
||||
: undefined)
|
||||
|
||||
if (matched) {
|
||||
selectedId.value = matched.id
|
||||
legacyOption.value = null
|
||||
return
|
||||
}
|
||||
|
||||
// 已选中的库内 id 仍在 options 中
|
||||
if (selectedId.value && options.value.some(item => item.id === selectedId.value)) {
|
||||
return
|
||||
}
|
||||
|
||||
legacyOption.value = buildLegacyOption(current)
|
||||
selectedId.value = legacyOption.value.id
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
syncSelectedFromModel()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -93,10 +236,51 @@ function handleLibrarySelect(row: TransportAddress) {
|
||||
.pl-address-with-library__picker {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pl-address-with-library__library-btn {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pl-address-option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
line-height: 1.4;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.pl-address-option__name {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pl-address-option__full {
|
||||
color: var(--el-text-color-secondary, var(--mint-text-muted, #64748b));
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
/* teleported 下拉挂到 body,需非 scoped;增高选项避免双行地址被裁切 */
|
||||
.pl-address-select-dropdown {
|
||||
.el-select-dropdown__wrap {
|
||||
/* 约 10 条 × 较高选项高度 */
|
||||
max-height: 560px;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item {
|
||||
height: auto !important;
|
||||
min-height: 56px;
|
||||
padding: 10px 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -784,17 +784,81 @@ function handleCustomerSelectFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
const MSG_SAME_CUSTOMER = '供方与需方不能为同一客户'
|
||||
const MSG_SAME_ADDRESS = '取货地址与送货地址不能相同'
|
||||
|
||||
/** 已确认的客户 id,用于冲突时回滚 el-select */
|
||||
const committedSupplierId = ref('')
|
||||
const committedCustomerId = ref('')
|
||||
/** 加载/重置表单时跳过地址互斥回滚 */
|
||||
const suppressAddressMutex = ref(false)
|
||||
|
||||
function isSamePartyCustomer(supplierId?: string, customerId?: string) {
|
||||
const s = String(supplierId || '').trim()
|
||||
const c = String(customerId || '').trim()
|
||||
return Boolean(s && c && s === c)
|
||||
}
|
||||
|
||||
function normalizeAddressText(value?: string | null) {
|
||||
return String(value || '').trim()
|
||||
}
|
||||
|
||||
function isSameCoordPair(
|
||||
lng1: number | null | undefined,
|
||||
lat1: number | null | undefined,
|
||||
lng2: number | null | undefined,
|
||||
lat2: number | null | undefined
|
||||
) {
|
||||
if (lng1 == null || lat1 == null || lng2 == null || lat2 == null) return false
|
||||
const aLng = Number(lng1)
|
||||
const aLat = Number(lat1)
|
||||
const bLng = Number(lng2)
|
||||
const bLat = Number(lat2)
|
||||
if ([aLng, aLat, bLng, bLat].some(n => Number.isNaN(n))) return false
|
||||
return aLng === bLng && aLat === bLat
|
||||
}
|
||||
|
||||
function isSameLogisticsAddress(
|
||||
addressA?: string | null,
|
||||
lngA?: number | null,
|
||||
latA?: number | null,
|
||||
addressB?: string | null,
|
||||
lngB?: number | null,
|
||||
latB?: number | null
|
||||
) {
|
||||
const ta = normalizeAddressText(addressA)
|
||||
const tb = normalizeAddressText(addressB)
|
||||
if (ta && tb && ta === tb) return true
|
||||
if (isSameCoordPair(lngA, latA, lngB, latB)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function syncCommittedPartyIds() {
|
||||
committedSupplierId.value = String(formData.supplierId || '')
|
||||
committedCustomerId.value = String(formData.customerId || '')
|
||||
}
|
||||
|
||||
function handleSupplierSelectChange(customerId?: string) {
|
||||
if (!customerId) {
|
||||
formData.supplierId = ''
|
||||
formData.supplierName = ''
|
||||
formData.supplierContracts = []
|
||||
committedSupplierId.value = ''
|
||||
return
|
||||
}
|
||||
const customer = customerOptions.value.find(item => String(item.id) === String(customerId))
|
||||
formData.supplierId = String(customerId)
|
||||
const nextId = String(customerId)
|
||||
if (isSamePartyCustomer(nextId, formData.customerId)) {
|
||||
ElMessage.warning(MSG_SAME_CUSTOMER)
|
||||
nextTick(() => {
|
||||
formData.supplierId = committedSupplierId.value
|
||||
})
|
||||
return
|
||||
}
|
||||
const customer = customerOptions.value.find(item => String(item.id) === nextId)
|
||||
formData.supplierId = nextId
|
||||
formData.supplierName = customer?.customerName || formData.supplierName || ''
|
||||
formData.supplierContracts = []
|
||||
committedSupplierId.value = nextId
|
||||
}
|
||||
|
||||
function handleDemanderSelectChange(customerId?: string) {
|
||||
@@ -806,15 +870,25 @@ function handleDemanderSelectChange(customerId?: string) {
|
||||
formData.demanderContracts = []
|
||||
formData.contactName = ''
|
||||
formData.contactPhone = ''
|
||||
committedCustomerId.value = ''
|
||||
return
|
||||
}
|
||||
const customer = customerOptions.value.find(item => String(item.id) === String(customerId))
|
||||
const nextId = String(customerId)
|
||||
if (isSamePartyCustomer(formData.supplierId, nextId)) {
|
||||
ElMessage.warning(MSG_SAME_CUSTOMER)
|
||||
nextTick(() => {
|
||||
formData.customerId = committedCustomerId.value
|
||||
})
|
||||
return
|
||||
}
|
||||
const customer = customerOptions.value.find(item => String(item.id) === nextId)
|
||||
const name = customer?.customerName || formData.customerName || ''
|
||||
formData.customerId = String(customerId)
|
||||
formData.customerId = nextId
|
||||
formData.customerName = name
|
||||
formData.destinationId = String(customerId)
|
||||
formData.destinationId = nextId
|
||||
formData.destinationName = name
|
||||
formData.demanderContracts = []
|
||||
committedCustomerId.value = nextId
|
||||
}
|
||||
|
||||
function openCustomerSelector(side: CustomerSide) {
|
||||
@@ -826,13 +900,23 @@ function handleCustomerConfirm(customer: CustomerConfirmRow) {
|
||||
if (!customer?.id) return
|
||||
const customerId = String(customer.id)
|
||||
const customerName = (customer.customerName || '').trim()
|
||||
ensureCustomerOption({ id: customerId, customerName })
|
||||
if (customerSelectorSide.value === 'supplier') {
|
||||
if (isSamePartyCustomer(customerId, formData.customerId)) {
|
||||
ElMessage.warning(MSG_SAME_CUSTOMER)
|
||||
return
|
||||
}
|
||||
ensureCustomerOption({ id: customerId, customerName })
|
||||
formData.supplierId = customerId
|
||||
formData.supplierName = customerName
|
||||
formData.supplierContracts = []
|
||||
committedSupplierId.value = customerId
|
||||
return
|
||||
}
|
||||
if (isSamePartyCustomer(formData.supplierId, customerId)) {
|
||||
ElMessage.warning(MSG_SAME_CUSTOMER)
|
||||
return
|
||||
}
|
||||
ensureCustomerOption({ id: customerId, customerName })
|
||||
formData.customerId = customerId
|
||||
formData.customerName = customerName
|
||||
formData.destinationId = customerId
|
||||
@@ -840,8 +924,98 @@ function handleCustomerConfirm(customer: CustomerConfirmRow) {
|
||||
formData.demanderContracts = []
|
||||
formData.contactName = customer.contactName || ''
|
||||
formData.contactPhone = customer.mobile || ''
|
||||
committedCustomerId.value = customerId
|
||||
}
|
||||
|
||||
watch(
|
||||
() =>
|
||||
[formData.pickupAddress, formData.pickupLng, formData.pickupLat] as [
|
||||
string,
|
||||
number | null,
|
||||
number | null
|
||||
],
|
||||
(curr, prev) => {
|
||||
if (suppressAddressMutex.value || !prev) return
|
||||
if (
|
||||
!isSameLogisticsAddress(
|
||||
curr[0],
|
||||
curr[1],
|
||||
curr[2],
|
||||
formData.destinationAddress,
|
||||
formData.destinationLng,
|
||||
formData.destinationLat
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
// 仅当「取货侧」变化导致冲突时回滚取货
|
||||
if (
|
||||
isSameLogisticsAddress(
|
||||
prev[0],
|
||||
prev[1],
|
||||
prev[2],
|
||||
formData.destinationAddress,
|
||||
formData.destinationLng,
|
||||
formData.destinationLat
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
ElMessage.warning(MSG_SAME_ADDRESS)
|
||||
suppressAddressMutex.value = true
|
||||
formData.pickupAddress = prev[0] || ''
|
||||
formData.pickupLng = prev[1]
|
||||
formData.pickupLat = prev[2]
|
||||
nextTick(() => {
|
||||
suppressAddressMutex.value = false
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() =>
|
||||
[formData.destinationAddress, formData.destinationLng, formData.destinationLat] as [
|
||||
string,
|
||||
number | null,
|
||||
number | null
|
||||
],
|
||||
(curr, prev) => {
|
||||
if (suppressAddressMutex.value || !prev) return
|
||||
if (
|
||||
!isSameLogisticsAddress(
|
||||
formData.pickupAddress,
|
||||
formData.pickupLng,
|
||||
formData.pickupLat,
|
||||
curr[0],
|
||||
curr[1],
|
||||
curr[2]
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
isSameLogisticsAddress(
|
||||
formData.pickupAddress,
|
||||
formData.pickupLng,
|
||||
formData.pickupLat,
|
||||
prev[0],
|
||||
prev[1],
|
||||
prev[2]
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
ElMessage.warning(MSG_SAME_ADDRESS)
|
||||
suppressAddressMutex.value = true
|
||||
formData.destinationAddress = prev[0] || ''
|
||||
formData.destinationLng = prev[1]
|
||||
formData.destinationLat = prev[2]
|
||||
nextTick(() => {
|
||||
suppressAddressMutex.value = false
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
async function handleProductConfirm(products: Array<Record<string, unknown>>) {
|
||||
for (const product of products) {
|
||||
const brandMeta = initLineBrandMetaFromProduct(product)
|
||||
@@ -1079,6 +1253,7 @@ function resetForm() {
|
||||
remark: '',
|
||||
products: []
|
||||
})
|
||||
syncCommittedPartyIds()
|
||||
freightCalc.resetFreightCalc()
|
||||
basicFormRef.value?.clearValidate()
|
||||
}
|
||||
@@ -1113,6 +1288,7 @@ function handleOpened() {
|
||||
|
||||
async function loadPlanForEdit(planId: string) {
|
||||
detailLoading.value = true
|
||||
suppressAddressMutex.value = true
|
||||
try {
|
||||
resetForm()
|
||||
const res = await planApi.get(planId)
|
||||
@@ -1152,6 +1328,7 @@ async function loadPlanForEdit(planId: string) {
|
||||
products: (data.products || []).map(mapPlanProductToFormRow)
|
||||
})
|
||||
loadedTransportStatus.value = data.transportStatus || 'pending'
|
||||
syncCommittedPartyIds()
|
||||
|
||||
formData.products = await enrichPlanProductLinesAlias(
|
||||
await enrichPlanProductsHazardFromCrm((data.products || []).map(mapPlanProductToFormRow))
|
||||
@@ -1168,12 +1345,15 @@ async function loadPlanForEdit(planId: string) {
|
||||
}
|
||||
syncSelectedCustomerOptions()
|
||||
} finally {
|
||||
await nextTick()
|
||||
suppressAddressMutex.value = false
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPlanForCopy(planId: string) {
|
||||
detailLoading.value = true
|
||||
suppressAddressMutex.value = true
|
||||
try {
|
||||
resetForm()
|
||||
const res = await planApi.get(planId)
|
||||
@@ -1212,6 +1392,7 @@ async function loadPlanForCopy(planId: string) {
|
||||
products: (data.products || []).map(mapPlanProductToCopyFormRow)
|
||||
})
|
||||
loadedTransportStatus.value = 'pending'
|
||||
syncCommittedPartyIds()
|
||||
|
||||
formData.products = await enrichPlanProductLinesAlias(
|
||||
await enrichPlanProductsHazardFromCrm((data.products || []).map(mapPlanProductToCopyFormRow))
|
||||
@@ -1228,6 +1409,8 @@ async function loadPlanForCopy(planId: string) {
|
||||
ElMessage.error(message.includes('加载') ? message : `加载源计划失败:${message}`)
|
||||
emit('update:visible', false)
|
||||
} finally {
|
||||
await nextTick()
|
||||
suppressAddressMutex.value = false
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
@@ -1307,6 +1490,23 @@ function validateBusinessRules() {
|
||||
ElMessage.warning('请选择需方客户')
|
||||
return false
|
||||
}
|
||||
if (isSamePartyCustomer(formData.supplierId, formData.customerId)) {
|
||||
ElMessage.warning(MSG_SAME_CUSTOMER)
|
||||
return false
|
||||
}
|
||||
if (
|
||||
isSameLogisticsAddress(
|
||||
formData.pickupAddress,
|
||||
formData.pickupLng,
|
||||
formData.pickupLat,
|
||||
formData.destinationAddress,
|
||||
formData.destinationLng,
|
||||
formData.destinationLat
|
||||
)
|
||||
) {
|
||||
ElMessage.warning(MSG_SAME_ADDRESS)
|
||||
return false
|
||||
}
|
||||
if (!formData.products.length) {
|
||||
ElMessage.warning('请至少保留一条产品明细')
|
||||
return false
|
||||
|
||||
@@ -108,41 +108,45 @@
|
||||
<el-tab-pane label="运输信息" name="transport">
|
||||
<section v-if="showArrangeLinks" class="pl-plan-form-section pl-plan-form-section--plain pl-plan-arrange-links">
|
||||
<h3 class="pl-plan-form-section-title">运输链接</h3>
|
||||
<div class="pl-arrange-link-row">
|
||||
<span class="pl-arrange-link-label">司机链接</span>
|
||||
<el-input :model-value="arrangeDriverUrl" readonly />
|
||||
<el-button @click="copyArrangeDriverUrl">
|
||||
<el-icon><CopyDocument /></el-icon>复制链接
|
||||
</el-button>
|
||||
</div>
|
||||
<template v-if="hasArrangeSiteLinks">
|
||||
<div v-if="arrangeSiteRecords.length === 1" class="pl-arrange-link-row">
|
||||
<span class="pl-arrange-link-label">现场链接</span>
|
||||
<el-input :model-value="arrangeSiteRecords[0].siteUrl || ''" readonly />
|
||||
<el-button @click="copyArrangeSiteUrl(arrangeSiteRecords[0].siteUrl)">
|
||||
<div class="pl-arrange-links-body">
|
||||
<div class="pl-arrange-link-row">
|
||||
<span class="pl-arrange-link-label">司机链接</span>
|
||||
<el-input :model-value="arrangeDriverUrl" readonly />
|
||||
<el-button @click="copyArrangeDriverUrl">
|
||||
<el-icon><CopyDocument /></el-icon>复制链接
|
||||
</el-button>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="record in arrangeSiteRecords"
|
||||
:key="record.siteLinkId"
|
||||
class="pl-arrange-link-row"
|
||||
<div class="pl-arrange-site-col">
|
||||
<template v-if="hasArrangeSiteLinks">
|
||||
<div v-if="arrangeSiteRecords.length === 1" class="pl-arrange-link-row">
|
||||
<span class="pl-arrange-link-label">现场链接</span>
|
||||
<el-input :model-value="arrangeSiteRecords[0].siteUrl || ''" readonly />
|
||||
<el-button @click="copyArrangeSiteUrl(arrangeSiteRecords[0].siteUrl)">
|
||||
<el-icon><CopyDocument /></el-icon>复制链接
|
||||
</el-button>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="record in arrangeSiteRecords"
|
||||
:key="record.siteLinkId"
|
||||
class="pl-arrange-link-row"
|
||||
>
|
||||
<span class="pl-arrange-link-label">{{ record.productName || '现场链接' }}</span>
|
||||
<el-input :model-value="record.siteUrl || ''" readonly />
|
||||
<el-button @click="copyArrangeSiteUrl(record.siteUrl)">
|
||||
<el-icon><CopyDocument /></el-icon>复制链接
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<p
|
||||
v-else-if="showArrangeLinks && planSupportsInventory"
|
||||
class="pl-plan-form-empty-hint pl-plan-arrange-links-hint"
|
||||
>
|
||||
<span class="pl-arrange-link-label">{{ record.productName || '现场链接' }}</span>
|
||||
<el-input :model-value="record.siteUrl || ''" readonly />
|
||||
<el-button @click="copyArrangeSiteUrl(record.siteUrl)">
|
||||
<el-icon><CopyDocument /></el-icon>复制链接
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<p
|
||||
v-else-if="showArrangeLinks && planSupportsInventory"
|
||||
class="pl-plan-form-empty-hint pl-plan-arrange-links-hint"
|
||||
>
|
||||
暂无现场链接
|
||||
</p>
|
||||
暂无现场链接
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
||||
@@ -1412,24 +1416,46 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pl-arrange-links-body {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 12px 16px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.pl-arrange-site-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pl-plan-arrange-links-hint {
|
||||
margin-top: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pl-arrange-link-row {
|
||||
display: grid;
|
||||
grid-template-columns: 72px 1fr auto;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
|
||||
&--readonly {
|
||||
grid-template-columns: 72px 1fr;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.pl-arrange-link-label {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.pl-arrange-links-body {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pl-plan-view-remark {
|
||||
|
||||
Reference in new issue
Block a user