diff --git a/src/detail/detail.js b/src/detail/detail.js new file mode 100644 index 0000000..a55d8dc --- /dev/null +++ b/src/detail/detail.js @@ -0,0 +1,404 @@ +Page({ + data: { + currentItem: {}, + showMap: false, + mapLongitude: 117.352447, // 默认校区中心经度 + mapLatitude: 39.111039, // 默认校区中心纬度 + markers: [], + showClaimModal: false, + claimPhone: '', + phoneError: false, + apiBaseUrl: 'http://10.11.72.16:8000/api', + itemId: null // 新增:存储物品ID + }, + + onLoad(options) { + if (options.item) { + const item = JSON.parse(decodeURIComponent(options.item)) + console.log('详情页接收到的数据:', item) + + // 存储物品ID + this.setData({ + itemId: item.id || item.number + }) + + // 直接从服务器获取最新数据,确保状态同步 + this.fetchCompleteItemInfo(item.id || item.number) + } else if (options.id) { + // 如果直接传递ID,也支持 + this.setData({ + itemId: options.id + }) + this.fetchCompleteItemInfo(options.id) + } + }, + + onShow() { + // 页面显示时重新获取数据,确保状态最新 + if (this.data.itemId) { + this.fetchCompleteItemInfo(this.data.itemId) + } + }, + + // 从服务器获取完整的物品信息 - 优化版本 + // 在 fetchCompleteItemInfo 方法中,修改状态处理部分 +fetchCompleteItemInfo(itemId) { + const that = this + console.log('🔄 开始获取物品详情,ID:', itemId) + + wx.request({ + url: `${this.data.apiBaseUrl}/items/`, + method: 'GET', + success(res) { + console.log('📡 服务器响应:', res) + + if (res.data.success && res.data.data) { + // 在所有物品中查找匹配的完整信息 + const completeItem = res.data.data.find(item => { + const match = (item.id === itemId) || + (item.number === itemId) || + (item.id && item.id.toString() === itemId.toString()) || + (item.number && item.number.toString() === itemId.toString()) + return match + }) + + if (completeItem) { + console.log('✅ 获取到完整物品信息:', completeItem) + + // 状态值转换:将英文状态转换为中文 + let stateDisplay = completeItem.state || '未领取'; + if (stateDisplay === 'unclaimed') { + stateDisplay = '未领取'; + } else if (stateDisplay === 'claimed') { + stateDisplay = '已领取'; + } + + // 构建完整的物品对象 + const itemData = { + id: completeItem.id || completeItem.number, + name: completeItem.object || completeItem.name, + color: completeItem.color, + feature: completeItem.feature, + brand: completeItem.brand, + location: completeItem.location, + telephone: completeItem.telephone, + icon: that.generateItemIcon(completeItem.object || completeItem.name), + description: `${completeItem.color}的${completeItem.object || completeItem.name},${completeItem.feature},品牌:${completeItem.brand}`, + // 使用转换后的状态值 + state: stateDisplay, + claim_phone: completeItem.claim_phone || '', + number: completeItem.number || `CAUC${(completeItem.id || completeItem.number).toString().padStart(8, '0')}`, + create_time: completeItem.create_time + } + + that.setData({ + currentItem: itemData + }) + that.initMapMarkers(completeItem.location) + + console.log('📊 最终设置的物品数据:', that.data.currentItem) + } else { + console.error('❌ 未找到对应的物品') + wx.showToast({ + title: '物品信息获取失败', + icon: 'none' + }) + } + } else { + console.error('❌ 服务器返回数据格式错误') + wx.showToast({ + title: '数据格式错误', + icon: 'none' + }) + } + }, + fail(err) { + console.error('❌ 获取物品信息失败:', err) + wx.showToast({ + title: '网络连接失败', + icon: 'none' + }) + } + }) + }, + + // 生成物品图标 + generateItemIcon(itemName) { + const iconMap = { + '手机': '📱', '耳机': '🎧', '钱包': '👛', '钥匙': '🔑', '水杯': '🥤', + '书包': '🎒', '书本': '📚', '电脑': '💻', '眼镜': '👓', '手表': '⌚', + '校园卡': '💳', '身份证': '🆔', '充电宝': '🔋', 'U盘': '💾', '鼠标': '🖱️', + '保温杯': '🥤', '充电器': '🔌', '数据线': '🔗', '台灯': '💡', '雨伞': '☂️' + }; + + for (let key in iconMap) { + if (itemName.includes(key)) { + return iconMap[key]; + } + } + return '📦'; + }, + + // 初始化地图标记 + initMapMarkers(location) { + // 中国民航大学东丽校区各公寓位置坐标 + const locationCoordinates = { + '北教25A座': { latitude: 39.116404, longitude: 117.350928 }, + '校史馆': { latitude: 39.113829, longitude: 117.349670 }, + '航空工程学院': { latitude: 39.111647, longitude: 117.348411 }, + '理学院': { latitude: 39.105889, longitude: 117.353043 }, + '南9公寓': { latitude: 39.103560, longitude: 117.354630 }, + '南7公寓': { latitude: 39.102861, longitude: 117.354008 }, + '厦航天合餐厅': { latitude: 39.103335, longitude: 117.352625 }, + '明德图书馆': { latitude: 39.101322, longitude: 117.354898 }, + '南22公寓': { latitude: 39.101205, longitude: 117.352368 }, + '北19公寓': { latitude: 39.111039, longitude: 117.352447 }, + '北14公寓': { latitude: 39.110350, longitude: 117.350845 }, + '北28公寓': { latitude: 39.112571, longitude: 117.353174 }, + '北8公寓': { latitude: 39.114969, longitude: 117.351384 }, + // 默认使用校区中心坐标 + 'default': { latitude: 39.111039, longitude: 117.352447 } + } + + // 查找匹配的位置(支持模糊匹配) + let coordinates = locationCoordinates['default']; + let matchedLocation = 'default'; + + // 遍历所有位置名称,查找匹配项 + for (const key in locationCoordinates) { + if (key !== 'default' && location.includes(key)) { + coordinates = locationCoordinates[key]; + matchedLocation = key; + break; + } + } + + // 如果上面没有匹配到,再尝试精确匹配 + if (matchedLocation === 'default' && locationCoordinates[location]) { + coordinates = locationCoordinates[location]; + matchedLocation = location; + } + + console.log('输入位置:', location, '匹配到:', matchedLocation, '坐标:', coordinates); + + const markers = [{ + id: 0, + latitude: coordinates.latitude, + longitude: coordinates.longitude, + title: matchedLocation === 'default' ? '校区中心' : matchedLocation, + iconPath: '/images/marker.png', + width: 35, + height: 35, + callout: { + content: matchedLocation === 'default' ? `📍 ${location}\n(使用默认位置)` : `📍 ${matchedLocation}`, + color: '#007AFF', + fontSize: 14, + borderRadius: 10, + bgColor: '#ffffff', + padding: 10, + display: 'ALWAYS' + } + }] + + this.setData({ + markers: markers, + mapLongitude: coordinates.longitude, + mapLatitude: coordinates.latitude + }); + + // 如果是地图视图,确保地图定位到正确位置 + if (this.data.showMap) { + this.adjustMapPosition(); + } + }, + + // 显示地图视图 + showMapView() { + this.setData({ + showMap: true + }, () => { + // 地图显示后确保定位准确 + setTimeout(() => { + this.adjustMapPosition(); + }, 300); + }) + }, + + // 隐藏地图视图 + hideMapView() { + this.setData({ + showMap: false + }) + }, + + // 调整地图位置到标记点 + adjustMapPosition() { + const mapContext = wx.createMapContext('campusMap'); + if (this.data.markers.length > 0) { + const marker = this.data.markers[0]; + mapContext.moveToLocation({ + longitude: marker.longitude, + latitude: marker.latitude, + success: () => { + console.log('地图定位成功'); + }, + fail: (err) => { + console.log('地图定位失败:', err); + } + }); + } + }, + + // 标记点点击事件 + onMarkerTap(e) { + console.log('标记点被点击', e.markerId); + const marker = this.data.markers.find(m => m.id === e.markerId); + if (marker) { + wx.showToast({ + title: marker.title, + icon: 'none' + }); + } + }, + + // 地图区域变化事件 + onRegionChange(e) { + console.log('地图区域变化', e.type); + }, + + // 显示认领模态框 + showClaimModal() { + const currentItem = this.data.currentItem; + + // 检查物品状态 + if (currentItem.state === '已领取') { + wx.showToast({ + title: '该物品已被认领', + icon: 'none' + }); + return; + } + + this.setData({ + showClaimModal: true, + claimPhone: '', + phoneError: false + }); + }, + + // 隐藏认领模态框 + hideClaimModal() { + this.setData({ + showClaimModal: false, + claimPhone: '', + phoneError: false + }); + }, + + // 认领手机号输入 + onClaimPhoneInput(e) { + const phone = e.detail.value; + const phoneRegex = /^1[3-9]\d{9}$/; + const isValid = phoneRegex.test(phone); + + this.setData({ + claimPhone: phone, + phoneError: !isValid && phone.length > 0 + }); + + if (phone.length === 11 && !isValid) { + wx.showToast({ + title: '手机号格式错误', + icon: 'none' + }); + } + }, + + // 确认认领 + confirmClaim() { + const that = this; + const { claimPhone, currentItem, itemId } = this.data; + + // 验证手机号 + const phoneRegex = /^1[3-9]\d{9}$/; + if (!phoneRegex.test(claimPhone)) { + this.setData({ + phoneError: true + }); + wx.showToast({ + title: '请输入正确的手机号', + icon: 'none' + }); + return; + } + + // 发送认领请求 + wx.showLoading({ + title: '认领中...', + mask: true + }); + + wx.request({ + url: `${this.data.apiBaseUrl}/claim-item/`, + method: 'POST', + header: { + 'Content-Type': 'application/json' + }, + data: { + item_id: itemId || currentItem.id || currentItem.number, + claim_phone: claimPhone + }, + success(res) { + wx.hideLoading(); + + if (res.data.success) { + wx.showToast({ + title: '认领成功', + icon: 'success', + duration: 2000 + }); + + // 重新从服务器获取最新数据 + setTimeout(() => { + that.fetchCompleteItemInfo(that.data.itemId); + }, 500); + + that.setData({ + showClaimModal: false + }); + } else { + wx.showToast({ + title: res.data.message || '认领失败', + icon: 'none', + duration: 3000 + }); + } + }, + fail(err) { + wx.hideLoading(); + wx.showToast({ + title: '网络连接失败', + icon: 'none' + }); + } + }); + }, + + // 刷新数据 + refreshData() { + if (this.data.itemId) { + this.fetchCompleteItemInfo(this.data.itemId); + wx.showToast({ + title: '刷新成功', + icon: 'success' + }); + } + }, + + onShareAppMessage() { + return { + title: `失物招领 - ${this.data.currentItem.name}`, + path: `/pages/detail/detail?item=${encodeURIComponent(JSON.stringify(this.data.currentItem))}` + } + } + }) \ No newline at end of file diff --git a/src/detail/detail.json b/src/detail/detail.json new file mode 100644 index 0000000..8b3f925 --- /dev/null +++ b/src/detail/detail.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationBarTitleText": "物品详情" +} \ No newline at end of file diff --git a/src/detail/detail.wxml b/src/detail/detail.wxml new file mode 100644 index 0000000..241f6c7 --- /dev/null +++ b/src/detail/detail.wxml @@ -0,0 +1,132 @@ + + + + + {{currentItem.icon}} + {{currentItem.name}} + + + + + + 物品名称: + {{currentItem.name}} + + + + 颜色: + {{currentItem.color}} + + + + 特征: + {{currentItem.feature}} + + + + 品牌: + {{currentItem.brand}} + + + + 联系方式: + {{currentItem.telephone}} + + + + 当前位置: + {{currentItem.location}} + + + + 详细描述: + {{currentItem.description}} + + + + + + + + + + + + + + + 返回详情 + + 物品位置 - {{currentItem.location}} + + + + + + + 📍 {{currentItem.location}} + {{currentItem.name}} - {{currentItem.color}} + + + + + + + + 物品状态 + + + + 物品状态: + + {{currentItem.state || '未领取'}} + + + + + 认领人手机: + {{currentItem.claim_phone}} + + + + + + + + + + + + + 认领物品 + + + 请输入您的手机号进行认领: + + 手机号格式不正确 + + + + + + + + \ No newline at end of file diff --git a/src/detail/detail.wxss b/src/detail/detail.wxss new file mode 100644 index 0000000..c003781 --- /dev/null +++ b/src/detail/detail.wxss @@ -0,0 +1,602 @@ +.container { + padding: 40rpx; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; +} + +.detail-card { + background: white; + border-radius: 32rpx; + padding: 60rpx 40rpx; + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1); + margin-top: 40rpx; +} + +/* 物品头部 */ +.item-header { + text-align: center; + margin-bottom: 60rpx; + padding-bottom: 40rpx; + border-bottom: 2rpx solid #f0f0f0; +} + +.item-icon { + font-size: 120rpx; + display: block; + margin-bottom: 20rpx; +} + +.item-title { + font-size: 48rpx; + font-weight: bold; + color: #333; +} + +/* 信息区域 */ +.info-section { + margin-bottom: 60rpx; +} + +.info-row { + display: flex; + align-items: flex-start; + margin-bottom: 40rpx; + padding: 20rpx 0; + border-bottom: 1rpx solid #f8f8f8; +} + +.info-row.full-width { + flex-direction: column; +} + +.info-label { + width: 200rpx; + font-size: 28rpx; + color: #666; + font-weight: bold; + flex-shrink: 0; +} + +.info-value { + flex: 1; + font-size: 28rpx; + color: #333; + line-height: 1.6; +} + +.info-value.description { + background-color: #f8f8f8; + padding: 30rpx; + border-radius: 16rpx; + margin-top: 20rpx; +} + +/* 操作按钮 */ +.action-buttons { + display: flex; + gap: 20rpx; +} + +.contact-btn { + flex: 1; + background-color: #007AFF; + color: white; + border: none; + border-radius: 16rpx; + padding: 28rpx; + font-size: 32rpx; + font-weight: bold; +} + +/* 地图容器 */ +.map-container { + height: 1300rpx; + background: white; + border-radius: 32rpx; + overflow: hidden; + margin-top: 20rpx; + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1); +} + +.map-header { + display: flex; + align-items: center; + padding: 30rpx 40rpx; + background: linear-gradient(135deg, #007AFF 0%, #0056CC 100%); + color: white; +} + +.back-btn { + display: flex; + align-items: center; + padding: 15rpx 25rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 25rpx; + margin-right: 30rpx; +} + +.back-icon { + font-size: 32rpx; + margin-right: 10rpx; +} + +.back-text { + font-size: 28rpx; +} + +.map-title { + flex: 1; + font-size: 32rpx; + font-weight: bold; + text-align: center; +} + +.campus-map { + width: 100%; + height: 1030rpx; +} + +.map-info { + padding: 30rpx 40rpx; + background: #f8f8f8; +} + +.location-info { + display: block; + font-size: 32rpx; + font-weight: bold; + color: #333; + margin-bottom: 15rpx; +} + +.item-info { + display: block; + font-size: 28rpx; + color: #666; +} + +/* 在原有样式基础上添加以下样式 */ + +/* 状态信息样式 */ +.status-section { + background: #fff; + padding: 20rpx; + margin: 20rpx; + border-radius: 10rpx; + box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1); + } + + .status-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15rpx 0; + border-bottom: 1rpx solid #f5f5f5; + } + + .status-item:last-child { + border-bottom: none; + } + + .status-label { + font-size: 28rpx; + color: #666; + } + + .status-value { + font-size: 28rpx; + font-weight: bold; + } + + .status-value.unclaimed { + color: #e74c3c; + } + + .status-value.claimed { + color: #27ae60; + } + + .claim-phone { + font-size: 28rpx; + color: #27ae60; + font-weight: bold; + } + + /* 认领按钮样式 */ + .claim-section { + padding: 20rpx; + margin: 20rpx; + } + + .claim-btn { + background: #07c160; + color: white; + border-radius: 50rpx; + font-size: 32rpx; + padding: 20rpx 0; + } + + .claim-btn:active { + background: #06a952; + } + + /* 模态框样式 */ + .modal-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 999; + } + + .modal-content { + background: white; + border-radius: 20rpx; + width: 600rpx; + overflow: hidden; + } + + .modal-header { + padding: 30rpx; + border-bottom: 1rpx solid #f0f0f0; + text-align: center; + } + + .modal-title { + font-size: 32rpx; + font-weight: bold; + color: #333; + } + + .modal-body { + padding: 40rpx 30rpx; + } + + .modal-tip { + font-size: 28rpx; + color: #666; + margin-bottom: 30rpx; + display: block; + } + + .phone-input { + border: 2rpx solid #e0e0e0; + border-radius: 10rpx; + padding: 20rpx; + font-size: 28rpx; + margin-bottom: 10rpx; + } + + .phone-input.error { + border-color: #e74c3c; + } + + .error-text { + color: #e74c3c; + font-size: 24rpx; + } + + .modal-footer { + display: flex; + border-top: 1rpx solid #f0f0f0; + } + + .btn-cancel, .btn-confirm { + flex: 1; + border: none; + border-radius: 0; + padding: 25rpx 0; + font-size: 28rpx; + } + + .btn-cancel { + background: #f8f8f8; + color: #666; + border-right: 1rpx solid #f0f0f0; + } + + .btn-confirm { + background: #07c160; + color: white; + } + + .btn-cancel:active { + background: #e8e8e8; + } + + .btn-confirm:active { + background: #06a952; + } + + /* 在原有样式基础上添加以下样式 */ + +/* 在原有样式基础上添加以下样式 */ + +.container { + padding-bottom: 40rpx; + } + + .section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20rpx; + } + + .section-title { + font-size: 32rpx; + font-weight: bold; + color: #333; + } + + .refresh-btn { + background: #1989fa; + color: white; + font-size: 24rpx; + padding: 10rpx 20rpx; + border-radius: 20rpx; + } + + .item-card { + background: #fff; + padding: 30rpx; + margin: 20rpx; + border-radius: 15rpx; + box-shadow: 0 4rpx 15rpx rgba(0,0,0,0.1); + } + + .item-header { + display: flex; + align-items: center; + margin-bottom: 20rpx; + } + + .item-icon { + font-size: 48rpx; + margin-right: 20rpx; + } + + .item-name { + font-size: 36rpx; + font-weight: bold; + color: #333; + } + + .item-info { + margin-bottom: 25rpx; + } + + .item-description { + font-size: 28rpx; + color: #666; + line-height: 1.5; + } + + .item-details { + border-top: 1rpx solid #f0f0f0; + padding-top: 20rpx; + } + + .detail-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12rpx 0; + border-bottom: 1rpx solid #f8f8f8; + } + + .detail-item:last-child { + border-bottom: none; + } + + .detail-label { + font-size: 28rpx; + color: #666; + } + + .detail-value { + font-size: 28rpx; + color: #333; + font-weight: 500; + } + + .map-section { + background: #fff; + padding: 30rpx; + margin: 20rpx; + border-radius: 15rpx; + box-shadow: 0 4rpx 15rpx rgba(0,0,0,0.1); + } + + .map-container { + margin-top: 20rpx; + } + + .map-placeholder { + background: #f8f9fa; + border: 2rpx dashed #dee2e6; + border-radius: 10rpx; + padding: 60rpx 20rpx; + text-align: center; + } + + .map-tip { + font-size: 28rpx; + color: #6c757d; + } + + .map-actions { + margin-top: 20rpx; + text-align: center; + } + + .btn-secondary { + background: #6c757d; + color: white; + font-size: 28rpx; + padding: 15rpx 30rpx; + border-radius: 25rpx; + } + + /* 状态信息样式 */ + .status-section { + background: #fff; + padding: 30rpx; + margin: 20rpx; + border-radius: 15rpx; + box-shadow: 0 4rpx 15rpx rgba(0,0,0,0.1); + } + + .status-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20rpx 0; + border-bottom: 1rpx solid #f5f5f5; + } + + .status-item:last-child { + border-bottom: none; + } + + .status-label { + font-size: 28rpx; + color: #666; + font-weight: 500; + } + + .status-value { + font-size: 28rpx; + font-weight: bold; + } + + .status-value.unclaimed { + color: #e74c3c; + } + + .status-value.claimed { + color: #27ae60; + } + + .claim-phone { + font-size: 28rpx; + color: #27ae60; + font-weight: bold; + } + + /* 认领按钮样式 */ + .claim-section { + padding: 0 20rpx; + margin: 30rpx 20rpx; + } + + .claim-btn { + background: #07c160; + color: white; + border-radius: 50rpx; + font-size: 32rpx; + padding: 25rpx 0; + font-weight: bold; + } + + .claim-btn:active { + background: #06a952; + } + + /* 模态框样式 */ + .modal-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 999; + } + + .modal-content { + background: white; + border-radius: 20rpx; + width: 600rpx; + overflow: hidden; + } + + .modal-header { + padding: 30rpx; + border-bottom: 1rpx solid #f0f0f0; + text-align: center; + } + + .modal-title { + font-size: 32rpx; + font-weight: bold; + color: #333; + } + + .modal-body { + padding: 40rpx 30rpx; + } + + .modal-tip { + font-size: 28rpx; + color: #666; + margin-bottom: 30rpx; + display: block; + } + + .phone-input { + border: 2rpx solid #e0e0e0; + border-radius: 10rpx; + padding: 20rpx; + font-size: 28rpx; + margin-bottom: 10rpx; + } + + .phone-input.error { + border-color: #e74c3c; + } + + .error-text { + color: #e74c3c; + font-size: 24rpx; + } + + .modal-footer { + display: flex; + border-top: 1rpx solid #f0f0f0; + } + + .btn-cancel, .btn-confirm { + flex: 1; + border: none; + border-radius: 0; + padding: 25rpx 0; + font-size: 28rpx; + } + + .btn-cancel { + background: #f8f8f8; + color: #666; + border-right: 1rpx solid #f0f0f0; + } + + .btn-confirm { + background: #07c160; + color: white; + } + + .btn-cancel:active { + background: #e8e8e8; + } + + .btn-confirm:active { + background: #06a952; + } \ No newline at end of file diff --git a/src/index/index.js b/src/index/index.js new file mode 100644 index 0000000..82b1440 --- /dev/null +++ b/src/index/index.js @@ -0,0 +1,218 @@ +// pages/home/home.js +Page({ + data: { + allItems: [], + isLoading: true, + apiBaseUrl: 'http://10.11.72.16:8000/api' + }, + + onLoad() { + this.loadAllItems(); + }, + + onShow() { + // 每次页面显示时重新加载数据 + this.loadAllItems(); + }, + + // 加载所有物品(优先服务器,失败则使用本地) + loadAllItems() { + const that = this; + that.setData({ isLoading: true }); + + console.log('🔄 开始加载物品数据...'); + + // 优先从服务器加载 + that.loadServerItems().then(serverItems => { + console.log('✅ 从服务器加载成功,物品数量:', serverItems.length); + + that.setData({ + allItems: serverItems, + isLoading: false + }); + + // 同时更新本地存储 + that.updateLocalStorage(serverItems); + + }).catch(err => { + console.log('❌ 服务器加载失败,尝试本地数据:', err); + + // 服务器失败则使用本地数据 + that.loadLocalItems().then(localItems => { + console.log('📱 从本地加载成功,物品数量:', localItems.length); + + that.setData({ + allItems: localItems, + isLoading: false + }); + + }).catch(localErr => { + console.log('❌ 本地加载也失败,使用默认数据:', localErr); + that.setDefaultItems(); + }); + }); + }, + + // 加载本地物品 + loadLocalItems() { + return new Promise((resolve, reject) => { + wx.getStorage({ + key: 'lostItems', + success: (res) => { + if (res.data && res.data.length > 0) { + // 按时间倒序排序 + const sortedItems = res.data.sort((a, b) => + new Date(b.timestamp || 0) - new Date(a.timestamp || 0) + ); + resolve(sortedItems); + } else { + resolve([]); + } + }, + fail: () => { + resolve([]); + } + }); + }); + }, + + // 从服务器加载物品 - 修复版本 + // 从服务器加载物品 - 修复字段映射 +loadServerItems() { + const that = this; + return new Promise((resolve, reject) => { + wx.request({ + url: `${this.data.apiBaseUrl}/items/`, + method: 'GET', + timeout: 10000, + success(res) { + console.log('🌐 服务器响应:', res); + + if (res.data.success && res.data.data) { + // 转换服务器数据格式为前端格式 - 修复字段映射 + const serverItems = res.data.data.map(item => ({ + id: item.id || item.number, + name: item.object || item.name, + color: item.color, + feature: item.feature, + brand: item.brand, + number: item.number || `CAUC${(item.id || item.number).toString().padStart(8, '0')}`, + location: item.location, + telephone: item.telephone, // 正确映射联系方式字段 + icon: that.generateItemIcon(item.object || item.name), + description: `${item.color}的${item.object || item.name},${item.feature},品牌:${item.brand}`, + timestamp: item.create_time || new Date().toISOString(), + fromServer: true + })); + console.log('📱 转换后的物品数据:', serverItems); + resolve(serverItems); + } else { + reject(new Error('服务器返回数据格式错误')); + } + }, + fail(err) { + console.error('❌ 请求服务器失败:', err); + reject(err); + } + }); + }); +}, + + // 更新本地存储 + updateLocalStorage(items) { + wx.setStorage({ + key: 'lostItems', + data: items, + success: () => { + console.log('💾 本地存储更新成功,物品数量:', items.length); + }, + fail: (err) => { + console.error('❌ 本地存储更新失败:', err); + } + }); + }, + + // 设置默认示例数据 + setDefaultItems() { + const defaultItems = [ + { + name: "耳机", + color: "黑色", + feature: "椭圆形", + number: "CAUC20240001", + location: "北苑19公寓", + icon: "🎧", + brand: "Sony", + description: "黑色椭圆形无线耳机,音质清晰,带有充电盒", + timestamp: new Date().toISOString() + }, + { + name: "校园卡", + color: "蓝色", + feature: "矩形有外壳", + number: "CAUC20240002", + location: "南苑17公寓", + icon: "💳", + brand: "学校统一", + description: "蓝色校园卡,装在透明保护壳内,印有学校logo", + timestamp: new Date().toISOString() + } + ]; + + this.setData({ + allItems: defaultItems, + isLoading: false + }); + + // 同时保存到本地存储 + this.updateLocalStorage(defaultItems); + }, + + // 点击物品卡片跳转到详情页 + onItemTap(e) { + const item = e.currentTarget.dataset.item; + wx.navigateTo({ + url: `/pages/detail/detail?item=${encodeURIComponent(JSON.stringify(item))}` + }); + }, + + // 生成物品图标(与identification.js保持一致) + generateItemIcon(itemName) { + const iconMap = { + '手机': '📱', + '耳机': '🎧', + '钱包': '👛', + '钥匙': '🔑', + '水杯': '🥤', + '书包': '🎒', + '书本': '📚', + '电脑': '💻', + '眼镜': '👓', + '手表': '⌚', + '校园卡': '💳', + '身份证': '🆔', + '充电宝': '🔋', + 'U盘': '💾', + '鼠标': '🖱️', + '发夹': '🎀', + '钥匙扣': '🔑' + }; + + for (let key in iconMap) { + if (itemName.includes(key)) { + return iconMap[key]; + } + } + + return '📦'; + }, + + // 下拉刷新 + onPullDownRefresh() { + console.log('🔄 手动刷新数据...'); + this.loadAllItems(); + setTimeout(() => { + wx.stopPullDownRefresh(); + }, 2000); + } +}); \ No newline at end of file diff --git a/src/index/index.json b/src/index/index.json new file mode 100644 index 0000000..a0ebf06 --- /dev/null +++ b/src/index/index.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationBarTitleText": "首页" +} \ No newline at end of file diff --git a/src/index/index.wxml b/src/index/index.wxml new file mode 100644 index 0000000..0f91ace --- /dev/null +++ b/src/index/index.wxml @@ -0,0 +1,70 @@ + + + + + + + 所有物品 + + + + 加载中... + + + + + 暂无物品信息 + + + + + {{item.icon}} + {{item.name}} + {{item.location}} + + + + + \ No newline at end of file diff --git a/src/index/index.wxss b/src/index/index.wxss new file mode 100644 index 0000000..667f4d8 --- /dev/null +++ b/src/index/index.wxss @@ -0,0 +1,279 @@ +/* pages/home/home.wxss */ +@import "/static/css/iconfont.wxss"; + +.li navigator{ + display: block; + line-height: 100rpx; + cursor: pointer; + height: 100rpx; + margin: auto; + padding: 10px; + background-position: center; + background-color: #B0E2FF; + border-radius: 20rpx; + padding-left: 20rpx; + border-bottom: solid black; + margin-top: 50rpx; +} +.li navigator:hover{ + columns: #bb0000; +} +.function{ + display: flex; + justify-content: space-between; +} +.li navigator .function view{ + font-size: 50rpx; +} + +/* 所有物品区域 */ +.all-items-section { + width: 90%; + margin: 40rpx auto; + background-color: white; + border-radius: 24rpx; + padding: 40rpx; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); +} + +.section-title { + font-size: 36rpx; + font-weight: bold; + color: #333; + margin-bottom: 30rpx; + display: block; +} + +.items-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20rpx; +} + +.item-card { + background-color: #f8f8f8; + border-radius: 16rpx; + padding: 30rpx 20rpx; + text-align: center; + transition: all 0.3s; + border: 1rpx solid #e0e0e0; +} + +.item-card:active { + background-color: #e0e0e0; + transform: scale(0.98); +} + +.card-icon { + font-size: 48rpx; + margin-bottom: 16rpx; + display: block; +} + +.card-name { + font-size: 28rpx; + font-weight: bold; + margin-bottom: 8rpx; + display: block; + color: #333; +} + +.card-location { + font-size: 22rpx; + color: #666; + display: block; +} + +.custom-tabbar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 150rpx; + background: #ffffff; + display: flex; + border-top: 1rpx solid black; +} + +.tab-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.tab-item image { + width: 40rpx; + height: 40rpx; +} + +.tab-item text { + font-size: 40rpx; + margin-top: 4rpx; +} + +/* 添加内容 */ +/* 加载状态 */ +.loading-section { + text-align: center; + padding: 40rpx; +} + +.loading-text { + color: #666; + font-size: 28rpx; +} + +/* 空状态 */ +.empty-section { + text-align: center; + padding: 80rpx 40rpx; +} + +.empty-text { + color: #999; + font-size: 32rpx; +} + +/* 来源标记 */ +.local-badge { + position: absolute; + top: 10rpx; + right: 10rpx; + background: #ff9500; + color: white; + font-size: 20rpx; + padding: 4rpx 8rpx; + border-radius: 8rpx; +} + +.server-badge { + position: absolute; + top: 10rpx; + right: 10rpx; + background: #09BB07; + color: white; + font-size: 20rpx; + padding: 4rpx 8rpx; + border-radius: 8rpx; +} + +.item-card { + position: relative; /* 为badge定位 */ +} + +/* 在原有 index.wxss 基础上添加 */ + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30rpx; +} + +.refresh-area { + display: flex; + align-items: center; + padding: 15rpx 25rpx; + background: #f0f0f0; + border-radius: 25rpx; +} + +.refresh-text { + font-size: 26rpx; + color: #666; + margin-right: 10rpx; +} + +.refresh-icon { + font-size: 26rpx; +} + +/* 加载状态 */ +.loading-section { + text-align: center; + padding: 80rpx 40rpx; +} + +.loading-spinner { + width: 60rpx; + height: 60rpx; + border: 4rpx solid #f0f0f0; + border-top: 4rpx solid #09BB07; + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 20rpx; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.loading-text { + color: #666; + font-size: 28rpx; +} + +/* 错误状态 */ +.error-section { + text-align: center; + padding: 80rpx 40rpx; +} + +.error-icon { + font-size: 80rpx; + display: block; + margin-bottom: 20rpx; +} + +.error-text { + color: #ff4d4f; + font-size: 32rpx; + display: block; + margin-bottom: 30rpx; +} + +.retry-btn { + background: #09BB07; + color: white; + border: none; + border-radius: 10rpx; + padding: 20rpx 40rpx; + font-size: 28rpx; +} + +/* 空状态 */ +.empty-section { + text-align: center; + padding: 100rpx 40rpx; +} + +.empty-icon { + font-size: 100rpx; + display: block; + margin-bottom: 20rpx; + opacity: 0.5; +} + +.empty-text { + color: #999; + font-size: 32rpx; + display: block; + margin-bottom: 10rpx; +} + +.empty-subtext { + color: #ccc; + font-size: 26rpx; + display: block; +} + +/* 时间显示 */ +.card-time { + font-size: 20rpx; + color: #999; + display: block; + margin-top: 8rpx; +} \ No newline at end of file