Browse Source

消息列表

wuhb 3 months ago
parent
commit
e70ec957df
5 changed files with 306 additions and 169 deletions
  1. 71 0
      api/message/index.uts
  2. 1 1
      manifest.json
  3. 1 1
      pages/index/index.uvue
  4. 232 166
      pages/message/index.uvue
  5. 1 1
      utils/request.uts

+ 71 - 0
api/message/index.uts

@@ -0,0 +1,71 @@
+/**
+ * 消息接口
+ */
+import { request } from '../../utils/request'
+
+/**
+ * 获取消息列表
+ * @param pageNum 页码
+ * @param pageSize 每页数量
+ * @param keyword 关键字
+ * @param status 状态 UNREAD未读,READ已读
+ * @returns 
+ */
+export const getMessageList = (pageNum: number, pageSize: number, keyword: string, status: string): Promise<any> => {
+	let url = `/system/message/unprocessedList?pageNum=${pageNum}&pageSize=${pageSize}`
+	if (keyword != null && keyword.length > 0) {
+		url += `&messageContent=${encodeURIComponent(keyword)}`
+	}
+	if (status != null && status.length > 0) {
+		url += `&status=${status}`
+	}
+	return request({
+		url: url,
+		method: 'GET'
+	})
+}
+
+/**
+ * 获取未处理消息列表
+ * @param pageNum 页码
+ * @param pageSize 每页数量
+ * @param keyword 关键字
+ * @returns 
+ */
+export const getUnprocessedMessageList = (pageNum: number, pageSize: number, keyword: string): Promise<any> => {
+	let url = `/system/message/unprocessedList?pageNum=${pageNum}&pageSize=${pageSize}`
+	if (keyword != null && keyword.length > 0) {
+		url += `&messageTitle=${encodeURIComponent(keyword)}`
+	}
+	return request({
+		url: url,
+		method: 'GET'
+	})
+}
+
+/**
+ * 标记消息为已读
+ * @param messageId 消息ID
+ * @returns 
+ */
+export const markMessageAsRead = (messageId: string): Promise<any> => {
+	return request({
+		url: `/system/message/read`,
+		method: 'PUT',
+		data: {
+			messageId: messageId
+		}
+	})
+}
+
+/**
+ * 删除消息
+ * @param messageId 消息ID
+ * @returns 
+ */
+export const deleteMessage = (messageId: string): Promise<any> => {
+	return request({
+		url: `/system/message/${messageId}`,
+		method: 'DELETE'
+	})
+}

+ 1 - 1
manifest.json

@@ -1,7 +1,7 @@
 {
 	"name": "EMCS",
 	"appid": "__UNI__5577A7F",
-	"description": "库存管理系统",
+	"description": "进销存管理",
 	"versionName": "1.0.1",
 	"versionCode": "1",
 	"uni-app-x": {},

+ 1 - 1
pages/index/index.uvue

@@ -105,7 +105,7 @@
         },
         {
             id: 3,
-            title: '物料清单',
+            title: '系统物料',
             bgImage: '/static/images/workbench/4.png',
             icon: '',
             path: '/pages/item/index',

+ 232 - 166
pages/message/index.uvue

@@ -8,7 +8,38 @@
         <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" />
+                <input class="search-input" type="text" placeholder="请输入关键字查询" v-model="keyword" @input="handleInput" @confirm="handleSearch" />
+                <view v-if="keyword.length > 0" class="clear-btn" @tap="handleClear">
+                    <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">
+            <view 
+                class="status-tab" 
+                :class="{ 'active': currentStatus === '' }" 
+                @tap="handleStatusChange('')"
+            >
+                <text class="status-tab-text" :class="{ 'active-text': currentStatus === '' }">全部</text>
+            </view>
+            <view 
+                class="status-tab" 
+                :class="{ 'active': currentStatus === 'UNREAD' }" 
+                @tap="handleStatusChange('UNREAD')"
+            >
+                <text class="status-tab-text" :class="{ 'active-text': currentStatus === 'UNREAD' }">未读</text>
+            </view>
+            <view 
+                class="status-tab" 
+                :class="{ 'active': currentStatus === 'READ' }" 
+                @tap="handleStatusChange('READ')"
+            >
+                <text class="status-tab-text" :class="{ 'active-text': currentStatus === 'READ' }">已读</text>
             </view>
         </view>
 
@@ -19,39 +50,21 @@
             :refreshing="refreshing" 
             :hasMore="hasMore" 
             @refresh="handleRefresh" 
-            @loadMore="loadMore" 
+            @loadMore="loadMore"
             @itemClick="handleItemClick"
         >
             <template #default="{ item, index }">
-                <view class="list-item">
+                <view class="list-item" :class="{ 'unread': getStatus(item) === 'UNREAD' }">
                     <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>
+                            <text class="item-title">{{ getMessageTitle(item) }}</text>
+                            <text class="item-status" :class="'status-' + getStatus(item)">{{ getStatusText(item) }}</text>
+                        </view>
+                        <view class="item-content">
+                            <text class="content-text">{{ getMessageContent(item) }}</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 class="item-footer">
+                            <text class="create-time">{{ getCreateTime(item) }}</text>
                         </view>
                     </view>
                 </view>
@@ -63,13 +76,13 @@
 </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, onBeforeUnmount, onMounted } from 'vue'
