This commit is contained in:
zpc 2025-09-10 13:54:09 +08:00
parent 4e191d6c39
commit 4b6f7641b6
6 changed files with 518 additions and 318 deletions

View File

@ -9,6 +9,8 @@ const development = {
// baseUrl: 'https://ydsapi.zpc-xy.com',
baseUrl: 'http://1.15.21.245:2401',
host: ['http://1.15.21.245:2401'],
// baseUrl: 'http://localhost:2015',
// host: ['http://localhost:2015'],
imageUrl: 'https://guyu-1308826010.cos.ap-shanghai.myqcloud.com',
};

View File

@ -51,4 +51,26 @@ export const getUserInfo = async () => {
return res.data;
}
return null;
}
/**
* 修改用户信息
* @param {*} nickName
* @param {*} avatar
* @param {*} sex
* @param {*} birthday
* @returns
*/
export const editUserInfo = async (nickName, avatar, sex, birthday) => {
const res = await request.post("user/EditUserInfo", {
nickName,
avatar,
sex,
birthday
});
if (res.code == 0) {
return true;
}
return false;
}

View File

@ -1,4 +1,4 @@
import { getUserInfo } from '@/common/server/interface/user'
import { getUserInfo, editUserInfo } from '@/common/server/interface/user'
import { ref } from 'vue'
export var userInfo = ref(null);
@ -6,7 +6,7 @@ export var userInfo = ref(null);
export const loadUserInfo = async () => {
const res = await getUserInfo();
if (res == null) {
userInfo.value = null;
uni.removeStorageSync('tokenInfo');
uni.removeStorageSync('userInfo');
@ -16,3 +16,10 @@ export const loadUserInfo = async () => {
uni.setStorageSync('userInfo', res);
}
export const updateUserInfo = async (nickName, avatar, sex, birthday) => {
const res = await editUserInfo(nickName, avatar, sex, birthday);
if (!res) {
return;
}
loadUserInfo();
}

View File

@ -9,9 +9,9 @@
</view>
</view>
<view style="z-index: 100;margin-top:10rpx;"
v-if="homeData != null && homeData.advertList != null && homeData.advertList.length > 0">
<view style="width: 100%;width:100%;display:flex;justify-content: center;">
<view style="z-index: 100;margin-top:10rpx;height:532rpx;">
<view style="width: 100%;width:100%;display:flex;justify-content: center;"
v-if="homeData != null && homeData.advertList != null && homeData.advertList.length > 0">
<view class="" style="width:97%; height: 386rpx;border-radius: 25rpx;">
<swiper class="img-swiper" :indicator-dots="false" circular autoplay interval="3000">
<swiper-item v-for="(item, index) in homeData.advertList" :key="index">
@ -174,189 +174,195 @@
</template>
<script setup>
import {
ref,
reactive,
onMounted
} from 'vue'
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import MahjongCard from '@/components/index/MahjongCard.vue'
import {
homeData,
preloadHomeData
} from '@/common/server/index'
import {
configData,
getConfigData,
preloadConfigData
} from '@/common/server/config'
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight);
const getBackgroundImg = () => {
return {
background: `url(${configData.value.config.defaultImage}) 100% 100% `,
backgroundSize: '100% 100%'
};
}
//
const title = ref('Hello')
const imgList = ref([1, 2, 3, 4, 5])
const dataList = ref([]) //
const rateValue = ref(3.5)
const mockData = ref([]) //
import {
ref,
reactive,
onMounted
} from 'vue'
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
import MahjongCard from '@/components/index/MahjongCard.vue'
import {
homeData,
preloadHomeData
} from '@/common/server/index'
import {
configData,
getConfigData,
preloadConfigData
} from '@/common/server/config'
const statusBarHeight = ref(uni.getSystemInfoSync().statusBarHeight);
const getBackgroundImg = () => {
return {
background: `url(${configData.value.config.defaultImage}) 100% 100% `,
backgroundSize: '100%',
backgroundRepeat: "no-repeat",
backgroundRepeatX: "0px",
backgroundSize: "100%",
backgroundRepeatY: "0px",
backgroundPositionX: "0px",
backgroundPositionY: statusBarHeight.value + "px",
};
}
//
const title = ref('Hello')
const imgList = ref([1, 2, 3, 4, 5])
const dataList = ref([]) //
const rateValue = ref(3.5)
const mockData = ref([]) //
//
const pagePaging = ref(null)
const cardInfo = ref(null)
const userInfo = ref(null)
//
const pagePaging = ref(null)
const cardInfo = ref(null)
const userInfo = ref(null)
//
const initMockData = () => {
mockData.value = []
for (let i = 1; i <= 50; i++) {
//
const personCount = [2, 3, 4][Math.floor(Math.random() * 3)]
const joinedCount = Math.floor(Math.random() * (personCount + 1))
//
const initMockData = () => {
mockData.value = []
for (let i = 1; i <= 50; i++) {
//
const personCount = [2, 3, 4][Math.floor(Math.random() * 3)]
const joinedCount = Math.floor(Math.random() * (personCount + 1))
//
const joinPerson = []
for (let j = 0; j < joinedCount; j++) {
joinPerson.push({
id: j + 1,
name: `玩家${j + 1}`,
avatar: '',
phone: `138****${String(j + 1).padStart(4, '0')}`
//
const joinPerson = []
for (let j = 0; j < joinedCount; j++) {
joinPerson.push({
id: j + 1,
name: `玩家${j + 1}`,
avatar: '',
phone: `138****${String(j + 1).padStart(4, '0')}`
})
}
mockData.value.push({
id: i,
title: `麻将局 ${i}`,
status: joinedCount >= personCount ? '已满员' : '组局中',
time: '2000 ~ 0430 共8小时30分钟',
room: `304包厢-大包,${personCount}`,
description: '休闲局,随便来',
requirements: '麻将、斗地主、不可吸烟、性别不限、信誉≧4.0',
personCount: personCount,
joinPerson: joinPerson
})
}
}
mockData.value.push({
id: i,
title: `麻将局 ${i}`,
status: joinedCount >= personCount ? '已满员' : '组局中',
time: '2000 ~ 0430 共8小时30分钟',
room: `304包厢-大包,${personCount}`,
description: '休闲局,随便来',
requirements: '麻将、斗地主、不可吸烟、性别不限、信誉≧4.0',
personCount: personCount,
joinPerson: joinPerson
//
const queryList = (pageNo, pageSize) => {
console.log(`加载第${pageNo}页,每页${pageSize}条数据`)
//
setTimeout(() => {
const startIndex = (pageNo - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = mockData.value.slice(startIndex, endIndex)
// z-paging
pagePaging.value.complete(pageData)
//
// if (pageNo === 1) {
// uni.showToast({
// title: '',
// icon: 'success',
// duration: 1500
// })
// }
}, 100) // 1
}
//
const refreshData = () => {
pagePaging.value.reload()
}
//
const clearData = () => {
pagePaging.value.clear()
}
//
const openPop = () => {
cardInfo.value.open()
}
const clasePop = () => {
cardInfo.value.close()
}
const openUserPop = () => {
userInfo.value.open()
}
const closeUserPop = () => {
userInfo.value.close()
}
const onChange = (e) => {
console.log('rate发生改变:' + JSON.stringify(e))
}
//
const handleJoin = (item) => {
console.log('加入麻将局:', item)
// API
uni.showToast({
title: '加入成功',
icon: 'success',
duration: 1500
})
}
}
//
const queryList = (pageNo, pageSize) => {
console.log(`加载第${pageNo}页,每页${pageSize}条数据`)
//
setTimeout(() => {
const startIndex = (pageNo - 1) * pageSize
const endIndex = startIndex + pageSize
const pageData = mockData.value.slice(startIndex, endIndex)
// z-paging
pagePaging.value.complete(pageData)
//
// if (pageNo === 1) {
// uni.showToast({
// title: '',
// icon: 'success',
// duration: 1500
// })
// }
}, 100) // 1
}
//
const refreshData = () => {
pagePaging.value.reload()
}
//
const clearData = () => {
pagePaging.value.clear()
}
//
const openPop = () => {
cardInfo.value.open()
}
const clasePop = () => {
cardInfo.value.close()
}
const openUserPop = () => {
userInfo.value.open()
}
const closeUserPop = () => {
userInfo.value.close()
}
const onChange = (e) => {
console.log('rate发生改变:' + JSON.stringify(e))
}
//
const handleJoin = (item) => {
console.log('加入麻将局:', item)
// API
uni.showToast({
title: '加入成功',
icon: 'success',
duration: 1500
//
onMounted(() => {
//
initMockData()
})
}
//
onMounted(() => {
//
initMockData()
})
onLoad(async () => {
if (!homeData.value) preloadHomeData();
if (!configData.value) preloadConfigData();
});
onLoad(async () => {
if (!homeData.value) preloadHomeData();
if (!configData.value) preloadConfigData();
});
</script>
<style lang="scss">
.content {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
.content {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
}
.img-swiper {
width: 100%;
height: 386rpx;
}
.img-swiper {
width: 100%;
height: 386rpx;
}
.slide-img {
width: 100%;
height: 100%;
.slide-img {
width: 100%;
height: 100%;
}
}
/* 网格容器 - 2列布局 */
.grid-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
/* 2列每列宽度相等 */
gap: 20rpx;
/* 网格间距(行列间距相同) */
padding: 0 15rpx;
}
/* 网格容器 - 2列布局 */
.grid-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
/* 2列每列宽度相等 */
gap: 20rpx;
/* 网格间距(行列间距相同) */
padding: 0 15rpx;
}
/* 网格项内容 */
.item-content {
height: 630rpx;
// background-color: #E6E6E6;
color: white;
border-radius: 10rpx;
font-size: 32rpx;
}
/* 网格项内容 */
.item-content {
height: 630rpx;
// background-color: #E6E6E6;
color: white;
border-radius: 10rpx;
font-size: 32rpx;
}
</style>

View File

@ -1,182 +1,347 @@
<template>
<view class="content column">
<view class="row" style="width: 90%; margin: 100rpx auto 0; justify-content: space-between;">
<image src="/static/back.png" style="width: 40rpx; height: 40rpx;" @click="goBack()" mode=""></image>
<text style="font-size: 30rpx;">修改信息</text>
<view style="width: 40rpx;"></view>
<view class="header">
<image src="/static/back.png" class="back-icon" @click="goBack()" mode=""></image>
<text class="header-title">修改信息</text>
<view class="header-spacer"></view>
</view>
<view class="column" style="width: 90%; margin: 80rpx auto 0;">
<view class="form-container">
<view class="row"
style="justify-content: space-between; width: 100%; align-items: center; margin-top: 40rpx; font-size: 26rpx;">
<text style="">我的UID</text>
<text style="">{{ user.id }}</text>
<view class="form-row">
<text class="form-label">我的UID</text>
<text class="form-value">{{ user.id }}</text>
</view>
<view style="width: 100%; height: 1rpx; background-color: antiquewhite; margin-top: 20rpx;"></view>
<view class="divider"></view>
<view class="row"
style="justify-content: space-between; width: 100%; align-items: center;margin-top:20rpx;">
<text style="font-size: 26rpx;">我的头像</text>
<view class="form-row avatar-row">
<text class="form-label">我的头像</text>
<button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image :src="user.avatarImage"
style="width: 70rpx; height: 70rpx; background-color: aquamarine; border-radius: 50%;" mode="">
<image :src="user.avatarImage" class="avatar-image" mode="">
</image>
</button>
</view>
<view style="width: 100%; height: 1rpx; background-color: antiquewhite; margin-top: 20rpx;"></view>
<view class="divider"></view>
<view class="row"
style="justify-content: space-between; width: 100%; align-items: center; margin-top: 40rpx; font-size: 26rpx;">
<text style="">我的昵称</text>
<input type="nickname" v-model="user.nickName" style="text-align: right;" />
<view class="form-row">
<text class="form-label">我的昵称</text>
<input type="nickname" v-model="user.nickName" class="form-input" />
</view>
<view style="width: 100%; height: 1rpx; background-color: antiquewhite; margin-top: 20rpx;"></view>
<view class="divider"></view>
<view class="row"
style="justify-content: space-between; width: 100%; align-items: center; margin-top: 40rpx; font-size: 26rpx;">
<view class="form-row">
<text class="form-label">我的年龄</text>
<text class="form-value">{{ calculatedAge }}</text>
</view>
<text style="">我的年龄</text>
<view class="divider"></view>
<picker mode="selector" :value="ageIndex" :range="ageOptions" @change="onAgeChange">
<view class="form-row">
<text class="form-label">我的性别</text>
<picker mode="selector" :value="genderIndex" :range="GENDER_OPTIONS" @change="onGenderChange">
<view class="picker-text">
{{ ageOptions[ageIndex] }}
{{ currentGender }}
</view>
</picker>
</view>
<view style="width: 100%; height: 1rpx; background-color: antiquewhite; margin-top: 20rpx;"></view>
<view class="divider"></view>
<view class="row"
style="justify-content: space-between; width: 100%; align-items: center; margin-top: 40rpx; font-size: 26rpx;">
<text style="">我的性别</text>
<picker mode="selector" :value="genderIndex" :range="genderOptions" @change="onGenderChange">
<view class="form-row">
<text class="form-label">我的生日</text>
<picker mode="date" :value="user.birthday" @change="onBirthdayChange">
<view class="picker-text">
{{ genderOptions[genderIndex] }}
{{ user.birthday || '请选择生日' }}
</view>
</picker>
</view>
<view style="width: 100%; height: 1rpx; background-color: antiquewhite; margin-top: 20rpx;"></view>
<view class="divider"></view>
</view>
<!-- 保存按钮 -->
<view class="save-button-container">
<button class="save-button" @click="saveUserInfo" :disabled="isSaving">
{{ isSaving ? '保存中...' : '保存修改' }}
</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { userInfo } from '@/common/server/user'
import { ref, computed, onMounted } from 'vue'
import { userInfo, updateUserInfo } from '@/common/server/user'
//
const genderOptions = ['男', '女']
//
const GENDER_OPTIONS = ['男', '女']
//
const genderIndex = ref(0)
const isSaving = ref(false)
// (18-90)
const ageOptions = Array.from({ length: 73 }, (_, i) => i + 18)
const ageIndex = ref(0)
//
const user = ref({
avatarImage: userInfo.value.avatarImage,
nickName: userInfo.value.nickName,
age: userInfo.value.age ?? 18,
id: userInfo.value.id,
sex: userInfo.value.sex == 1 ? '男' : '女'
avatarImage: userInfo.value.avatarImage || '',
nickName: userInfo.value.nickName || '',
id: userInfo.value.id || '',
sex: userInfo.value.sex == 1 ? '男' : '女',
birthday: userInfo.value.birthday ?? "1990-01-01"
})
onLoad(() => {
console.log("userInfo", userInfo.value);
// picker
if (userInfo.value.sex === '女') {
genderIndex.value = 1
} else {
genderIndex.value = 0
//
const currentGender = computed(() => GENDER_OPTIONS[genderIndex.value])
//
const calculatedAge = computed(() => {
if (!user.value.birthday) return 0
const today = new Date()
const birthDate = new Date(user.value.birthday)
let age = today.getFullYear() - birthDate.getFullYear()
const monthDiff = today.getMonth() - birthDate.getMonth()
// 1
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--
}
// picker
const userAge = userInfo.value.age ?? 18
const ageIndexValue = Math.max(0, Math.min(72, userAge - 18))
ageIndex.value = ageIndexValue
return age
})
// picker
const initializePickerIndexes = () => {
//
genderIndex.value = userInfo.value.sex === '女' ? 1 : 0
}
onMounted(() => {
console.log("userInfo", userInfo.value)
initializePickerIndexes()
})
//
const goBack = () => {
//
uni.navigateBack({
delta: 1
});
uni.navigateBack({ delta: 1 })
}
/**
* 选择头像
*/
* 选择头像
*/
const onChooseAvatar = (e) => {
if (e && e.detail && e.detail.avatarUrl) {
// user.value.avatarImage = e.detail.avatarUrl;
convertToBase64(user.value.avatarImage);
const { avatarUrl } = e?.detail || {}
console.log("avatarUrl", avatarUrl);
if (avatarUrl) {
convertToBase64(avatarUrl)
}
}
/**
* 性别选择变化
*/
const onGenderChange = (e) => {
genderIndex.value = e.detail.value
user.value.sex = genderOptions[genderIndex.value]
const selectedIndex = e.detail.value
genderIndex.value = selectedIndex
user.value.sex = GENDER_OPTIONS[selectedIndex]
}
/**
* 年龄选择变化
* 生日选择变化
*/
const onAgeChange = (e) => {
ageIndex.value = e.detail.value
user.value.age = ageOptions[ageIndex.value]
const onBirthdayChange = (e) => {
user.value.birthday = e.detail.value
}
/**
* 将图片转换为Base64
*/
const convertToBase64 = (filePath) => {
console.log(filePath);
if (!filePath) {
showErrorToast('图片路径无效')
return
}
uni.getFileSystemManager().readFile({
filePath: filePath,
filePath,
encoding: "base64",
success: (res) => {
user.value.avatarImage = "data:image/png;base64," + res.data;
user.value.avatarImage = `data:image/png;base64,${res.data}`
},
fail: (err) => {
console.error("读取文件失败:", err);
uni.showToast({
title: "图片处理失败",
icon: "none",
});
console.error("读取文件失败:", err)
showErrorToast("图片处理失败")
},
});
})
}
/**
* 保存用户信息
*/
const saveUserInfo = async () => {
if (isSaving.value) return
//
if (!user.value.nickName.trim()) {
showErrorToast('请输入昵称')
return
}
if (!user.value.birthday) {
showErrorToast('请选择生日')
return
}
isSaving.value = true
try {
//
const saveData = {
avatarImage: user.value.avatarImage,
nickName: user.value.nickName.trim(),
sex: user.value.sex === '男' ? 1 : 2,
birthday: user.value.birthday,
}
console.log('保存用户信息:', saveData)
// API
// await updateUserInfo(saveData)
//async (nickName, avatar, sex, birthday)
await updateUserInfo(user.value.nickName.trim(), user.value.avatarImage,
user.value.sex === '男' ? 1 : 2,
user.value.birthday
);
// API
// await new Promise(resolve => setTimeout(resolve, 1000))
//
// Object.assign(userInfo.value, saveData)
uni.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
})
//
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1500)
} catch (error) {
console.error('保存失败:', error)
showErrorToast('保存失败,请重试')
} finally {
isSaving.value = false
}
}
/**
* 显示错误提示
*/
const showErrorToast = (message) => {
uni.showToast({
title: message,
icon: "none",
duration: 2000
})
}
</script>
<style lang="scss">
//
$primary-color: #333;
$border-color: antiquewhite;
$font-size-normal: 26rpx;
$font-size-title: 30rpx;
$avatar-size: 70rpx;
$border-radius: 50%;
$container-width: 90%;
$header-margin-top: 100rpx;
$form-margin-top: 80rpx;
$row-margin-top: 40rpx;
$divider-margin-top: 20rpx;
.content {
width: 100%;
height: 100vh;
}
//
.header {
width: $container-width;
margin: $header-margin-top auto 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.header-title {
font-size: $font-size-title;
}
.header-spacer {
width: 40rpx;
}
//
.form-container {
width: $container-width;
margin: $form-margin-top auto 0;
}
//
.form-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
margin-top: $row-margin-top;
font-size: $font-size-normal;
&.avatar-row {
margin-top: $divider-margin-top;
}
}
.form-label {
font-size: $font-size-normal;
}
.form-value {
font-size: $font-size-normal;
}
.form-input {
text-align: right;
font-size: $font-size-normal;
}
// 线
.divider {
width: 100%;
height: 1rpx;
background-color: $border-color;
margin-top: $divider-margin-top;
}
//
.avatar-wrapper {
margin: 0rpx;
margin: 0;
float: right;
border-radius: 128rpx;
overflow: hidden;
border: 0rpx solid #F3F3F3;
border: 0;
background-color: transparent;
padding: 0;
line-height: normal;
@ -188,14 +353,56 @@ const convertToBase64 = (filePath) => {
.avatar {
width: 170rpx;
height: 160rpx;
border-radius: 50%;
border-radius: $border-radius;
}
}
.avatar-image {
width: $avatar-size;
height: $avatar-size;
background-color: aquamarine;
border-radius: $border-radius;
}
// Picker
.picker-text {
display: flex;
align-items: center;
color: #333;
font-size: 26rpx;
color: $primary-color;
font-size: $font-size-normal;
}
//
.save-button-container {
width: $container-width;
margin: 60rpx auto 0;
padding-bottom: 40rpx;
}
.save-button {
width: 100%;
height: 80rpx;
background: linear-gradient(135deg, #F36903 0%, #E55A00 100%);
color: white;
border: none;
border-radius: 40rpx;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(243, 105, 3, 0.3);
transition: all 0.3s ease;
&:active {
transform: translateY(2rpx);
box-shadow: 0 8rpx 20rpx rgba(243, 105, 3, 0.3);
}
&:disabled {
background: #ccc;
box-shadow: none;
transform: none;
}
}
</style>

View File

@ -314,17 +314,9 @@ const clasePop = () => {
}
const toAppointment = () => {
//
if (!userInfo.uid) {
uni.showModal({
title: '提示',
content: '请先登录查看预约记录',
showCancel: false,
success: () => {
uni.navigateTo({
url: '/pages/me/login'
});
}
if (userInfo.value == null) {
uni.navigateTo({
url: '/pages/me/login'
});
return;
}
@ -336,7 +328,7 @@ const toAppointment = () => {
const toEditInfo = () => {
//
if (userInfo == null) {
if (userInfo.value == null) {
uni.navigateTo({
url: '/pages/me/login'
});
@ -348,21 +340,12 @@ const toEditInfo = () => {
}
const toBlacklist = () => {
//
if (!userInfo.uid) {
uni.showModal({
title: '提示',
content: '请先登录查看黑名单',
showCancel: false,
success: () => {
uni.navigateTo({
url: '/pages/me/login'
});
}
if (userInfo.value == null) {
uni.navigateTo({
url: '/pages/me/login'
});
return;
}
uni.navigateTo({
url: '/pages/me/blacklist-page'
});
@ -379,31 +362,6 @@ const toLogin = () => {
});
}
// -
const loadUserData = async () => {
loading.value = true
try {
//
//
userInfo.nickname = '未登录'
userInfo.uid = ''
userInfo.rating = 0
userInfo.reputation = 0
userInfo.cardQuality = 0
userInfo.cardSkill = 0
userInfo.pigeonCount = 0
} catch (error) {
console.error('加载用户数据失败:', error)
uni.showToast({
title: '加载失败',
icon: 'error'
})
} finally {
loading.value = false
}
}
// -
const loadCurrentAppointment = async () => {
try {
@ -418,7 +376,6 @@ const loadCurrentAppointment = async () => {
//
onMounted(() => {
loadUserData()
loadCurrentAppointment()
})
@ -428,9 +385,8 @@ onShow(() => {
// getUserInfoData();
})
onLoad(async () => {
console.log('kiad');
await loadUserInfo();
})
});
</script>
<style lang="scss">