更改样式。增加UI动画

This commit is contained in:
wuyanchen 2025-12-26 05:37:37 +08:00
parent cc6ccb2f39
commit 3ebd866600
28 changed files with 5067 additions and 133 deletions

147
.gitignore vendored
View File

@ -1,143 +1,24 @@
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/
# ---> Node
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) node_modules
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist dist
dist-ssr
*.local
# Gatsby files # Editor directories and files
.cache/ .vscode/*
# Comment in the public line in if your project uses Gatsby and not Next.js !.vscode/extensions.json
# https://nextjs.org/blog/next-9-1#public-directory-support .idea
# public .DS_Store
*.suo
# vuepress build output *.ntvs*
.vuepress/dist *.njsproj
*.sln
# vuepress v2.x temp and cache directory *.sw?
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

19
index.html Normal file
View File

@ -0,0 +1,19 @@
<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>内向基金NX Fund</title>
<meta name="description" content="内向基金NX Fund是专注于消费升级领域的风险投资机构致力于发现和陪跑中国未来消费行业创新领袖成为新消费创业者最可信赖的「第一站」。">
<meta name="keywords" content="nxfund,内向基金,fund,基金,NX Fund,nx fund">
<link rel="icon" type="image/svg+xml" href="https://cdn.sya.org.cn/NXCaseLogo/favicon-brand.svg">
<link rel="icon" type="image/svg+xml" href="https://cdn.sya.org.cn/NXCaseLogo/favicon-brand.png">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

1441
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "web2",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"swiper": "^11.1.14",
"vue": "^3.4.37"
},
"devDependencies": {
"@types/node": "^22.5.5",
"@vitejs/plugin-vue": "^5.1.2",
"less": "^4.5.1",
"typescript": "^5.5.3",
"vite": "^5.4.1",
"vue-tsc": "^2.0.29"
},
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
}

15
public/vite.svg Normal file
View File

@ -0,0 +1,15 @@
<svg width="32" height="48" viewBox="0 0 32 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7_15)">
<path opacity="0.9" d="M0 45.175L10.6667 48V42.3583L0 45.175Z" fill="#494973"/>
<path opacity="0.9" d="M0 31.0583L10.6667 33.8833V28.2417L0 31.0583Z" fill="#494973"/>
<path opacity="0.9" d="M0 16.9417L10.6667 19.7667V14.125L0 16.9417Z" fill="#494973"/>
<path opacity="0.7" d="M32 28.2333L0 36.7083V45.175L32 36.7083V28.2333Z" fill="#494973"/>
<path opacity="0.7" d="M21.3333 16.9417L0 22.5917V31.0583L21.3333 25.4083V16.9417Z" fill="#494973"/>
<path opacity="0.7" d="M32 0L0 8.46667V16.9417L32 8.46667V0Z" fill="#494973"/>
</g>
<defs>
<clipPath id="clip0_7_15">
<rect width="32" height="48" fill="#494973"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 764 B

26
src/App.vue Normal file
View File

@ -0,0 +1,26 @@
<script setup lang="ts">
import Index from './pages/index/Index.vue'
// import SwiperWin from './components/swiper/SwiperWin.vue'
</script>
<template>
<div>
<Index />
<!-- <SwiperWin /> -->
</div>
</template>
<style scoped>
/* .logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
} */
</style>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

1
src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@ -0,0 +1,75 @@
.max-bg {
width: 100%;
height: 100%;
overflow: hidden;
}
.global-font {
font-family: 'DIN';
/* font-weight: normal; */
}
.global-font-bold {
font-weight: bold;
}
@font-face {
font-family: "DIN";
src: url('@/assets/fonts/DINCond-Bold.otf');
font-weight: normal;
font-style: normal;
}
.page-font {
position: absolute;
left: 0;
right: 0;
bottom: 0;
font-size: 24px;
z-index: 99;
}
.slide-container-path {
margin-top: 4%;
height: 87%;
display: flex;
align-items: center;
justify-content: center;
}
.swiper-size {
height: 100%;
}
.swiper-img {
width: 70%;
}
.swiper-slide {
width: 90% !important;
margin: 0 5%;
}
.swiper-slide-prev {
right: -30%;
}
.swiper-slide-next {
left: -30%;
}
.down-arraw-left {
position: absolute;
left: 14.2%;
top: 50%;
z-index: 100;
transform: rotateZ(90deg);
}
.down-arraw-right {
position: absolute;
right: 14.2%;
top: 50%;
z-index: 100;
transform: rotateZ(270deg);
}

View File

