Browse Source

物料清单

wuhb 3 months ago
parent
commit
78c904b926
2 changed files with 338 additions and 274 deletions
  1. 10 32
      pages/index/index.uvue
  2. 328 242
      pages/item/index.uvue

+ 10 - 32
pages/index/index.uvue

@@ -11,8 +11,8 @@
                 <view class="quick-card-content">
                     <text class="quick-card-title">{{ item.title }}</text>
                 </view>
-				<view class="management-badge">
-				    <text class="badge-text">0</text>
+				<view class="management-badge" v-if="item.badge > 0">
+				    <text class="badge-text">{{item.badge}}</text>
 				</view>
             </view>
         </view>
@@ -25,33 +25,7 @@
 				</view>
 			</view>
 			<template v-if="applyListData.length > 0">
-				<common-list
-					:dataList="applyListData.slice(0, 4)"
-					:loading="applyLoading"
-					:hasMore="false"
-					:refresherEnabled="false"
-					:itemKey="'id'"
-					@itemClick="handleApplyItemClick"
-				>
-					<template #default="{ item }">
-						<view class="list-item">
-							<view class="item-container">
-								<view class="item-header">
-									<text class="item-title">标题</text>
-								</view>
-								<view class="info-row">
-									<text class="text-gray">申请单编码:</text>
-								</view>
-								<view class="info-row">
-									<text class="overdue-title"></text>
-								</view>
-							</view>
-						</view>
-					</template>
-					<template #loadMore>
-						<view></view>
-					</template>
-				</common-list>
+				<view></view>
 			</template>
 			<template v-else>
 			  <view class="empty-tips">
@@ -74,6 +48,7 @@
         bgImage: string
         icon: string
         path: string
+		badge:number
     }
 	const applyListData = ref<any[]>([])
 	const applyLoading = ref<boolean>(false)
@@ -85,21 +60,24 @@
             title: '申请单',
             bgImage: '/static/images/workbench/2.png',
             icon: '',
-            path: '/pages/apply/index'
+            path: '/pages/apply/index',
+			badge: 0
         },
         {
             id: 2,
             title: '出库单',
             bgImage: '/static/images/workbench/3.png',
             icon: '',
-            path: '/pages/apply/outStockList'
+            path: '/pages/apply/outStockList',
+			badge: 2
         },
         {
             id: 3,
             title: '物料申请',
             bgImage: '/static/images/workbench/4.png',
             icon: '',
-            path: '/pages/apply/applyNew'
+            path: '/pages/apply/applyNew',
+			badge: 0
         }
     ])
 	

+ 328 - 242
pages/item/index.uvue

@@ -1,255 +1,278 @@
 <template>
-	<!-- <uni-navbar-lite :showLeft=false title="消息"></uni-navbar-lite> -->
 	<view class="page-container">
 		<view>
 			<text class="page-header"> 物料 </text>
 		</view>
 		<view class="list-page">