+    import { getMessageList, markMessageAsRead } from '../../api/message/index'
 
     // 列表数据
-    const dataList = ref<ContractorInfo[]>([])
+    const dataList = ref<any[]>([])
     const keyword = ref<string>("")
+    const currentStatus = ref<string>("")
     const page = ref<number>(1)
     const pageSize: number = 10
     const hasMore = ref<boolean>(true)
@@ -77,47 +90,59 @@
     const refreshing = ref<boolean>(false)
     const total = ref<number>(0)
 
-    // 辅助函数:从 any 类型提取属性
-    const getContractorName = (item: any | null): string => {
+    // 获取消息标题
+    const getMessageTitle = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.contractorName
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['messageTitle']
+        return val != null ? val.toString() : ''
     }
 
-    const getCompanyAddress = (item: any | null): string => {
+    // 获取消息内容
+    const getMessageContent = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.companyAddress
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['messageContent']
+        return val != null ? val.toString() : ''
     }
 
-    const getLegalRepresentative = (item: any | null): string => {
+    // 获取消息状态
+    const getStatus = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.legalRepresentative
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['status']
+        return val != null ? val.toString() : ''
     }
 
-    const getQualificationLevel = (item: any | null): string => {
-        if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.qualificationLevel
+    // 获取状态文本
+    const getStatusText = (item: any | null): string => {
+        const status = getStatus(item)
+        switch (status) {
+            case 'UNREAD': return '未读'
+            case 'READ': return '已读'
+            default: return status
+        }
     }
 
-    const getContactPhone = (item: any | null): string => {
+    // 获取创建时间
+    const getCreateTime = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.contactPhone
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['createTime']
+        return val != null ? val.toString() : ''
     }
 
-    const getCreateTime = (item: any | null): string => {
+    // 获取消息ID
+    const getMessageId = (item: any | null): string => {
         if (item == null) return ''
-        const contractorItem = item as ContractorInfo
-        return contractorItem.createTime
+        const jsonItem = item as UTSJSONObject
+        const val = jsonItem['messageId']
+        return val != null ? val.toString() : ''
     }
 
     // 加载列表数据
-    const loadData = async (isRefresh: boolean | null): Promise<void> => {
+    const loadData = async (isRefresh: boolean): Promise<void> => {
         if (loading.value) {
-            // 如果正在加载,直接重置刷新状态
             refreshing.value = false
             return
         }
@@ -125,51 +150,21 @@
         try {
             loading.value = true
 
-            // 处理默认值
-            const shouldRefresh = isRefresh != null ? isRefresh : false
-
-            if (shouldRefresh) {
+            if (isRefresh) {
                 page.value = 1
             }
 
             // 调用 API
-            const searchKeyword = keyword.value.length > 0 ? keyword.value : null
-            const result = await getContractorList(page.value, pageSize, searchKeyword)
+            const result = await getMessageList(page.value, pageSize, keyword.value, currentStatus.value)
 
             // 提取响应数据
             const resultObj = result as UTSJSONObject
-            const success = resultObj['success'] as boolean
-            const responseData = resultObj['data'] as any[]
+            const rows = resultObj['rows']
             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
-                    }
-                    newData.push(contractorItem)
-                }
-
-                if (shouldRefresh) {
+            if (rows != null) {
+                const newData = rows as any[]
+                if (isRefresh) {
                     dataList.value = newData
                 } else {
                     dataList.value = [...dataList.value, ...newData]
@@ -178,11 +173,10 @@
                 total.value = responseTotal
                 hasMore.value = dataList.value.length < responseTotal
             } else {
-                const msg = resultObj['msg'] as string | null
-                uni.showToast({
-                    title: msg ?? '加载失败',
-                    icon: 'none'
-                })
+                if (isRefresh) {
+                    dataList.value = []
+                }
+                hasMore.value = false
             }
 
         } catch (e: any) {
@@ -192,29 +186,16 @@
             })
         } finally {
             loading.value = false
-            // #ifdef WEB
-            // Web 平台立即重置
-            refreshing.value = false
-            // #endif
-            // #ifndef WEB
-            // App 平台延迟重置刷新状态,确保 UI 更新
             setTimeout(() => {
                 refreshing.value = false
             }, 100)
-            // #endif
         }
-        
-        // #ifdef WEB
-        // Web 平台额外确保重置
-        refreshing.value = false
-        // #endif
     }
 
     // 下拉刷新
     const handleRefresh = async (): Promise<void> => {
         refreshing.value = true
-        // await loadData(true as boolean | null)
-        
+        await loadData(true)
     }
 
     // 加载更多
@@ -223,22 +204,52 @@
             return
         }
         page.value++
-        // loadData(false as boolean | null)
+        loadData(false)
     }
 
     // 搜索
     const handleSearch = (): void => {
         page.value = 1
-        // loadData(true as boolean | null)
+        loadData(true)
+    }
+
+    // 输入时搜索
+    const handleInput = (): void => {
+        page.value = 1
+        loadData(true)
+    }
+
+    // 清除搜索
+    const handleClear = (): void => {
+        keyword.value = ''
+        page.value = 1
+        loadData(true)
+    }
+
+    // 切换状态标签
+    const handleStatusChange = (status: string): void => {
+        currentStatus.value = status
+        page.value = 1
+        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 messageId = getMessageId(item)
+        if (messageId.length == 0) return
+        
+        // 标记为已读
+        if (getStatus(item) == 'UNREAD') {
+            markMessageAsRead(messageId).then(() => {
+                // 重新加载数据
+                loadData(true)
+            }).catch((e) => {
+                console.error('标记已读失败:', e)
+            })
+        }
+        
+        // 可以在这里添加跳转到消息详情页的逻辑
     }
 
     // 组件卸载前清理
