feat: init mainPage portfolio site
Vue 3 模块化作品集主页,浅色极简风格,中英双语懒加载 i18n。 - 模块注册表(import.meta.glob)约定式收集项目模块,新增页签无需改导航/路由 - i18n: 中文同步注入主 chunk,英文动态 import() 拆独立 chunk,仅切换时下载 - 第一个页签 mobile-game: 展示 448/449 移动游戏毕设(概览/截图轮播/版本时间线/团队/排行榜/ML视频) - 排行榜复用 /449/449rest.php,失败优雅降级 - 构建: vite outDir=/root/html,emptyOutDir=false,产物用 mainPage/ 命名空间 - 样式: CSS 变量令牌,无 UI 框架运行时依赖
This commit is contained in:
130
src/modules/mobile-game/components/ScreenshotCarousel.vue
Normal file
130
src/modules/mobile-game/components/ScreenshotCarousel.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { screenshots } from '../data/versions.js'
|
||||
|
||||
const { locale } = useI18n()
|
||||
const current = ref(0)
|
||||
let timer = null
|
||||
|
||||
function go(i) {
|
||||
current.value = (i + screenshots.length) % screenshots.length
|
||||
}
|
||||
function next() {
|
||||
go(current.value + 1)
|
||||
}
|
||||
function pause() {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
function resume() {
|
||||
if (!timer) timer = setInterval(next, 5000)
|
||||
}
|
||||
|
||||
onMounted(resume)
|
||||
onUnmounted(pause)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="carousel" @mouseenter="pause" @mouseleave="resume">
|
||||
<div class="carousel__viewport">
|
||||
<div class="carousel__track" :style="{ transform: `translateX(-${current * 100}%)` }">
|
||||
<figure v-for="(s, i) in screenshots" :key="i" class="carousel__slide">
|
||||
<img :src="s.src" :alt="locale === 'zh-CN' ? s.caption.zh : s.caption.en" loading="lazy" />
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="carousel__btn carousel__btn--prev" @click="go(current - 1)" aria-label="prev">‹</button>
|
||||
<button class="carousel__btn carousel__btn--next" @click="go(current + 1)" aria-label="next">›</button>
|
||||
|
||||
<div class="carousel__dots">
|
||||
<button
|
||||
v-for="(s, i) in screenshots"
|
||||
:key="i"
|
||||
class="carousel__dot"
|
||||
:class="{ 'is-active': i === current }"
|
||||
@click="go(i)"
|
||||
:aria-label="`slide ${i + 1}`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.carousel {
|
||||
position: relative;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.carousel__viewport {
|
||||
overflow: hidden;
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
.carousel__track {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
.carousel__slide {
|
||||
flex: 0 0 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.carousel__slide img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.carousel__btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-primary);
|
||||
font-size: 1.3rem;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: var(--shadow);
|
||||
transition: all var(--transition);
|
||||
}
|
||||
.carousel__btn:hover {
|
||||
background: #fff;
|
||||
}
|
||||
.carousel__btn--prev { left: 12px; }
|
||||
.carousel__btn--next { right: 12px; }
|
||||
|
||||
.carousel__dots {
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
.carousel__dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
transition: all var(--transition);
|
||||
}
|
||||
.carousel__dot.is-active {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
width: 22px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user