统一排行榜对齐星雀UI,页面资源后台配置实时刷新

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-27 01:26:11 +08:00
parent 1ab2e2080a
commit 21112173f2
292 changed files with 64010 additions and 81 deletions

View File

@@ -0,0 +1,422 @@
import request from '../../utils/request.js'
Page({
data: {
// 头像相关
touxiangUrl: '', // 完整头像URL
touxiangRelative: '', // 头像相对URL用于上传后更新
// 打手信息字段
dashouUid: '', // UID不可修改
dashouNicheng: '', // 昵称
dashouJieshao: '', // 个人介绍
dashouDianhua: '', // 电话
dashouWeixin: '', // 微信
// 页面状态
isLoading: false, // 是否正在加载
isDataFromApi: false, // 数据是否来自API用于显示提示
isSubmitting: false, // 是否正在提交
hasLoadedFromCache: false, // 是否已从缓存加载
// 验证状态
nichengValid: true,
dianhuaValid: true,
weixinValid: true,
jieshaoValid: true
},
onLoad() {
// 页面加载时初始化数据
this.initPageData()
},
onShow() {
// 每次页面显示时检查头像是否需要更新
this.registerNotificationComponent();
this.checkAvatarUpdate()
},
// 🆕 新增:注册通知组件
registerNotificationComponent() {
const app = getApp();
const notificationComp = this.selectComponent('#global-notification');
if (notificationComp && notificationComp.showNotification) {
console.log('🏪 商城页面注册通知组件');
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification()
};
}
},
// 初始化页面数据
async initPageData() {
this.setData({ isLoading: true })
try {
// 1. 从缓存加载基本信息
const cachedData = this.loadFromCache()
// 2. 检查是否需要从API加载
const needApiLoad = this.checkNeedApiLoad(cachedData)
if (needApiLoad) {
// 从API加载数据
await this.loadFromApi()
this.setData({ isDataFromApi: true })
} else {
// 使用缓存数据
this.setDataFromCache(cachedData)
this.setData({ isDataFromApi: false })
}
// 3. 构建完整头像URL
this.buildAvatarUrl()
} catch (error) {
console.error('初始化页面数据失败:', error)
wx.showToast({
title: '数据加载失败',
icon: 'error',
duration: 2000
})
} finally {
this.setData({ isLoading: false, hasLoadedFromCache: true })
}
},
// 从缓存加载数据
loadFromCache() {
const app = getApp()
return {
// 从缓存获取头像相对URL
touxiang: wx.getStorageSync('touxiang') || '',
// 从缓存获取用户ID
uid: wx.getStorageSync('uid') || '',
// 从缓存获取打手信息
dsnc: wx.getStorageSync('dsnc') || '',
dsjieshao: wx.getStorageSync('dsjieshao') || '',
dsdianhua: wx.getStorageSync('dsdianhua') || '',
dsweixin: wx.getStorageSync('dsweixin') || ''
}
},
// 检查是否需要从API加载数据
checkNeedApiLoad(cachedData) {
// 如果昵称或介绍有一个为空则需要从API加载
return !cachedData.dsnc || !cachedData.dsjieshao
},
// 设置缓存数据到页面
setDataFromCache(cachedData) {
const app = getApp()
// 处理空值显示
const formatEmptyValue = (value) => {
return value && value.trim() !== '' ? value : '暂未填写'
}
this.setData({
dashouUid: cachedData.uid || '',
dashouNicheng: cachedData.dsnc || '',
dashouJieshao: cachedData.dsjieshao || '',
dashouDianhua: formatEmptyValue(cachedData.dsdianhua),
dashouWeixin: formatEmptyValue(cachedData.dsweixin),
touxiangRelative: cachedData.touxiang || ''
})
},
// 从API加载数据
async loadFromApi() {
try {
const res = await request({
url: '/yonghu/dszjxxhq',
method: 'POST'
})
if (res && res.data.code === 200) {
const data = res.data.data || {}
// 格式化空值
const formatValue = (value) => {
if (value === null || value === undefined || value.trim() === '') {
return '暂未填写'
}
return value
}
// 更新页面数据
this.setData({
dashouNicheng: formatValue(data.nicheng),
dashouJieshao: formatValue(data.jieshao),
dashouDianhua: formatValue(data.dianhua),
dashouWeixin: formatValue(data.wechat)
})
// 更新缓存(即使为空也存储,避免重复请求)
this.updateCache(data)
} else {
throw new Error(res?.data?.msg || '获取信息失败')
}
} catch (error) {
console.error('API加载失败:', error)
throw error
}
},
// 更新缓存
updateCache(data) {
// 存储到缓存,确保字段存在(即使为空)
wx.setStorageSync('dsnc', data.nicheng || '')
wx.setStorageSync('dsjieshao', data.jieshao || '')
wx.setStorageSync('dsdianhua', data.dianhua || '')
wx.setStorageSync('dsweixin', data.wechat || '')
},
// 构建完整头像URL
buildAvatarUrl() {
const app = getApp()
// 获取头像相对路径
const touxiangRelative = this.data.touxiangRelative || ''
// 获取全局配置
const ossImageUrl = app.globalData.ossImageUrl || ''
const morentouxiang = app.globalData.morentouxiang || ''
// 构建完整URL
let fullUrl = ''
if (touxiangRelative && touxiangRelative.trim() !== '') {
fullUrl = ossImageUrl + touxiangRelative
} else {
fullUrl = ossImageUrl + morentouxiang
}
this.setData({ touxiangUrl: fullUrl })
},
// 检查头像是否需要更新
checkAvatarUpdate() {
const app = getApp()
const currentAvatar = wx.getStorageSync('touxiang') || ''
// 如果缓存中的头像与当前显示的不同重新构建URL
if (currentAvatar !== this.data.touxiangRelative) {
this.setData({ touxiangRelative: currentAvatar })
this.buildAvatarUrl()
}
},
// 事件处理函数
onAvatarTap() {
// 头像点击事件(后续可扩展为更换头像)
wx.showToast({
title: '头像上传功能待实现',
icon: 'none',
duration: 2000
})
},
onNichengInput(e) {
const value = e.detail.value
this.setData({
dashouNicheng: value,
nichengValid: value.length <= 20
})
},
onDianhuaInput(e) {
const value = e.detail.value
// 简单的手机号验证
const isValid = /^1[3-9]\d{9}$/.test(value) || value === '' || value === '暂未填写'
this.setData({
dashouDianhua: value,
dianhuaValid: isValid
})
},
onWeixinInput(e) {
const value = e.detail.value
this.setData({
dashouWeixin: value,
weixinValid: value.length <= 30
})
},
onJieshaoInput(e) {
const value = e.detail.value
this.setData({
dashouJieshao: value,
jieshaoValid: value.length <= 200
})
},
// 表单验证
validateForm() {
const {
dashouNicheng,
dashouDianhua,
dashouWeixin,
dashouJieshao,
nichengValid,
dianhuaValid,
weixinValid,
jieshaoValid
} = this.data
// 1. 验证昵称
if (!dashouNicheng || dashouNicheng.trim() === '') {
wx.showToast({
title: '昵称不能为空',
icon: 'none',
duration: 2000
})
return false
}
if (dashouNicheng.length > 20) {
wx.showToast({
title: '昵称不能超过20字',
icon: 'none',
duration: 2000
})
return false
}
// 2. 验证手机号(非必填,但填写时必须正确)
if (dashouDianhua && dashouDianhua !== '暂未填写') {
if (!/^1[3-9]\d{9}$/.test(dashouDianhua)) {
wx.showToast({
title: '手机号格式不正确',
icon: 'none',
duration: 2000
})
return false
}
}
// 3. 验证微信号(非必填)
if (dashouWeixin && dashouWeixin.length > 30) {
wx.showToast({
title: '微信号不能超过30字符',
icon: 'none',
duration: 2000
})
return false
}
// 4. 验证个人介绍
if (dashouJieshao && dashouJieshao.length > 200) {
wx.showToast({
title: '介绍不能超过200字',
icon: 'none',
duration: 2000
})
return false
}
return true
},
// 格式化提交数据
formatSubmitData() {
const {
dashouNicheng,
dashouDianhua,
dashouWeixin,
dashouJieshao
} = this.data
// 将"暂未填写"转换为空字符串
const formatValue = (value) => {
return value === '暂未填写' ? '' : value.trim()
}
return {
nicheng: formatValue(dashouNicheng),
dianhua: formatValue(dashouDianhua),
wechat: formatValue(dashouWeixin),
jieshao: formatValue(dashouJieshao)
}
},
// 提交修改
async onSubmit() {
// 防止重复提交
if (this.data.isSubmitting) return
// 表单验证
if (!this.validateForm()) return
this.setData({ isSubmitting: true })
// 准备提交数据
const postData = this.formatSubmitData()
try {
const res = await request({
url: '/yonghu/dsgxxx',
method: 'POST',
data: postData
})
if (res && res.data.code === 200) {
// 更新成功
wx.showToast({
title: '修改成功',
icon: 'success',
duration: 2000
})
// 更新本地缓存
this.updateLocalCache(postData)
// 延迟返回上一页
setTimeout(() => {
wx.navigateBack()
}, 1500)
} else {
// 更新失败
const errorMsg = res?.data?.msg || '修改失败,请重试'
wx.showModal({
title: '修改失败',
content: errorMsg,
showCancel: false
})
this.setData({ isSubmitting: false })
}
} catch (error) {
console.error('提交修改失败:', error)
wx.showModal({
title: '请求失败',
content: error.message || '网络错误,请检查连接',
showCancel: false
})
this.setData({ isSubmitting: false })
}
},
// 更新本地缓存
updateLocalCache(data) {
if (data.nicheng !== undefined) {
wx.setStorageSync('dsnc', data.nicheng)
}
if (data.jieshao !== undefined) {
wx.setStorageSync('dsjieshao', data.jieshao)
}
if (data.dianhua !== undefined) {
wx.setStorageSync('dsdianhua', data.dianhua)
}
if (data.wechat !== undefined) {
wx.setStorageSync('dsweixin', data.wechat)
}
}
})

