Compare commits
20 Commits
6c1452be4d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4af58134fd | ||
|
|
e36b17642f | ||
|
|
57835e6416 | ||
|
|
892c568efc | ||
|
|
a92ceff8e4 | ||
|
|
2cf20d1b81 | ||
|
|
27a280ab46 | ||
|
|
1c84d3949f | ||
|
|
f282adfd87 | ||
|
|
09af83050a | ||
|
|
4587a59a89 | ||
|
|
d237dab098 | ||
| 6fc1da2884 | |||
| 5158b63ddd | |||
|
|
85708bb5a4 | ||
|
|
99af8da607 | ||
|
|
9235843f25 | ||
|
|
1153bce74e | ||
| e797612de6 | |||
| 3edda93691 |
@@ -5,9 +5,10 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>test-ssg</title>
|
||||
<link rel="stylesheet" href="https://at.alicdn.com/t/c/font_4403230_2woxf8yp826.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -11,11 +11,12 @@
|
||||
"dependencies": {
|
||||
"@kagol/vue-carousel": "^0.1.2",
|
||||
"@unhead/vue": "^2.1.15",
|
||||
"unhead": "2.1.15",
|
||||
"gsap": "^3.15.0",
|
||||
"less": "^4.6.4",
|
||||
"unhead": "2.1.15",
|
||||
"vite-ssg": "^28.3.0",
|
||||
"vue": "^3.5.34",
|
||||
"vue-i18n": "^11.4.4",
|
||||
"vue-router": "^4.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
62
pnpm-lock.yaml
generated
@@ -29,6 +29,9 @@ importers:
|
||||
vue:
|
||||
specifier: ^3.5.34
|
||||
version: 3.5.34(typescript@6.0.3)
|
||||
vue-i18n:
|
||||
specifier: ^11.4.4
|
||||
version: 11.4.4(vue@3.5.34(typescript@6.0.3))
|
||||
vue-router:
|
||||
specifier: ^4.6.4
|
||||
version: 4.6.4(vue@3.5.34(typescript@6.0.3))
|
||||
@@ -149,6 +152,22 @@ packages:
|
||||
'@noble/hashes':
|
||||
optional: true
|
||||
|
||||
'@intlify/core-base@11.4.4':
|
||||
resolution: {integrity: sha512-w/vItlylrAmhebkIbVl5YY8XMCtj8Mb2g70ttxktMYuf5AuRahgEHL2iLgLIsZBIbTSgs4hkUo7ucCL0uTJvOg==}
|
||||
engines: {node: '>= 22'}
|
||||
|
||||
'@intlify/devtools-types@11.4.4':
|
||||
resolution: {integrity: sha512-PcBLmGmDQsTSVV911P8upzpcLJO1CNVYi/IH6bGnLR2nA+0L963+kXN1ZrisTEnbtw2ewN6HMMSldqzjronA0Q==}
|
||||
engines: {node: '>= 22'}
|
||||
|
||||
'@intlify/message-compiler@11.4.4':
|
||||
resolution: {integrity: sha512-vn0OAV9pYkJlPPmgnsSm5eAG3mL0+9C/oaded2JY9jmxBbhmUXT3TcAUY8WRgLY9Hte7lkUJKpXrVlYjMXBD2w==}
|
||||
engines: {node: '>= 22'}
|
||||
|
||||
'@intlify/shared@11.4.4':
|
||||
resolution: {integrity: sha512-QRUCHqda1U6aR14FR0vvXD4+4gj6+fm0AhAozvSuRCw0fCvrmCugWpgiR4xH2NI6s8am6N9p5OhirplsX8ZS3g==}
|
||||
engines: {node: '>= 22'}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
@@ -212,36 +231,42 @@ packages:
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0':
|
||||
resolution: {integrity: sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.0.0':
|
||||
resolution: {integrity: sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.0.0':
|
||||
resolution: {integrity: sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0':
|
||||
resolution: {integrity: sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0':
|
||||
resolution: {integrity: sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0':
|
||||
resolution: {integrity: sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==}
|
||||
@@ -281,6 +306,7 @@ packages:
|
||||
resolution: {integrity: sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@tybys/wasm-util@0.10.2':
|
||||
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
|
||||
@@ -565,24 +591,28 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
||||
@@ -830,6 +860,12 @@ packages:
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
vue-i18n@11.4.4:
|
||||
resolution: {integrity: sha512-gIbXVSFQV4jcSJxfwdZ5zSZmZ+12CnX0K3vBkRSd6Zn+HSzCp+QwUgPwpD/uN0oKNKI9RzlUXPKVedEuMgNG0A==}
|
||||
engines: {node: '>= 22'}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-router@4.6.4:
|
||||
resolution: {integrity: sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==}
|
||||
peerDependencies:
|
||||
@@ -958,6 +994,24 @@ snapshots:
|
||||
|
||||
'@exodus/bytes@1.15.0': {}
|
||||
|
||||
'@intlify/core-base@11.4.4':
|
||||
dependencies:
|
||||
'@intlify/devtools-types': 11.4.4
|
||||
'@intlify/message-compiler': 11.4.4
|
||||
'@intlify/shared': 11.4.4
|
||||
|
||||
'@intlify/devtools-types@11.4.4':
|
||||
dependencies:
|
||||
'@intlify/core-base': 11.4.4
|
||||
'@intlify/shared': 11.4.4
|
||||
|
||||
'@intlify/message-compiler@11.4.4':
|
||||
dependencies:
|
||||
'@intlify/shared': 11.4.4
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@intlify/shared@11.4.4': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -1589,6 +1643,14 @@ snapshots:
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vue-i18n@11.4.4(vue@3.5.34(typescript@6.0.3)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 11.4.4
|
||||
'@intlify/devtools-types': 11.4.4
|
||||
'@intlify/shared': 11.4.4
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.34(typescript@6.0.3)
|
||||
|
||||
vue-router@4.6.4(vue@3.5.34(typescript@6.0.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<RouterView />
|
||||
<MainFooter />
|
||||
<BackTop />
|
||||
|
||||
<VideoModel />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -10,6 +12,7 @@
|
||||
import MainHeader from "./components/main-header.vue";
|
||||
import MainFooter from "./components/main-footer.vue";
|
||||
import BackTop from "./components/back-top.vue";
|
||||
import VideoModel from "./components/video-model.vue";
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
// .main {
|
||||
|
||||
@@ -24,7 +24,6 @@ h6,
|
||||
font-family: Poppins, sans-serif;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
color: #222222;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
@keyframes loading {
|
||||
@@ -154,3 +153,28 @@ button[custom="black-box"] {
|
||||
--button-click-color: #fff;
|
||||
--button-font-size: 1.6rem;
|
||||
}
|
||||
.hover-bottom-animation {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hover-bottom-animation::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
right: 0;
|
||||
left: auto;
|
||||
bottom: 0;
|
||||
transition: width 0.2s ease-in-out;
|
||||
-webkit-transition: width 0.2s ease-in-out;
|
||||
background-color: #fff;
|
||||
}
|
||||
.hover-bottom-animation:hover::before {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
.hover-bottom-animation.active:before,
|
||||
.hover-bottom-animation.router-link-exact-active:before {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,18 @@ p {
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6, .products-title{
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
.products-title {
|
||||
font-family: Poppins, sans-serif;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
color: #222222;
|
||||
text-transform: capitalize;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
@@ -175,4 +181,33 @@ button[custom="black-box"] {
|
||||
--button-click-bgcolor: #979797;
|
||||
--button-click-color: #fff;
|
||||
--button-font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.hover-bottom-animation {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
right: 0;
|
||||
left: auto;
|
||||
bottom: 0;
|
||||
transition: width 0.2s ease-in-out;
|
||||
-webkit-transition: width 0.2s ease-in-out;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
&.active:before,
|
||||
&.router-link-exact-active:before {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
3
src/assets/images/aida/diamond.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||
<path fill="#666" d="M464 0H112c-4 0-7.8 2-10 5.4L2 152.6c-2.9 4.4-2.6 10.2.7 14.2l276 340.8c4.8 5.9 13.8 5.9 18.6 0l276-340.8c3.3-4.1 3.6-9.8.7-14.2L474.1 5.4C471.8 2 468.1 0 464 0zm-19.3 48l63.3 96h-68.4l-51.7-96h56.8zm-202.1 0h90.7l51.7 96H191l51.6-96zm-111.3 0h56.8l-51.7 96H68l63.3-96zm-43 144h51.4L208 352 88.3 192zm102.9 0h193.6L288 435.3 191.2 192zM368 352l68.2-160h51.4L368 352z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
8
src/assets/images/aida/time.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<g transform="translate(1024 0) scale(-1 1)">
|
||||
<path
|
||||
fill="#666"
|
||||
d="M512 64C264.96 64 64 264.96 64 512s200.96 448 448 448 448-200.96 448-448S759.04 64 512 64zM512 895.712c-211.584 0-383.712-172.16-383.712-383.712C128.288 300.416 300.416 128.288 512 128.288c211.552 0 383.712 172.128 383.712 383.712C895.712 723.552 723.552 895.712 512 895.712zM671.968 512 512 512 512 288.064c0-17.76-14.24-32.128-32-32.128s-32 14.4-32 32.128L448 544c0 17.76 14.272 32 32 32l191.968 0c17.76 0 32.128-14.24 32.128-32S689.728 512 671.968 512z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 627 B |
@@ -7,24 +7,25 @@
|
||||
transition: 'stroke-dashoffset 10ms linear',
|
||||
strokeDasharray: `${progress}, ${max}`,
|
||||
stroke: '#222',
|
||||
strokeWidth: 4,
|
||||
strokeWidth: 4
|
||||
}"
|
||||
fill="none"
|
||||
></path>
|
||||
</svg>
|
||||
<span class="iconfont icon-direction-up"></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { ref } from "vue";
|
||||
const max = ref(98 * Math.PI);
|
||||
const progress = ref(0);
|
||||
const handleScroll = (e: number) => {
|
||||
progress.value = (e / 100) * max.value;
|
||||
};
|
||||
progress.value = (e / 100) * max.value
|
||||
}
|
||||
const handleClick = () => {
|
||||
document.documentElement.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
document.documentElement.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.back-top {
|
||||
@@ -51,6 +52,20 @@
|
||||
transition: stroke-dashoffset 10ms linear;
|
||||
}
|
||||
}
|
||||
&:hover > .iconfont {
|
||||
animation: animArrow 1s infinite;
|
||||
}
|
||||
@keyframes animArrow {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
animation: back-top-hidden 0.2s linear both;
|
||||
&.active {
|
||||
|
||||
100
src/components/down-menu.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="down-menu">
|
||||
<div class="title hover-bottom-animation" to="#">
|
||||
{{ title }}
|
||||
<span class="iconfont icon-arrow-down-bold"></span>
|
||||
</div>
|
||||
<div class="child">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.down-menu {
|
||||
position: relative;
|
||||
> .title {
|
||||
margin: 0 14px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
line-height: 37px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
right: 0;
|
||||
left: auto;
|
||||
bottom: 0;
|
||||
transition: width 0.2s ease-in-out;
|
||||
-webkit-transition: width 0.2s ease-in-out;
|
||||
background-color: #fff;
|
||||
}
|
||||
&:hover::before {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
&.router-link-exact-active:not(.parent):before {
|
||||
width: 100%;
|
||||
}
|
||||
> .iconfont {
|
||||
opacity: 0.5;
|
||||
font-size: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
> .child {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
visibility: hidden;
|
||||
width: 250px;
|
||||
height: auto;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e1e1e1;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: translateY(calc(100% + 5px));
|
||||
> * {
|
||||
display: inline-block;
|
||||
padding: 10px 15px;
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
text-decoration: none;
|
||||
text-align: left;
|
||||
&:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover > .child {
|
||||
animation: child-show 0.2s linear both;
|
||||
}
|
||||
@keyframes child-show {
|
||||
0% {
|
||||
visibility: hidden;
|
||||
// opacity: 0;
|
||||
}
|
||||
100% {
|
||||
// opacity: 1;
|
||||
transform: translateY(100%);
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,25 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<header class="header">
|
||||
|
||||
</header>
|
||||
<footer class="main-footer">
|
||||
<div class="left">
|
||||
<span>{{
|
||||
$t('MainFooter.Copyright', {
|
||||
year: new Date().getFullYear(),
|
||||
name: 'Code-Create'
|
||||
})
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div v-for="item in nav" :key="item.path">
|
||||
<router-link class="link" :to="item.path">{{ $t(item.name) }}</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from 'vue'
|
||||
const nav = ref([
|
||||
{
|
||||
name: 'MainFooter.PrivacyPolicy',
|
||||
path: '/privacy-policy'
|
||||
},
|
||||
{
|
||||
name: 'MainFooter.TermsOfUse',
|
||||
path: '/terms-of-use'
|
||||
},
|
||||
{
|
||||
name: 'MainFooter.Disclaimer',
|
||||
path: '/disclaimer'
|
||||
},
|
||||
{
|
||||
name: 'MainFooter.SiteMap',
|
||||
path: '/site-map'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.header{
|
||||
position: relative;
|
||||
.main-footer {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 15px 0;
|
||||
min-height: 140px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-family: Poppins, sans-serif;
|
||||
> .left {
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
line-height: 24px;
|
||||
color: #000000b3;
|
||||
}
|
||||
> .right {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-left: 20px;
|
||||
border-left: 1px solid #0a0a0a;
|
||||
margin-left: 20px;
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
> .link {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease-out;
|
||||
box-sizing: border-box;
|
||||
box-shadow: none;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
color: #0a0a0a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,49 +1,119 @@
|
||||
<template>
|
||||
<header class="main-header" v-scroll-progress>
|
||||
<img class="logo" src="../assets/logo-full.png" alt="code-create" />
|
||||
<a href="/" class="logo"><img src="../assets/logo-full.png" alt="code-create" /></a>
|
||||
<div class="center-nav">
|
||||
<div class="nav-item" v-for="item in navList" :key="item.path">
|
||||
<router-link class="nav-item-link" :to="item.path">
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
<div class="nav-item" v-for="item in navList" :key="item.name">
|
||||
<down-menu :title="$t(item.name)" v-if="item.children">
|
||||
<router-link :to="child.path" v-for="child in item.children" :key="child.name">
|
||||
{{ $t(child.name) }}
|
||||
</router-link>
|
||||
</down-menu>
|
||||
<router-link class="link hover-bottom-animation" :to="item.path" v-else>{{
|
||||
$t(item.name)
|
||||
}}</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span>English</span>
|
||||
<span>My Account</span>
|
||||
<down-menu :title="langList.find((v) => v.value === locale)?.label || 'English'">
|
||||
<router-link
|
||||
class="link"
|
||||
:to="`/${item.value}`"
|
||||
v-for="item in langList"
|
||||
:key="item.value"
|
||||
v-show="item.value !== locale"
|
||||
>
|
||||
{{ item.label }}
|
||||
</router-link>
|
||||
</down-menu>
|
||||
<router-link class="link" to="/my-account">
|
||||
<span class="iconfont icon-tubiao-"></span>
|
||||
<span>{{ $t('MainHeader.LoginOrSignin') }}</span>
|
||||
</router-link>
|
||||
<router-link class="link" to="/my-account">
|
||||
<span class="iconfont icon-tubiao-"></span>
|
||||
<span>{{ $t('MainHeader.MyAccount') }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import DownMenu from './down-menu.vue'
|
||||
import { setLang, LangType } from '../lang'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { locale } = useI18n()
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
const lang = computed(() => route.params.lang)
|
||||
if (lang.value) setLang(lang.value)
|
||||
watch(lang, (newVal) => {
|
||||
setLang(newVal)
|
||||
})
|
||||
const langList = ref([
|
||||
{
|
||||
label: 'English',
|
||||
value: LangType.en
|
||||
},
|
||||
{
|
||||
label: '简体中文',
|
||||
value: LangType.zhCn
|
||||
},
|
||||
{
|
||||
label: '繁體中文',
|
||||
value: LangType.zhTw
|
||||
}
|
||||
])
|
||||
|
||||
const navList = ref([
|
||||
{
|
||||
name: "Home",
|
||||
path: "/",
|
||||
name: 'MainHeader.Home',
|
||||
path: '/'
|
||||
},
|
||||
{
|
||||
name: "About Us",
|
||||
path: "/about",
|
||||
name: 'MainHeader.AboutUs',
|
||||
path: '/about-us'
|
||||
},
|
||||
{
|
||||
name: "Our Solutions",
|
||||
path: "/solutions",
|
||||
name: 'MainHeader.OurSolutions',
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
name: 'MainHeader.AiDA31',
|
||||
path: '/aida'
|
||||
},
|
||||
{
|
||||
name: 'MainHeader.Mixi',
|
||||
path: '/mixi'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Communities",
|
||||
path: "/communities",
|
||||
name: 'MainHeader.Communities',
|
||||
path: '',
|
||||
children: [
|
||||
{
|
||||
name: 'MainHeader.Events',
|
||||
path: '/events'
|
||||
},
|
||||
{
|
||||
name: 'MainHeader.UserStories',
|
||||
path: '/user-stories'
|
||||
},
|
||||
{
|
||||
name: 'MainHeader.HelpCentre',
|
||||
path: '/help-centre'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Media",
|
||||
path: "/media",
|
||||
name: 'MainHeader.Media',
|
||||
path: '/media'
|
||||
},
|
||||
{
|
||||
name: "Contact Us",
|
||||
path: "/contact",
|
||||
},
|
||||
]);
|
||||
name: 'MainHeader.ContactUs',
|
||||
path: '/contact-us'
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.main-header {
|
||||
@@ -65,18 +135,89 @@
|
||||
> .logo {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
img {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
> .center-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> .nav-item {
|
||||
> .nav-item-link {
|
||||
position: relative;
|
||||
&:deep(> .down-menu > .title),
|
||||
> .link {
|
||||
margin: 0 14px;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
line-height: 37px;
|
||||
display: inline-block;
|
||||
}
|
||||
> .child {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
visibility: hidden;
|
||||
width: 250px;
|
||||
height: auto;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e1e1e1;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: translateY(calc(100% + 5px));
|
||||
> .child-link {
|
||||
display: inline-block;
|
||||
padding: 10px 15px;
|
||||
color: #000;
|
||||
font-size: 15px;
|
||||
text-decoration: none;
|
||||
text-align: left;
|
||||
&:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover > .child {
|
||||
animation: child-show 0.2s linear both;
|
||||
}
|
||||
@keyframes child-show {
|
||||
0% {
|
||||
visibility: hidden;
|
||||
// opacity: 0;
|
||||
}
|
||||
100% {
|
||||
// opacity: 1;
|
||||
transform: translateY(100%);
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> .link {
|
||||
margin: 0 14px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
line-height: 37px;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
> .iconfont {
|
||||
margin-right: 5px;
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
74
src/components/video-model.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import MyEvent from "@/directives/myEvents";
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
const isVideoShow = ref(false)
|
||||
const url = ref('')
|
||||
const poster = ref('')
|
||||
const playVideo = (data)=>{
|
||||
url.value = data.url || ''
|
||||
poster.value = data.poster || ''
|
||||
isVideoShow.value = true
|
||||
}
|
||||
const closeVideoModel = ()=>{
|
||||
url.value = ''
|
||||
poster.value = ''
|
||||
isVideoShow.value = false
|
||||
}
|
||||
onMounted(()=>{
|
||||
MyEvent.add("playVideo",playVideo);
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
</script>
|
||||
<template>
|
||||
<div class="vide-model" v-if="isVideoShow">
|
||||
<div class="mask"></div>
|
||||
<video :src="url" autoplay controls preload="metadata" controlslist="nodownload" :poster="poster"></video>
|
||||
<div class="close-btn">
|
||||
<span class="iconfont icon-close" @click="closeVideoModel"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.vide-model{
|
||||
z-index: 99999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
> .mask{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
background-color: rgba(0,0,0,.8);
|
||||
}
|
||||
> video{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
max-height: 80vh;
|
||||
max-width: 80vw;
|
||||
transform: translate(-50%,-50%);
|
||||
}
|
||||
> .close-btn{
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
font-size: 30px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
span{
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
177
src/directives/custom-animation.js
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* 自定义动画指令
|
||||
* v-custom-animation.scroll.once.parent="{GetRoot, activeClass}"
|
||||
* 修饰符
|
||||
* scroll: 是否监听滚动事件
|
||||
* once: 是否只执行一次
|
||||
* parent: 是否监听父元素滚动事件-优先级(GetRoot > parent > document)
|
||||
* 参数
|
||||
* GetRoot: 获取根元素函数-优先级(GetRoot > parent > document)
|
||||
* activeClass: 激活类名-默认值(active)
|
||||
*
|
||||
* 子元素动画
|
||||
* <div translate-x-s="-100" translate-x="100"></div>
|
||||
* 添加动画属性
|
||||
* translate-x-s: 水平方向移动开始位置
|
||||
* translate-x: 水平方向移动结束位置
|
||||
* ......(属性支持查看 T 对象)
|
||||
*
|
||||
*/
|
||||
const roots = new Map()
|
||||
const els = new Map()
|
||||
const T = {
|
||||
translateX_s: 'translate-x-s',
|
||||
translateX: 'translate-x',
|
||||
translateY_s: 'translate-y-s',
|
||||
translateY: 'translate-y',
|
||||
scale_s: 'scale-s',
|
||||
scale: 'scale',
|
||||
scaleX_s: 'scale-x-s',
|
||||
scaleX: 'scale-x',
|
||||
scaleY_s: 'scale-y-s',
|
||||
scaleY: 'scale-y',
|
||||
rotate_s: 'rotate-s',
|
||||
rotate: 'rotate',
|
||||
rotateX_s: 'rotate-x-s',
|
||||
rotateX: 'rotate-x',
|
||||
rotateY_s: 'rotate-y-s',
|
||||
rotateY: 'rotate-y',
|
||||
rotateZ_s: 'rotate-z-s',
|
||||
rotateZ: 'rotate-z',
|
||||
opacity_s: 'opacity-s',
|
||||
opacity: 'opacity',
|
||||
}
|
||||
const types = Object.values(T)
|
||||
const typesStr = types.map(v => `[${v}]`).join(',')
|
||||
const resize = new ResizeObserver((e) => {
|
||||
e.forEach(({ target }) => {
|
||||
requestAnimationFrame(() => handleScroll({ target }))
|
||||
})
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'custom-animation',
|
||||
mounted(el, binding) {
|
||||
const { value, modifiers } = binding
|
||||
const {
|
||||
GetRoot,// 获取根元素函数
|
||||
activeClass = 'active'// 激活类名
|
||||
} = value || {}
|
||||
const {
|
||||
scroll = false,// 是否监听滚动事件
|
||||
once = false,// 是否只执行一次
|
||||
parent = false,// 是否监听父元素滚动事件
|
||||
} = modifiers
|
||||
const root = GetRoot ? GetRoot() : parent ? el.parentElement : document;
|
||||
if (el === root) return;
|
||||
add(el, root)
|
||||
els.set(el, {
|
||||
root,// 根元素
|
||||
scroll,
|
||||
once,
|
||||
activeClass,
|
||||
isActive: false,
|
||||
})
|
||||
},
|
||||
beforeUnmount(el, binding) {
|
||||
remove(el)
|
||||
els.delete(el)
|
||||
}
|
||||
};
|
||||
function add(el, root = document) {
|
||||
requestAnimationFrame(() => handleScroll({ target: root }))
|
||||
resize.observe(el)
|
||||
if (roots.has(root)) {
|
||||
let obj = roots.get(root)
|
||||
obj.els.push(el)
|
||||
obj.observer.observe(el)
|
||||
return
|
||||
}
|
||||
resize.observe(isDocumentRoot(root))
|
||||
root.addEventListener('scroll', handleScroll)
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
const { target } = entry
|
||||
const obj = els.get(target)
|
||||
if (!obj) return
|
||||
if (obj.once && obj.isActive) return;// 只执行一次,且已可见,不执行
|
||||
obj.isActive = entry.isIntersecting;
|
||||
target.classList.toggle(obj.activeClass, obj.isActive)
|
||||
})
|
||||
}, { root })
|
||||
observer.observe(el)
|
||||
roots.set(root, { els: [el], observer, resize })
|
||||
}
|
||||
function remove(el, root = document) {
|
||||
if (!roots.has(root)) return
|
||||
const obj = roots.get(root)
|
||||
if (obj.els.includes(el)) {
|
||||
obj.observer.unobserve(el)
|
||||
obj.resize.unobserve(el)
|
||||
obj.els.splice(obj.els.indexOf(el), 1)
|
||||
}
|
||||
if (obj.els.length === 0) {
|
||||
obj.observer.disconnect()
|
||||
resize.unobserve(isDocumentRoot(root))
|
||||
root.removeEventListener('scroll', handleScroll)
|
||||
roots.delete(root)
|
||||
}
|
||||
}
|
||||
|
||||
var timer = null
|
||||
async function handleScroll({ target: root }) {
|
||||
clearTimeout(timer)
|
||||
timer = await new Promise(resolve => setTimeout(resolve, 10))
|
||||
const obj = roots.get(root)
|
||||
if (!obj) return
|
||||
obj.els.forEach((el) => {
|
||||
const item = els.get(el)
|
||||
if (!item) return
|
||||
if (!item.scroll) return
|
||||
const children = Array.from(el.querySelectorAll(typesStr))
|
||||
if (Object.values(T).some(v => hasAttr(el, v))) children.push(el)
|
||||
if (children.length === 0) return
|
||||
const rootEl = isDocumentRoot(root)
|
||||
const offsetHeight = root === document ? window.innerHeight : rootEl.offsetHeight
|
||||
const offsetTop = rootEl.offsetTop
|
||||
const scrollTop = rootEl.scrollTop
|
||||
const elTop_bottom = offsetHeight - (el.offsetTop - offsetTop - rootEl.scrollTop)
|
||||
const maxHeight = offsetHeight + el.offsetHeight
|
||||
const p = Math.min(1, Math.max(0, elTop_bottom / maxHeight))
|
||||
children.forEach((item) => {
|
||||
item.style.transition = 'transform 0.5s ease-out'
|
||||
|
||||
const tX = getCurrentValue(item, T.translateX_s, T.translateX, p)
|
||||
const tY = getCurrentValue(item, T.translateY_s, T.translateY, p)
|
||||
const sx = getCurrentValue(item, T.scaleX_s, T.scaleX, p, T.scale_s, T.scale, 1)
|
||||
const sy = getCurrentValue(item, T.scaleY_s, T.scaleY, p, T.scale_s, T.scale, 1)
|
||||
const r = getCurrentValue(item, T.rotate_s, T.rotate, p)
|
||||
const rx = getCurrentValue(item, T.rotateX_s, T.rotateX, p)
|
||||
const ry = getCurrentValue(item, T.rotateY_s, T.rotateY, p)
|
||||
const rz = getCurrentValue(item, T.rotateZ_s, T.rotateZ, p)
|
||||
const transform = `translate(${tX}px, ${tY}px) scale(${sx}, ${sy}) rotate(${r}deg) rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`
|
||||
item.style.transform = transform
|
||||
if (hasAttr(item, [T.opacity_s, T.opacity])) {
|
||||
item.style.opacity = getCurrentValue(item, T.opacity_s, T.opacity, p, T.opacity_s, T.opacity, 1)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
function getCurrentValue(el, start, end, progress, bStart, bEnd, defaultValue = 0) {
|
||||
// const startNum = Number(el.getAttribute(start) || el.getAttribute(bStart)) || defaultValue
|
||||
// const endNum = Number(el.getAttribute(end) || el.getAttribute(bEnd)) || defaultValue
|
||||
const startNum = hasAttr(el, start) ? Number(el.getAttribute(start)) : hasAttr(el, bStart) ? Number(el.getAttribute(bStart)) : defaultValue
|
||||
const endNum = hasAttr(el, end) ? Number(el.getAttribute(end)) : hasAttr(el, bEnd) ? Number(el.getAttribute(bEnd)) : defaultValue
|
||||
return startNum + (endNum - startNum) * progress
|
||||
}
|
||||
function hasAttr(el, attr) {
|
||||
if (Array.isArray(attr)) {
|
||||
return attr.some(v => el.hasAttribute(v))
|
||||
} else {
|
||||
return el.hasAttribute(attr)
|
||||
}
|
||||
}
|
||||
// 检查document是否为根元素
|
||||
function isDocumentRoot(root) {
|
||||
return root === document ? document.documentElement : root
|
||||
}
|
||||
@@ -2,7 +2,9 @@ import type { App } from 'vue'
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
const directivesList = import.meta.glob('./*.ts', { eager: true }) as any;
|
||||
const directivesList1 = import.meta.glob('./*.ts', { eager: true }) as any;
|
||||
const directivesList2 = import.meta.glob('./*.js', { eager: true }) as any;
|
||||
const directivesList = { ...directivesList1, ...directivesList2 };
|
||||
Object.keys(directivesList).forEach(key => {
|
||||
app.directive(directivesList[key].default.name, directivesList[key].default);
|
||||
});
|
||||
|
||||
39
src/directives/myEvents.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
class MyEvent {
|
||||
private events: Map<string, Array<(data: any) => void>>;
|
||||
constructor() {
|
||||
// 使用 Object 或 Map 存储,实现 O(1) 级别的查找
|
||||
this.events = new Map()
|
||||
}
|
||||
add(name: string, call: (data: any) => void) {
|
||||
if (!this.events.has(name)) {
|
||||
this.events.set(name, [])
|
||||
}
|
||||
this.events.get(name)!.push(call)
|
||||
}
|
||||
remove(name: string, call?: (data: any) => void) {
|
||||
if (!this.events.has(name)) return
|
||||
|
||||
if (!call) {
|
||||
this.events.delete(name)
|
||||
} else {
|
||||
const callbacks = this.events.get(name)
|
||||
const index = callbacks.indexOf(call)
|
||||
if (index !== -1) {
|
||||
callbacks.splice(index, 1)
|
||||
}
|
||||
// 如果该事件没有监听者了,彻底清理 key
|
||||
if (callbacks.length === 0) {
|
||||
this.events.delete(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
emit(name: string, data?: any) {
|
||||
const callbacks = this.events.get(name)
|
||||
if (callbacks) {
|
||||
// 使用 slice() 镜像一份副本,防止在执行回调过程中有 remove 操作导致索引错乱
|
||||
callbacks.slice().forEach((cb) => cb(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new MyEvent()
|
||||
@@ -33,7 +33,7 @@ export default {
|
||||
rootMap.set(obj.root, [obj])
|
||||
obj.root.addEventListener('scroll', handleScroll)
|
||||
},
|
||||
beforeUnmount(el: HTMLElement, binding: any) {
|
||||
beforeUnmount(el: HTMLElement) {
|
||||
rootMap.forEach((objs, root) => {
|
||||
if (objs.some((v: any) => v.el === el)) {
|
||||
objs = objs.filter((v_: any) => v_.el !== el)
|
||||
@@ -60,4 +60,4 @@ function handleScroll(e: any) {
|
||||
obj.el.classList.toggle('active', isActive)
|
||||
}
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { gsap, } from 'gsap'
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||
export default {
|
||||
name: 'tween-animation',
|
||||
mounted(el: HTMLElement, binding: any) {
|
||||
mounted(el: HTMLElement) {
|
||||
// if(!binding.value.isGsap)return
|
||||
let dom = document.querySelector('body')
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
@@ -16,6 +16,7 @@ export default {
|
||||
animation:tl1,
|
||||
scroller:dom,//设置指定元素为滚动依据
|
||||
scrub:2,
|
||||
// toggleActions: "play reset play reset",
|
||||
// onUpdate:(v)=>{
|
||||
// if(v.progress < 0.1){
|
||||
// v.trigger?.classList.remove('active')
|
||||
@@ -26,4 +27,4 @@ export default {
|
||||
// }
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
24
src/lang/en.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export default {
|
||||
MainHeader: {
|
||||
Home: 'Home',
|
||||
AboutUs: 'About Us',
|
||||
OurSolutions: 'Our Solutions',
|
||||
AiDA31: 'AiDA 3.1',
|
||||
Mixi: 'Mixi',
|
||||
Communities: 'Communities',
|
||||
Events: 'Events',
|
||||
UserStories: 'User Stories',
|
||||
HelpCentre: 'Help Centre',
|
||||
Media: 'Media',
|
||||
ContactUs: 'Contact Us',
|
||||
MyAccount: 'My Account',
|
||||
LoginOrSignin: 'Log in / Sign in',
|
||||
},
|
||||
MainFooter: {
|
||||
Copyright: '©{year} {name} Limited',
|
||||
PrivacyPolicy: 'Privacy Policy',
|
||||
TermsOfUse: 'Terms of Use',
|
||||
Disclaimer: 'Disclaimer',
|
||||
SiteMap: 'Site Map',
|
||||
}
|
||||
}
|
||||
40
src/lang/index.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
import enLocale from './en.ts'
|
||||
import zhCnLocale from './zh-cn.ts'
|
||||
import zhTwLocale from './zh-tw.ts'
|
||||
|
||||
export const LangType = {
|
||||
en: "en", // 英文
|
||||
zhTw: "zh-tw", // 繁体中文
|
||||
zhCn: "zh-cn", // 简体中文
|
||||
}
|
||||
export const LANGS = Object.values(LangType)
|
||||
|
||||
// 语言配置整合
|
||||
const messages = {
|
||||
[LangType.en]: enLocale,
|
||||
[LangType.zhCn]: zhCnLocale,
|
||||
[LangType.zhTw]: zhTwLocale,
|
||||
}
|
||||
const localeLang = localStorage.getItem('language')
|
||||
const defaultLocale = checkLocale(localeLang) ? localeLang! : LangType.en
|
||||
// 创建 i18n
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
globalInjection: true, // 全局模式,可以直接使用 $t
|
||||
locale: defaultLocale,
|
||||
messages: messages
|
||||
})
|
||||
export default i18n
|
||||
// 检查语言是否存在
|
||||
function checkLocale(lang: any) {
|
||||
return LANGS.includes(lang)
|
||||
}
|
||||
export const setLang = (lang: any) => {
|
||||
if (lang === "") lang = LangType.en
|
||||
if (!checkLocale(lang)) return false
|
||||
i18n.global.locale.value = lang
|
||||
localStorage.setItem('language', lang)
|
||||
return true
|
||||
}
|
||||
24
src/lang/zh-cn.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export default {
|
||||
MainHeader: {
|
||||
Home: '首页',
|
||||
AboutUs: '关于我们',
|
||||
OurSolutions: '我们的产品',
|
||||
AiDA31: 'AiDA 3.1',
|
||||
Mixi: 'Mixi',
|
||||
Communities: '社区',
|
||||
Events: '活动',
|
||||
UserStories: '使用者故事',
|
||||
HelpCentre: '支援中心',
|
||||
Media: '媒体报道',
|
||||
ContactUs: '联系我们',
|
||||
MyAccount: '我的账户',
|
||||
LoginOrSignin: '登入 / 注册',
|
||||
},
|
||||
MainFooter: {
|
||||
Copyright: '©{year} {name} 有限公司',
|
||||
PrivacyPolicy: '隐私政策',
|
||||
TermsOfUse: '使用条款',
|
||||
Disclaimer: '免责声明',
|
||||
SiteMap: '网站地图',
|
||||
}
|
||||
}
|
||||
24
src/lang/zh-tw.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export default {
|
||||
MainHeader: {
|
||||
Home: '首頁',
|
||||
AboutUs: '關於我們',
|
||||
OurSolutions: '我們的產品',
|
||||
AiDA31: 'AiDA 3.1',
|
||||
Mixi: 'Mixi',
|
||||
Communities: '社區',
|
||||
Events: '活動',
|
||||
UserStories: '使用者故事',
|
||||
HelpCentre: '支援中心',
|
||||
Media: '媒體報導',
|
||||
ContactUs: '聯絡我們',
|
||||
MyAccount: '我的帳號',
|
||||
LoginOrSignin: '登錄 / 登冊',
|
||||
},
|
||||
MainFooter: {
|
||||
Copyright: '©{year} {name} 有限公司',
|
||||
PrivacyPolicy: '私隱政策',
|
||||
TermsOfUse: '使用條款',
|
||||
Disclaimer: '免責聲明',
|
||||
SiteMap: '網站地圖',
|
||||
}
|
||||
}
|
||||
12
src/main.ts
@@ -4,13 +4,15 @@ import { routes } from './routes'
|
||||
// import './style.css'
|
||||
import '@/assets/css/style.less'
|
||||
import directives from './directives/index'
|
||||
import i18n from './lang/index'
|
||||
|
||||
export const createApp = ViteSSG(App, {
|
||||
routes,
|
||||
base: import.meta.env.BASE_URL,
|
||||
},
|
||||
({ app, router, routes, isClient, initialState }) => {
|
||||
routes,
|
||||
base: import.meta.env.BASE_URL,
|
||||
},
|
||||
({ app }) => {
|
||||
// 注册全局指令
|
||||
app.use(directives)
|
||||
app.use(i18n)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<section id="center">
|
||||
<div class="hero">
|
||||
<img :src="heroImg" class="base" width="170" height="179" alt="" />
|
||||
<img :src="vueLogo" class="framework" alt="Vue logo" />
|
||||
<img :src="viteLogo" class="vite" alt="Vite logo" />
|
||||
</div>
|
||||
<div>
|
||||
<h1>Get started</h1>
|
||||
<p>Edit <code>src/pages/HomeView.vue</code> and save to test <code>HMR</code></p>
|
||||
</div>
|
||||
<button type="button" class="counter" @click="count++">
|
||||
Count is {{ count }}
|
||||
</button>
|
||||
<nav class="site-pages" aria-label="Site pages">
|
||||
<RouterLink
|
||||
v-for="page in pageLinks"
|
||||
:key="page.to"
|
||||
class="site-page-link"
|
||||
:to="page.to"
|
||||
>
|
||||
{{ page.label }}
|
||||
</RouterLink>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<div class="ticks"></div>
|
||||
|
||||
|
||||
|
||||
<div class="ticks"></div>
|
||||
<section id="spacer"></section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue'
|
||||
import { shallowRef } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import heroImg from '../assets/hero.png'
|
||||
import viteLogo from '../assets/vite.svg'
|
||||
import vueLogo from '../assets/vue.svg'
|
||||
|
||||
const count = shallowRef(0)
|
||||
const pageLinks = [
|
||||
{ label: 'Home', to: '/' },
|
||||
{ label: 'About', to: '/about' },
|
||||
{ label: 'Products', to: '/products' },
|
||||
{ label: 'Contact', to: '/contact' },
|
||||
] as const
|
||||
|
||||
useHead({
|
||||
title: 'Home | test-ssg',
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: 'Static generated home page for the Vite SSG test site.',
|
||||
},
|
||||
],
|
||||
})
|
||||
</script>
|
||||
@@ -1,19 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import { gsap, TweenMax, TweenLite } from 'gsap'
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
import { ref } from "vue";
|
||||
import MyEvent from "@/directives/myEvents";
|
||||
let videoUrl = 'https://code-create.com.hk/wp-content/uploads/2023/05/codec_brand_vid_16x9_ENG.mp4'
|
||||
let videoPosterUrl = 'https://code-create.com.hk/wp-content/uploads/revslider/video-media/codec_brand_vid_16x9_ENG_11_layer.jpeg'
|
||||
const playVideo = ()=>{
|
||||
MyEvent.emit("playVideo",{
|
||||
url: videoUrl,
|
||||
poster: videoPosterUrl,
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({})
|
||||
</script>
|
||||
<template>
|
||||
<section class="ecosystem">
|
||||
@@ -25,6 +22,8 @@ const {} = toRefs(data);
|
||||
</section>
|
||||
<section class="ecosystem-video">
|
||||
<div class="content">
|
||||
<img src="https://code-create.com.hk/wp-content/uploads/revslider/video-media/codec_brand_vid_16x9_ENG_11_layer.jpeg" alt="">
|
||||
<span class="iconfont icon-bofang" @click="playVideo"></span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -62,6 +61,36 @@ const {} = toRefs(data);
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 100px 0;
|
||||
position: relative;
|
||||
> img{
|
||||
width: 100%;
|
||||
}
|
||||
> .icon-bofang{
|
||||
font-size: 100px;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
animation: identifier 2s ease-in-out infinite;
|
||||
// box-shadow: 0 0 10px #fff;
|
||||
transition: .3s all;
|
||||
cursor: pointer;
|
||||
@keyframes identifier {
|
||||
0% {
|
||||
transform: translate(-50%,-50%) scale(1);
|
||||
filter: drop-shadow(0px 0px 8px rgba(255, 255, 255, 1));
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%,-50%) scale(.95);
|
||||
filter: drop-shadow(0px 0px 0px rgba(255, 255, 255, 1));
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%,-50%) scale(1);
|
||||
filter: drop-shadow(0px 0px 8px rgba(255, 255, 255, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import Ecosystem from './ecosystem.vue'
|
||||
import Title from './title.vue'
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
import Mission from './mission.vue'
|
||||
import OurTeam from './our-team.vue'
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<div class="about-us">
|
||||
@@ -22,6 +12,8 @@ const {} = toRefs(data);
|
||||
</div>
|
||||
<Title />
|
||||
<Ecosystem />
|
||||
<Mission />
|
||||
<OurTeam />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
@@ -39,4 +31,4 @@ const {} = toRefs(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
147
src/pages/about-us/mission.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import { gsap, TweenMax, TweenLite } from 'gsap'
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
const imgItem1 = ref(null)
|
||||
const imgItem2 = ref(null)
|
||||
const imgItem3 = ref(null)
|
||||
const imgItem4 = ref(null)
|
||||
const imgBox = ref(null)
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
let dom = document.querySelector('body')
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
let tl1 = gsap.timeline();
|
||||
tl1.from(imgItem1.value,1, {y:'200px',opacity:0,ease:'power2.out'},0)
|
||||
tl1.from(imgItem2.value,1, {y:'200px',opacity:0,ease:'power2.out'},0.2)
|
||||
tl1.from(imgItem3.value,1, {y:'200px',opacity:0,ease:'power2.out'},0.4)
|
||||
tl1.from(imgItem4.value,1, {y:'200px',opacity:0,ease:'power2.out'},0.6)
|
||||
ScrollTrigger.create({
|
||||
trigger: imgBox.value, // 触发器元素
|
||||
start: "top 90%", // 滚动触发器的起始滚动位置
|
||||
end: '100% 80%', // 滚动触发器的结束滚动位置
|
||||
markers: false, // 开启标注功能
|
||||
animation:tl1,
|
||||
scroller:dom,//设置指定元素为滚动依据
|
||||
// toggleActions: "play reset play reset",
|
||||
scrub:false,
|
||||
});
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<section class="mission">
|
||||
<div class="content">
|
||||
<h2>Mission</h2>
|
||||
<p>To be the world leading innovative leader in offering the state-of-the-art AI based solutions and systems to disrupt the fashion industry</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="mission-focus-area">
|
||||
<div class="content">
|
||||
<h2>Focus Area</h2>
|
||||
<div class="img-box" ref="imgBox">
|
||||
<div class="img-item" ref="imgItem1">
|
||||
<img src="https://code-create.com.hk/wp-content/uploads/2022/11/about_focus_01.png" alt="">
|
||||
<h2>Elevate Operation Efficiency</h2>
|
||||
</div>
|
||||
<div class="img-item" ref="imgItem2">
|
||||
<img src="https://code-create.com.hk/wp-content/uploads/2022/11/about_focus_02-1-600x888.png" alt="">
|
||||
<h2>Vertical Design Platform</h2>
|
||||
</div>
|
||||
<div class="img-item" ref="imgItem3">
|
||||
<img src="https://code-create.com.hk/wp-content/uploads/2022/11/about_focus_03-1-600x887.png" alt="">
|
||||
<h2>Education</h2>
|
||||
</div>
|
||||
<div class="img-item" ref="imgItem4">
|
||||
<img src="https://code-create.com.hk/wp-content/uploads/2022/11/about_focus_04-1-600x888.png" alt="">
|
||||
<h2>Community</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.mission{
|
||||
width: 100%;
|
||||
background-color: #eeeeee;
|
||||
> .content{
|
||||
margin: 0 auto;
|
||||
max-width: 730px;
|
||||
padding: 200px 0px 200px 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
> h2{
|
||||
margin-bottom: 30px;
|
||||
color: #000000;
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
font-family: "Poppins", Sans-serif;
|
||||
}
|
||||
> p{
|
||||
color: #555;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.mission-focus-area{
|
||||
width: 100%;
|
||||
background-color: #f9f9f9;
|
||||
> .content{
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
> h2{
|
||||
font-family: "Poppins", Sans-serif;
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
color: #000000;
|
||||
margin: 100px auto;
|
||||
}
|
||||
> .img-box{
|
||||
padding: 10px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
> .img-item{
|
||||
margin: 10px;
|
||||
// width: 1120px;
|
||||
width: 340px;
|
||||
position: relative;
|
||||
> h2{
|
||||
font-family: "Poppins", Sans-serif;
|
||||
font-weight: 600;
|
||||
line-height: 1.3em;
|
||||
letter-spacing: 2px;
|
||||
color: #FFFFFF;
|
||||
width: 320px;
|
||||
position: absolute;
|
||||
bottom: 25%;
|
||||
text-align: center;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
> img{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
99
src/pages/about-us/our-team.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import { gsap, TweenMax, TweenLite } from 'gsap'
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
const imgItem1 = ref(null)
|
||||
const imgItem2 = ref(null)
|
||||
const imgItem3 = ref(null)
|
||||
const imgItem4 = ref(null)
|
||||
const imgBox = ref(null)
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
let dom = document.querySelector('body')
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
let tl1 = gsap.timeline();
|
||||
tl1.from(imgItem1.value,1, {y:'200px',opacity:0,ease:'power2.out'},0)
|
||||
tl1.from(imgItem2.value,1, {y:'200px',opacity:0,ease:'power2.out'},0.2)
|
||||
tl1.from(imgItem3.value,1, {y:'200px',opacity:0,ease:'power2.out'},0.4)
|
||||
tl1.from(imgItem4.value,1, {y:'200px',opacity:0,ease:'power2.out'},0.6)
|
||||
ScrollTrigger.create({
|
||||
trigger: imgBox.value, // 触发器元素
|
||||
start: "top 90%", // 滚动触发器的起始滚动位置
|
||||
end: '100% 80%', // 滚动触发器的结束滚动位置
|
||||
markers: false, // 开启标注功能
|
||||
animation:tl1,
|
||||
scroller:dom,//设置指定元素为滚动依据
|
||||
toggleActions: "play reset play reset",
|
||||
scrub:false,
|
||||
});
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<section class="our-team">
|
||||
<div class="content">
|
||||
<h2>Our Team</h2>
|
||||
</div>
|
||||
</section>
|
||||
<section class="our-team-item bg1">
|
||||
<div class="content">
|
||||
<div class="text"></div>
|
||||
<div class="img">
|
||||
<img src="https://code-create.com.hk/wp-content/uploads/2022/11/about_team_01-600x887.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.our-team{
|
||||
width: 100%;
|
||||
background-color: #f9f9f9;
|
||||
> .content{
|
||||
margin: 0 auto;
|
||||
max-width: 730px;
|
||||
padding: 100px 0px 100px 0px;
|
||||
> h2{
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
color: #000000;
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
font-family: "Poppins", Sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
.our-team-item{
|
||||
width: 100%;
|
||||
&.bg1{
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
&.bg2{
|
||||
background-color: #ffffff;
|
||||
}
|
||||
> .content{
|
||||
padding: 100px 0;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
> div{
|
||||
width: 50%;
|
||||
}
|
||||
> .img{
|
||||
text-align: right;
|
||||
> img{
|
||||
width: 100%;
|
||||
max-width: 330px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,19 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
|
||||
import { gsap, TweenMax, TweenLite } from 'gsap'
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
||||
//const props = defineProps({
|
||||
//})
|
||||
//const emit = defineEmits([
|
||||
//])
|
||||
let data = reactive({
|
||||
})
|
||||
onMounted(()=>{
|
||||
})
|
||||
onUnmounted(()=>{
|
||||
})
|
||||
defineExpose({})
|
||||
const {} = toRefs(data);
|
||||
</script>
|
||||
<template>
|
||||
<section class="title-section">
|
||||
@@ -42,4 +28,4 @@ const {} = toRefs(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
819
src/pages/aida/index.vue
Normal file
@@ -0,0 +1,819 @@
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue'
|
||||
import { shallowRef, useTemplateRef } from 'vue'
|
||||
import aidaIntroBg from '@/assets/images/home/aida-intro-bg.png'
|
||||
import aidaPanel from '@/assets/images/home/aida-panel.png'
|
||||
import aidaBanner from '@/assets/images/aida/aida-banner.jpg'
|
||||
import demoVideo from '@/assets/images/aida/aida-demo-video.mp4'
|
||||
import diamondIcon from '@/assets/images/aida/diamond.svg'
|
||||
import industryOne from '@/assets/images/aida/industry-1.png'
|
||||
import industryTwo from '@/assets/images/aida/industry-2.png'
|
||||
import industryThree from '@/assets/images/aida/industry-3.png'
|
||||
import timeIcon from '@/assets/images/aida/time.svg'
|
||||
|
||||
const keyFeatures = [
|
||||
"The world's first AI system in fashion design emphasizing user control, ensuring AI-generated designs align seamlessly with the designer's unique vision and brand identity.",
|
||||
'Excels in synthesizing diverse inputs, such as moodboards, fabric prints, color choices, and sketches, into a cohesive collection quickly, harmoniously, and efficiently.',
|
||||
'Significantly speeds up the design process by over 60%, quickly generating unlimited designs based on user input.',
|
||||
'Incorporates cutting-edge AIGC technology to generate innovative designs and provide comprehensive assistance in the creative process.'
|
||||
] as const
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
image: industryOne,
|
||||
alt: 'Light bulb icon',
|
||||
text: 'Provides speedy ideation for fashion brands and individual designers'
|
||||
},
|
||||
{
|
||||
image: industryTwo,
|
||||
alt: 'Stopwatch icon',
|
||||
text: 'Speeds up the whole fashion design process to strive for the goal of sustainability and cost-saving'
|
||||
},
|
||||
{
|
||||
image: industryThree,
|
||||
alt: 'Drawing brush icon',
|
||||
text: 'Allows fashion novices who do not have drawing or sketching skills to create their own designs in a simple and easy mode'
|
||||
}
|
||||
] as const
|
||||
|
||||
const subscriptionHighlights = [
|
||||
'Easily create your fashion collections in around 10 seconds based on your creative inspirations and Brand DNA',
|
||||
'Upload mood boards, colour choices, fabric prints and sketches for generating unlimited design proposals',
|
||||
'Save and retrieve your own designs with just a few clicks',
|
||||
'A Cloud-based system by subscription for accessing anytime and anywhere',
|
||||
'Easy to use, can learn in 10 minutes',
|
||||
'Contact us for AiDA trial at info@code-create.com.hk'
|
||||
] as const
|
||||
|
||||
const plans = [
|
||||
{
|
||||
icon: timeIcon,
|
||||
name: 'Trial',
|
||||
description: '7 days free trial',
|
||||
action: 'Start Trial',
|
||||
href: 'mailto:info@code-create.com.hk?subject=AiDA%203.1%20Trial'
|
||||
},
|
||||
{
|
||||
icon: diamondIcon,
|
||||
name: 'Corporate',
|
||||
description: 'Customised plan',
|
||||
action: 'Contact Us',
|
||||
href: 'mailto:info@code-create.com.hk?subject=AiDA%203.1%20Corporate%20Plan'
|
||||
}
|
||||
] as const
|
||||
|
||||
const demoVideoRef = useTemplateRef<HTMLVideoElement>('demoVideoRef')
|
||||
const isVideoPlaying = shallowRef(false)
|
||||
|
||||
async function toggleDemoVideo() {
|
||||
const video = demoVideoRef.value
|
||||
|
||||
if (!video) {
|
||||
return
|
||||
}
|
||||
|
||||
if (video.paused) {
|
||||
await video.play()
|
||||
isVideoPlaying.value = true
|
||||
return
|
||||
}
|
||||
|
||||
video.pause()
|
||||
isVideoPlaying.value = false
|
||||
}
|
||||
|
||||
function handleVideoStateChange() {
|
||||
const video = demoVideoRef.value
|
||||
isVideoPlaying.value = Boolean(video && !video.paused && !video.ended)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: 'AiDA 3.1 | Code Create',
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content:
|
||||
'AiDA 3.1 is an AI-based interactive design assistant for fashion designers.'
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="aida-page">
|
||||
<section class="aida-hero" aria-labelledby="aida-title">
|
||||
<img class="aida-hero-image" :src="aidaBanner" alt="AiDA 3.1 purple silk banner" />
|
||||
<h1 id="aida-title" class="aida-hero-title">AiDA 3.1</h1>
|
||||
</section>
|
||||
|
||||
<section class="intro-section" aria-labelledby="intro-title">
|
||||
<div class="intro-inner" v-custom-animation.scroll>
|
||||
<h2 id="intro-title" class="intro-title">
|
||||
AI-Based Interactive Design Assistant For Fashion
|
||||
</h2>
|
||||
<p class="intro-copy">
|
||||
AiDA 3.1, a first-to-market technology that empowers fashion designers, based on
|
||||
their creative inspirations, to work with AI to create original designs. With
|
||||
just a few clicks, designers can choose or refine options to develop fashion
|
||||
collections, bringing agility, efficiency and flexibility to conventional and
|
||||
intensive studio processes.
|
||||
</p>
|
||||
<p class="pricing-copy">
|
||||
Annual Subscription Fee: $5,000 HKD / Year (50000 Credits)<br />
|
||||
Monthly Subscription Fee: $500 HKD / Month (3500 Credits)<br />
|
||||
Monthly Subscription Fee: $100 HKD / Month (500 Credits)
|
||||
</p>
|
||||
<p class="academic-copy">
|
||||
Special Academic rate available, please contact us for details.
|
||||
</p>
|
||||
<a
|
||||
class="primary-button"
|
||||
href="mailto:info@code-create.com.hk?subject=AiDA%203.1%20Subscription"
|
||||
>
|
||||
Subscribe Now
|
||||
</a>
|
||||
<a class="manual-link" href="#subscription">User Manual</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="features-section" aria-labelledby="features-title">
|
||||
<div class="features-inner" v-custom-animation.scroll>
|
||||
<div class="feature-art">
|
||||
<img
|
||||
class="feature-bg"
|
||||
:src="aidaIntroBg"
|
||||
alt="Fashion design sketches on paper"
|
||||
loading="lazy"
|
||||
/>
|
||||
<img
|
||||
class="feature-panel"
|
||||
:src="aidaPanel"
|
||||
alt="AiDA design workspace preview"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="feature-copy">
|
||||
<h2 id="features-title" class="section-title">Key Features</h2>
|
||||
<ul class="feature-list">
|
||||
<li v-for="feature in keyFeatures" :key="feature">
|
||||
{{ feature }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="benefits-section" aria-labelledby="benefits-title">
|
||||
<div class="benefits-inner">
|
||||
<h2 id="benefits-title" class="section-title benefits-title">
|
||||
Benefits to Industry
|
||||
</h2>
|
||||
<div class="benefits-grid">
|
||||
<article v-for="benefit in benefits" :key="benefit.text" class="benefit-card">
|
||||
<img
|
||||
class="benefit-icon"
|
||||
:src="benefit.image"
|
||||
:alt="benefit.alt"
|
||||
loading="lazy"
|
||||
/>
|
||||
<p class="benefit-copy">{{ benefit.text }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="demo-section" aria-labelledby="demo-title">
|
||||
<div class="demo-inner">
|
||||
<h2 id="demo-title" class="demo-title">Demo</h2>
|
||||
<div class="demo-video-wrap">
|
||||
<video
|
||||
ref="demoVideoRef"
|
||||
class="demo-video"
|
||||
:class="{ 'is-playing': isVideoPlaying }"
|
||||
preload="metadata"
|
||||
playsinline
|
||||
@click="toggleDemoVideo"
|
||||
@pause="handleVideoStateChange"
|
||||
@play="handleVideoStateChange"
|
||||
@ended="handleVideoStateChange"
|
||||
>
|
||||
<source :src="demoVideo" type="video/mp4" />
|
||||
</video>
|
||||
<button
|
||||
v-show="!isVideoPlaying"
|
||||
class="video-play-button"
|
||||
type="button"
|
||||
aria-label="Play AiDA demo video"
|
||||
@click="toggleDemoVideo"
|
||||
>
|
||||
Click to play
|
||||
<span class="play-dot" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
id="subscription"
|
||||
class="subscription-section"
|
||||
aria-labelledby="subscription-title"
|
||||
>
|
||||
<div class="subscription-inner">
|
||||
<h2 id="subscription-title" class="section-title subscription-title">
|
||||
Choose Your Subscription Plan
|
||||
</h2>
|
||||
<ul class="subscription-list">
|
||||
<li v-for="item in subscriptionHighlights" :key="item">
|
||||
{{ item }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="plans-grid">
|
||||
<article v-for="plan in plans" :key="plan.name" class="plan-card">
|
||||
<img
|
||||
class="plan-icon"
|
||||
:src="plan.icon"
|
||||
:alt="`${plan.name} plan icon`"
|
||||
loading="lazy"
|
||||
/>
|
||||
<h3 class="plan-title">{{ plan.name }}</h3>
|
||||
<p class="plan-copy">{{ plan.description }}</p>
|
||||
<a class="plan-button" :href="plan.href">{{ plan.action }}</a>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="legal-links">
|
||||
<a href="#subscription">Terms & Conditions</a>
|
||||
<a href="#subscription">Subscription Agreement</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.aida-page {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #f0f0f0;
|
||||
color: #333333;
|
||||
font-family: Poppins, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.aida-hero {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: clamp(280px, 36vw, 520px);
|
||||
min-height: 280px;
|
||||
background: #241023;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.aida-hero-image {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.aida-hero-title {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0;
|
||||
padding-top: 34px;
|
||||
color: #ffffff;
|
||||
font-size: clamp(40px, 5.2vw, 72px);
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intro-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 735px;
|
||||
padding: 110px 24px 122px;
|
||||
background: #ebebeb;
|
||||
}
|
||||
|
||||
.intro-inner {
|
||||
width: min(100%, 720px);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.intro-title,
|
||||
.section-title,
|
||||
.demo-title,
|
||||
.subscription-title,
|
||||
.plan-title {
|
||||
margin: 0;
|
||||
color: #333333;
|
||||
font-family: Poppins, Arial, sans-serif;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.4px;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
margin-bottom: 32px;
|
||||
font-size: clamp(18px, 1.55vw, 24px);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.intro-copy,
|
||||
.pricing-copy,
|
||||
.academic-copy {
|
||||
margin: 0 auto;
|
||||
color: #555555;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.intro-copy {
|
||||
max-width: 610px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pricing-copy {
|
||||
max-width: 520px;
|
||||
margin-bottom: 14px;
|
||||
color: #3f3f3f;
|
||||
font-weight: 700;
|
||||
line-height: 1.32;
|
||||
letter-spacing: 1.1px;
|
||||
}
|
||||
|
||||
.academic-copy {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.primary-button,
|
||||
.plan-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 43px;
|
||||
padding: 0 34px;
|
||||
border-radius: 999px;
|
||||
background: #a51f28;
|
||||
color: #ffffff;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 1.8px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.primary-button:hover,
|
||||
.primary-button:focus-visible,
|
||||
.plan-button:hover,
|
||||
.plan-button:focus-visible {
|
||||
background: #8f1720;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.primary-button:focus-visible,
|
||||
.plan-button:focus-visible,
|
||||
.manual-link:focus-visible,
|
||||
.legal-links a:focus-visible,
|
||||
.video-play-button:focus-visible {
|
||||
outline: 2px solid #a51f28;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
.manual-link {
|
||||
display: table;
|
||||
margin: 23px auto 0;
|
||||
color: #333333;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.features-section,
|
||||
.benefits-section,
|
||||
.subscription-section {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
.features-section {
|
||||
padding: clamp(82px, 9vw, 132px) 24px 70px;
|
||||
}
|
||||
|
||||
.features-inner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(300px, 520px) minmax(320px, 620px);
|
||||
align-items: center;
|
||||
gap: clamp(56px, 7vw, 112px);
|
||||
width: min(100%, 1180px);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.feature-art {
|
||||
position: relative;
|
||||
min-height: clamp(440px, 38vw, 565px);
|
||||
}
|
||||
|
||||
.feature-bg {
|
||||
display: block;
|
||||
width: min(75%, 430px);
|
||||
border-radius: 24px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.feature-panel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 32px;
|
||||
width: min(75%, 445px);
|
||||
filter: drop-shadow(0 12px 18px rgba(0, 0, 0, 0.12));
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.feature-copy {
|
||||
max-width: 610px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: clamp(26px, 2.35vw, 42px);
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
margin: 32px 0 0;
|
||||
padding-left: 18px;
|
||||
color: #444444;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.feature-list li + li {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.benefits-section {
|
||||
padding: 78px 24px 112px;
|
||||
}
|
||||
|
||||
.benefits-inner {
|
||||
width: min(100%, 1160px);
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.benefits-title {
|
||||
margin-bottom: clamp(56px, 7vw, 95px);
|
||||
}
|
||||
|
||||
.benefits-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: clamp(40px, 8vw, 118px);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.benefit-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.benefit-icon {
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 48px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.benefit-copy {
|
||||
max-width: 248px;
|
||||
margin: 0;
|
||||
color: #555555;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
padding: 78px 24px 104px;
|
||||
background: #372b28;
|
||||
}
|
||||
|
||||
.demo-inner {
|
||||
width: min(100%, 1060px);
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.demo-title {
|
||||
margin-bottom: 14px;
|
||||
color: #ffffff;
|
||||
font-size: clamp(24px, 2.15vw, 34px);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.demo-video-wrap {
|
||||
position: relative;
|
||||
width: min(100%, 960px);
|
||||
margin: 0 auto;
|
||||
background: #202020;
|
||||
aspect-ratio: 16 / 9;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.demo-video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
filter: grayscale(1);
|
||||
}
|
||||
|
||||
.demo-video.is-playing {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.video-play-button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 32px;
|
||||
padding: 0 16px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: #a51f28;
|
||||
color: #ffffff;
|
||||
font-family: Poppins, Arial, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
cursor: pointer;
|
||||
transform: translate(-50%, -50%);
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.video-play-button:hover {
|
||||
background: #8f1720;
|
||||
transform: translate(-50%, calc(-50% - 1px));
|
||||
}
|
||||
|
||||
.play-dot {
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-top: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
border-left: 8px solid #ffffff;
|
||||
}
|
||||
|
||||
.subscription-section {
|
||||
padding: clamp(76px, 8vw, 104px) 24px 86px;
|
||||
}
|
||||
|
||||
.subscription-inner {
|
||||
width: min(100%, 930px);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.subscription-title {
|
||||
margin-bottom: 27px;
|
||||
font-size: clamp(28px, 3vw, 43px);
|
||||
line-height: 1.2;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.subscription-list {
|
||||
margin: 0 auto 52px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: #333333;
|
||||
font-size: 13px;
|
||||
line-height: 1.48;
|
||||
}
|
||||
|
||||
.subscription-list li {
|
||||
position: relative;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.subscription-list li + li {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.subscription-list li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0.55em;
|
||||
left: 0;
|
||||
width: 10px;
|
||||
height: 5px;
|
||||
border-left: 2px solid #333333;
|
||||
border-bottom: 2px solid #333333;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.plans-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 36px;
|
||||
}
|
||||
|
||||
.plan-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-height: 324px;
|
||||
padding: 34px 28px 30px;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.plan-icon {
|
||||
width: 86px;
|
||||
height: 86px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.plan-title {
|
||||
margin-bottom: 34px;
|
||||
color: #a51f28;
|
||||
font-size: 22px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.plan-copy {
|
||||
margin: 0 0 44px;
|
||||
color: #555555;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.plan-button {
|
||||
min-height: 39px;
|
||||
margin-top: auto;
|
||||
padding: 0 27px;
|
||||
}
|
||||
|
||||
.legal-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
margin-top: 45px;
|
||||
}
|
||||
|
||||
.legal-links a {
|
||||
color: #333333;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.intro-section {
|
||||
min-height: 620px;
|
||||
padding: 84px 24px 94px;
|
||||
}
|
||||
|
||||
.features-inner {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 34px;
|
||||
width: min(100%, 680px);
|
||||
}
|
||||
|
||||
.feature-art {
|
||||
min-height: clamp(390px, 78vw, 560px);
|
||||
}
|
||||
|
||||
.feature-copy {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.benefits-grid {
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.subscription-inner {
|
||||
width: min(100%, 760px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.aida-hero {
|
||||
height: 292px;
|
||||
}
|
||||
|
||||
.aida-hero-title {
|
||||
padding-top: 24px;
|
||||
font-size: 42px;
|
||||
}
|
||||
|
||||
.intro-section {
|
||||
min-height: 520px;
|
||||
padding: 72px 20px 78px;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.features-section {
|
||||
padding: 64px 20px 44px;
|
||||
}
|
||||
|
||||
.feature-bg {
|
||||
width: 72%;
|
||||
}
|
||||
|
||||
.feature-panel {
|
||||
width: 78%;
|
||||
bottom: 24px;
|
||||
}
|
||||
|
||||
.benefits-section {
|
||||
padding: 52px 20px 76px;
|
||||
}
|
||||
|
||||
.benefits-grid,
|
||||
.plans-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.benefit-icon {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
padding: 58px 20px 76px;
|
||||
}
|
||||
|
||||
.subscription-section {
|
||||
padding: 62px 20px 70px;
|
||||
}
|
||||
|
||||
.subscription-title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.subscription-list {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.aida-hero {
|
||||
height: 250px;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.aida-hero-title {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.intro-copy,
|
||||
.pricing-copy,
|
||||
.academic-copy,
|
||||
.feature-list,
|
||||
.subscription-list {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.feature-art {
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.plan-card {
|
||||
min-height: 292px;
|
||||
padding: 30px 22px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
269
src/pages/home/components/ProductFeature.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
name: string
|
||||
title: string
|
||||
backgroundImage: string
|
||||
backgroundAlt: string
|
||||
panelImage: string
|
||||
panelAlt: string
|
||||
reversed?: boolean
|
||||
}>(),
|
||||
{
|
||||
reversed: false
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="product-feature"
|
||||
:class="{ 'product-feature-reversed': reversed }"
|
||||
v-custom-animation.scroll
|
||||
>
|
||||
<div class="product-feature-art">
|
||||
<img
|
||||
class="product-feature-bg"
|
||||
:src="backgroundImage"
|
||||
:alt="backgroundAlt"
|
||||
loading="lazy"
|
||||
translate-x-s="-100"
|
||||
translate-x="100"
|
||||
/>
|
||||
<img
|
||||
class="product-feature-panel"
|
||||
:src="panelImage"
|
||||
:alt="panelAlt"
|
||||
loading="lazy"
|
||||
translate-y-s="-100"
|
||||
translate-y="20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="product-feature-copy">
|
||||
<p class="product-feature-name">{{ name }}</p>
|
||||
<h2 class="product-feature-title">{{ title }}</h2>
|
||||
<RouterLink
|
||||
class="product-feature-link"
|
||||
to="/products"
|
||||
translate-y-s="100"
|
||||
translate-y="0"
|
||||
>
|
||||
View More
|
||||
</RouterLink>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.product-feature {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(48px, 5.5vw, 76px);
|
||||
width: 1440px;
|
||||
min-width: 0;
|
||||
min-height: 690px;
|
||||
margin: 0 auto;
|
||||
& > div {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.product-feature-reversed {
|
||||
grid-template-columns: minmax(420px, 0.95fr) minmax(0, 1.05fr);
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-art {
|
||||
order: 2;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-copy {
|
||||
order: 1;
|
||||
justify-self: start;
|
||||
max-width: 510px;
|
||||
}
|
||||
|
||||
.product-feature-art {
|
||||
position: relative;
|
||||
width: min(100%, 560px);
|
||||
min-width: 0;
|
||||
min-height: 560px;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.product-feature-bg {
|
||||
display: block;
|
||||
width: min(100%, 480px);
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.product-feature-panel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 94px;
|
||||
width: min(72%, 380px);
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-bg {
|
||||
width: min(100%, 460px);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-panel {
|
||||
right: auto;
|
||||
left: -88px;
|
||||
bottom: 70px;
|
||||
width: min(78%, 430px);
|
||||
}
|
||||
|
||||
.product-feature-copy {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 590px;
|
||||
min-width: 0;
|
||||
justify-self: start;
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
|
||||
.product-feature-name {
|
||||
margin: 0 0 20px;
|
||||
color: #6e6e6e;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.product-feature-title {
|
||||
margin: 0 0 28px;
|
||||
color: #252525;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: clamp(30px, 2.8vw, 38px);
|
||||
font-weight: 700;
|
||||
line-height: 1.16;
|
||||
letter-spacing: 2px;
|
||||
text-transform: none;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.product-feature-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 152px;
|
||||
min-height: 48px;
|
||||
padding: 0 28px;
|
||||
border-radius: 999px;
|
||||
color: #ffffff;
|
||||
background: #a72125;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 2px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.product-feature-link:hover,
|
||||
.product-feature-link:focus-visible {
|
||||
background: #8e171b;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.product-feature-link:focus-visible {
|
||||
outline: 2px solid #a72125;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.product-feature,
|
||||
.product-feature-reversed {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 24px;
|
||||
width: min(680px, calc(100% - 40px));
|
||||
min-height: 0;
|
||||
padding: 70px 0;
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-art,
|
||||
.product-feature-reversed .product-feature-copy {
|
||||
order: initial;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.product-feature-art {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: clamp(390px, 82vw, 560px);
|
||||
justify-self: stretch;
|
||||
}
|
||||
|
||||
.product-feature-copy {
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
justify-self: stretch;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-panel {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.product-feature,
|
||||
.product-feature-reversed {
|
||||
width: calc(100% - 32px);
|
||||
padding: 52px 0;
|
||||
}
|
||||
|
||||
.product-feature-art {
|
||||
min-height: clamp(300px, 82vw, 430px);
|
||||
}
|
||||
|
||||
.product-feature-copy,
|
||||
.product-feature-reversed .product-feature-copy {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
justify-self: stretch !important;
|
||||
}
|
||||
|
||||
.product-feature-bg {
|
||||
width: 78%;
|
||||
}
|
||||
|
||||
.product-feature-panel,
|
||||
.product-feature-reversed .product-feature-panel {
|
||||
left: auto;
|
||||
right: 0;
|
||||
bottom: 42px;
|
||||
width: 68%;
|
||||
}
|
||||
|
||||
.product-feature-reversed .product-feature-bg {
|
||||
width: 72%;
|
||||
}
|
||||
|
||||
.product-feature-title {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
font-size: clamp(26px, 7.2vw, 30px);
|
||||
line-height: 1.2;
|
||||
letter-spacing: 1.2px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
83
src/pages/home/components/ProjectCta.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterLink } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="project-cta">
|
||||
<div class="project-cta-inner">
|
||||
<h2 class="project-cta-title">Talk To Us About Your Next Project</h2>
|
||||
<RouterLink class="project-cta-link" to="/contact">
|
||||
Contact Us
|
||||
</RouterLink>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.project-cta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 435px;
|
||||
padding: 72px 20px;
|
||||
background: #473935;
|
||||
}
|
||||
|
||||
.project-cta-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 38px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.project-cta-title {
|
||||
margin: 0;
|
||||
color: #ffffff;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: clamp(22px, 2vw, 27px);
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
letter-spacing: 2px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.project-cta-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 158px;
|
||||
min-height: 48px;
|
||||
padding: 0 28px;
|
||||
border-radius: 999px;
|
||||
color: #ffffff;
|
||||
background: #ad2228;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
letter-spacing: 2px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
}
|
||||
|
||||
.project-cta-link:hover,
|
||||
.project-cta-link:focus-visible {
|
||||
background: #93191f;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.project-cta-link:focus-visible {
|
||||
outline: 2px solid #ffffff;
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.project-cta {
|
||||
min-height: 340px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,95 +1,181 @@
|
||||
<template>
|
||||
<main class="home-page">
|
||||
<HomeCarousel />
|
||||
|
||||
<section class="home-links" aria-label="Site pages">
|
||||
<RouterLink
|
||||
v-for="page in pageLinks"
|
||||
:key="page.to"
|
||||
class="home-link"
|
||||
:to="page.to"
|
||||
>
|
||||
{{ page.label }}
|
||||
</RouterLink>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useHead } from '@unhead/vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import HomeCarousel from './components/Carousel.vue'
|
||||
import { useHead } from '@unhead/vue'
|
||||
import aidaIntroBg from '@/assets/images/home/aida-intro-bg.png'
|
||||
import aidaPanel from '@/assets/images/home/aida-panel.png'
|
||||
import homeAiLogo from '@/assets/images/home/home-ai-logo.png'
|
||||
import mixiIntroBg from '@/assets/images/home/mixi-intro-bg.png'
|
||||
import mixiPanel from '@/assets/images/home/mixi-panel.png'
|
||||
import HomeCarousel from './components/Carousel.vue'
|
||||
import ProductFeature from './components/ProductFeature.vue'
|
||||
import ProjectCta from './components/ProjectCta.vue'
|
||||
|
||||
const pageLinks = [
|
||||
{ label: 'About', to: '/about' },
|
||||
{ label: 'Products', to: '/products' },
|
||||
{ label: 'Contact', to: '/contact' },
|
||||
] as const
|
||||
const productFeatures = [
|
||||
{
|
||||
name: 'AiDA 3.1',
|
||||
title: 'Empowers fashion designers to create a collection with just a few clicks based on their creative inspirations.',
|
||||
backgroundImage: aidaIntroBg,
|
||||
backgroundAlt: 'Fashion design sketches on paper',
|
||||
panelImage: aidaPanel,
|
||||
panelAlt: 'AiDA design workspace preview',
|
||||
reversed: false
|
||||
},
|
||||
{
|
||||
name: 'Mixi',
|
||||
title: "Drives sales by improving shoppers' experience through precise and fast search.",
|
||||
backgroundImage: mixiIntroBg,
|
||||
backgroundAlt: 'Layered fabric texture',
|
||||
panelImage: mixiPanel,
|
||||
panelAlt: 'Mixi visual search interface preview',
|
||||
reversed: true
|
||||
}
|
||||
] as const
|
||||
|
||||
useHead({
|
||||
title: 'Home | Code Create',
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: 'Code Create home page rendered with Vite SSG.',
|
||||
},
|
||||
],
|
||||
})
|
||||
useHead({
|
||||
title: 'Home | Code Create',
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content:
|
||||
'Code Create revitalises the fashion ecosystem through artificial intelligence.'
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home-page {
|
||||
/* width: 100%; */
|
||||
/* min-height: 100svh; */
|
||||
/* background: #ffffff; */
|
||||
}
|
||||
<template>
|
||||
<main class="home-page">
|
||||
<HomeCarousel />
|
||||
|
||||
.home-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 32px 20px 48px;
|
||||
}
|
||||
<div class="home-content">
|
||||
<section
|
||||
class="ecosystem-intro"
|
||||
aria-labelledby="ecosystem-title"
|
||||
v-custom-animation.scroll
|
||||
>
|
||||
<img
|
||||
class="ecosystem-logo"
|
||||
:src="homeAiLogo"
|
||||
alt="Code Create"
|
||||
loading="lazy"
|
||||
translate-y-s="80"
|
||||
translate-y="0"
|
||||
/>
|
||||
<h1
|
||||
id="ecosystem-title"
|
||||
class="ecosystem-title"
|
||||
translate-y-s="-60"
|
||||
translate-y="0"
|
||||
>
|
||||
Revitalise The Fashion Ecosystem
|
||||
</h1>
|
||||
<p translate-y-s="-60" translate-y="0" class="ecosystem-subtitle">
|
||||
Through Artificial Intelligence (AI)
|
||||
</p>
|
||||
</section>
|
||||
|
||||
.home-link {
|
||||
min-width: 112px;
|
||||
padding: 10px 16px;
|
||||
border: 1px solid #d9dde6;
|
||||
border-radius: 6px;
|
||||
color: #1d2430;
|
||||
background: #ffffff;
|
||||
font-size: 15px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
box-sizing: border-box;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
<ProductFeature
|
||||
v-for="feature in productFeatures"
|
||||
:key="feature.name"
|
||||
:name="feature.name"
|
||||
:title="feature.title"
|
||||
:background-image="feature.backgroundImage"
|
||||
:background-alt="feature.backgroundAlt"
|
||||
:panel-image="feature.panelImage"
|
||||
:panel-alt="feature.panelAlt"
|
||||
:reversed="feature.reversed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
.home-link:hover,
|
||||
.home-link:focus-visible,
|
||||
.home-link.router-link-active {
|
||||
border-color: #2f6df6;
|
||||
color: #2f6df6;
|
||||
box-shadow: 0 8px 24px rgba(36, 55, 92, 0.12);
|
||||
}
|
||||
<ProjectCta />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
.home-link:focus-visible {
|
||||
outline: 2px solid #2f6df6;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
<style scoped lang="less">
|
||||
.home-page {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.home-links {
|
||||
padding: 24px 16px 40px;
|
||||
}
|
||||
.home-content {
|
||||
padding: clamp(116px, 10vw, 172px) 0 clamp(92px, 8vw, 126px);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.home-link {
|
||||
flex: 1 1 calc(50% - 12px);
|
||||
}
|
||||
}
|
||||
.ecosystem-intro {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 20px clamp(92px, 10vw, 150px);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ecosystem-logo {
|
||||
display: block;
|
||||
width: clamp(146px, 15vw, 184px);
|
||||
height: auto;
|
||||
margin-bottom: 30px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ecosystem-title {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 0 9px;
|
||||
color: #101010;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: clamp(18px, 1.65vw, 22px);
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
letter-spacing: 4px;
|
||||
text-transform: uppercase;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.ecosystem-subtitle {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
color: #101010;
|
||||
font-family: Poppins, sans-serif;
|
||||
font-size: clamp(13px, 1.2vw, 16px);
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
letter-spacing: 2.5px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.home-content {
|
||||
padding: 96px 0 64px;
|
||||
}
|
||||
|
||||
.ecosystem-intro {
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.home-content {
|
||||
padding: 72px 0 36px;
|
||||
}
|
||||
|
||||
.ecosystem-logo {
|
||||
width: 138px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.ecosystem-title {
|
||||
width: min(100%, 330px);
|
||||
max-width: none;
|
||||
font-size: 14px;
|
||||
letter-spacing: 1.5px;
|
||||
line-height: 1.45;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.ecosystem-subtitle {
|
||||
font-size: 12px;
|
||||
letter-spacing: 1.5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import AboutView from './pages/about-us/index.vue'
|
||||
import ContactView from './pages/ContactView.vue'
|
||||
import HomeView from './pages/HomeView.vue'
|
||||
import HomeView from './pages/home/index.vue'
|
||||
import ProductsView from './pages/ProductsView.vue'
|
||||
|
||||
import { LANGS } from './lang'
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/:lang?',
|
||||
path: `/:lang(${LANGS.join('|')})?`,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: HomeView,
|
||||
alias: ['/:lang?', '/:lang?/home'],
|
||||
|
||||
},
|
||||
{
|
||||
path: 'about',
|
||||
component: AboutView,
|
||||
path: 'about-us',
|
||||
name: 'about-us',
|
||||
component: AboutView
|
||||
},
|
||||
{
|
||||
path: 'products',
|
||||
name: 'products',
|
||||
component: ProductsView,
|
||||
component: ProductsView
|
||||
},
|
||||
{
|
||||
path: 'contact',
|
||||
path: 'contact-us',
|
||||
name: 'contact',
|
||||
component: ContactView,
|
||||
component: ContactView
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'aida',
|
||||
name: 'Aida',
|
||||
component: () => import('./pages/aida/index.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
8
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
|
||||
const component: DefineComponent<Record<string, never>, Record<string, never>, unknown>
|
||||
export default component
|
||||
}
|
||||
@@ -1,7 +1,26 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
"files": [],
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/types/**/*.d.ts"],
|
||||
"compilerOptions": {
|
||||
"forceConsistentCasingInFileNames": false, // ⚠️ 禁用大小写检查
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["node", "unplugin-vue-define-options/macros-global"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"_c/*": ["./src/components/*"]
|
||||
},
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"noEmit": true,
|
||||
"noImplicitAny": false,
|
||||
"noEmitOnError": false
|
||||
},
|
||||
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||