@ -0,0 +1,76 @@
import { defineComponent, ref } from 'vue';
// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Pagination, Scrollbar, A11y, Autoplay, Mousewheel } from 'swiper/modules';
// Import Swiper styles
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import 'swiper/css/scrollbar';
export default defineComponent({
name: 'SwiperWin',
props: {
images: Array<string>
},
components: {
Swiper,
SwiperSlide
},
data() {
return {
}
},
// setup(props, context) {
setup(props: any) {
// const images = [
// util.getAssetsFile('imgs/swiper/swiper1.jpg'),
// util.getAssetsFile('imgs/swiper/swiper1.jpg'),
// util.getAssetsFile('imgs/swiper/swiper1.jpg')
// ]
const images = props.images as Array<string>
const refImages = ref(images)
const swiper = ref()
let realSwiperPage = ref(1)
const onSwiper = (swiperInst: any) => {
// swiper.activeIndex = 0
swiper.value = swiperInst
swiper.value.slideNext(0)
};
const onSlideChange = (swiper: any) => {
// console.log('slide change');
// console.log(swiper)
realSwiperPage.value = swiper.realIndex + 1
}
const slideNext = () => {
swiper.value.slideNext()
}
const slidePrev = () => {
swiper.value.slidePrev()
}
// const toNext = () => {
// swiper.value.slideNext(swiper.value.realIndex + 1)
// }
return {
// onSwiper,
images,
refImages,
realSwiperPage,
onSlideChange,
onSwiper,
slideNext,
slidePrev,
modules: [Navigation, Pagination, Scrollbar, A11y, Autoplay, Mousewheel]
// modules: [Navigation, Pagination, Scrollbar, A11y]
}
}
})

View File

@ -0,0 +1,53 @@
<script lang="ts">
import swiperWin from './SwiperWin'
export default swiperWin
</script>
<template>
<div>
<!-- <swiper :options="swiperOptions">
<swiper-slide>
<img src="@/assets/imgs/swiper/swiper1.jpg"
/></swiper-slide>
<swiper-slide>
<img src="@/assets/imgs/swiper/swiper1.jpg"
/></swiper-slide>
<swiper-slide>
<img src="@/assets/imgs/swiper/swiper1.jpg"
/></swiper-slide>
</swiper> -->
<!-- :autoplay="{ delay: 5000 }" -->
<swiper
class="swiper-size"
@swiper="onSwiper"
:modules="modules"
:slides-per-view="'auto'"
:loop="true"
:mousewheel="true"
:autoplay="{ delay: 5000, disableOnInteraction: false }"
@slideChange="onSlideChange"
>
<swiper-slide v-for="(img, index) in refImages" :key="index">
<div class="slide-container-path">
<img :src="img" class="swiper-img" />
</div>
</swiper-slide>
</swiper>
<img
src="@/assets/imgs/Down Arraw Purple.svg"
class="down-arraw-left"
@click="slidePrev"
/>
<img
src="@/assets/imgs/Down Arraw Purple.svg"
class="down-arraw-right"
@click="slideNext"
/>
<!-- <div class="global-font page-font">
{{ realSwiperPage }}/{{ refImages.length }}
</div> -->
</div>
</template>
<style scoped src="./SwiperWin.css"></style>

5
src/main.ts Normal file
View File

@ -0,0 +1,5 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')

1
src/module.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'vue-awesome-swiper2'

1966
src/pages/index/Index.less Normal file

File diff suppressed because it is too large Load Diff

294
src/pages/index/Index.ts Normal file
View File

