310 lines
7.7 KiB
Vue
310 lines
7.7 KiB
Vue
<template>
|
||
<view class="grid-item" @click="handleCardClick">
|
||
<uni-card margin="0px" padding="0px" spacing="0px">
|
||
<view
|
||
style="position:absolute;top:0;right:0;width:100%; background: linear-gradient(to bottom, rgba(255, 108, 0, 0.9), rgba(255, 108, 0, 0.7));box-shadow: 0 2px 8px rgba(235,135,58, 0.3);z-index:999;border-radius:4px 4px 0 0;font-size:24rpx;color:#fff;font-weight: 500; opacity: 0.7;">
|
||
<text style="padding-left:10rpx;">{{ item.status }}</text>
|
||
</view>
|
||
<view class="item-content column">
|
||
<!-- 麻将桌区域 -->
|
||
<view class="mahjong-table">
|
||
<!-- 九宫格布局 -->
|
||
<view style="width:300rpx;height: 300rpx;">
|
||
<view class="nine-grid-container">
|
||
<!-- 第一行 -->
|
||
<view class="grid-cell"></view>
|
||
<view class="grid-cell" :class="getCellClass(0)">
|
||
<view v-if="getPlayerAtPosition(0)" class="player-avatar">
|
||
<image :src="getPlayerAtPosition(0).avatar || ''" class="avatar-img"></image>
|
||
</view>
|
||
<view v-else-if="canJoinAtPosition(0)" class="join-button" @click.stop="handleJoin">
|
||
<text class="join-text">+</text>
|
||
</view>
|
||
</view>
|
||
<view class="grid-cell"></view>
|
||
|
||
<!-- 第二行 -->
|
||
<view class="grid-cell" :class="getCellClass(1)">
|
||
<view v-if="getPlayerAtPosition(1)" class="player-avatar">
|
||
<image :src="getPlayerAtPosition(1).avatar || ''" class="avatar-img"></image>
|
||
</view>
|
||
<view v-else-if="canJoinAtPosition(1)" class="join-button" @click.stop="handleJoin">
|
||
<text class="join-text">+</text>
|
||
</view>
|
||
</view>
|
||
<view class="grid-cell center-cell">
|
||
<!-- 麻将桌背景图 -->
|
||
<image src="/static/9.png" class="table-bg" mode=""></image>
|
||
</view>
|
||
<view class="grid-cell" :class="getCellClass(2)">
|
||
<view v-if="getPlayerAtPosition(2)" class="player-avatar">
|
||
<image :src="getPlayerAtPosition(2).avatar || ''" class="avatar-img"></image>
|
||
</view>
|
||
<view v-else-if="canJoinAtPosition(2)" class="join-button" @click.stop="handleJoin">
|
||
<text class="join-text">+</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 第三行 -->
|
||
<view class="grid-cell"></view>
|
||
<view class="grid-cell" :class="getCellClass(3)">
|
||
<view v-if="getPlayerAtPosition(3)" class="player-avatar">
|
||
<image :src="getPlayerAtPosition(3).avatar || ''" class="avatar-img"></image>
|
||
</view>
|
||
<view v-else-if="canJoinAtPosition(3)" class="join-button" @click.stop="handleJoin">
|
||
<text class="join-text">+</text>
|
||
</view>
|
||
</view>
|
||
<view class="grid-cell"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 信息区域 -->
|
||
<view class="info-section">
|
||
<text class="info-text" style="font-size: 26rpx;font-weight: 600;color: #FA690B;">{{
|
||
item.description }}</text>
|
||
<text class="info-text" style="margin-top: -7rpx;">{{ item.room }}</text>
|
||
<text class="info-text" style="font-size: 22rpx;font-weight: 600;line-height:28rpx;">2025-09-06</text>
|
||
<text class="info-text" style="font-size: 22rpx;font-weight: 600;line-height:28rpx;">{{ item.time }}</text>
|
||
<text class="info-text" style="color:#FA690B;line-height:30rpx;margin-top:6rpx;">{{ item.requirements }}</text>
|
||
</view>
|
||
</view>
|
||
</uni-card>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { defineProps, defineEmits, computed } from 'vue'
|
||
|
||
// 定义 props
|
||
const props = defineProps({
|
||
item: {
|
||
type: Object,
|
||
required: true,
|
||
default: () => ({
|
||
id: '',
|
||
status: '',
|
||
description: '',
|
||
time: '',
|
||
room: '',
|
||
requirements: '',
|
||
personCount: 4,//总人数
|
||
joinPerson: [
|
||
{
|
||
id: 1,
|
||
name: '张三',
|
||
avatar: '',
|
||
phone: '',
|
||
}
|
||
],//已加入人
|
||
})
|
||
}
|
||
})
|
||
|
||
// 定义 emits
|
||
const emit = defineEmits(['click', 'join'])
|
||
|
||
// 计算属性:获取玩家位置布局
|
||
const playerPositions = computed(() => {
|
||
const { personCount, joinPerson } = props.item
|
||
const joinedCount = joinPerson.length
|
||
|
||
// 根据总人数确定布局
|
||
if (personCount === 2) {
|
||
// 2人局:第二行左边和右边
|
||
return {
|
||
positions: [null, joinPerson[0] || null, joinPerson[1] || null, null],
|
||
joinPositions: [null, !joinPerson[0], !joinPerson[1], null]
|
||
}
|
||
} else if (personCount === 3) {
|
||
// 3人局:第一行中间、第二行左边和右边
|
||
return {
|
||
positions: [joinPerson[2] || null, joinPerson[0] || null, joinPerson[1] || null, null],
|
||
joinPositions: [!joinPerson[2], !joinPerson[0], !joinPerson[1], null]
|
||
}
|
||
} else {
|
||
// 4人局:标准布局(上、左、右、下)
|
||
return {
|
||
positions: [
|
||
joinPerson[2] || null, // 第一行中间
|
||
joinPerson[0] || null, // 第二行左边
|
||
joinPerson[1] || null, // 第二行右边
|
||
joinPerson[3] || null // 第三行中间
|
||
],
|
||
joinPositions: [
|
||
!joinPerson[2], // 第一行中间
|
||
!joinPerson[0], // 第二行左边
|
||
!joinPerson[1], // 第二行右边
|
||
!joinPerson[3] // 第三行中间
|
||
]
|
||
}
|
||
}
|
||
})
|
||
|
||
// 获取指定位置的玩家
|
||
const getPlayerAtPosition = (position) => {
|
||
return playerPositions.value.positions[position]
|
||
}
|
||
|
||
// 判断指定位置是否可以加入
|
||
const canJoinAtPosition = (position) => {
|
||
const { personCount, joinPerson } = props.item
|
||
const joinedCount = joinPerson.length
|
||
|
||
// 如果已满员,不能加入
|
||
if (joinedCount >= personCount) {
|
||
return false
|
||
}
|
||
|
||
// 直接使用计算属性中的 joinPositions
|
||
return playerPositions.value.joinPositions[position]
|
||
}
|
||
|
||
// 获取单元格样式类
|
||
const getCellClass = (position) => {
|
||
if (getPlayerAtPosition(position)) {
|
||
return 'player-cell'
|
||
} else if (canJoinAtPosition(position)) {
|
||
return 'join-cell'
|
||
}
|
||
return ''
|
||
}
|
||
|
||
// 处理卡片点击事件
|
||
const handleCardClick = () => {
|
||
emit('click', props.item)
|
||
}
|
||
|
||
// 处理加入按钮点击事件
|
||
const handleJoin = () => {
|
||
emit('join', props.item)
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.item-content {
|
||
height: 540rpx;
|
||
color: white;
|
||
border-radius: 10rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.mahjong-table {
|
||
width: 100%;
|
||
background-color: #D4D4D4;
|
||
border-radius: 10rpx 10rpx 0 0;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.status-tag {
|
||
position: absolute;
|
||
left: 15rpx;
|
||
top: 15rpx;
|
||
font-size: 16rpx;
|
||
color: black;
|
||
z-index: 10;
|
||
}
|
||
|
||
/* 九宫格容器 */
|
||
.nine-grid-container {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr 1fr;
|
||
width: 100%;
|
||
height: 100%;
|
||
padding: 20rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 网格单元格 */
|
||
.grid-cell {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
}
|
||
|
||
/* 玩家头像单元格 */
|
||
.player-cell {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 中心单元格(麻将桌) */
|
||
.center-cell {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 加入按钮单元格 */
|
||
.join-cell {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 麻将桌背景图 */
|
||
.table-bg {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
}
|
||
|
||
/* 玩家头像 */
|
||
.player-avatar {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
background-color: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.avatar-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: aqua;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* 加入按钮 */
|
||
.join-button {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
background-color: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.join-button:hover {
|
||
transform: scale(1.1);
|
||
background-color: #f0f0f0;
|
||
}
|
||
|
||
.join-text {
|
||
color: black;
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.info-section {
|
||
padding: 10rpx;
|
||
}
|
||
|
||
.info-text {
|
||
font-size: 24rpx;
|
||
color: black;
|
||
display: block;
|
||
}
|
||
</style>
|