| | |
| | | <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> |