Browse Source

出库单列表

wuhb 1 month ago
parent
commit
22b949307c
4 changed files with 956 additions and 16 deletions
  1. 71 0
      api/out/index.uts
  2. 7 0
      pages.json
  3. 405 11
      pages/out/detail.uvue
  4. 473 5
      pages/out/index.uvue

+ 71 - 0
api/out/index.uts

@@ -0,0 +1,71 @@
+/**
+ * 出库单接口
+ */
+import { request } from '../../utils/request'
+
+/**
+ * 获取我的领用出库单列表
+ * @param pageNum 页码
+ * @param pageSize 每页数量
+ * @param keyword 关键字
+ * @param receiverUserId 领用人ID
+ * @returns 
+ */
+export const getProductSalseList = (pageNum: number, pageSize: number, keyword: string, receiverUserId: string): Promise<any> => {
+	let url = `/mes/wm/productsalse/myList?pageNum=${pageNum}&pageSize=${pageSize}&receiverUserId=${receiverUserId}`
+	if (keyword != null && keyword.length > 0) {
+		url += `&salseCode=${encodeURIComponent(keyword)}`
+	}
+	return request({
+		url: url,
+		method: 'GET'
+	})
+}
+
+/**
+ * 获取出库单详情
+ * @param salseId 出库单ID
+ * @returns 
+ */
+export const getProductSalseById = (salseId: string): Promise<any> => {
+	return request({
+		url: `/mes/wm/productsalse/${salseId}`,
+		method: 'GET'
+	})
+}
+
+/**
+ * 签收取货
+ * @param salseId 出库单ID
+ * @returns 
+ */
+export const signReceive = (salseId: string): Promise<any> => {
+	return request({
+		url: `/mes/wm/productsalse/signReceive/${salseId}`,
+		method: 'PUT'
+	})
+}
+
+/**
+ * 签收(明细级)
+ * @param lineId 出库单明细ID
+ * @returns 
+ */
+export const signReceiveLine = (lineId: string): Promise<any> => {
+	return request({
+		url: `/mes/wm/productsalse/signReceiveLine/${lineId}`,
+		method: 'PUT'
+	})
+}
+
+/**
+ * 一键签收
+ * @param salseId 出库单ID
+ * @returns 
+ */
+export const signReceiveAll = (salseId: string): Promise<any> => {
+	return request({
+		url: `/mes/wm/productsalse/signReceiveAll/${salseId}`,
+		method: 'PUT'
+	})
+}

+ 7 - 0
pages.json

@@ -167,6 +167,13 @@
 				"navigationBarTitleText": "申请单信息",
 				"navigationStyle": "custom"
 			}
