From 552ec6b7d8ccc56c379da195fc6c9c74312b1070 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期五, 22 五月 2026 17:57:46 +0800
Subject: [PATCH] OA部分查询条件变更

---
 src/pages/productionManagement/productionOrder/index.vue |  834 +++++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 518 insertions(+), 316 deletions(-)

diff --git a/src/pages/productionManagement/productionOrder/index.vue b/src/pages/productionManagement/productionOrder/index.vue
index 7f7ae4f..5f31fb1 100644
--- a/src/pages/productionManagement/productionOrder/index.vue
+++ b/src/pages/productionManagement/productionOrder/index.vue
@@ -1,344 +1,546 @@
 <template>
-	<view class="production-order">
-		<!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
-		<PageHeader title="鐢熶骇璁㈠崟" @back="goBack" />
-		
-		<!-- 鎼滅储鍖哄煙 -->
-		<view class="search-section">
-			<view class="search-bar">
-				<view class="search-input">
-					<up-input
-						class="search-text"
-						placeholder="璇疯緭鍏ヨ鍗曞彿鎴栦骇鍝佸悕绉�"
-						v-model="searchForm.keyword"
-						@change="handleQuery"
-						clearable
-					/>
-				</view>
-				<view class="filter-button" @click="handleQuery">
-					<up-icon name="search" size="24" color="#999"></up-icon>
-				</view>
-			</view>
-		</view>
-		
-		<!-- 鍒楄〃鍖哄煙 -->
-		<scroll-view scroll-y class="list-container" v-if="tableData.length > 0" @scrolltolower="loadMore">
-			<view v-for="(item, index) in tableData" :key="item.id || index">
-				<view class="ledger-item">
-					<view class="item-header">
-						<view class="item-left">
-							<view class="document-icon">
-								<up-icon name="file-text" size="16" color="#ffffff"></up-icon>
-							</view>
-							<text class="item-id">{{ item.npsNo }}</text>
-						</view>
-						<view class="item-right">
-							<up-tag :text="getStatusText(item.status)" :type="getStatusType(item.status)" size="mini" />
-						</view>
-					</view>
-					<up-divider></up-divider>
-					
-					<view class="item-details">
-						<view class="detail-row">
-							<text class="detail-label">浜у搧鍚嶇О</text>
-							<text class="detail-value font-bold">{{ item.productName || '-' }}</text>
-						</view>
-						<view class="detail-row">
-							<text class="detail-label">瑙勬牸鍨嬪彿</text>
-							<text class="detail-value">{{ item.model || '-' }}</text>
-						</view>
-						<view class="detail-row">
-							<text class="detail-label">璁㈠崟鏁伴噺</text>
-							<text class="detail-value">{{ item.quantity || 0 }}</text>
-						</view>
-						<view class="detail-row">
-							<text class="detail-label">瀹屾垚杩涘害</text>
-							<view class="progress-box">
-								<up-line-progress :percentage="toProgressPercentage(item.completionStatus)" 
-												:activeColor="progressColor(item.completionStatus)" 
-												height="10"></up-line-progress>
-								<text class="progress-text">{{ item.completeQuantity || 0 }} / {{ item.quantity || 0 }}</text>
-							</view>
-						</view>
-						<view class="detail-row">
-							<text class="detail-label">璁″垝瀹屾垚</text>
-							<text class="detail-value">{{ formatDate(item.planCompleteTime) }}</text>
-						</view>
-					</view>
-					
-					<view class="item-footer">
-						<view class="action-btns">
-							<up-button type="primary" size="mini" plain text="鏉ユ簮" @click="goSource(item)"></up-button>
-							<up-button type="success" size="mini" plain text="棰嗘枡璇︽儏" @click="goPickingDetail(item)"></up-button>
-						</view>
-					</view>
-				</view>
-			</view>
-			<up-loadmore :status="loadStatus" v-if="tableData.length >= page.size" />
-		</scroll-view>
-		
-		<view v-else class="no-data">
-			<up-empty mode="data" text="鏆傛棤鐢熶骇璁㈠崟鏁版嵁"></up-empty>
-		</view>
-	</view>
+  <view class="production-order">
+    <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+    <PageHeader title="鐢熶骇璁㈠崟"
+                @back="goBack" />
+    <!-- 鎼滅储鍖哄煙 -->
+    <view class="search-section">
+      <view class="search-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    placeholder="璇疯緭鍏ヨ鍗曞彿鎴栦骇鍝佸悕绉�"
+                    v-model="searchForm.keyword"
+                    @change="handleQuery"
+                    clearable />
+        </view>
+        <view class="filter-button"
+              @click="handleQuery">
+          <up-icon name="search"
+                   size="24"
+                   color="#999"></up-icon>
+        </view>
+      </view>
+    </view>
+    <!-- 鍒楄〃鍖哄煙 -->
+    <scroll-view scroll-y
+                 class="list-container"
+                 v-if="tableData.length > 0"
+                 @scrolltolower="loadMore">
+      <view v-for="(item, index) in tableData"
+            :key="item.id || index">
+        <view class="ledger-item">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="document-icon">
+                <up-icon name="file-text"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.npsNo }}</text>
+            </view>
+            <view class="item-right">
+              <up-tag :text="getStatusText(item.status)"
+                      :type="getStatusType(item.status)"
+                      size="mini" />
+            </view>
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-row">
+              <text class="detail-label">浜у搧鍚嶇О</text>
+              <text class="detail-value font-bold">{{ item.productName || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">瑙勬牸鍨嬪彿</text>
+              <text class="detail-value">{{ item.model || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">璁㈠崟鏁伴噺</text>
+              <text class="detail-value">{{ item.quantity || 0 }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">瀹屾垚杩涘害</text>
+              <view class="progress-box">
+                <up-line-progress :percentage="toProgressPercentage(item.completionStatus)"
+                                  :activeColor="progressColor(item.completionStatus)"
+                                  height="10"></up-line-progress>
+                <text class="progress-text">{{ item.completeQuantity || 0 }} / {{ item.quantity || 0 }}</text>
+              </view>
+            </view>
+            <!-- 宸ュ簭鐢熶骇杩涘害灞曠ず -->
+            <view class="detail-row process-row">
+              <text class="detail-label">宸ュ簭杩涘害</text>
+              <scroll-view scroll-x
+                           class="process-scroll">
+                <view class="process-container">
+                  <view v-for="(process, pIdx) in item.processRouteStatus"
+                        :key="pIdx"
+                        class="process-item">
+                    <view class="process-node">
+                      <view class="node-circle"
+                            :class="{ 'is-complete': process.percentage >= 100 }">
+                        <text class="node-percentage"
+                              :style="{ color: process.percentage >= 100 ? '#52c41a' : (process.percentage >= 70 ? '#f56c6c' : '#3c9cff') }">{{ process.percentage }}%</text>
+                      </view>
+                      <text class="node-name">{{ process.name }}</text>
+                    </view>
+                    <view v-if="pIdx < item.processRouteStatus.length - 1"
+                          class="node-line"></view>
+                  </view>
+                  <view v-if="!item.processRouteStatus || !item.processRouteStatus.length"
+                        class="no-process">-</view>
+                </view>
+              </scroll-view>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">璁″垝瀹屾垚</text>
+              <text class="detail-value">{{ formatDate(item.planCompleteTime) }}</text>
+            </view>
+          </view>
+          <view class="item-footer">
+            <view class="action-btns">
+              <up-button type="info"
+                         size="small"
+                         plain
+                         text="鐢熶骇杩芥函"
+                         @click="goTraceability(item)"></up-button>
+              <up-button type="info"
+                         size="small"
+                         plain
+                         text="宸ヨ壓璺嚎"
+                         @click="goProcessRoute(item)"></up-button>
+              <up-button type="primary"
+                         size="small"
+                         plain
+                         text="鏉ユ簮"
+                         @click="goSource(item)"></up-button>
+              <up-button type="success"
+                         size="small"
+                         plain
+                         text="棰嗘枡璇︽儏"
+                         @click="goPickingDetail(item)"></up-button>
+            </view>
+          </view>
+        </view>
+      </view>
+      <up-loadmore :status="loadStatus"
+                   v-if="tableData.length >= page.size" />
+    </scroll-view>
+    <view v-else
+          class="no-data">
+      <up-empty mode="data"
+                text="鏆傛棤鐢熶骇璁㈠崟鏁版嵁"></up-empty>
+    </view>
+  </view>
 </template>
 
 <script setup>
-import { ref, reactive, toRefs, getCurrentInstance } from "vue";
-import { onShow } from '@dcloudio/uni-app';
-import dayjs from "dayjs";
-import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
-import PageHeader from "@/components/PageHeader.vue";
+  import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+  import { onShow } from "@dcloudio/uni-app";
+  import dayjs from "dayjs";
+  import {
+    productOrderListPage,
+    getOrderProcessRouteMain,
+  } from "@/api/productionManagement/productionOrder.js";
+  import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
+  import PageHeader from "@/components/PageHeader.vue";
 
-const { proxy } = getCurrentInstance();
+  const { proxy } = getCurrentInstance();
 
-// 鍔犺浇鐘舵��
-const loading = ref(false);
-const loadStatus = ref('loadmore');
-// 鍒楄〃鏁版嵁
-const tableData = ref([]);
+  // 鍔犺浇鐘舵��
+  const loading = ref(false);
+  const loadStatus = ref("loadmore");
+  // 鍒楄〃鏁版嵁
+  const tableData = ref([]);
 
-// 鍒嗛〉閰嶇疆
-const page = reactive({
-	current: 1,
-	size: 10,
-	total: 0,
-});
+  // 鍒嗛〉閰嶇疆
+  const page = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
 
-// 鎼滅储琛ㄥ崟鏁版嵁
-const data = reactive({
-	searchForm: {
-		keyword: "",
-	},
-});
-const { searchForm } = toRefs(data);
+  // 鎼滅储琛ㄥ崟鏁版嵁
+  const data = reactive({
+    searchForm: {
+      keyword: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
 
-// 杩斿洖涓婁竴椤�
-const goBack = () => {
-	uni.navigateBack();
-};
+  // 杩斿洖涓婁竴椤�
+  const goBack = () => {
+    uni.navigateBack();
+  };
 
-// 鏍煎紡鍖栨棩鏈�
-const formatDate = (date) => {
-	return date ? dayjs(date).format('YYYY-MM-DD') : '-';
-};
+  // 鏍煎紡鍖栨棩鏈�
+  const formatDate = date => {
+    return date ? dayjs(date).format("YYYY-MM-DD") : "-";
+  };
 
-// 鑾峰彇鐘舵�佹枃鏈�
-const getStatusText = (status) => {
-	const statusMap = {
-		1: "寰呭紑濮�",
-		2: "杩涜涓�",
-		3: "宸插畬鎴�",
-		4: "宸插彇娑�",
-	};
-	return statusMap[status] || "鏈煡";
-};
+  // 鑾峰彇鐘舵�佹枃鏈�
+  const getStatusText = status => {
+    const statusMap = {
+      1: "寰呭紑濮�",
+      2: "杩涜涓�",
+      3: "宸插畬鎴�",
+      4: "宸插彇娑�",
+      5: "宸茬粨鏉�",
+    };
+    return statusMap[status] || "鏈煡";
+  };
 
-// 鑾峰彇鐘舵�佺被鍨�
-const getStatusType = (status) => {
-	const typeMap = {
-		1: "primary",
-		2: "warning",
-		3: "success",
-		4: "danger",
-	};
-	return typeMap[status] || "info";
-};
+  // 鑾峰彇鐘舵�佺被鍨�
+  const getStatusType = status => {
+    const typeMap = {
+      1: "primary",
+      2: "warning",
+      3: "success",
+      4: "info",
+      5: "error",
+    };
+    return typeMap[status] || "info";
+  };
 
-// 瀹屾垚杩涘害鐧惧垎姣�
-const toProgressPercentage = (val) => {
-	const n = Number(val);
-	if (!Number.isFinite(n)) return 0;
-	if (n <= 0) return 0;
-	if (n >= 100) return 100;
-	return Math.round(n);
-};
+  // 瀹屾垚杩涘害鐧惧垎姣�
+  const toProgressPercentage = val => {
+    const n = Number(val);
+    if (!Number.isFinite(n)) return 0;
+    if (n <= 0) return 0;
+    if (n >= 100) return 100;
+    return Math.round(n);
+  };
 
-// 杩涘害鏉¢鑹�
-const progressColor = (percentage) => {
-	const p = toProgressPercentage(percentage);
-	if (p < 30) return "#f56c6c";
-	if (p < 50) return "#e6a23c";
-	if (p < 80) return "#409eff";
-	return "#67c23a";
-};
+  // 杩涘害鏉¢鑹�
+  const progressColor = percentage => {
+    const p = toProgressPercentage(percentage);
+    if (p < 30) return "#f56c6c";
+    if (p < 50) return "#e6a23c";
+    if (p < 80) return "#409eff";
+    return "#67c23a";
+  };
 
-// 鏌ヨ鍒楄〃
-const handleQuery = () => {
-	page.current = 1;
-	tableData.value = [];
-	getList();
-};
+  // 鏌ヨ鍒楄〃
+  const handleQuery = () => {
+    page.current = 1;
+    tableData.value = [];
+    getList();
+  };
 
-// 鍔犺浇鏇村
-const loadMore = () => {
-	if (loadStatus.value === 'nomore' || loading.value) return;
-	page.current++;
-	getList();
-};
+  // 鍔犺浇鏇村
+  const loadMore = () => {
+    if (loadStatus.value === "nomore" || loading.value) return;
+    page.current++;
+    getList();
+  };
 
-// 鑾峰彇鍒楄〃鏁版嵁
-const getList = () => {
-	loading.value = true;
-	loadStatus.value = 'loading';
-	
-	const params = { 
-		current: page.current,
-		size: page.size,
-		npsNo: searchForm.value.keyword,
-		productName: searchForm.value.keyword
-	};
-	
-	productOrderListPage(params).then((res) => {
-		loading.value = false;
-		const records = res.data.records || [];
-		if (page.current === 1) {
-			tableData.value = records;
-		} else {
-			tableData.value = [...tableData.value, ...records];
-		}
-		
-		if (records.length < page.size) {
-			loadStatus.value = 'nomore';
-		} else {
-			loadStatus.value = 'loadmore';
-		}
-		page.total = res.data.total || 0;
-	}).catch(() => {
-		loading.value = false;
-		loadStatus.value = 'loadmore';
-		uni.showToast({
-			title: '鍔犺浇澶辫触',
-			icon: 'error'
-		});
-	});
-};
+  // 鑾峰彇鍒楄〃鏁版嵁
+  const getList = () => {
+    loading.value = true;
+    loadStatus.value = "loading";
 
-// 璺宠浆鏉ユ簮
-const goSource = (item) => {
-	uni.navigateTo({
-		url: `/pages/productionManagement/productionOrder/source?id=${item.id}&productName=${encodeURIComponent(item.productName)}&model=${encodeURIComponent(item.model)}&quantity=${item.quantity}`
-	});
-};
+    const params = {
+      current: page.current,
+      size: page.size,
+      npsNo: searchForm.value.keyword,
+      productName: searchForm.value.keyword,
+    };
 
-// 璺宠浆棰嗘枡璇︽儏
-const goPickingDetail = (item) => {
-	uni.navigateTo({
-		url: `/pages/productionManagement/productionOrder/pickingDetail?id=${item.id}&npsNo=${item.npsNo}`
-	});
-};
+    productOrderListPage(params)
+      .then(async res => {
+        const records = res.data.records || [];
 
-// 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
-onShow(() => {
-	handleQuery();
-});
+        // 涓烘瘡涓鍗曞苟琛屾煡璇㈠伐搴忚繘搴�
+        const processPromises = records.map(async item => {
+          if (item.npsNo) {
+            try {
+              const workOrderRes = await productWorkOrderPage({
+                npsNo: item.npsNo,
+                size: 100,
+              });
+              const workOrders = workOrderRes.data.records || [];
+              const processRouteStatus = workOrders.map(wo => ({
+                name: wo.operationName || "鏈煡宸ュ簭",
+                percentage:
+                  Number(wo.completionStatus) > 100
+                    ? 100
+                    : Number(wo.completionStatus || 0),
+              }));
+              return { ...item, processRouteStatus };
+            } catch (error) {
+              console.error(`鑾峰彇宸ュ崟 ${item.npsNo} 杩涘害澶辫触:`, error);
+              return { ...item, processRouteStatus: [] };
+            }
+          }
+          return { ...item, processRouteStatus: [] };
+        });
+
+        const updatedRecords = await Promise.all(processPromises);
+
+        loading.value = false;
+        if (page.current === 1) {
+          tableData.value = updatedRecords;
+        } else {
+          tableData.value = [...tableData.value, ...updatedRecords];
+        }
+
+        if (updatedRecords.length < page.size) {
+          loadStatus.value = "nomore";
+        } else {
+          loadStatus.value = "loadmore";
+        }
+        page.total = res.data.total || 0;
+      })
+      .catch(() => {
+        loading.value = false;
+        loadStatus.value = "loadmore";
+        uni.showToast({
+          title: "鍔犺浇澶辫触",
+          icon: "error",
+        });
+      });
+  };
+
+  // 璺宠浆宸ヨ壓璺嚎 (BOM)
+  const goProcessRoute = item => {
+    getOrderProcessRouteMain(item.id)
+      .then(res => {
+        const data = res.data || {};
+        if (!data.id) {
+          uni.showToast({ title: "鏈壘鍒板伐鑹鸿矾绾�", icon: "none" });
+          return;
+        }
+        uni.navigateTo({
+          url: `/pages/productionManagement/processRoute/items?id=${
+            data.id
+          }&bomId=${data.orderBomId}&processRouteCode=${
+            data.processRouteCode || ""
+          }&productName=${encodeURIComponent(
+            item.productName || ""
+          )}&model=${encodeURIComponent(item.model || "")}&orderId=${
+            item.id
+          }&type=order`,
+        });
+      })
+      .catch(() => {
+        uni.showToast({ title: "鑾峰彇璺嚎澶辫触", icon: "none" });
+      });
+  };
+
+  // 璺宠浆鏉ユ簮
+  const goSource = item => {
+    uni.navigateTo({
+      url: `/pages/productionManagement/productionOrder/source?id=${
+        item.id
+      }&productName=${encodeURIComponent(
+        item.productName
+      )}&model=${encodeURIComponent(item.model)}&quantity=${item.quantity}`,
+    });
+  };
+
+  // 璺宠浆棰嗘枡璇︽儏
+  const goPickingDetail = item => {
+    uni.navigateTo({
+      url: `/pages/productionManagement/productionOrder/pickingDetail?id=${item.id}&npsNo=${item.npsNo}`,
+    });
+  };
+
+  // 璺宠浆鐢熶骇杩芥函
+  const goTraceability = item => {
+    uni.navigateTo({
+      url: `/pages/productionManagement/productionTraceability/index?npsNo=${item.npsNo}`,
+    });
+  };
+
+  // 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
+  onShow(() => {
+    handleQuery();
+  });
 </script>
 
 <style scoped lang="scss">
-@import '@/styles/sales-common.scss';
+  @import "@/styles/sales-common.scss";
 
-.production-order {
-	min-height: 100vh;
-	background: #f8f9fa;
-	display: flex;
-	flex-direction: column;
-}
+  .production-order {
+    min-height: 100vh;
+    background: #f8f9fa;
+    display: flex;
+    flex-direction: column;
+  }
 
-.list-container {
-	flex: 1;
-	height: 0;
-}
+  .list-container {
+    flex: 1;
+    height: 0;
+  }
 
-.ledger-item {
-	background: #fff;
-	margin: 20rpx;
-	padding: 24rpx;
-	border-radius: 16rpx;
-	box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
-	
-	.item-header {
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		padding-bottom: 12rpx;
-		
-		.item-left {
-			display: flex;
-			align-items: center;
-			
-			.document-icon {
-				width: 44rpx;
-				height: 44rpx;
-				background: #3c9cff;
-				border-radius: 10rpx;
-				display: flex;
-				justify-content: center;
-				align-items: center;
-				margin-right: 20rpx;
-			}
-			
-			.item-id {
-				font-size: 30rpx;
-				font-weight: bold;
-				color: #333;
-			}
-		}
-	}
-	
-	.item-details {
-		padding: 16rpx 0;
-		
-		.detail-row {
-			display: flex;
-			justify-content: space-between;
-			align-items: flex-start;
-			margin-bottom: 16rpx;
-			
-			.detail-label {
-				font-size: 26rpx;
-				color: #999;
-				min-width: 140rpx;
-			}
-			
-			.detail-value {
-				font-size: 26rpx;
-				color: #333;
-				text-align: right;
-				
-				&.font-bold {
-					font-weight: bold;
-				}
-			}
-			
-			.progress-box {
-				flex: 1;
-				margin-left: 40rpx;
-				
-				.progress-text {
-					font-size: 22rpx;
-					color: #999;
-					margin-top: 4rpx;
-					display: block;
-					text-align: right;
-				}
-			}
-		}
-	}
-	
-	.item-footer {
-		padding-top: 20rpx;
-		border-top: 1rpx solid #f0f0f0;
-		display: flex;
-		justify-content: flex-end;
-		
-		.action-btns {
-			display: flex;
-			gap: 20rpx;
-		}
-	}
-}
+  .ledger-item {
+    background: #fff;
+    margin: 20rpx;
+    padding: 24rpx;
+    border-radius: 16rpx;
+    box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
 
-.no-data {
-	padding-top: 200rpx;
-}
+    .item-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-bottom: 12rpx;
+
+      .item-left {
+        display: flex;
+        align-items: center;
+
+        .document-icon {
+          width: 44rpx;
+          height: 44rpx;
+          background: #3c9cff;
+          border-radius: 10rpx;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          margin-right: 20rpx;
+        }
+
+        .item-id {
+          font-size: 30rpx;
+          font-weight: bold;
+          color: #333;
+        }
+      }
+    }
+
+    .item-details {
+      padding: 16rpx 0;
+
+      .detail-row {
+        display: flex;
+        justify-content: space-between;
+        align-items: flex-start;
+        margin-bottom: 16rpx;
+
+        .detail-label {
+          font-size: 26rpx;
+          color: #999;
+          min-width: 140rpx;
+        }
+
+        .detail-value {
+          font-size: 26rpx;
+          color: #333;
+          text-align: right;
+
+          &.font-bold {
+            font-weight: bold;
+          }
+        }
+
+        .progress-box {
+          flex: 1;
+          margin-left: 40rpx;
+
+          .progress-text {
+            font-size: 22rpx;
+            color: #999;
+            margin-top: 4rpx;
+            display: block;
+            text-align: right;
+          }
+        }
+
+        &.process-row {
+          flex-direction: column;
+          margin: 20rpx 0;
+
+          .process-scroll {
+            width: 100%;
+            margin-top: 16rpx;
+
+            .process-container {
+              display: flex;
+              align-items: flex-start;
+              padding: 10rpx 0;
+              min-height: 120rpx;
+
+              .process-item {
+                display: flex;
+                align-items: center;
+
+                .process-node {
+                  display: flex;
+                  flex-direction: column;
+                  align-items: center;
+                  width: 100rpx;
+
+                  .node-circle {
+                    width: 60rpx;
+                    height: 60rpx;
+                    border-radius: 50%;
+                    border: 2rpx solid #3c9cff;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    background: #fff;
+                    margin-bottom: 8rpx;
+
+                    .node-percentage {
+                      font-size: 18rpx;
+                      color: #3c9cff;
+                      font-weight: bold;
+                    }
+
+                    &.is-complete {
+                      border-color: #52c41a;
+                      background: #f6ffed;
+
+                      .node-percentage {
+                        color: #52c41a;
+                      }
+                    }
+                  }
+
+                  .node-name {
+                    font-size: 20rpx;
+                    color: #666;
+                    text-align: center;
+                    width: 120rpx;
+                    white-space: nowrap;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                  }
+                }
+
+                .node-line {
+                  width: 40rpx;
+                  height: 2rpx;
+                  background: #e8e8e8;
+                  margin: -30rpx 0 0 0;
+                }
+              }
+
+              .no-process {
+                font-size: 24rpx;
+                color: #ccc;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .item-footer {
+      padding-top: 20rpx;
+      border-top: 1rpx solid #f0f0f0;
+      display: flex;
+      justify-content: flex-end;
+
+      .action-btns {
+        display: flex;
+        gap: 20rpx;
+      }
+    }
+  }
+
+  .no-data {
+    padding-top: 200rpx;
+  }
 </style>

--
Gitblit v1.9.3