# Conflicts:
#	src/router/index.js
This commit is contained in:
Phoenix 2025-05-23 09:57:43 +08:00
commit 6d1d22171a
41 changed files with 2103 additions and 70 deletions

View File

@ -13,6 +13,8 @@
"dependencies": { "dependencies": {
"@fingerprintjs/fingerprintjs": "^4.4.3", "@fingerprintjs/fingerprintjs": "^4.4.3",
"@unocss/reset": "^0.61.9", "@unocss/reset": "^0.61.9",
"@vicons/ionicons5": "^0.13.0",
"@vicons/utils": "^0.1.4",
"axios": "^1.7.3", "axios": "^1.7.3",
"cnjm-postcss-px-to-viewport": "^1.0.1", "cnjm-postcss-px-to-viewport": "^1.0.1",
"echarts": "^5.6.0", "echarts": "^5.6.0",

View File

@ -14,6 +14,12 @@ importers:
'@unocss/reset': '@unocss/reset':
specifier: ^0.61.9 specifier: ^0.61.9
version: 0.61.9 version: 0.61.9
'@vicons/ionicons5':
specifier: ^0.13.0
version: 0.13.0
'@vicons/utils':
specifier: ^0.1.4
version: 0.1.4(vue@3.4.35)
axios: axios:
specifier: ^1.7.3 specifier: ^1.7.3
version: 1.7.3 version: 1.7.3
@ -1341,6 +1347,9 @@ packages:
'@types/minimatch@5.1.2': '@types/minimatch@5.1.2':
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
'@types/node@14.14.45':
resolution: {integrity: sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==}
'@types/node@22.13.10': '@types/node@22.13.10':
resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==}
@ -1450,6 +1459,14 @@ packages:
peerDependencies: peerDependencies:
vue: ^3.0.0 vue: ^3.0.0
'@vicons/ionicons5@0.13.0':
resolution: {integrity: sha512-zvZKBPjEXKN7AXNo2Na2uy+nvuv6SP4KAMQxpKL2vfHMj0fSvuw7JZcOPCjQC3e7ayssKnaoFVAhbYcW6v41qQ==}
'@vicons/utils@0.1.4':
resolution: {integrity: sha512-OHI19qVNN6i+uPQ+Y3f2s0dUxwsYnOCcKBW7XOU4yXXO1aU3ZoKpblCc3+4N0qmgoJs5rWKRAaMisipqEXJwAg==}
peerDependencies:
vue: ^3.0.6
'@vitejs/plugin-legacy@5.4.1': '@vitejs/plugin-legacy@5.4.1':
resolution: {integrity: sha512-kee0l7dVevCNs1l3u2PnihVunvQ0WTJL2UJ/siQGD3Iht546mR9NO16tCv32uCP6lcGO1QDLqlPqInJtV1FE7A==} resolution: {integrity: sha512-kee0l7dVevCNs1l3u2PnihVunvQ0WTJL2UJ/siQGD3Iht546mR9NO16tCv32uCP6lcGO1QDLqlPqInJtV1FE7A==}
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
@ -1505,6 +1522,9 @@ packages:
'@vueuse/shared@10.11.0': '@vueuse/shared@10.11.0':
resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==} resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==}
'@xicons/utils@0.1.4':
resolution: {integrity: sha512-uXxKDLz9abr80yJC05XSTq6wlyFcdW+N/1IYJkeHjzzXVc4VQ0sEYMoMMTjAH7HQBOyOkzOB4pf5NGF72lwa8Q==}
acorn@8.12.1: acorn@8.12.1:
resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
@ -1819,6 +1839,9 @@ packages:
peerDependencies: peerDependencies:
postcss: ^8.4 postcss: ^8.4
css-render@0.13.9:
resolution: {integrity: sha512-n3C4ZH59rveBrUlAD7n0Ze9/gUMKa4dlH1C9CWKpGcIHR/xRcIVXzBGy1iw8WWq2ySmn2/ZqOpySQNAK5Pb6sw==}
css-render@0.15.14: css-render@0.15.14:
resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==} resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==}
@ -5147,6 +5170,8 @@ snapshots:
'@types/minimatch@5.1.2': {} '@types/minimatch@5.1.2': {}
'@types/node@14.14.45': {}
'@types/node@22.13.10': '@types/node@22.13.10':
dependencies: dependencies:
undici-types: 6.20.0 undici-types: 6.20.0
@ -5329,6 +5354,13 @@ snapshots:
dependencies: dependencies:
vue: 3.4.35 vue: 3.4.35
'@vicons/ionicons5@0.13.0': {}
'@vicons/utils@0.1.4(vue@3.4.35)':
dependencies:
'@xicons/utils': 0.1.4
vue: 3.4.35
'@vitejs/plugin-legacy@5.4.1(terser@5.31.3)(vite@5.3.5(@types/node@22.13.10)(sass@1.77.8)(terser@5.31.3))': '@vitejs/plugin-legacy@5.4.1(terser@5.31.3)(vite@5.3.5(@types/node@22.13.10)(sass@1.77.8)(terser@5.31.3))':
dependencies: dependencies:
'@babel/core': 7.25.2 '@babel/core': 7.25.2
@ -5424,6 +5456,10 @@ snapshots:
- '@vue/composition-api' - '@vue/composition-api'
- vue - vue
'@xicons/utils@0.1.4':
dependencies:
css-render: 0.13.9
acorn@8.12.1: {} acorn@8.12.1: {}
agent-base@7.1.1: agent-base@7.1.1:
@ -5782,6 +5818,12 @@ snapshots:
dependencies: dependencies:
postcss: 8.4.40 postcss: 8.4.40
css-render@0.13.9:
dependencies:
'@emotion/hash': 0.8.0
'@types/node': 14.14.45
csstype: 3.0.11
css-render@0.15.14: css-render@0.15.14:
dependencies: dependencies:
'@emotion/hash': 0.8.0 '@emotion/hash': 0.8.0

View File