@ -0,0 +1,294 @@
import { defineComponent, onMounted, onBeforeUnmount, ref } from 'vue'
import SwiperWin from '../../components/swiper/SwiperWin.vue'
import utils from '../../util/utils'
export default defineComponent({
name: 'Index',
components: {
SwiperWin
},
data() {
return {
}
},
// setup(props, context) {
setup() {
const fullPageEl = ref<HTMLElement>()
const fullPageContainerEl = ref<HTMLElement>()
const swiperWindowEl = ref<HTMLElement>()
const fullpage = {
current: 1, // 当前的页面编号
isScrolling: false, // 是否在滚动,是为了防止滚动多页,需要通过一个变量来控制是否滚动
deltaY: 0 // 返回鼠标滚轮的垂直滚动量保存的鼠标滚动事件的deleteY,用来判断是往下还是往上滚
};
let startTime: number; // 记录触摸开始的时间
// let startX: number; // 记录触摸开始的X坐标本次主要实现的是上下滑动所以当前坐标不做强制要求
let startY: number; // 记录触摸开始的Y坐标
let len = 4; // 页面的个数
const showArraw = ref(true);
const showOptionsPortrait = ref(false);
const showOptionsLandspace = ref(false);
const showDetailWinPortrait = ref(false);
const showDetailWinLandspace = ref(false);
const aboutusimg = ref<HTMLElement>()
const contactus = ref<HTMLElement>()
let isLandspaceTag = true
const resizeHandler = () => {
if (isLandspaceTag) {
if (!isLandspace()) {
move(1)
fullPageContainerEl.value!.style.transform = "translateY(0)"
isLandspaceTag = isLandspace()
}
}
else {
if (isLandspace()) {
move(1)
fullPageContainerEl.value!.style.transform = "translateY(0)"
isLandspaceTag = isLandspace()
}
}
}
onMounted(() => {
isLandspaceTag = isLandspace()
resizeHandler()
window.addEventListener("resize", resizeHandler)
})
onBeforeUnmount(() => {
window.removeEventListener("resize", resizeHandler)
})
const hideOptionsPage = () => {
showOptionsPortrait.value = false
showOptionsLandspace.value = false
showArraw.value = true
}
const showOptionsPage = () => {
hideOptionsPage()
if (isLandspace()) {//横屏
showOptionsLandspace.value = true
}
else {
showOptionsPortrait.value = true
}
showArraw.value = false
}
const showSwiperMaskWin = (images: Array<string>) => {
bannerImages.value = images
if (isLandspace()) {//横屏
showDetailWinPortrait.value = false
showDetailWinLandspace.value = true
}
else {//竖屏
showDetailWinLandspace.value = false
showDetailWinPortrait.value = true
}
showArraw.value = false
}
const stopEvent = (ev: Event) => {
ev.stopPropagation()
// ev.preventDefault()
}
const hideSwiperMaskWin = () => {
// bannerImages.value = []
showDetailWinPortrait.value = false
showDetailWinLandspace.value = false
showArraw.value = true
}
// 往下切换
const next = () => {
if (!isLandspace()) {
return
}
if (fullpage.current + 1 <= len) { // 如果当前页面编号+1 小于总个数,则可以执行向下滑动
fullpage.current += 1; // 页面+1
move(fullpage.current); // 执行切换
}
if (fullpage.current >= len) {
showArraw.value = false
}
else {
showArraw.value = true
}
}
// 往上切换
const pre = () => {
if (!isLandspace()) {
return
}
if (fullpage.current - 1 > 0) { // 如果当前页面编号-1 大于0则可以执行向下滑动
fullpage.current -= 1;// 页面+1
move(fullpage.current);// 执行切换
showArraw.value = true
}
}
// 滚动事件
const move = (index: number) => {
fullpage.current = index;
fullpage.isScrolling = true; // 为了防止滚动多页,需要通过一个变量来控制是否滚动
directToMove(index); //执行滚动
setTimeout(() => { //这里的动画是1s执行完使用setTimeout延迟1s后解锁
fullpage.isScrolling = false;
}, 1000);
}
// 执行滚动
const directToMove = (index: number) => {
let height = fullPageEl.value!.clientHeight; //获取屏幕的宽度
let scrollPage = fullPageContainerEl.value!; // 获取执行tarnsform的元素
let scrollHeight; // 计算滚动的告诉,是往上滚还往下滚
scrollHeight = -(index - 1) * height + "px";
scrollPage.style.transform = `translateY(${scrollHeight})`;
fullpage.current = index;
}
// 监听鼠标监听
const mouseWheelHandle = (event: any) => {
// 添加冒泡阻止
let evt = event || window.event;
if (evt.stopPropagation) {
evt.stopPropagation();
} else {
evt.returnValue = false;
}
if (fullpage.isScrolling) { // 判断是否可以滚动
return false;
}
let e = event.originalEvent || event;
fullpage.deltaY = e.deltaY || e.detail; // Firefox使用detail
if (fullpage.deltaY > 0) {
next();
} else if (fullpage.deltaY < 0) {
pre();
}
}
// 清除触摸事件
const handleTouchmove = (event: any) => {
if (isLandspace()) {
event.preventDefault()
}
}
//手指按下屏幕
const handleTouchstart = (event: any) => {
startTime = Date.now()
// startX = event.changedTouches[0].clientX
startY = event.changedTouches[0].clientY
}
//手指离开屏幕
const handleTouchend = (event: any) => {
const endTime = Date.now()
// const endX = event.changedTouches[0].clientX
const endY = event.changedTouches[0].clientY
//判断按下的时长
if (endTime - startTime > 2000) {
return
}
//滑动的方向
let direction = "";
//先判断用户滑动的距离,是否合法,合法:判断滑动的方向 注意 距离要加上绝对值
if (Math.abs(endY - startY) > 10) {
//滑动方向
direction = endY - startY > 0 ? "down" : "up"
} else {
return
}
//用户做了合法的滑动操作
// console.log('方向'+direction)
if (direction === 'up') {
next();
}
if (direction === 'down') {
pre();
}
}
const hotelImages = [
utils.getAssetsFile('imgs/banners/hotel/3.jpg'),
utils.getAssetsFile('imgs/banners/hotel/1.jpg'),
utils.getAssetsFile('imgs/banners/hotel/2.jpg')
]
const restaurantImages = [
utils.getAssetsFile('imgs/banners/restaurant/3.jpg'),
utils.getAssetsFile('imgs/banners/restaurant/1.jpg'),
utils.getAssetsFile('imgs/banners/restaurant/2.jpg')
]
const schoolImages = [
utils.getAssetsFile('imgs/banners/school/3.jpg'),
utils.getAssetsFile('imgs/banners/school/1.jpg'),
utils.getAssetsFile('imgs/banners/school/2.jpg')
]
const franchiseImages = [
utils.getAssetsFile('imgs/banners/franchise/3.jpg'),
utils.getAssetsFile('imgs/banners/franchise/1.jpg'),
utils.getAssetsFile('imgs/banners/franchise/2.jpg')
]
const bannerImages = ref<Array<string>>([])
const isLandspace = () => {
return document.body.clientWidth > document.body.clientHeight
}
return {
fullPageEl,
fullPageContainerEl,
mouseWheelHandle,
handleTouchmove,
handleTouchstart,
handleTouchend,
showOptionsPage,
hideOptionsPage,
showArraw,
showOptionsPortrait,
showOptionsLandspace,
move,
pre,
next,
swiperWindowEl,
hotelImages,
restaurantImages,
schoolImages,
franchiseImages,
bannerImages,
showDetailWinPortrait,
showDetailWinLandspace,
showSwiperMaskWin,
hideSwiperMaskWin,
isLandspace,
aboutusimg,
// calcOneImageSize,
contactus,
stopEvent
}
}
})

