125 lines
4.1 KiB
Vue
125 lines
4.1 KiB
Vue
<template>
|
||
<div class="flex items-center justify-center min-h-[calc(100vh-56px)] px-4">
|
||
<div
|
||
class="w-full max-w-4xl bg-white border border-slate-200 rounded-[var(--dt-radius-modal)] px-6 sm:px-8 py-10 grid gap-8 md:grid-cols-2"
|
||
>
|
||
<!-- 创建房间 -->
|
||
<section class="space-y-4">
|
||
<h2 class="text-xl font-semibold text-slate-900">创建房间</h2>
|
||
<p class="text-sm text-slate-600">
|
||
自动生成 6 位房间号,在本地浏览器会话之间快速传文本、文件和图片。
|
||
</p>
|
||
<div
|
||
class="mt-4 flex items-center justify-between rounded-[var(--dt-radius-card)] border border-dashed border-slate-300 bg-slate-50 px-4 py-3"
|
||
>
|
||
<span class="text-2xl font-mono font-semibold text-slate-900">
|
||
{{ createRoomCodeDisplay }}
|
||
</span>
|
||
<BaseButton
|
||
size="lg"
|
||
variant="primary"
|
||
:loading="createLoading"
|
||
:disabled="createLoading"
|
||
@click="handleCreateAndEnter"
|
||
>
|
||
创建并进入
|
||
</BaseButton>
|
||
</div>
|
||
<p v-if="createError" class="text-xs text-danger">
|
||
{{ createError }}
|
||
</p>
|
||
<p class="text-xs text-slate-500">
|
||
数据不落库,仅当前会话可见。关闭页面后历史记录仅保留在本地浏览器。
|
||
</p>
|
||
</section>
|
||
|
||
<!-- 加入房间 -->
|
||
<section class="space-y-4">
|
||
<h2 class="text-xl font-semibold text-slate-900">加入房间</h2>
|
||
<p class="text-sm text-slate-600">
|
||
输入房间号,与同一房间的其他终端进行数据同步传输。
|
||
</p>
|
||
<form class="space-y-3" @submit.prevent="handleJoin">
|
||
<div class="space-y-1.5">
|
||
<label class="block text-xs font-medium text-slate-700">
|
||
房间号(6 位数字)
|
||
</label>
|
||
<input
|
||
v-model="joinRoomCode"
|
||
type="text"
|
||
inputmode="numeric"
|
||
maxlength="6"
|
||
class="dt-input"
|
||
:class="{ 'border-danger focus:border-danger': joinError }"
|
||
placeholder="请输入 6 位数字房间号"
|
||
@input="joinError = ''"
|
||
/>
|
||
<p v-if="joinError" class="text-xs text-danger">
|
||
{{ joinError }}
|
||
</p>
|
||
</div>
|
||
<BaseButton
|
||
type="submit"
|
||
size="lg"
|
||
variant="primary"
|
||
class="w-full"
|
||
:disabled="!isJoinValid"
|
||
>
|
||
加入房间
|
||
</BaseButton>
|
||
</form>
|
||
</section>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, ref } from 'vue';
|
||
import { useRouter } from 'vue-router';
|
||
import axios from 'axios';
|
||
import BaseButton from '@/components/ui/BaseButton.vue';
|
||
|
||
// 开发时用空字符串走同源,由 Vite 代理到后端,便于其它设备通过本机 IP:5173 访问
|
||
const API_BASE = import.meta.env.VITE_API_BASE ?? (import.meta.env.DEV ? '' : 'http://localhost:8080');
|
||
|
||
const router = useRouter();
|
||
|
||
const createRoomCodeDisplay = ref('------');
|
||
const createLoading = ref(false);
|
||
const createError = ref('');
|
||
const joinRoomCode = ref('');
|
||
const joinError = ref('');
|
||
|
||
const isJoinValid = computed(() => /^[0-9]{6}$/.test(joinRoomCode.value));
|
||
|
||
async function handleCreateAndEnter() {
|
||
createError.value = '';
|
||
createLoading.value = true;
|
||
try {
|
||
const { data } = await axios.post<{ roomCode: string }>(
|
||
`${API_BASE}/api/room/create`,
|
||
);
|
||
const roomCode = data.roomCode;
|
||
createRoomCodeDisplay.value = roomCode;
|
||
createLoading.value = false;
|
||
router.push({ name: 'room', params: { roomCode } });
|
||
} catch {
|
||
createError.value = '创建房间失败,请稍后重试。';
|
||
createRoomCodeDisplay.value = String(
|
||
Math.floor(100000 + Math.random() * 900000),
|
||
);
|
||
createLoading.value = false;
|
||
}
|
||
}
|
||
|
||
function handleJoin() {
|
||
joinError.value = '';
|
||
if (!isJoinValid.value) {
|
||
joinError.value = '房间号必须是 6 位数字。';
|
||
return;
|
||
}
|
||
router.push({ name: 'room', params: { roomCode: joinRoomCode.value } });
|
||
}
|
||
</script>
|
||
|