-			<!-- 搜索栏 -->
-			<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" @confirm="handleSearch" />
-				</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">
-								<image class="location-icon" src="/static/images/workbench/list/2.png" mode="aspectFit"></image>
-								<text class="item-title">{{ getContractorName(item) }}</text>
-								<text class="detail-link">详情 ›</text>
-							</view>
-							<text class="item-address">{{ getCompanyAddress(item) }}</text>
-							<view class="item-info">
-								<view class="info-row">
-									<view class="info-item">
-										<text class="info-label">法定代表人</text>
-										<text class="info-value">{{ getLegalRepresentative(item) }}</text>
-									</view>
-									<view class="info-item">
-										<text class="info-label">资质等级</text>
-										<text class="info-value">{{ getQualificationLevel(item) }}</text>
-									</view>
-								</view>
-								<view class="info-row">
-									<view class="info-item">
-										<text class="info-label">联系电话</text>
-										<text class="info-value">{{ getContactPhone(item) }}</text>
-									</view>
-									<view class="info-item">
-										<text class="info-label">创建时间</text>
-										<text class="info-value">{{ getCreateTime(item) }}</text>
-									</view>
-								</view>
-							</view>
-						</view>
-					</view>
-				</template>
-			</common-list>
-			<custom-tabbar :current="2" />
+		    <!-- 搜索栏和类型标签 -->
+		    <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" @confirm="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 class="status-tabs">
+		            <scroll-view class="scroll-view_H" direction="horizontal" :show-scrollbar="false">
+		                <view 
+		                    v-for="(cat, idx) in categories" 
+		                    :key="idx" 
+		                    class="status-tab" 
+		                    :class="{ 'active': currentStatus === cat.id }" 
+		                    @click="switchStatus(cat.id)"
+		                >
+		                    <text class="status-tab-text" :class="{ 'active-text': currentStatus === cat.id }">{{ cat.name }}</text>
+		                </view>
+		            </scroll-view>
+		        </view>
+		    </view>
+		
+		    <!-- 列表内容 -->
+		    <common-list 
+		        :dataList="dataList" 
+		        :loading="loading" 
+		        :refreshing="refreshing" 
+		        :hasMore="hasMore" 
+		        @refresh="handleRefresh" 
+		        @loadMore="loadMore"
+		    >
+		        <template #default="{ item, index }">
+		            <view class="list-item">
+		                <view class="item-container">
+		                    <view class="item-header">
+		                        <view class="item-main-info">
+		                            <view class="item-name-row">
+		                                <text class="item-name">{{ getItemName(item) }}</text>
+		                                <text class="item-measure">{{ getMeasureName(item) }}</text>
+		                            </view>
+		                            <view class="item-details">
+		                                <text class="item-code">编码: {{ getItemCode(item) }}</text>
+		                                <text class="item-warehouse">仓库: {{ getWarehouseName(item) }}</text>
+		                            </view>
+		                        </view>
+		                        <text class="item-type">{{ getItemTypeName(item) }}</text>
+		                    </view>
+		                </view>
+		            </view>
+		        </template>
+		    </common-list>
+		    <custom-tabbar :current="2" />
 		</view>
 	</view>
 </template>
 
 <script setup lang="uts">
-    import { ref, computed, onBeforeUnmount } from 'vue'
-    import type { ContractorInfo, ContractorListResponse } from '../../types/workbench'
-    import { getContractorList } from '../../api/workbench/list'
+    import { ref, onShow, onMounted } from 'vue'
+    import { getItemList, getItemTypeListByParentId } from '../../api/apply/index'
+
+    type CategoryItem = { id: string; name: string; code: string }
 
     // 列表数据
-    const dataList = ref<ContractorInfo[]>([])
+    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)
-    const total = ref<number>(0)
 
-    // 辅助函数:从 any 类型提取属性
-    const getContractorName = (item: any | null): string => {
-        if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.contractorName
-    }
+    // 分类数据
+    const categories = ref<CategoryItem[]>([])
+    const currentStatus = ref<string>('')
+    const isSearching = ref<boolean>(false)
 
-    const getCompanyAddress = (item: any | null): string => {
+    // 获取物料名称
+    const getItemName = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.companyAddress
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['itemName']
+        return val != null ? val.toString() : ''
     }
 
-    const getLegalRepresentative = (item: any | null): string => {
+    // 获取物料类型
+    const getItemTypeName = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.legalRepresentative
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['itemTypeName']
+        return val != null ? val.toString() : ''
     }
 
-    const getQualificationLevel = (item: any | null): string => {
+    // 获取计量单位
+    const getMeasureName = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.qualificationLevel
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['measureName']
+        return val != null ? val.toString() : ''
     }
 
-    const getContactPhone = (item: any | null): string => {
+    // 获取物料编码
+    const getItemCode = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.contactPhone
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['itemCode']
+        return val != null ? val.toString() : ''
     }
 