@ -1,10 +1,10 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from 'vue'
import { useI18n } from "vue-i18n"; import { useI18n } from 'vue-i18n'
import { NConfigProvider, NDropdown } from "naive-ui"; import { NConfigProvider, NDropdown } from 'naive-ui'
const { locale } = useI18n(); const { locale } = useI18n()
const primaryColor = ref("#2B69A1"); const primaryColor = ref('#8B59F7')
const themeOverrides = ref({ const themeOverrides = ref({
common: { common: {
primaryColorPressed: primaryColor, primaryColorPressed: primaryColor,
@ -15,10 +15,7 @@ const themeOverrides = ref({
primaryColor: primaryColor, primaryColor: primaryColor,
primaryColorHover: primaryColor, primaryColorHover: primaryColor,
}, },
}); })
</script> </script>
<template> <template>

Binary file not shown.

BIN
src/assets/image/bg-375.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
src/assets/image/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="19" height="19" viewBox="0 0 19 19"><defs><style>.a{fill:#fff;stroke:#707070;}.b{clip-path:url(#a);}.c{fill:#2c2c2c;}</style><clipPath id="a"><rect class="a" width="19" height="19" transform="translate(333.601 660.601)"/></clipPath></defs><g class="b" transform="translate(-333.601 -660.601)"><g transform="translate(333.591 664.087)"><path class="c" d="M19.237,22.964H2.325a.922.922,0,0,1-.922-.922V15.408a.922.922,0,1,1,1.844,0V21.12H18.315V15.408a.922.922,0,1,1,1.844,0v6.634A.922.922,0,0,1,19.237,22.964Z" transform="translate(-1.403 -7.061)"/><path class="c" d="M12.451,13.284c-.509,0-.922-.311-.922-.694V1.332c0-.384.413-.694.922-.694s.922.311.922.694V12.589C13.373,12.973,12.96,13.284,12.451,13.284Z" transform="translate(-3.073 -0.638)"/><path class="c" d="M11.833,20.248a.922.922,0,0,1-.748-.384L7.956,15.51a.922.922,0,1,1,1.5-1.076l2.381,3.313,2.38-3.313a.922.922,0,1,1,1.5,1.076l-3.129,4.355A.921.921,0,0,1,11.833,20.248Z" transform="translate(-2.455 -6.989)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/assets/image/pdf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
import size375 from '@/components/customDefaultPage/size375/index.vue'
import size1920 from '@/components/customDefaultPage/size1920/index.vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
const router = useRouter()
const { width } = useWindowSize()
const { t } = useI18n()
const viewComponent = computed(() => {
const viewWidth = width.value
if (viewWidth <= 450) {
return size375
}
// else if (viewWidth <= 1100) {
// return size768;
// } else if (viewWidth <= 1500) {
// return size1440;
// }
else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920
}
})
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,18 @@
<template>
<!-- 通用缺省页 -->
<div class="custom-default-page">
<div class="search-area">
<customSelectSearch></customSelectSearch>
</div>
</div>
</template>
<script setup>
import customSelectSearch from '@/components/customSelectSearch/index.vue'
</script>
<style scoped lang="scss">
.search-area {
width: 300px;
margin: 100px 300px;
}
</style>

View File

@ -0,0 +1,18 @@
<template>
<!-- 通用缺省页 -->
<div class="custom-default-page">
<div class="search-area">
<customSelectSearch></customSelectSearch>
</div>
</div>
</template>
<script setup>
import customSelectSearch from '@/components/customSelectSearch/index.vue'
</script>
<style scoped lang="scss">
.search-area {
width: 1500px;
margin: 500px 200px;
}
</style>

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
import size375 from '@/components/customFooter/size375/index.vue'
import size1920 from '@/components/customFooter/size1920/index.vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
const router = useRouter()
const { width } = useWindowSize()
const { t } = useI18n()
const viewComponent = computed(() => {
const viewWidth = width.value
if (viewWidth <= 450) {
return size375
}
// else if (viewWidth <= 1100) {
// return size768;
// } else if (viewWidth <= 1500) {
// return size1440;
// }
else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920
}
})
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,26 @@
<template>
<!-- 通用页脚 -->
<div class="custom-footer">
<span>Copyright &copy; 2024-2027 FiEE</span>
</div>
</template>
<script setup></script>
<style scoped lang="scss">
.custom-footer {
width: 100%;
text-align: center;
padding: 24px 0;
color: #888;
font-size: 15px;
background: #f7f8fa;
letter-spacing: 1px;
border-top: 1px solid #ececec;
position: fixed;
left: 0;
bottom: 0;
z-index: 100;
}
</style>

View File

@ -0,0 +1,26 @@
<template>
<!-- 通用页脚 -->
<div class="custom-footer">
<span>Copyright &copy; 2024-2027 FiEE</span>
</div>
</template>
<script setup></script>
<style scoped lang="scss">
.custom-footer {
width: 100%;
text-align: center;
padding: 120px 0;
color: #888;
font-size: 75px;
background: #f7f8fa;
letter-spacing: 5px;
border-top: 5px solid #ececec;
position: fixed;
left: 0;
bottom: 0;
z-index: 100;
}
</style>

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
import size375 from '@/components/customHeader/size375/index.vue'
import size1920 from '@/components/customHeader/size1920/index.vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
const router = useRouter()
const { width } = useWindowSize()
const { t } = useI18n()
const viewComponent = computed(() => {
const viewWidth = width.value
if (viewWidth <= 450) {
return size375
}
// else if (viewWidth <= 1100) {
// return size768;
// } else if (viewWidth <= 1500) {
// return size1440;
// }
else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920
}
})
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,225 @@
<template>
<!-- 通用页头 -->
<NLayoutHeader
class="custom-header"
:class="{ 'header-scrolled': isScrolled }"
>
<div class="header-container">
<div class="logo" @click="handleToHome">
<NImage width="160" height="50" :src="FiEELogo" preview-disabled />
</div>
<div class="header-menu">
<NMenu
mode="horizontal"
:options="menuOptions"
:inverted="isScrolled"
v-model:value="selectedKey"
@update:value="handleMenuSelect"
/>
</div>
</div>
</NLayoutHeader>
</template>
<script setup>
import FiEELogo from '@/assets/image/header/logo.png'
import { ref, onMounted, onUnmounted } from 'vue'
import { NMenu, NLayoutHeader, NImage } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useHeaderMenuConfig } from '@/config/headerMenuConfig'
const { t } = useI18n()
const router = useRouter()
// 使
const menuOptions = useHeaderMenuConfig()
const selectedKey = ref(null)
const isScrolled = ref(false)
//
function findMenuOptionByKey(options, key) {
for (const option of options) {
if (option.key === key) return option;
if (option.children) {
const found = findMenuOptionByKey(option.children, key);
if (found) return found;
}
}
return null;
}
//
const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key);
if (option && option.href) {
router.push(option.href);
}
};
//
const handleScroll = () => {
//100pxheader
isScrolled.value = window.scrollY >= 100;
};
onMounted(() => {
window.addEventListener("scroll", handleScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
//
const handleToHome = () => {
router.push('/')
selectedKey.value = null //
}
</script>
<style scoped lang="scss">
.custom-header {
--header-height: 80px;
--primary-color: #8B59F7;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
transition: all 0.3s ease;
background: transparent;
height: var(--header-height);
&.header-scrolled {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(8px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
.header-container {
max-width: 1700px;
margin: 0 auto;
padding: 0 40px;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
flex-shrink: 0;
cursor: pointer;
transition: transform 0.3s ease;
margin-right: 100px;
&:hover {
transform: scale(1.05);
}
&:active {
transform: scale(0.98);
}
}
.header-menu {
display: block;
flex: 1;
:deep(.n-menu) {
background: transparent;
justify-content: flex-end;
}
:deep(.n-menu-item) {
position: relative;
margin: 0 20px;
transition: all 0.3s ease;
font-weight: 700;
font-size: 16px;
min-width: 120px;
text-align: center;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background: var(--primary-color);
transition: all 0.3s ease;
transform: translateX(-50%);
opacity: 0;
border-radius: 2px;
}
&:hover {
&::after {
width: 80px;
height: 3px;
opacity: 1;
}
}
//
&.n-menu-item--selected {
&::after {
width: 40px;
opacity: 1;
}
}
}
//
:deep(.n-submenu) {
.n-submenu-children {
backdrop-filter: blur(16px);
background: rgba(255, 255, 255, 0.9);
border-radius: 12px;
padding: 8px 0;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
transform-origin: top;
animation: dropDown 0.3s ease;
.n-menu-item {
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--primary-color);
transform: translateX(-100%);
transition: transform 0.3s ease;
opacity: 0.1;
z-index: -1;
}
&:hover {
&::before {
transform: translateX(0);
}
}
}
}
}
}
@keyframes dropDown {
from {
opacity: 0;
transform: translateY(-10px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
</style>

View File

@ -0,0 +1,215 @@
<template>
<!-- 通用页头 -->
<NLayoutHeader
class="custom-header"
:class="{ 'header-scrolled': isScrolled }"
>
<div class="header-container">
<div class="logo" @click="handleToHome">
<NImage
style="width: 108px; height: 33px; max-width: 100%"
:src="FiEELogo"
preview-disabled
/>
</div>
<div class="menu-btn" :class="{ 'menu-open': showMenu }" @click="toggleMenu">
<n-icon size="28" class="menu-icon menu-icon-menu">
<menu-sharp />
</n-icon>
<n-icon size="28" class="menu-icon menu-icon-close">
<close-sharp />
</n-icon>
</div>
</div>
</NLayoutHeader>
<transition name="fade-slide">
<div v-if="showMenu" class="mobile-menu-wrapper" @click.self="closeMenu">
<NMenu
mode="vertical"
:options="menuOptions"
:inverted="isScrolled"
class="mobile-menu"
accordion
v-model:value="selectedKey"
@update:value="handleMenuSelect"
/>
</div>
</transition>
</template>
<script setup>
import FiEELogo from '@/assets/image/header/logo.png'
import { ref, onMounted, onUnmounted } from 'vue'
import { NMenu, NLayoutHeader, NImage, NIcon } from 'naive-ui'
import { MenuSharp, CloseSharp } from '@vicons/ionicons5'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useHeaderMenuConfig } from '@/config/headerMenuConfig'
const { t } = useI18n()
const router = useRouter()
const isScrolled = ref(false)
const showMenu = ref(false)
const selectedKey = ref(null)
const toggleMenu = () => {
showMenu.value = !showMenu.value;
};
const closeMenu = () => {
showMenu.value = false;
};
//
function findMenuOptionByKey(options, key) {
for (const option of options) {
if (option.key === key) return option;
if (option.children) {
const found = findMenuOptionByKey(option.children, key);
if (found) return found;
}
}
return null;
}
//
const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key);
if (option && option.href) {
router.push(option.href);
showMenu.value = false; //
}
};
// 使
const menuOptions = useHeaderMenuConfig()
//
const handleScroll = () => {
//100pxheader
isScrolled.value = window.scrollY >= 100;
};
onMounted(() => {
window.addEventListener("scroll", handleScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
//
const handleToHome = () => {
router.push('/')
selectedKey.value = null //
showMenu.value = false //
}
</script>
<style scoped lang="scss">
.custom-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
transition: all 0.3s ease;
background: transparent;
height: 320px;
&.header-scrolled {
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
.header-container {
width: 100%;
min-width: 0;
box-sizing: border-box;
padding: 0 80px;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
flex-shrink: 0;
margin-right: 12px;
display: flex;
align-items: center;
}
.logo :deep(.n-image) {
max-width: 100px;
height: auto;
}
.menu-btn {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 75px;
padding: 40px 70px;
border-radius: 30px;
background: transparent;
user-select: none;
transition: background 0.2s;
position: relative;
.menu-icon {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(0deg);
opacity: 1;
transition:
opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
}
.menu-icon-close {
opacity: 0;
transform: translate(-50%, -50%) rotate(-90deg) scale(0.8);
}
&.menu-open {
.menu-icon-menu {
opacity: 0;
transform: translate(-50%, -50%) rotate(90deg) scale(0.8);
}
.menu-icon-close {
opacity: 1;
transform: translate(-50%, -50%) rotate(0deg) scale(1);
}
}
}
.mobile-menu-wrapper {
position: fixed;
top: 320px;
left: 0;
width: 100vw;
background: #fff;
z-index: 1100;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
padding: 40px 0 80px 0;
max-height: 1500px;
overflow-y: auto;
:deep(.n-menu-item) {
font-weight: 600;
}
}
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.fade-slide-enter-from,
.fade-slide-leave-to {
opacity: 0;
transform: translateY(-50px);
}
</style>

View File

@ -0,0 +1,38 @@
<template>
<div class="custom-select-search">
<n-select
:options="state.selectOptions"
v-model:value="state.selectedValue"
/>
<n-input
v-model:value="state.inputValue"
type="text"
placeholder="Search"
/>
<n-button type="primary">Go</n-button>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import { NSelect, NInput, NButton } from 'naive-ui'
const state = reactive({
selectedValue: 'all_years', //
selectOptions: [
{
label: 'All Years',
value: 'all_years',
},
], //
inputValue: '', //
})
</script>
<style scoped lang="scss">
.custom-select-search {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 10px;
}
</style>

View File

@ -0,0 +1,105 @@
import { useI18n } from 'vue-i18n'
export const useHeaderMenuConfig = () => {
const { t } = useI18n()
return [
{
label: t('header_menu.corporate_information.title'),
key: 'corporate_information',
children: [
{
label: t('header_menu.corporate_information.company_overview'),
key: 'company_overview',
},
{
label: t('header_menu.corporate_information.business_introduction'),
key: 'business_introduction',
},
{
label: t('header_menu.corporate_information.management'),
key: 'management',
},
{
label: t('header_menu.corporate_information.board_of_directors'),
key: 'board_of_directors',
},
{
label: t('header_menu.corporate_information.committee_appointments'),
key: 'committee_appointments',
},
{
label: t('header_menu.corporate_information.governance'),
key: 'governance',
},
{
label: t('header_menu.corporate_information.corporate_video'),
key: 'corporate_video',
},
],
},
{
label: t('header_menu.financial_information.title'),
key: 'financial_information',
children: [
{
label: t('header_menu.financial_information.sec_filings'),
key: 'sec_filings',
href: "/secfilings",
},
{
label: t('header_menu.financial_information.quarterly_results'),
key: 'quarterly_results',
href: "/quarterlyresults",
},
],
},
{
label: t('header_menu.stock_information.title'),
key: 'stock_information',
children: [
{
label: t('header_menu.stock_information.stock_quote'),
key: 'stock_quote',
},
{
label: t('header_menu.stock_information.historic_stock_price'),
key: 'historic_stock_price',
},
{
label: t('header_menu.stock_information.investment_calculator'),
key: 'investment_calculator',
},
],
},
{
label: t('header_menu.news_releases.title'),
key: 'news_releases',
children: [
{
label: t('header_menu.news_releases.press_releases'),
key: 'press_releases',
href: '/press-releases',
},
{
label: t('header_menu.news_releases.events_calendar'),
key: 'events_calendar',
},
],
},
{
label: t('header_menu.investor_resources.title'),
key: 'investor_resources',
children: [
{
label: t('header_menu.investor_resources.ir_contacts'),
key: 'ir_contacts',
},
{
label: t('header_menu.investor_resources.email_alerts'),
key: 'email_alerts',
},
],
},
]
}

View File

@ -463,5 +463,66 @@ export default {
], ],
note: 'Minim representatives (other than authorized speakers) who receive inquiries from the media, market professionals, or shareholders should not respond to such inquiries but should refer the inquirer to an authorized speaker. However, Minim representatives assigned to the Investor Relations and Marketing teams may respond to routine inquiries about public information in accordance with guidelines established by authorized speakers from time to time.' note: 'Minim representatives (other than authorized speakers) who receive inquiries from the media, market professionals, or shareholders should not respond to such inquiries but should refer the inquirer to an authorized speaker. However, Minim representatives assigned to the Investor Relations and Marketing teams may respond to routine inquiries about public information in accordance with guidelines established by authorized speakers from time to time.'
} }
},
financialinformation: {
secfilings: {
title: 'Financials',
overview: {
title: 'Company Financial Overview',
desc: 'This page offers access to our <strong>Annual Reports</strong> and <strong>SEC Filings</strong>. These sections provide key financial data and regulatory documents, keeping you informed about our financial performance and compliance.'
},
annual_reports: {
title: 'Annual Reports',
file_name: 'File Name',
view: 'View'
},
sec: {
title: 'SEC Filings',
desc: 'To Access All Of Our Fillings With Sec Sites, Please',
click_here: 'Click Here'
}
},
quarterlyresults: {
title: 'Quarterly Results',
search: {
placeholder: 'Search',
button: 'Go'
},
download: 'Download'
}
},
header_menu: {
corporate_information: {
title: 'Corporate Information',
company_overview: 'Company Overview',
business_introduction: 'Business Introduction',
management: 'Management',
board_of_directors: 'Board of Directors',
committee_appointments: 'Committee Appointments',
governance: 'Governance',
corporate_video: 'Corporate Video'
},
financial_information: {
title: 'Financial Information',
sec_filings: 'SEC Filings',
quarterly_results: 'Quarterly Results',
},
stock_information: {
title: 'Stock Information',
stock_quote: 'Stock Quote',
historic_stock_price: 'Historic Stock Price',
investment_calculator: 'Investment Calculator'
},
news_releases: {
title: 'News Releases',
press_releases: 'Press Releases',
events_calendar: 'Events Calendar'
},
investor_resources: {
title: 'Investor Resources',
ir_contacts: 'IR Contacts',
email_alerts: 'Email Alerts'
}
} }
} }