698
src/pages/index/Index.vue Normal file
View File

@ -0,0 +1,698 @@
<template>
<div class="max-bg" ref="fullPageEl">
<main class="bg-brand">
<div class="relative w-full h-screen overflow-hidden">
<div class="bg-brand movement scale_rotate h-screen z-0 overflow-hidden">
<img src="https://cdn.sya.org.cn/NXCaseLogo/bgd.svg" alt="背景图片"
class="absolute h-full w-full object-cover" />
</div>
<!-- 封面页 -->
<section class="absolute top-0 left-0 w-full h-screen transform-gpu transition-all duration-1000">
<div
class="flex flex-col justify-center items-center w-full h-full bg-brand bg-white transition-colors cover-content">
<div
class="text-white text-black text-sm font-light leading-8 text-center transition-colors cover-text transform-gpu opacity-0 translate-y-20">
<div style="width: 162px; height: auto; margin: auto"
class="cover-logo-container transform-gpu opacity-0 translate-y-20">
<img src="@/assets/imgs/logo-test.png" alt="内向基金LOGO" class="CoverSlide_logo__2K2qV opacity-100"
style="width: 162px; height: auto" />
</div>
<div
class="w-32 h-auto m-auto mt-8 mb-32 CoverSlide_opacityContent__3oJ1x cover-title transform-gpu opacity-0 translate-y-20">
<p class="text-white flex-nowrap" style="font-family: insight-fund; font-size: 32px">
内向基金
</p>
</div>
<p class="CoverSlide_opacityContent__3oJ1x cover-info transform-gpu opacity-0 translate-y-20">上海 · 中国</p>
<p class="CoverSlide_opacityContent__3oJ1x cover-info transform-gpu opacity-0 translate-y-20">
<span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" stroke="currentColor" fill="transparent"
class="inline-block align-text-bottom w-4 h-4">
<circle cx="5.5" cy="5.5" r="5"></circle>
<path d="M4.012943033121648 3.490357848456956L5.5 5.5L2.07648339743168 4.772309082137843"></path>
</svg>
{{ currentDate }}
</span>
</p>
</div>
</div>
<a href="#" class="absolute animate-bounce md:bottom-12 bottom-7 left-1/2 w-16 h-4 -ml-8 z-50 cursor-pointer" @click="handleScroll(100);">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 16" fill="currentColor"
class="w-full h-full text-white">
<path d="M64 0L48 6L32 12L16 6L0 0V4L32 16L64 4V0Z"></path>
</svg>
</a>
</section>
<!-- 使命声明页 -->
<section
class="absolute top-0 left-0 w-full h-screen transform-gpu translate-y-full transition-all duration-1000 flex flex-col items-center justify-center px-6 md:px-8 mission-section">
<div
class="z-10 text-center text-2xl md:text-3xl text-white transform-gpu mission-text text-animate transform-gpu"
style="font-family: slogan-handwrite">
<div class="mission-line text-animate transform-gpu">
<span class="transform-gpu inline-block text-gold -translate-y-4 opacity-0 quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129">
</span>
专业
<span class="transform-gpu inline-block text-gold translate-y-4 opacity-0 quote-mark quote-right"
style="text-shadow: 0.15em 0.15em 0 #ecc129">
</span>
<span class="transform-gpu inline-block text-gold -translate-y-4 opacity-0 quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129">
</span>
友好
<span class="transform-gpu inline-block text-gold translate-y-4 opacity-0 quote-mark quote-right"
style="text-shadow: 0.15em 0.15em 0 #ecc129">
</span>
</div>
<div class="opacity-0 translate-y-20 mt-4 mission-line text-animate transform-gpu">
做新消费创业者最可信赖的
<span class="transform-gpu inline-block text-gold -translate-y-4 opacity-0 quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129">
</span>
第一站
<span class="transform-gpu inline-block text-gold translate-y-4 opacity-0 quote-mark quote-right"
style="text-shadow: 0.15em 0.15em 0 #ecc129">
</span>
</div>
</div>
</section>
<!-- 介绍页 -->
<section
class="absolute top-0 left-0 w-full h-screen transform-gpu translate-y-full transition-all duration-1000 flex flex-col items-center justify-center px-6 md:px-8 intro-section">
<div class="absolute min-h-screen min-w-screen w-full h-full"></div>
<div
class="max-w-4xl px-4 md:px-6 z-10 md:text-2xl text-lg font-normal md:leading-15 leading-9 text-white intro-text text-animate transform-gpu">
<p class="intro-paragraph text-animate transform-gpu">
<span class="transform-gpu inline-block -translate-y-4 opacity-0 text-gold quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129"></span>
内向基金
<span class="transform-gpu inline-block translate-y-4 opacity-0 text-gold quote-mark quote-right"
style="text-shadow: 0.15em 0.15em 0 #ecc129"></span>
2020年夏天诞生于新消费时代专注早期消费投资<br />
</p>
<p class="transform-gpu opacity-0 translate-y-20 intro-paragraph text-animate">
我们持续寻找和发掘新消费领域的优秀创业者助力
<span class="transform-gpu inline-block -translate-y-4 opacity-0 text-gold quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129"></span>
务实的理想主义者
<span class="transform-gpu inline-block translate-y-4 opacity-0 text-gold quote-mark quote-right"
style="text-shadow: 0.15em 0.15em 0 #ecc129"></span>
成长为未来商业领袖以创新和爱让世界更美好
</p>
</div>
</section>
<!-- 孙婷婷页 -->
<section
class="absolute top-0 left-0 w-full h-screen transform-gpu translate-y-full transition-all duration-1000 flex items-center justify-center px-8 partner-section">
<div class="partner-content flex flex-col md:flex-row items-center gap-6 md:gap-16">
<div class="partner-image">
<img class="transform-gpu translate-y-20 opacity-0 partner-img-animation text-animate"
src="https://cdn.sya.org.cn/NXCaseLogo/Tingting.png" alt="孙婷婷" loading="eager" />
</div>
<div class="partner-text">
<div class="text-brand text-fxl font-bold mb-3">
<div class="w-auto h-auto overflow-hidden">
<div class="transform-gpu incline translate-y-10 opacity-0 text-animate partner-text-1">
孙婷婷 / 内向基金合伙人
</div>
</div>
<div class="line-container">
<div class="line-static" style="height: 4px; background-color: #ECC129; width: 73px;"></div>
<div class="line-animated transform-gpu" style="height: 4px; background-color: #2E3192;"></div>
</div>
</div>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
2016年至今担任穆棉资本合伙人穆棉资本是中国领先的新经济精品投行专注并深耕于消费和科技领域
</p>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
在联合创立穆棉资本之前孙婷婷曾在挚信资本从事私募股权投资工作多年在新零售消费品牌企业服务文化娱乐教育和医疗等领域有丰富的直接投资和投后管理经验此前曾在中海信托股份有限公司工作从事股权投资工作
</p>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
2020年联合创立
<span class="transform-gpu inline-block -translate-y-4 opacity-0 text-gold quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129"></span>
内向基金
<span class="transform-gpu inline-block translate-y-4 opacity-0 text-gold quote-mark quote-right "
style="text-shadow: 0.15em 0.15em 0 #ecc129"></span>
专注早期消费投资
</p>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
复旦大学 金融学学士
</p>
</div>
</div>
</section>
<!-- 应金凤页 -->
<section
class="absolute top-0 left-0 w-full h-screen transform-gpu translate-y-full transition-all duration-1000 flex items-center justify-center px-8 partner-section">
<div class="partner-content flex flex-col md:flex-row-reverse items-center gap-6 md:gap-16">
<div class="partner-image">
<img class="transform-gpu translate-y-20 opacity-0 partner-img-animation"
src="https://cdn.sya.org.cn/NXCaseLogo/Stefanie.png" alt="应金凤" loading="eager" />
</div>
<div class="partner-text">
<div class="text-brand text-fxl font-bold mb-3">
<div class="w-auto h-auto overflow-hidden">
<div class="transform-gpu incline translate-y-10 opacity-0 partner-text-1">
应金凤 / 内向基金合伙人
</div>
</div>
<div class="line-container">
<div class="line-static" style="height: 4px; background-color: #ECC129; width: 73px;"></div>
<div class="line-animated transform-gpu" style="height: 4px; background-color: #2E3192;"></div>
</div>
</div>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
2015年创立穆棉资本穆棉资本是中国领先的新经济精品投行致力于打造研究驱动的价值创造型精品投行
</p>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
在创立穆棉资本之前Stefanie曾在挚信资本从事私募股权投资工作多年在消费教育社区社交医疗企业服务和金融科技等领域有丰富的直接投资和投后管理经验此前曾在普华永道会计师事务所工作参与多个知名私募基金的审计工作
</p>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
2020年联合创立
<span class="transform-gpu inline-block -translate-y-4 opacity-0 text-gold quote-mark quote-left"
style="text-shadow: -0.15em -0.15em 0 #ecc129"></span>
内向基金
<span class="transform-gpu inline-block translate-y-4 opacity-0 text-gold quote-mark quote-right"
style="text-shadow: 0.15em 0.15em 0 #ecc129"></span>
专注早期消费投资
</p>
<p
class="my-3 md:my-4 text-xs md:leading-8 transform-gpu text-grayy text-base opacity-0 translate-y-20 text-animate">
浙江大学 经济学学士和硕士
</p>
</div>
</div>
</section>
<!-- 投资案例页 -->
<section
class="absolute top-0 left-0 w-full h-screen translate-y-full transition-all duration-1000 flex items-center justify-center px-4 bg-white">
<div class="cases-content w-full max-w-5xl flex flex-col justify-center">
<div
class="text-brand text-2xl md:text-3xl leading-10 text-center font-bold mb-8 text-animate transform-gpu case-title">
投资案例
<div class="bg-brand flex h-1 mt-3 mx-auto"></div>
</div>
<div class="cases-grid" v-show="isAnimateInited">
<!-- 第一行 -->
<div class="cases-row transform-gpu">
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="sweet7" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="acacia" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="霸蛮" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="sweet7" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="acacia" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="霸蛮" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="sweet7" />
</a>
<a href="#" class="case-item">
<img src="@/assets/imgs/logo-test.png" alt="acacia" />
</a>
</div>
</div>
</div>
</section>
<!-- 联系我们页 -->
<section
class="absolute top-0 left-0 w-full h-screen transform-gpu translate-y-full transition-all duration-1000 flex items-center justify-center px-8 bg-white">
<div class="flex flex-col items-center justify-center text-center z-20 font-light">
<div
class="flex flex-col text-brand text-2xl leading-10 text-center text-animate transform-gpu font-bold justify-center items-center mb-6 opacity-0 translate-y-20">
联系我们
<div class="bg-brand flex h-1 mt-3 mx-auto"></div>
</div>
<div
class="text-xs leading-loose md:text-sm md:leading-8 transform-gpu opacity-0 translate-y-20 text-animate transform-gpu">
<div class="font-normal font-black text-animate transform-gpu">
上海市 静安区<br />南京西路1266号 恒隆广场2期 2802<br />
</div>
<br />
<span class="font-normal font-black text-animate transform-gpu">项目投递</span><span
class="font-black text-animate transform-gpu">bp@nxfund.com</span><br />
<span class="font-normal font-black text-animate transform-gpu">招聘投递</span><span
class="font-black text-animate transform-gpu">hr@nxfund.com</span><br />
<span class="font-normal font-black text-animate transform-gpu">商务合作</span><span
class="font-black text-animate transform-gpu">pr@nxfund.com</span>
</div>
<div class="mt-6">
<div
class="text-xs font-normal font-black leading-loose md:text-sm md:leading-8 transform-gpu opacity-0 translate-y-20 gongzhonghao-title">
内向基金微信公众号
</div>
<div style="
display: inline-block;
max-width: 100%;
overflow: hidden;
position: relative;
box-sizing: border-box;
margin: 0;
">
<div style="
box-sizing: border-box;
display: block;
max-width: 100%;
">
<img style="
max-width: 100%;
display: block;
margin: 0;
border: none;
padding: 0;
" alt="" aria-hidden="true"
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2ZXJzaW9uPSIxLjEiLz4=" />
</div>
<img alt="内向基金微信公众号" srcset="
https://cdn.sya.org.cn/NXCaseLogo/QRcode.png?auto=format&fit=max&w=128 1x,
https://cdn.sya.org.cn/NXCaseLogo/QRcode.png?auto=format&fit=max&w=256 2x
" src="https://cdn.sya.org.cn/NXCaseLogo/QRcode.png?auto=format&fit=max&w=256" decoding="async"
style="
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
box-sizing: border-box;
padding: 0;
border: none;
margin: auto;
display: block;
width: 0;
height: 0;
min-width: 100%;
max-width: 100%;
min-height: 100%;
max-height: 100%;
" />
</div>
<div class="absolute text-xxs bottom-5 font-black text-animate transform-gpu"
style="left: 50%; transform: translate(-50%)">
© NX FUND 2021 ICP : 沪ICP备2021034894号-2
</div>
</div>
</div>
<div class="absolute md:right-auto -right-32 top-20">
<div style="
overflow: hidden;
box-sizing: border-box;
display: inline-block;
position: relative;
width: 1000px;
height: 530px;
">
<img alt="" srcset="
https://cdn.sya.org.cn/NXCaseLogo/WorldMap.png?auto=format&fit=max&w=1080 1x,
https://cdn.sya.org.cn/NXCaseLogo/WorldMap.png?auto=format&fit=max&w=2048 2x
" src="https://cdn.sya.org.cn/NXCaseLogo/WorldMap.png?auto=format&fit=max&w=2048" decoding="async"
style="
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
box-sizing: border-box;
padding: 0;
border: none;
margin: auto;
display: block;
width: 0;
height: 0;
min-width: 100%;
max-width: 100%;
min-height: 100%;
max-height: 100%;
" />
</div>
<div class="absolute" style="top: 180px; right: 150px">
<div class="w-8 h-8 flex flex-wrap ml-auto text-brand animate-pulse">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M18.0521 18.0576H22.1736L32.0002 27.8784C30.8415 29.4516 29.452 30.8411 27.8788 31.9998L18.0521 22.1791V18.0576Z"
fill="#2E3192"></path>
<path
d="M18.0521 13.9481H22.1736L32.0002 4.12144C30.8415 2.54821 29.452 1.15876 27.8788 0L18.0521 9.83846V13.9481Z"
fill="#2E3192"></path>
<path
d="M13.9481 13.9481H9.82669L0 4.12144C1.15876 2.54821 2.54821 1.15876 4.12144 0L13.9481 9.82669V13.9481Z"
fill="#2E3192"></path>
<path
d="M13.9481 18.0576H9.82669L0 27.8784C1.15876 29.4516 2.54821 30.8411 4.12144 31.9998L13.9481 22.1791V18.0576Z"
fill="#ECC129"></path>
</svg>
</div>
</div>
</div>
</section>
</div>
</main>
</div>
</template>
<script lang="ts">
import {
defineComponent,
onMounted,
onBeforeUnmount,
ref,
nextTick,
} from "vue";
export default defineComponent({
name: "Index",
setup() {
const fullPageEl = ref<HTMLElement | null>(null);
const currentIndex = ref(0);
const totalSections = 7;
let isScrolling = false;
let startY = 0;
let isTouching = false;
let animationFrameId: number | null = null;
const isAnimateInited = ref(false);
const formatDate = () => {
const now = new Date();
const days = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
const year = now.getFullYear();
const month = now.getMonth() + 1;
const date = now.getDate();
const day = days[now.getDay()];
const hours = now.getHours();
const minutes = now.getMinutes().toString().padStart(2, "0");
const ampm = hours >= 12 ? "PM" : "AM";
const displayHours = hours % 12 || 12;
return `${year}${month}${date}${day} ${displayHours}:${minutes} ${ampm}`;
};
const currentDate = ref(formatDate());
//
const timer = setInterval(() => {
currentDate.value = formatDate();
}, 60000);
let isAnimating = false;
const handleScroll = (delta: number) => {
//
if (isScrolling || isAnimating) return;
//
const scrollThreshold = 50;
if (Math.abs(delta) < scrollThreshold) return;
if (delta > 0 && currentIndex.value < totalSections - 1) {
currentIndex.value++;
isAnimating = true;
isScrolling = true;
// CSSJSCSS1000ms
const animationDuration = 1000;
setTimeout(() => {
isAnimating = false;
}, animationDuration);
// JSCSS
setTimeout(() => {
isScrolling = false;
}, animationDuration * 0.8);
animateToSection(currentIndex.value);
} else if (delta < 0 && currentIndex.value > 0) {
currentIndex.value--;
isAnimating = true;
isScrolling = true;
const animationDuration = 1000;
setTimeout(() => {
isAnimating = false;
}, animationDuration);
setTimeout(() => {
isScrolling = false;
}, animationDuration * 0.8);
animateToSection(currentIndex.value);
}
};
const animateToSection = (sectionIndex: number) => {
const sections = document.querySelectorAll("section");
//
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
// section
sections.forEach((section, index) => {
const el = section as HTMLElement;
el.classList.remove("active", "prev", "next");
if (index < sectionIndex) {
el.classList.add("prev");
//
setTimeout(() => animateElements(el, false), 600);
} else if (index === sectionIndex) {
el.classList.add("active");
//
setTimeout(() => animateElements(el, true), 600);
} else {
el.classList.add("next");
//
setTimeout(() => animateElements(el, false), 100);
}
});
isScrolling = false;
};
const animateLine = (line: HTMLElement) => {
// 线
requestAnimationFrame(() => {
line.classList.remove('animate-in');
//
void line.offsetWidth;
line.classList.add('animate-in');
});
};
const resetLine = (line: HTMLElement) => {
// 线
line.classList.remove('animate-in');
};
const animateElements = (section: Element, isActive: boolean) => {
// 使requestAnimationFrame
requestAnimationFrame(() => {
const animatedElements = section.querySelectorAll(".transform-gpu");
animatedElements.forEach((el) => {
const htmlEl = el as HTMLElement;
if (isActive) {
// -
htmlEl.classList.remove(
"opacity-0",
"-translate-y-4",
"-translate-y-5",
"translate-y-4",
"translate-y-5",
"translate-y-10",
"translate-y-20",
"translate-y-40",
"-translate-y-20",
"translate-y-96"
);
void htmlEl.offsetWidth; //
htmlEl.classList.add("opacity-100", "translate-y-0");
//
if (htmlEl.classList.contains("quote-mark")) {
htmlEl.classList.add("animate-in");
}
if (htmlEl.classList.contains("partner-img-animation")) {
htmlEl.classList.add("animate-in");
}
if (htmlEl.classList.contains("cases-row")) {
htmlEl.classList.add("animate-in");
}
if (htmlEl.classList.contains("cover-logo-container")) {
htmlEl.classList.add("animate-in");
}
if (htmlEl.classList.contains("cover-title")) {
htmlEl.classList.add("animate-in");
}
if (htmlEl.classList.contains("text-animate")) {
htmlEl.classList.add("animate-in");
}
if (htmlEl.classList.contains("quote-left")) {
setTimeout(() => {
htmlEl.classList.add("animate-in");
}, 1200);
}
if (htmlEl.classList.contains("quote-right")) {
setTimeout(() => {
htmlEl.classList.add("animate-in");
}, 1200);
}
if (htmlEl.classList.contains("line-animated")) {
// 线
animateLine(htmlEl);
}
} else {
//
htmlEl.classList.remove("opacity-100", "translate-y-0", "animate-in");
void htmlEl.offsetWidth; //
htmlEl.classList.add("opacity-0");
//
if (htmlEl.classList.contains("incline")) {
htmlEl.classList.add("translate-y-10");
} else if (htmlEl.classList.contains("quote-left")) {
htmlEl.classList.add("-translate-y-4");
} else if (htmlEl.classList.contains("quote-right")) {
htmlEl.classList.add("translate-y-4");
} else if (htmlEl.classList.contains("line-animated")) {
// 线
resetLine(htmlEl);
} else {
htmlEl.classList.add("translate-y-20");
}
}
});
});
};
const onWheel = (e: WheelEvent) => {
if (isTouching) return;
e.preventDefault();
const delta = e.deltaY;
handleScroll(delta);
};
const onTouchStart = (e: TouchEvent) => {
isTouching = true;
startY = e.touches[0].clientY;
};
const onTouchMove = (e: TouchEvent) => {
if (!isTouching) return;
e.preventDefault(); //
};
const onTouchEnd = (e: TouchEvent) => {
if (!isTouching) return;
const endY = e.changedTouches[0].clientY;
const delta = startY - endY;
if (Math.abs(delta) > 50) {
handleScroll(delta);
}
isTouching = false;
};
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "ArrowDown" || e.key === "PageDown") {
handleScroll(100);
} else if (e.key === "ArrowUp" || e.key === "PageUp") {
handleScroll(-100);
}
};
onMounted(async () => {
await nextTick();
if (fullPageEl.value) {
fullPageEl.value.addEventListener("wheel", onWheel, { passive: false });
fullPageEl.value.addEventListener("touchstart", onTouchStart, {
passive: false,
});
fullPageEl.value.addEventListener("touchmove", onTouchMove, {
passive: false,
});
fullPageEl.value.addEventListener("touchend", onTouchEnd, {
passive: false,
});
window.addEventListener("keydown", onKeyDown);
// -
const sections = document.querySelectorAll("section");
sections.forEach((section, index) => {
const el = section as HTMLElement;
if (index === 0) {
el.classList.add("active");
setTimeout(() => {
animateElements(el, true);
isAnimateInited.value = true;
}, 300);
}
else {
el.classList.add("next");
animateElements(el, false);
}
});
}
});
onBeforeUnmount(() => {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
if (timer) {
clearInterval(timer);
}
if (fullPageEl.value) {
fullPageEl.value.removeEventListener("wheel", onWheel);
fullPageEl.value.removeEventListener("touchstart", onTouchStart);
fullPageEl.value.removeEventListener("touchmove", onTouchMove);
fullPageEl.value.removeEventListener("touchend", onTouchEnd);
}
window.removeEventListener("keydown", onKeyDown);
});
return {
fullPageEl,
currentIndex,
currentDate,
isAnimateInited,
handleScroll,
};
},
});
</script>
<style src="./Index.less" lang="less"></style>

