zlwu 3 months ago
parent
commit
c00f19f8bd

+ 1 - 1
backend/app.pid

@@ -1 +1 @@
-29898
+76428

+ 29 - 14
graphify-out/manifest.json

@@ -1065,8 +1065,8 @@
     "semantic_hash": ""
   },
   "src/api/pl/index.ts": {
-    "mtime": 1785484576.5006232,
-    "ast_hash": "618f676f635932cb16ea78b4b316aca9",
+    "mtime": 1785637875.0674777,
+    "ast_hash": "c2f8fec9acb4519de443caffa9651ebb",
     "semantic_hash": ""
   },
   "src/api/pl/public-io-order.ts": {
@@ -1249,6 +1249,11 @@
     "ast_hash": "e3c51808508324c011fe3e069edfc646",
     "semantic_hash": ""
   },
+  "src/components/business/pl-carrier-list-attachment-cell/index.vue": {
+    "mtime": 1785635481.9345345,
+    "ast_hash": "36c22ab970d00df16060e59710b1b5a1",
+    "semantic_hash": ""
+  },
   "src/components/business/pl-carrier-view-dialog/index.vue": {
     "mtime": 1784957824.0,
     "ast_hash": "092c5ddd662464856054fd5321ead758",
@@ -1259,6 +1264,11 @@
     "ast_hash": "71f64456c73c67faf595e3ba8a73606a",
     "semantic_hash": ""
   },
+  "src/components/business/pl-cert-list-attachment-cell/index.vue": {
+    "mtime": 1785635623.7563994,
+    "ast_hash": "72e9edce9bb4c966311d78171f19d33c",
+    "semantic_hash": ""
+  },
   "src/components/business/pl-cert-view-list/index.vue": {
     "mtime": 1783753612.0,
     "ast_hash": "8c756fab63978e92794056486f34c1a1",
@@ -2784,6 +2794,11 @@
     "ast_hash": "ce4a23486763cdd36e54734d609f65b6",
     "semantic_hash": ""
   },