View File

@ -1,14 +1,20 @@
// router/index.js // router/index.js
import { createRouter, createWebHistory } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router'
import { setupRouterGuards } from './router-guards'; import { setupRouterGuards } from './router-guards'
const routes = [ const routes = [
{ {
path: '/', path: '/',
redirect: 'home' name: 'index',
}, component: () => import('@/views/index/index.vue'),
{ // beforeEnter: (to, from, next) => {
path: '/home',
// localStorage.clear()
// next()
// }
children: [
{
path: 'home',
name: 'home', name: 'home',
component: () => import('@/views/home/index.vue'), component: () => import('@/views/home/index.vue'),
// beforeEnter: (to, from, next) => { // beforeEnter: (to, from, next) => {
@ -38,16 +44,67 @@ const routes = [
component: () => import('@/views/email-alerts/index.vue'), component: () => import('@/views/email-alerts/index.vue'),
}, },
]; ];
},
{
path: 'press-releases',
name: 'press-releases',
component: () => import('@/views/press-releases/index.vue'),
},
{
path: '/quarterlyresults',
name: 'QuarterlyResults',
component: () =>
import('@/views/financialinformation/quarterlyresults/index.vue'),
},
{
path: '/secfilings',
name: 'SecFilings',
component: () =>
import('@/views/financialinformation/secfilings/index.vue'),
},
],
},
{
path: '/contacts',
name: 'contacts',
component: () => import('@/views/contacts/index.vue'),
},
// {
// path: '/companyprofil',
// name: 'Companyprofil',
// component: () => import('@/views/companyprofil/index.vue'),
// },
// {
// path: '/companyprofildetail',
// name: 'Companyprofildetail',
// component: () => import('@/views/companyprofildetail/index.vue'),
// },
// {
// path: '/businessintroduction',
// name: 'Businessintroduction',
// component: () => import('@/views/businessintroduction/index.vue'),
// },
// {
// path: '/investor',
// name: 'Investor',
// component: () => import('@/views/investor/index.vue'),
// },
// {
// path: '/investorhandbook',
// name: 'Investorhandbook',
// component: () => import('@/views/investorhandbook/index.vue'),
// },
]
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes routes,
}); })
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
if (to.meta?.title) { if (to.meta?.title) {
document.title = to.meta.title; document.title = to.meta.title
} }
next() next()
}); })
setupRouterGuards(router); setupRouterGuards(router)
export default router; export default router

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from "vue";
import { useWindowSize } from "@vueuse/core";
import size375 from "@/views/financialinformation/quarterlyresults/size375/index.vue";
import size768 from "@/views/financialinformation/quarterlyresults/size1920/index.vue";
import size1440 from "@/views/financialinformation/quarterlyresults/size1920/index.vue";
import size1920 from "@/views/financialinformation/quarterlyresults/size1920/index.vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
const router = useRouter();
const { width } = useWindowSize();
const { t } = useI18n();
const viewComponent = computed(() => {
const viewWidth = width.value;
if (viewWidth <= 450) {
return size375;
} else if (viewWidth <= 1100) {
return size768;
} else if (viewWidth <= 1500) {
return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920;
}
});
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,153 @@
<template>
<header></header>
<main class="p-[35px] max-w-[1200px] mx-auto">
<div class="title mb-[20px]">
{{ t("financialinformation.quarterlyresults.title") }}
</div>
<div class="search-container">
<input
type="text"
:placeholder="
t('financialinformation.quarterlyresults.search.placeholder')
"
v-model="searchQuery"
class="search-input"
/>
<button class="search-button" @click="handleSearch">
{{ t("financialinformation.quarterlyresults.search.button") }}
</button>
</div>
<div class="results-list">
<div
v-for="(item, index) in filteredList"
:key="index"
class="result-item"
>
<div class="content">
<a :href="item.url" class="result-title subtitle">{{ item.title }}</a>
<p class="result-description content-text">{{ item.description }}</p>
</div>
<div class="pdf-icon">
<a :href="item.url" target="_blank">
<img src="@/assets/image/pdf.png" alt="PDF" />
</a>
</div>
</div>
</div>
</main>
<footer></footer>
</template>
<script setup>
import { ref, watch, onMounted, computed, reactive } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const searchQuery = ref("");
const state = reactive({
list: [
{
title: "2025 Q1 Quarterly Results",
description:
"Unaudited First Quarter and Full Year 2025 Financial Results",
url: "/src/assets/file/2025 Q1 Quarterly Results.pdf",
},
],
});
onMounted(async () => {});
const filteredList = computed(() => {
if (!searchQuery.value) return state.list;
const query = searchQuery.value.toLowerCase();
return state.list.filter(
(item) =>
item.title.toLowerCase().includes(query) ||
item.description.toLowerCase().includes(query)
);
});
const handleSearch = () => {
//
console.log("搜索:", searchQuery.value);
};
</script>
<style scoped lang="scss">
.title {
font-size: 40px;
color: #333;
}
.subtitle {
font-size: 18px;
font-weight: 600;
color: #333;
}
.content-text {
font-size: 16px;
}
.search-container {
margin-bottom: 20px;
display: flex;
align-items: center;
}
.search-input {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
width: 240px;
margin-right: 10px;
}
.search-button {
padding: 8px 16px;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.results-list {
padding-right: 20px;
margin-top: 20px;
max-height: 1200px;
overflow-y: auto;
}
.result-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.content {
flex: 1;
}
.result-title {
color: #0078d7;
text-decoration: none;
display: block;
margin-bottom: 5px;
&:hover {
text-decoration: underline;
}
}
.result-description {
color: #666;
margin: 0;
}
.pdf-icon {
margin-left: 15px;
img {
width: 24px;
height: 24px;
}
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<header></header>
<main class="p-[80px] mx-auto" style="max-width: 100vw; min-width: 375px">
<div class="page-title mb-[24px]">
{{ t("financialinformation.quarterlyresults.title") }}
</div>
<div class="search-container">
<n-input
v-model:value="searchQuery"
:placeholder="
t('financialinformation.quarterlyresults.search.placeholder')
"
clearable
:font-size="72"
/>
<n-button
type="primary"
@click="handleSearch"
:font-size="72"
class="ml-[10px]"
>
{{ t("financialinformation.quarterlyresults.search.button") }}
</n-button>
</div>
<div class="results-list">
<div
v-for="(item, index) in filteredList"
:key="index"
class="result-item flex items-center mt-[20px] mb-[20px]"
>
<img
src="@/assets/image/pdf.png"
alt="PDF"
style="width: 20px; height: 20px"
/>
<div class="content">
<div class="result-title">{{ item.title }}</div>
</div>
<img
src="@/assets/image/download.svg"
style="width: 20px; height: 20px"
@click="handleDownload(item.url)"
/>
</div>
</div>
</main>
<footer></footer>
</template>
<script setup>
import { ref, watch, onMounted, computed, reactive } from "vue";
import { NButton, NInput, NTooltip } from "naive-ui";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const searchQuery = ref("");
const state = reactive({
list: [
{
title: "2025 Q1 Quarterly Results",
description:
"Unaudited First Quarter and Full Year 2025 Financial Results",
url: "/src/assets/file/2025 Q1 Quarterly Results.pdf",
},
],
});
onMounted(async () => {});
const filteredList = computed(() => {
if (!searchQuery.value) return state.list;
const query = searchQuery.value.toLowerCase();
return state.list.filter(
(item) =>
item.title.toLowerCase().includes(query) ||
item.description.toLowerCase().includes(query)
);
});
const handleSearch = () => {
//
console.log("搜索:", searchQuery.value);
};
const handleDownload = (url) => {
//
console.log("下载:", url);
// a
const link = document.createElement("a");
link.href = url;
link.download = url.split("/").pop(); // URL
link.target = "_blank";
//
const isMobile =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
);
if (isMobile) {
// 使fetchblob
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const objectUrl = URL.createObjectURL(blob);
link.href = objectUrl;
document.body.appendChild(link);
link.click();
//
setTimeout(() => {
document.body.removeChild(link);
URL.revokeObjectURL(objectUrl);
}, 100);
})
.catch((error) => {
console.error("下载文件时出错:", error);
// fetch退window.open
window.open(url, "_blank");
});
} else {
//
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
};
</script>
<style scoped lang="scss">
.page-title {
font-size: 113px;
font-weight: bold;
color: #333;
text-align: center;
margin-top: 8px;
}
.search-container {
margin-bottom: 24px;
display: flex;
align-items: center;
background-color: #f6f7f9;
border-radius: 8px;
padding: 8px;
}
.results-list {
margin-top: 46px;
max-height: 3000px;
overflow-y: auto;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
background-color: #fff;
}
.result-item {
display: flex;
justify-content: space-between;
padding: 46px;
border-bottom: 1px solid #f0f0f0;
transition: background-color 0.2s;
&:hover {
background-color: #f9fafc;
}
&:last-child {
border-bottom: none;
}
}
.result-title {
color: #2979ff;
text-decoration: none;
display: block;
margin-bottom: 8px;
font-size: 92px;
font-weight: 600;
&:hover {
text-decoration: underline;
}
}
.result-description {
color: #666;
margin: 0;
font-size: 72px;
line-height: 1.4;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
.pdf-icon {
margin-left: 16px;
display: flex;
flex-direction: column;
align-items: center;
.pdf-link {
display: flex;
flex-direction: column;
align-items: center;
text-decoration: none;
color: #ff5252;
font-size: 50px;
&:hover {
opacity: 0.8;
}
}
}
</style>

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from "vue";
import { useWindowSize } from "@vueuse/core";
import size375 from "@/views/financialinformation/secfilings/size375/index.vue";
import size768 from "@/views/financialinformation/secfilings/size1920/index.vue";
import size1440 from "@/views/financialinformation/secfilings/size1920/index.vue";
import size1920 from "@/views/financialinformation/secfilings/size1920/index.vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
const router = useRouter();
const { width } = useWindowSize();
const { t } = useI18n();
const viewComponent = computed(() => {
const viewWidth = width.value;
if (viewWidth <= 450) {
return size375;
} else if (viewWidth <= 1100) {
return size768;
} else if (viewWidth <= 1500) {
return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920;
}
});
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,262 @@
<template>
<div class="page-container">
<div class="financials-container">
<!-- 标题 -->
<div class="financials-title">
{{ t("financialinformation.secfilings.title") }}
</div>
<!-- 公司财务概览 -->
<section class="section">
<div class="section-title">
{{ t("financialinformation.secfilings.overview.title") }}
</div>
<p
class="overview-text"
v-html="t('financialinformation.secfilings.overview.desc')"
></p>
</section>
<!-- 年度报告 -->
<section class="section">
<div class="section-title">
{{ t("financialinformation.secfilings.annual_reports.title") }}
</div>
<!-- 报告表格 -->
<div class="reports-table">
<div class="table-header">
<div class="column file-name">
{{
t("financialinformation.secfilings.annual_reports.file_name")
}}
</div>
<div class="column download"></div>
</div>
<!-- 报告列表 -->
<div class="reports-list">
<div
class="table-row"
v-for="(report, index) in annualReports"
:key="index"
>
<div class="column file-name">{{ report.fileName }}</div>
<div class="column download">
<a :href="report.downloadUrl" class="download-link">{{
t("financialinformation.secfilings.annual_reports.view")
}}</a>
</div>
</div>
</div>
</div>
</section>
<!-- SEC文件 -->
<section class="section">
<div class="section-title">
{{ t("financialinformation.secfilings.sec.title") }}
</div>
<p class="sec-text">
{{ t("financialinformation.secfilings.sec.desc") }}
<a
href="https://www.sec.gov/cgi-bin/browse-edgar?company=&CIK=NCTY&filenum=&State=&SIC=&owner=include&action=getcompany"
class="link"
>{{ t("financialinformation.secfilings.sec.click_here") }}</a
>.
</p>
</section>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
//
const annualReports = ref([
{
fileName: "fieeinc_10q",
downloadUrl:
"https://www.sec.gov/ix?doc=/Archives/edgar/data/1467761/000182912625003706/fieeinc_10q.htm",
},
{
fileName: "fieeinc_ex31-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex31-1.htm",
},
{
fileName: "fieeinc_ex31-2",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex31-2.htm",
},
{
fileName: "fieeinc_ex32-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex32-1.htm",
},
{
fileName: "fieeinc_ex32-2",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex32-2.htm",
},
{
fileName: " ownership",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003622/xslF345X05/ownership.xml",
},
{
fileName: "primary_doc",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003620/xslSCHEDULE_13D_X01/primary_doc.xml",
},
{
fileName: "fieeinc_ex1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003620/fieeinc_ex1.htm",
},
{
fileName: "fiee_8ka",
downloadUrl:
"https://www.sec.gov/ix?doc=/Archives/edgar/data/1467761/000182912625003580/fiee_8ka.htm",
},
{
fileName: "fiee_ex4-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003580/fiee_ex4-1.htm",
},
{
fileName: "fiee_ex10-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003580/fiee_ex10-1.htm",
},
]);
</script>
<style scoped lang="scss">
.page-container {
background-image: url("@/assets/image/bg.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
.financials-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.financials-title {
font-size: 40px;
text-align: center;
margin-bottom: 60px;
color: #333;
}
.section {
margin-bottom: 60px;
}
.section-title {
font-size: 18px;
margin-bottom: 20px;
color: #333;
}
.overview-text {
font-size: 16px;
line-height: 1.6;
color: #333;
margin-bottom: 30px;
}
.tabs {
display: flex;
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
font-weight: bold;
cursor: pointer;
border-bottom: 2px solid transparent;
&.active {
border-bottom: 2px solid #333;
}
}
.reports-table {
width: 100%;
border-collapse: collapse;
}
.table-header {
display: flex;
padding: 10px 0;
border-bottom: 1px solid #ccc;
font-weight: bold;
}
.table-row {
display: flex;
padding: 15px 0;
border-bottom: 1px solid #eee;
align-items: center;
justify-content: space-between;
}
.reports-list {
// max-height: 600px;
// overflow-y: auto;
}
.column {
&.format {
width: 10%;
}
&.item {
width: 15%;
}
&.file-name {
width: 50%;
}
&.download {
width: 25%;
text-align: right;
margin-right: 50px;
}
}
.pdf-icon {
width: 36px;
height: 36px;
}
.download-link {
color: #0078d7;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
.sec-text {
font-size: 16px;
line-height: 1.6;
}
.link {
color: #f00;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
</style>

View File

@ -0,0 +1,261 @@
<template>
<div class="page-container">
<div class="financials-container">
<!-- 标题 -->
<div class="financials-title">
{{ t("financialinformation.secfilings.title") }}
</div>
<!-- 公司财务概览 -->
<section class="section">
<div class="section-title">
{{ t("financialinformation.secfilings.overview.title") }}
</div>
<p
class="overview-text"
v-html="t('financialinformation.secfilings.overview.desc')"
></p>
</section>
<!-- 年度报告 -->
<section class="section">
<div class="section-title">
{{ t("financialinformation.secfilings.annual_reports.title") }}
</div>
<!-- 报告表格 -->
<div class="reports-table">
<div class="table-header">
<div class="column file-name">
{{
t("financialinformation.secfilings.annual_reports.file_name")
}}
</div>
<div class="column download"></div>
</div>
<!-- 报告列表 -->
<div class="reports-list">
<div
class="table-row"
v-for="(report, index) in annualReports"
:key="index"
>
<div class="column file-name">{{ report.fileName }}</div>
<div class="column download">
<a :href="report.downloadUrl" class="download-link">{{
t("financialinformation.secfilings.annual_reports.view")
}}</a>
</div>
</div>
</div>
</div>
</section>
<!-- SEC文件 -->
<section class="section">
<div class="section-title">
{{ t("financialinformation.secfilings.sec.title") }}
</div>
<p class="sec-text">
{{ t("financialinformation.secfilings.sec.desc") }}
<a
href="https://www.sec.gov/cgi-bin/browse-edgar?company=&CIK=NCTY&filenum=&State=&SIC=&owner=include&action=getcompany"
class="link"
>{{ t("financialinformation.secfilings.sec.click_here") }}</a
>.
</p>
</section>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
//
const annualReports = ref([
{
fileName: "fieeinc_10q",
downloadUrl:
"https://www.sec.gov/ix?doc=/Archives/edgar/data/1467761/000182912625003706/fieeinc_10q.htm",
},
{
fileName: "fieeinc_ex31-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex31-1.htm",
},
{
fileName: "fieeinc_ex31-2",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex31-2.htm",
},
{
fileName: "fieeinc_ex32-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex32-1.htm",
},
{
fileName: "fieeinc_ex32-2",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003706/fieeinc_ex32-2.htm",
},
{
fileName: " ownership",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003622/xslF345X05/ownership.xml",
},
{
fileName: "primary_doc",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003620/xslSCHEDULE_13D_X01/primary_doc.xml",
},
{
fileName: "fieeinc_ex1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003620/fieeinc_ex1.htm",
},
{
fileName: "fiee_8ka",
downloadUrl:
"https://www.sec.gov/ix?doc=/Archives/edgar/data/1467761/000182912625003580/fiee_8ka.htm",
},
{
fileName: "fiee_ex4-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003580/fiee_ex4-1.htm",
},
{
fileName: "fiee_ex10-1",
downloadUrl:
"https://www.sec.gov/Archives/edgar/data/1467761/000182912625003580/fiee_ex10-1.htm",
},
]);
</script>
<style scoped lang="scss">
.page-container {
background-image: url("@/assets/image/bg-375.png");
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
.financials-container {
margin: 0 auto;
padding: 80px;
}
.financials-title {
font-size: 113px;
font-weight: 600;
text-align: center;
margin-bottom: 60px;
color: #333;
}
.section {
margin-bottom: 60px;
}
.section-title {
font-size: 92px;
font-weight: 600;
margin-bottom: 20px;
color: #333;
}
.overview-text {
font-size: 72px;
line-height: 1.6;
color: #333;
margin-bottom: 30px;
}
.tabs {
display: flex;
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
font-weight: bold;
cursor: pointer;
border-bottom: 2px solid transparent;
&.active {
border-bottom: 2px solid #333;
}
}
.reports-table {
width: 100%;
border-collapse: collapse;
}
.table-header {
display: flex;
padding: 10px 0;
border-bottom: 1px solid #ccc;
font-weight: bold;
}
.table-row {
display: flex;
padding: 45px 0;
border-bottom: 1px solid #eee;
align-items: center;
justify-content: space-between;
}
.reports-list {
// max-height: 600px;
// overflow-y: auto;
}
.column {
&.format {
width: 10%;
}
&.item {
width: 15%;
}
&.file-name {
width: 50%;
}
&.download {
margin-right: 100px;
}
}
.pdf-icon {
width: 36px;
height: 36px;
}
.download-link {
color: #0078d7;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
.sec-text {
font-size: 72px;
line-height: 1.6;
}
.link {
color: #f00;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
</style>

View File

@ -1,30 +1,18 @@
<script setup> <script setup>
import { computed } from "vue"; import size1920 from '@/views/home/size1920/index.vue'
import { useWindowSize } from "@vueuse/core"; import size375 from '@/views/home/size375/index.vue'
import { computed } from 'vue'
import size375 from "@/views/home/size375/index.vue"; import { useWindowSize } from '@vueuse/core'
import size768 from "@/views/home/size768/index.vue";
import size1440 from "@/views/home/size1440/index.vue";
import size1920 from "@/views/home/size1920/index.vue";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
const router = useRouter();
const { width } = useWindowSize();
const { t } = useI18n();
const { width } = useWindowSize()
const viewComponent = computed(() => { const viewComponent = computed(() => {
const viewWidth = width.value; const viewWidth = width.value
if (viewWidth <= 450) { if (viewWidth <= 450) {
return size375; return size375
} else if (viewWidth <= 1100) {
return size768;
} else if (viewWidth <= 1500) {
return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) { } else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920; return size1920
} }
}); })
</script> </script>
<template> <template>

View File

@ -1,22 +1,20 @@
<script setup> <script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from 'naive-ui'
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from 'vue'
import customHeader from '@/components/customHeader/index.vue'
import customFooter from '@/components/customFooter/index.vue'
</script> </script>
<template> <template>
<header className="header"> <header className="header">
1920 1920
</header> </header>
<main ref="main"> <main ref="main"></main>
</main>
<footer>
</footer>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
main {
padding: var(--header-height, 80px) 0 0;
}
</style> </style>

View File

@ -1,22 +1,20 @@
<script setup> <script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from 'naive-ui'
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from 'vue'
import customHeader from '@/components/customHeader/index.vue'
import customFooter from '@/components/customFooter/index.vue'
</script> </script>
<template> <template>
<header className="header"> <header className="header">
375 375
</header> </header>
<main ref="main"> <main ref="main"></main>
</main>
<footer>
</footer>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
main {
padding: var(--header-height, 80px) 0 0;
}
</style> </style>

22
src/views/index/index.vue Normal file
View File

@ -0,0 +1,22 @@
<script setup>
import size1920 from '@/views/index/size1920/index.vue'
import size375 from '@/views/index/size375/index.vue'
import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
const { width } = useWindowSize()
const viewComponent = computed(() => {
const viewWidth = width.value
if (viewWidth <= 450) {
return size375
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920
}
})
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,21 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from 'naive-ui'
import { onUnmounted, ref, watch, onMounted, computed } from 'vue'
import customHeader from '@/components/customHeader/index.vue'
import customFooter from '@/components/customFooter/index.vue'
</script>
<template>
<customHeader></customHeader>
<div style="margin: 80px 0;">
<router-view />
</div>
<customFooter></customFooter>
</template>
<style scoped lang="scss">
main {
padding: var(--header-height, 80px) 0 0;
}
</style>

View File

@ -0,0 +1,26 @@
<script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from 'naive-ui'
import { onUnmounted, ref, watch, onMounted, computed } from 'vue'
import customHeader from '@/components/customHeader/index.vue'
import customFooter from '@/components/customFooter/index.vue'
</script>
<template>
<!-- <header className="header">
375
</header> -->
<!-- <main ref="main"></main> -->
<customHeader />
<div style="margin: 80px 0;">
<router-view />
</div>
<customFooter />
</template>
<style scoped lang="scss">
main {
padding: var(--header-height, 80px) 0 0;
}
</style>

View File

@ -0,0 +1,23 @@
<template>
<div class="new-releases-page">
<component :is="viewComponent" />
</div>
</template>
<script setup>
import size1920 from '@/views/new-releases/size1920/index.vue'
import size375 from '@/views/new-releases/size375/index.vue'
import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
const { width } = useWindowSize()
const viewComponent = computed(() => {
const viewWidth = width.value
if (viewWidth <= 450) {
return size375
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920
}
})
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,10 @@
<template>
<div class="new-releases-page">
<customDefaultPage></customDefaultPage>
</div>
</template>
<script setup>
import customDefaultPage from '@/components/customDefaultPage/index.vue'
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,10 @@
<template>
<div class="new-releases-page">
<customDefaultPage></customDefaultPage>
</div>
</template>
<script setup>
import customDefaultPage from '@/components/customDefaultPage/index.vue'
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,23 @@
<template>
<div class="new-releases-page">
<component :is="viewComponent" />
</div>
</template>
<script setup>
import size1920 from '@/views/press-releases/size1920/index.vue'
import size375 from '@/views/press-releases/size375/index.vue'
import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
const { width } = useWindowSize()
const viewComponent = computed(() => {
const viewWidth = width.value
if (viewWidth <= 450) {
return size375
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920
}
})
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,10 @@
<template>
<div class="new-releases-page">
<customDefaultPage></customDefaultPage>
</div>
</template>
<script setup>
import customDefaultPage from '@/components/customDefaultPage/index.vue'
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,10 @@
<template>
<div class="new-releases-page">
<customDefaultPage></customDefaultPage>
</div>
</template>
<script setup>
import customDefaultPage from '@/components/customDefaultPage/index.vue'
</script>
<style scoped lang="scss"></style>