238
src/style.css Normal file
View File

@ -0,0 +1,238 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
overflow: hidden;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
/* max-width: 1280px; */
margin: 0 auto;
padding: 0;
text-align: center;
width: 100%;
height: 100%;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
/* 竖屏响应式优化 */
@media screen and (orientation: portrait) and (max-width: 768px) {
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
overflow: hidden;
}
#app {
margin: 0 auto;
padding: 1rem;
text-align: center;
width: 100%;
height: 100%;
box-sizing: border-box;
}
/* 文字大小自适应 */
h1 {
font-size: 2.2em;
line-height: 1.2;
}
h2 {
font-size: 1.8em;
}
h3 {
font-size: 1.4em;
}
p {
font-size: 1rem;
line-height: 1.6;
max-width: 100%;
margin: 0 auto;
padding: 0 1rem;
}
/* 容器居中 */
.partner-section,
.mission-section,
.intro-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1rem;
}
/* 合伙人内容居中 */
.partner-content {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
max-width: 100%;
width: 100%;
}
/* 合伙人文字放大 */
.partner-text p {
font-size: 1.1rem;
line-height: 1.7;
max-width: 90%;
margin: 0.5rem auto;
}
.partner-text .text-fxl {
font-size: 1.8rem;
margin-bottom: 1rem;
}
/* 投资案例区域居中 */
.cases-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
max-width: 100%;
margin: 0 auto;
}
.case-item {
flex: 0 1 calc(33.333% - 1rem);
margin: 0.5rem;
display: flex;
justify-content: center;
align-items: center;
}
/* 图片响应式 */
img {
max-width: 100%;
height: auto;
object-fit: contain;
}
/* 文字区域宽度自适应 */
.text-center {
max-width: 95%;
margin: 0 auto;
padding: 0 1rem;
}
}
/* 横屏优化 */
@media screen and (orientation: landscape) and (max-height: 500px) {
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
overflow: hidden;
}
#app {
margin: 0 auto;
padding: 0.5rem;
text-align: center;
width: 100%;
height: 100%;
}
/* 文字大小调整 */
h1 {
font-size: 2.5em;
line-height: 1.1;
}
.partner-text p {
font-size: 0.95rem;
line-height: 1.5;
}
.partner-text .text-fxl {
font-size: 1.5rem;
}
/* 合伙人内容横向排列 */
.partner-content {
flex-direction: row;
gap: 2rem;
max-width: 90%;
}
}

7
src/util/utils.ts Normal file
View File

@ -0,0 +1,7 @@
// 获取assets静态资源
const getAssetsFile = (url: string) => {
return new URL(`../assets/${url}`, import.meta.url).href;
};
export default {
getAssetsFile,
};

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

24
tsconfig.app.json Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

1
tsconfig.app.tsbuildinfo Normal file
View File

@ -0,0 +1 @@
{"root":["./src/main.ts","./src/module.d.ts","./src/vite-env.d.ts","./src/components/swiper/swiperwin.ts","./src/pages/index/index.ts","./src/util/utils.ts","./src/app.vue","./src/components/helloworld.vue","./src/components/swiper/swiperwin.vue","./src/pages/index/index.vue"],"version":"5.6.2"}

7
tsconfig.json Normal file
View File

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

22
tsconfig.node.json Normal file
View File

@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

View File

@ -0,0 +1 @@
{"root":["./vite.config.ts"],"version":"5.6.2"}

14
vite.config.ts Normal file
View File

@ -0,0 +1,14 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: './',
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
})