Initial commit: DataTool backend, frontend and Docker

This commit is contained in:
liu
2026-01-31 00:51:14 +08:00
commit 59bb8e16f5
69 changed files with 9449 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
<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>