123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- <template>
- <view class="create-room-page">
- <view class="room-header">
- <view class="game-info" v-if="gameInfo">
- <image class="game-image" :src="gameInfo.image" mode="aspectFill" />
- <view class="game-detail">
- <view class="game-title">{{ gameInfo.title }}</view>
- <view class="game-meta">
- <text>{{ gameInfo.players }}人</text>
- <text>{{ gameInfo.duration }}分钟</text>
- </view>
- </view>
- </view>
- </view>
-
- <view class="room-settings">
- <nut-divider
- content-position="center"
- :style="{ color: '#3C92FB', borderColor: '#3C92FB', padding: '0 16px', margin: '10px 0 20px 0' }"
- >
- 房间设置
- </nut-divider>
-
- <view class="setting-item">
- <view class="setting-label">房间名称</view>
- <nut-input v-model="roomName" placeholder="请输入房间名称..." />
- <view class="random-name" @click="generateRandomName">随机名称</view>
- </view>
-
- <view class="setting-item">
- <view class="setting-label">最大人数</view>
- <view class="setting-slider">
- <nut-range v-model="maxPlayers" :min="2" :max="12" inactive-color="#E5E5E5" button-color="#3C92FB" active-color="#3C92FB"></nut-range>
- <view class="slider-value">{{ maxPlayers }}人</view>
- </view>
- </view>
-
- <view class="setting-item">
- <view class="setting-label">房间可见性</view>
- <view class="visibility-options">
- <nut-radio-group v-model="roomVisibility" direction="horizontal">
- <nut-radio label="private" icon-name="check" icon-active-color="#3C92FB">仅限密码</nut-radio>
- <nut-radio label="public" icon-name="check" icon-active-color="#3C92FB">公开房间</nut-radio>
- </nut-radio-group>
- </view>
- </view>
-
- <view class="setting-item" v-if="roomVisibility === 'private'">
- <view class="setting-label">房间密码</view>
- <nut-input v-model="roomPassword" placeholder="请设置房间密码..." />
- </view>
- </view>
-
- <view class="action-buttons">
- <nut-button block color="#3C92FB" class="create-button" @click="createRoom">
- 创建并开始
- </nut-button>
- </view>
- </view>
- <Tabbar></Tabbar>
- </template>
- <script lang="ts">
- import Taro from '@tarojs/taro'
- import { ref } from 'vue'
- import { useGameStore, type Game } from '@/stores/game'
- import { useUserStore } from '@/stores/user'
- import { useRoomStore } from '@/stores/room'
- import { roomAPI } from '@/services/api/room'
- import Tabbar from '@/components/Tabbar.vue'
- // 随机房间名称列表
- const ROOM_NAME_PREFIXES = ['欢乐', '快乐', '奇妙', '有趣', '精彩', '神秘', '魔法', '激情', '疯狂', '幻想']
- const ROOM_NAME_SUFFIXES = ['小队', '团队', '俱乐部', '联盟', '聚会', '战队', '家族', '帮派', '协会', '同盟']
- export default {
- components: {
- Tabbar
- },
-
- // 生命周期钩子 - 页面显示
- onShow() {
- // 隐藏返回首页按钮
- Taro.hideHomeButton()
- console.log('已隐藏返回首页按钮')
- },
-
- // Composition API
- setup() {
- // 初始化store
- const gameStore = useGameStore()
- const userStore = useUserStore()
- const roomStore = useRoomStore()
- // 房间设置
- const roomName = ref('')
- const maxPlayers = ref(6)
- const roomVisibility = ref('private')
- const roomPassword = ref('')
- // 添加明确的类型注解
- const gameInfo = ref<Game | null>(null)
-
- // 生成随机房间名称
- const generateRandomName = () => {
- const prefix = ROOM_NAME_PREFIXES[Math.floor(Math.random() * ROOM_NAME_PREFIXES.length)]
- const suffix = ROOM_NAME_SUFFIXES[Math.floor(Math.random() * ROOM_NAME_SUFFIXES.length)]
- roomName.value = `${prefix}${gameInfo.value?.title || '游戏'}${suffix}`
- }
-
- // 获取游戏信息
- const initGameInfo = async () => {
- // 确保用户是主持人角色
- userStore.setRole('hoster')
-
- const pages = Taro.getCurrentPages()
- const currentPage = pages[pages.length - 1]
- const gameId = currentPage.$taroParams?.gameId
-
- if (!gameId) {
- Taro.showToast({
- title: '游戏ID不存在',
- icon: 'none'
- })
- return
- }
-
- try {
- // 获取游戏详情
- const game = await gameStore.getGameDetail(gameId)
- if (game) {
- gameInfo.value = game
-
- // 设置默认房间名
- roomName.value = `${game.title}房间`
-
- // 从游戏的人数范围设置最大人数
- const playersRange = game.players.split('-')
- if (playersRange.length > 1) {
- const max = parseInt(playersRange[1])
- if (!isNaN(max)) {
- maxPlayers.value = Math.min(max, 12) // 限制最大12人
- }
- }
- }
- } catch (error) {
- console.error('获取游戏信息失败:', error)
- Taro.showToast({
- title: '获取游戏信息失败',
- icon: 'none'
- })
- }
- }
- // 创建房间
- const createRoom = async () => {
- if (!roomName.value) {
- Taro.showToast({
- title: '请输入房间名称',
- icon: 'none'
- })
- return
- }
-
- if (roomVisibility.value === 'private' && !roomPassword.value) {
- Taro.showToast({
- title: '请设置房间密码',
- icon: 'none'
- })
- return
- }
-
- try {
- Taro.showLoading({ title: '创建房间中...' })
-
- // 构建房间数据 - 修复status类型
- const roomData = {
- id: `room_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
- name: roomName.value,
- gameId: gameInfo.value?.id || '',
- gameTitle: gameInfo.value?.title || '',
- maxPlayers: maxPlayers.value,
- visibility: roomVisibility.value as 'private' | 'public',
- password: roomVisibility.value === 'private' ? roomPassword.value : '',
- hosterId: userStore.openid || 'temp_host_id',
- hosterName: userStore.nickname || '未知用户',
- createTime: Date.now(),
- status: 'waiting' as 'waiting' | 'playing' | 'ended', // 使用类型断言确保类型匹配
- users: [{
- id: userStore.openid || 'temp_host_id',
- name: userStore.nickname || '未知用户',
- avatar: userStore.avatar || '',
- role: 'hoster' as 'hoster' | 'player',
- joinTime: Date.now()
- }]
- }
-
- // 保存到store
- roomStore.setCurrentRoom(roomData)
-
- // 同步到服务器
- const result = await roomAPI.createRoom(roomData)
-
- if (result.success) {
- Taro.navigateTo({
- url: `/pages/room/waiting/index?roomId=${result.roomId}`
- })
- } else {
- throw new Error('创建房间失败')
- }
- } catch (error) {
- console.error('创建房间失败:', error)
- Taro.showToast({
- title: '创建房间失败',
- icon: 'none'
- })
- } finally {
- Taro.hideLoading()
- }
- }
-
- // 页面初始化时获取游戏信息
- initGameInfo()
-
- return {
- roomName,
- maxPlayers,
- roomVisibility,
- roomPassword,
- gameInfo,
- createRoom,
- generateRandomName
- }
- },
-
- // 生命周期钩子 - 页面加载
- onLoad() {
- // 其他初始化逻辑
- }
- }
- </script>
- <style lang="scss">
- .create-room-page {
- padding: $spacing-base;
- background-color: $background-color-base;
- min-height: 100vh;
- padding-bottom: $spacing-large * 4; // 为底部tabbar留出空间
-
- .room-header {
- margin-bottom: $spacing-small;
-
- .game-info {
- display: flex;
- background-color: $background-color-light;
- border-radius: $border-radius-small;
- overflow: hidden;
- box-shadow: $shadow-light;
-
- .game-image {
- width: 120px;
- height: 120px;
- object-fit: cover; // 修复图片显示问题
- }
-
- .game-detail {
- flex: 1;
- padding: $spacing-large;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- .game-title {
- font-size: $font-size-medium;
- font-weight: $font-weight-medium;
- color: $text-color-primary;
- margin-bottom: $spacing-base;
- }
-
- .game-meta {
- font-size: $font-size-small;
- color: $text-color-secondary;
-
- text {
- margin-right: $spacing-large;
- display: inline-block;
-
- &:before {
- content: '•';
- margin-right: 4px;
- }
-
- &:first-child:before {
- content: '';
- margin-right: 0;
- }
- }
- }
- }
- }
- }
-
- .room-settings {
- background-color: $background-color-light;
- border-radius: $border-radius-small;
- padding: $spacing-large;
- margin-bottom: $spacing-large;
- box-shadow: $shadow-light;
-
- .setting-item {
- margin-bottom: $spacing-large;
- position: relative;
-
- .setting-label {
- font-size: $font-size-small;
- color: $text-color-secondary;
- margin-bottom: $spacing-mini;
- }
-
- .random-name {
- position: absolute;
- right: 0;
- top: 0;
- font-size: $font-size-small;
- color: $primary-color;
- padding: $spacing-mini $spacing-small;
- cursor: pointer;
- }
-
- .setting-slider {
- display: flex;
- align-items: center;
- margin-top: $spacing-base;
-
- .slider-value {
- width: 60px;
- text-align: center;
- font-size: $font-size-small;
- color: $text-color-primary;
- font-weight: $font-weight-medium;
- }
- }
-
- .visibility-options {
- margin-top: $spacing-base;
- }
- }
- }
-
- .action-buttons {
- padding: $spacing-large 0;
-
- .create-button {
- height: 44px;
- font-size: $font-size-medium;
- border-radius: $border-radius-base;
- }
- }
- }
- </style>
|