Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf47a26ba0 |
No files matched your search
@@ -351,7 +351,11 @@ def get_plan_quota(db, user, plan_id):
|
|||||||
),
|
),
|
||||||
'usages': []}
|
'usages': []}
|
||||||
for usage in usages:
|
for usage in usages:
|
||||||
_get_row(db, user, usage.cert_type, usage.cert_id)
|
cert = _get_row(db, user, usage.cert_type, usage.cert_id)
|
||||||
items = db.query(PlPlanCertUsageItem).filter_by(tenant_no=user.tenant_no, usage_id=usage.id, del_flag=0).order_by(PlPlanCertUsageItem.allocation_seq).all()
|
items = db.query(PlPlanCertUsageItem).filter_by(tenant_no=user.tenant_no, usage_id=usage.id, del_flag=0).order_by(PlPlanCertUsageItem.allocation_seq).all()
|
||||||
result['usages'].append({**_row_to_dict(usage), 'items': [_row_to_dict(row) for row in items]})
|
payload = {**_row_to_dict(usage), 'items': [_row_to_dict(row) for row in items]}
|
||||||
|
# 详情展示证面附件:用量行本身无附件字段,从证件档案回填
|
||||||
|
payload['attachmentUrls'] = getattr(cert, 'attachment_urls', None) or ''
|
||||||
|
payload['certNo'] = payload.get('certNo') or getattr(cert, 'cert_no', None) or usage.cert_no_snapshot
|
||||||
|
result['usages'].append(payload)
|
||||||
return result
|
return result
|
||||||
@@ -317,3 +317,39 @@ def test_missing_contract_is_empty_binding_not_unreadable(case, contract_id):
|
|||||||
assert rows[0]['bindingsReadable'] is True
|
assert rows[0]['bindingsReadable'] is True
|
||||||
assert rows[0]['certificates'] == []
|
assert rows[0]['certificates'] == []
|
||||||
assert rows[1]['bindingsReadable'] is False
|
assert rows[1]['bindingsReadable'] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_plan_quota_usages_include_cert_attachment_urls(case):
|
||||||
|
from app.models.crm_models import CrmContract
|
||||||
|
from app.models.pl_quota_models import (
|
||||||
|
PlPlanCertUsage, PlPlanCertUsageItem, PlPurchaseCert, PlTransportCert,
|
||||||
|
)
|
||||||
|
from app.services.pl_quota_planning import get_plan_quota
|
||||||
|
c = case
|
||||||
|
for model in (CrmContract, PlPlanCertUsage, PlPlanCertUsageItem, PlPurchaseCert, PlTransportCert):
|
||||||
|
model.__table__.create(c.db.bind)
|
||||||
|
c.db.add(PlPurchaseCert(
|
||||||
|
id='p1', cert_no='G-BUY', product_name_on_cert='硫酸', valid_use_text='一次有效',
|
||||||
|
buyer_name='买方', seller_name='卖方', total_qty=33, quantity_unit='吨',
|
||||||
|
attachment_urls='https://example.com/buy.jpg', tenant_no='t', status='active',
|
||||||
|
))
|
||||||
|
c.db.add(PlTransportCert(
|
||||||
|
id='t1', cert_no='Y-TR', product_name_on_cert='硫酸', valid_use_text='多次有效',
|
||||||
|
shipper_name='货主', total_qty=70, quantity_unit='吨', vehicle_numbers_json=['皖A1'],
|
||||||
|
attachment_urls='', tenant_no='t', status='active',
|
||||||
|
))
|
||||||
|
c.db.add(PlPlanCertUsage(
|
||||||
|
id='u1', tenant_no='t', plan_id='plan', plan_no_snapshot='PLAN',
|
||||||
|
cert_type='purchase', cert_id='p1', cert_no_snapshot='G-BUY', cert_revision=1,
|
||||||
|
quantity_unit='吨', reserved_qty=10, used_qty=0, usage_status='reserved',
|
||||||
|
))
|
||||||
|
c.db.add(PlPlanCertUsage(
|
||||||
|
id='u2', tenant_no='t', plan_id='plan', plan_no_snapshot='PLAN',
|
||||||
|
cert_type='transport', cert_id='t1', cert_no_snapshot='Y-TR', cert_revision=1,
|
||||||
|
quantity_unit='吨', reserved_qty=10, used_qty=0, usage_status='reserved',
|
||||||
|
))
|
||||||
|
c.db.flush()
|
||||||
|
usages = {u['certId']: u for u in get_plan_quota(c.db, c.user, 'plan')['usages']}
|
||||||
|
assert usages['p1']['attachmentUrls'] == 'https://example.com/buy.jpg'
|
||||||
|
assert usages['p1']['certNo'] == 'G-BUY'
|
||||||
|
assert usages['t1']['attachmentUrls'] in ('', None)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import test from 'node:test'
|
||||||
|
|
||||||
|
const viewTabs = readFileSync(
|
||||||
|
new URL('../src/components/business/pl-plan-view-tabs/index.vue', import.meta.url),
|
||||||
|
'utf8'
|
||||||
|
)
|
||||||
|
const h5Detail = readFileSync(
|
||||||
|
new URL('../src/views/m/pl/plan/detail.vue', import.meta.url),
|
||||||
|
'utf8'
|
||||||
|
)
|
||||||
|
|
||||||
|
test('Web 计划详情:关联证件在装送货后且无危化品证书信息区', () => {
|
||||||
|
assert.doesNotMatch(viewTabs, /危化品证书信息/)
|
||||||
|
assert.doesNotMatch(viewTabs, /PlPlanHazardCertViewTable/)
|
||||||
|
const loadIdx = viewTabs.indexOf('装送货信息')
|
||||||
|
const certIdx = viewTabs.indexOf('关联购买证 / 运输证')
|
||||||
|
const freightIdx = viewTabs.indexOf('运费信息')
|
||||||
|
assert.ok(loadIdx >= 0 && certIdx > loadIdx && freightIdx > certIdx)
|
||||||
|
assert.match(viewTabs, /AttachmentUploadField/)
|
||||||
|
assert.match(viewTabs, /attachmentUrls/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('H5 计划详情:关联购买证/运输证替换危化品证书信息并展示附件', () => {
|
||||||
|
assert.doesNotMatch(h5Detail, /危化品证书信息/)
|
||||||
|
assert.match(h5Detail, /关联购买证 \/ 运输证/)
|
||||||
|
assert.match(h5Detail, /关联证件/)
|
||||||
|
const loadIdx = h5Detail.indexOf('plan-sec-load')
|
||||||
|
const certIdx = h5Detail.indexOf('plan-sec-cert')
|
||||||
|
const freightIdx = h5Detail.indexOf('plan-sec-freight')
|
||||||
|
assert.ok(loadIdx >= 0 && certIdx > loadIdx && freightIdx > certIdx)
|
||||||
|
assert.match(h5Detail, /AttachmentUploadField|CertAttachments/)
|
||||||
|
assert.match(h5Detail, /attachmentUrls/)
|
||||||
|
})
|
||||||
@@ -71,23 +71,6 @@
|
|||||||
<p v-else-if="!loading" class="pl-plan-form-empty-hint">暂无车辆信息</p>
|
<p v-else-if="!loading" class="pl-plan-form-empty-hint">暂无车辆信息</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
|
||||||
<h3 class="pl-plan-form-section-title">关联购买证 / 运输证</h3>
|
|
||||||
<p v-if="plan?.quotaCertPendingFulfill" class="pl-plan-form-empty-hint">
|
|
||||||
证书齐备(待完善):请到「证书齐备 → 已标记待完善证书」挂证并完善并关闭
|
|
||||||
</p>
|
|
||||||
<div v-else-if="linkedCertRows.length" class="pl-plan-linked-certs">
|
|
||||||
<div v-for="item in linkedCertRows" :key="item.key" class="pl-plan-linked-cert-row">
|
|
||||||
<el-tag size="small" :type="item.kind === 'purchase' ? 'success' : 'warning'">
|
|
||||||
{{ item.kind === 'purchase' ? '购买证' : '运输证' }}
|
|
||||||
</el-tag>
|
|
||||||
<span>{{ item.certNo || item.certId }}</span>
|
|
||||||
<span class="text-slate-500">预占 {{ item.reservedQty }} / 已用 {{ item.usedQty }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-else class="pl-plan-form-empty-hint">暂无关联证件</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
||||||
<h3 class="pl-plan-form-section-title">装送货信息</h3>
|
<h3 class="pl-plan-form-section-title">装送货信息</h3>
|
||||||
<PlPlanStopsSplitShell
|
<PlPlanStopsSplitShell
|
||||||
@@ -251,8 +234,31 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
||||||
<h3 class="pl-plan-form-section-title">危化品证书信息</h3>
|
<h3 class="pl-plan-form-section-title">关联购买证 / 运输证</h3>
|
||||||
<PlPlanHazardCertViewTable :products="hazardProducts" />
|
<p v-if="plan?.quotaCertPendingFulfill" class="pl-plan-form-empty-hint">
|
||||||
|
证书齐备(待完善):请到「证书齐备 → 已标记待完善证书」挂证并完善并关闭
|
||||||
|
</p>
|
||||||
|
<div v-else-if="linkedCertRows.length" class="pl-plan-linked-certs">
|
||||||
|
<div v-for="item in linkedCertRows" :key="item.key" class="pl-plan-linked-cert-block">
|
||||||
|
<div class="pl-plan-linked-cert-block__meta">
|
||||||
|
<el-tag size="small" :type="item.kind === 'purchase' ? 'success' : 'warning'">
|
||||||
|
{{ item.kind === 'purchase' ? '购买证' : '运输证' }}
|
||||||
|
</el-tag>
|
||||||
|
<span class="pl-plan-linked-cert-block__no">{{ item.certNo || item.certId }}</span>
|
||||||
|
<span class="pl-plan-linked-cert-block__qty"
|
||||||
|
>预占 {{ item.reservedQty }} / 已用 {{ item.usedQty }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="pl-plan-linked-cert-block__slot">
|
||||||
|
<span class="pl-plan-linked-cert-block__label">证件附件</span>
|
||||||
|
<div v-if="item.attachmentUrls" class="pl-plan-linked-cert-block__attachment">
|
||||||
|
<AttachmentUploadField :model-value="item.attachmentUrls" disabled />
|
||||||
|
</div>
|
||||||
|
<span v-else class="pl-plan-linked-cert-block__missing">未上传</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p v-else class="pl-plan-form-empty-hint">暂无关联证件</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
<section class="pl-plan-form-section pl-plan-form-section--plain">
|
||||||
@@ -430,7 +436,6 @@
|
|||||||
type BrandQtyRow
|
type BrandQtyRow
|
||||||
} from '@/components/business/brand-qty-chips-field/index.vue'
|
} from '@/components/business/brand-qty-chips-field/index.vue'
|
||||||
import PlPlanDriverFeedbackDialog from '@/components/business/pl-plan-driver-feedback-dialog/index.vue'
|
import PlPlanDriverFeedbackDialog from '@/components/business/pl-plan-driver-feedback-dialog/index.vue'
|
||||||
import PlPlanHazardCertViewTable from '@/components/business/pl-plan-hazard-cert-view-table/index.vue'
|
|
||||||
import PlPlanArchiveBrandViewBlock from '@/components/business/pl-plan-archive-brand-view-block/index.vue'
|
import PlPlanArchiveBrandViewBlock from '@/components/business/pl-plan-archive-brand-view-block/index.vue'
|
||||||
import PlPlanStopFeedbackPanel from '@/components/business/pl-plan-stop-feedback-panel/index.vue'
|
import PlPlanStopFeedbackPanel from '@/components/business/pl-plan-stop-feedback-panel/index.vue'
|
||||||
import PlPlanStopProductTable from '@/components/business/pl-plan-stop-product-table/index.vue'
|
import PlPlanStopProductTable from '@/components/business/pl-plan-stop-product-table/index.vue'
|
||||||
@@ -476,7 +481,6 @@
|
|||||||
} from '@/utils/pl-plan-stop-form'
|
} from '@/utils/pl-plan-stop-form'
|
||||||
import { buildArchiveBrandViewsBySegment } from '@/utils/pl-plan-archive-brand-view'
|
import { buildArchiveBrandViewsBySegment } from '@/utils/pl-plan-archive-brand-view'
|
||||||
import { resolveFreightUnitLabel } from '@/composables/usePlFreightCalc'
|
import { resolveFreightUnitLabel } from '@/composables/usePlFreightCalc'
|
||||||
import { isHazardProduct } from '@/composables/usePlToxicProductCertCheck'
|
|
||||||
import { useDictStore } from '@/store/modules/dict'
|
import { useDictStore } from '@/store/modules/dict'
|
||||||
import { formatDate, formatDateTime } from '@/utils/format'
|
import { formatDate, formatDateTime } from '@/utils/format'
|
||||||
import {
|
import {
|
||||||
@@ -1638,18 +1642,6 @@
|
|||||||
return fields
|
return fields
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 危化证区域展示 CRM 标准名(productRows 已回填 standardProductName;库内 productName 可能是别名) */
|
|
||||||
const hazardProducts = computed(() =>
|
|
||||||
products.value
|
|
||||||
.filter((item) => isHazardProduct(item))
|
|
||||||
.map((item) => {
|
|
||||||
const formRow = item.id ? planProductFormById.value.get(String(item.id)) : undefined
|
|
||||||
const standardName = String(formRow?.standardProductName || '').trim()
|
|
||||||
if (!standardName || standardName === item.productName) return item
|
|
||||||
return { ...item, productName: standardName }
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const linkedCertRows = ref<
|
const linkedCertRows = ref<
|
||||||
Array<{
|
Array<{
|
||||||
key: string
|
key: string
|
||||||
@@ -1658,6 +1650,7 @@
|
|||||||
certNo?: string
|
certNo?: string
|
||||||
reservedQty: string | number
|
reservedQty: string | number
|
||||||
usedQty: string | number
|
usedQty: string | number
|
||||||
|
attachmentUrls: string
|
||||||
}>
|
}>
|
||||||
>([])
|
>([])
|
||||||
|
|
||||||
@@ -1670,9 +1663,10 @@
|
|||||||
key: `${u.certType || u.cert_type}-${u.certId || u.cert_id}-${index}`,
|
key: `${u.certType || u.cert_type}-${u.certId || u.cert_id}-${index}`,
|
||||||
kind: String(u.certType || u.cert_type || ''),
|
kind: String(u.certType || u.cert_type || ''),
|
||||||
certId: String(u.certId || u.cert_id || ''),
|
certId: String(u.certId || u.cert_id || ''),
|
||||||
certNo: u.certNo || u.cert_no,
|
certNo: u.certNo || u.cert_no || u.certNoSnapshot || u.cert_no_snapshot,
|
||||||
reservedQty: u.reservedQty ?? u.reserved_qty ?? '—',
|
reservedQty: u.reservedQty ?? u.reserved_qty ?? '—',
|
||||||
usedQty: u.usedQty ?? u.used_qty ?? '—'
|
usedQty: u.usedQty ?? u.used_qty ?? '—',
|
||||||
|
attachmentUrls: String(u.attachmentUrls || u.attachment_urls || '').trim()
|
||||||
}))
|
}))
|
||||||
} catch {
|
} catch {
|
||||||
linkedCertRows.value = []
|
linkedCertRows.value = []
|
||||||
@@ -1932,6 +1926,93 @@
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-certs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 16px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid var(--border, #d9e8e2);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px 12px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__no {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e293b;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__qty {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--mint-text-muted, #64748b);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__slot {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--mint-text);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__attachment {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
:deep(.attachment-upload) {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.attachment-list) {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.attachment-item) {
|
||||||
|
width: 140px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.attachment-preview) {
|
||||||
|
height: 96px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-plan-linked-cert-block__missing {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 140px;
|
||||||
|
height: 96px;
|
||||||
|
border: 1px dashed var(--border, #d9e8e2);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--table-head, #f8fafc);
|
||||||
|
color: var(--mint-text-muted);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.pl-plan-kv-grid {
|
.pl-plan-kv-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
|||||||
@@ -248,7 +248,37 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="plan-sec-cert" class="mp-wv-section mp-h5-anchor">
|
<section id="plan-sec-cert" class="mp-wv-section mp-h5-anchor">
|
||||||
<h3>危化品证书信息</h3>
|
<h3>关联购买证 / 运输证</h3>
|
||||||
|
<p v-if="plan.quotaCertPendingFulfill" class="plan-empty-hint">
|
||||||
|
证书齐备(待完善):请到「证书齐备 → 已标记待完善证书」挂证并完善并关闭
|
||||||
|
</p>
|
||||||
|
<div v-else-if="linkedCertRows.length" class="plan-linked-certs">
|
||||||
|
<div v-for="item in linkedCertRows" :key="item.key" class="plan-linked-cert-block">
|
||||||
|
<div class="plan-linked-cert-block__meta">
|
||||||
|
<span
|
||||||
|
class="status-tag"
|
||||||
|
:class="
|
||||||
|
item.kind === 'purchase' ? 'status-tag--success' : 'status-tag--warning'
|
||||||
|
"
|
||||||
|
>{{ item.kind === 'purchase' ? '购买证' : '运输证' }}</span
|
||||||
|
>
|
||||||
|
<span class="plan-linked-cert-block__no">{{ item.certNo || item.certId }}</span>
|
||||||
|
<span class="plan-vehicle-card__meta"
|
||||||
|
>预占 {{ item.reservedQty }} / 已用 {{ item.usedQty }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="plan-linked-cert-block__slot">
|
||||||
|
<div class="plan-subcard__label">证件附件</div>
|
||||||
|
<AttachmentUploadField
|
||||||
|
v-if="item.attachmentUrls"
|
||||||
|
:model-value="item.attachmentUrls"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<p v-else class="plan-linked-cert-block__missing">未上传</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p v-else class="plan-empty-hint">暂无关联证件</p>
|
||||||
<div class="plan-cert-actions">
|
<div class="plan-cert-actions">
|
||||||
<van-button block plain type="primary" @click="quotaOpen = true">查看用证</van-button>
|
<van-button block plain type="primary" @click="quotaOpen = true">查看用证</van-button>
|
||||||
<van-button
|
<van-button
|
||||||
@@ -536,10 +566,40 @@
|
|||||||
const errorText = ref('')
|
const errorText = ref('')
|
||||||
const plan = ref<Plan | null>(null)
|
const plan = ref<Plan | null>(null)
|
||||||
const quotaOpen = ref(false)
|
const quotaOpen = ref(false)
|
||||||
|
const linkedCertRows = ref<
|
||||||
|
Array<{
|
||||||
|
key: string
|
||||||
|
kind: string
|
||||||
|
certId: string
|
||||||
|
certNo?: string
|
||||||
|
reservedQty: string | number
|
||||||
|
usedQty: string | number
|
||||||
|
attachmentUrls: string
|
||||||
|
}>
|
||||||
|
>([])
|
||||||
const needsLegalUseDocument = computed(() =>
|
const needsLegalUseDocument = computed(() =>
|
||||||
(plan.value?.products || []).some((row) => resolveProductHazardFlags(row).isExplosive)
|
(plan.value?.products || []).some((row) => resolveProductHazardFlags(row).isExplosive)
|
||||||
)
|
)
|
||||||
const quotaTitle = computed(() => `计划用证 · ${plan.value?.planNo || ''}`)
|
const quotaTitle = computed(() => `计划用证 · ${plan.value?.planNo || ''}`)
|
||||||
|
|
||||||
|
async function loadLinkedCerts(planId: string) {
|
||||||
|
linkedCertRows.value = []
|
||||||
|
try {
|
||||||
|
const { planQuotaApi } = await import('@/api/pl/quota-cert')
|
||||||
|
const data = await planQuotaApi.get(planId)
|
||||||
|
linkedCertRows.value = (data?.usages || []).map((u: any, index: number) => ({
|
||||||
|
key: `${u.certType || u.cert_type}-${u.certId || u.cert_id}-${index}`,
|
||||||
|
kind: String(u.certType || u.cert_type || ''),
|
||||||
|
certId: String(u.certId || u.cert_id || ''),
|
||||||
|
certNo: u.certNo || u.cert_no || u.certNoSnapshot || u.cert_no_snapshot,
|
||||||
|
reservedQty: u.reservedQty ?? u.reserved_qty ?? '—',
|
||||||
|
usedQty: u.usedQty ?? u.used_qty ?? '—',
|
||||||
|
attachmentUrls: String(u.attachmentUrls || u.attachment_urls || '').trim()
|
||||||
|
}))
|
||||||
|
} catch {
|
||||||
|
linkedCertRows.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
const deliveryIndex = ref(0)
|
const deliveryIndex = ref(0)
|
||||||
const moreOpen = ref(false)
|
const moreOpen = ref(false)
|
||||||
const voidOpen = ref(false)
|
const voidOpen = ref(false)
|
||||||
@@ -568,7 +628,7 @@
|
|||||||
{ key: 'base', label: '基础信息', anchor: 'plan-sec-base' },
|
{ key: 'base', label: '基础信息', anchor: 'plan-sec-base' },
|
||||||
{ key: 'transport', label: '运输信息', anchor: 'plan-sec-transport' },
|
{ key: 'transport', label: '运输信息', anchor: 'plan-sec-transport' },
|
||||||
{ key: 'load', label: '装送货', anchor: 'plan-sec-load' },
|
{ key: 'load', label: '装送货', anchor: 'plan-sec-load' },
|
||||||
{ key: 'cert', label: '危化品证', anchor: 'plan-sec-cert' },
|
{ key: 'cert', label: '关联证件', anchor: 'plan-sec-cert' },
|
||||||
{ key: 'freight', label: '运费', anchor: 'plan-sec-freight' },
|
{ key: 'freight', label: '运费', anchor: 'plan-sec-freight' },
|
||||||
{ key: 'remark', label: '备注', anchor: 'plan-sec-remark' }
|
{ key: 'remark', label: '备注', anchor: 'plan-sec-remark' }
|
||||||
]
|
]
|
||||||
@@ -751,6 +811,7 @@
|
|||||||
loadVersion++
|
loadVersion++
|
||||||
actionVersion++
|
actionVersion++
|
||||||
plan.value = null
|
plan.value = null
|
||||||
|
linkedCertRows.value = []
|
||||||
readDetail.value = null
|
readDetail.value = null
|
||||||
partialErrors.value = []
|
partialErrors.value = []
|
||||||
errorText.value = ''
|
errorText.value = ''
|
||||||
@@ -807,9 +868,12 @@
|
|||||||
readDetail.value = detail
|
readDetail.value = detail
|
||||||
partialErrors.value = [...detail.errors, ...(arrangeFailed ? ['安排信息'] : [])]
|
partialErrors.value = [...detail.errors, ...(arrangeFailed ? ['安排信息'] : [])]
|
||||||
plan.value = data
|
plan.value = data
|
||||||
|
await loadLinkedCerts(id)
|
||||||
|
if (!current()) return
|
||||||
} catch {
|
} catch {
|
||||||
if (!current()) return
|
if (!current()) return
|
||||||
plan.value = null
|
plan.value = null
|
||||||
|
linkedCertRows.value = []
|
||||||
resetArrangeActions()
|
resetArrangeActions()
|
||||||
errorText.value = '计划详情加载失败或记录不存在,请重试'
|
errorText.value = '计划详情加载失败或记录不存在,请重试'
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1516,6 +1580,57 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-linked-certs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-linked-cert-block {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #d9e8e2;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-linked-cert-block__meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-linked-cert-block__no {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1e293b;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-linked-cert-block__slot {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-linked-cert-block__missing {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 140px;
|
||||||
|
height: 96px;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px dashed #d9e8e2;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8fafc;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mp-plan-quota-sheet {
|
.mp-plan-quota-sheet {
|
||||||
|
|||||||
Reference in new issue
Block a user