Compare commits
163 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
@ -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>
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"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",
|
||||||
|
@ -29,6 +29,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
|
||||||
@ -2068,6 +2071,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'}
|
||||||
@ -6478,6 +6484,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
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
BIN
public/icon.png
BIN
public/icon.png
Binary file not shown.
Before Width: | Height: | Size: 105 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
src/assets/file/FiEE, Inc._Audit Committee Charter.pdf
Normal file
BIN
src/assets/file/FiEE, Inc._Audit Committee Charter.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/FiEE, Inc._Compensation Committee Charter.pdf
Normal file
BIN
src/assets/file/FiEE, Inc._Compensation Committee Charter.pdf
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf
Normal file
BIN
src/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/footer/FiEE, Inc. _ Site Map.pdf
Normal file
BIN
src/assets/file/footer/FiEE, Inc. _ Site Map.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf
Normal file
BIN
src/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2009-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2009-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2010-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2010-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2010-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2010-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2010-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2010-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2011-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2011-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2011-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2011-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2011-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2011-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2012-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2012-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2012-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2012-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2012-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2012-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2013-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2013-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2013-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2013-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2013-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2013-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2014-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2014-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2014-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2014-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2014-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2014-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2015-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2015-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2015-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2015-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2015-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2015-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2016-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2016-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2016-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2016-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2016-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2016-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2017-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2017-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2017-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2017-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2017-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2017-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2018-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2018-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2018-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2018-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2018-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2018-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2019-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2019-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2019-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2019-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2019-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2019-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2020-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2020-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2020-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2020-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2020-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2020-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2021-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2021-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2021-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2021-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2021-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2021-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2022-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2022-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2022-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2022-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2022-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2022-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2023-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2023-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2023-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2023-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2023-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2023-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2024-Q1.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2024-Q1.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2024-Q2.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2024-Q2.pdf
Normal file
Binary file not shown.
BIN
src/assets/file/quarterly/10Q 2024-Q3.pdf
Normal file
BIN
src/assets/file/quarterly/10Q 2024-Q3.pdf
Normal file
Binary file not shown.
BIN
src/assets/image/icon/echarts_markPointer.png
Normal file
BIN
src/assets/image/icon/echarts_markPointer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
src/assets/image/icon/icon-link.png
Normal file
BIN
src/assets/image/icon/icon-link.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/image/icon/icon-new.png
Normal file
BIN
src/assets/image/icon/icon-new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 72 KiB |
34
src/components/customEcharts/index.vue
Normal file
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
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
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;
|
|
||||||
padding: 24px 0;
|
|
||||||
color: #888;
|
|
||||||
// font-size: 15px;
|
|
||||||
font-size: 1.05rem;
|
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
letter-spacing: 1px;
|
|
||||||
border-top: 1px solid #ececec;
|
border-top: 1px solid #ececec;
|
||||||
z-index: 100;
|
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;
|
||||||
|
// font-size: 15px;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
padding: 1rem 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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>
|
||||||
</template>
|
</div>
|
||||||
|
</template>
|
||||||
<script setup></script>
|
|
||||||
|
<script setup>
|
||||||
<style scoped lang="scss">
|
import { useRouter } from "vue-router";
|
||||||
.custom-footer {
|
const router = useRouter();
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
import privacyPolicy from "@/assets/file/footer/FiEE, Inc. _ Privacy policy.pdf";
|
||||||
padding: 120px 0;
|
import termsOfUse from "@/assets/file/footer/FiEE, Inc. _ Terms of Use.pdf";
|
||||||
color: #888;
|
import siteMap from "@/assets/file/footer/FiEE, Inc. _ Site Map.pdf";
|
||||||
font-size: 75px;
|
|
||||||
background: #f7f8fa;
|
//点击跳转到对应的链接页面
|
||||||
letter-spacing: 5px;
|
const handleLink = (link) => {
|
||||||
border-top: 5px solid #ececec;
|
// if (link === "privacyPolicy") {
|
||||||
z-index: 100;
|
// 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: 1rem 0;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
background: #f7f8fa;
|
||||||
|
letter-spacing: 5px;
|
||||||
|
border-top: 5px solid #ececec;
|
||||||
|
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
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
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>
|
@ -48,15 +48,20 @@ export const useHeaderMenuConfig = () => {
|
|||||||
label: t("header_menu.financial_information.title"),
|
label: t("header_menu.financial_information.title"),
|
||||||
key: "financial_information",
|
key: "financial_information",
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
label: t("header_menu.financial_information.sec_filings"),
|
||||||
|
key: "sec_filings",
|
||||||
|
href: "/secfilings",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t("header_menu.financial_information.sec_filings"),
|
label: t("header_menu.financial_information.annual_reports"),
|
||||||
key: "sec_filings",
|
key: "annual_reports",
|
||||||
href: "/secfilings",
|
href: "/annualreports",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t("header_menu.financial_information.quarterly_results"),
|
label: t("header_menu.financial_information.quarterly_reports"),
|
||||||
key: "quarterly_results",
|
key: "quarterly_reports",
|
||||||
href: "/quarterlyresults",
|
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
35754
src/dict/secFiles.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -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,60 +607,60 @@ 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: MINM), 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:
|
||||||
"•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 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",
|
||||||
},
|
},
|
||||||
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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -674,31 +675,30 @@ export default {
|
|||||||
"FiEE, Inc.’s core solutions encompass the following four major categories.",
|
"FiEE, Inc.’s core solutions encompass the following four major categories.",
|
||||||
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.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,6 +121,26 @@ 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"),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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 getStockQuate= async()=>{
|
const date = new Date();
|
||||||
const res = await axios.get('https://saas-test.szjixun.cn/api/chart/forward/test')
|
const options = {
|
||||||
stockQuote.value=res.data
|
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 res = await axios.get('https://saas-test.szjixun.cn/api/fiee/chart/forward/test')
|
||||||
|
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
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") }}
|
</h2>
|
||||||
</h1>
|
<div style="font-size: 18px" class="hero-description">
|
||||||
<div class="title-decoration"></div>
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}
|
||||||
</div>
|
|
||||||
<div class="hero-description">
|
|
||||||
<p>{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<!-- 业务核心解决方案 -->
|
||||||
|
<main style="margin-top: 40px" class="container">
|
||||||
|
<section>
|
||||||
|
<h1 style="font-size: 30px" class="hero-title">
|
||||||
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- 业务核心解决方案 - 重新设计布局 -->
|
|
||||||
<main class="container">
|
|
||||||
<p style="font-size: 18px; font-weight: bold">
|
|
||||||
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENTTWO") }}
|
|
||||||
</p>
|
|
||||||
<!-- 解决方案网格 - 新布局 -->
|
|
||||||
<div class="solution-grid">
|
<div class="solution-grid">
|
||||||
<!-- 主推解决方案 -->
|
<!-- 统一使用弹性列布局,通过媒体查询控制排列方式 -->
|
||||||
<div class="featured-solution">
|
<div
|
||||||
<div class="solution-card" :style="{ '--delay': '0s' }">
|
v-for="(solution, sIndex) in solutions"
|
||||||
<!-- <div class="card-badge">旗舰方案</div> -->
|
:key="sIndex"
|
||||||
<div class="card-header">
|
class="featured-solution"
|
||||||
<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
|
<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 {
|
||||||
|
padding: 2rem;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-point {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 卡片公共样式 */
|
||||||
.solution-card {
|
.solution-card {
|
||||||
background: white;
|
|
||||||
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 style="font-size: 18px" class="hero-description">
|
||||||
<div class="title-decoration"></div>
|
{{ $t("BusinessiIntroduction.CONTAIN.TITLEONE.CONTENT") }}
|
||||||
</div>
|
|
||||||
<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;
|
||||||
|
251
src/views/BusinessServices/size768/index.vue
Normal file
251
src/views/BusinessServices/size768/index.vue
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
<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 style="font-size: 40px" class="hero-title">
|
||||||
|
{{ $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>
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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-mobile.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 解决方案网格 - 响应式布局 */
|
||||||
|
.solution-grid {
|
||||||
|
padding: 0rem 0 4rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr; /* 默认单列(移动端) */
|
||||||
|
gap: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中等屏幕(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 {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 2.5rem;
|
||||||
|
box-shadow: 0 10px 40px rgba(137, 91, 255, 0.1);
|
||||||
|
transform: translateY(20px);
|
||||||
|
opacity: 0;
|
||||||
|
animation: cardEnter 0.6s ease forwards;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(137, 91, 255, 0.2);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
.solution-card:hover {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
box-shadow: 0 15px 50px rgba(137, 91, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decorative-line {
|
||||||
|
width: 50px;
|
||||||
|
height: 3px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-point:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-icon {
|
||||||
|
color: #4a3a6b;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-text {
|
||||||
|
color: #4a3a6b;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cardEnter {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,14 +1,26 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import size1920 from "@/views/CommitteeAppointment/size1920/index.vue";
|
|
||||||
import size375 from "@/views/CommitteeAppointment/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;
|
||||||
}
|
}
|
||||||
|
373
src/views/CommitteeAppointment/size1440/index.vue
Normal file
373
src/views/CommitteeAppointment/size1440/index.vue
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
<template>
|
||||||
|
<div class="committees-page">
|
||||||
|
<!-- 标题区 -->
|
||||||
|
|
||||||
|
<div class="title mb-[50px] text-center">
|
||||||
|
<h1 style="font-size: 40px; margin-top: 60px">Committee Composition</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 委员会表格 -->
|
||||||
|
<div class="container">
|
||||||
|
<div class="committees-table">
|
||||||
|
<!-- 表头 - 委员会名称 -->
|
||||||
|
<div class="table-header">
|
||||||
|
<div class="director-cell"></div>
|
||||||
|
<div class="committee-cell">
|
||||||
|
<h3>Audit Committee</h3>
|
||||||
|
</div>
|
||||||
|
<div class="committee-cell">
|
||||||
|
<h3>Compensation Committee</h3>
|
||||||
|
</div>
|
||||||
|
<div class="committee-cell">
|
||||||
|
<h3>Nominating and Corporate Governance Committee</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格内容 - 每位董事 -->
|
||||||
|
<div
|
||||||
|
class="table-row"
|
||||||
|
v-for="(director, index) in otherDirectors"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<!-- 董事姓名 -->
|
||||||
|
<div class="director-cell">
|
||||||
|
<div class="director-info">
|
||||||
|
<div class="avatar"></div>
|
||||||
|
<div>
|
||||||
|
<router-link
|
||||||
|
:to="'/boarddirectors'"
|
||||||
|
style="font-size: 18px"
|
||||||
|
class="director-link"
|
||||||
|
>
|
||||||
|
{{ director.name }}
|
||||||
|
</router-link>
|
||||||
|
<!-- <p class="director-title">{{ director.title }}</p> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 委员会职位 -->
|
||||||
|
<div class="committee-cell">
|
||||||
|
<div class="role-badges">
|
||||||
|
<template v-if="getCommitteeRole(director.name, 'Audit')">
|
||||||
|
<div
|
||||||
|
class="role-badge"
|
||||||
|
:class="{
|
||||||
|
[getCommitteeRole(
|
||||||
|
director.name,
|
||||||
|
'Audit'
|
||||||
|
)?.toLowerCase()]: true,
|
||||||
|
chair: getCommitteeRole(director.name, 'Audit') === 'Chair',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ getCommitteeRole(director.name, "Audit") }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="committee-cell">
|
||||||
|
<div class="role-badges">
|
||||||
|
<template v-if="getCommitteeRole(director.name, 'Compensation')">
|
||||||
|
<div
|
||||||
|
class="role-badge"
|
||||||
|
:class="{
|
||||||
|
[getCommitteeRole(
|
||||||
|
director.name,
|
||||||
|
'Compensation'
|
||||||
|
)?.toLowerCase()]: true,
|
||||||
|
chair:
|
||||||
|
getCommitteeRole(director.name, 'Compensation') ===
|
||||||
|
'Chair',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ getCommitteeRole(director.name, "Compensation") }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="committee-cell">
|
||||||
|
<div class="role-badges">
|
||||||
|
<template v-if="getCommitteeRole(director.name, 'Governance')">
|
||||||
|
<div
|
||||||
|
class="role-badge"
|
||||||
|
:class="{
|
||||||
|
[getCommitteeRole(
|
||||||
|
director.name,
|
||||||
|
'Governance'
|
||||||
|
)?.toLowerCase()]: true,
|
||||||
|
chair:
|
||||||
|
getCommitteeRole(director.name, 'Governance') === 'Chair',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ getCommitteeRole(director.name, "Governance") }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const otherDirectors = [
|
||||||
|
// {
|
||||||
|
// name: "Cao Yu",
|
||||||
|
// title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
|
// },
|
||||||
|
// { name: "David Lazar", title: "Director" },
|
||||||
|
{ name: "Hu Bin", title: "Director" },
|
||||||
|
{ name: "David Natan", title: "Director" },
|
||||||
|
{ name: "Chan Oi Fat", title: "Director" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Updated committee roles according to requirements
|
||||||
|
const committeeRoles = {
|
||||||
|
"Cao Yu": {},
|
||||||
|
"David Lazar": {},
|
||||||
|
"Hu Bin": {
|
||||||
|
Audit: "Member",
|
||||||
|
Compensation: "Member",
|
||||||
|
Governance: "Member",
|
||||||
|
},
|
||||||
|
"David Natan": {
|
||||||
|
Audit: "Chair",
|
||||||
|
Compensation: "Member",
|
||||||
|
Governance: "Member",
|
||||||
|
},
|
||||||
|
"Chan Oi Fat": {
|
||||||
|
Audit: "Member",
|
||||||
|
Compensation: "Chair",
|
||||||
|
Governance: "Chair",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCommitteeRole = (name, committee) => {
|
||||||
|
return committeeRoles[name]?.[committee] || null;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.role-badge.chair {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
.title h1 {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -14px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 80px;
|
||||||
|
height: 3px;
|
||||||
|
background: #895bff;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 紫色主题变量 */
|
||||||
|
:root {
|
||||||
|
--primary: #895bff;
|
||||||
|
--primary-light: #a07cff;
|
||||||
|
--primary-dark: #6a11cb;
|
||||||
|
--primary-transparent: rgba(137, 91, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.committees-page {
|
||||||
|
background-image: url("@/assets/image/bg.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题区设计 */
|
||||||
|
.hero-section {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--primary-light) 0%,
|
||||||
|
var(--primary) 100%
|
||||||
|
);
|
||||||
|
padding: 6rem 2rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #895bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section h1 {
|
||||||
|
font-size: 2.8rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格设计 */
|
||||||
|
.committees-table {
|
||||||
|
margin: 4rem 0;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10px 30px rgba(137, 91, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header,
|
||||||
|
.table-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.5fr repeat(3, 1fr);
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
background: #f9f6ff;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.committee-cell {
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.committee-cell:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.committee-cell h3 {
|
||||||
|
color: var(--primary-dark);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-cell {
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-info h4 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 职位徽章设计 */
|
||||||
|
.role-badges {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
background: currentColor;
|
||||||
|
mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l5 5L20 7"/></svg>')
|
||||||
|
no-repeat center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 悬停效果 */
|
||||||
|
.table-row {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row:hover {
|
||||||
|
background: #fdfcff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 5px 15px rgba(137, 91, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.committees-table {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header,
|
||||||
|
.table-row {
|
||||||
|
grid-template-columns: 250px repeat(3, 200px);
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero-section {
|
||||||
|
padding: 4rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-section h1 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-info {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-cell {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.committee-cell {
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.director-link {
|
||||||
|
color: #895bff;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-link:hover {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,11 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="committees-page">
|
<div class="committees-page">
|
||||||
<!-- 标题区 -->
|
<!-- 标题区 -->
|
||||||
<section class="hero-section">
|
<div class="title mb-[50px] text-center">
|
||||||
<div class="container">
|
<h1 style="font-size: 40px; margin-top: 60px">Committee Composition</h1>
|
||||||
<h1>Committee Appointments</h1>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- 委员会表格 -->
|
<!-- 委员会表格 -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -35,10 +33,13 @@
|
|||||||
<div class="director-info">
|
<div class="director-info">
|
||||||
<div class="avatar"></div>
|
<div class="avatar"></div>
|
||||||
<div>
|
<div>
|
||||||
<router-link :to="'/boarddirectors'" class="director-link">
|
<router-link
|
||||||
|
:to="'/boarddirectors'"
|
||||||
|
style="font-size: 18px"
|
||||||
|
class="director-link"
|
||||||
|
>
|
||||||
{{ director.name }}
|
{{ director.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<!-- <p class="director-title">{{ director.title }}</p> -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,11 +50,15 @@
|
|||||||
<template v-if="getCommitteeRole(director.name, 'Audit')">
|
<template v-if="getCommitteeRole(director.name, 'Audit')">
|
||||||
<div
|
<div
|
||||||
class="role-badge"
|
class="role-badge"
|
||||||
:class="
|
:class="{
|
||||||
getCommitteeRole(director.name, 'Audit').toLowerCase()
|
[getCommitteeRole(
|
||||||
"
|
director.name,
|
||||||
|
'Audit'
|
||||||
|
)?.toLowerCase()]: true,
|
||||||
|
chair: getCommitteeRole(director.name, 'Audit') === 'Chair',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<span class="badge-icon"></span>
|
{{ getCommitteeRole(director.name, "Audit") }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -64,14 +69,17 @@
|
|||||||
<template v-if="getCommitteeRole(director.name, 'Compensation')">
|
<template v-if="getCommitteeRole(director.name, 'Compensation')">
|
||||||
<div
|
<div
|
||||||
class="role-badge"
|
class="role-badge"
|
||||||
:class="
|
:class="{
|
||||||
getCommitteeRole(
|
[getCommitteeRole(
|
||||||
director.name,
|
director.name,
|
||||||
'Compensation'
|
'Compensation'
|
||||||
).toLowerCase()
|
)?.toLowerCase()]: true,
|
||||||
"
|
chair:
|
||||||
|
getCommitteeRole(director.name, 'Compensation') ===
|
||||||
|
'Chair',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<span class="badge-icon"></span>
|
{{ getCommitteeRole(director.name, "Compensation") }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -82,11 +90,16 @@
|
|||||||
<template v-if="getCommitteeRole(director.name, 'Governance')">
|
<template v-if="getCommitteeRole(director.name, 'Governance')">
|
||||||
<div
|
<div
|
||||||
class="role-badge"
|
class="role-badge"
|
||||||
:class="
|
:class="{
|
||||||
getCommitteeRole(director.name, 'Governance').toLowerCase()
|
[getCommitteeRole(
|
||||||
"
|
director.name,
|
||||||
|
'Governance'
|
||||||
|
)?.toLowerCase()]: true,
|
||||||
|
chair:
|
||||||
|
getCommitteeRole(director.name, 'Governance') === 'Chair',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<span class="badge-icon"></span>
|
{{ getCommitteeRole(director.name, "Governance") }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -99,34 +112,34 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const otherDirectors = [
|
const otherDirectors = [
|
||||||
{
|
// {
|
||||||
name: "Cao Yu",
|
// name: "Cao Yu",
|
||||||
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
// title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
},
|
// },
|
||||||
{ name: "David Lazar", title: "Director" },
|
// { name: "David Lazar", title: "Director" },
|
||||||
{ name: "Hu Bin", title: "Director" },
|
{ name: "Hu Bin", title: "Director" },
|
||||||
{ name: "David Natan", title: "Director" },
|
{ name: "David Natan", title: "Director" },
|
||||||
{ name: "Chan Oi Fat", title: "Director" },
|
{ name: "Chan Oi Fat", title: "Director" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 模拟数据 - 实际应从API获取
|
// Updated committee roles according to requirements
|
||||||
const committeeRoles = {
|
const committeeRoles = {
|
||||||
"Cao Yu": {},
|
"Cao Yu": {},
|
||||||
"David Lazar": {},
|
"David Lazar": {},
|
||||||
"Hu Bin": {
|
"Hu Bin": {
|
||||||
Compensation: "Chair",
|
Audit: "Member",
|
||||||
|
Compensation: "Member",
|
||||||
Governance: "Member",
|
Governance: "Member",
|
||||||
Audit: "Chair",
|
|
||||||
},
|
},
|
||||||
"David Natan": {
|
"David Natan": {
|
||||||
Compensation: "Chair",
|
|
||||||
Governance: "Member",
|
|
||||||
Audit: "Chair",
|
Audit: "Chair",
|
||||||
|
Compensation: "Member",
|
||||||
|
Governance: "Member",
|
||||||
},
|
},
|
||||||
"Chan Oi Fat": {
|
"Chan Oi Fat": {
|
||||||
|
Audit: "Member",
|
||||||
Compensation: "Chair",
|
Compensation: "Chair",
|
||||||
Governance: "Member",
|
Governance: "Chair",
|
||||||
Audit: "Chair",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -137,6 +150,9 @@ const getCommitteeRole = (name, committee) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 紫色主题变量 */
|
/* 紫色主题变量 */
|
||||||
|
.role-badge.chair {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
:root {
|
:root {
|
||||||
--primary: #895bff;
|
--primary: #895bff;
|
||||||
--primary-light: #a07cff;
|
--primary-light: #a07cff;
|
||||||
@ -151,7 +167,26 @@ const getCommitteeRole = (name, committee) => {
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
.title h1 {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -14px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 80px;
|
||||||
|
height: 3px;
|
||||||
|
background: #895bff;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* 标题区设计 */
|
/* 标题区设计 */
|
||||||
.hero-section {
|
.hero-section {
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
@ -322,6 +357,8 @@ const getCommitteeRole = (name, committee) => {
|
|||||||
}
|
}
|
||||||
.director-link {
|
.director-link {
|
||||||
color: #895bff;
|
color: #895bff;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="board-members-page">
|
<div class="board-members-page">
|
||||||
<!-- 页面头部 -->
|
<!-- 页面头部 -->
|
||||||
<section class="hero-section">
|
<div class="title mb-[50px] text-center">
|
||||||
<div class="container">
|
<h1 style="font-size: 40px; margin-top: 60px">Committee Composition</h1>
|
||||||
<h1 class="page-title">Board of Directors</h1>
|
</div>
|
||||||
<p class="page-subtitle">Meet our leadership team</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- 移动端视图 -->
|
<!-- 移动端视图 -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div
|
<div
|
||||||
@ -28,7 +24,7 @@
|
|||||||
>
|
>
|
||||||
{{ director.name }}
|
{{ director.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<p class="director-title">{{ director.title }}</p>
|
<!-- <p class="director-title">{{ director.title }}</p> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -44,16 +40,18 @@
|
|||||||
v-for="(committee, idx) in getCommittees(director.name)"
|
v-for="(committee, idx) in getCommittees(director.name)"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
>
|
>
|
||||||
<div
|
<div class="committee-position">
|
||||||
class="role-badge"
|
<div
|
||||||
:class="
|
class="role-badge"
|
||||||
getCommitteeRole(director.name, committee).toLowerCase()
|
:class="
|
||||||
"
|
getCommitteeRole(director.name, committee).toLowerCase()
|
||||||
>
|
"
|
||||||
<span>{{ getCommitteeRole(director.name, committee) }}</span>
|
>
|
||||||
<span class="committee-name">{{
|
<span>{{ getCommitteeShortName(committee) }}</span>
|
||||||
getCommitteeShortName(committee)
|
</div>
|
||||||
}}</span>
|
<div style="font-size: 16px" class="role-title">
|
||||||
|
{{ getCommitteeRole(director.name, committee) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -69,35 +67,42 @@ import { ref, computed } from "vue";
|
|||||||
|
|
||||||
// 董事会成员数据
|
// 董事会成员数据
|
||||||
const otherDirectors = [
|
const otherDirectors = [
|
||||||
{
|
// {
|
||||||
name: "Cao Yu",
|
// name: "Cao Yu",
|
||||||
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
// title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
},
|
// },
|
||||||
{ name: "David Lazar", title: "Director" },
|
// { name: "David Lazar", title: "Director" },
|
||||||
{ name: "Hu Bin", title: "Director" },
|
{ name: "Hu Bin", title: "Director" },
|
||||||
{ name: "David Natan", title: "Director" },
|
{ name: "David Natan", title: "Director" },
|
||||||
{ name: "Chan Oi Fat", title: "Director" },
|
{ name: "Chan Oi Fat", title: "Director" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 委员会角色数据
|
// 委员会角色数据 - 现在包含职位类型 (Chair/Member)
|
||||||
const committeeRoles = {
|
const committeeRoles = {
|
||||||
"Cao Yu": {},
|
"Cao Yu": {},
|
||||||
"David Lazar": {},
|
"David Lazar": {},
|
||||||
"Hu Bin": {
|
"Hu Bin": {
|
||||||
Compensation: "Chair",
|
|
||||||
Governance: "Member",
|
|
||||||
Audit: "Member",
|
Audit: "Member",
|
||||||
|
Compensation: "Member",
|
||||||
|
Governance: "Member",
|
||||||
},
|
},
|
||||||
"David Natan": {
|
"David Natan": {
|
||||||
Compensation: "Member",
|
|
||||||
Governance: "Chair",
|
|
||||||
Audit: "Chair",
|
Audit: "Chair",
|
||||||
},
|
|
||||||
"Chan Oi Fat": {
|
|
||||||
Compensation: "Member",
|
Compensation: "Member",
|
||||||
Governance: "Member",
|
Governance: "Member",
|
||||||
Audit: "Member",
|
|
||||||
},
|
},
|
||||||
|
"Chan Oi Fat": {
|
||||||
|
Audit: "Member",
|
||||||
|
Compensation: "Chair",
|
||||||
|
Governance: "Chair",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 委员会完整名称
|
||||||
|
const committeeFullNames = {
|
||||||
|
Audit: "Audit Committee",
|
||||||
|
Compensation: "Compensation Committee",
|
||||||
|
Governance: "Nominating and Corporate Governance Committee",
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取委员会列表
|
// 获取委员会列表
|
||||||
@ -105,7 +110,7 @@ const getCommittees = (name) => {
|
|||||||
return Object.keys(committeeRoles[name] || {});
|
return Object.keys(committeeRoles[name] || {});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取委员会角色
|
// 获取委员会角色 (Chair/Member)
|
||||||
const getCommitteeRole = (name, committee) => {
|
const getCommitteeRole = (name, committee) => {
|
||||||
return committeeRoles[name]?.[committee] || "";
|
return committeeRoles[name]?.[committee] || "";
|
||||||
};
|
};
|
||||||
@ -131,6 +136,60 @@ const getInitials = (name) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 添加这些样式来显示职位类型 */
|
||||||
|
.committee-position {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 4px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 保持原有的角色徽章样式 */
|
||||||
|
.role-badge {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge.chair {
|
||||||
|
background-color: #e6f2ff;
|
||||||
|
color: #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge.member {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.title h1 {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -14px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 80px;
|
||||||
|
height: 3px;
|
||||||
|
background: #895bff;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* 基础变量 */
|
/* 基础变量 */
|
||||||
:root {
|
:root {
|
||||||
--primary: #895bff;
|
--primary: #895bff;
|
||||||
@ -144,14 +203,16 @@ const getInitials = (name) => {
|
|||||||
|
|
||||||
/* 页面样式 */
|
/* 页面样式 */
|
||||||
.board-members-page {
|
.board-members-page {
|
||||||
background-color: #faf9ff;
|
background-image: url("@/assets/image/bg-mobile.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 768px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 头部样式 */
|
/* 头部样式 */
|
||||||
@ -163,7 +224,7 @@ const getInitials = (name) => {
|
|||||||
);
|
);
|
||||||
padding: 3rem 1rem;
|
padding: 3rem 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: #895bff;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
337
src/views/CommitteeAppointment/size768/index.vue
Normal file
337
src/views/CommitteeAppointment/size768/index.vue
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
<template>
|
||||||
|
<div class="board-members-page">
|
||||||
|
<!-- 页面头部 -->
|
||||||
|
<div class="title mb-[50px] text-center">
|
||||||
|
<h1 style="font-size: 40px; margin-top: 60px">Committee Composition</h1>
|
||||||
|
</div>
|
||||||
|
<!-- 移动端视图 -->
|
||||||
|
<div class="container">
|
||||||
|
<div
|
||||||
|
class="director-card"
|
||||||
|
v-for="(director, index) in otherDirectors"
|
||||||
|
:key="director.name"
|
||||||
|
:style="{ '--delay': index * 0.1 + 's' }"
|
||||||
|
>
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="director-info">
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="initials">{{ getInitials(director.name) }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<router-link
|
||||||
|
:to="`/boarddirectors/${director.name}`"
|
||||||
|
class="director-name"
|
||||||
|
>
|
||||||
|
{{ director.name }}
|
||||||
|
</router-link>
|
||||||
|
<!-- <p class="director-title">{{ director.title }}</p> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="committee-groups">
|
||||||
|
<!-- 委员会职位 -->
|
||||||
|
<div
|
||||||
|
class="committee-group"
|
||||||
|
v-if="getCommittees(director.name).length > 0"
|
||||||
|
>
|
||||||
|
<div class="role-badges">
|
||||||
|
<template
|
||||||
|
v-for="(committee, idx) in getCommittees(director.name)"
|
||||||
|
:key="idx"
|
||||||
|
>
|
||||||
|
<div class="committee-position">
|
||||||
|
<div
|
||||||
|
class="role-badge"
|
||||||
|
:class="
|
||||||
|
getCommitteeRole(director.name, committee).toLowerCase()
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span>{{ getCommitteeShortName(committee) }}</span>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 16px" class="role-title">
|
||||||
|
{{ getCommitteeRole(director.name, committee) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
|
// 董事会成员数据
|
||||||
|
const otherDirectors = [
|
||||||
|
// {
|
||||||
|
// name: "Cao Yu",
|
||||||
|
// title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
|
// },
|
||||||
|
// { name: "David Lazar", title: "Director" },
|
||||||
|
{ name: "Hu Bin", title: "Director" },
|
||||||
|
{ name: "David Natan", title: "Director" },
|
||||||
|
{ name: "Chan Oi Fat", title: "Director" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 委员会角色数据 - 现在包含职位类型 (Chair/Member)
|
||||||
|
const committeeRoles = {
|
||||||
|
"Cao Yu": {},
|
||||||
|
"David Lazar": {},
|
||||||
|
"Hu Bin": {
|
||||||
|
Audit: "Member",
|
||||||
|
Compensation: "Member",
|
||||||
|
Governance: "Member",
|
||||||
|
},
|
||||||
|
"David Natan": {
|
||||||
|
Audit: "Chair",
|
||||||
|
Compensation: "Member",
|
||||||
|
Governance: "Member",
|
||||||
|
},
|
||||||
|
"Chan Oi Fat": {
|
||||||
|
Audit: "Member",
|
||||||
|
Compensation: "Chair",
|
||||||
|
Governance: "Chair",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 委员会完整名称
|
||||||
|
const committeeFullNames = {
|
||||||
|
Audit: "Audit Committee",
|
||||||
|
Compensation: "Compensation Committee",
|
||||||
|
Governance: "Nominating and Corporate Governance Committee",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取委员会列表
|
||||||
|
const getCommittees = (name) => {
|
||||||
|
return Object.keys(committeeRoles[name] || {});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取委员会角色 (Chair/Member)
|
||||||
|
const getCommitteeRole = (name, committee) => {
|
||||||
|
return committeeRoles[name]?.[committee] || "";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取委员会简称
|
||||||
|
const getCommitteeShortName = (committee) => {
|
||||||
|
const names = {
|
||||||
|
Audit: "Audit",
|
||||||
|
Compensation: "Comp.",
|
||||||
|
Governance: "Governance",
|
||||||
|
};
|
||||||
|
return names[committee] || committee;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取姓名首字母
|
||||||
|
const getInitials = (name) => {
|
||||||
|
return name
|
||||||
|
.split(" ")
|
||||||
|
.map((word) => word[0])
|
||||||
|
.join("")
|
||||||
|
.toUpperCase();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 添加这些样式来显示职位类型 */
|
||||||
|
.committee-position {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 4px;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 保持原有的角色徽章样式 */
|
||||||
|
.role-badge {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge.chair {
|
||||||
|
background-color: #e6f2ff;
|
||||||
|
color: #0066cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge.member {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.title h1 {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: -14px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 80px;
|
||||||
|
height: 3px;
|
||||||
|
background: #895bff;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* 基础变量 */
|
||||||
|
:root {
|
||||||
|
--primary: #895bff;
|
||||||
|
--primary-light: #a07cff;
|
||||||
|
--primary-dark: #6a11cb;
|
||||||
|
--text-primary: #333;
|
||||||
|
--text-secondary: #666;
|
||||||
|
--bg-light: #f9f6ff;
|
||||||
|
--border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面样式 */
|
||||||
|
.board-members-page {
|
||||||
|
background-image: url("@/assets/image/bg-mobile.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 0 16px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 头部样式 */
|
||||||
|
.hero-section {
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--primary-light) 0%,
|
||||||
|
var(--primary) 100%
|
||||||
|
);
|
||||||
|
padding: 3rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #895bff;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: clamp(1.75rem, 5vw, 2.25rem);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-subtitle {
|
||||||
|
font-size: clamp(1rem, 3vw, 1.25rem);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 董事卡片 */
|
||||||
|
.director-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: 0 5px 20px rgba(137, 91, 255, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateY(20px);
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 0.5s ease forwards;
|
||||||
|
animation-delay: var(--delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
padding: 1.25rem;
|
||||||
|
background: var(--bg-light);
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: var(--primary-transparent);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.initials {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-name {
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 委员会职位 */
|
||||||
|
.committee-groups {
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badges {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge {
|
||||||
|
padding: 0.35rem 0.75rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
background: rgba(137, 91, 255, 0.08);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-badge.chair {
|
||||||
|
background: rgba(137, 91, 255, 0.15);
|
||||||
|
color: var(--primary-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.committee-name {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
</style>
|
137
src/views/boarddirectors/size1440/index.vue
Normal file
137
src/views/boarddirectors/size1440/index.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home-page">
|
||||||
|
<div class="directors-page">
|
||||||
|
<h1 class="page-title">Board of Directors</h1>
|
||||||
|
<n-divider />
|
||||||
|
|
||||||
|
<div class="directors-list">
|
||||||
|
<div
|
||||||
|
v-for="(director, index) in otherDirectors"
|
||||||
|
:key="index"
|
||||||
|
class="director-item"
|
||||||
|
>
|
||||||
|
<n-h2 style="font-size: 18px" class="director-name">{{
|
||||||
|
director.name
|
||||||
|
}}</n-h2>
|
||||||
|
<n-text style="font-size: 16px" class="director-title">{{
|
||||||
|
director.title
|
||||||
|
}}</n-text>
|
||||||
|
<n-divider class="divider" />
|
||||||
|
<n-p class="director-bio">{{ director.contain }}</n-p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const otherDirectors = [
|
||||||
|
{
|
||||||
|
name: "Hu Bin",
|
||||||
|
title: "Chairman of the Board of Directors",
|
||||||
|
contain:
|
||||||
|
"Served as a director of DC International Service Trade GmbH since December 2024. Prior to that, Mr. Hu worked as a freelancer in the tourism industry from April 2001 to October 2024. From April 1994 to October 2000, he served as the general manager of Suzhou Wintime Advertising Co., Ltd. Before that, he served as the general manager of Suzhou Bauhaus Advertising Design Co., Ltd. from August 1992 to April 1994, where he was engaged in computer-aided design and 3D computer animation production. Mr. Hu began his career at Suzhou Advertising Company in October 1989, where he worked as a designer responsible for graphic design, platemaking, printing, and interior decoration. Mr. Hu graduated from Suzhou Academy of Arts in 1989.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cao Yu",
|
||||||
|
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
|
contain:
|
||||||
|
"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.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "David E. Lazar",
|
||||||
|
title: "Director",
|
||||||
|
contain:
|
||||||
|
"Served as the Chief Executive Officer of OpGen, Inc., a precision medicine company listed on the Nasdaq (OPGN) since April 11, 2024, where he also servs as a director and board chairman, beginning on March 25, 2024. Mr. Lazar served as the Chief Executive Officer of Titan Pharmaceuticals Inc. listed on the Nasdaq (TTNP) from August 2022 through April 11, 2024, where he also served as a director and board chairman from August 2022 until October 2023. He has also served as the CEO of Custodian Ventures LLC, a company which specializes in assisting distressed public companies through custodianship, since February 2018, and Activist Investing LLC, an actively managed private investment fund, since March 2018. Previously, Mr. Lazar served as Managing Partner at Zenith Partners International Inc., a boutique consulting firm, from July 2012 to April 2018. In his role as Chief Executive Officer of Custodian Ventures LLC, Mr. Lazar has successfully served as a custodian to numerous public companies across a wide range of industries.",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "David Natan",
|
||||||
|
title: "Director",
|
||||||
|
contain:
|
||||||
|
"Currently serves as President and Chief Executive Officer of Natan & Associates, LLC, a consulting firm offering chief financial officer services to public and private companies in a variety of industries, since 2007. Mr. Natan previously served as a Director of the Company from November 2023 to February 2025. From February 2010 to May 2020, Mr. Natan served as Chief Executive Officer of ForceField Energy, Inc. (OTCMKTS: FNRG), a company focused on the solar industry and LED lighting products. From February 2002 to November 2007, Mr. Natan served as Executive Vice President of Reporting and Chief Financial Officer of PharmaNet Development Group, Inc., a drug development services company, and, from June 1995 to February 2002, as Chief Financial Officer and Vice President of Global Technovations, Inc., a manufacturer and marketer of oil analysis instruments and speakers and speaker components. Prior to that, Mr. Natan served in various roles of increasing responsibility with Deloitte & Touche LLP, a global consulting firm. Mr. Natan currently serves as a member of the Board of Directors and Chair of the Audit Committee of Sunshine Biopharma, Inc. (Nasdaq: SBFM), a pharmaceutical and nutritional supplement company, since February 2022. Previously, Mr. Natan has served as a director for the following public companies: Global Technovations, Forcefield Energy, Titan Pharmaceuticals (Nasdaq: TTNP), Vivakor Inc. (Nasdaq: VIVK), NetBrands Corp. (OTC: NBND), and OpGen Inc. (OTC: OPGN), and Cyclacel Pharmaceuticals (Nasdaq: CYCC). Mr. Natan holds a B.A. in Economics from Boston University.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Chan Oi Fat",
|
||||||
|
title: "Director",
|
||||||
|
contain:
|
||||||
|
"Served as Vice President – Finance of SML Group Corporation since March 2018 and as Company Secretary of China Leon Inspection Holding Limited (HKEX: 1586) since February 2018 and of Raily Aesthetic Medicine International Holdings Limited (HKEX: 2135) since November 2020. He is an independent non-executive director of Huajin International Holdings Limited (HKEX: 2738) (since March 2025) and UBoT Holding Limited (HKEX GEM: 8529) (since May 2024) and previously served as an independent non-executive director of China Saftower International Holding Group Limited (HKEX GEM: 8623) from June 2020 to December 2023 and Shanghai Prime Machinery Company Limited (HKEX: 2345) from June 2014 to January 2021. Mr. Chan holds a B.B.A. (Hons) in Accountancy from the City University of Hong Kong (2000) and is a member of the Association of Chartered Certified Accountants (since 2003) and the Hong Kong Institute of Certified Public Accountants (since 2004).",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-page {
|
||||||
|
background-image: url("@/assets/image/bg.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
.directors-page {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 60px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 2.5rem; /* 18px */
|
||||||
|
margin-bottom: 30px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.directors-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 48px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-item {
|
||||||
|
padding-bottom: 48px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-name {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin: 16px 0;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-bio {
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #4a4a4a;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.directors-page {
|
||||||
|
padding: 80px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-name {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home-page">
|
<div class="home-page">
|
||||||
<div class="directors-page">
|
<div class="directors-page">
|
||||||
<n-h1 class="page-title">Board of Directors</n-h1>
|
<h1 class="page-title">Board of Directors</h1>
|
||||||
<n-divider />
|
<n-divider />
|
||||||
|
|
||||||
<div class="directors-list">
|
<div class="directors-list">
|
||||||
@ -22,35 +22,36 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const otherDirectors = [
|
const otherDirectors = [
|
||||||
|
{
|
||||||
|
name: "Hu Bin",
|
||||||
|
title: "Chairman of the Board of Directors",
|
||||||
|
contain:
|
||||||
|
"Served as a director of DC International Service Trade GmbH since December 2024. Prior to that, Mr. Hu worked as a freelancer in the tourism industry from April 2001 to October 2024. From April 1994 to October 2000, he served as the general manager of Suzhou Wintime Advertising Co., Ltd. Before that, he served as the general manager of Suzhou Bauhaus Advertising Design Co., Ltd. from August 1992 to April 1994, where he was engaged in computer-aided design and 3D computer animation production. Mr. Hu began his career at Suzhou Advertising Company in October 1989, where he worked as a designer responsible for graphic design, platemaking, printing, and interior decoration. Mr. Hu graduated from Suzhou Academy of Arts in 1989.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Cao Yu",
|
name: "Cao Yu",
|
||||||
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
contain:
|
contain:
|
||||||
"Cao Yu, age 34, previously served as the treasury director of Taifeng Cultural Communication Co., Ltd where she oversees its financial matters fromNovember 2018 to November 2024. Prior to that, Ms. Cao served as a business manager of Yangfeng Art Exchange Co., Ltd from February 2016 toOctober 2018. From March 2011 to January 2016, she served as the treasury officer of financial department of Suzhou Industrial Park Xinfushida PlasticProfile Products Co., Ltd.",
|
"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.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David Lazar",
|
name: "David E. Lazar",
|
||||||
title: "Director",
|
title: "Director",
|
||||||
contain:
|
contain:
|
||||||
"David E. Lazar, age 34, has served as the Chief Executive Officer of OpGen, Inc., a precision medicine company listed on the Nasdaq (OPGN) since April11, 2024, where he also servs as a director and board chairman, beginning on March 25, 2024. Mr. Lazar served as the Chief Executive Officer of TitanPharmaceuticals Inc. listed on the Nasdaq (TTNP) from August 2022 through April 11, 2024, where he also served as a director and board chairman fromAugust 2022 until October 2023. He has also served as the CEO of Custodian Ventures LLC, a company which specializes in assisting distressed publiccompanies through custodianship, since February 2018, and Activist Investing LLC, an actively managed private investment fund, since March 2018.Previously, Mr. Lazar served as Managing Partner at Zenith Partners International Inc., a boutique consulting firm, from July 2012 to April 2018. In his roleas Chief Eecutive Officer of Custodian Ventures LLC, Mr. Lazar has successfully served as a custodian to numerous public companies across a widerange of industries.",
|
"Served as the Chief Executive Officer of OpGen, Inc., a precision medicine company listed on the Nasdaq (OPGN) since April 11, 2024, where he also servs as a director and board chairman, beginning on March 25, 2024. Mr. Lazar served as the Chief Executive Officer of Titan Pharmaceuticals Inc. listed on the Nasdaq (TTNP) from August 2022 through April 11, 2024, where he also served as a director and board chairman from August 2022 until October 2023. He has also served as the CEO of Custodian Ventures LLC, a company which specializes in assisting distressed public companies through custodianship, since February 2018, and Activist Investing LLC, an actively managed private investment fund, since March 2018. Previously, Mr. Lazar served as Managing Partner at Zenith Partners International Inc., a boutique consulting firm, from July 2012 to April 2018. In his role as Chief Executive Officer of Custodian Ventures LLC, Mr. Lazar has successfully served as a custodian to numerous public companies across a wide range of industries.",
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Hu Bin",
|
|
||||||
title: "Director",
|
|
||||||
contain:
|
|
||||||
"Hu Bin,age 55, has served as a director of DC International Service Trade GmbH since December 2024. Prior to that, Mr. Hu worked as a freelancer in thetourism industry from April 2001 to October 2024. From April 1994 to October 2000, he served as the general manager of Suzhou Wintime AdvertisingCo., Ltd. Before that, he served as the general manager of Suzhou Bauhaus Advertising Design Co., Ltd. from August 1992 to April 1994, where he wasengaged in computer-aided design and 3D computer animation production. Mr. Hu began his career at Suzhou Advertising Company in October 1989,where he worked as a designer responsible for graphic design, platemaking, printing, and interior decoration. Mr. Hu graduated from Suzhou Academy of Arts in 1989.",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "David Natan",
|
name: "David Natan",
|
||||||
title: "Director",
|
title: "Director",
|
||||||
contain:
|
contain:
|
||||||
"David Natan,age 72, currently serves as President and Chief Executive Officer of Natan & Associates, LLC, a consulting firm offering chief financialofficer services to public and private companies in a variety of industries, since 2007. Mr. Natan previously served as a Director of the Company fromNovember 2023 to February 2025. From February 2010 to May 2020, Mr. Natan served as Chief Executive Officer of ForceField Energy, Inc.(OTCMKTS: FNRG), a company focused on the solar industry and LED lighting products. From February 2002 to November 2007, Mr. Natan served asExecutive Vice President of Reporting and Chief Financial Officer of PharmaNet Development Group, Inc., a drug development services company, and,from June 1995 to February 2002, as Chief Financial Officer and Vice President of Global Technovations, Inc., a manufacturer and marketer of oil analysisinstruments and speakers and speaker components. Prior to that, Mr. Natan served in various roles of increasing responsibility with Deloitte & Touche LLP,a global consulting firm. Mr. Natan currently serves as a member of the Board of Directors and Chair of the Audit Committee of Sunshine Biopharma, Inc.(Nasdaq: SBFM), a pharmaceutical and nutritional supplement company, since February 2022. Previously, Mr. Natan has served as a director for thefollowing public companies: Global Technovations, Forcefield Energy, Titan Pharmaceuticals (Nasdaq: TTNP), Vivakor Inc. (Nasdaq: VIVK), NetBrandsCorp. (OTC: NBND), and OpGen Inc. (OTC: OPGN), and Cyclacel Pharmaceuticals (Nasdaq: CYCC). Mr. Natan holds a B.A. in Economics from Boston University.",
|
"Currently serves as President and Chief Executive Officer of Natan & Associates, LLC, a consulting firm offering chief financial officer services to public and private companies in a variety of industries, since 2007. Mr. Natan previously served as a Director of the Company from November 2023 to February 2025. From February 2010 to May 2020, Mr. Natan served as Chief Executive Officer of ForceField Energy, Inc. (OTCMKTS: FNRG), a company focused on the solar industry and LED lighting products. From February 2002 to November 2007, Mr. Natan served as Executive Vice President of Reporting and Chief Financial Officer of PharmaNet Development Group, Inc., a drug development services company, and, from June 1995 to February 2002, as Chief Financial Officer and Vice President of Global Technovations, Inc., a manufacturer and marketer of oil analysis instruments and speakers and speaker components. Prior to that, Mr. Natan served in various roles of increasing responsibility with Deloitte & Touche LLP, a global consulting firm. Mr. Natan currently serves as a member of the Board of Directors and Chair of the Audit Committee of Sunshine Biopharma, Inc. (Nasdaq: SBFM), a pharmaceutical and nutritional supplement company, since February 2022. Previously, Mr. Natan has served as a director for the following public companies: Global Technovations, Forcefield Energy, Titan Pharmaceuticals (Nasdaq: TTNP), Vivakor Inc. (Nasdaq: VIVK), NetBrands Corp. (OTC: NBND), and OpGen Inc. (OTC: OPGN), and Cyclacel Pharmaceuticals (Nasdaq: CYCC). Mr. Natan holds a B.A. in Economics from Boston University.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Chan Oi Fat",
|
name: "Chan Oi Fat",
|
||||||
title: "Director",
|
title: "Director",
|
||||||
contain:
|
contain:
|
||||||
"Chan Oi Fat, age 46, has served as Vice President – Finance of SML Group Corporation since March 2018 and as Company Secretary of China LeonInspection Holding Limited (HKEX: 1586) since February 2018 and of Raily Aesthetic Medicine International Holdings Limited (HKEX: 2135) sinceNovember 2020. He is an independent non-executive director of Huajin International Holdings Limited (HKEX: 2738) (since March 2025) and UBoTHolding Limited (HKEX GEM: 8529) (since May 2024) and previously served as an independent non-executive director of China Saftower InternationalHolding Group Limited (HKEX GEM: 8623) from June 2020 to December 2023 and Shanghai Prime Machinery Company Limited (HKEX: 2345) fromJune 2014 to January 2021. Mr. Chan holds a B.B.A. (Hons) in Accountancy from the City University of Hong Kong (2000) and is a member of theAssociation of Chartered Certified Accountants (since 2003) and the Hong Kong Institute of Certified Public Accountants (since 2004).",
|
"Served as Vice President – Finance of SML Group Corporation since March 2018 and as Company Secretary of China Leon Inspection Holding Limited (HKEX: 1586) since February 2018 and of Raily Aesthetic Medicine International Holdings Limited (HKEX: 2135) since November 2020. He is an independent non-executive director of Huajin International Holdings Limited (HKEX: 2738) (since March 2025) and UBoT Holding Limited (HKEX GEM: 8529) (since May 2024) and previously served as an independent non-executive director of China Saftower International Holding Group Limited (HKEX GEM: 8623) from June 2020 to December 2023 and Shanghai Prime Machinery Company Limited (HKEX: 2345) from June 2014 to January 2021. Mr. Chan holds a B.B.A. (Hons) in Accountancy from the City University of Hong Kong (2000) and is a member of the Association of Chartered Certified Accountants (since 2003) and the Hong Kong Institute of Certified Public Accountants (since 2004).",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
@ -69,10 +70,9 @@ const otherDirectors = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
text-align: center;
|
font-size: 2.5rem; /* 18px */
|
||||||
margin-bottom: 16px;
|
margin-bottom: 30px;
|
||||||
font-weight: 500;
|
color: #333;
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.directors-list {
|
.directors-list {
|
||||||
|
@ -7,14 +7,11 @@
|
|||||||
<div class="directors-list">
|
<div class="directors-list">
|
||||||
<div
|
<div
|
||||||
v-for="(director, index) in otherDirectors"
|
v-for="(director, index) in otherDirectors"
|
||||||
:key="director.name"
|
:key="index"
|
||||||
class="director-item"
|
class="director-item"
|
||||||
v-motion-fade
|
|
||||||
>
|
>
|
||||||
<div class="director-header">
|
<n-h2 class="director-name">{{ director.name }}</n-h2>
|
||||||
<n-h2 class="director-name">{{ director.name }}</n-h2>
|
<n-text class="director-title">{{ director.title }}</n-text>
|
||||||
<n-text class="director-title">{{ director.title }}</n-text>
|
|
||||||
</div>
|
|
||||||
<n-divider class="divider" />
|
<n-divider class="divider" />
|
||||||
<n-p class="director-bio">{{ director.contain }}</n-p>
|
<n-p class="director-bio">{{ director.contain }}</n-p>
|
||||||
</div>
|
</div>
|
||||||
@ -25,74 +22,79 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const otherDirectors = [
|
const otherDirectors = [
|
||||||
|
{
|
||||||
|
name: "Hu Bin",
|
||||||
|
title: "Chairman of the Board of Directors",
|
||||||
|
contain:
|
||||||
|
"Served as a director of DC International Service Trade GmbH since December 2024. Prior to that, Mr. Hu worked as a freelancer in the tourism industry from April 2001 to October 2024. From April 1994 to October 2000, he served as the general manager of Suzhou Wintime Advertising Co., Ltd. Before that, he served as the general manager of Suzhou Bauhaus Advertising Design Co., Ltd. from August 1992 to April 1994, where he was engaged in computer-aided design and 3D computer animation production. Mr. Hu began his career at Suzhou Advertising Company in October 1989, where he worked as a designer responsible for graphic design, platemaking, printing, and interior decoration. Mr. Hu graduated from Suzhou Academy of Arts in 1989.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Cao Yu",
|
name: "Cao Yu",
|
||||||
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
contain:
|
contain:
|
||||||
"Cao Yu, age 34, 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.",
|
"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.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David Lazar",
|
name: "David E. Lazar",
|
||||||
title: "Director",
|
title: "Director",
|
||||||
contain:
|
contain:
|
||||||
"David E. Lazar, age 34, has served as the Chief Executive Officer of OpGen, Inc., a precision medicine company listed on the Nasdaq (OPGN) since April 11, 2024, where he also servs as a director and board chairman, beginning on March 25, 2024. Mr. Lazar served as the Chief Executive Officer of Titan Pharmaceuticals Inc. listed on the Nasdaq (TTNP) from August 2022 through April 11, 2024, where he also served as a director and board chairman from August 2022 until October 2023. He has also served as the CEO of Custodian Ventures LLC, a company which specializes in assisting distressed public companies through custodianship, since February 2018, and Activist Investing LLC, an actively managed private investment fund, since March 2018. Previously, Mr. Lazar served as Managing Partner at Zenith Partners International Inc., a boutique consulting firm, from July 2012 to April 2018. In his role as Chief Eecutive Officer of Custodian Ventures LLC, Mr. Lazar has successfully served as a custodian to numerous public companies across a wide range of industries.",
|
"Served as the Chief Executive Officer of OpGen, Inc., a precision medicine company listed on the Nasdaq (OPGN) since April 11, 2024, where he also servs as a director and board chairman, beginning on March 25, 2024. Mr. Lazar served as the Chief Executive Officer of Titan Pharmaceuticals Inc. listed on the Nasdaq (TTNP) from August 2022 through April 11, 2024, where he also served as a director and board chairman from August 2022 until October 2023. He has also served as the CEO of Custodian Ventures LLC, a company which specializes in assisting distressed public companies through custodianship, since February 2018, and Activist Investing LLC, an actively managed private investment fund, since March 2018. Previously, Mr. Lazar served as Managing Partner at Zenith Partners International Inc., a boutique consulting firm, from July 2012 to April 2018. In his role as Chief Executive Officer of Custodian Ventures LLC, Mr. Lazar has successfully served as a custodian to numerous public companies across a wide range of industries.",
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Hu Bin",
|
|
||||||
title: "Director",
|
|
||||||
contain:
|
|
||||||
"Hu Bin,age 55, has served as a director of DC International Service Trade GmbH since December 2024. Prior to that, Mr. Hu worked as a freelancer in the tourism industry from April 2001 to October 2024. From April 1994 to October 2000, he served as the general manager of Suzhou Wintime Advertising Co., Ltd. Before that, he served as the general manager of Suzhou Bauhaus Advertising Design Co., Ltd. from August 1992 to April 1994, where he was engaged in computer-aided design and 3D computer animation production. Mr. Hu began his career at Suzhou Advertising Company in October 1989, where he worked as a designer responsible for graphic design, platemaking, printing, and interior decoration. Mr. Hu graduated from Suzhou Academy of Arts in 1989.",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "David Natan",
|
name: "David Natan",
|
||||||
title: "Director",
|
title: "Director",
|
||||||
contain:
|
contain:
|
||||||
"David Natan,age 72, currently serves as President and Chief Executive Officer of Natan & Associates, LLC, a consulting firm offering chief financial officer services to public and private companies in a variety of industries, since 2007. Mr. Natan previously served as a Director of the Company from November 2023 to February 2025. From February 2010 to May 2020, Mr. Natan served as Chief Executive Officer of ForceField Energy, Inc. (OTCMKTS: FNRG), a company focused on the solar industry and LED lighting products. From February 2002 to November 2007, Mr. Natan served as Executive Vice President of Reporting and Chief Financial Officer of PharmaNet Development Group, Inc., a drug development services company, and, from June 1995 to February 2002, as Chief Financial Officer and Vice President of Global Technovations, Inc., a manufacturer and marketer of oil analysis instruments and speakers and speaker components. Prior to that, Mr. Natan served in various roles of increasing responsibility with Deloitte & Touche LLP, a global consulting firm. Mr. Natan currently serves as a member of the Board of Directors and Chair of the Audit Committee of Sunshine Biopharma, Inc. (Nasdaq: SBFM), a pharmaceutical and nutritional supplement company, since February 2022. Previously, Mr. Natan has served as a director for the following public companies: Global Technovations, Forcefield Energy, Titan Pharmaceuticals (Nasdaq: TTNP), Vivakor Inc. (Nasdaq: VIVK), NetBrands Corp. (OTC: NBND), and OpGen Inc. (OTC: OPGN), and Cyclacel Pharmaceuticals (Nasdaq: CYCC). Mr. Natan holds a B.A. in Economics from Boston University.",
|
"Currently serves as President and Chief Executive Officer of Natan & Associates, LLC, a consulting firm offering chief financial officer services to public and private companies in a variety of industries, since 2007. Mr. Natan previously served as a Director of the Company from November 2023 to February 2025. From February 2010 to May 2020, Mr. Natan served as Chief Executive Officer of ForceField Energy, Inc. (OTCMKTS: FNRG), a company focused on the solar industry and LED lighting products. From February 2002 to November 2007, Mr. Natan served as Executive Vice President of Reporting and Chief Financial Officer of PharmaNet Development Group, Inc., a drug development services company, and, from June 1995 to February 2002, as Chief Financial Officer and Vice President of Global Technovations, Inc., a manufacturer and marketer of oil analysis instruments and speakers and speaker components. Prior to that, Mr. Natan served in various roles of increasing responsibility with Deloitte & Touche LLP, a global consulting firm. Mr. Natan currently serves as a member of the Board of Directors and Chair of the Audit Committee of Sunshine Biopharma, Inc. (Nasdaq: SBFM), a pharmaceutical and nutritional supplement company, since February 2022. Previously, Mr. Natan has served as a director for the following public companies: Global Technovations, Forcefield Energy, Titan Pharmaceuticals (Nasdaq: TTNP), Vivakor Inc. (Nasdaq: VIVK), NetBrands Corp. (OTC: NBND), and OpGen Inc. (OTC: OPGN), and Cyclacel Pharmaceuticals (Nasdaq: CYCC). Mr. Natan holds a B.A. in Economics from Boston University.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Chan Oi Fat",
|
name: "Chan Oi Fat",
|
||||||
title: "Director",
|
title: "Director",
|
||||||
contain:
|
contain:
|
||||||
"Chan Oi Fat, age 46, has served as Vice President – Finance of SML Group Corporation since March 2018 and as Company Secretary of China Leon Inspection Holding Limited (HKEX: 1586) since February 2018 and of Raily Aesthetic Medicine International Holdings Limited (HKEX: 2135) since November 2020. He is an independent non-executive director of Huajin International Holdings Limited (HKEX: 2738) (since March 2025) and UBoT Holding Limited (HKEX GEM: 8529) (since May 2024) and previously served as an independent non-executive director of China Saftower International Holding Group Limited (HKEX GEM: 8623) from June 2020 to December 2023 and Shanghai Prime Machinery Company Limited (HKEX: 2345) from June 2014 to January 2021. Mr. Chan holds a B.B.A. (Hons) in Accountancy from the City University of Hong Kong (2000) and is a member of the Association of Chartered Certified Accountants (since 2003) and the Hong Kong Institute of Certified Public Accountants (since 2004).",
|
"Served as Vice President – Finance of SML Group Corporation since March 2018 and as Company Secretary of China Leon Inspection Holding Limited (HKEX: 1586) since February 2018 and of Raily Aesthetic Medicine International Holdings Limited (HKEX: 2135) since November 2020. He is an independent non-executive director of Huajin International Holdings Limited (HKEX: 2738) (since March 2025) and UBoT Holding Limited (HKEX GEM: 8529) (since May 2024) and previously served as an independent non-executive director of China Saftower International Holding Group Limited (HKEX GEM: 8623) from June 2020 to December 2023 and Shanghai Prime Machinery Company Limited (HKEX: 2345) from June 2014 to January 2021. Mr. Chan holds a B.B.A. (Hons) in Accountancy from the City University of Hong Kong (2000) and is a member of the Association of Chartered Certified Accountants (since 2003) and the Hong Kong Institute of Certified Public Accountants (since 2004).",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 基础变量定义 */
|
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #895bff;
|
--primary-color: #333;
|
||||||
--text-primary: #1a1a1a;
|
--secondary-color: #666;
|
||||||
--text-secondary: #4a4a4a;
|
--divider-color: #f0f0f0;
|
||||||
--text-light: #666;
|
|
||||||
--border-color: #f0f0f0;
|
|
||||||
--mobile-padding: 16px;
|
--mobile-padding: 16px;
|
||||||
--section-spacing: 32px;
|
--tablet-padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-page {
|
||||||
|
background-image: url("@/assets/image/bg-mobile.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.directors-page {
|
.directors-page {
|
||||||
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 40px var(--mobile-padding);
|
padding: clamp(2rem, 5vw, 4rem)
|
||||||
|
clamp(var(--mobile-padding), 4vw, var(--tablet-padding));
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
text-align: center;
|
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
||||||
margin-bottom: 12px;
|
margin-bottom: clamp(1.5rem, 3vw, 2rem);
|
||||||
font-weight: 500;
|
color: var(--primary-color);
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: clamp(1.5rem, 5vw, 2rem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.directors-list {
|
.directors-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--section-spacing);
|
gap: clamp(2rem, 4vw, 3rem);
|
||||||
margin-top: 32px;
|
margin-top: clamp(1.5rem, 3vw, 2.5rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.director-item {
|
.director-item {
|
||||||
padding-bottom: var(--section-spacing);
|
padding-bottom: clamp(2rem, 4vw, 3rem);
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--divider-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.director-item:last-child {
|
.director-item:last-child {
|
||||||
@ -100,47 +102,44 @@ const otherDirectors = [
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.director-header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.director-name {
|
.director-name {
|
||||||
margin-bottom: 0;
|
margin-bottom: clamp(0.5rem, 1vw, 0.75rem);
|
||||||
font-size: clamp(1.25rem, 4vw, 1.75rem);
|
font-size: clamp(1.25rem, 3vw, 1.75rem);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--text-primary);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.director-title {
|
.director-title {
|
||||||
font-size: clamp(0.95rem, 3vw, 1.1rem);
|
font-size: clamp(1rem, 2vw, 1.25rem);
|
||||||
color: var(--text-light);
|
color: var(--secondary-color);
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 16px;
|
margin-bottom: clamp(1rem, 2vw, 1.5rem);
|
||||||
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
margin: 12px 0;
|
margin: clamp(1rem, 2vw, 1.5rem) 0;
|
||||||
background-color: var(--border-color);
|
background-color: var(--divider-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.director-bio {
|
.director-bio {
|
||||||
line-height: 1.7;
|
line-height: 1.8;
|
||||||
color: var(--text-secondary);
|
color: #4a4a4a;
|
||||||
font-size: clamp(0.9rem, 2.8vw, 1rem);
|
font-size: clamp(0.9rem, 1.8vw, 1rem);
|
||||||
text-align: justify;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 动画效果 */
|
/* 针对超小屏幕的优化 */
|
||||||
.v-motion-fade {
|
@media (max-width: 375px) {
|
||||||
opacity: 0;
|
.directors-page {
|
||||||
transform: translateY(10px);
|
padding: clamp(1.5rem, 5vw, 2rem) clamp(12px, 4vw, 16px);
|
||||||
transition: opacity 0.5s ease, transform 0.5s ease;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.v-motion-fade.appear {
|
.director-name {
|
||||||
opacity: 1;
|
font-size: 1.25rem;
|
||||||
transform: translateY(0);
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
137
src/views/boarddirectors/size768/index.vue
Normal file
137
src/views/boarddirectors/size768/index.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<div class="home-page">
|
||||||
|
<div class="directors-page">
|
||||||
|
<h1 class="page-title">Board of Directors</h1>
|
||||||
|
<n-divider />
|
||||||
|
|
||||||
|
<div class="directors-list">
|
||||||
|
<div
|
||||||
|
v-for="(director, index) in otherDirectors"
|
||||||
|
:key="index"
|
||||||
|
class="director-item"
|
||||||
|
>
|
||||||
|
<n-h2 style="font-size: 18px" class="director-name">{{
|
||||||
|
director.name
|
||||||
|
}}</n-h2>
|
||||||
|
<n-text style="font-size: 16px" class="director-title">{{
|
||||||
|
director.title
|
||||||
|
}}</n-text>
|
||||||
|
<n-divider class="divider" />
|
||||||
|
<n-p class="director-bio">{{ director.contain }}</n-p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const otherDirectors = [
|
||||||
|
{
|
||||||
|
name: "Hu Bin",
|
||||||
|
title: "Chairman of the Board of Directors",
|
||||||
|
contain:
|
||||||
|
"Served as a director of DC International Service Trade GmbH since December 2024. Prior to that, Mr. Hu worked as a freelancer in the tourism industry from April 2001 to October 2024. From April 1994 to October 2000, he served as the general manager of Suzhou Wintime Advertising Co., Ltd. Before that, he served as the general manager of Suzhou Bauhaus Advertising Design Co., Ltd. from August 1992 to April 1994, where he was engaged in computer-aided design and 3D computer animation production. Mr. Hu began his career at Suzhou Advertising Company in October 1989, where he worked as a designer responsible for graphic design, platemaking, printing, and interior decoration. Mr. Hu graduated from Suzhou Academy of Arts in 1989.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cao Yu",
|
||||||
|
title: "Chief Financial Officer, Secretary, Treasurer and Director",
|
||||||
|
contain:
|
||||||
|
"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.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "David E. Lazar",
|
||||||
|
title: "Director",
|
||||||
|
contain:
|
||||||
|
"Served as the Chief Executive Officer of OpGen, Inc., a precision medicine company listed on the Nasdaq (OPGN) since April 11, 2024, where he also servs as a director and board chairman, beginning on March 25, 2024. Mr. Lazar served as the Chief Executive Officer of Titan Pharmaceuticals Inc. listed on the Nasdaq (TTNP) from August 2022 through April 11, 2024, where he also served as a director and board chairman from August 2022 until October 2023. He has also served as the CEO of Custodian Ventures LLC, a company which specializes in assisting distressed public companies through custodianship, since February 2018, and Activist Investing LLC, an actively managed private investment fund, since March 2018. Previously, Mr. Lazar served as Managing Partner at Zenith Partners International Inc., a boutique consulting firm, from July 2012 to April 2018. In his role as Chief Executive Officer of Custodian Ventures LLC, Mr. Lazar has successfully served as a custodian to numerous public companies across a wide range of industries.",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "David Natan",
|
||||||
|
title: "Director",
|
||||||
|
contain:
|
||||||
|
"Currently serves as President and Chief Executive Officer of Natan & Associates, LLC, a consulting firm offering chief financial officer services to public and private companies in a variety of industries, since 2007. Mr. Natan previously served as a Director of the Company from November 2023 to February 2025. From February 2010 to May 2020, Mr. Natan served as Chief Executive Officer of ForceField Energy, Inc. (OTCMKTS: FNRG), a company focused on the solar industry and LED lighting products. From February 2002 to November 2007, Mr. Natan served as Executive Vice President of Reporting and Chief Financial Officer of PharmaNet Development Group, Inc., a drug development services company, and, from June 1995 to February 2002, as Chief Financial Officer and Vice President of Global Technovations, Inc., a manufacturer and marketer of oil analysis instruments and speakers and speaker components. Prior to that, Mr. Natan served in various roles of increasing responsibility with Deloitte & Touche LLP, a global consulting firm. Mr. Natan currently serves as a member of the Board of Directors and Chair of the Audit Committee of Sunshine Biopharma, Inc. (Nasdaq: SBFM), a pharmaceutical and nutritional supplement company, since February 2022. Previously, Mr. Natan has served as a director for the following public companies: Global Technovations, Forcefield Energy, Titan Pharmaceuticals (Nasdaq: TTNP), Vivakor Inc. (Nasdaq: VIVK), NetBrands Corp. (OTC: NBND), and OpGen Inc. (OTC: OPGN), and Cyclacel Pharmaceuticals (Nasdaq: CYCC). Mr. Natan holds a B.A. in Economics from Boston University.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Chan Oi Fat",
|
||||||
|
title: "Director",
|
||||||
|
contain:
|
||||||
|
"Served as Vice President – Finance of SML Group Corporation since March 2018 and as Company Secretary of China Leon Inspection Holding Limited (HKEX: 1586) since February 2018 and of Raily Aesthetic Medicine International Holdings Limited (HKEX: 2135) since November 2020. He is an independent non-executive director of Huajin International Holdings Limited (HKEX: 2738) (since March 2025) and UBoT Holding Limited (HKEX GEM: 8529) (since May 2024) and previously served as an independent non-executive director of China Saftower International Holding Group Limited (HKEX GEM: 8623) from June 2020 to December 2023 and Shanghai Prime Machinery Company Limited (HKEX: 2345) from June 2014 to January 2021. Mr. Chan holds a B.B.A. (Hons) in Accountancy from the City University of Hong Kong (2000) and is a member of the Association of Chartered Certified Accountants (since 2003) and the Hong Kong Institute of Certified Public Accountants (since 2004).",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-page {
|
||||||
|
background-image: url("@/assets/image/bg.png");
|
||||||
|
background-size: 100% 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
.directors-page {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 60px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 2.5rem; /* 18px */
|
||||||
|
margin-bottom: 30px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.directors-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 48px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-item {
|
||||||
|
padding-bottom: 48px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-name {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #666;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin: 16px 0;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-bio {
|
||||||
|
line-height: 1.8;
|
||||||
|
color: #4a4a4a;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.directors-page {
|
||||||
|
padding: 80px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-name {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.director-title {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user