feat:去除多余的【回到首页】

This commit is contained in:
M了个J 2026-06-01 14:59:14 +08:00
parent 027e7025f2
commit 38cfa8684f

View File

@ -1,112 +1,143 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue' import { ref, computed, onMounted, onUnmounted } from "vue";
import { RouterLink } from 'vue-router' import { RouterLink } from "vue-router";
import { Monitor, Download, ArrowLeft, CheckCircle2, AlertCircle } from 'lucide-vue-next' import {
import logoImg from '@/assets/logo.png' Monitor,
Download,
ArrowLeft,
CheckCircle2,
AlertCircle,
} from "lucide-vue-next";
import logoImg from "@/assets/logo.png";
import { import {
TEACHER_APP_DOWNLOAD_URL, TEACHER_APP_DOWNLOAD_URL,
TEACHER_APP_BACKUP_DOWNLOAD_URL, TEACHER_APP_BACKUP_DOWNLOAD_URL,
TEACHER_APP_FILE_NAME, TEACHER_APP_FILE_NAME,
TEACHER_APP_FILE_SIZE, TEACHER_APP_FILE_SIZE,
TEACHER_DOWNLOAD_COUNTDOWN_SECONDS, TEACHER_DOWNLOAD_COUNTDOWN_SECONDS,
} from '@/constants/teacherApp' } from "@/constants/teacherApp";
const secondsLeft = ref(TEACHER_DOWNLOAD_COUNTDOWN_SECONDS) const secondsLeft = ref(TEACHER_DOWNLOAD_COUNTDOWN_SECONDS);
const downloadStarted = ref(false) const downloadStarted = ref(false);
let timer: ReturnType<typeof setInterval> | null = null let timer: ReturnType<typeof setInterval> | null = null;
const progress = computed(() => { const progress = computed(() => {
const total = TEACHER_DOWNLOAD_COUNTDOWN_SECONDS const total = TEACHER_DOWNLOAD_COUNTDOWN_SECONDS;
return ((total - secondsLeft.value) / total) * 100 return ((total - secondsLeft.value) / total) * 100;
}) });
const countdownLabel = computed(() => String(Math.max(secondsLeft.value, 0))) const countdownLabel = computed(() => String(Math.max(secondsLeft.value, 0)));
function triggerDownload() { function triggerDownload() {
if (downloadStarted.value) return if (downloadStarted.value) return;
downloadStarted.value = true downloadStarted.value = true;
const link = document.createElement('a') const link = document.createElement("a");
link.href = TEACHER_APP_DOWNLOAD_URL link.href = TEACHER_APP_DOWNLOAD_URL;
link.target = '_blank' link.target = "_blank";
link.rel = 'noopener noreferrer' link.rel = "noopener noreferrer";
document.body.appendChild(link) document.body.appendChild(link);
link.click() link.click();
document.body.removeChild(link) document.body.removeChild(link);
} }
function skipCountdown() { function skipCountdown() {
if (timer) { if (timer) {
clearInterval(timer) clearInterval(timer);
timer = null timer = null;
} }
secondsLeft.value = 0 secondsLeft.value = 0;
triggerDownload() triggerDownload();
} }
function startCountdown() { function startCountdown() {
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
if (prefersReducedMotion) { if (prefersReducedMotion) {
secondsLeft.value = 0 secondsLeft.value = 0;
triggerDownload() triggerDownload();
return return;
} }
timer = setInterval(() => { timer = setInterval(() => {
if (secondsLeft.value > 0) { if (secondsLeft.value > 0) {
secondsLeft.value -= 1 secondsLeft.value -= 1;
} }
if (secondsLeft.value <= 0) { if (secondsLeft.value <= 0) {
if (timer) clearInterval(timer) if (timer) clearInterval(timer);
timer = null timer = null;
triggerDownload() triggerDownload();
} }
}, 1000) }, 1000);
} }
onMounted(startCountdown) onMounted(startCountdown);
onUnmounted(() => { onUnmounted(() => {
if (timer) clearInterval(timer) if (timer) clearInterval(timer);
}) });
</script> </script>
<template> <template>
<div class="min-h-screen relative overflow-hidden flex flex-col"> <div class="min-h-screen relative overflow-hidden flex flex-col">
<div class="absolute inset-0 bg-gradient-to-br from-bg-soft via-white to-indigo-50/80" /> <div
<div class="absolute top-16 -left-24 w-80 h-80 rounded-full bg-primary/10 blur-3xl" /> class="absolute inset-0 bg-gradient-to-br from-bg-soft via-white to-indigo-50/80"
<div class="absolute bottom-20 -right-32 w-96 h-96 rounded-full bg-cyan-500/10 blur-3xl" /> />
<div
<header class="relative z-10 max-w-7xl mx-auto w-full px-4 sm:px-6 lg:px-8 pt-6"> class="absolute top-16 -left-24 w-80 h-80 rounded-full bg-primary/10 blur-3xl"
<RouterLink />
to="/" <div
class="inline-flex items-center gap-2 text-sm font-medium text-text-muted hover:text-primary transition-colors duration-200 cursor-pointer" class="absolute bottom-20 -right-32 w-96 h-96 rounded-full bg-cyan-500/10 blur-3xl"
> />
<ArrowLeft class="w-4 h-4" /> <main
返回首页 class="relative z-10 flex-1 flex items-center justify-center px-4 sm:px-6 py-12"
</RouterLink> >
</header>
<main class="relative z-10 flex-1 flex items-center justify-center px-4 sm:px-6 py-12">
<div class="w-full max-w-lg"> <div class="w-full max-w-lg">
<div class="glass-card-strong rounded-3xl p-8 sm:p-10 shadow-xl shadow-indigo-100/50 border border-white/60"> <div
class="glass-card-strong rounded-3xl p-8 sm:p-10 shadow-xl shadow-indigo-100/50 border border-white/60"
>
<div class="flex flex-col items-center text-center"> <div class="flex flex-col items-center text-center">
<img :src="logoImg" alt="灵犀学" class="h-10 w-auto mb-6" /> <img :src="logoImg" alt="灵犀学" class="h-10 w-auto mb-6" />
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-cyan-50 to-teal-50 border border-cyan-500/20 flex items-center justify-center mb-5"> <div
class="w-14 h-14 rounded-2xl bg-gradient-to-br from-cyan-50 to-teal-50 border border-cyan-500/20 flex items-center justify-center mb-5"
>
<Monitor class="w-7 h-7 text-cyan-600" /> <Monitor class="w-7 h-7 text-cyan-600" />
</div> </div>
<h1 class="text-2xl sm:text-3xl font-bold text-text-heading tracking-tight"> <h1
class="text-2xl sm:text-3xl font-bold text-text-heading tracking-tight"
>
灵犀学AI 教师端 灵犀学AI 教师端
</h1> </h1>
<p class="mt-2 text-text-muted text-sm sm:text-base"> <p class="mt-2 text-text-muted text-sm sm:text-base">
{{ downloadStarted ? '下载已启动,请查看浏览器下载栏' : '即将自动开始下载,请稍候' }} {{
downloadStarted
? "下载已启动,请查看浏览器下载栏"
: "即将自动开始下载,请稍候"
}}
</p> </p>
<div class="relative my-10" aria-live="polite" :aria-label="`倒计时 ${countdownLabel} `"> <div
<svg class="w-36 h-36 -rotate-90" viewBox="0 0 120 120" aria-hidden="true"> class="relative my-10"
<circle cx="60" cy="60" r="52" fill="none" stroke="currentColor" stroke-width="8" class="text-indigo-100" /> aria-live="polite"
:aria-label="`倒计时 ${countdownLabel} 秒`"
>
<svg
class="w-36 h-36 -rotate-90"
viewBox="0 0 120 120"
aria-hidden="true"
>
<circle
cx="60"
cy="60"
r="52"
fill="none"
stroke="currentColor"
stroke-width="8"
class="text-indigo-100"
/>
<circle <circle
cx="60" cx="60"
cy="60" cy="60"
@ -120,21 +151,38 @@ onUnmounted(() => {
:stroke-dashoffset="`${2 * Math.PI * 52 * (1 - progress / 100)}`" :stroke-dashoffset="`${2 * Math.PI * 52 * (1 - progress / 100)}`"
/> />
</svg> </svg>
<div class="absolute inset-0 flex flex-col items-center justify-center"> <div
<span v-if="!downloadStarted" class="text-5xl font-extrabold tabular-nums gradient-text"> class="absolute inset-0 flex flex-col items-center justify-center"
>
<span
v-if="!downloadStarted"
class="text-5xl font-extrabold tabular-nums gradient-text"
>
{{ countdownLabel }} {{ countdownLabel }}
</span> </span>
<CheckCircle2 v-else class="w-12 h-12 text-accent" /> <CheckCircle2 v-else class="w-12 h-12 text-accent" />
<span class="mt-1 text-xs font-medium text-text-muted uppercase tracking-wider"> <span
{{ downloadStarted ? '已开始' : '秒后下载' }} class="mt-1 text-xs font-medium text-text-muted uppercase tracking-wider"
>
{{ downloadStarted ? "已开始" : "秒后下载" }}
</span> </span>
</div> </div>
</div> </div>
<div class="w-full rounded-2xl bg-slate-50/80 border border-slate-200/80 px-4 py-3 text-left mb-6"> <div
<p class="text-xs font-semibold text-text-muted uppercase tracking-wide mb-1">安装包信息</p> class="w-full rounded-2xl bg-slate-50/80 border border-slate-200/80 px-4 py-3 text-left mb-6"
<p class="text-sm font-semibold text-text-heading break-all">{{ TEACHER_APP_FILE_NAME }}</p> >
<p class="text-xs text-text-muted mt-1">Windows 64 · {{ TEACHER_APP_FILE_SIZE }}</p> <p
class="text-xs font-semibold text-text-muted uppercase tracking-wide mb-1"
>
安装包信息
</p>
<p class="text-sm font-semibold text-text-heading break-all">
{{ TEACHER_APP_FILE_NAME }}
</p>
<p class="text-xs text-text-muted mt-1">
Windows 64 · {{ TEACHER_APP_FILE_SIZE }}
</p>
</div> </div>
<div class="flex flex-col sm:flex-row gap-3 w-full"> <div class="flex flex-col sm:flex-row gap-3 w-full">
@ -174,7 +222,9 @@ onUnmounted(() => {
</RouterLink> </RouterLink>
</div> </div>
<div class="mt-6 flex items-start gap-2 text-left w-full rounded-xl bg-amber-50/80 border border-amber-200/60 px-3 py-2.5"> <div
class="mt-6 flex items-start gap-2 text-left w-full rounded-xl bg-amber-50/80 border border-amber-200/60 px-3 py-2.5"
>
<AlertCircle class="w-4 h-4 text-amber-600 shrink-0 mt-0.5" /> <AlertCircle class="w-4 h-4 text-amber-600 shrink-0 mt-0.5" />
<p class="text-xs text-amber-900/80 leading-relaxed"> <p class="text-xs text-amber-900/80 leading-relaxed">
若未弹出下载请检查浏览器是否拦截弹窗或点击重新下载 若未弹出下载请检查浏览器是否拦截弹窗或点击重新下载