zlwu 4 месяцев назад
Родитель
Сommit
cedb7ee681

+ 16 - 16
graphify-out/manifest.json

@@ -5905,8 +5905,8 @@
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/vehicle/index.vue": {
-    "mtime": 1781966055.86601,
-    "ast_hash": "c5bd0a2aff42afffa4a95c047baa97d4",
+    "mtime": 1782017734.4540741,
+    "ast_hash": "c619257173d067e51e637694d427fb6e",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/driver/index.vue": {
@@ -5925,8 +5925,8 @@
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/carrier/index.vue": {
-    "mtime": 1781949223.0486753,
-    "ast_hash": "7099bdc45d754c0087ecf33e625353db",
+    "mtime": 1782017734.0768383,
+    "ast_hash": "068a4a50111ac227345f263a36c8a080",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/router/modules/pl.ts": {
@@ -5965,8 +5965,8 @@
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/personnel/index.vue": {
-    "mtime": 1781966055.2295575,
-    "ast_hash": "982c536d997007419942ba22748f2ab6",
+    "mtime": 1782017734.9490426,
+    "ast_hash": "9ca8b4c51107b5128f0b33ac32f99b53",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/backend/app/schemas/pl/base_data.py": {
@@ -5990,8 +5990,8 @@
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-cert-view-list/index.vue": {
-    "mtime": 1781949997.043425,
-    "ast_hash": "f1b24be2137a383fc204ec4553d62f1a",
+    "mtime": 1782017369.1065211,
+    "ast_hash": "c9b021a9d8aa1fd8baed449716dc7047",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-personnel-view-list/index.vue": {
@@ -6010,23 +6010,23 @@
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-view-panel/index.vue": {
-    "mtime": 1781949995.629496,
-    "ast_hash": "45d7ac945c7948a0b543917a4eec3025",
+    "mtime": 1782017363.5142002,
+    "ast_hash": "5eefb28c2dde89df79a43a792722f07e",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-carrier-view-dialog/index.vue": {
-    "mtime": 1781949994.7691498,
-    "ast_hash": "5e879c7fa06cfd490e6dae22c722b2a3",
+    "mtime": 1782017029.7709475,
+    "ast_hash": "445f743f6c6af15c0f9aaa8fc89ca7b7",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/views/pl/self-vehicle/index.vue": {
-    "mtime": 1781966055.7689826,
-    "ast_hash": "3cdef2b24f297e35428636347c61d8af",
+    "mtime": 1782017734.7090108,
+    "ast_hash": "83cc6f5ddac7d812014c180a017c4f97",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-vehicle-personnel-view-list/PersonnelViewPanel.vue": {
-    "mtime": 1781949998.7342877,
-    "ast_hash": "21365a0199c5c22eb3519e44a88d6c37",
+    "mtime": 1782017371.946757,
+    "ast_hash": "fbb52644ee368d1b8e292cb426004814",
     "semantic_hash": ""
   },
   "/Users/zlwu/work/00code/shanxia2.0/web-admin/src/components/business/pl-plan-form-dialog/index.vue": {

+ 0 - 12
web-admin/graphify-out/.graphify_labels.json

@@ -433,12 +433,10 @@
   "431": "Community 431",
   "432": "Community 432",
   "433": "Community 433",
-  "434": "Community 434",
   "435": "Community 435",
   "436": "Community 436",
   "437": "Community 437",
   "438": "Community 438",
-  "439": "Community 439",
   "440": "Community 440",
   "441": "Community 441",
   "442": "Community 442",
@@ -554,7 +552,6 @@
   "552": "Community 552",
   "553": "Community 553",
   "554": "Community 554",
-  "555": "Community 555",
   "556": "Community 556",
   "557": "Community 557",
   "558": "Community 558",
@@ -610,13 +607,11 @@
   "608": "Community 608",
   "609": "Community 609",
   "610": "Community 610",
-  "611": "Community 611",
   "612": "Community 612",
   "613": "Community 613",
   "614": "Community 614",
   "615": "Community 615",
   "616": "Community 616",
-  "617": "Community 617",
   "618": "Community 618",
   "619": "Community 619",
   "620": "Community 620",
@@ -629,8 +624,6 @@
   "627": "Community 627",
   "628": "Community 628",
   "629": "Community 629",
-  "630": "Community 630",
-  "631": "Community 631",
   "632": "Community 632",
   "633": "Community 633",
   "634": "Community 634",
@@ -668,12 +661,9 @@
   "666": "Community 666",
   "667": "Community 667",
   "669": "Community 669",
-  "670": "Community 670",
   "671": "Community 671",
   "672": "Community 672",
-  "673": "Community 673",
   "674": "Community 674",
-  "675": "Community 675",
   "676": "Community 676",
   "678": "Community 678",
   "679": "Community 679",
@@ -682,13 +672,11 @@
   "684": "Community 684",
   "685": "Community 685",
   "686": "Community 686",
-  "687": "Community 687",
   "688": "Community 688",
   "690": "Community 690",
   "691": "Community 691",
   "692": "Community 692",
   "694": "Community 694",
-  "696": "Community 696",
   "700": "Community 700",
   "704": "Community 704"
 }

