Compare commits
182 Commits
Author | SHA1 | Date | |
---|---|---|---|
cab2491b2b | |||
|
e5e9030519 | ||
f1b25de541 | |||
5d9ca4c120 | |||
41254e2c5a | |||
261956b174 | |||
55419e7bb3 | |||
7dd385e115 | |||
|
bb1c4483f6 | ||
|
7561f0d22e | ||
|
c0fa196ac6 | ||
|
86799ccf1d | ||
|
1a2f9417fb | ||
|
d5ea7b295b | ||
c445366624 | |||
b7767f0bfa | |||
07bbe8fbdf | |||
9fb3db0ced | |||
|
0b000f2eb0 | ||
|
1bdea06916 | ||
|
1aad765dc1 | ||
b298d07854 | |||
710bca3486 | |||
|
3029de7916 | ||
|
a39c4cf620 | ||
1195b8f8e4 | |||
fc4f4ab3f2 | |||
35faed56cb | |||
|
460060c087 | ||
|
a38ea6964f | ||
|
ab43f5f8b9 | ||
|
ae1a562bcb | ||
|
e0c28b5522 | ||
|
461705f337 | ||
b841a6d911 | |||
d2dd262b9e | |||
|
1949046344 | ||
|
a92f9ca971 | ||
|
697dd232f6 | ||
|
61b5a9eaea | ||
|
36d961fcb6 | ||
dbe739ed50 | |||
2dc262fbd8 | |||
|
01723e2a2f | ||
|
0b5494b55b | ||
4cd3e2ae6e | |||
|
49eb1646c2 | ||
efd50bc82e | |||
396015f628 | |||
|
029b3a978a | ||
651443a95c | |||
096fea2b65 | |||
a13a49f666 | |||
|
9d10a578e5 | ||
|
5c90cd9486 | ||
|
9d6d85490c | ||
|
f5c213eac8 | ||
c987f08490 | |||
e7af9a09de | |||
|
07ff81ef66 | ||
|
1fbda73a3f | ||
|
7e0ef75a5e | ||
|
ed85bc2771 | ||
afe010ae1d | |||
afec6b8ef1 | |||
|
1abd94cdce | ||
1a031d0be2 | |||
|
1179c99782 | ||
|
3d96acdc4e | ||
664b66b414 | |||
|
51a52f9696 | ||
|
2471bcd0a7 | ||
|
917f76dac3 | ||
|
4930853816 | ||
|
ad5d6f7b1a | ||
|
fa19e8bc87 | ||
|
dbc8ecb84e | ||
|
afb01fab8e | ||
|
363d1e1f9e | ||
|
98cd9fbbe0 | ||
|
af39cb7bb3 | ||
|
624824e956 | ||
|
047a1ebc88 | ||
|
4933765a4c | ||
|
9bd9c0b95a | ||
|
5614ddf4ef | ||
|
59202a8471 | ||
|
ad11498a31 | ||
|
f1a90c8b6a | ||
|
283f73d2c3 | ||
|
37d9be4c38 | ||
|
0971e78597 | ||
|
7eb791d38e | ||
|
a0ada9e66a | ||
|
d0f3fbc1dd | ||
|
ae0d82ae16 | ||
|
7b78b04f38 | ||
|
be833783f2 | ||
|
de9fb51ec3 | ||
|
16241a203d | ||
|
78d8364816 | ||
8fff836596 | |||
3022ec9756 | |||
|
abb1c220cf | ||
|
81de59b9eb | ||
126bf56327 | |||
208a426324 | |||
75824076ef | |||
f55a24ea63 | |||
2f94959e19 | |||
|
d71e435ee1 | ||
|
42b2313857 | ||
|
0a5088449a | ||
|
ba62f71796 | ||
969a16577e | |||
bad5b37b05 | |||
|
8a9002a9e3 | ||
60590596b7 | |||
5d0f85ba9b | |||
7c6a61b99f | |||
|
de8617321d | ||
|
786740cdfa | ||
caf62145a0 | |||
f33003eb04 | |||
|
c4b11ec76a | ||
39deb7bd70 | |||
946652acae | |||
|
99ec5382bf | ||
|
d4df67bd76 | ||
75c377eafe | |||
614ae9fce4 | |||
af91764f94 | |||
|
b06317e581 | ||
|
44d3555e06 | ||
97fc7d966b | |||
7c741962d4 | |||
9cba09c2fb | |||
91485a72b4 | |||
|
22e9b74173 | ||
b184eba64d | |||
28f6123e93 | |||
|
c4a8271816 | ||
|
f72dd8ad9d | ||
|
82711a9645 | ||
|
b4a3a5b72e | ||
|
ce58c9886e | ||
1164d4ef7b | |||
24c27d5efb | |||
|
af8348107b | ||
|
fb45f534da | ||
334bef4b05 | |||
|
309d5fcb25 | ||
f2246e1b69 | |||
|
bb11173ce1 | ||
|
e052d59c2b | ||
|
c44a1bb1c1 | ||
d5c887f437 | |||
64b8d83c9e | |||
9881122e5f | |||
46687211dd | |||
|
ee07e825e2 | ||
|
aad1267c61 | ||
108970fd41 | |||
62a565d986 | |||
|
bdb29764be | ||
fa00cdd6bc | |||
8252812dfb | |||
bb57f44b60 | |||
9bbd983253 | |||
|
abc39b13af | ||
|
8958bf8847 | ||
|
4b3d0a57d9 | ||
e9e7ad52de | |||
8eedc65345 | |||
|
d450e2171c | ||
|
c4f61ddc3c | ||
f3ce08a945 | |||
15b15e0351 | |||
4ccb9c256b | |||
|
a5b7334c6b | ||
|
7c75bab274 | ||
|
6f95ccb4ab |
3
env/.env.prod
vendored
@ -5,3 +5,6 @@ VITE_DELETE_CONSOLE = true
|
|||||||
# 是否开启sourcemap
|
# 是否开启sourcemap
|
||||||
VITE_SHOW_SOURCEMAP = false
|
VITE_SHOW_SOURCEMAP = false
|
||||||
VITE_BASEURL = '//appointteam.szjixun.cn'
|
VITE_BASEURL = '//appointteam.szjixun.cn'
|
||||||
|
|
||||||
|
# 文档查看
|
||||||
|
VITE_PAGE_URL="https://www.fiee.com"
|
||||||
|
4
env/.env.test
vendored
@ -3,3 +3,7 @@ NODE_ENV = 'test'
|
|||||||
# 是否去除console 和 debugger
|
# 是否去除console 和 debugger
|
||||||
VITE_DELETE_CONSOLE = false
|
VITE_DELETE_CONSOLE = false
|
||||||
VITE_BASEURL = '//kid-art-test.szjixun.cn'
|
VITE_BASEURL = '//kid-art-test.szjixun.cn'
|
||||||
|
|
||||||
|
# 文档查看
|
||||||
|
VITE_PAGE_URL="http://172.16.100.22:8045"
|
||||||
|
# VITE_PAGE_URL="http://192.168.88.50:5878"
|
||||||
|
1
googledeec2461668656b4.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
google-site-verification: googledeec2461668656b4.html
|
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/src/assets/image/icon.png" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<title>FiEE</title>
|
<title>FiEE</title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -12,12 +12,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fingerprintjs/fingerprintjs": "^4.4.3",
|
"@fingerprintjs/fingerprintjs": "^4.4.3",
|
||||||
|
"@onlyoffice/document-editor-vue": "^1.5.0",
|
||||||
"@unocss/reset": "^0.61.9",
|
"@unocss/reset": "^0.61.9",
|
||||||
"@vicons/ionicons5": "^0.13.0",
|
"@vicons/ionicons5": "^0.13.0",
|
||||||
"@vicons/utils": "^0.1.4",
|
"@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",
|
||||||
"countup.js": "^2.8.2",
|
"countup.js": "^2.8.2",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"gsap": "^3.12.5",
|
"gsap": "^3.12.5",
|
||||||
"jsdom": "^24.0.0",
|
"jsdom": "^24.0.0",
|
||||||
|
@ -11,6 +11,9 @@ importers:
|
|||||||
'@fingerprintjs/fingerprintjs':
|
'@fingerprintjs/fingerprintjs':
|
||||||
specifier: ^4.4.3
|
specifier: ^4.4.3
|
||||||
version: 4.4.3
|
version: 4.4.3
|
||||||
|
'@onlyoffice/document-editor-vue':
|
||||||
|
specifier: ^1.5.0
|
||||||
|
version: 1.5.0(vue@3.4.35)
|
||||||
'@unocss/reset':
|
'@unocss/reset':
|
||||||
specifier: ^0.61.9
|
specifier: ^0.61.9
|
||||||
version: 0.61.9
|
version: 0.61.9
|
||||||
@ -29,6 +32,9 @@ importers:
|
|||||||
countup.js:
|
countup.js:
|
||||||
specifier: ^2.8.2
|
specifier: ^2.8.2
|
||||||
version: 2.8.2
|
version: 2.8.2
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.13
|
||||||
|
version: 1.11.13
|
||||||
echarts:
|
echarts:
|
||||||
specifier: ^5.6.0
|
specifier: ^5.6.0
|
||||||
version: 5.6.0
|
version: 5.6.0
|
||||||
@ -1276,6 +1282,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
'@onlyoffice/document-editor-vue@1.5.0':
|
||||||
|
resolution: {integrity: sha512-HZEebUhBloP4LomspI5BddgoQdhtPq91h57yA9K/Lk70MMc1vgOTQ4Wq+N5TZYXNxdDTv+TSsEVFLnBCl1Y71A==}
|
||||||
|
peerDependencies:
|
||||||
|
vue: ^3.0.0
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.25':
|
'@polka/url@1.0.0-next.25':
|
||||||
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
|
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
|
||||||
|
|
||||||
@ -2068,6 +2079,9 @@ packages:
|
|||||||
date-fns@3.6.0:
|
date-fns@3.6.0:
|
||||||
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
|
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
|
||||||
|
|
||||||
|
dayjs@1.11.13:
|
||||||
|
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
||||||
|
|
||||||
debug@4.3.6:
|
debug@4.3.6:
|
||||||
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
|
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@ -5530,6 +5544,11 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.17.1
|
fastq: 1.17.1
|
||||||
|
|
||||||
|
'@onlyoffice/document-editor-vue@1.5.0(vue@3.4.35)':
|
||||||
|
dependencies:
|
||||||
|
lodash: 4.17.21
|
||||||
|
vue: 3.4.35
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.25': {}
|
'@polka/url@1.0.0-next.25': {}
|
||||||
|
|
||||||
'@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(rollup@4.20.0)':
|
'@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(rollup@4.20.0)':
|
||||||
@ -6478,6 +6497,8 @@ snapshots:
|
|||||||
|
|
||||||
date-fns@3.6.0: {}
|
date-fns@3.6.0: {}
|
||||||
|
|
||||||
|
dayjs@1.11.13: {}
|
||||||
|
|
||||||
debug@4.3.6:
|
debug@4.3.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
public/icon.png
Before Width: | Height: | Size: 105 KiB |
BIN
src/assets/file/FiEE, Inc._Audit Committee Charter.pdf
Normal file
BIN
src/assets/file/FiEE, Inc._Compensation Committee Charter.pdf
Normal file
BIN
src/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf
Normal file
BIN
src/assets/file/footer/FiEE, Inc. _ Site Map.pdf
Normal file
BIN
src/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2009-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2010-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2010-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2010-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2011-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2011-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2011-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2012-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2012-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2012-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2013-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2013-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2013-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2014-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2014-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2014-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2015-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2015-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2015-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2016-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2016-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2016-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2017-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2017-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2017-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2018-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2018-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2018-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2019-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2019-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2019-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2020-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2020-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2020-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2021-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2021-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2021-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2022-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2022-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2022-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2023-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2023-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2023-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2024-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2024-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2024-Q3.pdf
Normal file
BIN
src/assets/image/icon/echarts_markPointer.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/image/icon/icon-excel.png
Normal file
After Width: | Height: | Size: 176 KiB |
BIN
src/assets/image/icon/icon-link.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/icon/icon-new.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
src/assets/image/icon/icon-pdf.png
Normal file
After Width: | Height: | Size: 184 KiB |
BIN
src/assets/image/icon/icon-word.png
Normal file
After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 72 KiB |
34
src/components/customEcharts/index.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
|
||||||
|
import size375 from '@/components/customEcharts/size375/index.vue'
|
||||||
|
import size768 from '@/components/customEcharts/size375/index.vue'
|
||||||
|
import size1440 from '@/components/customEcharts/size1920/index.vue'
|
||||||
|
import size1920 from '@/components/customEcharts/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 <= 500) {
|
||||||
|
return size375
|
||||||
|
} else if (viewWidth <= 960) {
|
||||||
|
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>
|
602
src/components/customEcharts/size1920/index.vue
Normal file
@ -0,0 +1,602 @@
|
|||||||
|
<template>
|
||||||
|
<div class="custom-echarts">
|
||||||
|
<div>
|
||||||
|
<div class="echarts-header">
|
||||||
|
<div class="echarts-header-title">
|
||||||
|
<span>FiEE, Inc. Stock Price History</span>
|
||||||
|
</div>
|
||||||
|
<div class="echarts-search-area">
|
||||||
|
<div class="echarts-search-byRange">
|
||||||
|
<text style="font-size: 0.9rem; font-weight: 400; color: #666666;">
|
||||||
|
Range
|
||||||
|
</text>
|
||||||
|
<div class="search-range-list">
|
||||||
|
<div
|
||||||
|
class="search-range-list-each"
|
||||||
|
v-for="(item, index) in state.searchRange"
|
||||||
|
:key="index"
|
||||||
|
@click="changeSearchRange(item)"
|
||||||
|
>
|
||||||
|
<span>{{ item }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="echarts-search-byDate">
|
||||||
|
<n-date-picker
|
||||||
|
v-model:value="state.selectHistoricStartDate"
|
||||||
|
type="date"
|
||||||
|
:is-date-disabled="disableAfterDate"
|
||||||
|
@update:value="changeSearchRangeStartDate"
|
||||||
|
input-readonly
|
||||||
|
/>
|
||||||
|
<!-- <n-icon size="16">
|
||||||
|
<ArrowForwardOutline />
|
||||||
|
</n-icon> -->
|
||||||
|
<span>to</span>
|
||||||
|
<n-date-picker
|
||||||
|
v-model:value="state.selectHistoricEndDate"
|
||||||
|
type="date"
|
||||||
|
:is-date-disabled="disablePreviousDate"
|
||||||
|
@update:value="changeSearchRangeEndDate"
|
||||||
|
input-readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="myEcharts" class="myChart"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, watch, reactive } from 'vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import markPointerIcon from '@/assets/image/icon/echarts_markPointer.png'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { NDatePicker, NIcon } from 'naive-ui'
|
||||||
|
import { ArrowForwardOutline } from '@vicons/ionicons5'
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
searchRange: ['1m', '3m', 'YTD', '1Y', '5Y', '10Y', 'Max'],
|
||||||
|
selectHistoricStartDate: '2009-10-07',
|
||||||
|
selectHistoricEndDate: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
let myCharts = null
|
||||||
|
let historicData = []
|
||||||
|
let xAxisData = []
|
||||||
|
|
||||||
|
//初始化eCharts
|
||||||
|
const initEcharts = (data) => {
|
||||||
|
historicData = data
|
||||||
|
xAxisData = data.map((item) => {
|
||||||
|
return new Date(item.date).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const yAxisData = data.map((item) => item.price)
|
||||||
|
// console.error(xAxisData, yAxisData)
|
||||||
|
// 基于准备好的dom,初始化echarts实例
|
||||||
|
myCharts = echarts.init(document.getElementById('myEcharts'), null, {
|
||||||
|
renderer: 'canvas',
|
||||||
|
useDirtyRect: true
|
||||||
|
})
|
||||||
|
// 绘制图表
|
||||||
|
myCharts.setOption({
|
||||||
|
animation: false,
|
||||||
|
progressive: 500,
|
||||||
|
progressiveThreshold: 3000,
|
||||||
|
// title: {
|
||||||
|
// text: 'FiEE, Inc. Stock Price History',
|
||||||
|
// },
|
||||||
|
grid: {
|
||||||
|
left: '8%', // 或 '2%',根据实际情况调整
|
||||||
|
right: '12%', // 给右侧y轴留空间,数值可根据y轴label宽度调整
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line',
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formatter: function (params) {
|
||||||
|
const p = params[0]
|
||||||
|
return `<span style="font-size: 1.1rem; font-weight: 600;">${p.axisValue}</span><br/><span style="font-size: 0.9rem; font-weight: 400;">Price: ${p.data}</span>`
|
||||||
|
},
|
||||||
|
triggerOn: 'mousemove',
|
||||||
|
confine: true,
|
||||||
|
hideDelay: 1500
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: xAxisData,
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
inverse: true,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#CCD6EB',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#323232',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
interval: 'auto',
|
||||||
|
hideOverlap: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
position: 'right',
|
||||||
|
interval: 25,
|
||||||
|
// max: 75.0,
|
||||||
|
show: true,
|
||||||
|
axisLabel: {
|
||||||
|
color: '#323232',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
formatter: function (value) {
|
||||||
|
return value > 0 ? value.toFixed(2) : value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: yAxisData,
|
||||||
|
type: 'line',
|
||||||
|
sampling: 'lttb',
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2c6288',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: '#2c6288',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: '#F4F6F8',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'image://' + markPointerIcon,
|
||||||
|
symbolSize: 24,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
progressive: 500,
|
||||||
|
progressiveThreshold: 3000,
|
||||||
|
large: true,
|
||||||
|
largeThreshold: 2000
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
show: true,
|
||||||
|
dataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2C6288',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 1, color: '#2c6288' },
|
||||||
|
{ offset: 0, color: '#F4F6F8' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selectedDataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2C6288',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 1, color: '#2c6288' },
|
||||||
|
{ offset: 0, color: '#F4F6F8' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fillerColor: 'rgba(44, 98, 136, 0.3)',
|
||||||
|
realtime: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 showTip 事件,动态显示 markPoint
|
||||||
|
myCharts.on('showTip', function (params) {
|
||||||
|
if (params) {
|
||||||
|
const dataIndex = params.dataIndex
|
||||||
|
const x = myCharts.getOption().xAxis[0].data[dataIndex]
|
||||||
|
const y = myCharts.getOption().series[0].data[dataIndex]
|
||||||
|
myCharts.setOption({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'image://' + markPointerIcon,
|
||||||
|
symbolSize: 24,
|
||||||
|
data: [{ coord: [x, y] }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 鼠标移出时,清除 markPoint
|
||||||
|
myCharts.on('globalout', function () {
|
||||||
|
myCharts.setOption({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'image://' + markPointerIcon,
|
||||||
|
symbolSize: 24,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
myCharts.on('dataZoom', function (params) {
|
||||||
|
// 获取当前 dataZoom 范围
|
||||||
|
const option = myCharts.getOption()
|
||||||
|
const xAxisData = option.xAxis[0].data
|
||||||
|
const dataZoom = option.dataZoom[1] || option.dataZoom[0]
|
||||||
|
|
||||||
|
// 获取 dataZoom 的 startValue 和 endValue
|
||||||
|
let startValue = dataZoom.endValue
|
||||||
|
let endValue = dataZoom.startValue
|
||||||
|
|
||||||
|
// 如果是索引,转为日期
|
||||||
|
if (typeof startValue === 'number') {
|
||||||
|
startValue = xAxisData[startValue]
|
||||||
|
}
|
||||||
|
if (typeof endValue === 'number') {
|
||||||
|
endValue = xAxisData[endValue]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新日期选择器
|
||||||
|
state.selectHistoricStartDate = new Date(startValue)
|
||||||
|
state.selectHistoricEndDate = new Date(endValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getHistoricalData()
|
||||||
|
})
|
||||||
|
|
||||||
|
//获取历史数据
|
||||||
|
const getHistoricalData = async () => {
|
||||||
|
let now = new Date()
|
||||||
|
let toDate =
|
||||||
|
now.getFullYear() +
|
||||||
|
'-' +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
'-' +
|
||||||
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
let url =
|
||||||
|
'https://common.szjixun.cn/api/stock/history/base/list?from=2009-10-07&to=' +
|
||||||
|
toDate
|
||||||
|
const res = await axios.get(url)
|
||||||
|
if (res.status === 200) {
|
||||||
|
if (res.data.status === 0) {
|
||||||
|
initEcharts(res.data.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 适配倒序数据,返回大于等于目标日期的最近一天索引
|
||||||
|
function findClosestDateIndex(data, targetDateStr) {
|
||||||
|
let left = 0,
|
||||||
|
right = data.length - 1
|
||||||
|
const target = new Date(targetDateStr).getTime()
|
||||||
|
let res = data.length - 1 // 默认返回最后一个
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2)
|
||||||
|
const midTime = new Date(data[mid].date).getTime()
|
||||||
|
if (midTime > target) {
|
||||||
|
left = mid + 1
|
||||||
|
} else {
|
||||||
|
res = mid
|
||||||
|
right = mid - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// 适配倒序数据,返回小于等于目标日期的最近一天索引
|
||||||
|
function findClosestDateIndexDescLeft(data, targetDateStr) {
|
||||||
|
let left = 0,
|
||||||
|
right = data.length - 1
|
||||||
|
const target = new Date(targetDateStr).getTime()
|
||||||
|
let res = -1
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2)
|
||||||
|
const midTime = new Date(data[mid].date).getTime()
|
||||||
|
if (midTime > target) {
|
||||||
|
left = mid + 1 // mid 比目标新,往更旧的方向找
|
||||||
|
} else {
|
||||||
|
res = mid // mid <= target,记录下来,继续往更新的方向找
|
||||||
|
right = mid - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
//点击切换搜索区间
|
||||||
|
const changeSearchRange = (range, dateTime) => {
|
||||||
|
const now = new Date()
|
||||||
|
let startDate = ''
|
||||||
|
let endDate = ''
|
||||||
|
if (range === '1m') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setMonth(now.getMonth() - 1)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '3m') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setMonth(now.getMonth() - 3)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === 'YTD') {
|
||||||
|
startDate = new Date(now.getFullYear(), 0, 1).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '1Y') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setFullYear(now.getFullYear() - 1)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '5Y') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setFullYear(now.getFullYear() - 5)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '10Y') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setFullYear(now.getFullYear() - 10)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === 'Max') {
|
||||||
|
startDate = ''
|
||||||
|
endDate = ''
|
||||||
|
} else if (range === 'startDateTime') {
|
||||||
|
startDate = dateTime
|
||||||
|
endDate = ''
|
||||||
|
} else if (range === 'endDateTime') {
|
||||||
|
startDate = ''
|
||||||
|
endDate = dateTime
|
||||||
|
}
|
||||||
|
if (startDate || endDate) {
|
||||||
|
// historicData 和 xAxisData 需在 initEcharts 作用域可用
|
||||||
|
if (
|
||||||
|
typeof historicData !== 'undefined' &&
|
||||||
|
typeof xAxisData !== 'undefined'
|
||||||
|
) {
|
||||||
|
let startValue = xAxisData[0]
|
||||||
|
if (startDate) {
|
||||||
|
const idx = findClosestDateIndex(historicData, startDate)
|
||||||
|
// 用 historicData[idx].date 格式化为 xAxisData 的格式
|
||||||
|
startValue = new Date(historicData[idx].date).toLocaleDateString(
|
||||||
|
'en-US',
|
||||||
|
{
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let endValue = endDate
|
||||||
|
if (endDate) {
|
||||||
|
// console.warn(endDate)
|
||||||
|
const idx = findClosestDateIndexDescLeft(historicData, endDate)
|
||||||
|
// console.warn(idx)
|
||||||
|
// 用 historicData[idx].date 格式化为 xAxisData 的格式
|
||||||
|
endValue = new Date(historicData[idx].date).toLocaleDateString(
|
||||||
|
'en-US',
|
||||||
|
{
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// console.warn(endValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startDate) {
|
||||||
|
myCharts.setOption({
|
||||||
|
dataZoom: {
|
||||||
|
endValue: startValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
state.selectHistoricStartDate = new Date(startValue)
|
||||||
|
}
|
||||||
|
if (endDate) {
|
||||||
|
myCharts.setOption({
|
||||||
|
dataZoom: {
|
||||||
|
startValue: endValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
state.selectHistoricEndDate = new Date(endValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
myCharts.setOption({
|
||||||
|
dataZoom: {
|
||||||
|
startValue: '',
|
||||||
|
endValue: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
state.selectHistoricStartDate = new Date('2009-10-07')
|
||||||
|
state.selectHistoricEndDate = new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用2009-10-07之后的日期
|
||||||
|
const disableAfterDate = (date) => {
|
||||||
|
return date < new Date('2009-10-06') || date > new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用过去的日期
|
||||||
|
const disablePreviousDate = (date) => {
|
||||||
|
return date < new Date(state.selectHistoricStartDate) || date > new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换搜索区间开始日期
|
||||||
|
const changeSearchRangeStartDate = (date) => {
|
||||||
|
// console.error(date)
|
||||||
|
changeSearchRange(
|
||||||
|
'startDateTime',
|
||||||
|
new Date(date).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换搜索区间结束日期
|
||||||
|
const changeSearchRangeEndDate = (date) => {
|
||||||
|
// console.error(date)
|
||||||
|
changeSearchRange(
|
||||||
|
'endDateTime',
|
||||||
|
new Date(date).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.custom-echarts {
|
||||||
|
.myChart {
|
||||||
|
width: 100%;
|
||||||
|
height: 25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-header {
|
||||||
|
.echarts-header-title {
|
||||||
|
span {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #323232;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.echarts-search-area {
|
||||||
|
padding: 2rem 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.echarts-search-byRange {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
.search-range-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
.search-range-list-each {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
cursor: pointer;
|
||||||
|
span {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-search-byDate {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
604
src/components/customEcharts/size375/index.vue
Normal file
@ -0,0 +1,604 @@
|
|||||||
|
<template>
|
||||||
|
<div class="custom-echarts">
|
||||||
|
<div>
|
||||||
|
<div class="echarts-header">
|
||||||
|
<div class="echarts-header-title">
|
||||||
|
<span>FiEE, Inc. Stock Price History</span>
|
||||||
|
</div>
|
||||||
|
<div class="echarts-search-area">
|
||||||
|
<div class="echarts-search-byRange">
|
||||||
|
<text style="font-size: 0.9rem; font-weight: 400; color: #666666;">
|
||||||
|
Range
|
||||||
|
</text>
|
||||||
|
<div class="search-range-list">
|
||||||
|
<div
|
||||||
|
class="search-range-list-each"
|
||||||
|
v-for="(item, index) in state.searchRange"
|
||||||
|
:key="index"
|
||||||
|
@click="changeSearchRange(item)"
|
||||||
|
>
|
||||||
|
<span>{{ item }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="echarts-search-byDate">
|
||||||
|
<n-date-picker
|
||||||
|
v-model:value="state.selectHistoricStartDate"
|
||||||
|
type="date"
|
||||||
|
:is-date-disabled="disableAfterDate"
|
||||||
|
@update:value="changeSearchRangeStartDate"
|
||||||
|
input-readonly
|
||||||
|
/>
|
||||||
|
<!-- <n-icon size="30">
|
||||||
|
<ArrowForwardOutline />
|
||||||
|
</n-icon> -->
|
||||||
|
<span>to</span>
|
||||||
|
<n-date-picker
|
||||||
|
v-model:value="state.selectHistoricEndDate"
|
||||||
|
type="date"
|
||||||
|
:is-date-disabled="disablePreviousDate"
|
||||||
|
@update:value="changeSearchRangeEndDate"
|
||||||
|
input-readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="myEcharts" class="myChart"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, watch, reactive } from 'vue'
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
import markPointerIcon from '@/assets/image/icon/echarts_markPointer.png'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { NDatePicker } from 'naive-ui'
|
||||||
|
import { ArrowForwardOutline } from '@vicons/ionicons5'
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
searchRange: ['1m', '3m', 'YTD', '1Y', '5Y', '10Y', 'Max'],
|
||||||
|
selectHistoricStartDate: '2009-10-07',
|
||||||
|
selectHistoricEndDate: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
let myCharts = null
|
||||||
|
let historicData = []
|
||||||
|
let xAxisData = []
|
||||||
|
|
||||||
|
//初始化eCharts
|
||||||
|
const initEcharts = (data) => {
|
||||||
|
historicData = data
|
||||||
|
xAxisData = data.map((item) => {
|
||||||
|
return new Date(item.date).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const yAxisData = data.map((item) => item.price)
|
||||||
|
// console.error(xAxisData, yAxisData)
|
||||||
|
// 基于准备好的dom,初始化echarts实例
|
||||||
|
myCharts = echarts.init(document.getElementById('myEcharts'))
|
||||||
|
// 绘制图表
|
||||||
|
myCharts.setOption({
|
||||||
|
animation: false,
|
||||||
|
progressive: 500,
|
||||||
|
progressiveThreshold: 3000,
|
||||||
|
// title: {
|
||||||
|
// text: 'FiEE, Inc. Stock Price History',
|
||||||
|
// },
|
||||||
|
grid: {
|
||||||
|
left: '8%', // 或 '2%',根据实际情况调整
|
||||||
|
right: '15%', // 给右侧y轴留空间,数值可根据y轴label宽度调整
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line',
|
||||||
|
snap: true,
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#6a7985',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formatter: function (params) {
|
||||||
|
const p = params[0]
|
||||||
|
return `<span style="font-size: 1.1rem; font-weight: 600;">${p.axisValue}</span><br/><span style="font-size: 0.9rem; font-weight: 400;">Price: ${p.data}</span>`
|
||||||
|
},
|
||||||
|
confine: true,
|
||||||
|
hideDelay: 1500
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
data: xAxisData,
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
inverse: true,
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#CCD6EB',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#323232',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
// formatter: function (value) {
|
||||||
|
// return value ? value.split('-')[0] : ''
|
||||||
|
// },
|
||||||
|
// interval: function (index, value) {
|
||||||
|
// if (index === 0) return true;
|
||||||
|
// const axisData = this && this.axis && this.axis.data ? this.axis.data : [];
|
||||||
|
// if (!axisData[index - 1]) return true;
|
||||||
|
// return value.split('-')[0] !== axisData[index - 1].split('-')[0];
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
position: 'right',
|
||||||
|
interval: 25,
|
||||||
|
// max: 75.0,
|
||||||
|
show: true,
|
||||||
|
axisLabel: {
|
||||||
|
color: '#323232',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
formatter: function (value) {
|
||||||
|
return value > 0 ? value.toFixed(2) : value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: yAxisData,
|
||||||
|
type: 'line',
|
||||||
|
sampling: 'lttb',
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2c6288',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: '#2c6288',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: '#F4F6F8',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'image://' + markPointerIcon,
|
||||||
|
symbolSize: 24,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'slider',
|
||||||
|
show: true,
|
||||||
|
dataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2C6288',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 1, color: '#2c6288' },
|
||||||
|
{ offset: 0, color: '#F4F6F8' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selectedDataBackground: {
|
||||||
|
lineStyle: {
|
||||||
|
color: '#2C6288',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: 'linear',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 1, color: '#2c6288' },
|
||||||
|
{ offset: 0, color: '#F4F6F8' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fillerColor: 'rgba(44, 98, 136, 0.3)',
|
||||||
|
realtime: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 showTip 事件,动态显示 markPoint
|
||||||
|
myCharts.on('showTip', function (params) {
|
||||||
|
if (params) {
|
||||||
|
const dataIndex = params.dataIndex
|
||||||
|
const x = myCharts.getOption().xAxis[0].data[dataIndex]
|
||||||
|
const y = myCharts.getOption().series[0].data[dataIndex]
|
||||||
|
myCharts.setOption({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'image://' + markPointerIcon,
|
||||||
|
symbolSize: 24,
|
||||||
|
data: [{ coord: [x, y] }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 鼠标移出时,清除 markPoint
|
||||||
|
myCharts.on('globalout', function () {
|
||||||
|
myCharts.setOption({
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'image://' + markPointerIcon,
|
||||||
|
symbolSize: 24,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
myCharts.on('dataZoom', function (params) {
|
||||||
|
// 获取当前 dataZoom 范围
|
||||||
|
const option = myCharts.getOption()
|
||||||
|
const xAxisData = option.xAxis[0].data
|
||||||
|
const dataZoom = option.dataZoom[1] || option.dataZoom[0]
|
||||||
|
|
||||||
|
// 获取 dataZoom 的 startValue 和 endValue
|
||||||
|
let startValue = dataZoom.endValue
|
||||||
|
let endValue = dataZoom.startValue
|
||||||
|
|
||||||
|
// 如果是索引,转为日期
|
||||||
|
if (typeof startValue === 'number') {
|
||||||
|
startValue = xAxisData[startValue]
|
||||||
|
}
|
||||||
|
if (typeof endValue === 'number') {
|
||||||
|
endValue = xAxisData[endValue]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新日期选择器
|
||||||
|
state.selectHistoricStartDate = new Date(startValue)
|
||||||
|
state.selectHistoricEndDate = new Date(endValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getHistoricalData()
|
||||||
|
})
|
||||||
|
|
||||||
|
//获取历史数据
|
||||||
|
const getHistoricalData = async () => {
|
||||||
|
let now = new Date()
|
||||||
|
let toDate =
|
||||||
|
now.getFullYear() +
|
||||||
|
'-' +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
'-' +
|
||||||
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
let url =
|
||||||
|
'https://common.szjixun.cn/api/stock/history/base/list?from=2009-10-07&to=' +
|
||||||
|
toDate
|
||||||
|
const res = await axios.get(url)
|
||||||
|
if (res.status === 200) {
|
||||||
|
if (res.data.status === 0) {
|
||||||
|
initEcharts(res.data.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 适配倒序数据,返回大于等于目标日期的最近一天索引
|
||||||
|
function findClosestDateIndex(data, targetDateStr) {
|
||||||
|
let left = 0,
|
||||||
|
right = data.length - 1
|
||||||
|
const target = new Date(targetDateStr).getTime()
|
||||||
|
let res = data.length - 1 // 默认返回最后一个
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2)
|
||||||
|
const midTime = new Date(data[mid].date).getTime()
|
||||||
|
if (midTime > target) {
|
||||||
|
left = mid + 1
|
||||||
|
} else {
|
||||||
|
res = mid
|
||||||
|
right = mid - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// 适配倒序数据,返回小于等于目标日期的最近一天索引
|
||||||
|
function findClosestDateIndexDescLeft(data, targetDateStr) {
|
||||||
|
let left = 0,
|
||||||
|
right = data.length - 1
|
||||||
|
const target = new Date(targetDateStr).getTime()
|
||||||
|
let res = -1
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2)
|
||||||
|
const midTime = new Date(data[mid].date).getTime()
|
||||||
|
if (midTime > target) {
|
||||||
|
left = mid + 1 // mid 比目标新,往更旧的方向找
|
||||||
|
} else {
|
||||||
|
res = mid // mid <= target,记录下来,继续往更新的方向找
|
||||||
|
right = mid - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
//点击切换搜索区间
|
||||||
|
const changeSearchRange = (range, dateTime) => {
|
||||||
|
const now = new Date()
|
||||||
|
let startDate = ''
|
||||||
|
let endDate = ''
|
||||||
|
if (range === '1m') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setMonth(now.getMonth() - 1)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '3m') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setMonth(now.getMonth() - 3)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === 'YTD') {
|
||||||
|
startDate = new Date(now.getFullYear(), 0, 1).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '1Y') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setFullYear(now.getFullYear() - 1)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '5Y') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setFullYear(now.getFullYear() - 5)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === '10Y') {
|
||||||
|
const last = new Date(now)
|
||||||
|
last.setFullYear(now.getFullYear() - 10)
|
||||||
|
startDate = last.toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
endDate = new Date(new Date()).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
})
|
||||||
|
} else if (range === 'Max') {
|
||||||
|
startDate = ''
|
||||||
|
endDate = ''
|
||||||
|
} else if (range === 'startDateTime') {
|
||||||
|
startDate = dateTime
|
||||||
|
endDate = ''
|
||||||
|
} else if (range === 'endDateTime') {
|
||||||
|
startDate = ''
|
||||||
|
endDate = dateTime
|
||||||
|
}
|
||||||
|
if (startDate || endDate) {
|
||||||
|
// historicData 和 xAxisData 需在 initEcharts 作用域可用
|
||||||
|
if (
|
||||||
|
typeof historicData !== 'undefined' &&
|
||||||
|
typeof xAxisData !== 'undefined'
|
||||||
|
) {
|
||||||
|
let startValue = xAxisData[0]
|
||||||
|
if (startDate) {
|
||||||
|
const idx = findClosestDateIndex(historicData, startDate)
|
||||||
|
// 用 historicData[idx].date 格式化为 xAxisData 的格式
|
||||||
|
startValue = new Date(historicData[idx].date).toLocaleDateString(
|
||||||
|
'en-US',
|
||||||
|
{
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let endValue = endDate
|
||||||
|
if (endDate) {
|
||||||
|
// console.warn(endDate)
|
||||||
|
const idx = findClosestDateIndexDescLeft(historicData, endDate)
|
||||||
|
// console.warn(idx)
|
||||||
|
// 用 historicData[idx].date 格式化为 xAxisData 的格式
|
||||||
|
endValue = new Date(historicData[idx].date).toLocaleDateString(
|
||||||
|
'en-US',
|
||||||
|
{
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// console.warn(endValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startDate) {
|
||||||
|
myCharts.setOption({
|
||||||
|
dataZoom: {
|
||||||
|
endValue: startValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
state.selectHistoricStartDate = new Date(startValue)
|
||||||
|
}
|
||||||
|
if (endDate) {
|
||||||
|
myCharts.setOption({
|
||||||
|
dataZoom: {
|
||||||
|
startValue: endValue,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
state.selectHistoricEndDate = new Date(endValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
myCharts.setOption({
|
||||||
|
dataZoom: {
|
||||||
|
startValue: '',
|
||||||
|
endValue: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
state.selectHistoricStartDate = new Date('2009-10-07')
|
||||||
|
state.selectHistoricEndDate = new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用2009-10-07之后的日期
|
||||||
|
const disableAfterDate = (date) => {
|
||||||
|
return date < new Date('2009-10-06') || date > new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用过去的日期
|
||||||
|
const disablePreviousDate = (date) => {
|
||||||
|
return date < new Date(state.selectHistoricStartDate) || date > new Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换搜索区间开始日期
|
||||||
|
const changeSearchRangeStartDate = (date) => {
|
||||||
|
// console.error(date)
|
||||||
|
changeSearchRange(
|
||||||
|
'startDateTime',
|
||||||
|
new Date(date).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换搜索区间结束日期
|
||||||
|
const changeSearchRangeEndDate = (date) => {
|
||||||
|
// console.error(date)
|
||||||
|
changeSearchRange(
|
||||||
|
'endDateTime',
|
||||||
|
new Date(date).toLocaleDateString('en-US', {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.custom-echarts {
|
||||||
|
.myChart {
|
||||||
|
width: 100%;
|
||||||
|
height: 25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-header {
|
||||||
|
.echarts-header-title {
|
||||||
|
span {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #323232;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.echarts-search-area {
|
||||||
|
padding: 2rem 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.echarts-search-byRange {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.7rem;
|
||||||
|
.search-range-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.7rem;
|
||||||
|
.search-range-list-each {
|
||||||
|
padding: 0.2rem 0.3rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
cursor: pointer;
|
||||||
|
span {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-search-byDate {
|
||||||
|
padding: 1.5rem 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -3,7 +3,7 @@ import { computed } from 'vue'
|
|||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
|
||||||
import size375 from '@/components/customFooter/size375/index.vue'
|
import size375 from '@/components/customFooter/size375/index.vue'
|
||||||
import size768 from '@/components/customFooter/size1920/index.vue'
|
import size768 from '@/components/customFooter/size768/index.vue'
|
||||||
import size1440 from '@/components/customFooter/size1920/index.vue'
|
import size1440 from '@/components/customFooter/size1920/index.vue'
|
||||||
import size1920 from '@/components/customFooter/size1920/index.vue'
|
import size1920 from '@/components/customFooter/size1920/index.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -15,7 +15,7 @@ const { t } = useI18n()
|
|||||||
|
|
||||||
const viewComponent = computed(() => {
|
const viewComponent = computed(() => {
|
||||||
const viewWidth = width.value
|
const viewWidth = width.value
|
||||||
if (viewWidth <= 450) {
|
if (viewWidth <= 500) {
|
||||||
return size375
|
return size375
|
||||||
} else if (viewWidth <= 1100) {
|
} else if (viewWidth <= 1100) {
|
||||||
return size768
|
return size768
|
||||||
|
@ -1,23 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 通用页脚 -->
|
<!-- 通用页脚 -->
|
||||||
<div class="custom-footer">
|
<div class="custom-footer">
|
||||||
<span>Copyright © 2024-2027 FiEE</span>
|
<div class="custom-footer-box">
|
||||||
|
<span>© 2025 FiEE, Inc. All Rights Reserved.</span>
|
||||||
|
<div class="footer-links">
|
||||||
|
<span @click="handleLink('privacyPolicy')">Privacy Policy</span>
|
||||||
|
<span @click="handleLink('termsOfUse')">Terms of use</span>
|
||||||
|
<span @click="handleLink('siteMap')">Site Map</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
const router = useRouter();
|
||||||
|
import privacyPolicy from "@/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf";
|
||||||
|
import termsOfUse from "@/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf";
|
||||||
|
import siteMap from "@/assets/file/footer/FiEE, Inc. _ Site Map.pdf";
|
||||||
|
|
||||||
|
//点击跳转到对应的链接页面
|
||||||
|
const handleLink = (link) => {
|
||||||
|
// if (link === "privacyPolicy") {
|
||||||
|
// window.open(privacyPolicy, "_blank");
|
||||||
|
// } else if (link === "termsOfUse") {
|
||||||
|
// window.open(termsOfUse, "_blank");
|
||||||
|
// } else if (link === "siteMap") {
|
||||||
|
// window.open(siteMap, "_blank");
|
||||||
|
// }
|
||||||
|
router.push(link)
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.custom-footer {
|
.custom-footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
background: #f7f8fa;
|
||||||
padding: 24px 0;
|
border-top: 1px solid #ececec;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
.custom-footer-box {
|
||||||
|
max-width: 1700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
letter-spacing: 1px;
|
||||||
color: #888;
|
color: #888;
|
||||||
// font-size: 15px;
|
// font-size: 15px;
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
background: #f7f8fa;
|
padding: 1rem 40px;
|
||||||
letter-spacing: 1px;
|
text-align: center;
|
||||||
border-top: 1px solid #ececec;
|
}
|
||||||
z-index: 100;
|
|
||||||
|
.footer-links {
|
||||||
|
margin: 0.4rem 0 0;
|
||||||
|
span {
|
||||||
|
border-right: 1px solid #d2d2d7;
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
span:nth-last-child(1) {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,23 +1,70 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 通用页脚 -->
|
<!-- 通用页脚 -->
|
||||||
<div class="custom-footer">
|
<div class="custom-footer">
|
||||||
<span>Copyright © 2024-2027 FiEE</span>
|
<span>© 2025 FiEE, Inc. All Rights Reserved.</span>
|
||||||
|
<div class="footer-links-box">
|
||||||
|
<div class="footer-links">
|
||||||
|
<span @click="handleLink('privacyPolicy')">Privacy Policy</span>
|
||||||
|
<span @click="handleLink('termsOfUse')">Terms of use</span>
|
||||||
|
<span @click="handleLink('siteMap')">Site Map</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
import privacyPolicy from "@/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf";
|
||||||
|
import termsOfUse from "@/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf";
|
||||||
|
import siteMap from "@/assets/file/footer/FiEE, Inc. _ Site Map.pdf";
|
||||||
|
|
||||||
|
//点击跳转到对应的链接页面
|
||||||
|
const handleLink = (link) => {
|
||||||
|
// if (link === "privacyPolicy") {
|
||||||
|
// window.open(privacyPolicy, "_blank");
|
||||||
|
// } else if (link === "termsOfUse") {
|
||||||
|
// window.open(termsOfUse, "_blank");
|
||||||
|
// } else if (link === "siteMap") {
|
||||||
|
// window.open(siteMap, "_blank");
|
||||||
|
// }
|
||||||
|
router.push(link)
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.custom-footer {
|
.custom-footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 120px 0;
|
padding: 1rem 0;
|
||||||
color: #888;
|
color: #888;
|
||||||
font-size: 75px;
|
font-size: 0.9rem;
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
letter-spacing: 5px;
|
letter-spacing: 5px;
|
||||||
border-top: 5px solid #ececec;
|
border-top: 5px solid #ececec;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
|
.footer-links-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0.6rem 0 0;
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
span {
|
||||||
|
border-right: 1px solid #d2d2d7;
|
||||||
|
padding: 0 0.8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
span:nth-last-child(1) {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
59
src/components/customFooter/size768/index.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 通用页脚 -->
|
||||||
|
<div class="custom-footer">
|
||||||
|
<span>© 2025 FiEE, Inc. All Rights Reserved.</span>
|
||||||
|
<div class="footer-links">
|
||||||
|
<span @click="handleLink('privacyPolicy')">Privacy Policy</span>
|
||||||
|
<span @click="handleLink('termsOfUse')">Terms of use</span>
|
||||||
|
<span @click="handleLink('siteMap')">Site Map</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
const router = useRouter();
|
||||||
|
import privacyPolicy from "@/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf";
|
||||||
|
import termsOfUse from "@/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf";
|
||||||
|
import siteMap from "@/assets/file/footer/FiEE, Inc. _ Site Map.pdf";
|
||||||
|
|
||||||
|
//点击跳转到对应的链接页面
|
||||||
|
const handleLink = (link) => {
|
||||||
|
// if (link === "privacyPolicy") {
|
||||||
|
// window.open(privacyPolicy, "_blank");
|
||||||
|
// } else if (link === "termsOfUse") {
|
||||||
|
// window.open(termsOfUse, "_blank");
|
||||||
|
// } else if (link === "siteMap") {
|
||||||
|
// window.open(siteMap, "_blank");
|
||||||
|
// }
|
||||||
|
router.push(link)
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.custom-footer {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px 0;
|
||||||
|
color: #888;
|
||||||
|
// font-size: 15px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background: #f7f8fa;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
border-top: 1px solid #ececec;
|
||||||
|
z-index: 100;
|
||||||
|
padding: 1rem 0;
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
margin: 0.4rem 0 0;
|
||||||
|
span {
|
||||||
|
border-right: 1px solid #d2d2d7;
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
span:nth-last-child(1) {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,7 +4,7 @@ import { useWindowSize } from '@vueuse/core'
|
|||||||
|
|
||||||
import size375 from '@/components/customHeader/size375/index.vue'
|
import size375 from '@/components/customHeader/size375/index.vue'
|
||||||
import size768 from '@/components/customHeader/size375/index.vue'
|
import size768 from '@/components/customHeader/size375/index.vue'
|
||||||
import size1440 from '@/components/customHeader/size1920/index.vue'
|
import size1440 from '@/components/customHeader/size1440/index.vue'
|
||||||
import size1920 from '@/components/customHeader/size1920/index.vue'
|
import size1920 from '@/components/customHeader/size1920/index.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
@ -17,9 +17,9 @@ const viewComponent = computed(() => {
|
|||||||
const viewWidth = width.value
|
const viewWidth = width.value
|
||||||
if (viewWidth <= 450) {
|
if (viewWidth <= 450) {
|
||||||
return size375
|
return size375
|
||||||
} else if (viewWidth <= 768) {
|
} else if (viewWidth <= 835) {
|
||||||
return size768
|
return size768
|
||||||
} else if (viewWidth <= 1500) {
|
} else if (viewWidth <= 1640) {
|
||||||
return size1440
|
return size1440
|
||||||
} else if (viewWidth <= 1920 || viewWidth > 1920) {
|
} else if (viewWidth <= 1920 || viewWidth > 1920) {
|
||||||
return size1920
|
return size1920
|
||||||
|
230
src/components/customHeader/size1440/index.vue
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
<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 = () => {
|
||||||
|
//滚动距离大于100px时,处理对应的header样式
|
||||||
|
isScrolled.value = window.scrollY >= 100
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('scroll', handleScroll)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('scroll', handleScroll)
|
||||||
|
})
|
||||||
|
|
||||||
|
//点击回到首页
|
||||||
|
const handleToHome = () => {
|
||||||
|
router.push('/myhome')
|
||||||
|
selectedKey.value = null // 重置菜单选中状态
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.custom-header {
|
||||||
|
--header-height: 5rem;
|
||||||
|
--primary-color: #8b59f7;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: transparent;
|
||||||
|
height: var(--header-height);
|
||||||
|
|
||||||
|
&.header-scrolled {
|
||||||
|
background: rgba(220, 207, 248, 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 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-weight: 700;
|
||||||
|
// font-size: 16px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
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>
|
||||||
|
<style>
|
||||||
|
.header-menu .n-menu .n-menu-item-content .n-menu-item-content-header {
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: unset !important;
|
||||||
|
}
|
||||||
|
.header-menu .n-menu .n-submenu .n-menu-item-content{
|
||||||
|
padding: 0 8px!important;
|
||||||
|
}
|
||||||
|
</style>
|
@ -54,9 +54,14 @@ export const useHeaderMenuConfig = () => {
|
|||||||
href: "/secfilings",
|
href: "/secfilings",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("header_menu.financial_information.quarterly_results"),
|
label: t("header_menu.financial_information.annual_reports"),
|
||||||
key: "quarterly_results",
|
key: "annual_reports",
|
||||||
href: "/quarterlyresults",
|
href: "/annualreports",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("header_menu.financial_information.quarterly_reports"),
|
||||||
|
key: "quarterly_reports",
|
||||||
|
href: "/quarterlyreports",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -74,11 +79,11 @@ export const useHeaderMenuConfig = () => {
|
|||||||
key: "historic_stock_price",
|
key: "historic_stock_price",
|
||||||
href:'/historic-stock'
|
href:'/historic-stock'
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
label: t("header_menu.stock_information.investment_calculator"),
|
// label: t("header_menu.stock_information.investment_calculator"),
|
||||||
key: "investment_calculator",
|
// key: "investment_calculator",
|
||||||
href:'/calculator'
|
// href:'/calculator'
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
// src/api/testRequest.js
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export const getTestData = async () => {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(
|
|
||||||
"http://saas-test.szjixun.cn/api/chart/forward/test"
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求出错:", error);
|
|
||||||
throw error; // 可选:让调用方处理异常
|
|
||||||
}
|
|
||||||
};
|
|
35754
src/dict/secFiles.js
Normal file
@ -350,7 +350,7 @@ export default {
|
|||||||
},
|
},
|
||||||
investor: {
|
investor: {
|
||||||
title: 'Investor Relations',
|
title: 'Investor Relations',
|
||||||
subtitle: 'Finanzstatus von Minim (NASDAQ: MINM)',
|
subtitle: 'Finanzstatus von Minim (NASDAQ: FIEE)',
|
||||||
latest_news: {
|
latest_news: {
|
||||||
title: 'Aktuelle Nachrichten',
|
title: 'Aktuelle Nachrichten',
|
||||||
financial: {
|
financial: {
|
||||||
@ -367,8 +367,8 @@ export default {
|
|||||||
},
|
},
|
||||||
stock: {
|
stock: {
|
||||||
title: 'Aktienkurs',
|
title: 'Aktienkurs',
|
||||||
content: 'MINM-Kurs auf TradingView',
|
content: 'FIEE-Kurs auf TradingView',
|
||||||
link: 'MINM-Kurs'
|
link: 'FIEE-Kurs'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
financial_data: {
|
financial_data: {
|
||||||
|
@ -378,7 +378,7 @@ export default {
|
|||||||
},
|
},
|
||||||
investor: {
|
investor: {
|
||||||
title: "Investor Relations",
|
title: "Investor Relations",
|
||||||
subtitle: "Minim (NASDAQ: MINM) Financial Status",
|
subtitle: "Minim (NASDAQ: FIEE) Financial Status",
|
||||||
latest_news: {
|
latest_news: {
|
||||||
title: "Latest News",
|
title: "Latest News",
|
||||||
financial: {
|
financial: {
|
||||||
@ -395,8 +395,8 @@ export default {
|
|||||||
},
|
},
|
||||||
stock: {
|
stock: {
|
||||||
title: "Stock Quote",
|
title: "Stock Quote",
|
||||||
content: "MINM Quote on TradingView",
|
content: "FIEE Quote on TradingView",
|
||||||
link: "MINM Quote",
|
link: "FIEE Quote",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
financial_data: {
|
financial_data: {
|
||||||
@ -488,21 +488,22 @@ export default {
|
|||||||
title: "Financials",
|
title: "Financials",
|
||||||
overview: {
|
overview: {
|
||||||
title: "Company Financial 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.",
|
desc: "This page offers access to our <strong>Annual Reports</strong>. These sections provide key financial data and regulatory documents, keeping you informed about our financial performance and compliance.",
|
||||||
},
|
},
|
||||||
annual_reports: {
|
annual_reports: {
|
||||||
title: "Annual Reports",
|
title: "Annual Reports",
|
||||||
file_name: "File Name",
|
file_name: "File Name",
|
||||||
view: "View",
|
view: "View",
|
||||||
|
date: "Date",
|
||||||
},
|
},
|
||||||
sec: {
|
sec: {
|
||||||
title: "SEC Filings",
|
title: "SEC Filings",
|
||||||
desc: "To Access All Of Our Fillings With Sec Sites, Please",
|
desc: "To access all of our filings with the SEC, please",
|
||||||
click_here: "Click Here",
|
click_here: "click here",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
quarterlyresults: {
|
quarterlyreports: {
|
||||||
title: "Quarterly Results",
|
title: "Quarterly Reports",
|
||||||
search: {
|
search: {
|
||||||
placeholder: "Search",
|
placeholder: "Search",
|
||||||
button: "Go",
|
button: "Go",
|
||||||
@ -524,7 +525,8 @@ export default {
|
|||||||
financial_information: {
|
financial_information: {
|
||||||
title: "Financial Information",
|
title: "Financial Information",
|
||||||
sec_filings: "SEC Filings",
|
sec_filings: "SEC Filings",
|
||||||
quarterly_results: "Quarterly Results",
|
annual_reports: "Annual Reports",
|
||||||
|
quarterly_reports: "Quarterly Reports",
|
||||||
},
|
},
|
||||||
stock_information: {
|
stock_information: {
|
||||||
title: "Stock Information",
|
title: "Stock Information",
|
||||||
@ -559,18 +561,17 @@ export default {
|
|||||||
HOME: {
|
HOME: {
|
||||||
CONTAIN: {
|
CONTAIN: {
|
||||||
TITLEONE: {
|
TITLEONE: {
|
||||||
TITLE: "Company positioning",
|
TITLE: "Company Positioning",
|
||||||
CONTENT:
|
CONTENT: "",
|
||||||
"To empower global talents through innovative technology solutions",
|
|
||||||
CONTENTTWO:
|
CONTENTTWO:
|
||||||
"Leveraging IoT, connectivity, and artificial intelligence to create targeted, multilingual digital brands, fostering a global community of Key Opinion Leaders and providing unparalleled value throughout the digital content lifecycle",
|
"Leveraging IoT, connectivity, and Al to create targeted, multilingual digital brands, fostering a global community of Key Opinion Leaders and providing unparalleled value throughout the digital content lifecycle",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CONTAINY: {
|
CONTAINY: {
|
||||||
STOCK_INFO: {
|
STOCK_INFO: {
|
||||||
TITLE: "Stock Info",
|
TITLE: "Stock Information",
|
||||||
LAST_PRICE: "Last Price",
|
LAST_PRICE: "Price",
|
||||||
CHANGE: "Change",
|
CHANGE: "% Change",
|
||||||
STOCK_CODE: "Stock Code",
|
STOCK_CODE: "Stock Code",
|
||||||
VOLUME: "Volume",
|
VOLUME: "Volume",
|
||||||
MARKET_CAP: "Market Cap",
|
MARKET_CAP: "Market Cap",
|
||||||
@ -606,15 +607,15 @@ export default {
|
|||||||
TITLETWO: {
|
TITLETWO: {
|
||||||
TITLE: "About FiEE, Inc.",
|
TITLE: "About FiEE, Inc.",
|
||||||
CONTENT:
|
CONTENT:
|
||||||
'FiEE Inc., (NASDAQ: MINM) was formerly Minim, Inc. founded in 1977. We have a historical track record of delivering a comprehensive WiFi/Software as a Service platform in the market. After years of development, we made the strategic decision to transition to a Software First Model in 2023 to expand our technology portfolio and revenue streams. By 2025, we have rebranded ourselves as a technology company leveraging our expertise in IoT, connectivity, and artificial intelligence ("AI") to explore new business prospects and extend our global footprint.',
|
'FiEE, Inc. (NASDAQ: FIEE), formerly Minim, Inc., was founded in 1977. We have a historical track record of delivering a comprehensive WiFi/Software as a Service platform in the market. After years of development, we made the strategic decision to transition to a Software First Model in 2023 to expand our technology portfolio and revenue streams. In 2025, we rebranded ourselves as a technology company leveraging our expertise in IoT, connectivity, and artificial intelligence ("AI") to explore new business prospects and extend our global footprint.',
|
||||||
CONTENTTWO:
|
CONTENTTWO:
|
||||||
'Our services are structured into four key categories: Cloud-Managed Connectivity (WiFi) Platform, IoT Hardware Sales & Licensing, SAAS Solutions, and Professional To-C and To-B Services & Support. Notably, we have introduced our innovative Software as a Service ("SaaS") solutions, which integrate our AI and data analytics capabilities into content creation and brand management. This initiative has led to the nurturing of a robust pool of Key Opinion Leaders (KOLs) on major social media platforms worldwide, assisting them in developing, managing, and optimizing their digital presence across global platforms. Our services include customized graphics and posts, short videos, and editorial calendars tailored to align with brand objectives.',
|
'into four key categories: Cloud-Managed Connectivity (WiFi) Platform, IoT Hardware Sales & Licensing, SAAS Solutions, and Professional To-C and To-B Services & Support. Notably, we have introduced our innovative Software as a Service ("SaaS") solutions, which integrate our AI and data analytics capabilities into content creation and brand management. This initiative has led to the nurturing of a robust pool of Key Opinion Leaders (KOLs) on major social media platforms worldwide, assisting them in developing, managing, and optimizing their digital presence across global platforms. Our services include customized graphics and posts, short videos, and editorial calendars tailored to align with brand objectives.',
|
||||||
CONTENTTHREE:
|
CONTENTTHREE:
|
||||||
"Powered by IoT-enabled Connectivity Solutions, we are empowering talents with value throughout the entire lifecycle. We are committed to ongoing investments in AI technology for data analysis and fan behavior insights to develop highly targeted multimedia and multilingual content. Our goal is to expand our solutions and services to reach a broader global audience.",
|
"Powered by IoT-enabled Connectivity Solutions, we are empowering talents with value throughout the entire lifecycle. We are committed to ongoing investments in AI technology for data analysis and fan behavior insights to develop highly targeted multimedia and multilingual content. Our goal is to expand our solutions and services to reach a broader global audience.",
|
||||||
CONTENTTWOTITLE: "Our services are structured",
|
CONTENTTWOTITLE: "Our services are structured",
|
||||||
},
|
},
|
||||||
TITLETHREE: {
|
TITLETHREE: {
|
||||||
TITLE: "Our mission and vision",
|
TITLE: "Our Mission and Vision",
|
||||||
CONTENT:
|
CONTENT:
|
||||||
"• To empower global talents through innovative technology solutions",
|
"• To empower global talents through innovative technology solutions",
|
||||||
CONTENTTWO:
|
CONTENTTWO:
|
||||||
@ -623,43 +624,43 @@ export default {
|
|||||||
TITLEFOUR: {
|
TITLEFOUR: {
|
||||||
CONTENT: "",
|
CONTENT: "",
|
||||||
|
|
||||||
TITLE: "Corporate Milestone",
|
TITLE: "Corporate Milestones",
|
||||||
SUBHEADING: "•1977 – 2015 : Founding & Licensing",
|
SUBHEADING: "1977 – 2015 : Founding & Licensing",
|
||||||
paragraph: {
|
paragraph: {
|
||||||
ONE: "I.Founded as Zoom Telephonics in 1977. ",
|
ONE: "• Founded as Zoom Telephonics in 1977. ",
|
||||||
TWO: "II.Secured a five-year Motorola home-network license effective 1 Jan, 2016. ",
|
TWO: "• Secured a five-year Motorola home-network license effective 1 Jan, 2016. ",
|
||||||
},
|
},
|
||||||
SUBHEADINGTWO: "•2020 : Merger & AI Advancement",
|
SUBHEADINGTWO: "2020 : Merger & AI Advancement",
|
||||||
paragraphTwo: {
|
paragraphTwo: {
|
||||||
ONE: "I.November 2020: Merged with Minim Inc.; adopted Minim name/OTCQB ticker",
|
ONE: "• November 2020: Merged with Minim Inc.; adopted Minim name/OTCQB ticker.",
|
||||||
TWO: "II.Rolled out AI-driven Wi-Fi management and IoT security platform.",
|
TWO: "• Rolled out AI-driven Wi-Fi management and IoT security platform.",
|
||||||
THREE: "",
|
THREE: "",
|
||||||
},
|
},
|
||||||
SUBHEADINGTHREE: "•2021: NASDAQ Listing",
|
SUBHEADINGTHREE: "2021: NASDAQ Listing",
|
||||||
paragraphTHREE: {
|
paragraphTHREE: {
|
||||||
ONE: "I.Transitioned from OTCQB to NASDAQ Capital Market on 7 July, 2021, under ticker MINM.",
|
ONE: "• Transitioned from OTCQB to NASDAQ Capital Market on 7 July, 2021, under ticker MINM.",
|
||||||
TWO: "II.Raised $25 M in a public offering to fund expansion.",
|
TWO: "• Raised $25 M in a public offering to fund expansion.",
|
||||||
THREE: "",
|
THREE: "",
|
||||||
},
|
},
|
||||||
SUBHEADINGFOREFF: "•2023 – 2024: Pivoting to A Software First Model",
|
SUBHEADINGFOREFF: "2023 – 2024: Pivoting to A Software First Model",
|
||||||
paragraphFOUR: {
|
paragraphFOUR: {
|
||||||
ONE: "I.Enhanced its MinimOS cloud platform and API suite for ISPs/OEMs, with major deployments like Vox’s AI-driven Wi-Fi Home Manager.",
|
ONE: "• Enhanced its MinimOS cloud platform and API suite for ISPs/OEMs, with major deployments like Vox’s AI-driven Wi-Fi Home Manager.",
|
||||||
TWO: "II.Signed a merger agreement with e2Companies to broaden its technology and revenue base",
|
TWO: "• Signed a merger agreement with e2Companies to broaden its technology and revenue base.",
|
||||||
THREE: "",
|
THREE: "",
|
||||||
},
|
},
|
||||||
SUBHEADINGFIVE: "•2025 Rebranding & New C-Suite",
|
SUBHEADINGFIVE: "2025 Rebranding & New C-Suite",
|
||||||
paragraphFIVE: {
|
paragraphFIVE: {
|
||||||
ONE: "I.Officially rebranded from Minim Inc. to FiEE Inc.",
|
ONE: "• Officially rebranded from Minim Inc. to FiEE, Inc.",
|
||||||
TWO: "II.Appointed Li Wai Chung as CEO and Cao Yu as CFO.",
|
TWO: "• Appointed Li Wai Chung as CEO and Cao Yu as CFO.",
|
||||||
THREE:
|
THREE:
|
||||||
"III.Leverage on foundation in IoT, connectivity and AI to pursue new business opportunities.",
|
"• Leverage on foundation in IoT, connectivity and AI to pursue new business opportunities.",
|
||||||
FOUR: "IV.Launched SaaS product in the market to generate recurring revenue streams",
|
FOUR: "• Launched SaaS product in the market to generate recurring revenue streams.",
|
||||||
FIVE: "V.Acquisition of Suzhou Yixuntong Network Technology Co., Ltd., a high growth technology service provider in PRC",
|
FIVE: "• Acquisition of Suzhou Yixuntong Network Technology Co., Ltd., a high growth technology service provider in PRC.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TITLEFIVE: {
|
TITLEFIVE: {
|
||||||
CONTENT:
|
CONTENT:
|
||||||
"•We developed our own AI-driven cloud software platform and applications, providing efficient and high quality network management and security solutions for home and business users, as well as the service providers that assisted them.",
|
"•We developed our own AI-driven cloud software platform and applications, providing efficient and high-quality network management and security solutions for home and business users, as well as the service providers that assisted them.",
|
||||||
TITLE: "Outstanding Achievements",
|
TITLE: "Outstanding Achievements",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -675,30 +676,29 @@ export default {
|
|||||||
paragraph: {
|
paragraph: {
|
||||||
ONE: {
|
ONE: {
|
||||||
TITLE: "(1) Cloud-Managed Connectivity (WiFi) Platform: ",
|
TITLE: "(1) Cloud-Managed Connectivity (WiFi) Platform: ",
|
||||||
CONTENT: "•SaaS powering OS for consumers and SMBs ",
|
CONTENT: "SaaS powering OS for consumers and SMBs ",
|
||||||
CONTENTTWO:
|
CONTENTTWO: "AI-driven threat protection, and over-the-air updates",
|
||||||
"•AI-driven threat protection, and over-the-air updates",
|
|
||||||
},
|
},
|
||||||
TWO: {
|
TWO: {
|
||||||
TITLE: "(2) IoT Hardware Sales & Licensing: ",
|
TITLE: "(2) IoT Hardware Sales & Licensing: ",
|
||||||
CONTENT:
|
CONTENT:
|
||||||
"•IoT products and technologies, including developing VR/AR online sharing technologies",
|
"IoT products and technologies, including developing VR/AR online sharing technologies",
|
||||||
CONTENTTWO: "•IoT data collection, analysis and management",
|
CONTENTTWO: "IoT data collection, analysis and management",
|
||||||
},
|
},
|
||||||
THREE: {
|
THREE: {
|
||||||
TITLE: "(3) SAAS Solutions",
|
TITLE: "(3) SAAS Solutions",
|
||||||
CONTENT: "•Internet sales and IoT support",
|
CONTENT: "Internet sales and IoT support",
|
||||||
CONTENTTWO: "•KOL branding services",
|
CONTENTTWO: "KOL branding services",
|
||||||
CONTENTTHREE:
|
CONTENTTHREE:
|
||||||
"•AI-enabled content creation and fans habit analysis solutions",
|
"AI-enabled content creation and fans habit analysis solutions",
|
||||||
},
|
},
|
||||||
FOUR: {
|
FOUR: {
|
||||||
TITLE: "(4) Professional To-C and To-B Services & Support",
|
TITLE: "(4) Professional To-C and To-B Services & Support",
|
||||||
CONTENT:
|
CONTENT:
|
||||||
"•Managed-service agreements with ISPs and enterprise customers for network-support, security monitoring, and custom development",
|
"Managed-service agreements with ISPs and enterprise customers for network-support, security monitoring, and custom development",
|
||||||
CONTENTTWO: "•KOL branding services",
|
CONTENTTWO: "KOL branding services",
|
||||||
CONTENTTHREE:
|
CONTENTTHREE:
|
||||||
"•AI-enabled content creation and fans habit analysis solutions",
|
"AI-enabled content creation and fans habit analysis solutions",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -707,20 +707,21 @@ export default {
|
|||||||
// 管理
|
// 管理
|
||||||
MANAGEMENT: {
|
MANAGEMENT: {
|
||||||
ONE: {
|
ONE: {
|
||||||
TITLE: "Wai Chung LI",
|
TITLE: "Li Wai Chung",
|
||||||
TITLETWO: "Chief Executive Officer and President",
|
TITLETWO: "Chief Executive Officer",
|
||||||
CONTENT:
|
CONTENT:
|
||||||
"Mr. Li is the Chief Executive Officer and President, with extensive experience in accounting, corporate management and finance management. His role encompasses the oversight of our daily business operations and plays a vital part in the overall management of our Group.With a track record spanning prestigious roles at Deloitte China, Shanghai Prime Machinery Company Limited, Lens Technology Co., Ltd., and more, Mr. Li brings invaluable expertise to lead the team. He chaired Audit Committees for Fulu Holdings and Taizhou Water Group in Hong Kong, and Nedschroef in the Netherlands, showcasing his global leadership. Holding key positions in investment management, business consulting, and directorial roles in listedcompanies, his insights drive strategic decision-making. ",
|
"Mr. Li is our Chief Executive Officer. Mr. Li has extensive experience in accounting, corporate management and finance management. His role encompasses the oversight of our daily business operations and plays a vital part in the overall management of our Group.With a track record spanning prestigious roles at Deloitte China, Shanghai Prime Machinery Company Limited, Lens Technology Co., Ltd., and more, Mr. Li brings invaluable expertise to our team.",
|
||||||
CONTENTTWO: "",
|
CONTENTTWO:
|
||||||
|
"He served as chair of the Audit Committees for Fulu Holdings and Taizhou Water Group in Hong Kong, and Nedschroef in the Netherlands, showcasing his global leadership. Mr. Li has previously held key positions in investment management, business consulting, and directorial roles in publicly listed companies.",
|
||||||
CONTENTTHREE: "",
|
CONTENTTHREE: "",
|
||||||
},
|
},
|
||||||
TWO: {
|
TWO: {
|
||||||
TITLE: "Cao Yu",
|
TITLE: "Cao Yu",
|
||||||
TITLETWO: "Chief Financial Officer, Secretary, Treasurer and Director",
|
TITLETWO: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
CONTENTONE:
|
CONTENTONE:
|
||||||
"Ms. Cao is the Chief Financial Officer, Secretary, Treasurer and Director, with a wealth of experience in financial management. She oversees financial operations, strategic planning, risk management, and reporting to ensure the company's financial health and compliance with regulations.",
|
"Ms. Cao is our Chief Financial Officer, Secretary, Treasurer and Director. Ms. Cao has a wealth of experience in financial management. She oversees financial operations, strategic planning, risk management, and reporting to ensure our financial health and compliance with regulations.",
|
||||||
CONTENTTWO:
|
CONTENTTWO:
|
||||||
"Ms. Cao was previously served as the treasury director of Taifeng Cultural Communication Co., Ltd where she oversees its financial matters from November 2018 to November 2024. Prior to that, Ms. Cao served as a business manager of Yangfeng Art Exchange Co., Ltd from February 2016 to October 2018. From March 2011 to January 2016, she served as the treasury officer of financial department of Suzhou Industrial Park Xinfushida Plastic Profile Products Co., Ltd.",
|
"Ms. Cao previously served as the treasury director of Taifeng Cultural Communication Co., Ltd where she oversaw its financial matters from November 2018 to November 2024. Prior to that, Ms. Cao served as a business manager of Yangfeng Art Exchange Co., Ltd from February 2016 to October 2018. From March 2011 to January 2016, she served as the treasury officer of financial department of Suzhou Industrial Park Xinfushida Plastic Profile Products Co., Ltd.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -350,7 +350,7 @@ export default {
|
|||||||
},
|
},
|
||||||
investor: {
|
investor: {
|
||||||
title: '投資家向け情報',
|
title: '投資家向け情報',
|
||||||
subtitle: 'Minim(NASDAQ: MINM)財務状況',
|
subtitle: 'Minim(NASDAQ: FIEE)財務状況',
|
||||||
latest_news: {
|
latest_news: {
|
||||||
title: '最新ニュース',
|
title: '最新ニュース',
|
||||||
financial: {
|
financial: {
|
||||||
@ -367,8 +367,8 @@ export default {
|
|||||||
},
|
},
|
||||||
stock: {
|
stock: {
|
||||||
title: '株価情報',
|
title: '株価情報',
|
||||||
content: 'TradingViewのMINM株価',
|
content: 'TradingViewのFIEE株価',
|
||||||
link: 'MINM株価'
|
link: 'FIEE株価'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
financial_data: {
|
financial_data: {
|
||||||
|
@ -346,7 +346,7 @@ export default {
|
|||||||
},
|
},
|
||||||
investor: {
|
investor: {
|
||||||
title: '投資者關係',
|
title: '投資者關係',
|
||||||
subtitle: 'Minim(納斯達克股票代碼:MINM)財務狀況',
|
subtitle: 'Minim(納斯達克股票代碼:FIEE)財務狀況',
|
||||||
latest_news: {
|
latest_news: {
|
||||||
title: '最新動態',
|
title: '最新動態',
|
||||||
financial: {
|
financial: {
|
||||||
@ -363,8 +363,8 @@ export default {
|
|||||||
},
|
},
|
||||||
stock: {
|
stock: {
|
||||||
title: '股票報價',
|
title: '股票報價',
|
||||||
content: 'TradingView的MINM報價',
|
content: 'TradingView的FIEE報價',
|
||||||
link: 'MINM報價'
|
link: 'FIEE報價'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
financial_data: {
|
financial_data: {
|
||||||
|
@ -351,7 +351,7 @@ export default {
|
|||||||
},
|
},
|
||||||
investor: {
|
investor: {
|
||||||
title: '投资者关系',
|
title: '投资者关系',
|
||||||
subtitle: 'Minim(纳斯达克股票代码:MINM)财务状况',
|
subtitle: 'Minim(纳斯达克股票代码:FIEE)财务状况',
|
||||||
latest_news: {
|
latest_news: {
|
||||||
title: '最新动态',
|
title: '最新动态',
|
||||||
financial: {
|
financial: {
|
||||||
@ -368,8 +368,8 @@ export default {
|
|||||||
},
|
},
|
||||||
stock: {
|
stock: {
|
||||||
title: '股票报价',
|
title: '股票报价',
|
||||||
content: 'TradingView的MINM报价',
|
content: 'TradingView的FIEE报价',
|
||||||
link: 'MINM报价'
|
link: 'FIEE报价'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
financial_data: {
|
financial_data: {
|
||||||
|
@ -48,10 +48,10 @@ const routes = [
|
|||||||
component: () => import("@/views/email-alerts/index.vue"),
|
component: () => import("@/views/email-alerts/index.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/quarterlyresults",
|
path: "/quarterlyreports",
|
||||||
name: "QuarterlyResults",
|
name: "quarterlyreports",
|
||||||
component: () =>
|
component: () =>
|
||||||
import("@/views/financialinformation/quarterlyresults/index.vue"),
|
import("@/views/financialinformation/quarterlyreports/index.vue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/secfilings",
|
path: "/secfilings",
|
||||||
@ -59,11 +59,28 @@ const routes = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import("@/views/financialinformation/secfilings/index.vue"),
|
import("@/views/financialinformation/secfilings/index.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/secfilingsDefail",
|
||||||
|
name: "SecFilingsDetail",
|
||||||
|
component: () =>
|
||||||
|
import("@/views/financialinformation/secfilingsdetail/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/annualreports",
|
||||||
|
name: "AnnualReports",
|
||||||
|
component: () =>
|
||||||
|
import("@/views/financialinformation/annualreports/index.vue"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/press-releases",
|
path: "/press-releases",
|
||||||
name: "press-releases",
|
name: "press-releases",
|
||||||
component: () => import("@/views/press-releases/index.vue"),
|
component: () => import("@/views/press-releases/index.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/news",
|
||||||
|
name: "news",
|
||||||
|
component: () => import("@/views/news/index.vue"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/events-calendar",
|
path: "/events-calendar",
|
||||||
name: "events-calendar",
|
name: "events-calendar",
|
||||||
@ -104,8 +121,33 @@ const routes = [
|
|||||||
name: "govern",
|
name: "govern",
|
||||||
component: () => import("@/views/govern/index.vue"),
|
component: () => import("@/views/govern/index.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/privacyPolicy",
|
||||||
|
name: "privacyPolicy",
|
||||||
|
component: () => import("@/views/footerLinks/privacyPolicy/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/termsOfUse",
|
||||||
|
name: "termsOfUse",
|
||||||
|
component: () => import("@/views/footerLinks/termsOfUse/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/cookiesSettings",
|
||||||
|
name: "cookiesSettings",
|
||||||
|
component: () => import("@/views/footerLinks/cookiesSettings/index.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/siteMap",
|
||||||
|
name: "siteMap",
|
||||||
|
component: () => import("@/views/footerLinks/siteMap/index.vue"),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/office',
|
||||||
|
name: 'office',
|
||||||
|
component: () => import('@/views/office/index.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// path: '/companyprofil',
|
// path: '/companyprofil',
|
||||||
|
@ -15,7 +15,7 @@ import { useRouter } from "vue-router";
|
|||||||
import { showImagePreview } from "vant";
|
import { showImagePreview } from "vant";
|
||||||
|
|
||||||
export const useAuth = createGlobalState(() => {
|
export const useAuth = createGlobalState(() => {
|
||||||
console.log("useRouter", useRouter);
|
// console.log("useRouter", useRouter);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const token = useStorage("token", "", localStorage);
|
const token = useStorage("token", "", localStorage);
|
||||||
const workUid = useStorage("workUid", "", localStorage);
|
const workUid = useStorage("workUid", "", localStorage);
|
||||||
@ -44,7 +44,7 @@ export const useAuth = createGlobalState(() => {
|
|||||||
const millisecondsIn48Hours = 48 * 60 * 60 * 1000;
|
const millisecondsIn48Hours = 48 * 60 * 60 * 1000;
|
||||||
voteToken.value.expireTime = currentTimestamp + millisecondsIn48Hours;
|
voteToken.value.expireTime = currentTimestamp + millisecondsIn48Hours;
|
||||||
voteToken.value.authorization = res.data?.authorization;
|
voteToken.value.authorization = res.data?.authorization;
|
||||||
console.log("voteToken", voteToken.value);
|
// console.log("voteToken", voteToken.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const sendVote = async () => {
|
const sendVote = async () => {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { createGlobalState, useLocalStorage } from '@vueuse/core'
|
import { createGlobalState, useLocalStorage } from '@vueuse/core'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import utc from 'dayjs/plugin/utc'
|
||||||
|
import timezone from 'dayjs/plugin/timezone'
|
||||||
|
|
||||||
export const useStockQuote = createGlobalState(() => {
|
export const useStockQuote = createGlobalState(() => {
|
||||||
const stockQuote = useLocalStorage('stockQuote', {
|
const stockQuote = useLocalStorage('stockQuote', {
|
||||||
@ -14,11 +17,65 @@ export const useStockQuote = createGlobalState(() => {
|
|||||||
""
|
""
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
const date = new Date();
|
||||||
|
const options = {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: true,
|
||||||
|
timeZone: 'America/New_York',
|
||||||
|
timeZoneName: 'short'
|
||||||
|
};
|
||||||
|
let lastTradingDay
|
||||||
|
dayjs.extend(utc)
|
||||||
|
dayjs.extend(timezone)
|
||||||
|
/*
|
||||||
|
美股的常规发行日(交易日)为周一至周五,遇到法定假日则顺延。
|
||||||
|
如果你只需要“上一个交易日”(不考虑法定假日)的情况下
|
||||||
|
获取当前美东时间。
|
||||||
|
如果今天是周一,则上一个交易日为上周五。
|
||||||
|
如果今天是周日,则上一个交易日为上周五。
|
||||||
|
如果今天是周六,则上一个交易日为周五。
|
||||||
|
其他情况,上一个交易日为昨天。
|
||||||
|
*/
|
||||||
|
|
||||||
|
const getLastTradingDay = async () => {
|
||||||
|
const toDate = dayjs().format('YYYY-MM-DD');
|
||||||
|
const finalFromDate = dayjs().subtract(7, 'day').format('YYYY-MM-DD');
|
||||||
|
let url =
|
||||||
|
'https://common.szjixun.cn/api/stock/history/list?from=' +
|
||||||
|
finalFromDate +
|
||||||
|
'&to=' +
|
||||||
|
toDate;
|
||||||
|
const res = await axios.get(url)
|
||||||
|
if (res.status === 200) {
|
||||||
|
if (res.data.status === 0) {
|
||||||
|
lastTradingDay = dayjs(res.data.data[0].date)
|
||||||
|
}
|
||||||
|
return lastTradingDay.format('MMM D, YYYY') + ' 4:00 PM EDT'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatted = ref(null)
|
||||||
|
const init = async () => {
|
||||||
|
formatted.value = await getLastTradingDay()
|
||||||
|
}
|
||||||
|
init()
|
||||||
const getStockQuate = async () => {
|
const getStockQuate = async () => {
|
||||||
const res = await axios.get('https://saas-test.szjixun.cn/api/chart/forward/test')
|
// const res = await axios.get('https://saas-test.szjixun.cn/api/fiee/chart/forward/test')
|
||||||
stockQuote.value=res.data
|
const res = await axios.get('https://common.szjixun.cn/api/stock/company/data')
|
||||||
|
// console.error(res)
|
||||||
|
if (res.status === 200) {
|
||||||
|
if (res.data.status === 0) {
|
||||||
|
stockQuote.value = res.data.data
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
formatted,
|
||||||
getStockQuate,
|
getStockQuate,
|
||||||
stockQuote
|
stockQuote
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,26 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import size1920 from "@/views/BusinessServices/size1920/index.vue";
|
|
||||||
import size375 from "@/views/BusinessServices/size375/index.vue";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { useWindowSize } from "@vueuse/core";
|
import { useWindowSize } from "@vueuse/core";
|
||||||
|
|
||||||
|
import size375 from "./size375/index.vue";
|
||||||
|
import size768 from "./size768/index.vue";
|
||||||
|
import size1440 from "./size1440/index.vue";
|
||||||
|
import size1920 from "./size1920/index.vue";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
265
src/views/BusinessServices/size1440/index.vue
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home-page">
|
||||||
|
<div class="business-page">
|
||||||
|
<!-- 渐变背景标题区 - 增加层次感 -->
|
||||||
|
<section class="hero-section">
|
||||||
|
<div class="container">
|
||||||
|
<h1 style="font-size: 40px" class="hero-title">
|
||||||
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.TITLE") }}
|
||||||
|
</h1>
|
||||||
|
<div style="font-size: 18px" class="hero-description">
|
||||||
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- 业务核心解决方案 -->
|
||||||
|
<main style="margin-top: 40px" class="container">
|
||||||
|
<section>
|
||||||
|
<h1 class="hero-title" style="font-size: 22px">
|
||||||
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="solution-grid">
|
||||||
|
<!-- 统一使用弹性列布局,通过媒体查询控制排列方式 -->
|
||||||
|
<div
|
||||||
|
v-for="(solution, sIndex) in solutions"
|
||||||
|
:key="sIndex"
|
||||||
|
class="featured-solution"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="solution-card"
|
||||||
|
:style="{ '--delay': `${sIndex * 0.2}s` }"
|
||||||
|
>
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="decorative-line"></div>
|
||||||
|
<h2 class="card-title">{{ solution.title }}</h2>
|
||||||
|
</div>
|
||||||
|
<ul class="card-content">
|
||||||
|
<li
|
||||||
|
v-for="(point, pIndex) in solution.points"
|
||||||
|
:key="pIndex"
|
||||||
|
class="content-point"
|
||||||
|
>
|
||||||
|
<div class="point-icon">•</div>
|
||||||
|
<div style="font-size: 18px" class="point-text">
|
||||||
|
{{ point }}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const solutions = computed(() => [
|
||||||
|
{
|
||||||
|
title: t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.ONE.TITLE"),
|
||||||
|
points: [
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.ONE.CONTENT"),
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.ONE.CONTENTTWO"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.TWO.TITLE"),
|
||||||
|
points: [
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.TWO.CONTENT"),
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.TWO.CONTENTTWO"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.THREE.TITLE"),
|
||||||
|
points: [
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.THREE.CONTENT"),
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.THREE.CONTENTTWO"),
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.THREE.CONTENTTHREE"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.FOUR.TITLE"),
|
||||||
|
points: [
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.FOUR.CONTENT"),
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.FOUR.CONTENTTWO"),
|
||||||
|
t("BusinessiIntroduction.CONTAIN.TITLEONE.paragraph.FOUR.CONTENTTHREE"),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 基础样式 */
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 40px;
|
||||||
|
color: black;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
animation: slideIn 1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-description {
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary-color: #895bff;
|
||||||
|
--primary-light: #a07cff;
|
||||||
|
--primary-dark: #6a11cb;
|
||||||
|
--primary-gradient: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--primary-light) 0%,
|
||||||
|
var(--primary-color) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.home-page {
|
||||||
|
background-image: url("@/assets/image/bg.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题区 - 紫色渐变 */
|
||||||
|
.hero-section {
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
padding: 5rem 0 0rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-decoration {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -15px;
|
||||||
|
left: 0;
|
||||||
|
width: 80%;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3rem;
|
||||||
|
margin-bottom: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 桌面端布局(>=768px) */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.solution-group {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-solution {
|
||||||
|
flex: 1;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端布局(<768px) */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.home-page {
|
||||||
|
background-image: url("@/assets/image/bg-mobile.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-group {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-solution {
|
||||||
|
width: 100% !important; /* 强制占满容器 */
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-card {
|
||||||
|
padding: 2rem;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-point {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片公共样式 */
|
||||||
|
.solution-card {
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 2.5rem;
|
||||||
|
transform: translateY(20px);
|
||||||
|
opacity: 0;
|
||||||
|
animation: cardEnter 0.6s ease forwards;
|
||||||
|
border: 1px solid rgba(137, 91, 255, 0.2);
|
||||||
|
background: linear-gradient(135deg, #f9f6ff 0%, #f0e9ff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decorative-line {
|
||||||
|
width: 50px;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-card:hover .decorative-line {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
color: #2c0850;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-point {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem 0;
|
||||||
|
border-bottom: 1px solid rgba(137, 91, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-icon {
|
||||||
|
color: #4a3a6b;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-text {
|
||||||
|
color: #4a3a6b;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cardEnter {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -3,67 +3,48 @@
|
|||||||
<div class="business-page">
|
<div class="business-page">
|
||||||
<!-- 渐变背景标题区 - 增加层次感 -->
|
<!-- 渐变背景标题区 - 增加层次感 -->
|
||||||
<section class="hero-section">
|
<section class="hero-section">
|
||||||
<div class="hero-content container">
|
<div class="container">
|
||||||
<div class="title-wrapper">
|
<h2 class="hero-title">
|
||||||
<h1 class="hero-title">
|
|
||||||
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.TITLE") }}
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.TITLE") }}
|
||||||
</h1>
|
</h2>
|
||||||
<div class="title-decoration"></div>
|
<div style="font-size: 18px" class="hero-description">
|
||||||
</div>
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}
|
||||||
<div class="hero-description">
|
|
||||||
<p>{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<!-- 业务核心解决方案 -->
|
||||||
<!-- 业务核心解决方案 - 重新设计布局 -->
|
<main style="margin-top: 40px" class="container">
|
||||||
<main class="container">
|
<section>
|
||||||
<p style="font-size: 18px; font-weight: bold">
|
<h1 style="font-size: 30px" class="hero-title">
|
||||||
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}
|
||||||
</p>
|
</h1>
|
||||||
<!-- 解决方案网格 - 新布局 -->
|
</section>
|
||||||
<div class="solution-grid">
|
|
||||||
<!-- 主推解决方案 -->
|
|
||||||
<div class="featured-solution">
|
|
||||||
<div class="solution-card" :style="{ '--delay': '0s' }">
|
|
||||||
<!-- <div class="card-badge">旗舰方案</div> -->
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="decorative-line"></div>
|
|
||||||
<h2 class="card-title">{{ solutions[0].title }}</h2>
|
|
||||||
</div>
|
|
||||||
<ul class="card-content">
|
|
||||||
<li
|
|
||||||
v-for="(point, pIndex) in solutions[0].points"
|
|
||||||
:key="pIndex"
|
|
||||||
class="content-point"
|
|
||||||
>
|
|
||||||
<div class="point-icon">➤</div>
|
|
||||||
<div class="point-text">{{ point }}</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 次要解决方案组 -->
|
<div class="solution-grid">
|
||||||
<div class="secondary-solutions">
|
<!-- 统一使用弹性列布局,通过媒体查询控制排列方式 -->
|
||||||
|
<div
|
||||||
|
v-for="(solution, sIndex) in solutions"
|
||||||
|
:key="sIndex"
|
||||||
|
class="featured-solution"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in solutions.slice(1)"
|
|
||||||
:key="index + 1"
|
|
||||||
class="solution-card"
|
class="solution-card"
|
||||||
:style="{ '--delay': (index + 1) * 0.1 + 's' }"
|
:style="{ '--delay': `${sIndex * 0.2}s` }"
|
||||||
>
|
>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="decorative-line"></div>
|
<div class="decorative-line"></div>
|
||||||
<h2 class="card-title">{{ item.title }}</h2>
|
<h2 class="card-title">{{ solution.title }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<ul class="card-content">
|
<ul class="card-content">
|
||||||
<li
|
<li
|
||||||
v-for="(point, pIndex) in item.points"
|
v-for="(point, pIndex) in solution.points"
|
||||||
:key="pIndex"
|
:key="pIndex"
|
||||||
class="content-point"
|
class="content-point"
|
||||||
>
|
>
|
||||||
<div class="point-icon">➤</div>
|
<div class="point-icon">•</div>
|
||||||
<div class="point-text">{{ point }}</div>
|
<div style="font-size: 18px" class="point-text">
|
||||||
|
{{ point }}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -115,51 +96,20 @@ const solutions = computed(() => [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.home-page {
|
|
||||||
background-image: url("@/assets/image/bg-mobile.png");
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
/* 基础样式 */
|
/* 基础样式 */
|
||||||
.container {
|
|
||||||
max-width: 1280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 标题区样式 */
|
|
||||||
.hero-section {
|
|
||||||
background: linear-gradient(135deg, #f8fbfe 0%, #e6f0ff 100%);
|
|
||||||
padding: 8rem 0 6rem;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
bottom: -50px;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100px;
|
|
||||||
background: white;
|
|
||||||
transform: skewY(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-title {
|
.hero-title {
|
||||||
font-size: 3.5rem;
|
font-size: 40px;
|
||||||
color: #2c3e50;
|
color: black;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
animation: slideIn 1s ease;
|
animation: slideIn 1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-description {
|
.hero-description {
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
color: #5a6d80;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@ -188,29 +138,12 @@ const solutions = computed(() => [
|
|||||||
/* 标题区 - 紫色渐变 */
|
/* 标题区 - 紫色渐变 */
|
||||||
.hero-section {
|
.hero-section {
|
||||||
background: var(--primary-gradient);
|
background: var(--primary-gradient);
|
||||||
padding: 10rem 0 8rem;
|
padding: 5rem 0 0rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-section::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><path fill="rgba(255,255,255,0.05)" d="M0,0 L100,0 L100,100 Q50,80 0,100 Z"></path></svg>')
|
|
||||||
no-repeat bottom/100% 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-title {
|
|
||||||
font-size: 3.5rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-decoration {
|
.title-decoration {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -15px;
|
bottom: -15px;
|
||||||
@ -221,96 +154,77 @@ const solutions = computed(() => [
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-description {
|
.solution-group {
|
||||||
max-width: 800px;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
line-height: 1.8;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 解决方案网格 */
|
|
||||||
.solution-grid {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 2.5rem;
|
gap: 3rem;
|
||||||
padding: 5rem 0;
|
margin-bottom: 4rem;
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.solution-grid::before {
|
/* 桌面端布局(>=768px) */
|
||||||
content: "";
|
@media (min-width: 768px) {
|
||||||
position: absolute;
|
.solution-group {
|
||||||
top: 0;
|
flex-direction: row;
|
||||||
left: 50%;
|
gap: 4rem;
|
||||||
transform: translateX(-50%);
|
}
|
||||||
width: 80%;
|
|
||||||
height: 100%;
|
.featured-solution {
|
||||||
opacity: 0.03;
|
flex: 1;
|
||||||
z-index: -1;
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端布局(<768px) */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.home-page {
|
||||||
|
background-image: url("@/assets/image/bg-mobile.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-group {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featured-solution {
|
||||||
|
width: 100% !important; /* 强制占满容器 */
|
||||||
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片设计 */
|
|
||||||
.solution-card {
|
.solution-card {
|
||||||
background: white;
|
padding: 2rem;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-point {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片公共样式 */
|
||||||
|
.solution-card {
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 2.5rem;
|
padding: 2.5rem;
|
||||||
box-shadow: 0 10px 40px rgba(137, 91, 255, 0.1);
|
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: cardEnter 0.6s ease forwards;
|
animation: cardEnter 0.6s ease forwards;
|
||||||
animation-delay: var(--delay);
|
|
||||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.1);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.solution-card::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(
|
|
||||||
135deg,
|
|
||||||
rgba(137, 91, 255, 0.03) 0%,
|
|
||||||
rgba(137, 91, 255, 0) 100%
|
|
||||||
);
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.solution-card:hover {
|
|
||||||
transform: translateY(-8px);
|
|
||||||
box-shadow: 0 15px 50px rgba(137, 91, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-solution .solution-card {
|
|
||||||
border: 1px solid rgba(137, 91, 255, 0.2);
|
border: 1px solid rgba(137, 91, 255, 0.2);
|
||||||
background: linear-gradient(135deg, #f9f6ff 0%, #f0e9ff 100%);
|
background: linear-gradient(135deg, #f9f6ff 0%, #f0e9ff 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-badge {
|
.card-header {
|
||||||
position: absolute;
|
margin-bottom: 2rem;
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
background: var(--primary-gradient);
|
|
||||||
color: white;
|
|
||||||
padding: 0.3rem 1.2rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 600;
|
|
||||||
box-shadow: 0 4px 12px rgba(137, 91, 255, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.decorative-line {
|
.decorative-line {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background: var(--primary-gradient);
|
background: var(--primary-gradient);
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1rem;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.solution-card:hover .decorative-line {
|
.solution-card:hover .decorative-line {
|
||||||
@ -318,87 +232,30 @@ const solutions = computed(() => [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1.6rem;
|
font-size: 1.3rem;
|
||||||
color: #2c0850;
|
color: #2c0850;
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-point {
|
.content-point {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1.2rem;
|
gap: 1rem;
|
||||||
padding: 1.2rem 0;
|
padding: 1rem 0;
|
||||||
border-bottom: 1px solid rgba(137, 91, 255, 0.1);
|
border-bottom: 1px solid rgba(137, 91, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.point-icon {
|
.point-icon {
|
||||||
color: var(--primary-color);
|
color: #4a3a6b;
|
||||||
font-size: 1.4rem;
|
font-size: 1.2rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-top: 0.1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.point-text {
|
.point-text {
|
||||||
color: #4a3a6b;
|
color: #4a3a6b;
|
||||||
line-height: 1.7;
|
line-height: 1.6;
|
||||||
font-size: 1.05rem;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-button {
|
|
||||||
background: var(--primary-gradient);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 0.8rem 1.8rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
box-shadow: 0 4px 15px rgba(137, 91, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-button:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 20px rgba(137, 91, 255, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 布局调整 */
|
|
||||||
.secondary-solutions {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.secondary-solutions {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.featured-solution {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.hero-section {
|
|
||||||
padding: 7rem 0 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-title {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary-solutions {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.solution-card {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 动画 */
|
|
||||||
@keyframes cardEnter {
|
@keyframes cardEnter {
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -3,64 +3,49 @@
|
|||||||
<div class="business-page">
|
<div class="business-page">
|
||||||
<!-- 渐变背景标题区 -->
|
<!-- 渐变背景标题区 -->
|
||||||
<section class="hero-section">
|
<section class="hero-section">
|
||||||
<div class="hero-content container">
|
<div class="container">
|
||||||
<div class="title-wrapper">
|
<h1 style="font-size: 40px" class="hero-title">
|
||||||
<h1 class="hero-title">
|
|
||||||
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.TITLE") }}
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.TITLE") }}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="title-decoration"></div>
|
<div style="font-size: 18px" class="hero-description">
|
||||||
</div>
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}
|
||||||
<div class="hero-description">
|
|
||||||
<p>{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}</p>
|
|
||||||
<p>{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- 业务核心解决方案 -->
|
<!-- 业务核心解决方案 -->
|
||||||
<main class="container">
|
<main style="margin-top: 40px" class="container">
|
||||||
<!-- 解决方案网格 -->
|
<section>
|
||||||
<div class="solution-grid">
|
<h1 style="font-size: 40px" class="hero-title">
|
||||||
<!-- 主推解决方案 -->
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}
|
||||||
<div class="featured-solution">
|
</h1>
|
||||||
<div class="solution-card" :style="{ '--delay': '0s' }">
|
</section>
|
||||||
<div class="card-header">
|
|
||||||
<div class="decorative-line"></div>
|
|
||||||
<h2 class="card-title">{{ solutions[0].title }}</h2>
|
|
||||||
</div>
|
|
||||||
<ul class="card-content">
|
|
||||||
<li
|
|
||||||
v-for="(point, pIndex) in solutions[0].points"
|
|
||||||
:key="pIndex"
|
|
||||||
class="content-point"
|
|
||||||
>
|
|
||||||
<div class="point-icon">➤</div>
|
|
||||||
<div class="point-text">{{ point }}</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 次要解决方案组 -->
|
<!-- 解决方案网格 - 响应式弹性布局 -->
|
||||||
<div class="secondary-solutions">
|
<div class="solution-grid">
|
||||||
|
<!-- 统一使用弹性列布局,通过媒体查询控制排列方式 -->
|
||||||
|
<div
|
||||||
|
v-for="(solution, sIndex) in solutions"
|
||||||
|
:key="sIndex"
|
||||||
|
class="featured-solution"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in solutions.slice(1)"
|
|
||||||
:key="index + 1"
|
|
||||||
class="solution-card"
|
class="solution-card"
|
||||||
:style="{ '--delay': (index + 1) * 0.1 + 's' }"
|
:style="{ '--delay': `${sIndex * 0.2}s` }"
|
||||||
>
|
>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="decorative-line"></div>
|
<div class="decorative-line"></div>
|
||||||
<h2 class="card-title">{{ item.title }}</h2>
|
<h2 class="card-title">{{ solution.title }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<ul class="card-content">
|
<ul class="card-content">
|
||||||
<li
|
<li
|
||||||
v-for="(point, pIndex) in item.points"
|
v-for="(point, pIndex) in solution.points"
|
||||||
:key="pIndex"
|
:key="pIndex"
|
||||||
class="content-point"
|
class="content-point"
|
||||||
>
|
>
|
||||||
<div class="point-icon">➤</div>
|
<div class="point-icon">•</div>
|
||||||
<div class="point-text">{{ point }}</div>
|
<div style="font-size: 18px" class="point-text">
|
||||||
|
{{ point }}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -112,7 +97,34 @@ const solutions = computed(() => [
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 基础变量定义 */
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题区 - 紫色渐变 */
|
||||||
|
.hero-section {
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
padding: 5rem 0 0rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 40px;
|
||||||
|
color: black;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
animation: slideIn 1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-description {
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #895bff;
|
--primary-color: #895bff;
|
||||||
--primary-light: #a07cff;
|
--primary-light: #a07cff;
|
||||||
@ -122,124 +134,73 @@ const solutions = computed(() => [
|
|||||||
var(--primary-light) 0%,
|
var(--primary-light) 0%,
|
||||||
var(--primary-color) 100%
|
var(--primary-color) 100%
|
||||||
);
|
);
|
||||||
--text-primary: #2c0850;
|
|
||||||
--text-secondary: #4a3a6b;
|
|
||||||
--text-light: #6c5ce7;
|
|
||||||
--bg-light: #f9f6ff;
|
|
||||||
--border-radius: 16px;
|
|
||||||
--box-shadow: 0 10px 30px rgba(137, 91, 255, 0.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 基础样式 */
|
.home-page {
|
||||||
.container {
|
background-image: url("@/assets/image/bg-mobile.png");
|
||||||
padding: 0 1.25rem;
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标题区样式 */
|
/* 解决方案网格 - 响应式布局 */
|
||||||
.hero-section {
|
|
||||||
background: var(--primary-gradient);
|
|
||||||
padding: 6rem 0 4rem;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
color: #895bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="none"><path fill="rgba(255,255,255,0.05)" d="M0,0 L100,0 L100,100 Q50,80 0,100 Z"></path></svg>')
|
|
||||||
no-repeat bottom/100% 30%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
bottom: -20px;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 40px;
|
|
||||||
background: white;
|
|
||||||
transform: skewY(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-title {
|
|
||||||
font-size: clamp(1.8rem, 5vw, 2.5rem);
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-decoration {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -10px;
|
|
||||||
left: 0;
|
|
||||||
width: 60%;
|
|
||||||
height: 3px;
|
|
||||||
background: rgba(255, 255, 255, 0.5);
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-description {
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.7;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 解决方案网格 */
|
|
||||||
.solution-grid {
|
.solution-grid {
|
||||||
display: flex;
|
padding: 0rem 0 4rem;
|
||||||
flex-direction: column;
|
display: grid;
|
||||||
gap: 1.5rem;
|
grid-template-columns: 1fr; /* 默认单列(移动端) */
|
||||||
padding: 3rem 0;
|
gap: 3rem;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 卡片设计 */
|
/* 中等屏幕(768px-1439px) - 单列布局 */
|
||||||
|
@media (min-width: 768px) and (max-width: 1439px) {
|
||||||
|
.solution-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 大屏幕(≥1440px) - 双列布局 */
|
||||||
|
@media (min-width: 1440px) {
|
||||||
|
.solution-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片样式 */
|
||||||
.solution-card {
|
.solution-card {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: var(--border-radius);
|
border-radius: 16px;
|
||||||
padding: 1.5rem;
|
padding: 2.5rem;
|
||||||
box-shadow: var(--box-shadow);
|
box-shadow: 0 10px 40px rgba(137, 91, 255, 0.1);
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: cardEnter 0.6s ease forwards;
|
animation: cardEnter 0.6s ease forwards;
|
||||||
animation-delay: var(--delay);
|
|
||||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.1);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid rgba(137, 91, 255, 0.1);
|
border: 1px solid rgba(137, 91, 255, 0.2);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.solution-card::after {
|
.solution-card::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 4px;
|
||||||
background: linear-gradient(
|
background: var(--primary-gradient);
|
||||||
135deg,
|
|
||||||
rgba(137, 91, 255, 0.03) 0%,
|
|
||||||
rgba(137, 91, 255, 0) 100%
|
|
||||||
);
|
|
||||||
z-index: -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.solution-card:hover {
|
.solution-card:hover {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-8px);
|
||||||
box-shadow: 0 15px 40px rgba(137, 91, 255, 0.2);
|
box-shadow: 0 15px 50px rgba(137, 91, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.featured-solution .solution-card {
|
.card-header {
|
||||||
background: var(--bg-light);
|
margin-bottom: 2rem;
|
||||||
border: 1px solid rgba(137, 91, 255, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.decorative-line {
|
.decorative-line {
|
||||||
width: 40px;
|
width: 50px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background: var(--primary-gradient);
|
background: var(--primary-gradient);
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@ -248,20 +209,19 @@ const solutions = computed(() => [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.solution-card:hover .decorative-line {
|
.solution-card:hover .decorative-line {
|
||||||
width: 60px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
color: var(--text-primary);
|
color: #2c0850;
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-point {
|
.content-point {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.8rem;
|
gap: 1rem;
|
||||||
padding: 0.8rem 0;
|
padding: 1rem 0;
|
||||||
border-bottom: 1px solid rgba(137, 91, 255, 0.1);
|
border-bottom: 1px solid rgba(137, 91, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,26 +230,17 @@ const solutions = computed(() => [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.point-icon {
|
.point-icon {
|
||||||
color: var(--primary-color);
|
color: #4a3a6b;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-top: 0.1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.point-text {
|
.point-text {
|
||||||
color: var(--text-secondary);
|
color: #4a3a6b;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
font-size: 0.95rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 布局调整 */
|
|
||||||
.secondary-solutions {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 动画 */
|
|
||||||
@keyframes cardEnter {
|
@keyframes cardEnter {
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|