1
This commit is contained in:
196
scoring/pages/history-game/history-game.vue
Normal file
196
scoring/pages/history-game/history-game.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<view class="history-game">
|
||||
<view class="filter-tab">
|
||||
<view class="filter-item" @click="filterClick('all')" :class="{'filter-active': filterActive == 'all'}">全部</view>
|
||||
<view class="filter-item" @click="filterClick('win')" :class="{'filter-active': filterActive == 'win'}">胜场</view>
|
||||
<view class="filter-item" @click="filterClick('lose')" :class="{'filter-active': filterActive == 'lose'}">负场</view>
|
||||
</view>
|
||||
<view class="game-list">
|
||||
<view class="game-item" v-for="item in 10" :key="item">
|
||||
<view class="game-info">
|
||||
<view class="game-type">单人-1237房间</view>
|
||||
<view class="game-date">2025-06-12 12:00</view>
|
||||
<view class="del-btn">
|
||||
<uni-icons type="trash" size="30"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="game-user">
|
||||
<view class="game-user-item" v-for="(user, index) in userList" :key="index">
|
||||
<view class="user-avatar">
|
||||
<image :src="user.avatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="user-name">{{user.name}}</view>
|
||||
<view class="user-score">{{user.score}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="game-foot">
|
||||
<view class="game-status">
|
||||
已结束
|
||||
</view>
|
||||
<view class="check-detail">
|
||||
<uni-icons type="right" size="25" color="#748cec"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance } from 'vue'
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
||||
const proxy = getCurrentInstance().proxy;
|
||||
|
||||
// 过滤相关
|
||||
const filterActive = ref('all'); // all:全部,win:胜场,lose:负场
|
||||
const filterClick = (type) => {
|
||||
filterActive.value = type;
|
||||
};
|
||||
|
||||
|
||||
// 参与人
|
||||
const userList = ref([
|
||||
{
|
||||
avatar: 'https://q3.itc.cn/q_70/images03/20250110/1e71eecf56b34344bcae6a5b85c0bec2.jpeg',
|
||||
name: '赵云',
|
||||
score: 100
|
||||
},
|
||||
{
|
||||
avatar: 'https://q1.itc.cn/q_70/images03/20241119/197701bb9ef34b20b6497720081a9972.jpeg',
|
||||
name: '张飞',
|
||||
score: -50
|
||||
},
|
||||
{
|
||||
avatar: 'https://img1.baidu.com/it/u=3612220943,2414740890&fm=253&app=138&f=JPEG?w=526&h=500',
|
||||
name: '刘备',
|
||||
score: 30
|
||||
},
|
||||
{
|
||||
avatar: 'https://c-ssl.dtstatic.com/uploads/blog/202206/12/20220612135738_992b1.thumb.1000_0.jpg',
|
||||
name: '诸葛亮',
|
||||
score: -80
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.history-game {
|
||||
width: 750rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.filter-tab {
|
||||
--filter-height: 80rpx;
|
||||
--filter-color: #748cec;
|
||||
--filter-border: 1rpx;
|
||||
width: 700rpx;
|
||||
height: var(--filter-height);
|
||||
margin: 20rpx auto;
|
||||
display: flex;
|
||||
.filter-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
line-height: calc(var(--filter-height) - 2 * var(--filter-border));
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
border: var(--filter-border) solid var(--filter-color);
|
||||
}
|
||||
.filter-active {
|
||||
background-color: var(--filter-color);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.game-list {
|
||||
width: 700rpx;
|
||||
margin: 0 auto;
|
||||
.game-item {
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 0 10rpx 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
.game-info {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.game-type {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.game-date {
|
||||
font-size: 32rpx;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
.del-btn {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-left: 1rpx solid #cccccc;
|
||||
}
|
||||
}
|
||||
.game-user {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
overflow: auto;
|
||||
padding: 20rpx 0;
|
||||
.game-user-item {
|
||||
width: 140rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
.user-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 10%;
|
||||
overflow: hidden;
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
}
|
||||
.user-score {
|
||||
font-size: 32rpx;
|
||||
color: #fa5d5d;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.game-foot {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.game-status {
|
||||
font-size: 32rpx;
|
||||
color: #57bcef;
|
||||
}
|
||||
.check-detail {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
210
scoring/pages/index/index.vue
Normal file
210
scoring/pages/index/index.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<view class="index">
|
||||
<view class="notice">
|
||||
<view class="icon">
|
||||
<uni-icons type="sound" size="20"></uni-icons>
|
||||
</view>
|
||||
<view class="text">
|
||||
您还有未结束的对局,
|
||||
<text class="underline">点击前往</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="function-list">
|
||||
<view class="function-item">
|
||||
<view class="function-icon">
|
||||
<image src="https://t14.baidu.com/it/u=3165460156,649373630&fm=224&app=112&f=JPEG?w=500&h=500"></image>
|
||||
</view>
|
||||
<view class="function-introduce">
|
||||
<view class="function-name">
|
||||
多人模式
|
||||
</view>
|
||||
<view class="function-desc">
|
||||
所有玩家自己计分
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="function-item" :class="{ 'active': isSinglePlayActive }" @click="handleSinglePlay" hover-class="function-item-hover">
|
||||
<view class="function-icon">
|
||||
<image src="https://pic.rmb.bdstatic.com/bjh/down/1742bc3845cbbcf0c78c01eb59bb1c1a.jpeg"></image>
|
||||
</view>
|
||||
<view class="function-introduce">
|
||||
<view class="function-name">
|
||||
单人模式
|
||||
</view>
|
||||
<view class="function-desc">
|
||||
房主给所有玩家计分
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
// 响应式数据
|
||||
const isSinglePlayActive = ref(false)
|
||||
let debounceTimer = null
|
||||
|
||||
// 防抖函数 - 防止重复点击
|
||||
const debounce = (func, wait = 300) => {
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(debounceTimer)
|
||||
func(...args)
|
||||
}
|
||||
clearTimeout(debounceTimer)
|
||||
debounceTimer = setTimeout(later, wait)
|
||||
}
|
||||
}
|
||||
|
||||
// 优化后的单人模式处理函数
|
||||
const handleSinglePlay = debounce(() => {
|
||||
// 设置激活状态,提供视觉反馈
|
||||
isSinglePlayActive.value = true
|
||||
console.log('进入单人模式')
|
||||
|
||||
// 页面跳转,增加错误处理
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/singleplay/singleplay',
|
||||
success: () => {
|
||||
console.log('单人模式页面跳转成功')
|
||||
// 跳转成功后重置状态
|
||||
setTimeout(() => {
|
||||
isSinglePlayActive.value = false
|
||||
}, 200)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('单人模式页面跳转失败:', err)
|
||||
// 跳转失败也需要重置状态
|
||||
isSinglePlayActive.value = false
|
||||
// 显示错误提示
|
||||
uni.showToast({
|
||||
title: '页面跳转失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
console.log('首页加载完成')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.index {
|
||||
width: 750rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.notice {
|
||||
width: 80%;
|
||||
margin: 20rpx auto;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 5rpx 5rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20rpx;
|
||||
.icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
color: #fa5d5d;
|
||||
}
|
||||
.text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
}
|
||||
.underline {
|
||||
text-decoration: underline;
|
||||
color: #fa5d5d;
|
||||
}
|
||||
}
|
||||
.function-list {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.function-item {
|
||||
--content-height: 100rpx;
|
||||
width: 70%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
margin: 40rpx auto;
|
||||
box-shadow: 5rpx 5rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
gap: 10rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.2s ease;
|
||||
/* 微信小程序特有样式:条件编译用多行注释包裹 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
user-select: none; /* 防止文本选中 */
|
||||
/* #endif */
|
||||
|
||||
.function-icon {
|
||||
width: var(--content-height);
|
||||
height: var(--content-height);
|
||||
border-radius: 10%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* 新增:防止图片拉伸变形 */
|
||||
}
|
||||
}
|
||||
|
||||
.function-introduce {
|
||||
flex: 1;
|
||||
height: var(--content-height);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
box-sizing: border-box;
|
||||
|
||||
.function-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
/* 微信小程序特有样式:条件编译修复 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
pointer-events: none; /* 确保整个区域都可点击 */
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.function-desc {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #a8a8a8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击时的视觉反馈效果
|
||||
.function-item-hover {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 2rpx 2rpx 5rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
// 激活状态的样式
|
||||
.function-item.active {
|
||||
background-color: #f0f8ff;
|
||||
box-shadow: 3rpx 3rpx 8rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
</style>
|
||||
255
scoring/pages/index/singleplay/change.vue
Normal file
255
scoring/pages/index/singleplay/change.vue
Normal file
@@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 自定义头部 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<uni-icons type="left" size="22" color="#fff" />
|
||||
</view>
|
||||
<view class="title">用户信息</view>
|
||||
<view class="placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="content">
|
||||
<!-- 昵称输入 -->
|
||||
<view class="form-item">
|
||||
<view class="label">昵称:</view>
|
||||
<input class="input" v-model="userInfo.nickName" placeholder="请输入昵称" />
|
||||
</view>
|
||||
|
||||
<!-- 头像选择 -->
|
||||
<view class="form-item">
|
||||
<view class="label">头像:</view>
|
||||
<view class="avatar-wrapper" @click="chooseNewAvatar">
|
||||
<image :src="userInfo.avatarUrl || '/static/logo.png'" mode="aspectFill" class="avatar" />
|
||||
<view class="avatar-tip">点击更新头像</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 保存按钮 -->
|
||||
<button class="save-btn" @click="saveUserInfo">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { updateGlobalUserInfo } from '../../../main.js'
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref({
|
||||
id: '',
|
||||
nickName: '',
|
||||
avatarUrl: ''
|
||||
})
|
||||
|
||||
// 初始化
|
||||
onLoad((options) => {
|
||||
// 从选项中获取用户数据
|
||||
if (options.userData) {
|
||||
try {
|
||||
const data = JSON.parse(decodeURIComponent(options.userData))
|
||||
userInfo.value = { ...data }
|
||||
} catch (e) {
|
||||
console.error('解析用户数据失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 记录来源页面信息,支持返回到正确页面
|
||||
if (options.from) {
|
||||
console.log('来源页面:', options.from)
|
||||
}
|
||||
})
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
// 检查是否有未保存的修改
|
||||
if (userInfo.value.nickName.trim()) {
|
||||
// 使用全局方法保存用户信息,确保全局可用
|
||||
updateGlobalUserInfo(userInfo.value)
|
||||
}
|
||||
|
||||
wx.navigateBack()
|
||||
}
|
||||
|
||||
// 选择新头像
|
||||
const chooseNewAvatar = () => {
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
maxDuration: 30,
|
||||
camera: 'back',
|
||||
success: (res) => {
|
||||
// 统一处理头像选择逻辑,支持所有平台
|
||||
try {
|
||||
const tempFilePath = res.tempFiles[0].tempFilePath
|
||||
userInfo.value.avatarUrl = tempFilePath
|
||||
} catch (error) {
|
||||
console.error('处理头像文件路径失败:', error)
|
||||
wx.showToast({
|
||||
title: '头像设置失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择图片失败:', err)
|
||||
wx.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 保存用户信息
|
||||
const saveUserInfo = () => {
|
||||
if (!userInfo.value.nickName.trim()) {
|
||||
wx.showToast({
|
||||
title: '昵称不能为空',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 使用全局方法保存用户信息,确保全局可用
|
||||
updateGlobalUserInfo(userInfo.value)
|
||||
|
||||
// 保存成功,返回上一页并传递更新的数据
|
||||
const pages = getCurrentPages()
|
||||
const prevPage = pages[pages.length - 2]
|
||||
if (prevPage) {
|
||||
// 通过事件通知上一页更新数据
|
||||
if (prevPage.$vm && prevPage.$vm.updateUserData) {
|
||||
prevPage.$vm.updateUserData(userInfo.value)
|
||||
} else {
|
||||
// 如果上一页没有updateUserData方法,通过全局事件通知
|
||||
wx.$emit && wx.$emit('userDataUpdated', userInfo.value)
|
||||
}
|
||||
}
|
||||
|
||||
// 显示保存成功提示
|
||||
wx.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
success: () => {
|
||||
setTimeout(() => {
|
||||
wx.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 自定义头部 */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 44px;
|
||||
background-color: #1989fa;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
/* 适配刘海屏 */
|
||||
padding-top: env(safe-area-inset-top, 0);
|
||||
height: calc(44px + env(safe-area-inset-top, 0));
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
background-color: #fff;
|
||||
margin-bottom: 10rpx;
|
||||
padding: 30rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 120rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
height: 60rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
/* 头像样式 */
|
||||
.avatar-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.avatar-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 保存按钮 */
|
||||
.save-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background-color: #1989fa;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
border-radius: 44rpx;
|
||||
margin-top: 40rpx;
|
||||
/* 去除默认边框和背景 */
|
||||
border: none;
|
||||
}
|
||||
|
||||
.save-btn:active {
|
||||
background-color: #0d75d4;
|
||||
}
|
||||
</style>
|
||||
805
scoring/pages/index/singleplay/scoring.vue
Normal file
805
scoring/pages/index/singleplay/scoring.vue
Normal file
@@ -0,0 +1,805 @@
|
||||
<template>
|
||||
<view class="scoring-page">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<uni-icons type="arrow-left" size="24" color="#fff"></uni-icons>
|
||||
</view>
|
||||
<view class="title">第{{ roundCount }}局</view>
|
||||
<view class="header-right">
|
||||
<view class="more-btn" @click="showMoreOptions">...</view>
|
||||
<view class="separator"></view>
|
||||
<view class="sound-toggle" @click="toggleSound">
|
||||
<text class="sound-icon">{{ soundEnabled ? '🔊' : '🔇' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 玩家列表区域 -->
|
||||
<view class="players-list">
|
||||
<!-- 表头 -->
|
||||
<view class="list-header">
|
||||
<view class="col-player">玩家</view>
|
||||
<view class="col-result">胜负</view>
|
||||
<view class="col-score">得分</view>
|
||||
</view>
|
||||
|
||||
<!-- 玩家行 -->
|
||||
<view
|
||||
v-for="(player, index) in players"
|
||||
:key="player.id"
|
||||
class="player-row"
|
||||
:class="{ 'selected': selectedPlayerIndex === index }"
|
||||
@click="selectPlayer(index)"
|
||||
>
|
||||
<view class="col-player">
|
||||
<image :src="player.avatar" mode="aspectFill" class="player-avatar"></image>
|
||||
<text class="player-name">{{ player.name }}</text>
|
||||
</view>
|
||||
|
||||
<view class="col-result">
|
||||
<view class="result-buttons">
|
||||
<button
|
||||
class="result-btn win"
|
||||
:class="{ 'active': player.result === 'win' }"
|
||||
@click.stop="setPlayerResult(index, 'win')"
|
||||
>胜</button>
|
||||
<button
|
||||
class="result-btn lose"
|
||||
:class="{ 'active': player.result === 'lose' }"
|
||||
@click.stop="setPlayerResult(index, 'lose')"
|
||||
>负</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="col-score">
|
||||
<view class="score-input-container">
|
||||
<input
|
||||
type="number"
|
||||
class="score-input"
|
||||
:value="selectedPlayerIndex === index && showKeyboard ? currentInput : player.score"
|
||||
@focus.prevent
|
||||
@click.stop="selectPlayer(index)"
|
||||
readonly
|
||||
disabled
|
||||
/>
|
||||
<!-- 合分按钮 -->
|
||||
<button
|
||||
class="combine-btn"
|
||||
@click.stop="combineScore(index)"
|
||||
:disabled="!selectedPlayerIndex === index"
|
||||
>合分</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部数字键盘 -->
|
||||
<view class="number-keyboard" v-if="showKeyboard">
|
||||
<view class="keyboard-row">
|
||||
<view class="key" @click="inputNumber(1)">1</view>
|
||||
<view class="key" @click="inputNumber(2)">2</view>
|
||||
<view class="key" @click="inputNumber(3)">3</view>
|
||||
<view class="key operation" @click="inputOperation('+')">+</view>
|
||||
<view class="key delete" @click="deleteLastChar">
|
||||
<text>⌫</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="keyboard-row">
|
||||
<view class="key" @click="inputNumber(4)">4</view>
|
||||
<view class="key" @click="inputNumber(5)">5</view>
|
||||
<view class="key" @click="inputNumber(6)">6</view>
|
||||
<view class="key operation" @click="inputOperation('-')">-</view>
|
||||
<view class="key empty"></view>
|
||||
</view>
|
||||
<view class="keyboard-row">
|
||||
<view class="key" @click="inputNumber(7)">7</view>
|
||||
<view class="key" @click="inputNumber(8)">8</view>
|
||||
<view class="key" @click="inputNumber(9)">9</view>
|
||||
<view class="key operation" @click="clearInput">C</view>
|
||||
<view class="key confirm" @click="confirmInput">提交</view>
|
||||
</view>
|
||||
<view class="keyboard-row">
|
||||
<view class="key zero" @click="inputNumber(0)">0</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-actions" v-if="!showKeyboard">
|
||||
<button class="action-btn" type="default" @click="nextRound">下一局</button>
|
||||
<button class="action-btn primary" type="primary" @click="endGame">结束对局</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
// 从上个页面接收的玩家数据
|
||||
const players = ref([])
|
||||
const roundCount = ref(1)
|
||||
const history = ref([])
|
||||
const selectedPlayerIndex = ref(-1)
|
||||
const showKeyboard = ref(false)
|
||||
const currentInput = ref('')
|
||||
const soundEnabled = ref(true)
|
||||
|
||||
// 初始化玩家数据
|
||||
const loadPlayersData = () => {
|
||||
try {
|
||||
const playersData = uni.getStorageSync('currentPlayers')
|
||||
if (playersData) {
|
||||
// 解析并添加result字段
|
||||
const parsedPlayers = JSON.parse(playersData).map(player => ({
|
||||
...player,
|
||||
result: '', // 初始胜负状态为空
|
||||
score: player.score || 0
|
||||
}))
|
||||
players.value = parsedPlayers
|
||||
saveHistoryState()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载玩家数据失败:', error)
|
||||
uni.showToast({
|
||||
title: '加载数据失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 保存历史状态
|
||||
const saveHistoryState = () => {
|
||||
const stateCopy = JSON.parse(JSON.stringify(players.value))
|
||||
history.value.push(stateCopy)
|
||||
if (history.value.length > 50) {
|
||||
history.value.shift()
|
||||
}
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '当前分数未保存,确定要返回吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选择玩家
|
||||
const selectPlayer = (index) => {
|
||||
selectedPlayerIndex.value = index
|
||||
showKeyboard.value = true
|
||||
// 显示当前分数
|
||||
currentInput.value = players.value[index].score.toString()
|
||||
}
|
||||
|
||||
// 设置玩家胜负
|
||||
const setPlayerResult = (index, result) => {
|
||||
// 如果已经是当前状态,则取消选择
|
||||
if (players.value[index].result === result) {
|
||||
players.value[index].result = ''
|
||||
// 取消状态时,保持当前分数符号不变
|
||||
} else {
|
||||
players.value[index].result = result
|
||||
|
||||
// 根据胜负状态调整分数正负号,并考虑当前输入框的内容
|
||||
let currentScore = players.value[index].score
|
||||
let currentInputValue = currentInput.value
|
||||
|
||||
if (result === 'win') {
|
||||
// 胜利时确保分数为正数
|
||||
if (currentScore !== 0) {
|
||||
// 根据输入框当前内容或分数值判断是否需要调整符号
|
||||
if (currentInputValue && currentInputValue !== '-' && parseInt(currentInputValue) !== 0) {
|
||||
// 使用输入框的值并确保为正数
|
||||
currentScore = Math.abs(parseInt(currentInputValue))
|
||||
} else {
|
||||
// 使用现有分数值并确保为正数
|
||||
currentScore = Math.abs(currentScore)
|
||||
}
|
||||
}
|
||||
} else if (result === 'lose') {
|
||||
// 失败时确保分数为负数
|
||||
if (currentScore !== 0) {
|
||||
// 根据输入框当前内容或分数值判断是否需要调整符号
|
||||
if (currentInputValue && currentInputValue !== '-' && parseInt(currentInputValue) !== 0) {
|
||||
// 使用输入框的值并确保为负数
|
||||
currentScore = -Math.abs(parseInt(currentInputValue))
|
||||
} else {
|
||||
// 使用现有分数值并确保为负数
|
||||
currentScore = -Math.abs(currentScore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新分数
|
||||
players.value[index].score = currentScore
|
||||
}
|
||||
|
||||
// 如果当前正在编辑该玩家,同步更新currentInput
|
||||
if (selectedPlayerIndex.value === index && showKeyboard.value) {
|
||||
currentInput.value = players.value[index].score.toString()
|
||||
}
|
||||
|
||||
saveHistoryState()
|
||||
}
|
||||
|
||||
// 数字键盘输入
|
||||
const inputNumber = (num) => {
|
||||
// 处理特殊情况:如果当前是负号,直接添加数字
|
||||
if (currentInput.value === '-') {
|
||||
currentInput.value += num.toString()
|
||||
}
|
||||
// 避免以0开头的数字(除了单独的0和负数中的0)
|
||||
else if (currentInput.value === '0' && num !== 0) {
|
||||
currentInput.value = num.toString()
|
||||
}
|
||||
// 避免输入过多的数字
|
||||
else if (currentInput.value.length < 10) {
|
||||
currentInput.value += num.toString()
|
||||
}
|
||||
|
||||
// 实时保存输入内容到玩家分数(不等待提交)
|
||||
if (selectedPlayerIndex.value !== -1) {
|
||||
let score = 0
|
||||
if (currentInput.value && currentInput.value !== '-') {
|
||||
score = parseInt(currentInput.value)
|
||||
if (isNaN(score)) {
|
||||
score = 0
|
||||
}
|
||||
}
|
||||
players.value[selectedPlayerIndex.value].score = score
|
||||
saveHistoryState()
|
||||
}
|
||||
}
|
||||
|
||||
// 操作符输入
|
||||
const inputOperation = (op) => {
|
||||
if (op === '-') {
|
||||
// 支持负数输入
|
||||
if (currentInput.value === '') {
|
||||
// 当输入框为空时,按减号开始负数输入
|
||||
currentInput.value = '-'
|
||||
} else if (currentInput.value.startsWith('-')) {
|
||||
// 如果已经是负数,移除负号变为正数
|
||||
currentInput.value = currentInput.value.substring(1)
|
||||
} else {
|
||||
// 如果已有数字,添加负号变为负数
|
||||
currentInput.value = '-' + currentInput.value
|
||||
}
|
||||
} else if (op === '+') {
|
||||
// 按加号确保是正数(移除负号)
|
||||
if (currentInput.value.startsWith('-')) {
|
||||
currentInput.value = currentInput.value.substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
// 实时保存操作符结果到玩家分数
|
||||
if (selectedPlayerIndex.value !== -1) {
|
||||
let score = 0
|
||||
if (currentInput.value && currentInput.value !== '-') {
|
||||
score = parseInt(currentInput.value)
|
||||
if (isNaN(score)) {
|
||||
score = 0
|
||||
}
|
||||
}
|
||||
players.value[selectedPlayerIndex.value].score = score
|
||||
saveHistoryState()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除最后一个字符
|
||||
const deleteLastChar = () => {
|
||||
if (currentInput.value.length > 0) {
|
||||
currentInput.value = currentInput.value.slice(0, -1)
|
||||
|
||||
// 实时保存删除后的内容到玩家分数
|
||||
if (selectedPlayerIndex.value !== -1) {
|
||||
let score = 0
|
||||
if (currentInput.value && currentInput.value !== '-') {
|
||||
score = parseInt(currentInput.value)
|
||||
if (isNaN(score)) {
|
||||
score = 0
|
||||
}
|
||||
}
|
||||
players.value[selectedPlayerIndex.value].score = score
|
||||
saveHistoryState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
currentInput.value = ''
|
||||
|
||||
// 实时保存清空操作到玩家分数
|
||||
if (selectedPlayerIndex.value !== -1) {
|
||||
players.value[selectedPlayerIndex.value].score = 0
|
||||
saveHistoryState()
|
||||
}
|
||||
}
|
||||
|
||||
// 确认输入
|
||||
const confirmInput = () => {
|
||||
if (selectedPlayerIndex.value !== -1) {
|
||||
// 播放音效
|
||||
if (soundEnabled.value) {
|
||||
playSound()
|
||||
}
|
||||
|
||||
// 隐藏键盘
|
||||
showKeyboard.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 合分功能
|
||||
const combineScore = (index) => {
|
||||
// 计算其他玩家分数的总和
|
||||
const otherPlayersTotal = players.value.reduce((sum, player, i) => {
|
||||
if (i !== index) {
|
||||
return sum + player.score
|
||||
}
|
||||
return sum
|
||||
}, 0)
|
||||
|
||||
// 设置被点击玩家的分数为其他玩家分数总和的相反数,确保所有分数相加为零
|
||||
players.value[index].score = -otherPlayersTotal
|
||||
|
||||
// 如果当前正在编辑该玩家,同步更新currentInput
|
||||
if (selectedPlayerIndex.value === index && showKeyboard.value) {
|
||||
currentInput.value = players.value[index].score.toString()
|
||||
}
|
||||
|
||||
saveHistoryState()
|
||||
|
||||
uni.showToast({
|
||||
title: '合分成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
// 下一局
|
||||
const nextRound = () => {
|
||||
uni.showModal({
|
||||
title: '确认',
|
||||
content: '确定要开始下一局吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 保存当前状态
|
||||
saveHistoryState()
|
||||
|
||||
// 重置分数和胜负状态
|
||||
players.value.forEach(player => {
|
||||
player.score = 0
|
||||
player.result = ''
|
||||
})
|
||||
|
||||
roundCount.value++
|
||||
uni.showToast({
|
||||
title: `第${roundCount.value}局`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 播放音效
|
||||
const playSound = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
const innerAudioContext = uni.createInnerAudioContext()
|
||||
innerAudioContext.src = '/static/sound/click.mp3' // 需要准备音效文件
|
||||
innerAudioContext.play()
|
||||
innerAudioContext.onEnded(() => {
|
||||
innerAudioContext.destroy()
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 切换音效
|
||||
const toggleSound = () => {
|
||||
soundEnabled.value = !soundEnabled.value
|
||||
uni.setStorageSync('soundEnabled', soundEnabled.value)
|
||||
}
|
||||
|
||||
// 显示更多选项
|
||||
const showMoreOptions = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['历史记录', '设置', '帮助'],
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0:
|
||||
uni.navigateTo({ url: '/pages/history-game/index' })
|
||||
break
|
||||
case 1:
|
||||
uni.showToast({ title: '设置功能开发中', icon: 'none' })
|
||||
break
|
||||
case 2:
|
||||
uni.showToast({ title: '帮助文档开发中', icon: 'none' })
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 结束对局
|
||||
const endGame = () => {
|
||||
uni.showModal({
|
||||
title: '确认',
|
||||
content: '确定要结束当前对局吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
// 保存最终分数
|
||||
uni.setStorageSync('updatedPlayers', JSON.stringify(players.value))
|
||||
|
||||
// 触发全局事件通知其他页面
|
||||
uni.$emit('updatePlayers')
|
||||
|
||||
uni.showToast({
|
||||
title: '对局已结束',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
success: () => {
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('结束对局失败:', error)
|
||||
uni.showToast({
|
||||
title: '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
console.log('计分页面加载完成')
|
||||
loadPlayersData()
|
||||
|
||||
// 加载音效设置
|
||||
const savedSound = uni.getStorageSync('soundEnabled')
|
||||
if (savedSound !== '') {
|
||||
soundEnabled.value = savedSound
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理工作
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" scoped>
|
||||
.scoring-page {
|
||||
width: 750rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #ifdef MP-WEIXIN */
|
||||
user-select: none;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 90rpx;
|
||||
background-color: #3a68b9;
|
||||
color: white;
|
||||
padding: 0 30rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
font-size: 36rpx;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.back-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.more-btn,
|
||||
.sound-toggle {
|
||||
font-size: 36rpx;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.separator {
|
||||
width: 2rpx;
|
||||
height: 40rpx;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
|
||||
/* 玩家列表区域 */
|
||||
.players-list {
|
||||
flex: 1;
|
||||
background-color: #fff;
|
||||
margin: 20rpx;
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 列表表头 */
|
||||
.list-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 80rpx;
|
||||
background-color: #f0f0f0;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.col-player,
|
||||
.col-result,
|
||||
.col-score {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.col-player {
|
||||
flex: 2;
|
||||
justify-content: flex-start;
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
|
||||
.col-result {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.col-score {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 玩家行 */
|
||||
.player-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.player-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.player-row.selected {
|
||||
background-color: #e6f0ff;
|
||||
}
|
||||
|
||||
.player-row .col-player {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.player-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 胜负按钮 */
|
||||
.result-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.result-btn {
|
||||
width: 60rpx;
|
||||
height: 40rpx;
|
||||
line-height: 40rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
|
||||
.result-btn.win.active {
|
||||
background-color: #4cd964;
|
||||
color: white;
|
||||
border-color: #4cd964;
|
||||
}
|
||||
|
||||
.result-btn.lose.active {
|
||||
background-color: #ff3b30;
|
||||
color: white;
|
||||
border-color: #ff3b30;
|
||||
}
|
||||
|
||||
/* 分数输入区域 */
|
||||
.score-input-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.score-input {
|
||||
width: 100rpx;
|
||||
height: 50rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.combine-btn {
|
||||
position: absolute;
|
||||
bottom: -40rpx;
|
||||
width: 80rpx;
|
||||
height: 30rpx;
|
||||
line-height: 30rpx;
|
||||
font-size: 20rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: #34aadc;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
/* 数字键盘 */
|
||||
.number-keyboard {
|
||||
width: 100%;
|
||||
background-color: #f0f0f0;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.keyboard-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.keyboard-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.key {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: white;
|
||||
border-radius: 10rpx;
|
||||
margin: 0 10rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.key:active {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.key.zero {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.key.operation {
|
||||
background-color: #ff9500;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.key.delete {
|
||||
background-color: #ff3b30;
|
||||
color: white;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 80rpx; /* 确保按钮有足够宽度 */
|
||||
}
|
||||
|
||||
.key.delete img {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
object-fit: contain;
|
||||
transition: transform 0.1s ease, opacity 0.1s ease;
|
||||
/* #ifdef MP-WEIXIN */
|
||||
pointer-events: none; /* 防止图片在微信小程序中捕获点击事件 */
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.key.delete:active {
|
||||
background-color: #e03024; /* 点击时背景色变深 */
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.key.delete:active img {
|
||||
transform: scale(0.9);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.key.delete-icon {
|
||||
font-size: 36rpx;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.key.confirm {
|
||||
background-color: #34c759;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.key.empty {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bottom-actions {
|
||||
padding: 20rpx;
|
||||
background-color: white;
|
||||
border-top: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 32rpx;
|
||||
margin-bottom: 15rpx;
|
||||
border-radius: 40rpx;
|
||||
}
|
||||
|
||||
.action-btn:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background-color: #007aff;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
751
scoring/pages/index/singleplay/singleplay.vue
Normal file
751
scoring/pages/index/singleplay/singleplay.vue
Normal file
@@ -0,0 +1,751 @@
|
||||
<template>
|
||||
<view class="singleplay">
|
||||
<!-- 顶部功能区 -->
|
||||
<view class="top-functions">
|
||||
<view class="function-btn" @click="addPlayer">
|
||||
<image src="/static/add.png" class="btn-icon" mode="aspectFill"></image>
|
||||
<text>添加玩家</text>
|
||||
</view>
|
||||
<view class="function-btn" @click="transferScorer">
|
||||
<image src="/static/transfer.png" class="btn-icon" mode="aspectFill"></image>
|
||||
<text>转让计分员</text>
|
||||
</view>
|
||||
<view class="switch-group">
|
||||
<text>语音播报</text>
|
||||
<switch :checked="voiceBroadcast" @change="voiceBroadcast = $event.detail.value" color="#007aff" />
|
||||
</view>
|
||||
<view class="switch-group">
|
||||
<text>台板</text>
|
||||
<switch :checked="tableMode" @change="tableMode = $event.detail.value" color="#007aff" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 对局记录区域 -->
|
||||
<view class="game-record">
|
||||
<view class="record-title">
|
||||
对局记录
|
||||
<text class="hint">点击对局分数进行修改</text>
|
||||
</view>
|
||||
|
||||
<!-- 用户信息区域 -->
|
||||
<view class="user-section">
|
||||
<text class="user-hint">点击自己头像编辑用户头像和昵称~</text>
|
||||
<view class="user-info" @click="editUserInfo">
|
||||
<image class="user-avatar" :src="currentUser.avatar" mode="aspectFill"></image>
|
||||
<view class="user-details">
|
||||
<view class="user-name">
|
||||
{{ currentUser.name }}
|
||||
<text class="self-tag">自己</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 玩家列表 -->
|
||||
<view class="players-section">
|
||||
<view class="section-header">
|
||||
<text class="header-title">玩家</text>
|
||||
<text class="player-count">({{ players.length }}位)</text>
|
||||
</view>
|
||||
|
||||
<!-- 玩家表格 -->
|
||||
<view class="players-table">
|
||||
<view class="table-header">
|
||||
<view class="player-column">玩家</view>
|
||||
<view class="score-column">总分</view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-for="(player, index) in displayPlayers" :key="player.id" class="player-row">
|
||||
<view class="player-info">
|
||||
<image :src="player.avatar" mode="aspectFill"></image>
|
||||
<text>{{ player.name }}</text>
|
||||
</view>
|
||||
<view class="score-display" @click="editScore(index)">
|
||||
{{ player.score }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="bottom-buttons">
|
||||
<button class="start-btn" type="primary" @click="startScoring">开局计分</button>
|
||||
<button class="settle-btn" type="default" @click="settleRoom">结算房间</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
// 状态管理
|
||||
const voiceBroadcast = ref(false)
|
||||
const tableMode = ref(false)
|
||||
const roomId = ref('')
|
||||
const currentUser = ref({
|
||||
id: 'self',
|
||||
name: '玩家50950',
|
||||
avatar: 'https://t14.baidu.com/it/u=3165460156,649373630&fm=224&app=112&f=JPEG?w=500&h=500'
|
||||
})
|
||||
|
||||
// 玩家列表(初始包含自己)
|
||||
const players = ref([
|
||||
{
|
||||
id: 'self',
|
||||
name: '玩家50950',
|
||||
avatar: 'https://t14.baidu.com/it/u=3165460156,649373630&fm=224&app=112&f=JPEG?w=500&h=500',
|
||||
score: 0
|
||||
}
|
||||
])
|
||||
|
||||
// 台板玩家对象
|
||||
const tablePlayer = {
|
||||
id: 'table',
|
||||
name: '台板',
|
||||
avatar: '/static/robot.png',
|
||||
score: 0
|
||||
}
|
||||
|
||||
// 计算属性:动态返回包含或不包含台板玩家的列表
|
||||
const displayPlayers = computed(() => {
|
||||
if (tableMode.value) {
|
||||
// 检查台板玩家是否已存在,避免重复添加
|
||||
const hasTablePlayer = players.value.some(player => player.id === 'table')
|
||||
if (hasTablePlayer) {
|
||||
return players.value
|
||||
}
|
||||
// 添加台板玩家
|
||||
return [...players.value, tablePlayer]
|
||||
} else {
|
||||
// 移除台板玩家
|
||||
return players.value.filter(player => player.id !== 'table')
|
||||
}
|
||||
})
|
||||
|
||||
// 添加玩家
|
||||
const addPlayer = () => {
|
||||
console.log('添加玩家')
|
||||
|
||||
// 生成默认玩家名
|
||||
const defaultPlayerName = '请输入玩家名称'
|
||||
|
||||
// 弹出对话框让用户输入玩家名称
|
||||
uni.showModal({
|
||||
title: '添加玩家',
|
||||
editable: true,
|
||||
placeholderText: defaultPlayerName,
|
||||
// #ifdef MP-WEIXIN
|
||||
confirmText: '添加',
|
||||
cancelText: '取消',
|
||||
autofocus: true, // 微信小程序下自动聚焦输入框
|
||||
// #endif
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 获取用户输入的名称,如果为空则使用默认名称
|
||||
const playerName = res.content && res.content.trim() !== '' ? res.content.trim() : defaultPlayerName
|
||||
|
||||
// 创建新玩家
|
||||
const newPlayer = {
|
||||
id: `player_${Date.now()}`,
|
||||
name: playerName,
|
||||
avatar: '/static/people.png',
|
||||
score: 0
|
||||
}
|
||||
|
||||
// 添加到玩家列表
|
||||
players.value.push(newPlayer)
|
||||
|
||||
// 语音提示或普通提示(如果开启)
|
||||
if (voiceBroadcast.value) {
|
||||
uni.showToast({
|
||||
title: `已添加玩家 ${newPlayer.name}`,
|
||||
icon: 'none'
|
||||
})
|
||||
// 这里可以添加语音播报逻辑
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: `已添加玩家 ${newPlayer.name}`,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('添加玩家失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 转让计分员
|
||||
const transferScorer = () => {
|
||||
console.log('转让计分员')
|
||||
|
||||
// 显示提示对话框
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '房间内暂无扫码或分享加入房间的玩家,无法转让计分员。',
|
||||
showCancel: false,
|
||||
// #ifdef MP-WEIXIN
|
||||
confirmText: '确定',
|
||||
// #endif
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
console.log('用户确认提示')
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('显示提示失败:', err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑用户信息 - 跳转到change页面
|
||||
const editUserInfo = () => {
|
||||
console.log('跳转到用户信息编辑页面')
|
||||
|
||||
// 跳转到change页面,并传递当前用户信息
|
||||
uni.navigateTo({
|
||||
url: `/pages/index/singleplay/change?userData=${encodeURIComponent(JSON.stringify(currentUser.value))}`
|
||||
})
|
||||
}
|
||||
|
||||
// 更新用户数据 - 从change页面返回时调用
|
||||
const updateUserData = (updatedUser) => {
|
||||
console.log('更新用户数据', updatedUser)
|
||||
|
||||
// 更新当前用户信息
|
||||
currentUser.value = { ...updatedUser }
|
||||
|
||||
// 更新玩家列表中的用户信息
|
||||
const selfPlayerIndex = players.value.findIndex(p => p.id === 'self')
|
||||
if (selfPlayerIndex !== -1) {
|
||||
players.value[selfPlayerIndex] = {
|
||||
...players.value[selfPlayerIndex],
|
||||
name: updatedUser.name,
|
||||
avatar: updatedUser.avatar
|
||||
}
|
||||
}
|
||||
|
||||
// 当启用台板模式时,确保台板玩家信息正确
|
||||
if (tableMode.value) {
|
||||
const tablePlayerIndex = players.value.findIndex(p => p.id === 'table')
|
||||
if (tablePlayerIndex === -1) {
|
||||
// 如果需要,重新添加台板玩家(这通常不会发生,因为计算属性会处理)
|
||||
players.value.push(tablePlayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 头像和昵称编辑功能已迁移到change页面
|
||||
|
||||
// 编辑分数
|
||||
const editScore = (index) => {
|
||||
console.log('编辑分数', index)
|
||||
const player = displayPlayers.value[index]
|
||||
|
||||
// 检查是否是台板玩家
|
||||
if (player.id === 'table') {
|
||||
// 台板玩家的分数编辑逻辑
|
||||
uni.showModal({
|
||||
title: '修改台板分数',
|
||||
content: `当前分数: ${player.score}`,
|
||||
editable: true,
|
||||
placeholderText: '请输入新分数',
|
||||
success: (res) => {
|
||||
if (res.confirm && res.content !== null) {
|
||||
const newScore = parseInt(res.content)
|
||||
if (!isNaN(newScore)) {
|
||||
// 找到台板玩家在原始数组中的位置
|
||||
const tableIndex = players.value.findIndex(p => p.id === 'table')
|
||||
if (tableIndex !== -1) {
|
||||
players.value[tableIndex].score = newScore
|
||||
} else {
|
||||
// 如果台板玩家不在原始数组中,创建一个副本并更新
|
||||
tablePlayer.score = newScore
|
||||
}
|
||||
|
||||
// 语音播报分数(如果开启)
|
||||
if (voiceBroadcast.value) {
|
||||
console.log(`语音播报: 台板 分数更新为 ${newScore}`)
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请输入有效的数字',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 普通玩家的分数编辑逻辑
|
||||
uni.showModal({
|
||||
title: '修改分数',
|
||||
content: `${player.score}`,
|
||||
editable: true,
|
||||
placeholderText: '请输入新分数',
|
||||
success: (res) => {
|
||||
if (res.confirm && res.content !== null) {
|
||||
const newScore = parseInt(res.content)
|
||||
if (!isNaN(newScore)) {
|
||||
// 找到对应的普通玩家
|
||||
const playerIndex = players.value.findIndex(p => p.id === player.id)
|
||||
if (playerIndex !== -1) {
|
||||
players.value[playerIndex].score = newScore
|
||||
}
|
||||
|
||||
// 语音播报分数(如果开启)
|
||||
if (voiceBroadcast.value) {
|
||||
console.log(`语音播报: ${player.name} 分数更新为 ${newScore}`)
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '请输入有效的数字',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 开局计分
|
||||
const startScoring = () => {
|
||||
console.log('开局计分')
|
||||
|
||||
// 检查是否有足够的玩家(至少需要2个玩家才能开始计分)
|
||||
if (displayPlayers.value.length < 2) {
|
||||
uni.showToast({
|
||||
title: '玩家数不超过两个人时无法点击进行开始计分',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 重置所有玩家分数
|
||||
players.value.forEach(player => {
|
||||
player.score = 0
|
||||
})
|
||||
|
||||
// 重置台板玩家分数(即使不在原始数组中)
|
||||
if (tableMode.value) {
|
||||
tablePlayer.score = 0
|
||||
}
|
||||
|
||||
// 准备要传递的玩家数据
|
||||
const playersToPass = [...displayPlayers.value]
|
||||
|
||||
// 保存到临时存储,用于页面间数据传递
|
||||
uni.setStorageSync('currentPlayers', JSON.stringify(playersToPass))
|
||||
|
||||
// 跳转到计分页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/singleplay/scoring'
|
||||
})
|
||||
}
|
||||
|
||||
// 结算房间
|
||||
const settleRoom = () => {
|
||||
console.log('结算房间')
|
||||
|
||||
// 显示结算结果,使用displayPlayers确保包含台板玩家
|
||||
let result = '结算结果:\n'
|
||||
displayPlayers.value.forEach(player => {
|
||||
result += `${player.name}: ${player.score}分\n`
|
||||
})
|
||||
|
||||
uni.showModal({
|
||||
title: '结算',
|
||||
content: result,
|
||||
confirmText: '保存记录',
|
||||
cancelText: '关闭',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 这里可以保存记录到数据库
|
||||
uni.showToast({
|
||||
title: '记录已保存',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
console.log('单人模式页面加载完成')
|
||||
// 可以在这里初始化数据或加载用户信息
|
||||
|
||||
// 生成随机房间号(4位数字)
|
||||
roomId.value = Math.floor(1000 + Math.random() * 9000).toString()
|
||||
|
||||
// 动态设置导航栏标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: `单人 - ${roomId.value}号房间`
|
||||
})
|
||||
|
||||
// 监听页面返回事件,用于接收从计分页面传回的更新后的分数
|
||||
const updateListener = () => {
|
||||
// 尝试获取更新后的玩家数据
|
||||
const updatedPlayers = uni.getStorageSync('updatedPlayers')
|
||||
if (updatedPlayers) {
|
||||
try {
|
||||
const parsedPlayers = JSON.parse(updatedPlayers)
|
||||
console.log('收到更新的玩家数据:', parsedPlayers)
|
||||
|
||||
// 更新玩家列表中的分数
|
||||
parsedPlayers.forEach(updatedPlayer => {
|
||||
// 查找对应的玩家
|
||||
const playerIndex = players.value.findIndex(p => p.id === updatedPlayer.id)
|
||||
if (playerIndex !== -1) {
|
||||
// 更新普通玩家的分数
|
||||
players.value[playerIndex].score = updatedPlayer.score
|
||||
} else if (updatedPlayer.id === 'table') {
|
||||
// 更新台板玩家的分数
|
||||
tablePlayer.score = updatedPlayer.score
|
||||
}
|
||||
})
|
||||
|
||||
// 清除临时存储的数据
|
||||
uni.removeStorageSync('updatedPlayers')
|
||||
} catch (error) {
|
||||
console.error('解析更新的玩家数据失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听页面显示事件
|
||||
uni.$on('updatePlayers', updateListener)
|
||||
|
||||
// 监听页面显示生命周期
|
||||
uni.onShow(() => {
|
||||
updateListener()
|
||||
})
|
||||
|
||||
// 组件卸载时移除监听
|
||||
onUnmounted(() => {
|
||||
uni.$off('updatePlayers', updateListener)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.singleplay {
|
||||
width: 750rpx;
|
||||
margin: 0;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: #f8f8f8;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
user-select: none;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* 顶部功能区 */
|
||||
.top-functions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
//padding: 10rpx 15rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-x: auto;
|
||||
|
||||
.function-btn {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 5rpx;
|
||||
padding: 8rpx 8rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
margin: 0 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
|
||||
// 图标样式
|
||||
.btn-icon {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
}
|
||||
|
||||
// 文本样式
|
||||
text {
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// 微信小程序特有点击反馈
|
||||
/* #ifdef MP-WEIXIN */
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.switch-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
margin: 0 8rpx;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
|
||||
text {
|
||||
font-size: 22rpx;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
switch {
|
||||
transform: scale(0.75);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 对局记录区域 */
|
||||
.game-record {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 30rpx;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.record-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.hint {
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 用户信息区域 */
|
||||
.user-section {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.user-hint {
|
||||
font-size: 24rpx;
|
||||
color: #fa5d5d;
|
||||
margin-bottom: 15rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 15rpx;
|
||||
padding: 10rpx;
|
||||
border-radius: 10rpx;
|
||||
background-color: #f0f8ff;
|
||||
|
||||
.user-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
.user-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
|
||||
.self-tag {
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 玩家列表区域 */
|
||||
.players-section {
|
||||
.section-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 15rpx;
|
||||
|
||||
.header-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.player-count {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 玩家表格 */
|
||||
.players-table {
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
|
||||
.player-column {
|
||||
flex: 2;
|
||||
padding: 15rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
border-right: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.score-column {
|
||||
flex: 1;
|
||||
padding: 15rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.table-body {
|
||||
.player-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.player-info {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 15rpx;
|
||||
padding: 15rpx;
|
||||
border-right: 1rpx solid #e0e0e0;
|
||||
|
||||
image {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.score-display {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #007aff;
|
||||
|
||||
&:active {
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部操作按钮 */
|
||||
.bottom-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20rpx;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 20rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
/* #ifdef MP-WEIXIN */
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
/* #endif */
|
||||
|
||||
.start-btn,
|
||||
.settle-btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 90rpx;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
background-color: #007aff;
|
||||
}
|
||||
|
||||
.settle-btn {
|
||||
border: 1rpx solid #007aff;
|
||||
color: #007aff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 适配不同屏幕尺寸 */
|
||||
@media screen and (max-width: 375px) {
|
||||
.singleplay {
|
||||
padding: 15rpx;
|
||||
}
|
||||
|
||||
.top-functions {
|
||||
gap: 8rpx;
|
||||
|
||||
.function-btn {
|
||||
padding: 1rpx 1rpx;
|
||||
|
||||
text {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
191
scoring/pages/my/favorites.vue
Normal file
191
scoring/pages/my/favorites.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<view class="history-game">
|
||||
<view class="game-list">
|
||||
<view class="game-item" v-for="item in 2" :key="item">
|
||||
<view class="game-info">
|
||||
<view class="game-type">单人-1237房间</view>
|
||||
<view class="game-date">2025-06-12 12:00</view>
|
||||
<view class="del-btn">
|
||||
<uni-icons type="trash" size="30"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="game-user">
|
||||
<view class="game-user-item" v-for="(user, index) in userList" :key="index">
|
||||
<view class="user-avatar">
|
||||
<image :src="user.avatar" mode="aspectFill"></image>
|
||||
</view>
|
||||
<view class="user-name">{{user.name}}</view>
|
||||
<view class="user-score">{{user.score}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="game-foot">
|
||||
<view class="game-status">
|
||||
已结束
|
||||
</view>
|
||||
<view class="check-detail">
|
||||
<uni-icons type="right" size="25" color="#748cec"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, getCurrentInstance } from 'vue'
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
||||
const proxy = getCurrentInstance().proxy;
|
||||
|
||||
// 过滤相关
|
||||
const filterActive = ref('all'); // all:全部,win:胜场,lose:负场
|
||||
const filterClick = (type) => {
|
||||
filterActive.value = type;
|
||||
};
|
||||
|
||||
|
||||
// 参与人
|
||||
const userList = ref([
|
||||
{
|
||||
avatar: 'https://q3.itc.cn/q_70/images03/20250110/1e71eecf56b34344bcae6a5b85c0bec2.jpeg',
|
||||
name: '赵云',
|
||||
score: 100
|
||||
},
|
||||
{
|
||||
avatar: 'https://q1.itc.cn/q_70/images03/20241119/197701bb9ef34b20b6497720081a9972.jpeg',
|
||||
name: '张飞',
|
||||
score: -50
|
||||
},
|
||||
{
|
||||
avatar: 'https://img1.baidu.com/it/u=3612220943,2414740890&fm=253&app=138&f=JPEG?w=526&h=500',
|
||||
name: '刘备',
|
||||
score: 30
|
||||
},
|
||||
{
|
||||
avatar: 'https://c-ssl.dtstatic.com/uploads/blog/202206/12/20220612135738_992b1.thumb.1000_0.jpg',
|
||||
name: '诸葛亮',
|
||||
score: -80
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.history-game {
|
||||
width: 750rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.filter-tab {
|
||||
--filter-height: 80rpx;
|
||||
--filter-color: #748cec;
|
||||
--filter-border: 1rpx;
|
||||
width: 700rpx;
|
||||
height: var(--filter-height);
|
||||
margin: 20rpx auto;
|
||||
display: flex;
|
||||
.filter-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
line-height: calc(var(--filter-height) - 2 * var(--filter-border));
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
border: var(--filter-border) solid var(--filter-color);
|
||||
}
|
||||
.filter-active {
|
||||
background-color: var(--filter-color);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.game-list {
|
||||
width: 700rpx;
|
||||
margin: 0 auto;
|
||||
.game-item {
|
||||
width: 100%;
|
||||
background-color: #ffffff;
|
||||
margin: 20rpx 0;
|
||||
box-shadow: 0 0 10rpx 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
.game-info {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.game-type {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
.game-date {
|
||||
font-size: 32rpx;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
.del-btn {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-left: 1rpx solid #cccccc;
|
||||
}
|
||||
}
|
||||
.game-user {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
overflow: auto;
|
||||
padding: 20rpx 0;
|
||||
.game-user-item {
|
||||
width: 140rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
.user-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 10%;
|
||||
overflow: hidden;
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.user-name {
|
||||
font-size: 32rpx;
|
||||
color: #000000;
|
||||
}
|
||||
.user-score {
|
||||
font-size: 32rpx;
|
||||
color: #fa5d5d;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.game-foot {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.game-status {
|
||||
font-size: 32rpx;
|
||||
color: #57bcef;
|
||||
}
|
||||
.check-detail {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
537
scoring/pages/my/feedback.vue
Normal file
537
scoring/pages/my/feedback.vue
Normal file
@@ -0,0 +1,537 @@
|
||||
<template>
|
||||
<view class="feedback-page">
|
||||
|
||||
|
||||
<view class="feedback-content">
|
||||
<!-- 常见问题 -->
|
||||
<view class="section">
|
||||
<view class="section-title">常见问题</view>
|
||||
<view class="faq-list">
|
||||
<view class="faq-item" v-for="(faq, index) in faqList" :key="index" @click="toggleFaq(index)">
|
||||
<view class="faq-header">
|
||||
<text class="faq-question">{{faq.question}}</text>
|
||||
<uni-icons
|
||||
:type="expandedIndex === index ? 'up' : 'down'"
|
||||
size="24"
|
||||
color="#999"
|
||||
></uni-icons>
|
||||
</view>
|
||||
<view class="faq-answer" v-if="expandedIndex === index">
|
||||
<text>{{faq.answer}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 问题类型选择 -->
|
||||
<view class="section" v-if="!showSubmitForm">
|
||||
<view class="section-title">问题类型</view>
|
||||
<view class="problem-types">
|
||||
<view
|
||||
class="problem-type"
|
||||
v-for="(type, index) in problemTypes"
|
||||
:key="index"
|
||||
@click="selectProblemType(type)"
|
||||
>
|
||||
<view class="type-icon">
|
||||
<image :src="type.img" alt="问题类型图标" class="type-icon-image" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="type-name">{{type.name}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 意见反馈表单 -->
|
||||
<view class="section" v-if="showSubmitForm">
|
||||
<view class="section-title">意见反馈</view>
|
||||
<view class="form-group">
|
||||
<view class="form-label">反馈内容</view>
|
||||
<textarea
|
||||
v-model="feedbackContent"
|
||||
class="feedback-textarea"
|
||||
placeholder="请详细描述您遇到的问题或建议"
|
||||
maxlength="500"
|
||||
rows="6"
|
||||
></textarea>
|
||||
<view class="word-count">{{feedbackContent.length}}/500</view>
|
||||
</view>
|
||||
|
||||
<!-- 图片上传 -->
|
||||
<view class="form-group">
|
||||
<view class="form-label">上传截图(选填)</view>
|
||||
<view class="image-upload">
|
||||
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
|
||||
<uni-icons type="plus" size="40" color="#999"></uni-icons>
|
||||
</view>
|
||||
<view class="image-item" v-for="(image, index) in images" :key="index">
|
||||
<image :src="image" mode="aspectFill" class="preview-image"></image>
|
||||
<view class="delete-btn" @click="deleteImage(index)">
|
||||
<uni-icons type="close" size="20" color="white"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="upload-text">最多上传3张图片,每张不超过5MB</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<view class="form-group">
|
||||
<view class="form-label">联系方式(选填)</view>
|
||||
<input
|
||||
v-model="contactInfo"
|
||||
class="contact-input"
|
||||
placeholder="请留下您的邮箱或手机号,方便我们联系您"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<button
|
||||
class="submit-btn"
|
||||
:disabled="!feedbackContent.trim()"
|
||||
@click="submitFeedback"
|
||||
>提交反馈</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系方式 -->
|
||||
<view class="contact-options">
|
||||
<view class="contact-item">
|
||||
<view class="contact-icon">
|
||||
<image src="/static/emile.png" alt="客服邮箱" class="contact-icon-image" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="contact-info">
|
||||
<view class="contact-label">客服邮箱</view>
|
||||
<view class="contact-value" @click="copyEmail">support@example.com</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="contact-item">
|
||||
<view class="contact-icon">
|
||||
<uni-icons type="chat" size="32" color="#4cd964"></uni-icons>
|
||||
</view>
|
||||
<view class="contact-info">
|
||||
<view class="contact-label">在线客服</view>
|
||||
<view class="contact-value">立即联系</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
// 响应式数据
|
||||
const expandedIndex = ref(-1)
|
||||
const showSubmitForm = ref(false)
|
||||
const selectedType = ref('')
|
||||
const feedbackContent = ref('')
|
||||
const images = ref([])
|
||||
const contactInfo = ref('')
|
||||
|
||||
// 常见问题数据
|
||||
const faqList = [
|
||||
{
|
||||
question: '如何添加新玩家?',
|
||||
answer: '在游戏开始页面,点击"添加玩家"按钮,输入玩家名称即可添加新玩家。您可以添加多个玩家参与计分。'
|
||||
},
|
||||
{
|
||||
question: '如何修改玩家分数?',
|
||||
answer: '点击玩家对应的分数区域,会弹出数字键盘,输入新的分数后点击"提交"即可更新玩家分数。'
|
||||
},
|
||||
{
|
||||
question: '如何查看历史对局记录?',
|
||||
answer: '在首页点击"历史记录"图标,进入历史记录页面即可查看所有保存的对局记录。'
|
||||
},
|
||||
{
|
||||
question: '如何清除应用缓存?',
|
||||
answer: '在"我的"页面,点击"设置",然后选择"清除缓存"选项即可清除应用缓存数据。'
|
||||
},
|
||||
{
|
||||
question: '计分数据会同步到云端吗?',
|
||||
answer: '目前计分数据仅保存在本地,不会自动同步到云端。如需备份,请定期导出数据。'
|
||||
}
|
||||
]
|
||||
|
||||
// 问题类型数据
|
||||
const problemTypes = [
|
||||
{
|
||||
name: '功能建议',
|
||||
img: '/static/advise.png',
|
||||
color: '#ffcc00'
|
||||
},
|
||||
{
|
||||
name: 'bug反馈',
|
||||
img: '/static/bug.png',
|
||||
color: '#ff3b30'
|
||||
},
|
||||
{
|
||||
name: '界面优化',
|
||||
img: '/static/optimize.png',
|
||||
color: '#34aadc'
|
||||
},
|
||||
{
|
||||
name: '其他问题',
|
||||
img: '/static/other.png',
|
||||
color: '#999999'
|
||||
}
|
||||
]
|
||||
|
||||
// 切换FAQ展开状态
|
||||
const toggleFaq = (index) => {
|
||||
expandedIndex.value = expandedIndex.value === index ? -1 : index
|
||||
}
|
||||
|
||||
// 选择问题类型
|
||||
const selectProblemType = (type) => {
|
||||
selectedType.value = type.name
|
||||
showSubmitForm.value = true
|
||||
}
|
||||
|
||||
// 选择图片
|
||||
const chooseImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 3 - images.value.length,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
images.value = [...images.value, ...res.tempFilePaths]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除图片
|
||||
const deleteImage = (index) => {
|
||||
images.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 提交反馈
|
||||
const submitFeedback = () => {
|
||||
if (!feedbackContent.value.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入反馈内容',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 模拟提交
|
||||
uni.showLoading({
|
||||
title: '提交中...'
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '反馈提交成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 重置表单
|
||||
feedbackContent.value = ''
|
||||
images.value = []
|
||||
contactInfo.value = ''
|
||||
showSubmitForm.value = false
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
// 移除多余的返回函数,使用默认的导航返回
|
||||
|
||||
// 复制邮箱
|
||||
const copyEmail = () => {
|
||||
uni.setClipboardData({
|
||||
data: 'support@example.com',
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: '邮箱已复制',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.feedback-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 简化的页面标题 */
|
||||
.page-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.feedback-content {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 常见问题样式 */
|
||||
.faq-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.faq-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.faq-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 25rpx 0;
|
||||
}
|
||||
|
||||
.faq-question {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.faq-answer {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
padding: 0 0 25rpx 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 问题类型样式 */
|
||||
.problem-types {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.problem-type {
|
||||
width: 45%;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 15rpx;
|
||||
padding: 30rpx 0;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.problem-type:active {
|
||||
background-color: #e8e8e8;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.type-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 15rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.type-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.form-group {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 15rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.feedback-textarea {
|
||||
width: 100%;
|
||||
height: 240rpx;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
background-color: #fafafa;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.word-count {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-align: right;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
/* 优化的图片上传样式 */
|
||||
.image-upload {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -10rpx;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
border: 2rpx dashed #007aff;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 10rpx;
|
||||
background-color: #f0f8ff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.upload-btn:active {
|
||||
background-color: #e0e8ff;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.image-item {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
position: relative;
|
||||
margin: 10rpx;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
background-color: rgba(255, 0, 0, 0.8);
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.contact-input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
border: 1rpx solid #e0e0e0;
|
||||
border-radius: 10rpx;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
margin: 40rpx 30rpx 20rpx;
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
border-radius: 40rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.submit-btn[disabled] {
|
||||
background-color: #cccccc;
|
||||
}
|
||||
|
||||
/* 联系方式样式 */
|
||||
.contact-options {
|
||||
padding: 0 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.contact-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
background-color: #f0f8ff;
|
||||
border-radius: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.contact-icon-image {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.contact-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.contact-value {
|
||||
font-size: 30rpx;
|
||||
color: #007aff;
|
||||
display: block;
|
||||
margin-top: 5rpx;
|
||||
}
|
||||
</style>
|
||||
495
scoring/pages/my/my.vue
Normal file
495
scoring/pages/my/my.vue
Normal file
@@ -0,0 +1,495 @@
|
||||
<template>
|
||||
<view class="my">
|
||||
<!-- 用户信息区域 -->
|
||||
<view class="user-info">
|
||||
<view class="background">
|
||||
|
||||
</view>
|
||||
<view class="info-content">
|
||||
<view class="top-user-name" :style="{'height': topHeight, 'line-height': topHeight}">
|
||||
{{userInfo.nickName}}
|
||||
</view>
|
||||
<view class="avatars">
|
||||
<image class="avatar-img" :src="userInfo.avatarUrl ? userInfo.avatarUrl : 'https://pic.rmb.bdstatic.com/bjh/down/1742bc3845cbbcf0c78c01eb59bb1c1a.jpeg'" @click="updateAvatar"></image>
|
||||
</view>
|
||||
<view class="hello-text">
|
||||
<p>你好,{{userInfo.nickName}}</p>
|
||||
<p>欢迎使用计分小程序</p>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 数据统计区域 -->
|
||||
<view class="data-section">
|
||||
<view class="data-item" @click="navigateToHistory">
|
||||
<text class="data-label">总对局数</text>
|
||||
<text class="data-number">{{totalGames || 0}}</text>
|
||||
</view>
|
||||
<view class="data-divider"></view>
|
||||
<view class="data-item" @click="navigateToHistory">
|
||||
<text class="data-label">胜利局数</text>
|
||||
<text class="data-number">{{totalWins || 0}}</text>
|
||||
</view>
|
||||
<view class="data-divider"></view>
|
||||
<view class="data-item" @click="navigateToHistory">
|
||||
<text class="data-label">胜率</text>
|
||||
<text class="data-number">{{winRate || '0%'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能菜单区域 -->
|
||||
<view class="menu-section">
|
||||
<!-- 第一组菜单 -->
|
||||
<view class="menu-group">
|
||||
|
||||
<view class="menu-item" @click="navigateToFavorites">
|
||||
<view class="menu-left">
|
||||
<image src="/static/favorites.png" alt="收藏的对局" class="menu-icon" mode="aspectFit"></image>
|
||||
<text class="menu-text">收藏的对局</text>
|
||||
</view>
|
||||
<uni-icons type="right" size="24" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 第二组菜单 -->
|
||||
<view class="menu-group">
|
||||
<view class="menu-item" @click="openSettings">
|
||||
<view class="menu-left">
|
||||
<image src="/static/setting.png" alt="设置" class="menu-icon" mode="aspectFit"></image>
|
||||
<text class="menu-text">设置</text>
|
||||
</view>
|
||||
<uni-icons type="right" size="24" color="#999"></uni-icons>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="openFeedback">
|
||||
<view class="menu-left">
|
||||
<image src="/static/feedback.png" alt="帮助与反馈" class="menu-icon" mode="aspectFit"></image>
|
||||
<text class="menu-text">帮助与反馈</text>
|
||||
</view>
|
||||
<uni-icons type="right" size="24" color="#999"></uni-icons>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" @click="aboutUs">
|
||||
<view class="menu-left">
|
||||
<image src="/static/about.png" alt="关于我们" class="menu-icon" mode="aspectFit"></image>
|
||||
<text class="menu-text">关于我们</text>
|
||||
</view>
|
||||
<uni-icons type="right" size="24" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 版本信息 -->
|
||||
<view class="version-info">
|
||||
<text>版本 {{version || '1.0.0'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted, ref, getCurrentInstance } from 'vue'
|
||||
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
|
||||
import { getOpenId } from '@/utils/wxutils.js'
|
||||
import { register, login } from '@/api/user.js'
|
||||
|
||||
const proxy = getCurrentInstance().proxy;
|
||||
|
||||
// 小程序胶囊高度
|
||||
const topHeight = ref(proxy.$StaticValue.getTopHeight());
|
||||
|
||||
// 用户信息相关
|
||||
const userInfo = ref({});
|
||||
const totalGames = ref(0);
|
||||
const totalWins = ref(0);
|
||||
const winRate = ref('0%');
|
||||
const unreadCount = ref(0);
|
||||
const version = ref('1.0.0');
|
||||
|
||||
// 获取用户信息
|
||||
const getUserInfo = async () => {
|
||||
// 从本地获取用户信息
|
||||
userInfo.value = proxy.$StaticValue.getUserInfo();
|
||||
if(!userInfo.value?.userId) {
|
||||
// 如果没有登录, 获取到当前用户的openid
|
||||
const openIdRes = await getOpenId();
|
||||
const openId = openIdRes.openid;
|
||||
userInfo.value = {
|
||||
openId: openId,
|
||||
nickName: '用户'+openId.substring(0, 6)
|
||||
};
|
||||
register(userInfo.value).then(res => {
|
||||
// 注册成功就执行登录
|
||||
login(userInfo.value).then(loginRes => {
|
||||
userInfo.value = loginRes.data;
|
||||
// 存储用户信息到本地
|
||||
proxy.$StaticValue.setUserInfo(userInfo.value);
|
||||
// 获取用户统计数据
|
||||
fetchUserStats();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// 已登录,获取用户统计数据
|
||||
fetchUserStats();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户统计数据
|
||||
const fetchUserStats = () => {
|
||||
// 实际项目中应该调用API获取真实数据
|
||||
// 这里使用模拟数据
|
||||
totalGames.value = 42;
|
||||
totalWins.value = 28;
|
||||
winRate.value = '66.7%';
|
||||
unreadCount.value = 2;
|
||||
|
||||
// 实际API调用示例:
|
||||
// getUserStats(userInfo.value.userId).then(res => {
|
||||
// totalGames.value = res.data.totalGames;
|
||||
// totalWins.value = res.data.totalWins;
|
||||
// winRate.value = res.data.winRate;
|
||||
// unreadCount.value = res.data.unreadCount;
|
||||
// });
|
||||
}
|
||||
|
||||
// 更新头像
|
||||
const updateAvatar = () => {
|
||||
/* #ifdef MP-WEIXIN */
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
sizeType: ['compressed'],
|
||||
success: (res) => {
|
||||
if (res.tempFiles && res.tempFiles.length > 0) {
|
||||
const tempFilePath = res.tempFiles[0].tempFilePath;
|
||||
|
||||
// 预览选择的头像
|
||||
wx.previewImage({
|
||||
urls: [tempFilePath],
|
||||
success: () => {
|
||||
// 更新头像
|
||||
userInfo.value.avatarUrl = tempFilePath;
|
||||
// 在实际项目中应该上传头像到服务器
|
||||
proxy.$StaticValue.setUserInfo(userInfo.value);
|
||||
|
||||
uni.showToast({
|
||||
title: '头像更新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('选择头像失败', err);
|
||||
if (err.errMsg !== 'chooseMedia:fail cancel') {
|
||||
uni.showToast({
|
||||
title: '选择头像失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
/* #endif */
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
|
||||
userInfo.value.avatarUrl = res.tempFilePaths[0];
|
||||
proxy.$StaticValue.setUserInfo(userInfo.value);
|
||||
uni.showToast({
|
||||
title: '头像更新成功',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
// 导航到历史对局页面
|
||||
const navigateToHistory = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/history-game/history-game'
|
||||
});
|
||||
}
|
||||
|
||||
// 导航到收藏对局页面
|
||||
const navigateToFavorites = () => {
|
||||
// 如果收藏页面不存在,可以创建一个或跳转到历史页面并筛选
|
||||
uni.navigateTo({
|
||||
url: '/pages/my/favorites'
|
||||
});
|
||||
}
|
||||
|
||||
// 打开设置页面
|
||||
const openSettings = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/my/setting'
|
||||
});
|
||||
}
|
||||
|
||||
// 打开帮助与反馈
|
||||
const openFeedback = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/my/feedback'
|
||||
});
|
||||
}
|
||||
|
||||
// 关于我们
|
||||
const aboutUs = () => {
|
||||
uni.showModal({
|
||||
title: '关于计分小程序',
|
||||
content: '计分小程序 v' + version.value + '\n\n一款便捷的计分工具,支持多种游戏模式。\n\n© 2023 计分小程序团队',
|
||||
showCancel: false,
|
||||
confirmText: '确定'
|
||||
});
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const logout = () => {
|
||||
uni.showModal({
|
||||
title: '确认退出',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 清除本地存储的用户信息
|
||||
proxy.$StaticValue.clearUserInfo();
|
||||
// 重置用户信息
|
||||
userInfo.value = {};
|
||||
totalGames.value = 0;
|
||||
totalWins.value = 0;
|
||||
winRate.value = '0%';
|
||||
|
||||
// 重新获取用户信息(会自动重新登录)
|
||||
getUserInfo();
|
||||
|
||||
uni.showToast({
|
||||
title: '已退出登录',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getUserInfo();
|
||||
// 获取版本信息
|
||||
/* #ifdef MP-WEIXIN */
|
||||
const appInfo = wx.getAccountInfoSync();
|
||||
version.value = appInfo.miniProgram.version || '1.0.0';
|
||||
/* #endif */
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
// 每次显示页面时刷新数据
|
||||
fetchUserStats();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.my {
|
||||
width: 750rpx;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
/* 用户信息区域样式 */
|
||||
.user-info {
|
||||
--user-height: 500rpx;
|
||||
width: 750rpx;
|
||||
height: var(--user-height);
|
||||
position: relative;
|
||||
.background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(rgb(87, 255, 87), rgb(144, 251, 144));
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
.info-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding-top: calc(var(--top-margin) + 20rpx);
|
||||
.top-user-name {
|
||||
width: 100%;
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
ttext-align: center;
|
||||
}
|
||||
.avatars {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20rpx;
|
||||
.avatar-img {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
border-radius: 75rpx;
|
||||
border: 4rpx solid #ffffff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
// 添加点击效果
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hello-text {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 20rpx;
|
||||
font-size: 30rpx;
|
||||
color: #000000;
|
||||
p {
|
||||
margin: 5rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 数据统计区域样式 */
|
||||
.data-section {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
background-color: #ffffff;
|
||||
margin-top: -30rpx;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 0 40rpx;
|
||||
margin-bottom: 30rpx;
|
||||
.data-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
.data-number {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.data-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
// 添加点击效果
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
.data-divider {
|
||||
width: 1rpx;
|
||||
height: 80rpx;
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
/* 功能菜单区域样式 */
|
||||
.menu-section {
|
||||
width: 100%;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
.menu-group {
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 30rpx;
|
||||
overflow: hidden;
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
height: 100rpx;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
// 最后一个菜单项不需要底边框
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.menu-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.menu-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.menu-text {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
.menu-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.menu-badge {
|
||||
background-color: #FF3B30;
|
||||
color: #ffffff;
|
||||
font-size: 20rpx;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 10rpx;
|
||||
margin-right: 10rpx;
|
||||
min-width: 20rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
// 添加点击效果
|
||||
&:active {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 退出登录按钮样式 */
|
||||
.logout-section {
|
||||
width: 100%;
|
||||
padding: 0 40rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 40rpx;
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
border-radius: 45rpx;
|
||||
font-size: 34rpx;
|
||||
background-color: #ffffff;
|
||||
color: #FF3B30;
|
||||
border: 1rpx solid #FF3B30;
|
||||
}
|
||||
}
|
||||
|
||||
/* 版本信息样式 */
|
||||
.version-info {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding-bottom: 60rpx;
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
/* 安全区域适配 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.my {
|
||||
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
383
scoring/pages/my/setting.vue
Normal file
383
scoring/pages/my/setting.vue
Normal file
@@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<view class="setting-page">
|
||||
<view class="setting-content">
|
||||
<!-- 账户设置 -->
|
||||
<view class="setting-section">
|
||||
<view class="section-title">账户设置</view>
|
||||
<view class="setting-list">
|
||||
<view class="setting-item" @click="navigateToProfile">
|
||||
<view class="item-left">
|
||||
<text class="item-title">个人资料</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">查看</text>
|
||||
<uni-icons type="right" size="24" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="navigateToSecurity">
|
||||
<view class="item-left">
|
||||
<text class="item-title">账户安全</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">设置</text>
|
||||
<uni-icons type="right" size="24" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 应用设置 -->
|
||||
<view class="setting-section">
|
||||
<view class="section-title">应用设置</view>
|
||||
<view class="setting-list">
|
||||
<view class="setting-item" @click="toggleNotifications">
|
||||
<view class="item-left">
|
||||
<text class="item-title">消息通知</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-switch
|
||||
:checked="notificationsEnabled"
|
||||
activeColor="#007aff"
|
||||
@change="toggleNotifications"
|
||||
></uni-switch>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="toggleDarkMode">
|
||||
<view class="item-left">
|
||||
<text class="item-title">深色模式</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-switch
|
||||
:checked="darkModeEnabled"
|
||||
activeColor="#007aff"
|
||||
@change="toggleDarkMode"
|
||||
></uni-switch>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="clearCache">
|
||||
<view class="item-left">
|
||||
<text class="item-title">清除缓存</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{cacheSize}}</text>
|
||||
<uni-icons type="right" size="24" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 关于 -->
|
||||
<view class="setting-section">
|
||||
<view class="section-title">关于</view>
|
||||
<view class="setting-list">
|
||||
<view class="setting-item" @click="navigateToAbout">
|
||||
<view class="item-left">
|
||||
<text class="item-title">关于我们</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<text class="item-value">{{appVersion}}</text>
|
||||
<uni-icons type="right" size="24" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="navigateToPrivacy">
|
||||
<view class="item-left">
|
||||
<text class="item-title">隐私政策</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="24" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="setting-item" @click="navigateToTerms">
|
||||
<view class="item-left">
|
||||
<text class="item-title">用户协议</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="24" color="#ccc"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 退出登录 -->
|
||||
<view class="logout-section">
|
||||
<button class="logout-btn" @click="logout">退出登录</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
// 响应式数据
|
||||
const notificationsEnabled = ref(true)
|
||||
const darkModeEnabled = ref(false)
|
||||
const cacheSize = ref('12.3MB')
|
||||
const appVersion = ref('1.0.0')
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 初始化时检查缓存大小等信息
|
||||
checkCacheSize()
|
||||
})
|
||||
|
||||
// 切换通知开关
|
||||
const toggleNotifications = (e) => {
|
||||
notificationsEnabled.value = e.detail.checked || !notificationsEnabled.value
|
||||
uni.showToast({
|
||||
title: notificationsEnabled.value ? '通知已开启' : '通知已关闭',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 切换深色模式
|
||||
const toggleDarkMode = (e) => {
|
||||
darkModeEnabled.value = e.detail.checked || !darkModeEnabled.value
|
||||
uni.showToast({
|
||||
title: darkModeEnabled.value ? '深色模式已开启' : '深色模式已关闭',
|
||||
icon: 'none'
|
||||
})
|
||||
|
||||
// 这里可以添加实际的深色模式切换逻辑
|
||||
// 例如:document.body.classList.toggle('dark-mode')
|
||||
}
|
||||
|
||||
// 检查缓存大小
|
||||
const checkCacheSize = () => {
|
||||
// 模拟获取缓存大小
|
||||
// 实际项目中可以调用uni.getStorageInfoSync()等API获取
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
const clearCache = () => {
|
||||
uni.showModal({
|
||||
title: '清除缓存',
|
||||
content: '确定要清除所有缓存数据吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 模拟清除缓存
|
||||
setTimeout(() => {
|
||||
cacheSize.value = '0KB'
|
||||
uni.showToast({
|
||||
title: '缓存清除成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 导航到个人资料页面
|
||||
const navigateToProfile = () => {
|
||||
// 实际项目中添加导航逻辑
|
||||
uni.showToast({
|
||||
title: '跳转到个人资料页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 导航到账户安全页面
|
||||
const navigateToSecurity = () => {
|
||||
// 实际项目中添加导航逻辑
|
||||
uni.showToast({
|
||||
title: '跳转到账户安全页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 导航到关于页面
|
||||
const navigateToAbout = () => {
|
||||
// 实际项目中添加导航逻辑
|
||||
uni.showToast({
|
||||
title: '跳转到关于页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 导航到隐私政策页面
|
||||
const navigateToPrivacy = () => {
|
||||
// 实际项目中添加导航逻辑
|
||||
uni.showToast({
|
||||
title: '跳转到隐私政策页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 导航到用户协议页面
|
||||
const navigateToTerms = () => {
|
||||
// 实际项目中添加导航逻辑
|
||||
uni.showToast({
|
||||
title: '跳转到用户协议页面',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const logout = () => {
|
||||
uni.showModal({
|
||||
title: '退出登录',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 模拟退出登录
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
title: '已退出登录',
|
||||
icon: 'success'
|
||||
})
|
||||
// 实际项目中可以添加跳转到登录页面的逻辑
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.setting-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 页面标题 */
|
||||
.page-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.setting-content {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
/* 设置分组 */
|
||||
.setting-section {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 分组标题 */
|
||||
.section-title {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
padding: 0 30rpx 10rpx;
|
||||
}
|
||||
|
||||
/* 设置列表 */
|
||||
.setting-list {
|
||||
background-color: #fff;
|
||||
border-radius: 12rpx;
|
||||
margin: 0 30rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 设置项 */
|
||||
.setting-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx;
|
||||
position: relative;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.setting-item:active {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.setting-item:not(:last-child)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 30rpx;
|
||||
right: 30rpx;
|
||||
height: 1rpx;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
/* 左侧内容 */
|
||||
.item-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 图标容器 */
|
||||
.item-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #f0f8ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
/* 标题文本 */
|
||||
.item-title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 右侧内容 */
|
||||
.item-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 值文本 */
|
||||
.item-value {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
/* 退出登录区域 */
|
||||
.logout-section {
|
||||
padding: 40rpx 30rpx 0;
|
||||
}
|
||||
|
||||
/* 退出登录按钮 */
|
||||
.logout-btn {
|
||||
background-color: #fff;
|
||||
color: #ff3b30;
|
||||
border-radius: 80rpx;
|
||||
font-size: 32rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
/* 微信小程序特定样式优化 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.setting-item {
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.setting-list {
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
Reference in New Issue
Block a user