@@ -248,7 +259,9 @@
     })
 
     // 初始化
-    // loadData(true as boolean | null)
+    onMounted(() => {
+        loadData(true)
+    })
 </script>
 
 <style lang="scss">
@@ -295,80 +308,133 @@
             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 {
+        display: flex;
+        flex-direction: row;
+        padding: 0rpx 30rpx 20rpx;
+        background-color: #d7eafe;
+    }
+
+    .status-tab {
+        flex: 1;
+        padding: 16rpx 0;
+        text-align: center;
+        margin-right: 16rpx;
+        border-radius: 8rpx;
+        background-color: #f5f5f5;
+        justify-content: center;
+        align-items: center;
+
+        &: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: 16rpx 30rpx;
         background-color: #ffffff;
-        border-radius: 16rpx;
+        border-radius: 12rpx;
+        box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
+        
+        &.unread {
+            border-left: 4rpx solid #007aff;
+        }
     }
 
     .item-container {
-        padding: 30rpx;
+        padding: 20rpx;
     }
 
     .item-header {
         flex-direction: row;
         align-items: center;
-        margin-bottom: 16rpx;
-
-        .location-icon {
-            width: 32rpx;
-            height: 32rpx;
-            margin-right: 8rpx;
-        }
+        justify-content: space-between;
+        margin-bottom: 12rpx;
 
         .item-title {
             flex: 1;
-            font-size: 32rpx;
+            font-size: 28rpx;
             color: #333333;
             font-weight: bold;
         }
 
-        .detail-link {
-            font-size: 28rpx;
-            color: #999999;
+        .item-status {
+            font-size: 20rpx;
+            padding: 6rpx 12rpx;
+            border-radius: 6rpx;
+            
+            &.status-UNREAD {
+                background-color: #e6f7ff;
+                color: #007aff;
+            }
+            &.status-READ {
+                background-color: #f6ffed;
+                color: #52c41a;
+            }
         }
     }
 
-    .item-address {
-        font-size: 26rpx;
-        color: #999999;
-        margin-bottom: 20rpx;
-        line-height: 40rpx;
+    .item-content {
+        margin-bottom: 12rpx;
+        
+        .content-text {
+            font-size: 24rpx;
+            color: #666666;
+            line-height: 36rpx;
+        }
     }
 
-    .item-info {
-        padding: 20rpx;
-        background-color: #f8f9fa;
-        border-radius: 8rpx;
-
-        .info-row {
-            flex-direction: row;
-            margin-bottom: 16rpx;
-
-            &:last-child {
-                margin-bottom: 0;
-            }
-
-            .info-item {
-                flex: 1;
-                flex-direction: row;
-                align-items: center;
-
-                .info-label {
-                    font-size: 26rpx;
-                    color: #666666;
-                    margin-right: 8rpx;
-                    white-space: nowrap;
-                }
-
-                .info-value {
-                    flex: 1;
-                    font-size: 26rpx;
-                    color: #333333;
-                }
-            }
+    .item-footer {
+        display: flex;
+        justify-content: flex-end;
+        
+        .create-time {
+            font-size: 20rpx;
+            color: #999999;
         }
     }
 </style>

+ 1 - 1
utils/request.uts

@@ -13,7 +13,7 @@ export type RequestConfig = {
 };
 
 // 基础 URL
-const BASE_URL = "http://192.168.2.31:83";
+const BASE_URL = "http://192.168.2.22:83";
 // const BASE_URL = "http://192.168.189.43:83";
 // const BASE_URL = "http://222.243.138.146:8150/prod-api" //测试服务器;
 // const BASE_URL = "http://222.243.138.146:880/prod-api" //正式服务器;