Разница между файлами не показана из-за своего большого размера
+ 224 - 228
web-admin/graphify-out/GRAPH_REPORT.md


Разница между файлами не показана из-за своего большого размера
+ 112 - 112
web-admin/graphify-out/graph.json


+ 21 - 56
web-admin/src/components/business/pl-carrier-view-dialog/index.vue

@@ -15,36 +15,7 @@
     <div class="carrier-view-body" v-loading="loading">
       <section class="carrier-view-top">
         <h3 class="carrier-view-block-title">承运商信息</h3>
-        <dl v-if="carrier" class="carrier-view-kv-grid">
-          <div class="carrier-view-kv">
-            <dt>承运商名称</dt>
-            <dd>{{ carrier.carrierName || '—' }}</dd>
-          </div>
-          <div class="carrier-view-kv">
-            <dt>联系人</dt>
-            <dd>{{ carrier.contactName || '—' }}</dd>
-          </div>
-          <div class="carrier-view-kv">
-            <dt>联系电话</dt>
-            <dd>{{ carrier.contactPhone || '—' }}</dd>
-          </div>
-          <div class="carrier-view-kv">
-            <dt>合作状态</dt>
-            <dd>
-              <el-tag :type="getPlCarrierStatusTagType(carrier.status)" size="small">
-                {{ getPlCarrierStatusLabel(carrier.status) }}
-              </el-tag>
-            </dd>
-          </div>
-          <div class="carrier-view-kv">
-            <dt>创建时间</dt>
-            <dd>{{ formatDateTime(carrier.createTime) }}</dd>
-          </div>
-          <div class="carrier-view-kv carrier-view-kv-span">
-            <dt>备注</dt>
-            <dd>{{ carrier.remark || '—' }}</dd>
-          </div>
-        </dl>
+        <ViewKvTable v-if="carrier" :fields="carrierViewFields" mode="triple" />
       </section>
 
       <section class="carrier-view-bottom">
@@ -80,6 +51,8 @@
 import { Close } from '@element-plus/icons-vue'
 import { carrierApi, vehicleApi, type Carrier } from '@/api/pl'
 import PlVehicleViewPanel from '@/components/business/pl-vehicle-view-panel/index.vue'