View File

@@ -0,0 +1,11 @@
{
"navigationBarTitleText": "服务者信息修改",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"backgroundColor": "#f8f9fa",
"enablePullDownRefresh": false,
"usingComponents": {
"global-notification": "/components/global-notification/global-notification"
}
}

View File

@@ -0,0 +1,103 @@
<!-- 打手信息修改页面 -->
<view class="container">
<!-- 头像区域 -->
<view class="avatar-section">
<view class="avatar-wrapper">
<image
src="{{ touxiangUrl }}"
mode="aspectFill"
class="avatar"
bindtap="onAvatarTap"
></image>
<view class="avatar-halo"></view>
<text class="avatar-hint">点击更换头像</text>
</view>
</view>
<!-- 信息展示与修改区域 -->
<view class="info-section">
<!-- UID不可修改 -->
<view class="info-item">
<text class="info-label">服务者UID</text>
<text class="info-value info-value-disabled">{{ dashouUid }}</text>
</view>
<!-- 昵称 -->
<view class="info-item">
<text class="info-label">服务昵称</text>
<input
value="{{ dashouNicheng }}"
placeholder="请输入昵称"
maxlength="20"
bindinput="onNichengInput"
class="info-input"
/>
<text class="char-count">{{ dashouNicheng.length || 0 }}/20</text>
</view>
<!-- 电话 -->
<view class="info-item">
<text class="info-label">联系电话</text>
<input
value="{{ dashouDianhua }}"
placeholder="请输入手机号"
type="number"
maxlength="11"
bindinput="onDianhuaInput"
class="info-input"
/>
</view>
<!-- 微信 -->
<view class="info-item">
<text class="info-label">微信号</text>
<input
value="{{ dashouWeixin }}"
placeholder="请输入微信号"
maxlength="30"
bindinput="onWeixinInput"
class="info-input"
/>
<text class="char-count">{{ dashouWeixin.length || 0 }}/30</text>
</view>
<!-- 个人介绍 -->
<view class="info-item info-item-textarea">
<text class="info-label">个人介绍</text>
<textarea
value="{{ dashouJieshao }}"
placeholder="请简单介绍一下自己..."
maxlength="200"
bindinput="onJieshaoInput"
class="info-textarea"
></textarea>
<text class="char-count">{{ dashouJieshao.length || 0 }}/200</text>
</view>
</view>
<!-- 状态提示 -->
<view class="status-section" wx:if="{{ isDataFromApi }}">
<text class="status-text">提示:已从服务器加载最新信息</text>
</view>
<!-- 提交按钮 -->
<view class="button-section">
<button
class="submit-btn"
bindtap="onSubmit"
disabled="{{ isSubmitting }}"
loading="{{ isSubmitting }}"
>
{{ isSubmitting ? '提交中...' : '立即修改' }}
</button>
</view>
<!-- 加载中提示 -->
<view class="loading-wrapper" wx:if="{{ isLoading }}">
<view class="loading-content">
<image src="/images/loading.gif" class="loading-icon"></image>
<text class="loading-text">加载中...</text>
</view>
</view>
</view>
<global-notification id="global-notification" />

