mysq 3 months ago
parent 99a4baaf02
commit 48c1b91ed1

@ -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))}`
}
}
})

@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationBarTitleText": "物品详情"
}

@ -0,0 +1,132 @@
<view class="container">
<view class="detail-card" wx:if="{{!showMap}}">
<!-- 物品图标和名称 -->
<view class="item-header">
<text class="item-icon">{{currentItem.icon}}</text>
<text class="item-title">{{currentItem.name}}</text>
</view>
<!-- 详细信息 -->
<view class="info-section">
<view class="info-row">
<text class="info-label">物品名称:</text>
<text class="info-value">{{currentItem.name}}</text>
</view>
<view class="info-row">
<text class="info-label">颜色:</text>
<text class="info-value">{{currentItem.color}}</text>
</view>
<view class="info-row">
<text class="info-label">特征:</text>
<text class="info-value">{{currentItem.feature}}</text>
</view>
<view class="info-row">
<text class="info-label">品牌:</text>
<text class="info-value">{{currentItem.brand}}</text>
</view>
<view class="info-row">
<text class="info-label">联系方式:</text>
<text class="info-value">{{currentItem.telephone}}</text>
</view>
<view class="info-row">
<text class="info-label">当前位置:</text>
<text class="info-value">{{currentItem.location}}</text>
</view>
<view class="info-row full-width">
<text class="info-label">详细描述:</text>
<text class="info-value description">{{currentItem.description}}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<button class="contact-btn" bindtap="showMapView">查看地图</button>
</view>
</view>
<!-- 地图视图 -->
<view class="map-container" wx:if="{{showMap}}">
<view class="map-header">
<view class="back-btn" bindtap="hideMapView">
<text class="back-icon">←</text>
<text class="back-text">返回详情</text>
</view>
<text class="map-title">物品位置 - {{currentItem.location}}</text>
</view>
<map
id="campusMap"
class="campus-map"
longitude="{{mapLongitude}}"
latitude="{{mapLatitude}}"
scale="18"
markers="{{markers}}"
show-location
bindmarkertap="onMarkerTap"
>
</map>
<view class="map-info">
<text class="location-info">📍 {{currentItem.location}}</text>
<text class="item-info">{{currentItem.name}} - {{currentItem.color}}</text>
</view>
</view>
<!-- 物品状态信息 -->
<view class="status-section">
<view class="section-header">
<text class="section-title">物品状态</text>
</view>
<view class="status-item">
<text class="status-label">物品状态:</text>
<text class="status-value {{currentItem.state === '已领取' ? 'claimed' : 'unclaimed'}}">
{{currentItem.state || '未领取'}}
</text>
</view>
<view wx:if="{{currentItem.state === '已领取' && currentItem.claim_phone}}" class="status-item">
<text class="status-label">认领人手机:</text>
<text class="claim-phone">{{currentItem.claim_phone}}</text>
</view>
</view>
<!-- 认领按钮 -->
<view wx:if="{{currentItem.state !== '已领取'}}" class="claim-section">
<button class="claim-btn" bindtap="showClaimModal">
我要认领
</button>
</view>
<!-- 认领模态框 -->
<view wx:if="{{showClaimModal}}" class="modal-mask">
<view class="modal-content">
<view class="modal-header">
<text class="modal-title">认领物品</text>
</view>
<view class="modal-body">
<text class="modal-tip">请输入您的手机号进行认领:</text>
<input
class="phone-input {{phoneError ? 'error' : ''}}"
type="number"
placeholder="请输入11位手机号"
maxlength="11"
value="{{claimPhone}}"
bindinput="onClaimPhoneInput"
/>
<text wx:if="{{phoneError}}" class="error-text">手机号格式不正确</text>
</view>
<view class="modal-footer">
<button class="btn-cancel" bindtap="hideClaimModal">取消</button>
<button class="btn-confirm" bindtap="confirmClaim">确认认领</button>
</view>
</view>
</view>
</view>

@ -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;
}

@ -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);
}
});

@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationBarTitleText": "首页"
}

@ -0,0 +1,70 @@
<!--pages/home/home.wxml-->
<view class="banner">
<view class="li">
<navigator url="/pages/identification/identification">
<view class="function">
<view>智能拾物登记</view>
<view class="iconfont icon-xiangyou-1 iconright"></view>
</view>
</navigator>
</view>
<view class="li">
<navigator url="/pages/logs/logs">
<view class="function">
<view>搜索匹配</view>
<view class="iconfont icon-xiangyou-1 iconright"></view>
</view>
</navigator>
</view>
</view>
<!-- 所有物品展示 -->
<!-- <view class="all-items-section">
<text class="section-title">所有物品</text>
<view class="items-grid">
<view
class="item-card"
wx:for="{{allItems}}"
wx:key="index"
bindtap="onItemTap"
data-item="{{item}}"
>
<view class="card-icon">{{item.icon}}</view>
<text class="card-name">{{item.name}}</text>
<text class="card-location">{{item.location}}</text>
</view>
</view>
</view> -->
<!-- 在 index.wxml 的 items-grid 上方添加 -->
<view class="all-items-section">
<text class="section-title">所有物品</text>
<!-- 加载状态 -->
<view wx:if="{{isLoading}}" class="loading-section">
<text class="loading-text">加载中...</text>
</view>
<!-- 空状态 -->
<view wx:if="{{!isLoading && allItems.length === 0}}" class="empty-section">
<text class="empty-text">暂无物品信息</text>
</view>
<view
class="items-grid"
wx:if="{{!isLoading && allItems.length > 0}}"
>
<view
class="item-card"
wx:for="{{allItems}}"
wx:key="index"
bindtap="onItemTap"
data-item="{{item}}"
>
<view class="card-icon">{{item.icon}}</view>
<text class="card-name">{{item.name}}</text>
<text class="card-location">{{item.location}}</text>
<!-- 标记来源 -->
<!-- <text wx:if="{{item.isLocalOnly}}" class="local-badge">本地</text>
<text wx:if="{{item.fromServer}}" class="server-badge">云端</text> -->
</view>
</view>
</view>

@ -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;
}
Loading…
Cancel
Save