+import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vue'
+import type { ViewField } from '@/components/business/system-view-dialog/types'
 import {
   loadVehicleViewBundles,
   type VehicleViewBundle
@@ -106,6 +79,24 @@ const carrier = ref<Carrier | null>(null)
 const vehicleBundles = ref<VehicleViewBundle[]>([])
 const activeTab = ref('')
 
+const carrierViewFields = computed<ViewField[]>(() => {
+  const c = carrier.value
+  if (!c) return []
+
+  return [
+    { label: '承运商名称', value: c.carrierName },
+    { label: '联系人', value: c.contactName || '—' },
+    { label: '联系电话', value: c.contactPhone || '—' },
+    {
+      label: '合作状态',
+      value: getPlCarrierStatusLabel(c.status),
+      tagType: getPlCarrierStatusTagType(c.status)
+    },
+    { label: '创建时间', value: formatDateTime(c.createTime) },
+    { label: '备注', value: c.remark || '—', span: 'full' }
+  ]
+})
+
 watch(
   () => [props.visible, props.carrierId] as const,
   async ([visible, carrierId]) => {
@@ -164,20 +155,6 @@ function handleClosed() {
   border-bottom: 1px solid #e2e8f0;
 }
 
-.carrier-view-kv-grid {
-  display: grid;
-  grid-template-columns: repeat(3, minmax(0, 1fr));
-  gap: 12px 20px;
-}
-
-.carrier-view-kv {
-  min-width: 0;
-}
-
-.carrier-view-kv-span {
-  grid-column: 1 / -1;
-}
-
 .carrier-view-empty {
   margin: 0;
   font-size: 13px;
@@ -193,16 +170,4 @@ function handleClosed() {
     padding-top: 4px;
   }
 }
-
-@media (max-width: 900px) {
-  .carrier-view-kv-grid {
-    grid-template-columns: repeat(2, minmax(0, 1fr));
-  }
-}
-
-@media (max-width: 640px) {
-  .carrier-view-kv-grid {
-    grid-template-columns: 1fr;
-  }
-}
 </style>

+ 70 - 56
web-admin/src/components/business/pl-cert-view-list/index.vue

@@ -10,40 +10,25 @@
       >
         <h4 class="cert-view-card-title">证书 {{ index + 1 }}</h4>
 
-        <dl class="cert-view-kv-grid">
-          <div class="cert-view-kv">
-            <dt>证书名称</dt>
-            <dd>{{ cert.certName || '—' }}</dd>
-          </div>
-          <div class="cert-view-kv">
-            <dt>证号</dt>
-            <dd>{{ cert.certNo || '—' }}</dd>
-          </div>
-          <div class="cert-view-kv">
-            <dt>有效期</dt>
-            <dd>
-              <el-tag v-if="cert.expireDate" :type="getExpireTagType(cert.expireDate)" size="small">
-                {{ cert.expireDate }}
-              </el-tag>
-              <span v-else>—</span>
-            </dd>
-          </div>
-          <div class="cert-view-kv">
-            <dt>提醒天数</dt>
-            <dd>{{ cert.remindDays ?? 30 }} 天</dd>
-          </div>
-          <div class="cert-view-kv cert-view-kv-span">
-            <dt>附件</dt>
-            <dd>
-              <AttachmentUploadField
-                v-if="cert.attachmentUrls"
-                :model-value="cert.attachmentUrls"
-                disabled
-              />
-              <span v-else class="view-empty-inline">—</span>
-            </dd>
-          </div>
-        </dl>
+        <div class="cert-view-kv-table-group">
+          <ViewKvTable :fields="buildCertViewFields(cert)" mode="triple" />
+          <table
+            v-if="cert.attachmentUrls"
+            class="cert-view-attachment-table view-kv-table view-kv-table--triple"
+          >
+            <tbody>
+              <tr class="row-long">
+                <th>附件</th>
+                <td colspan="5">
+                  <AttachmentUploadField
+                    :model-value="cert.attachmentUrls"
+                    disabled
+                  />
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
       </div>
     </div>
   </div>
@@ -51,6 +36,8 @@
 
 <script setup lang="ts">
 import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
+import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vue'
+import type { ViewField } from '@/components/business/system-view-dialog/types'
 import type { CertRead } from '@/api/pl'
 
 withDefaults(
@@ -71,6 +58,19 @@ function getExpireTagType(dateStr: string | undefined) {
   if (diffDays <= 30) return 'warning'
   return 'success'
 }
+
+function buildCertViewFields(cert: CertRead): ViewField[] {
+  return [
+    { label: '证书名称', value: cert.certName || '—' },
+    { label: '证号', value: cert.certNo || '—' },
+    {
+      label: '有效期',
+      value: cert.expireDate || '—',
+      tagType: cert.expireDate ? getExpireTagType(cert.expireDate) : undefined
+    },
+    { label: '提醒天数', value: `${cert.remindDays ?? 30} 天` }
+  ]
+}
 </script>
 
 <style scoped lang="scss">
@@ -109,33 +109,47 @@ function getExpireTagType(dateStr: string | undefined) {
   line-height: 1;
 }
 
-.cert-view-kv-grid {
-  display: grid;
-  grid-template-columns: repeat(4, minmax(0, 1fr));
-  gap: 12px 20px;
-}
-
-.cert-view-kv {
-  min-width: 0;
-}
-
-.cert-view-kv-span {
-  grid-column: 1 / -1;
-}
+.cert-view-kv-table-group {
+  border: 1px solid var(--border, #d9e8e2);
+  border-radius: 8px;
+  overflow: hidden;
+  background: #fff;
 
-.view-empty-inline {
-  color: #000;
+  :deep(.view-kv-table-wrap) {
+    border: none;
+    border-radius: 0;
+  }
 }
 
-@media (max-width: 1200px) {
-  .cert-view-kv-grid {
-    grid-template-columns: repeat(2, minmax(0, 1fr));
+.cert-view-attachment-table {
+  width: 100%;
+  border-collapse: collapse;
+  font-size: 14px;
+  table-layout: fixed;
+  border-top: 1px solid #eef2f6;
+
+  th {
+    width: 10%;
+    padding: 10px 12px;
+    background: var(--table-head, #f8fafc);
+    color: var(--text-muted, #64748b);
+    font-size: 13px;
+    font-weight: 500;
+    text-align: left;
+    vertical-align: top;
+    border-right: 1px solid #eef2f6;
+    white-space: normal;
+    word-break: break-word;
+    line-height: 1.4;
   }
-}
 
-@media (max-width: 640px) {
-  .cert-view-kv-grid {
-    grid-template-columns: 1fr;
+  td {
+    padding: 10px 14px;
+    font-weight: 600;
+    color: var(--text, #1e293b);
+    vertical-align: top;
+    word-break: break-word;
+    line-height: 1.45;
   }
 }
 </style>

+ 31 - 36
web-admin/src/components/business/pl-vehicle-personnel-view-list/PersonnelViewPanel.vue

@@ -10,20 +10,7 @@
       </el-tag>
     </h4>
 
-    <dl class="personnel-view-kv-grid">
-      <div class="personnel-view-kv">
-        <dt>手机号</dt>
-        <dd>{{ person.phoneNo || '—' }}</dd>
-      </div>
-      <div class="personnel-view-kv">
-        <dt>年龄</dt>
-        <dd>{{ person.age ?? '—' }}</dd>
-      </div>
-      <div class="personnel-view-kv personnel-view-kv-span">
-        <dt>备注</dt>
-        <dd>{{ person.remark || '—' }}</dd>
-      </div>
-    </dl>
+    <ViewKvTable :fields="personnelViewFields" mode="triple" />
 
     <div class="personnel-certs-block">
       <p class="personnel-certs-title">人员证书</p>
@@ -34,11 +21,13 @@
 
 <script setup lang="ts">
 import PlCertViewList from '@/components/business/pl-cert-view-list/index.vue'
+import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vue'
+import type { ViewField } from '@/components/business/system-view-dialog/types'
 import type { CertRead, VehiclePersonnelRow } from '@/api/pl'
 
 type VehiclePersonnelViewRow = VehiclePersonnelRow & { certList?: CertRead[] }
 
-withDefaults(
+const props = withDefaults(
   defineProps<{
     person: VehiclePersonnelViewRow
     showTitle?: boolean
@@ -48,6 +37,32 @@ withDefaults(
   }
 )
 
+const personnelViewFields = computed<ViewField[]>(() => {
+  const person = props.person
+  const fields: ViewField[] = [
+    { label: '姓名', value: person.personnelName || '—' },
+    { label: '手机号', value: person.phoneNo || '—' },
+    { label: '年龄', value: person.age ?? '—' },
+    {
+      label: '角色',
+      value: getRoleLabel(person.role),
+      tagType: getRoleTagType(person.role)
+    }
+  ]
+
+  if (person.role === 'driver') {
+    fields.push({
+      label: '默认司机',
+      value: person.isDefault === 1 ? '是' : '否',
+      tagType: person.isDefault === 1 ? 'success' : 'info'
+    })
+  }
+
+  fields.push({ label: '备注', value: person.remark || '—', span: 'full' })
+
+  return fields
+})
+
 function getRoleLabel(role: string) {
   const map: Record<string, string> = {
     driver: '司机',
@@ -76,22 +91,8 @@ function getRoleTagType(role: string) {
   line-height: 1.4;
 }
 
-.personnel-view-kv-grid {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 12px 20px;
-  margin: 0 0 12px;
-}
-
-.personnel-view-kv {
-  min-width: 0;
-}
-
-.personnel-view-kv-span {
-  grid-column: 1 / -1;
-}
-
 .personnel-certs-block {
+  margin-top: 12px;
   padding-top: 12px;
   border-top: 1px solid #e2e8f0;
 }
@@ -102,10 +103,4 @@ function getRoleTagType(role: string) {
   font-weight: 600;
   color: #1e293b;
 }
-
-@media (max-width: 640px) {
-  .personnel-view-kv-grid {
-    grid-template-columns: 1fr;
-  }
-}
 </style>

+ 25 - 68
web-admin/src/components/business/pl-vehicle-view-panel/index.vue

@@ -19,48 +19,7 @@
     <div v-show="!collapsible || expanded" class="vehicle-view-panel-body">
       <section class="vehicle-view-section">
         <h4 v-if="!collapsible" class="vehicle-view-section-title">车辆信息</h4>
-        <dl class="vehicle-view-kv-grid">
-          <div class="vehicle-view-kv">
-            <dt>车牌号</dt>
-            <dd>{{ detail.licensePlate || '—' }}</dd>
-          </div>
-          <div class="vehicle-view-kv">
-            <dt>车辆类型</dt>
-            <dd>
-              <el-tag :type="getPlVehicleTypeTagType(detail.vehicleType)" size="small">
-                {{ getPlVehicleTypeLabel(detail.vehicleType) }}
-              </el-tag>
-            </dd>
-          </div>
-          <div class="vehicle-view-kv">
-            <dt>承运商</dt>
-            <dd>{{ detail.carrierName || '—' }}</dd>
-          </div>
-          <div class="vehicle-view-kv">
-            <dt>载重(吨)</dt>
-            <dd>{{ detail.loadCapacity ?? '—' }}</dd>
-          </div>
-          <div class="vehicle-view-kv">
-            <dt>车辆状态</dt>
-            <dd>
-              <el-tag :type="getPlVehicleStatusTagType(detail.vehicleStatus)" size="small">
-                {{ getPlVehicleStatusLabel(detail.vehicleStatus) }}
-              </el-tag>
-            </dd>
-          </div>
-          <div class="vehicle-view-kv">
-            <dt>关联人员摘要</dt>
-            <dd>{{ detail.personnelSummary || '—' }}</dd>
-          </div>
-          <div class="vehicle-view-kv vehicle-view-kv-span">
-            <dt>备注</dt>
-            <dd>{{ detail.remark || '—' }}</dd>
-          </div>
-          <div class="vehicle-view-kv">
-            <dt>创建时间</dt>
-            <dd>{{ formatDateTime(detail.createTime) }}</dd>
-          </div>
-        </dl>
+        <ViewKvTable :fields="vehicleViewFields" mode="triple" />
       </section>
 
       <section class="vehicle-view-section">
@@ -76,6 +35,8 @@
 <script setup lang="ts">
 import { ArrowDown } from '@element-plus/icons-vue'
 import PlVehicleCertPersonnelTabs from '@/components/business/pl-vehicle-cert-personnel-tabs/index.vue'
+import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vue'
+import type { ViewField } from '@/components/business/system-view-dialog/types'
 import type { VehiclePersonnelViewRow } from '@/components/business/pl-vehicle-personnel-view-list/index.vue'
 import type { VehicleDetail } from '@/api/pl'
 import { formatDateTime } from '@/utils/format'
@@ -102,6 +63,28 @@ const props = withDefaults(
 
 const expanded = ref(props.defaultExpanded)
 
+const vehicleViewFields = computed<ViewField[]>(() => {
+  const d = props.detail
+  return [
+    { label: '车牌号', value: d.licensePlate },
+    {
+      label: '车辆类型',
+      value: getPlVehicleTypeLabel(d.vehicleType),
+      tagType: getPlVehicleTypeTagType(d.vehicleType)
+    },
+    { label: '承运商', value: d.carrierName || '—' },
+    { label: '载重(吨)', value: d.loadCapacity ?? '—' },
+    {
+      label: '车辆状态',
+      value: getPlVehicleStatusLabel(d.vehicleStatus),
+      tagType: getPlVehicleStatusTagType(d.vehicleStatus)
+    },
+    { label: '关联人员摘要', value: d.personnelSummary || '—', span: true },
+    { label: '创建时间', value: formatDateTime(d.createTime) },
+    { label: '备注', value: d.remark || '—', span: 'full' }
+  ]
+})
+
 watch(
   () => props.defaultExpanded,
   value => {
@@ -170,30 +153,4 @@ watch(
   border-left: 3px solid #10b981;
   line-height: 1;
 }
-
-.vehicle-view-kv-grid {
-  display: grid;
-  grid-template-columns: repeat(4, minmax(0, 1fr));
-  gap: 12px 20px;
-}
-
-.vehicle-view-kv {
-  min-width: 0;
-}
-
-.vehicle-view-kv-span {
-  grid-column: 1 / -1;
-}
-
-@media (max-width: 1200px) {
-  .vehicle-view-kv-grid {
-    grid-template-columns: repeat(2, minmax(0, 1fr));
-  }
-}
-
-@media (max-width: 640px) {
-  .vehicle-view-kv-grid {
-    grid-template-columns: 1fr;
-  }
-}
 </style>

+ 45 - 8
web-admin/src/views/pl/carrier/index.vue

@@ -77,13 +77,15 @@
             </div>
 
             <dl class="carrier-card-kv">
-              <div class="carrier-card-kv-item">
-                <dt>联系人</dt>
-                <dd>{{ row.contactName || '—' }}</dd>
-              </div>
-              <div class="carrier-card-kv-item">
-                <dt>联系电话</dt>
-                <dd>{{ row.contactPhone || '—' }}</dd>
+              <div class="carrier-card-kv-item carrier-card-kv-contact-row">
+                <div class="carrier-card-contact-part">
+                  <dt>联系人</dt>
+                  <dd>{{ row.contactName || '—' }}</dd>
+                </div>
+                <div class="carrier-card-contact-part">
+                  <dt>联系电话</dt>
+                  <dd>{{ row.contactPhone || '—' }}</dd>
+                </div>
               </div>
               <div class="carrier-card-kv-item">
                 <dt>备注</dt>
@@ -360,7 +362,7 @@ onMounted(() => {
 
 .carrier-card-head {
   display: flex;
-  align-items: flex-start;
+  align-items: center;
   gap: 10px;
 }
 
@@ -394,6 +396,41 @@ onMounted(() => {
   gap: 4px;
   font-size: 13px;
 
+  &.carrier-card-kv-contact-row {
+    display: flex;
+    align-items: flex-start;
+    gap: 16px;
+  }
+}
+
+.carrier-card-contact-part {
+  display: grid;
+  gap: 4px;
+  flex: 1;
+  min-width: 0;
+  font-size: 13px;
+
+  dt {
+    margin: 0;
+    color: #1e293b;
+    font-weight: 500;
+    font-size: 11px;
+    line-height: 1.4;
+  }
+
+  dd {
+    margin: 0;
+    color: #1e293b;
+    word-break: break-word;
+    line-height: 1.5;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+    overflow: hidden;
+  }
+}
+
+.carrier-card-kv-item:not(.carrier-card-kv-contact-row) {
   dt {
     margin: 0;
     color: #1e293b;

+ 6 - 4
web-admin/src/views/pl/personnel/index.vue

@@ -311,10 +311,12 @@ const viewSections = computed<ViewSection[]>(() => {
           value: d.nearestExpireDate || '—',
           tagType: d.nearestExpireDate ? getExpireTagType(d.nearestExpireDate) : undefined
         },
-        { label: '备注', value: d.remark || '—', span: true },
         { label: '创建时间', value: formatDateTime(d.createTime) },
-        { label: '更新时间', value: formatDateTime(d.updateTime) }
-      ]
+        { label: '更新时间', value: formatDateTime(d.updateTime) },
+        { label: '备注', value: d.remark || '—', span: 'full' }
+      ],
+      fieldLayout: 'table',
+      tableMode: 'triple'
     },
     {
       title: '人员证书',
@@ -555,7 +557,7 @@ onMounted(() => {
 
 .personnel-card-head {
   display: flex;
-  align-items: flex-start;
+  align-items: center;
   gap: 10px;
 }
 

+ 5 - 3
web-admin/src/views/pl/self-vehicle/index.vue

@@ -357,9 +357,11 @@ const viewSections = computed<ViewSection[]>(() => {
           tagType: getStatusTagType(d.vehicleStatus)
         },
         { label: '关联人员摘要', value: d.personnelSummary || '—', span: true },
-        { label: '备注', value: d.remark || '—', span: true },
-        { label: '创建时间', value: formatDateTime(d.createTime) }
+        { label: '创建时间', value: formatDateTime(d.createTime) },
+        { label: '备注', value: d.remark || '—', span: 'full' }
       ],
+      fieldLayout: 'table',
+      tableMode: 'triple',
       slot: 'vehicleDetailTabs'
     }
   ]
@@ -745,7 +747,7 @@ onMounted(() => {
 
 .vehicle-card-head {
   display: flex;
-  align-items: flex-start;
+  align-items: center;
   gap: 10px;
 }
 

+ 1 - 1
web-admin/src/views/pl/vehicle/index.vue

@@ -857,7 +857,7 @@ onMounted(() => {
 
 .vehicle-card-head {
   display: flex;
-  align-items: flex-start;
+  align-items: center;
   gap: 10px;
 }
 

Некоторые файлы не были показаны из-за большого количества измененных файлов