This commit is contained in:
zlwu committed 2026-08-06 22:49:00 +08:00
1 parent f3693be913
commit 3d645b4112
272 files changed
+950716 -27033

No files matched your search

+19 -5
View File
@@ -82,25 +82,35 @@
"80": "product.ts",
"81": "Community 81",
"82": "Community 82",
"83": "useDictStore",
"84": "Community 84",
"85": "handleEdit",
"86": "Community 86",
"87": "canDeleteByCreator",
"88": "useTheme.ts",
"89": "PlVehicleSelect.vue",
"90": "Community 90",
"91": "Community 91",
"92": "Community 92",
"93": "ArtUserMenu.vue",
"94": "formatYmd",
"95": "Community 95",
"96": "Community 96",
"97": "product.ts",
"98": "fetchCustomerDetail",
"99": "StorageKeyManager",
"100": "v2.4.2.1 - 2025-06-16",
"101": "price-letter.ts",
"102": "price-letter-template.ts",
"103": "SettingDrawer.vue",
"104": "contractType.ts",
"105": "index.vue",
"106": "Community 106",
"107": "Community 107",
"108": "Community 108",
"109": "Community 109",
"110": "Community 110",
"111": "fetchData",
"112": "Community 112",
"113": "Community 113",
"114": "index.vue",
@@ -110,20 +120,27 @@
"118": "zh_CN.js",
"119": "Community 119",
"120": "Community 120",
"121": "fetchData",
"122": "form.vue",
"123": "detail.vue",
"124": "index.vue",
"125": "Community 125",
"126": "Community 126",
"127": "index.vue",
"127": "loadUserOptions",
"128": "Community 128",
"129": "Community 129",
"130": "applySortAndPage",
"131": "detail.vue",
"132": "Community 132",
"133": "Community 133",
"134": "weighing.ts",
"134": "handleSubmit",
"135": "handleSearch",
"136": "Community 136",
"137": "Community 137",
"140": "Community 140",
"141": "Community 141",
"142": "Community 142",
"143": "index.js",
"144": "Community 144",
"148": "Community 148",
"150": "Community 150",
@@ -157,7 +174,6 @@
"213": "Community 213",
"214": "Community 214",
"219": "initNew",
"222": "types.ts",
"223": "Community 223",
"224": "Community 224",
"227": "Community 227",
@@ -246,7 +262,6 @@
"380": "Community 380",
"381": "Community 381",
"383": "ApproverSelector.vue",
"388": "index.vue",
"391": "Community 391",
"392": "Community 392",
"393": "Community 393",
@@ -278,7 +293,6 @@
"460": "Community 460",
"462": "Community 462",
"465": "index.vue",
"470": "Ta",
"471": "Community 471",
"472": "Community 472",
"473": "Community 473",
@@ -82,35 +82,35 @@
"80": "product.ts",
"81": "Community 81",
"82": "Community 82",
"83": "StorageKeyManager",
"83": "useDictStore",
"84": "Community 84",
"85": "handleEdit",
"86": "Community 86",
"87": "canDeleteByCreator",
"88": "index.vue",
"89": "handleDisburse",
"88": "useTheme.ts",
"89": "PlVehicleSelect.vue",
"90": "Community 90",
"91": "Community 91",
"92": "Community 92",
"93": "loadUserOptions",
"94": "error-handle.ts",
"93": "ArtUserMenu.vue",
"94": "formatYmd",
"95": "Community 95",
"96": "Community 96",
"97": "canDeleteByCreator",
"98": "loadUserOptions",
"97": "product.ts",
"98": "fetchCustomerDetail",
"99": "StorageKeyManager",
"100": "v2.4.2.1 - 2025-06-16",
"101": "price-letter.ts",
"102": "handleSearch",
"102": "price-letter-template.ts",
"103": "SettingDrawer.vue",
"104": "loadUserOptions",
"104": "contractType.ts",
"105": "index.vue",
"106": "Community 106",
"107": "Community 107",
"108": "Community 108",
"109": "Community 109",
"110": "Community 110",
"111": "api",
"111": "fetchData",
"112": "Community 112",
"113": "Community 113",
"114": "index.vue",
@@ -120,32 +120,26 @@
"118": "zh_CN.js",
"119": "Community 119",
"120": "Community 120",
"121": "zh_CN.js",
"122": "price-letter-template.ts",
"123": "contractType.ts",
"124": "fetchData",
"122": "form.vue",
"123": "detail.vue",
"125": "Community 125",
"126": "Community 126",
"127": "index.vue",
"128": "Community 128",
"129": "Community 129",
"130": "price-letter.ts",
"130": "applySortAndPage",
"131": "detail.vue",
"132": "Community 132",
"133": "Community 133",
"134": "weighing.ts",
"135": "detail.vue",
"135": "handleSearch",
"136": "Community 136",
"137": "Community 137",
"138": "handleSearch",
"139": "applySortAndPage",
"138": "handleEdit",
"139": "zh_CN.js",
"140": "Community 140",
"141": "Community 141",
"142": "Community 142",
"143": "Community 143",
"143": "index.js",
"144": "Community 144",
"145": "handleEdit",
"146": "zh_CN.js",
"148": "Community 148",
"150": "Community 150",
"151": "Community 151",
@@ -178,7 +172,6 @@
"213": "Community 213",
"214": "Community 214",
"219": "initNew",
"222": "types.ts",
"223": "Community 223",
"224": "Community 224",
"227": "Community 227",
@@ -267,7 +260,6 @@
"380": "Community 380",
"381": "Community 381",
"383": "ApproverSelector.vue",
"388": "index.vue",
"391": "Community 391",
"392": "Community 392",
"393": "Community 393",
@@ -294,7 +286,6 @@
"444": "index.vue",
"445": "Community 445",
"453": "ViewKvTableCell.vue",
"454": "zh_CN.js",
"458": "Community 458",
"459": "Community 459",
"460": "Community 460",
@@ -542,7 +533,6 @@
"1271": "plugin.js",
"1272": "plugin.min.js",
"1273": "index.js",
"1274": "index.js",
"1275": "index.js",
"1277": "plugin.js",
"1278": "plugin.min.js",
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -47,8 +47,7 @@
<AttachmentUploadField
v-model="item[slot.key]"
folder="pl/plan-cert"
:limit="1"
:hint="`支持图片、PDF 等,上传${slot.label}`"
:hint="`支持图片、PDF 等,可上传多个${slot.label}`"
/>
</el-form-item>
</div>
@@ -112,7 +112,7 @@ withDefaults(
}
:deep(.attachment-list) {
gap: 0;
gap: 8px;
}
:deep(.attachment-item) {
@@ -55,14 +55,14 @@
<div
class="pl-plan-list-cert-cell__thumb"
:class="{
'pl-plan-list-cert-cell__thumb--empty': !getProductCertUrl(product, slot.key),
'pl-plan-list-cert-cell__thumb--filled': getProductCertUrl(product, slot.key)
'pl-plan-list-cert-cell__thumb--empty': !getPrimaryProductCertUrl(product, slot.key),
'pl-plan-list-cert-cell__thumb--filled': getPrimaryProductCertUrl(product, slot.key)
}"
>
<template v-if="getProductCertUrl(product, slot.key)">
<template v-if="getPrimaryProductCertUrl(product, slot.key)">
<img
v-if="isCertImageUrl(getProductCertUrl(product, slot.key))"
:src="displayCertSrc(getProductCertUrl(product, slot.key))"
v-if="isCertImageUrl(getPrimaryProductCertUrl(product, slot.key))"
:src="displayCertSrc(getPrimaryProductCertUrl(product, slot.key))"
:alt="slot.label"
loading="lazy"
decoding="async"
@@ -71,7 +71,7 @@
v-else
class="pl-plan-list-cert-cell__thumb-icon"
:class="
isCertPdfUrl(getProductCertUrl(product, slot.key))
isCertPdfUrl(getPrimaryProductCertUrl(product, slot.key))
? 'pl-plan-list-cert-cell__thumb-icon--pdf'
: 'pl-plan-list-cert-cell__thumb-icon--doc'
"
@@ -79,6 +79,12 @@
<el-icon :size="20"><Document /></el-icon>
</div>
<span class="pl-plan-list-cert-cell__cert-caption">{{ slot.label }}</span>
<span
v-if="getCertUrlCount(product, slot.key) > 1"
class="pl-plan-list-cert-cell__cert-count"
>
{{ getCertUrlCount(product, slot.key) }}
</span>
</template>
<template v-else>
<el-icon :size="18"><Upload /></el-icon>
@@ -94,18 +100,16 @@
class="pl-plan-list-cert-cell__cert-item"
>
<div
v-if="getProductCertUrl(product, slot.key)"
v-if="getPrimaryProductCertUrl(product, slot.key)"
class="pl-plan-list-cert-cell__thumb pl-plan-list-cert-cell__thumb--filled"
>
<el-image
v-if="isCertImageUrl(getProductCertUrl(product, slot.key))"
v-if="isCertImageUrl(getPrimaryProductCertUrl(product, slot.key))"
class="pl-plan-list-cert-cell__image"
fit="cover"
lazy
:src="displayCertSrc(getProductCertUrl(product, slot.key))"
:preview-src-list="
mediaActive ? [resolveCertFileUrl(getProductCertUrl(product, slot.key))] : []
"
:src="displayCertSrc(getPrimaryProductCertUrl(product, slot.key))"
:preview-src-list="mediaActive ? getCertPreviewSrcList(product, slot.key) : []"
preview-teleported
hide-on-click-modal
>
@@ -119,13 +123,19 @@
v-else
class="pl-plan-list-cert-cell__thumb-icon"
:class="
isCertPdfUrl(getProductCertUrl(product, slot.key))
isCertPdfUrl(getPrimaryProductCertUrl(product, slot.key))
? 'pl-plan-list-cert-cell__thumb-icon--pdf'
: 'pl-plan-list-cert-cell__thumb-icon--doc'
"
>
<el-icon :size="20"><Document /></el-icon>
</div>
<span
v-if="getCertUrlCount(product, slot.key) > 1"
class="pl-plan-list-cert-cell__cert-count"
>
{{ getCertUrlCount(product, slot.key) }}
</span>
</div>
<span v-else class="pl-plan-list-cert-cell__missing">未上传</span>
<span class="pl-plan-list-cert-cell__cert-label">{{ slot.label }}</span>
@@ -141,10 +151,13 @@
import { Document, Upload } from '@element-plus/icons-vue'
import type { PlanProduct } from '@/api/pl'
import {
getPrimaryProductCertUrl,
getProductCertUrl,
getRequiredCertSlots,
isHazardProduct,
isProductCertSlotFilled
isProductCertSlotFilled,
splitProductCertUrls,
type HazardCertSlotKey
} from '@/composables/usePlToxicProductCertCheck'
const props = withDefaults(
@@ -217,6 +230,17 @@
return /\.pdf(\?|$)/i.test(name) || /\.pdf(\?|$)/i.test(normalized)
}
function getCertUrlCount(product: PlanProduct, key: HazardCertSlotKey) {
return splitProductCertUrls(getProductCertUrl(product, key)).length
}
function getCertPreviewSrcList(product: PlanProduct, key: HazardCertSlotKey) {
return splitProductCertUrls(getProductCertUrl(product, key))
.filter((url) => isCertImageUrl(url))
.map((url) => resolveCertFileUrl(url))
.filter(Boolean)
}
function handleCertClick() {
if (props.clickableDisabled) return
emit('click')
@@ -336,6 +360,23 @@
word-break: keep-all;
}
.pl-plan-list-cert-cell__cert-count {
position: absolute;
top: 4px;
right: 4px;
z-index: 1;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.72);
color: #fff;
font-size: 11px;
font-weight: 600;
line-height: 18px;
text-align: center;
}
.pl-plan-list-cert-cell__thumb--empty {
flex-direction: column;
gap: 2px;
@@ -82,9 +82,9 @@
<AttachmentUploadField
ref="photoUploadRef"
v-model="formData.photoUrls"
:limit="1"
accept="image/*"
folder="pl/vehicle-photo"
hint="支持图片,可上传多张;可拖拽或在此区域粘贴上传"
/>
</el-form-item>
</div>
@@ -66,8 +66,21 @@ export function getProductCertUrl(product: PlanProduct, key: HazardCertSlotKey):
return typeof value === 'string' ? value.trim() : ''
}
/** 危化证书字段支持逗号分隔多附件 */
export function splitProductCertUrls(raw?: string): string[] {
return String(raw || '')
.split(',')
.map(item => item.trim())
.filter(Boolean)
}
/** 列表缩略图等场景取首个附件 URL */
export function getPrimaryProductCertUrl(product: PlanProduct, key: HazardCertSlotKey): string {
return splitProductCertUrls(getProductCertUrl(product, key))[0] || ''
}
export function isProductCertSlotFilled(product: PlanProduct, key: HazardCertSlotKey): boolean {
return Boolean(getProductCertUrl(product, key))
return Boolean(getPrimaryProductCertUrl(product, key))
}
export function isProductCertComplete(product: PlanProduct): boolean {
+2 -3
View File
@@ -323,9 +323,9 @@
<AttachmentUploadField
ref="photoUploadRef"
v-model="formData.photoUrls"
:limit="1"
accept="image/*"
upload-folder="pl/personnel-photo"
folder="pl/personnel-photo"
hint="支持图片,可上传多张;可拖拽或在此区域粘贴上传"
/>
</el-form-item>
</div>
@@ -385,7 +385,6 @@
v-if="viewDetail?.photoUrls"
:model-value="viewDetail.photoUrls"
disabled
:limit="1"
accept="image/*"
/>
<span v-else>—</span>
@@ -305,9 +305,9 @@
<AttachmentUploadField
ref="photoUploadRef"
v-model="formData.photoUrls"
:limit="1"
accept="image/*"
upload-folder="pl/vehicle-photo"
folder="pl/vehicle-photo"
hint="支持图片,可上传多张;可拖拽或在此区域粘贴上传"
/>
</el-form-item>
</div>
@@ -434,7 +434,6 @@
v-if="viewDetail?.photoUrls"
:model-value="viewDetail.photoUrls"
disabled
:limit="1"
accept="image/*"
/>
<span v-else>—</span>
+2 -3
View File
@@ -313,9 +313,9 @@
<AttachmentUploadField
ref="photoUploadRef"
v-model="formData.photoUrls"
:limit="1"
accept="image/*"
upload-folder="pl/vehicle-photo"
folder="pl/vehicle-photo"
hint="支持图片,可上传多张;可拖拽或在此区域粘贴上传"
/>
</el-form-item>
</div>
@@ -445,7 +445,6 @@
v-if="viewDetail?.photoUrls"
:model-value="viewDetail.photoUrls"
disabled
:limit="1"
accept="image/*"
/>
<span v-else>—</span>