View File

@@ -0,0 +1,197 @@
/* 页面容器 */
.container {
min-height: 100vh;
background: linear-gradient(135deg, #fffacd 0%, #e6f7ff 100%);
padding: 20rpx;
box-sizing: border-box;
}
/* 头像区域 */
.avatar-section {
display: flex;
justify-content: center;
align-items: center;
padding: 40rpx 0;
position: relative;
}
.avatar-wrapper {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.avatar {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
border: 6rpx solid #ffffff;
box-shadow: 0 0 30rpx rgba(255, 215, 0, 0.4);
z-index: 2;
position: relative;
background-color: #f5f5f5;
}
.avatar-halo {
position: absolute;
width: 220rpx;
height: 220rpx;
border-radius: 50%;
background: radial-gradient(circle, rgba(255, 215, 0, 0.2) 0%, transparent 70%);
z-index: 1;
animation: halo-pulse 2s infinite ease-in-out;
}
@keyframes halo-pulse {
0%, 100% { opacity: 0.6; transform: scale(1); }
50% { opacity: 0.8; transform: scale(1.05); }
}
.avatar-hint {
margin-top: 20rpx;
font-size: 26rpx;
color: #666;
text-align: center;
}
/* 信息区域 */
.info-section {
background: rgba(255, 255, 255, 0.9);
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 40rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
}
.info-item {
display: flex;
flex-direction: column;
margin-bottom: 40rpx;
position: relative;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 15rpx;
}
.info-value-disabled {
font-size: 32rpx;
color: #999;
font-weight: bold;
background: #f9f9f9;
padding: 20rpx;
border-radius: 10rpx;
}
.info-input {
font-size: 32rpx;
color: #333;
padding: 20rpx;
background: #f9f9f9;
border-radius: 10rpx;
border: 1rpx solid #e0e0e0;
}
.info-textarea {
font-size: 30rpx;
color: #333;
padding: 20rpx;
background: #f9f9f9;
border-radius: 10rpx;
border: 1rpx solid #e0e0e0;
min-height: 200rpx;
line-height: 1.5;
}
.info-item-textarea {
padding-bottom: 20rpx;
}
.char-count {
position: absolute;
right: 0;
bottom: 5rpx;
font-size: 24rpx;
color: #999;
}
/* 状态提示 */
.status-section {
background: #e8f4fd;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 30rpx;
border-left: 6rpx solid #1890ff;
}
.status-text {
font-size: 26rpx;
color: #1890ff;
}
/* 按钮区域 */
.button-section {
padding: 20rpx 0 40rpx;
}
.submit-btn {
background: linear-gradient(90deg, #52c41a, #73d13d);
color: white;
font-size: 32rpx;
font-weight: 600;
height: 90rpx;
line-height: 70rpx;
border-radius: 45rpx;
border: none;
box-shadow: 0 10rpx 20rpx rgba(82, 196, 26, 0.2);
}
.submit-btn:disabled {
background: #cccccc;
box-shadow: none;
}
.submit-btn::after {
border: none;
}
/* 加载中 */
.loading-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
}
.loading-icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 30rpx;
}
.loading-text {
font-size: 28rpx;
color: #666;
}