yanghao 6 дней назад
Родитель
Сommit
c1d613848e
1 измененных файлов с 151 добавлено и 13 удалено
  1. 151 13
      src/views/researchProject/approval/index.vue

+ 151 - 13
src/views/researchProject/approval/index.vue

@@ -210,26 +210,39 @@
 
           <!-- Tab5:审批记录 -->
           <el-tab-pane label="审批记录" name="history">
-            <el-timeline>
+            <el-timeline class="approval-timeline">
               <el-timeline-item
                 v-for="record in selectedProject.approvalRecords || []"
                 :key="record.id"
                 :timestamp="formatDateTime(record.approveTime)"
+                placement="top"
                 :type="getRecordTagType(record.result)"
+                :hollow="false"
               >
-                <strong>{{ record.nodeName }}</strong>
-                <span style="margin-left: 8px">{{ record.approverName }}</span>
-                <el-tag
-                  size="small"
-                  :type="getRecordTagType(record.result)"
-                  style="margin-left: 8px"
-                >
-                  {{ getResultLabel(record.result) }}
-                </el-tag>
-                <p v-if="record.opinion" style="margin: 4px 0 0; color: #909399">
-                  {{ record.opinion }}
-                </p>
+                <!-- 自定义节点图标 -->
+                <template #dot>
+                  <div class="node-dot" :class="getNodeClass(record.result)">
+                    <Icon :icon="getNodeIcon(record)" class="node-icon" />
+                    <span class="node-badge" :class="getBadgeClass(record.result)">
+                      <Icon :icon="getBadgeIcon(record.result)" />
+                    </span>
+                  </div>
+                </template>
+
+                <div class="record-content">
+                  <strong>{{ record.nodeName }}</strong>
+                  <span class="approver">{{ record.approverName }}</span>
+                  <el-tag
+                    size="small"
+                    :type="getRecordTagType(record.result)"
+                    style="margin-left: 8px"
+                  >
+                    {{ getResultLabel(record.result) }}
+                  </el-tag>
+                  <p v-if="record.opinion" class="opinion">{{ record.opinion }}</p>
+                </div>
               </el-timeline-item>
+
               <el-empty
                 v-if="!selectedProject.approvalRecords?.length"
                 description="暂无审批记录"
@@ -518,6 +531,39 @@ const getStageNodeLabel = (p: any) => {
   return ''
 }
 
+// 节点主图标:提交用"人",审批用"审批/印章"图标
+const getNodeIcon = (record: any) => {
+  // result === 4 是"提交",其它是审批
+  return record.result === 4 ? 'ep:user' : 'ep:stamp'
+}
+
+// 节点主色:都统一蓝色,也可以按状态给不同色
+const getNodeClass = (result: number) => {
+  // 目前截图里节点都是蓝色,就统一返回 blue
+  return 'node-dot--blue'
+}
+
+// 右下角状态角标
+const getBadgeClass = (result: number) => {
+  const map: Record<number, string> = {
+    1: 'badge--success', // 通过 绿勾
+    2: 'badge--danger', // 驳回 红叉
+    3: 'badge--info', // 撤回 灰
+    4: 'badge--primary' // 提交 蓝
+  }
+  return map[result] || 'badge--info'
+}
+
+const getBadgeIcon = (result: number) => {
+  const map: Record<number, string> = {
+    1: 'ep:check',
+    2: 'ep:close',
+    3: 'ep:refresh-left',
+    4: 'ep:upload'
+  }
+  return map[result] || 'ep:info-filled'
+}
+
 const formatDateTime = (dt: any) => {
   if (!dt) return ''
   // 1. 如果是数字(时间戳),转成 Date
@@ -821,4 +867,96 @@ onMounted(async () => {
     grid-template-columns: 1fr;
   }
 }
+
+.approval-timeline {
+  padding: 8px 0;
+
+  :deep(.el-timeline-item__node) {
+    background: transparent;
+    width: auto;
+    height: auto;
+    left: 0;
+  }
+
+  :deep(.el-timeline-item__tail) {
+    border-left: 2px solid var(--el-border-color-lighter);
+    left: 18px; /* 对齐圆点中心 */
+  }
+
+  :deep(.el-timeline-item__wrapper) {
+    padding-left: 56px; /* 给左侧大圆点让位 */
+  }
+
+  :deep(.el-timeline-item__timestamp) {
+    color: var(--el-text-color-secondary);
+    font-size: 12px;
+  }
+}
+
+/* 节点大圆 */
+.node-dot {
+  position: relative;
+  width: 38px;
+  height: 38px;
+  border-radius: 50%;
+  display: grid;
+  place-items: center;
+  color: #fff;
+  box-shadow: 0 2px 6px rgba(64, 158, 255, 0.35);
+
+  &--blue {
+    background: linear-gradient(135deg, #5b8ff9, #3f6fe0);
+  }
+
+  .node-icon {
+    font-size: 20px;
+  }
+
+  /* 右下角状态角标 */
+  .node-badge {
+    position: absolute;
+    right: -4px;
+    bottom: -4px;
+    width: 18px;
+    height: 18px;
+    border-radius: 50%;
+    display: grid;
+    place-items: center;
+    color: #fff;
+    font-size: 11px;
+    border: 2px solid #fff;
+    box-sizing: content-box;
+
+    &.badge--success {
+      background: #52c41a;
+    }
+    &.badge--danger {
+      background: #f56c6c;
+    }
+    &.badge--info {
+      background: #909399;
+    }
+    &.badge--primary {
+      background: #409eff;
+    }
+  }
+}
+
+/* 右侧内容 */
+.record-content {
+  strong {
+    font-size: 14px;
+  }
+  .approver {
+    margin-left: 8px;
+    color: var(--el-text-color-regular);
+    font-size: 13px;
+  }
+  .opinion {
+    margin: 6px 0 0;
+    color: var(--el-text-color-secondary);
+    font-size: 13px;
+    line-height: 1.6;
+  }
+}
 </style>