guci 5 months ago
parent aaca346538
commit 5d1de382fa

@ -0,0 +1,419 @@
Page({
data: {
currentItem: {},
showMap: false,
mapLongitude: 117.352447, // 默认校区中心经度
mapLatitude: 39.111039, // 默认校区中心纬度
markers: [],
showClaimModal: false,
claimPhone: '',
phoneError: false,
apiBaseUrl: 'http://192.168.174.1: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) {
this.setData({itemId: options.id})
this.fetchCompleteItemInfo(options.id)
}
},
onShow() {
// 页面显示时重新获取数据,确保状态最新
if (this.data.itemId) {
this.fetchCompleteItemInfo(this.data.itemId)
}
},
// 图片加载错误处理
onImageError(e) {
console.error('❌ 图片加载失败:', e.detail.errMsg);
console.log('📸 当前照片路径:', this.data.currentItem.photo);
wx.showToast({
title: '照片加载失败',
icon: 'none'
});
},
// 从服务器获取完整的物品信息
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 = '已领取';
}
// 处理照片URL
let photoUrl = completeItem.photo || completeItem.imagePath || '';
if (photoUrl && !photoUrl.startsWith('http')) {
// 如果是相对路径,转换为绝对路径
photoUrl = `http://192.168.28.80:8000${photoUrl}`;
}
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,
photo: photoUrl // 使用处理后的照片URL
}
that.setData({
currentItem: itemData
})
that.initMapMarkers(completeItem.location)
console.log('📊 最终设置的物品数据:', that.data.currentItem)
console.log('🖼️ 最终照片URL:', that.data.currentItem.photo)
} 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'
})
}
})
},
// 预览照片
previewPhoto() {
const { currentItem } = this.data;
if (currentItem.photo) {
wx.previewImage({
urls: [currentItem.photo],
current: currentItem.photo
});
}
},
// 生成物品图标
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,146 @@
<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 class="info-row full-width" wx:if="{{currentItem.photo}}">
<text class="info-label">物品照片:</text>
<view class="photo-container">
<image
src="{{currentItem.photo}}"
class="item-photo"
mode="aspectFit"
bindtap="previewPhoto"
binderror="onImageError"
/>
<text class="photo-tip">点击查看大图</text>
</view>
</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,468 @@
.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;
}
/* 新增:照片容器样式 */
.photo-container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
margin-top: 20rpx;
}
.item-photo {
width: 100%;
height: 400rpx;
border-radius: 16rpx;
background-color: #f8f8f8;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.photo-tip {
font-size: 24rpx;
color: #999;
margin-top: 16rpx;
text-align: center;
}
/* 操作按钮 */
.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;
}
/* 在原有样式基础上添加以下样式 */
/* 在原有样式基础上添加以下样式 */
.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,125 @@
// pages/login/login.js
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
const API_BASE_URL = 'http://192.168.174.1:8000'
Page({
data: {
userInfo: {
avatarUrl: defaultAvatarUrl,
nickName: '',
},
canLogin: false,
defaultAvatarUrl: defaultAvatarUrl
},
onLoad: function(options) {
// 从首页传递过来的回调页面
if (options.from) {
this.setData({
fromPage: options.from
})
}
// 检查本地是否有用户信息
this.checkUserInfo()
},
checkUserInfo: function() {
const userInfo = wx.getStorageSync('userInfo')
if (userInfo) {
this.setData({
userInfo: userInfo,
canLogin: true
})
}
},
onChooseAvatar: function(e) {
const { avatarUrl } = e.detail
const { nickName } = this.data.userInfo
this.setData({
"userInfo.avatarUrl": avatarUrl,
canLogin: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
onInputChange: function(e) {
const nickName = e.detail.value
const { avatarUrl } = this.data.userInfo
this.setData({
"userInfo.nickName": nickName,
canLogin: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
onGetUserInfo: function(e) {
if (e.detail.userInfo) {
// 用户同意授权
const userInfo = e.detail.userInfo
this.setData({
userInfo: userInfo,
canLogin: true
})
this.handleWechatLogin(userInfo)
} else {
// 用户拒绝授权
wx.showToast({
title: '授权失败',
icon: 'none'
})
}
},
handleWechatLogin: function(userInfo) {
wx.showLoading({
title: '登录中...',
})
wx.request({
url: `${API_BASE_URL}/api/wechat/login/`,
method: 'POST',
data: {
nickname: userInfo.nickName,
},
success: (res) => {
wx.hideLoading()
if (res.data.code === 200) {
const userData = res.data.data
// 保存用户信息到本地
wx.setStorageSync('userInfo', {
nickName: userData.nickname,
user_id: userData.user_id,
wechat_id: userData.wechat_id
})
wx.setStorageSync('isLoggedIn', true)
wx.setStorageSync('user_id', userData.user_id)
wx.showToast({
title: '登录成功',
icon: 'success',
duration: 1500,
success: () => {
setTimeout(() => {
wx.redirectTo({
url: '/pages/match/match'
})
}, 1500)
}
})
} else {
wx.showToast({
title: res.data.message || '登录失败',
icon: 'none'
})
}
},
fail: (err) => {
wx.hideLoading()
wx.showToast({
title: '网络连接失败',
icon: 'none'
})
}
})
}
})

@ -0,0 +1,4 @@
{
"usingComponents": {},
"navigationBarTitleText": "登录"
}

@ -0,0 +1,40 @@
<!--logs.wxml-->
<!--pages/login/login.wxml-->
<view class="login-container">
<view class="login-header">
<text class="title">用户登录</text>
<text class="subtitle">请先登录以使用搜索匹配功能</text>
</view>
<view class="login-content">
<view class="avatar-section">
<view class="avatar-wrapper">
<image class="avatar" src="{{userInfo.avatarUrl || defaultAvatarUrl}}" mode="aspectFill"></image>
<button class="avatar-btn" wx:if="{{!userInfo.avatarUrl}}" open-type="chooseAvatar" bindchooseavatar="onChooseAvatar">
选择头像
</button>
</view>
</view>
<view class="nickname-section">
<view class="form-item">
<text class="label">昵称</text>
<input
class="nickname-input"
type="nickname"
placeholder="请输入昵称"
value="{{userInfo.nickName}}"
bindinput="onInputChange"
/>
</view>
</view>
<!-- <button class="login-btn" bindtap="handleLogin" disabled="{{!canLogin}}">
{{canLogin ? '立即登录' : '请填写完整信息'}}
</button> -->
<button class="wechat-login-btn" open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">
微信一键登录
</button>
</view>
</view>

@ -0,0 +1,105 @@
/* pages/login/login.wxss */
.login-container {
padding: 60rpx 40rpx;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.login-header {
text-align: center;
margin-bottom: 80rpx;
}
.title {
display: block;
font-size: 48rpx;
font-weight: bold;
color: white;
margin-bottom: 20rpx;
}
.subtitle {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
}
.login-content {
background: white;
border-radius: 20rpx;
padding: 60rpx 40rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
}
.avatar-section {
display: flex;
justify-content: center;
margin-bottom: 60rpx;
}
.avatar-wrapper {
position: relative;
text-align: center;
}
.avatar {
width: 160rpx;
height: 160rpx;
border-radius: 50%;
border: 4rpx solid #667eea;
}
.avatar-btn {
margin-top: 20rpx;
font-size: 24rpx;
background: #667eea;
color: white;
border-radius: 30rpx;
}
.form-item {
margin-bottom: 40rpx;
}
.label {
display: block;
font-size: 32rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: bold;
}
.nickname-input {
border: 2rpx solid #e0e0e0;
border-radius: 12rpx;
padding: 24rpx;
font-size: 28rpx;
}
.nickname-input:focus {
border-color: #667eea;
}
.login-btn {
width: 100%;
height: 88rpx;
background: #667eea;
color: white;
border-radius: 44rpx;
font-size: 32rpx;
margin-bottom: 30rpx;
border: none;
}
.login-btn:disabled {
background: #cccccc;
}
.wechat-login-btn {
width: 100%;
height: 88rpx;
background: #07c160;
color: white;
border-radius: 44rpx;
font-size: 32rpx;
border: none;
}

@ -0,0 +1,72 @@
# models.py - 修复字段名称
from django.db import models
from django.utils import timezone
# 物品信息表
class T_Object_Info(models.Model):
number = models.AutoField(primary_key=True, verbose_name="编号")
object = models.CharField(max_length=100, verbose_name="物品名称")
color = models.CharField(max_length=50, verbose_name="物品颜色")
feature = models.TextField(verbose_name="物品特征")
brand = models.CharField(max_length=100, verbose_name="物品品牌")
telephone = models.CharField(max_length=11, verbose_name="联系电话")
location = models.CharField(max_length=200, verbose_name="位置")
# 修改使用统一的photo字段名
photo = models.CharField(
max_length=500,
verbose_name="识别照片路径",
blank=True,
null=True,
default=''
)
create_time = models.DateTimeField(default=timezone.now, verbose_name="创建时间")
# 状态信息
state = models.CharField(
max_length=20,
default='未领取',
choices=[('未领取', '未领取'), ('已领取', '已领取')],
verbose_name="物品状态"
)
claim_phone = models.CharField(
max_length=11,
blank=True,
null=True,
verbose_name="认领人手机号"
)
class Meta:
db_table = 'T_Object_Info'
verbose_name = '物品信息'
verbose_name_plural = verbose_name
def __str__(self):
return f"{self.object} - {self.create_time.strftime('%Y-%m-%d %H:%M')}"
# 用户表
class T_User(models.Model):
user_id = models.AutoField(primary_key=True, verbose_name="用户序号")
wechat_id = models.CharField(max_length=100, unique=True, verbose_name="微信号")
nickname = models.CharField(max_length=100, verbose_name="微信昵称")
class Meta:
db_table = 'T_User'
verbose_name = '用户信息'
verbose_name_plural = verbose_name
def __str__(self):
return f"{self.nickname}({self.wechat_id})"
class T_Description(models.Model):
desc_id = models.AutoField(primary_key=True, verbose_name="描述序号")
wechat = models.ForeignKey(T_User, on_delete=models.CASCADE, verbose_name="微信号", to_field='wechat_id',
db_column='wechat_id')
description = models.TextField(verbose_name="用户描述")
class Meta:
db_table = 'T_Description'
verbose_name = '用户描述'
def __str__(self):
return f"描述{self.desc_id}({self.wechat.nickname})"

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save