+		},
+		{
+			"path": "pages/out/detail",
+			"style": {
+				"navigationBarTitleText": "出库单详情",
+				"navigationStyle": "custom"
+			}
 		}
 	],
 	"globalStyle": {

+ 405 - 11
pages/out/detail.uvue

@@ -1,17 +1,411 @@
 <template>
-	<!-- #ifdef APP -->
-	<scroll-view style="flex:1">
-	<!-- #endif -->
-		
-	<!-- #ifdef APP -->
-	</scroll-view>
-	<!-- #endif -->
+	<uni-navbar-lite @leftClick="handleBack" :show-right=false title="出库单详情"></uni-navbar-lite>
+    <view class="page-container">
+        <scroll-view class="page-content" :scroll-y="true">
+            <!-- 出库单信息 -->
+            <view class="section">
+                <view class="info-card">
+                    <view class="info-row">
+                        <text class="info-label">出库单号</text>
+                        <text class="info-value">{{ salseCode }}</text>
+                    </view>
+                    <view class="info-row">
+                        <text class="info-label">状态</text>
+                        <text class="info-value status-text" :class="'status-' + status">{{ statusText }}</text>
+                    </view>
+                    <view class="info-row">
+                        <text class="info-label">创建人</text>
+                        <text class="info-value">{{ createBy }}</text>
+                    </view>
+                    <view class="info-row">
+                        <text class="info-label">领用人</text>
+                        <text class="info-value">{{ receiverUser }}</text>
+                    </view>
+                    <view class="info-row">
+                        <text class="info-label">创建时间</text>
+                        <text class="info-value">{{ createTime }}</text>
+                    </view>
+                </view>
+            </view>
+
+            <!-- 物料明细 -->
+            <view class="section">
+                <view class="section-header">
+                    <view class="section-indicator"></view>
+                    <text class="section-title">物料明细</text>
+                </view>
+                <view class="material-list">
+                    <view v-if="lineList.length === 0" class="empty-tip">
+                        <text class="empty-tip-text">暂无物料</text>
+                    </view>
+                    <view 
+                        v-else
+                        v-for="(item, index) in lineList" 
+                        :key="index" 
+                        class="material-item"
+                    >
+                        <view class="material-header">
+                            <text class="material-name">{{ getItemName(item) }}</text>
+                            <text class="material-status" :class="getLineStatus(item) === 'Y' ? 'status-received' : 'status-pending'">
+                                {{ getLineStatusText(item) }}
+                            </text>
+                        </view>
+                        <view class="material-spec" v-if="getSpecification(item)">
+                            <text class="spec-label">规格:</text>
+                            <text class="spec-value">{{ getSpecification(item) }}</text>
+                        </view>
+                        <view class="material-footer">
+                            <view class="quantity-info">
+                                <text class="quantity-label">数量:</text>
+                                <text class="quantity-value">{{ getQuantity(item) }} {{ getMeasureName(item) }}</text>
+                            </view>
+                            <view v-if="status == 'FINISHED' && getLineStatus(item) != 'Y'" class="receive-btn-wrap">
+                                <button class="receive-btn" @click="handleSignReceive(item)">签收</button>
+                            </view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </scroll-view>
+    </view>
 </template>
 
-<script setup>
-		
+<script setup lang="uts">
+    import { ref, computed } from 'vue'
+    import { getProductSalseById, signReceiveLine } from '../../api/out/index'
+    import { getUserInfo } from '../../utils/storage'
+
+    const salseId = ref<string>("")
+    const salseCode = ref<string>("")
+    const status = ref<string>("")
+    const createBy = ref<string>("")
+    const receiverUser = ref<string>("")
+    const createTime = ref<string>("")
+    const lineList = ref<UTSJSONObject[]>([])
+    
+    let currentUserId: string = ''
+
+    const statusText = computed((): string => {
+        const s = status.value
+        switch (s) {
+            case 'PREPARE': return '待确认'
+            case 'CONFIRMED': return '已确认'
+            case 'EXECUTING': return '执行中'
+            case 'FINISHED': return '已完成'
+            case 'CANCEL': return '已取消'
+            default: return s
+        }
+    })
+
+    const getItemName = (item: UTSJSONObject): string => {
+        if (item == null) return ''
+        const val = item['itemName']
+        return val != null ? val.toString() : ''
+    }
+
+    const getSpecification = (item: UTSJSONObject): string => {
+        if (item == null) return ''
+        const val = item['specification']
+        return val != null ? val.toString() : ''
+    }
+
+    const getQuantity = (item: UTSJSONObject): string => {
+        if (item == null) return '0'
+        const val = item['quantitySalse']
+        return val != null ? val.toString() : '0'
+    }
+
+    const getMeasureName = (item: UTSJSONObject): string => {
+        if (item == null) return ''
+        const val = item['measureName']
+        return val != null ? val.toString() : ''
+    }
+
+    const getLineStatus = (item: UTSJSONObject): string => {
+        if (item == null) return ''
+        const val = item['receiverStatus']
+        return val != null ? val.toString() : ''
+    }
+
+    const getLineStatusText = (item: UTSJSONObject): string => {
+        const s = getLineStatus(item)
+        if (s == 'Y') return '已签收'
+        if (s == 'N') return '待签收'
+        return s
+    }
+
+    const loadDetail = (): void => {
+        if (salseId.value.length === 0) return
+        getProductSalseById(salseId.value).then((res: any) => {
+            const resData = res as UTSJSONObject
+			const data = resData["data"] as UTSJSONObject
+            salseCode.value = data['salseCode'] != null ? data['salseCode'].toString() : ''
+            status.value = data['status'] != null ? data['status'].toString() : ''
+            createBy.value = data['createNickName'] != null ? data['createNickName'].toString() : ''
+            receiverUser.value = data['receiverUser'] != null ? data['receiverUser'].toString() : ''
+            createTime.value = data['createTime'] != null ? data['createTime'].toString() : ''
+            
+            const lines = data['wmProductSalseLineList']
+            if (lines != null) {
+                const allLines = lines as UTSJSONObject[]
+                // 只显示当前用户的明细
+                lineList.value = allLines.filter((line: UTSJSONObject) => {
+                    const receiverUserId = line['receiverUserId']
+                    return receiverUserId != null && receiverUserId.toString() === currentUserId
+                })
+            } else {
+                lineList.value = []
+            }
+        }).catch((e) => {
+            const error = e as UTSError
+            const errMsg = error?.message
+            uni.showToast({ title: errMsg.toString(), icon: 'none' })
+        })
+    }
+
+    const handleSignReceive = (item: UTSJSONObject): void => {
+        const lineId = item['lineId']
+        if (lineId == null) {
+            uni.showToast({ title: '明细ID不存在', icon: 'none' })
+            return
+        }
+        uni.showModal({
+            title: '提示',
+            content: '确认签收该物料?',
+            success: (res) => {
+                if (res.confirm) {
+                    signReceiveLine(lineId.toString()).then((res: any) => {
+                        uni.showToast({ title: '签收成功', icon: 'success' })
+                        loadDetail()
+                    }).catch((e) => {
+                        const error = e as UTSError
+                        const errMsg = error?.message
+                        uni.showToast({ title: errMsg.toString(), icon: 'none' })
+                    })
+                }
+            }
+        })
+    }
+
+    const handleBack = (): void => {
+        uni.navigateBack()
+    }
+
+    onLoad((options: any) => {
+		const params = options as UTSJSONObject
+        if (params != null && params['id'] != null) {
+            salseId.value = params['id'].toString()
+            
+            const userInfo = getUserInfo()
+            if (userInfo != null) {
+                const userId = userInfo['userId']
+                currentUserId = userId != null ? userId.toString() : ''
+            }
+            
+            loadDetail()
+        }
+    })
 </script>
 
-<style>
-		
+<style lang="scss">
+    .page-container {
+        flex: 1;
+        background-color: #e8f0f9;
+    }
+
+    .page-content {
+        flex: 1;
+        padding: 20rpx;
+    }
+
+    .section {
+        margin-bottom: 20rpx;
+        background: #ffffff;
+        border-radius: 16rpx;
+        padding: 20rpx;
+    }
+
+    .section-header {
+        flex-direction: row;
+        align-items: center;
+        margin-bottom: 20rpx;
+    }
+
+    .section-indicator {
+        width: 6rpx;
+        height: 32rpx;
+        background-color: #007aff;
+        border-radius: 3rpx;
+        margin-right: 12rpx;
+    }
+
+    .section-title {
+        font-size: 32rpx;
+        color: #333333;
+        font-weight: bold;
+    }
+
+    .info-card {
+        background-color: #f8f9fa;
+        border-radius: 8rpx;
+        padding: 20rpx;
+    }
+
+    .info-row {
+        flex-direction: row;
+        justify-content: space-between;
+        margin-bottom: 16rpx;
+
+        &:last-child {
+            margin-bottom: 0;
+        }
+    }
+
+    .info-label {
+        font-size: 28rpx;
+        color: #666666;
+    }
+
+    .info-value {
+        font-size: 28rpx;
+        color: #333333;
+    }
+
+    .status-text {
+        &.status-PREPARE {
+            color: #faad14;
+        }
+        &.status-CONFIRMED {
+            color: #1890ff;
+        }
+        &.status-EXECUTING {
+            color: #722ed1;
+        }
+        &.status-FINISHED {
+            color: #52c41a;
+        }
+        &.status-CANCEL {
+            color: #ff4d4f;
+        }
+        &.status-received {
+            color: #52c41a;
+        }
+        &.status-pending {
+            color: #faad14;
+        }
+    }
+
+    .material-list {
+        background-color: #f8f9fa;
+        border-radius: 8rpx;
+        padding: 20rpx;
+    }
+
+    .empty-tip {
+        align-items: center;
+        padding: 40rpx;
+    }
+
+    .empty-tip-text {
+        color: #999999;
+        font-size: 28rpx;
+    }
+
+    .material-item {
+        background-color: #ffffff;
+        border-radius: 8rpx;
+        padding: 24rpx;
+        margin-bottom: 16rpx;
+
+        &:last-child {
+            margin-bottom: 0;
+        }
+    }
+
+    .material-header {
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 12rpx;
+    }
+
+    .material-name {
+        font-size: 30rpx;
+        color: #333333;
+        font-weight: bold;
+        flex: 1;
+    }
+
+    .material-status {
+        font-size: 24rpx;
+        padding: 6rpx 16rpx;
+        border-radius: 6rpx;
+        
+        &.status-received {
+            background-color: #f6ffed;
+            color: #52c41a;
+        }
+        &.status-pending {
+            background-color: #fff7e6;
+            color: #fa8c16;
+        }
+    }
+
+    .material-spec {
+        flex-direction: row;
+        margin-bottom: 12rpx;
+    }
+
+    .spec-label {
+        font-size: 24rpx;
+        color: #999999;
+    }
+
+    .spec-value {
+        font-size: 24rpx;
+        color: #666666;
+    }
+
+    .material-footer {
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+    }
+
+    .quantity-info {
+        flex-direction: row;
+    }
+
+    .quantity-label {
+        font-size: 26rpx;
+        color: #999999;
+    }
+
+    .quantity-value {
+        font-size: 26rpx;
+        color: #007aff;
+        font-weight: bold;
+    }
+
+    .receive-btn-wrap {
+        margin-left: 20rpx;
+    }
+
+    .receive-btn {
+        padding: 12rpx 32rpx;
+        background-color: #007aff;
+        color: #ffffff;
+        font-size: 26rpx;
+        border-radius: 8rpx;
+        border: none;
+    }
+
+    .material-detail {
+        flex-direction: row;
+        justify-content: space-between;
+    }
+
+    .detail-row {
+        flex-direction: row;
+    }
 </style>

+ 473 - 5
pages/out/index.uvue

@@ -1,14 +1,482 @@
 <template>
 	<uni-navbar-lite :show-right=false title="出库单"></uni-navbar-lite>
-	<view>
-		
+	<view class="list-page">
+		<!-- 搜索栏 -->
+		<view class="search-block">
+			<view class="search-bar">
+				<view class="search-box">
+					<image class="search-icon" src="/static/images/workbench/list/1.png" mode="aspectFit"></image>
+					<input class="search-input" type="text" placeholder="请输入出库单号查询" v-model="keyword" @input="handleSearch" />
+					<view v-if="keyword.length > 0" class="clear-btn" @tap="clearKeyword">
+						<text class="clear-btn-text">×</text>
+					</view>
+					<view class="search-btn" @tap="handleSearch">
+						<text class="search-btn-text">搜索</text>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 列表内容 -->
+		<common-list 
+			:dataList="dataList" 
+			:loading="loading" 
+			:refreshing="refreshing" 
+			:hasMore="hasMore" 
+			@refresh="handleRefresh" 
+			@loadMore="loadMore" 
+			@itemClick="handleItemClick"
+		>
+			<template #default="{ item, index }">
+				<view class="list-item">
+					<view class="item-container">
+						<view class="item-header">
+							<text class="item-title">{{ getSalseCode(item) }}</text>
+							<text class="item-status" :class="'status-' + getStatus(item)">{{ getStatusText(item) }}</text>
+						</view>
+						<view class="item-info">
+							<view class="info-row">
+								<view class="info-item">
+									<text class="info-label">创建人</text>
+									<text class="info-value">{{ getCreateBy(item) }}</text>
+								</view>
+								<view class="info-item">
+									<text class="info-label">领用人</text>
+									<text class="info-value">{{ getReceiverUser(item) }}</text>
+								</view>
+							</view>
+							<view class="info-row">
+								<view class="info-item">
+									<text class="info-label">创建时间</text>
+									<text class="info-value">{{ getCreateTime(item) }}</text>
+								</view>
+							</view>
+							<view class="info-row stat-row">
+								<view class="info-item" v-if="getStatus(item) == 'FINISHED'">
+									<text class="info-label stat-label">已签收</text>
+									<text class="info-value success stat-value">{{ getSignedCount(item) }}</text>
+								</view>
+								<view class="info-item" v-if="getStatus(item) == 'FINISHED'">
+									<text class="info-label stat-label">待签收</text>
+									<text class="info-value warning stat-value">{{ getUnsignedCount(item) }}</text>
+								</view>
+								<view class="info-item" v-if="getStatus(item) == 'CONFIRMED'">
+									<text class="info-label stat-label">待出库</text>
+									<text class="info-value warning stat-value">{{ getTotalCount(item) }}</text>
+								</view>
+							</view>
+							<view v-if="item != null && getStatus(item) == 'FINISHED' && getUnsignedCount(item) != '0'" class="sign-all-btn-wrap">
+								<text class="sign-all-btn" @click.stop="handleSignAll(item)">一键签收</text>
+							</view>
+						</view>
+					</view>
+				</view>
+			</template>
+		</common-list>
+		<custom-tabbar :current="3" />
 	</view>
 </template>
 
-<script setup>
+<script setup lang="uts">
+    import { ref, onShow, onMounted } from 'vue'
+    import { getProductSalseList, signReceiveAll } from '../../api/out/index'
+    import { getUserInfo } from '../../utils/storage'
+
+    let searchTimer: number | null = null
+
+    // 列表数据
+    const dataList = ref<any[]>([])
+    const keyword = ref<string>("")
+    const page = ref<number>(1)
+    const pageSize: number = 10
+    const hasMore = ref<boolean>(true)
+    const loading = ref<boolean>(false)
+    const refreshing = ref<boolean>(false)
+    
+    // 当前用户ID
+    let currentUserId: string = ''
+
+    // 获取出库单号
+    const getSalseCode = (item: any | null): string => {
+        if (item == null) return ''
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['salseCode']
+        return val != null ? val.toString() : ''
+    }
+
+    // 获取状态
+    const getStatus = (item: any | null): string => {
+        if (item == null) return ''
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['status']
+        return val != null ? val.toString() : ''
+    }
+
+    // 获取状态文本
+    const getStatusText = (item: any | null): string => {
+        if (item == null) return ''
+        const status = getStatus(item)
+        switch (status) {
+            case 'PREPARE': return '待确认'
+            case 'CONFIRMED': return '待出库'
+            case 'EXECUTING': return '执行中'
+            case 'FINISHED': return '已完成'
+            case 'CANCEL': return '已取消'
+            default: return status
+        }
+    }
+
+    // 获取创建人
+    const getCreateBy = (item: any | null): string => {
+        if (item == null) return ''
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['createNickName']
+        return val != null ? val.toString() : ''
+    }
+
+    // 获取领用人
+    const getReceiverUser = (item: any | null): string => {
+        if (item == null) return ''
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['receiverUser']
+        return val != null ? val.toString() : ''
+    }
+
+    // 获取创建时间
+    const getCreateTime = (item: any | null): string => {
+        if (item == null) return ''
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['createTime']
+        return val != null ? val.toString() : ''
+    }
+
+    // 获取已签收数量
+    const getSignedCount = (item: any | null): string => {
+        if (item == null) return '0'
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['signedCount']
+        return val != null ? val.toString() : '0'
+    }
+
+    // 获取未签收数量
+    const getUnsignedCount = (item: any | null): string => {
+        if (item == null) return '0'
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['unsignedCount']
+        return val != null ? val.toString() : '0'
+    }
+
+    // 获取总数(待出库数量)
+    const getTotalCount = (item: any | null): string => {
+        if (item == null) return '0'
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['totalCount']
+        return val != null ? val.toString() : '0'
+    }
 	
+	// 加载列表数据
+	const loadData = async (isRefresh: boolean): Promise<void> => {
+	    if (loading.value) return
+	
+	    try {
+	        loading.value = true
+	
+	        if (isRefresh) {
+	            page.value = 1
+	        }
+	
+	        const result = await getProductSalseList(page.value, pageSize, keyword.value, currentUserId)
+	        const resultObj = result as UTSJSONObject
+	        const rows = resultObj['rows']
+	        const total = resultObj['total'] as number
+	
+	        if (rows != null) {
+	            const newData = rows as any[]
+	            if (isRefresh) {
+	                dataList.value = newData
+	            } else {
+	                dataList.value = [...dataList.value, ...newData]
+	            }
+	            hasMore.value = dataList.value.length < total
+	        } else {
+	            if (isRefresh) {
+	                dataList.value = []
+	            }
+	            hasMore.value = false
+	        }
+	
+	    } catch (e) {
+	        console.error('加载失败:', e)
+	    } finally {
+	        loading.value = false
+	        refreshing.value = false
+	    }
+	}
+
+    // 搜索
+    const handleSearch = (): void => {
+		const timer = searchTimer
+		if (timer != null) {
+			clearTimeout(timer)
+		}
+		searchTimer = setTimeout(() => {
+			loadData(true)
+		}, 300)
+    }
+
+    // 清空搜索关键字
+    const clearKeyword = (): void => {
+        keyword.value = ''
+        loadData(true)
+    }
+	
+    // 下拉刷新
+    const handleRefresh = (): void => {
+        refreshing.value = true
+        loadData(true)
+    }
+
+    // 加载更多
+    const loadMore = (): void => {
+        if (!hasMore.value || loading.value) return
+        page.value++
+        loadData(false)
+    }
+
+    // 点击列表项
+    const handleItemClick = (item: any): void => {
+        const jsonItem = item as UTSJSONObject
+        const salseId = jsonItem['salseId']
+        uni.navigateTo({
+            url: `/pages/out/detail?id=${salseId}`
+        })
+    }
+
+    // 一键签收
+    const handleSignAll = (item: any | null): void => {
+        if (item == null) return
+        const jsonItem = item as UTSJSONObject
+        const salseId = jsonItem['salseId']
+        if (salseId == null) {
+            uni.showToast({ title: '数据错误', icon: 'none' })
+            return
+        }
+        const idStr = salseId.toString()
+        uni.showModal({
+            title: '提示',
+            content: '确认一键签收所有物料?',
+            success: (res) => {
+                if (res.confirm) {
+                    signReceiveAll(idStr).then(() => {
+                        uni.showToast({ title: '签收成功', icon: 'success' })
+                        loadData(true)
+                    }).catch((e) => {
+						const error = e as UTSError
+                        const errMsg = error?.message ?? '签收失败'
+                        uni.showToast({ title: errMsg, icon: 'none' })
+                    })
+                }
+            }
+        })
+    }
+
+    // 初始化
+    onMounted(() => {
+        const userInfo = getUserInfo()
+        if (userInfo != null) {
+            const userId = userInfo['userId']
+            currentUserId = userId != null ? userId.toString() : ''
+        }
+        loadData(true)
+    })
+
+    // 页面显示时刷新列表
+    onShow(() => {
+        loadData(true)
+    })
 </script>
 
-<style>
+<style lang="scss">
+	.page-container {
+	    flex: 1;
+	    background-color: #f5f8fe;
+	    padding-top: env(safe-area-inset-top);
+		background-color: #e8f0f9;
+		padding: 20rpx 10rpx 20rpx 10rpx;
+	}
+    .list-page {
+        flex: 1;
+        background-color: #e8f0f9;
+    }
+
+    .search-block {
+        background-color: #ffffff;
+		border-radius: 15rpx;
+    }
+
+    .search-bar {
+        padding: 20rpx 30rpx;
+    }
+
+    .search-box {
+        flex-direction: row;
+        align-items: center;
+        height: 72rpx;
+        padding: 0 24rpx;
+        background-color: #f5f5f5;
+        border-radius: 36rpx;
+
+        .search-icon {
+            width: 32rpx;
+            height: 32rpx;
+            margin-right: 12rpx;
+        }
+
+        .search-input {
+            flex: 1;
+            font-size: 28rpx;
+            color: #333333;
+        }
+
+        .search-btn {
+            padding: 10rpx 20rpx;
+            background-color: #007aff;
+            border-radius: 8rpx;
+            margin-left: 10rpx;
+        }
+
+        .search-btn-text {
+            color: #ffffff;
+            font-size: 24rpx;
+        }
+
+        .clear-btn {
+            width: 36rpx;
+            height: 36rpx;
+            border-radius: 18rpx;
+            background-color: #cccccc;
+            align-items: center;
+            justify-content: center;
+            margin-left: 10rpx;
+        }
+
+        .clear-btn-text {
+            color: #ffffff;
+            font-size: 24rpx;
+            font-weight: bold;
+        }
+    }
+
+    .list-item {
+        margin: 8rpx 10rpx;
+        background-color: #ffffff;
+        border-radius: 12rpx;
+    }
+
+    .item-container {
+        padding: 20rpx;
+    }
+
+    .item-header {
+        flex-direction: row;
+        align-items: center;
+        justify-content: space-between;
+        margin-bottom: 12rpx;
+
+        .item-title {
+            font-size: 28rpx;
+            color: #333333;
+            font-weight: bold;
+        }
+
+        .item-status {
+            font-size: 24rpx;
+            padding: 8rpx 16rpx;
+            border-radius: 8rpx;
+            
+            &.status-PREPARE {
+                background-color: #fff7e6;
+                color: #fa8c16;
+            }
+            &.status-CONFIRMED {
+                background-color: #e6f7ff;
+                color: #1890ff;
+            }
+            &.status-EXECUTING {
+                background-color: #f9f0ff;
+                color: #722ed1;
+            }
+            &.status-FINISHED {
+                background-color: #f6ffed;
+                color: #52c41a;
+            }
+            &.status-CANCEL {
+                background-color: #fff1f0;
+                color: #ff4d4f;
+            }
+        }
+    }
+
+    .item-info {
+        .info-row {
+            flex-direction: row;
+            margin-bottom: 5rpx;
+            
+            &:last-child {
+                margin-bottom: 0;
+            }
+        }
+
+        .stat-row {
+            padding: 6rpx 0;
+        }
+
+        .stat-label {
+            font-size: 28rpx;
+            color: #333333;
+            font-weight: 600;
+        }
+
+        .stat-value {
+            font-size: 28rpx;
+            font-weight: 600;
+        }
+
+        .info-item {
+            flex: 1;
+            flex-direction: row;
+        }
+
+        .sign-all-btn-wrap {
+            margin-top: 5rpx;
+            align-items: flex-end;
+        }
+
+        .sign-all-btn {
+            padding: 12rpx 32rpx;
+            background-color: #007aff;
+            color: #ffffff;
+            font-size: 24rpx;
+            border-radius: 8rpx;
+            text-align: center;
+        }
+
+        .info-label {
+            font-size: 24rpx;
+            color: #999999;
+            margin-right: 8rpx;
+        }
 
-</style>
+        .info-value {
+            font-size: 24rpx;
+            color: #333333;
+            
+            &.success {
+                color: #52c41a;
+            }
+            &.warning {
+                color: #fa8c16;
+            }
+        }
+    }
+</style>