-    const getCreateTime = (item: any | null): string => {
+    // 获取仓库名称
+    const getWarehouseName = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.createTime
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['warehouseName']
+        return val != null ? val.toString() : ''
     }
 
-    // 加载列表数据
-    const loadData = async (isRefresh: boolean | null): Promise<void> => {
-        if (loading.value) {
-            // 如果正在加载,直接重置刷新状态
-            refreshing.value = false
-            return
-        }
-
-        try {
-            loading.value = true
-
-            // 处理默认值
-            const shouldRefresh = isRefresh != null ? isRefresh : false
-
-            if (shouldRefresh) {
-                page.value = 1
-            }
+    // 获取当前选中的分类code
+    const getCurrentCategoryCode = (): string => {
+        const currentCat = categories.value.find((c: CategoryItem) => c.id === currentStatus.value)
+        return currentCat != null ? currentCat.code : ''
+    }
+	
+	// 加载列表数据
+	const loadData = async (isRefresh: boolean): Promise<void> => {
+	    if (loading.value) return
+	
+	    try {
+	        loading.value = true
+	
+	        if (isRefresh) {
+	            page.value = 1
+	        }
+	
+	        const searchKeyword = keyword.value != null ? keyword.value : ''
+	        const itemTypeCode = getCurrentCategoryCode()
+	        const result = await getItemList(itemTypeCode, page.value, pageSize, searchKeyword)
+	        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
+	    }
+	}
 
-            // 调用 API
-            const searchKeyword = keyword.value.length > 0 ? keyword.value : null
-            const result = await getContractorList(page.value, pageSize, searchKeyword)
-
-            // 提取响应数据
-            const resultObj = result as UTSJSONObject
-            const success = resultObj['success'] as boolean
-            const responseData = resultObj['data'] as any[]
-            const responseTotal = resultObj['total'] as number
-
-            if (success) {
-                // 将 any[] 转换为 ContractorInfo[]
-                const newData: ContractorInfo[] = []
-                for (let i = 0; i < responseData.length; i++) {
-                    const item = responseData[i] as UTSJSONObject
-                    const contractorItem: ContractorInfo = {
-                        id: item['id'] as string,
-                        createBy: item['createBy'] as string,
-                        createTime: item['createTime'] as string,
-                        updateBy: item['updateBy'] as string | null,
-                        updateTime: item['updateTime'] as string | null,
-                        isDeleted: item['isDeleted'] as number,
-                        remark: item['remark'] as string,
-                        creditCode: item['creditCode'] as string,
-                        contractorName: item['contractorName'] as string,
-                        companyAddress: item['companyAddress'] as string,
-                        legalRepresentative: item['legalRepresentative'] as string,
-                        contactPhone: item['contactPhone'] as string,
-                        qualificationLevel: item['qualificationLevel'] as string,
-                        qualificationCertNo: item['qualificationCertNo'] as string,
-                        businessScope: item['businessScope'] as string,
-                        bankName: item['bankName'] as string,
-                        bankAccount: item['bankAccount'] as string
+    // 加载分类数据
+    const loadCategories = (): void => {
+        getItemTypeListByParentId(200).then((res: any) => {
+            const data = res as UTSJSONObject
+            const list = data['data']
+            if (list != null) {
+                const listData = list as UTSJSONObject[]
+                if (listData.length > 0) {
+                    const arr: CategoryItem[] = []
+                    listData.forEach((item: UTSJSONObject) => {
+                        const idVal = item['itemTypeId']
+                        const nameVal = item['itemTypeName']
+                        const codeVal = item['itemTypeCode']
+                        const cat: CategoryItem = {
+                            id: idVal != null ? idVal.toString() : '',
+                            name: nameVal != null ? nameVal.toString() : '',
+                            code: codeVal != null ? codeVal.toString() : ''
+                        }
+                        arr.push(cat)
+                    })
+                    const allItem: CategoryItem = {id: '', name: '全部', code: ''}
+                    arr.unshift(allItem)
+                    categories.value = arr
+                    // 加载第一个分类的物料
+                    if (arr.length > 0) {
+                        currentStatus.value = arr[0].id
+                        loadData(true)
                     }
-                    newData.push(contractorItem)
-                }
-
-                if (shouldRefresh) {
-                    dataList.value = newData
-                } else {
-                    dataList.value = [...dataList.value, ...newData]
+                    return
                 }
-
-                total.value = responseTotal
-                hasMore.value = dataList.value.length < responseTotal
-            } else {
-                const msg = resultObj['msg'] as string | null
-                uni.showToast({
-                    title: msg ?? '加载失败',
-                    icon: 'none'
-                })
             }
+            const defaultArr: CategoryItem[] = []
+            const defaultItem: CategoryItem = {id: '', name: '全部', code: ''}
+            defaultArr.push(defaultItem)
+            categories.value = defaultArr
+        }).catch(() => {
+            const defaultArr: CategoryItem[] = []
+            const defaultItem: CategoryItem = {id: '', name: '全部', code: ''}
+            defaultArr.push(defaultItem)
+            categories.value = defaultArr
+        })
+    }
 
-        } catch (e: any) {
-            uni.showToast({
-                title: e.message ?? '加载失败',
-                icon: 'none'
-            })
-        } finally {
-            loading.value = false
-            // #ifdef WEB
-            // Web 平台立即重置
-            refreshing.value = false
-            // #endif
-            // #ifndef WEB
-            // App 平台延迟重置刷新状态,确保 UI 更新
-            setTimeout(() => {
-                refreshing.value = false
-            }, 100)
-            // #endif
+    // 切换分类
+    const switchStatus = (status: string): void => {
+        if (loading.value) {
+            return;
+        }
+        if (isSearching.value) {
+            return
         }
-        
-        // #ifdef WEB
-        // Web 平台额外确保重置
-        refreshing.value = false
-        // #endif
+        isSearching.value = true
+        currentStatus.value = status
+        page.value = 1
+        loadData(true)
+        setTimeout(() => {
+            isSearching.value = false
+        }, 100)
     }
-
+	
     // 下拉刷新
-    const handleRefresh = async (): Promise<void> => {
+    const handleRefresh = (): void => {
         refreshing.value = true
-        // await loadData(true as boolean | null)
-        
+        loadData(true)
     }
 
     // 加载更多
     const loadMore = (): void => {
-        if (!hasMore.value || loading.value) {
-            return
-        }
+        if (!hasMore.value || loading.value) return
         page.value++
-        // loadData(false as boolean | null)
+        loadData(false)
     }
 
     // 搜索
     const handleSearch = (): void => {
         page.value = 1
-        // loadData(true as boolean | null)
+        loadData(true)
     }
 
-    // 点击列表项
-    const handleItemClick = (item: any | null, index: number): void => {
-        if (item == null) return
-        const contractorItem = item as ContractorInfo
-        uni.navigateTo({
-            url: `/pages/workbench/detail/index?id=${contractorItem.id}`
-        })
+    // 清空搜索关键字
+    const clearKeyword = (): void => {
+        keyword.value = ''
+        page.value = 1
+        loadData(true)
     }
 
-    // 组件卸载前清理
-    onBeforeUnmount(() => {
-        refreshing.value = false
-        loading.value = false
-    })
+
 
     // 初始化
-    // loadData(true as boolean | null)
+    onMounted(() => {
+        loadCategories()
+    })
+
+    // 页面显示时刷新列表
+    onShow(() => {
+        loadData(true)
+    })
 </script>
 
 <style lang="scss">
@@ -258,7 +281,7 @@
 	    background-color: #f5f8fe;
 	    padding-top: env(safe-area-inset-top);
 		background-color: #e8f0f9;
-		padding: 20rpx;
+		padding: 20rpx 10rpx 20rpx 10rpx;
 	}
 	.page-header{
 		font-size: 32rpx;
@@ -269,13 +292,16 @@
 	}
     .list-page {
         flex: 1;
-        background-color: #fff;
-		border-radius: 16rpx;
+        background-color: #e8f0f9;
+    }
+
+    .search-block {
+        background-color: #ffffff;
+		border-radius: 15rpx;
     }
 
     .search-bar {
         padding: 20rpx 30rpx;
-        // background-color: #d7eafe;
     }
 
     .search-box {
@@ -297,10 +323,76 @@
             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;
+        }
+    }
+
+    .status-tabs {
+        padding: 20rpx 30rpx;
+        background-color: #ffffff;
+        border-bottom: 1rpx solid #eeeeee;
+    }
+
+    .scroll-view_H {
+        width: 100%;
+        flex-direction: row;
+    }
+
+    .status-tab {
+        padding: 16rpx 24rpx;
+        text-align: center;
+        margin-right: 16rpx;
+        border-radius: 8rpx;
+        background-color: #f5f5f5;
+        flex-shrink: 0;
+
+        &:last-child {
+            margin-right: 0;
+        }
+
+        &.active {
+            background-color: #007aff;
+        }
+
+        .status-tab-text {
+            font-size: 26rpx;
+            color: #666666;
+
+            &.active-text {
+                color: #ffffff;
+                font-weight: bold;
+            }
+        }
     }
 
     .list-item {
-        margin: 24rpx 30rpx;
+        margin: 10rpx 10rpx;
         background-color: #ffffff;
         border-radius: 16rpx;
     }
@@ -311,66 +403,60 @@
 
     .item-header {
         flex-direction: row;
-        align-items: center;
-        margin-bottom: 16rpx;
+        align-items: flex-start;
+        justify-content: space-between;
+        margin-bottom: 10rpx;
+        width: 100%;
 
-        .location-icon {
-            width: 32rpx;
-            height: 32rpx;
-            margin-right: 8rpx;
+        .item-main-info {
+            flex: 1;
+            margin-right: 20rpx;
         }
 
-        .item-title {
-            flex: 1;
-            font-size: 32rpx;
+        .item-name-row {
+            flex-direction: row;
+            align-items: center;
+            margin-bottom: 8rpx;
+        }
+
+        .item-name {
+            font-size: 28rpx;
             color: #333333;
             font-weight: bold;
         }
 
-        .detail-link {
-            font-size: 28rpx;
+        .item-measure {
+            font-size: 24rpx;
             color: #999999;
+            margin-left: 10rpx;
         }
-    }
-
-    .item-address {
-        font-size: 26rpx;
-        color: #999999;
-        margin-bottom: 20rpx;
-        line-height: 40rpx;
-    }
-
-    .item-info {
-        padding: 20rpx;
-        background-color: #f8f9fa;
-        border-radius: 8rpx;
 
-        .info-row {
+        .item-details {
             flex-direction: row;
-            margin-bottom: 16rpx;
-
-            &:last-child {
-                margin-bottom: 0;
-            }
+            justify-content: space-between;
+            width: 100%;
+        }
 
-            .info-item {
-                flex: 1;
-                flex-direction: row;
-                align-items: center;
+        .item-code {
+            font-size: 23rpx;
+            color: #797979;
+            flex: 1;
+        }
 
-                .info-label {
-                    font-size: 26rpx;
-                    color: #666666;
-                    margin-right: 8rpx;
-                    white-space: nowrap;
-                }
+        .item-warehouse {
+            font-size: 23rpx;
+            color: #797979;
+            flex: 1;
+            text-align: right;
+        }
 
-                .info-value {
-                    flex: 1;
-                    font-size: 26rpx;
-                    color: #333333;
-                }
-            }
+        .item-type {
+            font-size: 24rpx;
+            color: #007aff;
+            background-color: #e6f7ff;
+            padding: 8rpx 16rpx;
+            border-radius: 8rpx;
+            white-space: nowrap;
         }
     }
 </style>