+  "src/utils/pl-photo.ts": {
+    "mtime": 1785637871.6080778,
+    "ast_hash": "6c38aee291a78f98b2fdc35e69de2038",
+    "semantic_hash": ""
+  },
   "src/utils/pl-slip-pdf-render.ts": {
     "mtime": 1784445096.0,
     "ast_hash": "ade9a729d714e9f3bb3f069a69bc92fb",
@@ -3135,8 +3150,8 @@
     "semantic_hash": ""
   },
   "src/views/crm/contract/components/ContractFormDialog.vue": {
-    "mtime": 1785632135.8392358,
-    "ast_hash": "e4b2ec706e9bfb6ea24d903cec1179fd",
+    "mtime": 1785633696.9241896,
+    "ast_hash": "5327a57a90552e3041923e8f06ef5f1c",
     "semantic_hash": ""
   },
   "src/views/crm/contract/components/ExecuteDialog.vue": {
@@ -3375,8 +3390,8 @@
     "semantic_hash": ""
   },
   "src/views/crm/quote/components/QuoteFormPanel.vue": {
-    "mtime": 1785632133.791225,
-    "ast_hash": "eaec7e8e9ee281d4864a41ed895a480f",
+    "mtime": 1785633695.5956545,
+    "ast_hash": "5edd7b3bdd31330234d384ddc3bc5276",
     "semantic_hash": ""
   },
   "src/views/crm/quote/components/ShareDialog.vue": {
@@ -3505,8 +3520,8 @@
     "semantic_hash": ""
   },
   "src/views/pl/carrier/index.vue": {
-    "mtime": 1785484060.378389,
-    "ast_hash": "048d4ee0a23b918d5f3f76cf68619cd4",
+    "mtime": 1785635443.614732,
+    "ast_hash": "8c19a853b54dd2bb5e0fdfa9fa3a67b7",
     "semantic_hash": ""
   },
   "src/views/pl/driver/index.vue": {
@@ -3585,8 +3600,8 @@
     "semantic_hash": ""
   },
   "src/views/pl/personnel/index.vue": {
-    "mtime": 1785484060.3787086,
-    "ast_hash": "4147f6500602567ff0304731d7212080",
+    "mtime": 1785638230.521841,
+    "ast_hash": "24db27d24af4ccf1f7c06339203eb98c",
     "semantic_hash": ""
   },
   "src/views/pl/plan-archive/index.vue": {
@@ -3620,8 +3635,8 @@
     "semantic_hash": ""
   },
   "src/views/pl/self-vehicle/index.vue": {
-    "mtime": 1785484060.3775532,
-    "ast_hash": "0fae9d03efc6d5c5eeea1182f5b3ba03",
+    "mtime": 1785638169.6844594,
+    "ast_hash": "e4c7bf3f1d172e126fd1e3a0f3e00a44",
     "semantic_hash": ""
   },
   "src/views/pl/transport-address/index.vue": {
@@ -3665,8 +3680,8 @@
     "semantic_hash": ""
   },
   "src/views/pl/vehicle/index.vue": {
-    "mtime": 1785484060.3667428,
-    "ast_hash": "d2fde8d1efcfe3f281404c6788a83133",
+    "mtime": 1785638067.9880946,
+    "ast_hash": "f009de2fcf7bfb569913db0af966a680",
     "semantic_hash": ""
   },
   "src/views/public/db/inventory/index.vue": {

+ 172 - 0
web-admin/src/components/business/pl-carrier-list-attachment-cell/index.vue

@@ -0,0 +1,172 @@
+<template>
+  <div class="pl-carrier-list-attachment-cell">
+    <div
+      v-for="item in items"
+      :key="item.key"
+      class="pl-carrier-list-attachment-cell__item"
+      :title="item.fullLabel"
+    >
+      <AttachmentUploadField
+        v-if="item.url"
+        :model-value="item.url"
+        disabled
+        :category-labels="item.categoryLabels"
+      />
+      <div v-else class="pl-carrier-list-attachment-cell__empty">
+        <span class="pl-carrier-list-attachment-cell__empty-text">未上传</span>
+        <span class="pl-carrier-list-attachment-cell__caption">{{ item.label }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import type { Carrier } from '@/api/pl'
+import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
+import { normalizeAttachmentUrls } from '@/utils/download-attachment'
+
+const props = defineProps<{
+  carrier?: Pick<
+    Carrier,
+    'businessLicenseUrls' | 'roadTransportLicenseUrls' | 'transportAgreementUrls'
+  > | null
+}>()
+
+type ItemDef = {
+  key: string
+  label: string
+  fullLabel: string
+  url: string
+  categoryLabels: Record<string, string>
+}
+
+function parseUrls(raw?: string | null): string[] {
+  const normalized = normalizeAttachmentUrls(raw)
+  if (!normalized) return []
+  return normalized
+    .split(',')
+    .map((item) => item.trim())
+    .filter(Boolean)
+}
+
+const items = computed<ItemDef[]>(() => {
+  const defs = [
+    {
+      key: 'businessLicense',
+      label: '营业执照',
+      fullLabel: '营业执照',
+      raw: props.carrier?.businessLicenseUrls
+    },
+    {
+      key: 'roadTransportLicense',
+      label: '道路许可',
+      fullLabel: '道路经营许可证',
+      raw: props.carrier?.roadTransportLicenseUrls
+    },
+    {
+      key: 'transportAgreement',
+      label: '运输协议',
+      fullLabel: '运输协议',
+      raw: props.carrier?.transportAgreementUrls
+    }
+  ]
+
+  const result: ItemDef[] = []
+  for (const def of defs) {
+    const urls = parseUrls(def.raw)
+    if (!urls.length) {
+      result.push({
+        key: def.key,
+        label: def.label,
+        fullLabel: def.fullLabel,
+        url: '',
+        categoryLabels: {}
+      })
+      continue
+    }
+    urls.forEach((url, index) => {
+      result.push({
+        key: `${def.key}-${index}`,
+        label: def.label,
+        fullLabel: def.fullLabel,
+        url,
+        categoryLabels: { [url]: def.label }
+      })
+    })
+  }
+  return result
+})
+</script>
+
+<style scoped lang="scss">
+.pl-carrier-list-attachment-cell {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+  flex-wrap: wrap;
+}
+
+.pl-carrier-list-attachment-cell__item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 96px;
+}
+
+.pl-carrier-list-attachment-cell__empty {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 96px;
+  height: 56px;
+  overflow: hidden;
+  border: 1px dashed var(--border, #d9e8e2);
+  border-radius: 8px;
+  background: var(--table-head, #f8fafc);
+  color: var(--mint-text);
+  font-size: 12px;
+}
+
+.pl-carrier-list-attachment-cell__empty-text {
+  margin-bottom: 14px;
+  font-size: 11px;
+  line-height: 1.3;
+}
+
+.pl-carrier-list-attachment-cell__caption {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  padding: 2px 4px;
+  background: rgba(15, 23, 42, 0.62);
+  color: #fff;
+  font-size: 11px;
+  line-height: 1.3;
+  text-align: center;
+  word-break: keep-all;
+}
+
+:deep(.attachment-upload) {
+  width: 100%;
+}
+
+:deep(.attachment-list) {
+  flex-wrap: nowrap;
+}
+
+:deep(.attachment-item) {
+  width: 96px;
+  margin-bottom: 0;
+}
+
+:deep(.attachment-preview) {
+  height: 56px;
+}
+
+:deep(.attachment-footer) {
+  display: none;
+}
+</style>

+ 15 - 2
web-admin/src/views/pl/carrier/index.vue

@@ -93,7 +93,7 @@
           <el-table-column
             type="index"
             label="序号"
-            min-width="68"
+            min-width="52"
             align="center"
             header-align="center"
             :index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
@@ -126,6 +126,17 @@
             align="center"
             header-align="center"
           />
+          <el-table-column
+            v-if="isColumnVisible('qualificationAttachments')"
+            label="资质附件"
+            min-width="340"
+            align="left"
+            header-align="left"
+          >
+            <template #default="{ row }">
+              <PlCarrierListAttachmentCell :carrier="row" />
+            </template>
+          </el-table-column>
           <el-table-column
             v-if="isColumnVisible('status')"
             label="合作状态"
@@ -310,6 +321,7 @@
 import { Check, Close, Delete, Edit, Plus, RefreshLeft, Search } from '@element-plus/icons-vue'
 import { carrierApi, type Carrier } from '@/api/pl'
 import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
+import PlCarrierListAttachmentCell from '@/components/business/pl-carrier-list-attachment-cell/index.vue'
 import PlCarrierViewDialog from '@/components/business/pl-carrier-view-dialog/index.vue'
 import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
 import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
@@ -324,6 +336,7 @@ const quickSearchKeyword = ref('')
 const listColumnDefs = [
   { prop: 'contactName', label: '联系人' },
   { prop: 'contactPhone', label: '联系电话' },
+  { prop: 'qualificationAttachments', label: '资质附件' },
   { prop: 'status', label: '合作状态' },
   { prop: 'qualificationHint', label: '资质提示' },
   { prop: 'transportAgreementExpireDate', label: '协议有效期' },
@@ -336,7 +349,7 @@ const {
   isIndeterminate,
   isColumnVisible,
   resetToDefault
-} = usePersistedListColumns('pl-carrier-list-columns', listColumnDefs)
+} = usePersistedListColumns('pl-carrier-list-columns-v2', listColumnDefs)
 
 const submitLoading = ref(false)
 const carrierList = ref<Carrier[]>([])