Compare commits

...

16 Commits

Author SHA1 Message Date
55c02afc0d Merge pull request 'feat: 代码暂存' (#3) from yk-20250926 into newmain-20250926
Reviewed-on: http://gitea.tools.fontree.cn:3000/scout666/fiee-official-website/pulls/3
2025-10-21 07:12:19 +00:00
d94f9b779f Merge pull request 'yk-20250926' (#2) from yk-20250926 into newmain-20250926
Reviewed-on: http://gitea.tools.fontree.cn:3000/scout666/fiee-official-website/pulls/2
2025-10-20 03:09:42 +00:00
yuanshan
5f4d101d4c Merge branch 'zhangyuanshan-20250925' into newmain-20250926 2025-10-17 11:53:38 +08:00
yuanshan
8566012575 echart检测尺寸变动重绘 2025-10-16 15:59:52 +08:00
yuanshan
26e7047359 fix press-releases 2025-10-16 15:41:03 +08:00
yuanshan
8b249c0341 Merge branch 'zhangyuanshan-20250925' into newmain-20250926 2025-10-16 09:29:29 +08:00
yuanshan
b3ab1781c3 fix email-alerts api 2025-10-16 09:29:00 +08:00
yuanshan
d86ad2c832 fix i18n 2025-10-15 18:26:36 +08:00
yuanshan
79210d8402 fix stock-quote i18n 2025-10-15 14:48:23 +08:00
yuanshan
56609fed31 fix quarterlyreports i18n 2025-10-15 14:42:10 +08:00
yuanshan
faac577341 fix quarterlyreports add api 2025-10-15 14:30:38 +08:00
yuanshan
ee596a518f fix email-alerts 2025-10-15 10:27:33 +08:00
yuanshan
bbc63346a1 fix product-introduction 1440 2025-10-14 17:16:36 +08:00
yuanshan
ab1e94b25e Merge branch 'zhangyuanshan-20250925' into newmain-20250926 2025-10-14 16:32:40 +08:00
yuanshan
7466bcdcf7 add language change 2025-10-14 15:47:40 +08:00
yuanshan
f6b2956ac3 fix email-alerts 375 2025-10-14 13:32:13 +08:00
61 changed files with 4382 additions and 1607 deletions

View File

@ -37,7 +37,7 @@ export default {
customPxToViewportPlugin({ customPxToViewportPlugin({
defaultViewportWidth:1920, defaultViewportWidth:1920,
unitPrecision: 5, // 保留的小数位数 unitPrecision: 5, // 保留的小数位数
selectorBlackList: [/^\.van/, '.px-fixed'], // 以 .van 开头的类名不转换 selectorBlackList: [/^\.van/], // 以 .van 开头的类名不转换
minPixelValue: 1, // 小于或等于 1px 不转换 minPixelValue: 1, // 小于或等于 1px 不转换
viewportUnit: "vw", // 转换后的单位 viewportUnit: "vw", // 转换后的单位
fontViewportUnit: "vw", // 字体单位 fontViewportUnit: "vw", // 字体单位

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -6,23 +6,23 @@
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="stock-title"> <div class="stock-title">
<span>FiEE, Inc. Stock Price History</span> <span>{{ t("historic_stock.echarts.title") }}</span>
</div> </div>
</div> </div>
<div class="echarts-search-area"> <div class="echarts-search-area">
<div class="echarts-search-byRange"> <div class="echarts-search-byRange">
<text style="font-size: 0.9rem; font-weight: 400; color: #666666"> <text style="font-size: 0.9rem; font-weight: 400; color: #666666">
Range {{ t("historic_stock.echarts.range") }}
</text> </text>
<div class="search-range-list"> <div class="search-range-list">
<div <div
class="search-range-list-each" class="search-range-list-each"
v-for="(item, index) in state.searchRange" v-for="(item, index) in searchRangeOptions"
:key="index" :key="index"
:class="{ activeRange: state.activeRange === item }" :class="{ activeRange: state.activeRange === item.key }"
@click="changeSearchRange(item)" @click="changeSearchRange(item.key)"
> >
<span>{{ item }}</span> <span>{{ item.label }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -42,17 +42,30 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, watch, reactive } from "vue"; import { onMounted, onBeforeUnmount, watch, reactive, computed } from "vue";
import { useI18n } from "vue-i18n";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { NDatePicker, NIcon } from "naive-ui"; import { NDatePicker, NIcon } from "naive-ui";
import { ArrowForwardOutline } from "@vicons/ionicons5"; import { ArrowForwardOutline } from "@vicons/ionicons5";
import axios from "axios"; import axios from "axios";
const { t, locale } = useI18n();
const state = reactive({ const state = reactive({
searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"], searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"],
dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()], dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()],
activeRange: "", activeRange: "",
}); });
const searchRangeOptions = computed(() => [
{ label: t("historic_stock.echarts.1m"), key: "1m" },
{ label: t("historic_stock.echarts.3m"), key: "3m" },
{ label: t("historic_stock.echarts.ytd_short"), key: "YTD" },
{ label: t("historic_stock.echarts.1y"), key: "1Y" },
{ label: t("historic_stock.echarts.5y"), key: "5Y" },
{ label: t("historic_stock.echarts.10y"), key: "10Y" },
{ label: t("historic_stock.echarts.max"), key: "Max" },
]);
let myCharts = null; let myCharts = null;
let historicData = []; let historicData = [];
let xAxisData = []; let xAxisData = [];
@ -96,7 +109,11 @@ const initEcharts = (data) => {
}, },
formatter: function (params) { formatter: function (params) {
const p = params[0]; 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>`; return `<span style="font-size: 1.1rem; font-weight: 600;">${
p.axisValue
}</span><br/><span style="font-size: 0.9rem; font-weight: 400;">${t(
"historic_stock.echarts.price"
)}: ${p.data}</span>`;
}, },
triggerOn: "mousemove", triggerOn: "mousemove",
confine: true, confine: true,
@ -293,8 +310,44 @@ const initEcharts = (data) => {
}); });
}; };
//
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
//
const handleResize = () => {
if (myCharts) {
myCharts.resize();
}
};
// resize
const debouncedResize = debounce(handleResize, 300);
onMounted(() => { onMounted(() => {
getHistoricalData(); getHistoricalData();
// resize
window.addEventListener("resize", debouncedResize);
});
//
onBeforeUnmount(() => {
// resize
window.removeEventListener("resize", debouncedResize);
// echarts
if (myCharts) {
myCharts.dispose();
myCharts = null;
}
}); });
// //
@ -576,6 +629,7 @@ const handleDateRangeChange = (range) => {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap;
gap: 10px; gap: 10px;
.echarts-search-byRange { .echarts-search-byRange {

View File

@ -6,23 +6,23 @@
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="stock-title"> <div class="stock-title">
<span>FiEE, Inc. Stock Price History</span> <span>{{ t("historic_stock.echarts.title") }}</span>
</div> </div>
</div> </div>
<div class="echarts-search-area"> <div class="echarts-search-area">
<div class="echarts-search-byRange"> <div class="echarts-search-byRange">
<text style="font-size: 0.9rem; font-weight: 400; color: #666666"> <text style="font-size: 0.9rem; font-weight: 400; color: #666666">
Range {{ t("historic_stock.echarts.range") }}
</text> </text>
<div class="search-range-list"> <div class="search-range-list">
<div <div
class="search-range-list-each" class="search-range-list-each"
v-for="(item, index) in state.searchRange" v-for="(item, index) in searchRangeOptions"
:key="index" :key="index"
:class="{ activeRange: state.activeRange === item }" :class="{ activeRange: state.activeRange === item.key }"
@click="changeSearchRange(item)" @click="changeSearchRange(item.key)"
> >
<span>{{ item }}</span> <span>{{ item.label }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -42,17 +42,29 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, watch, reactive } from "vue"; import { onMounted, onBeforeUnmount, watch, reactive, computed } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { NDatePicker, NIcon } from "naive-ui"; import { NDatePicker, NIcon } from "naive-ui";
import { ArrowForwardOutline } from "@vicons/ionicons5"; import { ArrowForwardOutline } from "@vicons/ionicons5";
import axios from "axios"; import axios from "axios";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
const state = reactive({ const state = reactive({
searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"], searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"],
dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()], dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()],
activeRange: "", activeRange: "",
}); });
const searchRangeOptions = computed(() => [
{ label: t("historic_stock.echarts.1m"), key: "1m" },
{ label: t("historic_stock.echarts.3m"), key: "3m" },
{ label: t("historic_stock.echarts.ytd_short"), key: "YTD" },
{ label: t("historic_stock.echarts.1y"), key: "1Y" },
{ label: t("historic_stock.echarts.5y"), key: "5Y" },
{ label: t("historic_stock.echarts.10y"), key: "10Y" },
{ label: t("historic_stock.echarts.max"), key: "Max" },
]);
let myCharts = null; let myCharts = null;
let historicData = []; let historicData = [];
let xAxisData = []; let xAxisData = [];
@ -96,7 +108,11 @@ const initEcharts = (data) => {
}, },
formatter: function (params) { formatter: function (params) {
const p = params[0]; 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>`; return `<span style="font-size: 1.1rem; font-weight: 600;">${
p.axisValue
}</span><br/><span style="font-size: 0.9rem; font-weight: 400;">${t(
"historic_stock.echarts.price"
)}: ${p.data}</span>`;
}, },
triggerOn: "mousemove", triggerOn: "mousemove",
confine: true, confine: true,
@ -293,8 +309,44 @@ const initEcharts = (data) => {
}); });
}; };
//
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
//
const handleResize = () => {
if (myCharts) {
myCharts.resize();
}
};
// resize
const debouncedResize = debounce(handleResize, 300);
onMounted(() => { onMounted(() => {
getHistoricalData(); getHistoricalData();
// resize
window.addEventListener("resize", debouncedResize);
});
//
onBeforeUnmount(() => {
// resize
window.removeEventListener("resize", debouncedResize);
// echarts
if (myCharts) {
myCharts.dispose();
myCharts = null;
}
}); });
// //
@ -522,12 +574,12 @@ const isDateDisabled = (ts, type, range) => {
// //
const handleDateRangeChange = (range) => { const handleDateRangeChange = (range) => {
if (range && range[0] && range[1]) { if (range && range[0] && range[1]) {
const startDate = new Date(range[0]).toLocaleDateString("en-US", { const startDate = new Date(range[0]).toLocaleDateString(locale.value, {
month: "short", month: "short",
day: "numeric", day: "numeric",
year: "numeric", year: "numeric",
}); });
const endDate = new Date(range[1]).toLocaleDateString("en-US", { const endDate = new Date(range[1]).toLocaleDateString(locale.value, {
month: "short", month: "short",
day: "numeric", day: "numeric",
year: "numeric", year: "numeric",
@ -576,6 +628,8 @@ const handleDateRangeChange = (range) => {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
.echarts-search-byRange { .echarts-search-byRange {
display: flex; display: flex;

View File

@ -6,27 +6,31 @@
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title-text"> <div class="title-text">
<span>FiEE, Inc. Stock Price History</span> <span>{{ t("historic_stock.echarts.title") }}</span>
</div> </div>
</div> </div>
<div class="echarts-search-area"> <div class="echarts-search-area">
<div class="echarts-search-byRange"> <div class="echarts-search-byRange">
<span class="range-label">Range</span> <span class="range-label">{{
t("historic_stock.echarts.range")
}}</span>
<div class="search-range-list"> <div class="search-range-list">
<div <div
class="search-range-list-each" class="search-range-list-each"
v-for="(item, index) in state.searchRange" v-for="(item, index) in searchRangeOptions"
:key="index" :key="index"
:class="{ activeRange: state.activeRange === item }" :class="{ activeRange: state.activeRange === item.key }"
@click="changeSearchRange(item)" @click="changeSearchRange(item.key)"
> >
<span>{{ item }}</span> <span>{{ item.label }}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="echarts-search-byDate"> <div class="echarts-search-byDate">
<div class="date-row" @click="openPicker('start')"> <div class="date-row" @click="openPicker('start')">
<span class="date-label">Start Time</span> <span class="date-label">{{
t("historic_stock.echarts.start_time")
}}</span>
<div class="date-value"> <div class="date-value">
<span>{{ formatYMD(state.dateRange[0]) }}</span> <span>{{ formatYMD(state.dateRange[0]) }}</span>
<svg <svg
@ -46,7 +50,9 @@
</div> </div>
</div> </div>
<div class="date-row" @click="openPicker('end')"> <div class="date-row" @click="openPicker('end')">
<span class="date-label">End Time</span> <span class="date-label">{{
t("historic_stock.echarts.end_time")
}}</span>
<div class="date-value"> <div class="date-value">
<span>{{ formatYMD(state.dateRange[1]) }}</span> <span>{{ formatYMD(state.dateRange[1]) }}</span>
<svg <svg
@ -92,12 +98,15 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, watch, reactive } from "vue"; import { onMounted, onBeforeUnmount, watch, reactive, computed } from "vue";
import { useI18n } from "vue-i18n";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { NDatePicker, NIcon } from "naive-ui"; import { NDatePicker, NIcon } from "naive-ui";
import { ArrowForwardOutline } from "@vicons/ionicons5"; import { ArrowForwardOutline } from "@vicons/ionicons5";
import axios from "axios"; import axios from "axios";
import DateWheelPicker from "../../DateWheelPicker.vue"; import DateWheelPicker from "../../DateWheelPicker.vue";
const { t, locale } = useI18n();
const state = reactive({ const state = reactive({
searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"], searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"],
dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()], dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()],
@ -109,6 +118,16 @@ const state = reactive({
pickerDay: new Date().getDate(), pickerDay: new Date().getDate(),
}); });
const searchRangeOptions = computed(() => [
{ label: t("historic_stock.echarts.1m"), key: "1m" },
{ label: t("historic_stock.echarts.3m"), key: "3m" },
{ label: t("historic_stock.echarts.ytd_short"), key: "YTD" },
{ label: t("historic_stock.echarts.1y"), key: "1Y" },
{ label: t("historic_stock.echarts.5y"), key: "5Y" },
{ label: t("historic_stock.echarts.10y"), key: "10Y" },
{ label: t("historic_stock.echarts.max"), key: "Max" },
]);
// //
let myCharts = null; let myCharts = null;
@ -154,7 +173,11 @@ const initEcharts = (data) => {
}, },
formatter: function (params) { formatter: function (params) {
const p = params[0]; 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>`; return `<span style="font-size: 1.1rem; font-weight: 600;">${
p.axisValue
}</span><br/><span style="font-size: 0.9rem; font-weight: 400;">${t(
"historic_stock.echarts.price"
)}: ${p.data}</span>`;
}, },
triggerOn: "mousemove", triggerOn: "mousemove",
confine: true, confine: true,
@ -351,8 +374,44 @@ const initEcharts = (data) => {
}); });
}; };
//
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
//
const handleResize = () => {
if (myCharts) {
myCharts.resize();
}
};
// resize
const debouncedResize = debounce(handleResize, 300);
onMounted(() => { onMounted(() => {
getHistoricalData(); getHistoricalData();
// resize
window.addEventListener("resize", debouncedResize);
});
//
onBeforeUnmount(() => {
// resize
window.removeEventListener("resize", debouncedResize);
// echarts
if (myCharts) {
myCharts.dispose();
myCharts = null;
}
}); });
// //
@ -706,6 +765,7 @@ watch(
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
gap: 10 * 5.12px; gap: 10 * 5.12px;
flex-wrap: wrap;
.echarts-search-byRange { .echarts-search-byRange {
width: 100%; width: 100%;

View File

@ -6,23 +6,23 @@
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title-text"> <div class="title-text">
<span>FiEE, Inc. Stock Price History</span> <span>{{ t("historic_stock.echarts.title") }}</span>
</div> </div>
</div> </div>
<div class="echarts-search-area"> <div class="echarts-search-area">
<div class="echarts-search-byRange"> <div class="echarts-search-byRange">
<text style="font-size: 0.9rem; font-weight: 400; color: #666666"> <text style="font-size: 0.9rem; font-weight: 400; color: #666666">
Range {{ t("historic_stock.echarts.range") }}
</text> </text>
<div class="search-range-list"> <div class="search-range-list">
<div <div
class="search-range-list-each" class="search-range-list-each"
v-for="(item, index) in state.searchRange" v-for="(item, index) in searchRangeOptions"
:key="index" :key="index"
:class="{ activeRange: state.activeRange === item }" :class="{ activeRange: state.activeRange === item.key }"
@click="changeSearchRange(item)" @click="changeSearchRange(item.key)"
> >
<span>{{ item }}</span> <span>{{ item.label }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -42,17 +42,30 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, watch, reactive } from "vue"; import { onMounted, onBeforeUnmount, watch, reactive, computed } from "vue";
import { useI18n } from "vue-i18n";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { NDatePicker, NIcon } from "naive-ui"; import { NDatePicker, NIcon } from "naive-ui";
import { ArrowForwardOutline } from "@vicons/ionicons5"; import { ArrowForwardOutline } from "@vicons/ionicons5";
import axios from "axios"; import axios from "axios";
const { t, locale } = useI18n();
const state = reactive({ const state = reactive({
searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"], searchRange: ["1m", "3m", "YTD", "1Y", "5Y", "10Y", "Max"],
dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()], dateRange: [new Date("2009-10-07").getTime(), new Date().getTime()],
activeRange: "", activeRange: "",
}); });
const searchRangeOptions = computed(() => [
{ label: t("historic_stock.echarts.1m"), key: "1m" },
{ label: t("historic_stock.echarts.3m"), key: "3m" },
{ label: t("historic_stock.echarts.ytd_short"), key: "YTD" },
{ label: t("historic_stock.echarts.1y"), key: "1Y" },
{ label: t("historic_stock.echarts.5y"), key: "5Y" },
{ label: t("historic_stock.echarts.10y"), key: "10Y" },
{ label: t("historic_stock.echarts.max"), key: "Max" },
]);
let myCharts = null; let myCharts = null;
let historicData = []; let historicData = [];
let xAxisData = []; let xAxisData = [];
@ -96,7 +109,11 @@ const initEcharts = (data) => {
}, },
formatter: function (params) { formatter: function (params) {
const p = params[0]; 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>`; return `<span style="font-size: 1.1rem; font-weight: 600;">${
p.axisValue
}</span><br/><span style="font-size: 0.9rem; font-weight: 400;">${t(
"historic_stock.echarts.price"
)}: ${p.data}</span>`;
}, },
triggerOn: "mousemove", triggerOn: "mousemove",
confine: true, confine: true,
@ -293,8 +310,44 @@ const initEcharts = (data) => {
}); });
}; };
//
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
//
const handleResize = () => {
if (myCharts) {
myCharts.resize();
}
};
// resize
const debouncedResize = debounce(handleResize, 300);
onMounted(() => { onMounted(() => {
getHistoricalData(); getHistoricalData();
// resize
window.addEventListener("resize", debouncedResize);
});
//
onBeforeUnmount(() => {
// resize
window.removeEventListener("resize", debouncedResize);
// echarts
if (myCharts) {
myCharts.dispose();
myCharts = null;
}
}); });
// //
@ -578,6 +631,7 @@ const handleDateRangeChange = (range) => {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
gap: 10 * 2.5px; gap: 10 * 2.5px;
flex-wrap: wrap;
.echarts-search-byRange { .echarts-search-byRange {
width: 100%; width: 100%;
@ -592,6 +646,7 @@ const handleDateRangeChange = (range) => {
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
gap: 16 * 2.5px; gap: 16 * 2.5px;
flex-wrap: wrap;
.search-range-list-each { .search-range-list-each {
padding: 7 * 2.5px 22 * 2.5px; padding: 7 * 2.5px 22 * 2.5px;
border-radius: 5px; border-radius: 5px;

View File

@ -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 <= 835) { } else if (viewWidth <= 1100) {
return size768; return size768;
} else if (viewWidth <= 1640) { } else if (viewWidth <= 1500) {
return size1440; return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) { } else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920; return size1920;

View File

@ -19,17 +19,46 @@
/> />
</NConfigProvider> </NConfigProvider>
</div> </div>
<div class="header-right">
<div class="lang-switch-container">
<div class="lang-switch" @click.stop="toggleLanguagePicker">
<span class="lang-label">{{ currentLanguageLabel }}</span>
<svg
:class="{ rotated: showLanguagePicker }"
xmlns="http://www.w3.org/2000/svg"
width="7"
height="4"
viewBox="0 0 7 4"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 4L7 0L0 0L3.5 4Z"
fill="black"
/>
</svg>
</div>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@select="handleSelectLanguage"
/>
</div>
</div>
</div> </div>
</NLayoutHeader> </NLayoutHeader>
</template> </template>
<script setup> <script setup>
import FiEELogo from "@/assets/image/header/logo.png"; import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NConfigProvider } from "naive-ui"; import { NMenu, NLayoutHeader, NImage, NConfigProvider } from "naive-ui";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig"; import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/size1440/index.vue";
const themeOverrides = { const themeOverrides = {
Menu: { Menu: {
@ -51,15 +80,51 @@ const themeOverrides = {
}, },
}; };
const { t } = useI18n(); const { t, locale } = useI18n();
const router = useRouter(); const router = useRouter();
// 使 // 使
const menuOptions = useHeaderMenuConfig(); const { menuOptions } = useHeaderMenuConfig();
const selectedKey = ref(null); const selectedKey = ref(null);
const isScrolled = ref(false); const isScrolled = ref(false);
// language picker
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const toggleLanguagePicker = () => {
showLanguagePicker.value = !showLanguagePicker.value;
};
const closeLanguagePicker = () => {
showLanguagePicker.value = false;
};
const handleSelectLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
closeLanguagePicker();
selectedLanguage.value = locale.value;
};
// //
function findMenuOptionByKey(options, key) { function findMenuOptionByKey(options, key) {
for (const option of options) { for (const option of options) {
@ -74,7 +139,7 @@ function findMenuOptionByKey(options, key) {
// //
const handleMenuSelect = (key) => { const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key); const option = findMenuOptionByKey(menuOptions.value, key);
if (option && option.href) { if (option && option.href) {
router.push(option.href); router.push(option.href);
} }
@ -88,10 +153,12 @@ const handleScroll = () => {
onMounted(() => { onMounted(() => {
window.addEventListener("scroll", handleScroll); window.addEventListener("scroll", handleScroll);
window.addEventListener("click", closeLanguagePicker);
}); });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
window.removeEventListener("click", closeLanguagePicker);
}); });
// //
@ -247,6 +314,29 @@ const handleToHome = () => {
transform: translateY(0) scale(1); transform: translateY(0) scale(1);
} }
} }
.lang-switch-container {
position: relative;
}
.lang-switch {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
.lang-label {
font-size: 12 * 1.33px;
font-weight: 600;
line-height: normal;
color: #000;
}
svg {
transition: transform 0.2s;
&.rotated {
transform: rotate(180deg);
}
}
}
</style> </style>
<style> <style>
.header-menu .n-menu .n-menu-item-content .n-menu-item-content-header { .header-menu .n-menu .n-menu-item-content .n-menu-item-content-header {

View File

@ -19,17 +19,46 @@
/> />
</NConfigProvider> </NConfigProvider>
</div> </div>
<div class="header-right">
<div class="lang-switch-container">
<div class="lang-switch" @click.stop="toggleLanguagePicker">
<span class="lang-label">{{ currentLanguageLabel }}</span>
<svg
:class="{ rotated: showLanguagePicker }"
xmlns="http://www.w3.org/2000/svg"
width="7"
height="4"
viewBox="0 0 7 4"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.5 4L7 0L0 0L3.5 4Z"
fill="black"
/>
</svg>
</div>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@select="handleSelectLanguage"
/>
</div>
</div>
</div> </div>
</NLayoutHeader> </NLayoutHeader>
</template> </template>
<script setup> <script setup>
import FiEELogo from "@/assets/image/header/logo.png"; import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NConfigProvider } from "naive-ui"; import { NMenu, NLayoutHeader, NImage, NConfigProvider } from "naive-ui";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig"; import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/size1920/index.vue";
const themeOverrides = { const themeOverrides = {
Menu: { Menu: {
@ -50,15 +79,49 @@ const themeOverrides = {
optionColorHoverInverted: "#fddfea", // optionColorHoverInverted: "#fddfea", //
}, },
}; };
const { t } = useI18n(); const { t, locale } = useI18n();
const router = useRouter(); const router = useRouter();
// 使 // 使
const menuOptions = useHeaderMenuConfig(); const { menuOptions } = useHeaderMenuConfig();
const selectedKey = ref(null); const selectedKey = ref(null);
const isScrolled = ref(false); const isScrolled = ref(false);
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const toggleLanguagePicker = () => {
showLanguagePicker.value = !showLanguagePicker.value;
};
const closeLanguagePicker = () => {
showLanguagePicker.value = false;
};
const handleSelectLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
closeLanguagePicker();
selectedLanguage.value = locale.value;
};
// //
function findMenuOptionByKey(options, key) { function findMenuOptionByKey(options, key) {
for (const option of options) { for (const option of options) {
@ -73,7 +136,7 @@ function findMenuOptionByKey(options, key) {
// //
const handleMenuSelect = (key) => { const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key); const option = findMenuOptionByKey(menuOptions.value, key);
if (option && option.href) { if (option && option.href) {
router.push(option.href); router.push(option.href);
} }
@ -87,10 +150,12 @@ const handleScroll = () => {
onMounted(() => { onMounted(() => {
window.addEventListener("scroll", handleScroll); window.addEventListener("scroll", handleScroll);
window.addEventListener("click", closeLanguagePicker);
}); });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
window.removeEventListener("click", closeLanguagePicker);
}); });
// //
@ -240,4 +305,32 @@ const handleToHome = () => {
transform: translateY(0) scale(1); transform: translateY(0) scale(1);
} }
} }
.header-right {
margin-left: auto;
padding-left: 20px;
}
.lang-switch-container {
position: relative;
}
.lang-switch {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
.lang-label {
font-size: 12px;
font-weight: 600;
line-height: normal;
color: #000;
}
svg {
transition: transform 0.2s;
&.rotated {
transform: rotate(180deg);
}
}
}
</style> </style>

View File

@ -12,23 +12,42 @@
preview-disabled preview-disabled
/> />
</div> </div>
<div <div class="header-right">
class="menu-btn" <div v-show="!showMenu" class="lang-switch" @click="openLanguagePicker">
:class="{ 'menu-open': showMenu }" <span class="lang-label">{{ currentLanguageLabel }}</span>
@click="toggleMenu" <svg
> xmlns="http://www.w3.org/2000/svg"
<img width="7"
v-if="showMenu" height="4"
src="@/assets/image/375/menu-close.png" viewBox="0 0 7 4"
alt="menu" fill="none"
class="menu-icon" >
/> <path
<img fill-rule="evenodd"
v-else clip-rule="evenodd"
src="@/assets/image/375/menu-open.png" d="M3.5 4L7 0L0 0L3.5 4Z"
alt="menu" fill="black"
class="menu-icon" />
/> </svg>
</div>
<div
class="menu-btn"
:class="{ 'menu-open': showMenu }"
@click="toggleMenu"
>
<img
v-if="showMenu"
src="@/assets/image/375/menu-close.png"
alt="menu"
class="menu-icon"
/>
<img
v-else
src="@/assets/image/375/menu-open.png"
alt="menu"
class="menu-icon"
/>
</div>
</div> </div>
</div> </div>
</NLayoutHeader> </NLayoutHeader>
@ -47,16 +66,24 @@
</NConfigProvider> </NConfigProvider>
</div> </div>
</transition> </transition>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@close="showLanguagePicker = false"
@confirm="handleConfirmLanguage"
/>
</template> </template>
<script setup> <script setup>
import FiEELogo from "@/assets/image/header/logo.png"; import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NIcon, NConfigProvider } from "naive-ui"; import { NMenu, NLayoutHeader, NImage, NIcon, NConfigProvider } from "naive-ui";
import { MenuSharp, CloseSharp } from "@vicons/ionicons5"; import { MenuSharp, CloseSharp } from "@vicons/ionicons5";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig"; import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/index.vue";
const themeOverrides = { const themeOverrides = {
Menu: { Menu: {
itemTextColorHover: "#000", itemTextColorHover: "#000",
@ -67,13 +94,45 @@ const themeOverrides = {
itemColorActiveHover: "#fff8fb", itemColorActiveHover: "#fff8fb",
}, },
}; };
const { t } = useI18n(); const { t, locale } = useI18n();
const router = useRouter(); const router = useRouter();
const isScrolled = ref(false); const isScrolled = ref(false);
const showMenu = ref(false); const showMenu = ref(false);
const selectedKey = ref(null); const selectedKey = ref(null);
// language picker
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const openLanguagePicker = () => {
if (showMenu.value) {
return;
}
showLanguagePicker.value = true;
selectedLanguage.value = locale.value;
};
const handleConfirmLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
showLanguagePicker.value = false;
};
const toggleMenu = () => { const toggleMenu = () => {
showMenu.value = !showMenu.value; showMenu.value = !showMenu.value;
}; };
@ -95,7 +154,7 @@ function findMenuOptionByKey(options, key) {
// //
const handleMenuSelect = (key) => { const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key); const option = findMenuOptionByKey(menuOptions.value, key);
if (option && option.href) { if (option && option.href) {
router.push(option.href); router.push(option.href);
showMenu.value = false; // showMenu.value = false; //
@ -103,7 +162,7 @@ const handleMenuSelect = (key) => {
}; };
// 使 // 使
const menuOptions = useHeaderMenuConfig(); const { menuOptions } = useHeaderMenuConfig();
// //
const handleScroll = () => { const handleScroll = () => {
@ -150,6 +209,19 @@ const handleToHome = () => {
justify-content: space-between; justify-content: space-between;
} }
.lang-switch {
display: flex;
align-items: center;
gap: 11 * 5.12px;
font-weight: 600;
font-size: 14 * 5.12px;
cursor: pointer;
user-select: none;
}
.lang-caret {
font-size: 10 * 5.12px;
}
.logo { .logo {
flex-shrink: 0; flex-shrink: 0;
margin-right: 12px; margin-right: 12px;
@ -259,4 +331,9 @@ const handleToHome = () => {
opacity: 0; opacity: 0;
transform: translateY(-50px); transform: translateY(-50px);
} }
.header-right {
display: flex;
align-items: center;
gap: 24 * 5.12px;
}
</style> </style>

View File

@ -8,23 +8,42 @@
<div class="logo" @click="handleToHome"> <div class="logo" @click="handleToHome">
<NImage class="logo-image" :src="FiEELogo" preview-disabled /> <NImage class="logo-image" :src="FiEELogo" preview-disabled />
</div> </div>
<div <div class="header-right">
class="menu-btn" <div v-show="!showMenu" class="lang-switch" @click="openLanguagePicker">
:class="{ 'menu-open': showMenu }" <span class="lang-label">{{ currentLanguageLabel }}</span>
@click="toggleMenu" <svg
> xmlns="http://www.w3.org/2000/svg"
<img width="7"
v-if="showMenu" height="4"
src="@/assets/image/768/menu-close.png" viewBox="0 0 7 4"
alt="menu" fill="none"
class="menu-icon" >
/> <path
<img fill-rule="evenodd"
v-else clip-rule="evenodd"
src="@/assets/image/768/menu-open.png" d="M3.5 4L7 0L0 0L3.5 4Z"
alt="menu" fill="black"
class="menu-icon" />
/> </svg>
</div>
<div
class="menu-btn"
:class="{ 'menu-open': showMenu }"
@click="toggleMenu"
>
<img
v-if="showMenu"
src="@/assets/image/768/menu-close.png"
alt="menu"
class="menu-icon"
/>
<img
v-else
src="@/assets/image/768/menu-open.png"
alt="menu"
class="menu-icon"
/>
</div>
</div> </div>
</div> </div>
</NLayoutHeader> </NLayoutHeader>
@ -43,16 +62,24 @@
</NConfigProvider> </NConfigProvider>
</div> </div>
</transition> </transition>
<LanguagePicker
v-if="showLanguagePicker"
v-model="selectedLanguage"
:options="languageOptions"
@close="showLanguagePicker = false"
@confirm="handleConfirmLanguage"
/>
</template> </template>
<script setup> <script setup>
import FiEELogo from "@/assets/image/header/logo.png"; import FiEELogo from "@/assets/image/header/logo.png";
import { ref, onMounted, onUnmounted } from "vue"; import { ref, onMounted, onUnmounted, computed } from "vue";
import { NMenu, NLayoutHeader, NImage, NIcon, NConfigProvider } from "naive-ui"; import { NMenu, NLayoutHeader, NImage, NIcon, NConfigProvider } from "naive-ui";
import { MenuSharp, CloseSharp } from "@vicons/ionicons5"; import { MenuSharp, CloseSharp } from "@vicons/ionicons5";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useHeaderMenuConfig } from "@/config/headerMenuConfig"; import { useHeaderMenuConfig } from "@/config/headerMenuConfig";
import LanguagePicker from "@/components/languagePicker/index.vue";
const themeOverrides = { const themeOverrides = {
Menu: { Menu: {
itemTextColorHover: "#000", itemTextColorHover: "#000",
@ -63,13 +90,45 @@ const themeOverrides = {
itemColorActiveHover: "#fff8fb", itemColorActiveHover: "#fff8fb",
}, },
}; };
const { t } = useI18n(); const { t, locale } = useI18n();
const router = useRouter(); const router = useRouter();
const isScrolled = ref(false); const isScrolled = ref(false);
const showMenu = ref(false); const showMenu = ref(false);
const selectedKey = ref(null); const selectedKey = ref(null);
// language picker
const showLanguagePicker = ref(false);
const selectedLanguage = ref(
localStorage.getItem("language") || locale.value || "en"
);
const languageOptions = computed(() => [
{ label: t("language.ja"), value: "ja", key: "ja" },
{ label: t("language.en"), value: "en", key: "en" },
{ label: t("language.zh"), value: "zh", key: "zh" },
{ label: t("language.zhTW"), value: "zh-TW", key: "zh-TW" },
]);
const currentLanguageLabel = computed(() => {
const found = languageOptions.value.find(
(opt) => opt.value === (locale.value || "en")
);
return found ? found.label : "English";
});
const openLanguagePicker = () => {
if (showMenu.value) {
return;
}
showLanguagePicker.value = true;
selectedLanguage.value = locale.value;
};
const handleConfirmLanguage = (lang) => {
locale.value = lang;
localStorage.setItem("language", lang);
showLanguagePicker.value = false;
};
const toggleMenu = () => { const toggleMenu = () => {
showMenu.value = !showMenu.value; showMenu.value = !showMenu.value;
}; };
@ -91,7 +150,7 @@ function findMenuOptionByKey(options, key) {
// //
const handleMenuSelect = (key) => { const handleMenuSelect = (key) => {
const option = findMenuOptionByKey(menuOptions, key); const option = findMenuOptionByKey(menuOptions.value, key);
if (option && option.href) { if (option && option.href) {
router.push(option.href); router.push(option.href);
showMenu.value = false; // showMenu.value = false; //
@ -99,7 +158,7 @@ const handleMenuSelect = (key) => {
}; };
// 使 // 使
const menuOptions = useHeaderMenuConfig(); const { menuOptions } = useHeaderMenuConfig();
// //
const handleScroll = () => { const handleScroll = () => {
@ -146,6 +205,22 @@ const handleToHome = () => {
justify-content: space-between; justify-content: space-between;
} }
.header-right {
display: flex;
align-items: center;
gap: 24 * 2.5px;
}
.lang-switch {
display: flex;
align-items: center;
gap: 11 * 2.5px;
font-weight: 600;
font-size: 14 * 2.5px;
cursor: pointer;
user-select: none;
}
.logo { .logo {
flex-shrink: 0; flex-shrink: 0;
margin-left: 11 * 2.5px; margin-left: 11 * 2.5px;

View File

@ -0,0 +1,48 @@
<script setup>
import { computed } from "vue";
import { useWindowSize } from "@vueuse/core";
import size375 from "@/components/customSelect/size375/index.vue";
import size768 from "@/components/customSelect/size768/index.vue";
import size1440 from "@/components/customSelect/size1440/index.vue";
import size1920 from "@/components/customSelect/size1920/index.vue";
const props = defineProps({
options: {
type: Array,
required: true,
},
modelValue: {
type: [String, Number],
required: true,
},
});
const emit = defineEmits(["update:modelValue"]);
const { width } = useWindowSize();
const viewComponent = computed(() => {
const viewWidth = width.value;
if (viewWidth <= 450) {
return size375;
} else if (viewWidth <= 1100) {
return size768;
} else if (viewWidth <= 1500) {
return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920;
}
});
</script>
<template>
<component
:is="viewComponent"
:options="props.options"
:modelValue="props.modelValue"
@update:modelValue="emit('update:modelValue', $event)"
/>
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,207 @@
<template>
<div class="custom-select-wrapper" v-click-outside="closeDropdown">
<div class="custom-select-display" @click="toggleDropdown">
<span class="selected-text">{{ selectedLabel }}</span>
<div class="select-arrow" :class="{ open: isOpen }">
<svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path
d="M1 1L5 4L9 1"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
<transition name="dropdown">
<div v-if="isOpen" class="custom-select-dropdown">
<div
v-for="option in props.options"
:key="option.value"
class="custom-select-option"
:class="{ selected: option.value === props.modelValue }"
@click="selectOption(option.value)"
>
{{ option.label }}
</div>
</div>
</transition>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
const props = defineProps({
options: {
type: Array,
required: true,
},
modelValue: {
type: [String, Number],
required: true,
},
});
const emit = defineEmits(["update:modelValue"]);
const isOpen = ref(false);
const selectedLabel = computed(() => {
const option = props.options.find((opt) => opt.value === props.modelValue);
return option ? option.label : "";
});
const toggleDropdown = () => {
isOpen.value = !isOpen.value;
};
const closeDropdown = () => {
isOpen.value = false;
};
const selectOption = (value) => {
emit("update:modelValue", value);
closeDropdown();
};
//
const vClickOutside = {
mounted(el, binding) {
el.clickOutsideEvent = (event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted(el) {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
</script>
<style scoped lang="scss">
.custom-select-wrapper {
position: relative;
width: 100%;
height: 46px;
user-select: none;
}
.custom-select-display {
width: 100%;
height: 100%;
padding: 7px 36px 7px 12px;
border: 1px solid #e0e0e6;
border-radius: 3px;
background: #fff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 16px;
line-height: 1.375em;
letter-spacing: 0.03em;
color: #455363;
cursor: pointer;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
&:hover {
border-color: #ff7bac;
}
}
.selected-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-arrow {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
&.open {
transform: translateY(-50%) rotate(180deg);
}
}
.custom-select-dropdown {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
max-height: 300px;
overflow-y: auto;
background: #fff;
border: 1px solid #e0e0e6;
border-radius: 3px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
//
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb:hover {
background: #fff0f5;
cursor: pointer;
}
}
.custom-select-option {
padding: 8px 12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 16px;
cursor: pointer;
transition: background 0.2s ease;
background: #fff;
&:hover {
background: rgba(204, 204, 204, 0.2);
}
&.selected {
color: #ff7bac;
font-weight: 500;
}
}
//
.dropdown-enter-active,
.dropdown-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.dropdown-enter-from {
opacity: 0;
transform: translateY(-10px);
}
.dropdown-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>

View File

@ -0,0 +1,207 @@
<template>
<div class="custom-select-wrapper" v-click-outside="closeDropdown">
<div class="custom-select-display" @click="toggleDropdown">
<span class="selected-text">{{ selectedLabel }}</span>
<div class="select-arrow" :class="{ open: isOpen }">
<svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path
d="M1 1L5 4L9 1"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
<transition name="dropdown">
<div v-if="isOpen" class="custom-select-dropdown">
<div
v-for="option in props.options"
:key="option.value"
class="custom-select-option"
:class="{ selected: option.value === props.modelValue }"
@click="selectOption(option.value)"
>
{{ option.label }}
</div>
</div>
</transition>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
const props = defineProps({
options: {
type: Array,
required: true,
},
modelValue: {
type: [String, Number],
required: true,
},
});
const emit = defineEmits(["update:modelValue"]);
const isOpen = ref(false);
const selectedLabel = computed(() => {
const option = props.options.find((opt) => opt.value === props.modelValue);
return option ? option.label : "";
});
const toggleDropdown = () => {
isOpen.value = !isOpen.value;
};
const closeDropdown = () => {
isOpen.value = false;
};
const selectOption = (value) => {
emit("update:modelValue", value);
closeDropdown();
};
//
const vClickOutside = {
mounted(el, binding) {
el.clickOutsideEvent = (event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted(el) {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
</script>
<style scoped lang="scss">
.custom-select-wrapper {
position: relative;
width: 100%;
height: 34px;
user-select: none;
}
.custom-select-display {
width: 100%;
height: 100%;
padding: 7px 36px 7px 12px;
border: 1px solid #e0e0e6;
border-radius: 3px;
background: #fff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 16px;
line-height: 1.375em;
letter-spacing: 0.03em;
color: #455363;
cursor: pointer;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
&:hover {
border-color: #ff7bac;
}
}
.selected-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-arrow {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
&.open {
transform: translateY(-50%) rotate(180deg);
}
}
.custom-select-dropdown {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
max-height: 300px;
overflow-y: auto;
background: #fff;
border: 1px solid #e0e0e6;
border-radius: 3px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
//
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb:hover {
background: #fff0f5;
cursor: pointer;
}
}
.custom-select-option {
padding: 8px 12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 16px;
cursor: pointer;
transition: background 0.2s ease;
background: #fff;
&:hover {
background: rgba(204, 204, 204, 0.2);
}
&.selected {
color: #ff7bac;
font-weight: 500;
}
}
//
.dropdown-enter-active,
.dropdown-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.dropdown-enter-from {
opacity: 0;
transform: translateY(-10px);
}
.dropdown-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>

View File

@ -0,0 +1,116 @@
<template>
<div class="custom-select-mobile">
<div class="search-select" @click="openYearPicker">
<div class="search-select-label">Year</div>
<div class="search-select-icon">
<span class="selected-year-label">{{ selectedLabel }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="10"
height="10"
viewBox="0 0 10 10"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.58882 9.69047C2.38633 9.48798 2.36383 9.17365 2.52132 8.9463L2.58882 8.86552L6.45447 5.00022L2.58882 1.13492C2.38633 0.932428 2.36383 0.618098 2.52132 0.390748L2.58882 0.309958C2.79132 0.107469 3.10565 0.0849685 3.33299 0.242458L3.41378 0.309958L7.69156 4.58774C7.89405 4.79023 7.91655 5.10456 7.75906 5.33191L7.69156 5.4127L3.41378 9.69047C3.18597 9.91828 2.81663 9.91828 2.58882 9.69047Z"
fill="black"
/>
</svg>
</div>
</div>
<year-wheel-picker
v-if="showYearPicker"
v-model="localValue"
:options="props.options"
@close="closeYearPicker"
@confirm="onYearConfirm"
></year-wheel-picker>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import YearWheelPicker from "@/components/YearWheelPicker.vue";
const props = defineProps({
options: {
type: Array,
required: true,
},
modelValue: {
type: [String, Number],
required: true,
},
});
const emit = defineEmits(["update:modelValue"]);
const showYearPicker = ref(false);
const localValue = ref(props.modelValue);
const selectedLabel = computed(() => {
const option = props.options.find((opt) => opt.value === props.modelValue);
return option ? option.label : "";
});
const openYearPicker = () => {
localValue.value = props.modelValue;
showYearPicker.value = true;
};
const closeYearPicker = () => {
showYearPicker.value = false;
};
const onYearConfirm = (year) => {
emit("update:modelValue", year);
closeYearPicker();
};
</script>
<style scoped lang="scss">
.custom-select-mobile {
width: 100%;
}
.search-select {
width: 100%;
height: 34 * 5.12px;
padding: 0 12px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
color: #455363;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
background: #fff;
}
.search-select-icon {
display: flex;
align-items: center;
gap: 8 * 5.12px;
}
.selected-year-label {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #000;
}
.search-select-label {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #000;
}
</style>

View File

@ -0,0 +1,207 @@
<template>
<div class="custom-select-wrapper" v-click-outside="closeDropdown">
<div class="custom-select-display" @click="toggleDropdown">
<span class="selected-text">{{ selectedLabel }}</span>
<div class="select-arrow" :class="{ open: isOpen }">
<svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path
d="M1 1L5 4L9 1"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
<transition name="dropdown">
<div v-if="isOpen" class="custom-select-dropdown">
<div
v-for="option in props.options"
:key="option.value"
class="custom-select-option"
:class="{ selected: option.value === props.modelValue }"
@click="selectOption(option.value)"
>
{{ option.label }}
</div>
</div>
</transition>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
const props = defineProps({
options: {
type: Array,
required: true,
},
modelValue: {
type: [String, Number],
required: true,
},
});
const emit = defineEmits(["update:modelValue"]);
const isOpen = ref(false);
const selectedLabel = computed(() => {
const option = props.options.find((opt) => opt.value === props.modelValue);
return option ? option.label : "";
});
const toggleDropdown = () => {
isOpen.value = !isOpen.value;
};
const closeDropdown = () => {
isOpen.value = false;
};
const selectOption = (value) => {
emit("update:modelValue", value);
closeDropdown();
};
//
const vClickOutside = {
mounted(el, binding) {
el.clickOutsideEvent = (event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value();
}
};
document.addEventListener("click", el.clickOutsideEvent);
},
unmounted(el) {
document.removeEventListener("click", el.clickOutsideEvent);
},
};
</script>
<style scoped lang="scss">
.custom-select-wrapper {
position: relative;
width: 100%;
height: 34 * 2.5px;
user-select: none;
}
.custom-select-display {
width: 100%;
height: 100%;
padding: 7 * 2.5px 36 * 2.5px 7 * 2.5px 12 * 2.5px;
border: 1 * 2.5px solid #e0e0e6;
border-radius: 3 * 2.5px;
background: #fff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 2.5px;
line-height: 1.375em;
letter-spacing: 0.48 * 2.5px;
color: #455363;
cursor: pointer;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
&:hover {
border-color: #ff7bac;
}
}
.selected-text {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select-arrow {
position: absolute;
right: 12 * 2.5px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.3s ease;
&.open {
transform: translateY(-50%) rotate(180deg);
}
}
.custom-select-dropdown {
position: absolute;
top: calc(100% + 4 * 2.5px);
left: 0;
right: 0;
max-height: 300 * 2.5px;
overflow-y: auto;
background: #fff;
border: 1 * 2.5px solid #e0e0e6;
border-radius: 3 * 2.5px;
box-shadow: 0 2 * 2.5px 8 * 2.5px rgba(0, 0, 0, 0.1);
z-index: 1000;
//
&::-webkit-scrollbar {
width: 5 * 2.5px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4 * 2.5px;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 4 * 2.5px;
}
&::-webkit-scrollbar-thumb:hover {
background: #fff0f5;
cursor: pointer;
}
}
.custom-select-option {
padding: 8 * 2.5px 12 * 2.5px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 2.5px;
cursor: pointer;
transition: background 0.2s ease;
background: #fff;
&:hover {
background: rgba(204, 204, 204, 0.2);
}
&.selected {
color: #ff7bac;
font-weight: 500;
}
}
//
.dropdown-enter-active,
.dropdown-leave-active {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.dropdown-enter-from {
opacity: 0;
transform: translateY(-10 * 2.5px);
}
.dropdown-leave-to {
opacity: 0;
transform: translateY(-10 * 2.5px);
}
</style>

View File

@ -0,0 +1,34 @@
<script setup>
import { computed } from "vue";
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 { t } = useI18n();
const viewComponent = computed(() => {
const viewWidth = width.value;
if (viewWidth <= 450) {
return size375;
} else if (viewWidth <= 1100) {
return size768;
} else if (viewWidth <= 1500) {
return size1440;
} else if (viewWidth <= 1920 || viewWidth > 1920) {
return size1920;
}
});
</script>
<template>
<component :is="viewComponent" />
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,59 @@
<template>
<div class="language-popover">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === modelValue }"
@click="selectLanguage(opt.value)"
>
{{ opt.label }}
</div>
</div>
</template>
<script setup>
defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "select"]);
function selectLanguage(lang) {
emit("update:modelValue", lang);
emit("select", lang);
}
</script>
<style scoped lang="scss">
.language-popover {
position: absolute;
top: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 8px;
z-index: 1000;
width: max-content;
min-width: 120px;
}
.picker-item {
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
border-radius: 6px;
text-align: center;
transition: background-color 0.2s;
&:hover {
background: #f5f5f5;
}
&.active {
background: #fddfea;
color: #000;
}
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<div class="language-popover">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === modelValue }"
@click="selectLanguage(opt.value)"
>
{{ opt.label }}
</div>
</div>
</template>
<script setup>
defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "select"]);
function selectLanguage(lang) {
emit("update:modelValue", lang);
emit("select", lang);
}
</script>
<style scoped lang="scss">
.language-popover {
position: absolute;
top: calc(100% + 10px);
left: 50%;
transform: translateX(-50%);
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 8px;
z-index: 1000;
width: max-content;
min-width: 120px;
}
.picker-item {
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
border-radius: 6px;
text-align: center;
transition: background-color 0.2s;
&:hover {
background: #f5f5f5;
}
&.active {
background: #fddfea;
color: #000;
}
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<Teleport to="body">
<div class="picker-mask" @click.self="emit('close')">
<div class="picker-panel">
<div class="picker-title">
{{ t("home.nav.please_select") }}
<svg
@click="emit('close')"
style="cursor: pointer"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.666016 9.74935C0.666016 4.59469 4.84469 0.416016 9.99935 0.416016C15.154 0.416016 19.3327 4.59469 19.3327 9.74935C19.3327 14.904 15.154 19.0827 9.99935 19.0827C4.84469 19.0827 0.666016 14.904 0.666016 9.74935Z"
fill="#CCCCCC"
/>
<path
d="M12.833 5.84961C13.1273 5.55596 13.6042 5.55565 13.8965 5.84863C14.1907 6.14223 14.1893 6.61848 13.8965 6.91211L11.0615 9.74609L13.9043 12.5898C14.1973 12.8848 14.1986 13.3607 13.9043 13.6543C13.6114 13.947 13.1344 13.9471 12.8408 13.6543L9.99707 10.8105L7.1582 13.6504C6.86386 13.9444 6.38729 13.9446 6.09375 13.6504C5.8002 13.3574 5.80045 12.8809 6.09473 12.5859L8.93359 9.74707L6.10254 6.91602C5.80956 6.62236 5.80889 6.1452 6.10254 5.85156C6.39486 5.55817 6.87209 5.55802 7.16699 5.85156L9.99805 8.68262L12.833 5.84961Z"
fill="white"
/>
</svg>
</div>
<div class="language-list">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === localValue }"
@click="localValue = opt.value"
>
{{ opt.label }}
</div>
</div>
<div class="picker-actions">
<button class="picker-confirm" @click="confirm">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
import { ref, watch, Teleport } from "vue";
import { useI18n } from "vue-i18n";
const props = defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "close", "confirm"]);
const { t } = useI18n();
const localValue = ref(props.modelValue);
watch(
() => props.modelValue,
(v) => {
localValue.value = v;
}
);
function confirm() {
emit("update:modelValue", localValue.value);
emit("confirm", localValue.value);
}
</script>
<style scoped lang="scss">
.picker-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.picker-panel {
width: 100%;
background: #fff;
box-shadow: 0 3 * 5.12px 14 * 5.12px rgba(0, 0, 0, 0.16);
padding: 16 * 5.12px;
position: fixed;
bottom: 0;
}
.picker-title {
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
color: #455363;
margin-bottom: 16 * 5.12px;
display: flex;
justify-content: space-between;
align-items: center;
}
.language-list {
border-top: 1px solid #ededed;
border-bottom: 1px solid #ededed;
}
.picker-item {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
color: #9da3ad;
display: flex;
justify-content: center;
align-items: center;
height: 44 * 5.12px;
border-top: 1px solid #f2f2f2;
}
.picker-item:first-child {
border-top: none;
}
.picker-item.active {
color: #000;
font-weight: 500;
}
.picker-actions {
margin-top: 16 * 5.12px;
}
.picker-confirm {
width: 100%;
height: 44 * 5.12px;
background: #ff7bac;
color: #fff;
border: none;
border-radius: 8 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
}
</style>

View File

@ -0,0 +1,141 @@
<template>
<Teleport to="body">
<div class="picker-mask" @click.self="emit('close')">
<div class="picker-panel">
<div class="picker-title">
{{ t("home.nav.please_select") }}
<svg
@click="emit('close')"
style="cursor: pointer"
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.666016 9.74935C0.666016 4.59469 4.84469 0.416016 9.99935 0.416016C15.154 0.416016 19.3327 4.59469 19.3327 9.74935C19.3327 14.904 15.154 19.0827 9.99935 19.0827C4.84469 19.0827 0.666016 14.904 0.666016 9.74935Z"
fill="#CCCCCC"
/>
<path
d="M12.833 5.84961C13.1273 5.55596 13.6042 5.55565 13.8965 5.84863C14.1907 6.14223 14.1893 6.61848 13.8965 6.91211L11.0615 9.74609L13.9043 12.5898C14.1973 12.8848 14.1986 13.3607 13.9043 13.6543C13.6114 13.947 13.1344 13.9471 12.8408 13.6543L9.99707 10.8105L7.1582 13.6504C6.86386 13.9444 6.38729 13.9446 6.09375 13.6504C5.8002 13.3574 5.80045 12.8809 6.09473 12.5859L8.93359 9.74707L6.10254 6.91602C5.80956 6.62236 5.80889 6.1452 6.10254 5.85156C6.39486 5.55817 6.87209 5.55802 7.16699 5.85156L9.99805 8.68262L12.833 5.84961Z"
fill="white"
/>
</svg>
</div>
<div class="language-list">
<div
v-for="opt in options"
:key="opt.value"
class="picker-item"
:class="{ active: opt.value === localValue }"
@click="localValue = opt.value"
>
{{ opt.label }}
</div>
</div>
<div class="picker-actions">
<button class="picker-confirm" @click="confirm">
{{ t("home.nav.confirm_select") }}
</button>
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
import { ref, watch, Teleport } from "vue";
import { useI18n } from "vue-i18n";
const props = defineProps({
modelValue: { type: String, required: true },
options: { type: Array, required: true },
});
const emit = defineEmits(["update:modelValue", "close", "confirm"]);
const { t } = useI18n();
const localValue = ref(props.modelValue);
watch(
() => props.modelValue,
(v) => {
localValue.value = v;
}
);
function confirm() {
emit("update:modelValue", localValue.value);
emit("confirm", localValue.value);
}
</script>
<style scoped lang="scss">
.picker-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.picker-panel {
width: 100%;
background: #fff;
box-shadow: 0 3 * 2.5px 14 * 2.5px rgba(0, 0, 0, 0.16);
padding: 16 * 2.5px;
position: fixed;
bottom: 0;
}
.picker-title {
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 2.5px;
color: #455363;
margin-bottom: 16 * 2.5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.language-list {
border-top: 1px solid #ededed;
border-bottom: 1px solid #ededed;
}
.picker-item {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 2.5px;
color: #9da3ad;
display: flex;
justify-content: center;
align-items: center;
height: 44 * 2.5px;
border-top: 1px solid #f2f2f2;
}
.picker-item:first-child {
border-top: none;
}
.picker-item.active {
color: #000;
font-weight: 500;
}
.picker-actions {
margin-top: 16 * 2.5px;
}
.picker-confirm {
width: 100%;
height: 44 * 2.5px;
background: #ff7bac;
color: #fff;
border: none;
border-radius: 8 * 2.5px;
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 2.5px;
}
</style>

View File

@ -1,9 +1,10 @@
import { computed } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
export const useHeaderMenuConfig = () => { export const useHeaderMenuConfig = () => {
const { t } = useI18n(); const { t } = useI18n();
return [ const menuOptions = computed(() => [
{ {
label: t("header_menu.corporate_information.title"), label: t("header_menu.corporate_information.title"),
key: "corporate_information", key: "corporate_information",
@ -72,12 +73,12 @@ export const useHeaderMenuConfig = () => {
{ {
label: t("header_menu.stock_information.stock_quote"), label: t("header_menu.stock_information.stock_quote"),
key: "stock_quote", key: "stock_quote",
href: '/stock-quote' href: "/stock-quote",
}, },
{ {
label: t("header_menu.stock_information.historic_stock_price"), label: t("header_menu.stock_information.historic_stock_price"),
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"),
@ -109,19 +110,23 @@ export const useHeaderMenuConfig = () => {
{ {
label: t("header_menu.investor_resources.ir_contacts"), label: t("header_menu.investor_resources.ir_contacts"),
key: "ir_contacts", key: "ir_contacts",
href: '/contacts' href: "/contacts",
}, },
{ {
label: t("header_menu.investor_resources.email_alerts"), label: t("header_menu.investor_resources.email_alerts"),
key: "email_alerts", key: "email_alerts",
href: '/email-alerts' href: "/email-alerts",
}, },
], ],
}, },
{ {
label: "Product Introduction", label: t("header_menu.product_introduction"),
key: "product-introduction", key: "product-introduction",
href: '/product-introduction', href: "/product-introduction",
}, },
]; ]);
return {
menuOptions,
};
}; };

View File

@ -1,10 +1,12 @@
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import en from '../locales/en' // import en from '../locales/en'
import zh from '../locales/zh' // import zh from '../locales/zh'
import ja from '../locales/ja' // import ja from '../locales/ja'
import zhTW from '../locales/zh-TW' // import zhTW from '../locales/zh-TW'
import de from '../locales/de' import en from '../locales/enc'
import zh from '../locales/zhc'
import ja from '../locales/jac'
import zhTW from '../locales/zh-TWc'
// 获取浏览器语言 // 获取浏览器语言
function getBrowserLanguage() { function getBrowserLanguage() {
const language = navigator.language || navigator.userLanguage const language = navigator.language || navigator.userLanguage
@ -20,14 +22,12 @@ function getBrowserLanguage() {
if (lang.includes('ja')) { if (lang.includes('ja')) {
return 'ja' return 'ja'
} }
if (lang.includes('de')) {
return 'de'
}
return 'en' // 默认英语 return 'en' // 默认英语
} }
// 直接设为英文 // 读取本地或浏览器语言,默认英文
const savedLanguage = 'en' const savedLanguage = (typeof localStorage !== 'undefined' && localStorage.getItem('language')) || getBrowserLanguage()
const i18n = createI18n({ const i18n = createI18n({
legacy: false, // 使用 Composition API legacy: false, // 使用 Composition API
locale: savedLanguage, locale: savedLanguage,
@ -37,7 +37,6 @@ const i18n = createI18n({
zh, zh,
ja, ja,
'zh-TW': zhTW, 'zh-TW': zhTW,
de
} }
}) })

View File

@ -1,455 +0,0 @@
export default {
common: {
submit: 'Einreichen',
cancel: 'Abbrechen',
confirm: 'Bestätigen'
},
language: {
zh: 'Vereinfachtes Chinesisch',
zhTW: 'Traditionelles Chinesisch',
en: 'Englisch',
ja: 'Japanisch',
de: 'Deutsch' // Hinzugefügt für die deutsche Version
},
home: {
nav: {
home: 'Startseite',
company: 'Unternehmensprofil',
businessintroduction: 'Geschäftsvorstellung',
please_select: 'Bitte auswählen',
confirm_select: 'Bestätigen Sie die Auswahl',
investor: 'Investor Guide'
},
scroll: {
tip: 'Nach unten scrollen'
},
section2: {
title1: 'FiEE Hand in Hand mit kreativen Künstlern',
title2: 'Start einer neuen Reise zu globalem Einfluss'
},
section3: {
label: 'Unternehmensprofil',
title: 'FiEE',
desc: 'Als innovativer Vorreiter, tief verwurzelt im Bereich modernster Technologien, erforscht FiEE kontinuierlich die Anwendung von KI und Big Data in der kreativen Kunst. Mit tiefem Verständnis künstlerischer Konzepte und fundierten Einblicken in kreative Praktiken erfassen wir präzise den Puls künstlerischer Entwicklungen. Mit großer Leidenschaft integrieren wir verschiedene Technologien und Ressourcen, um Künstlern eine umfassende Unterstützung von der Inspiration bis zur Werkpromotion zu bieten.',
features: {
data: {
title: 'Big Data für Kreation',
desc: 'Unsere Big-Data-Modelle analysieren den Kunstmarkt und bieten Künstlern klare Orientierung.'
},
ai: {
title: 'KI für grenzenlose Verbreitung',
desc: 'Mit KI-Algorithmen schaffen wir personalisierte Empfehlungen und erreichen präzise Zielgruppen.'
},
cloud: {
title: 'Cloud für künstlerischen Wert',
desc: 'Effiziente Datenverarbeitung durch Cloud-Computing unterstützt Künstler bei ihrer Entwicklung.'
},
cooperation: {
title: 'Professionelle Zusammenarbeit',
desc: 'Kooperationen mit Kunstinstitutionen helfen Künstlern, Anerkennung zu erlangen.'
},
promotion: {
title: 'Globale Markenbildung',
desc: 'Durch vielfältige Promotion unterstützen wir Künstler beim Aufbau internationaler Marken.'
}
}
},
section4: {
label: 'Geschäftsvorstellung',
title: 'Vielfältige Geschäfte im Zusammenspiel fördern den Aufstieg künstlerischen Einflusses',
title1: 'Vielfältige Geschäfte im Zusammenspiel',
title2: 'Fördern den Aufstieg künstlerischen Einflusses',
desc: 'FiEE konzentriert sich darauf, Künstlern globale Promotion und professionelle Betriebsdienste zu bieten. Durch präzise Positionierung und Multi-Plattform-Verknüpfung überwinden wir geografische Grenzen und bringen Werke auf die internationale Bühne. Mit einem starken Ressourcennetzwerk verbinden wir kommerzielle Möglichkeiten und nutzen KI und Big Data für präzises Marketing, um Zielgruppen effizient zu erreichen und Künstlern sowohl künstlerische als auch kommerzielle Durchbrüche zu ermöglichen.',
cards1: {
title: 'Globale Blumenindustrie, ideal für internationales Kunst-Austauschzentrum',
desc: 'FiEE verfolgt die Philosophie "Kunst ohne Grenzen" und nutzt Multi-Plattform-Verknüpfungen, um geografische Barrieren zu überwinden, Werke auf den Weltmarkt zu bringen, potenzielle Konsumenten anzuziehen und Künstler auf der internationalen Bühne erstrahlen zu lassen.'
},
cards2: {
title: 'Professionelles Betriebsteam, präzise Zielgruppenansprache',
desc: 'Das FiEE-Betriebsteam besteht aus erfahrenen Experten verschiedener Bereiche, die mit reicher Erfahrung und Marktverständnis durch Datenanalyse präzise Promotionsstrategien entwickeln, um Werke gezielt an die richtige Zielgruppe zu bringen und tiefe Resonanz zu erzeugen.'
},
cards3: {
title: 'Starkes Ressourcennetzwerk eröffnet unbegrenzte Geschäftsmöglichkeiten',
desc: 'FiEE hat tiefe Kooperationen mit weltweit bekannten Kunstinstitutionen und Mainstream-Medien aufgebaut, ein riesiges Ressourcennetzwerk geschaffen und hilft Künstlern, hochwertige Ressourcen zu nutzen und Geschäftsmöglichkeiten wie Werkautorisierung und Entwicklung von Derivaten zu erweitern.'
},
cards4: {
title: 'Technologiegetriebenes Marketing erreicht effizient Fan-Gruppen',
desc: 'FiEE nutzt KI-Algorithmen und Big-Data-Analysen, um Zielgruppen präzise darzustellen, personalisierte Promotionen zu realisieren, Fan-Gruppen schnell zu erreichen, loyale Fan-Communities aufzubauen und mit intelligenten Tools Promotionsstrategien zu optimieren, um die künstlerische Karriere zu fördern.'
}
}
},
companyprofil: {
slogan: {
title1: 'Führung durch den gesamten Kunstzyklus',
title2: 'Schaffung neuer Wertgipfel',
desc: 'FiEE strebt an, der Steuermann des gesamten Kunstschöpfungszyklus zu sein und tief in jeden kritischen Schritt involviert zu sein von der ersten Inspiration über die Fertigstellung des Werks bis hin zur Marktpromotion und kulturellen Verbreitung.'
},
intro: {
label: 'Unternehmensvorstellung',
title1: 'Einzigartig',
title2: 'Führender Anbieter ganzheitlicher Wertschöpfung',
desc: 'FiEE durchbricht die Grenzen traditioneller Einzel-Dienstleister radikal und tritt als Branchenveränderer auf, indem es modernste Technologien mit vielfältigen Ressourcen tief integriert. Wir haben ein "ganzheitliches" Wertschöpfungssystem aufgebaut, das den gesamten Prozess der künstlerischen Schöpfung von der ersten Idee bis zum strahlenden Erfolg durchdringt. Egal ob Sie ein Neuling mit Träumen sind, der gerade den Kunstpalast betritt, oder ein erfahrener Künstler, der nach einem Durchbruch strebt und höhere künstlerische Gipfel erklimmen möchte FiEE wird Ihr verlässlichster Partner sein, der Sie auf Ihrer künstlerischen Reise begleitet, vor Wind und Regen schützt und den Weg nach vorne weist.'
},
team: {
label: 'Teamvorstellung',
title1: 'Versammlung von Eliten',
title2: 'Antrieb für künstlerische Veränderung',
desc: 'Das FiEE-Team besteht aus Betriebsexperten, technischen Eliten und internationalen Beratern und bietet umfassende Unterstützung von der Inhaltsplanung bis zur globalen Promotion. Durch branchenübergreifende Zusammenarbeit und Ressourcenintegration durchbrechen wir traditionelle Grenzen und erkunden neue künstlerische Ausdrucksformen. Mit modernster Technologie und präzisem Marketing helfen wir Werken, sowohl kommerziellen Wert als auch gesellschaftlichen Einfluss zu steigern und der Kunstentwicklung nachhaltige Dynamik zu verleihen.',
features: {
global: {
title: 'Global vernetzt, vielfältig führend',
desc: 'Präzises Marketing im Ausland, Aufbau vielfältiger Kanäle, Schaffung internationaler Marken. Intelligentes Management multilingualer Plattformen und Bereitstellung lokalisierter Dienstleistungen.'
},
fans: {
title: 'Fans vertiefen, Ökosystem aufbauen',
desc: 'Fein abgestimmte Community-Betreuung, Bereitstellung vielfältiger Mehrwertdienste. Verbesserung von Community-Management-Tools, präzise Ansprache der Nutzer. Einführung innovativer Anreizmechanismen und Entwicklung charakteristischer Derivate.'
},
talent: {
title: 'Talente sammeln, Team verbessern',
desc: 'Rekrutierung von Technik- und Marketing-Eliten zur Förderung von Innovationskraft. Verstärkung interner Schulungen, Optimierung der Organisationsstruktur. Einführung fortschrittlicher Konzepte zur Steigerung von Management- und Serviceeffizienz.'
}
}
},
achievement: {
label: 'Herausragende Errungenschaften',
title: 'Mit Pioniergeist zum Gipfel der Kunst',
title1: 'Mit Pioniergeist',
title2: 'Zum Gipfel der Kunst',
desc: 'Durch langjährige Arbeit im Kunstbereich erweitert FiEE kontinuierlich sein Geschäftsfeld, sammelt umfangreiche Branchenressourcen und baut ein weitreichendes Kooperationsnetzwerk auf. Derzeit arbeiten wir tiefgehend mit mehreren weltweit beliebten Social-Media-Plattformen zusammen, um Künstlern aus verschiedenen Perspektiven zu helfen, auf der internationalen Bühne zu glänzen und ihre einzigartige künstlerische Strahlkraft zu entfalten.',
certification: {
title1: 'Offizielle Qualifikationszertifizierung',
title2: 'Solide Grundlage für künstlerische Karrieren',
desc: 'FiEE bietet professionelle und offizielle Zertifizierungsdienste, die Künstlern helfen, branchenweit anerkannte Qualifikationen zu erlangen. Dies steigert nicht nur den Wert ihrer Werke erheblich, sondern hebt sie auch im hart umkämpften Kunstmarkt hervor, stärkt ihre Markt wettbewerbsfähigkeit erheblich und legt ein stabiles Fundament für ihre Karriere.'
},
platform: {
title1: 'Globale Plattformmatrix',
title2: 'Erweiterung der Grenzen künstlerischer Verbreitung',
desc: 'Mit tiefen Branchenressourcen und einem breiten Kooperationsnetzwerk hat FiEE tiefe strategische Partnerschaften mit über 30 weltweit beliebten Social-Media-Plattformen aufgebaut. Von international bekannten sozialen Plattformen bis hin zu spezialisierten Medien im Kunstbereich erstellen wir maßgeschneiderte Konten für Künstler und nutzen fortschrittliche Optimierungsstrategien, um ihre Konten unter vielen Schaffenden hervorzuheben.'
}
},
news: {
label: 'Fokus auf aktuelle Entwicklungen bei FiEE',
title: 'Trends erkennen, Leuchtturm für die künstlerische Zukunft',
title1: 'Trends erkennen',
title2: 'Leuchtturm für die künstlerische Zukunft',
desc: 'FiEE ist fest im Kunstbereich verwurzelt und folgt stets den globalen Trends der Kunstwelt. Durch tiefgehende Fallanalysen und branchenübergreifende Seminare erforschen wir die tiefe Integration von Kunst mit Technologie und Wirtschaft und bieten vorausschauende Einblicke und Inspiration für die zukünftige Entwicklung der Kunst.',
carousel: {
item1: {
title: 'Technologischer Durchbruch erzielt, führend in der technischen Revolution der Kunstschöpfung',
desc: 'In einer Zeit, in der die digitale Welle den Kunstbereich mit noch nie dagewesener Kraft durchzieht, erlebt die künstlerische Schöpfung tiefgreifende Veränderungen. Mit unermüdlichem Einsatz und Innovationsgeist hat FiEE im Bereich der kreativen Technologien einen bahnbrechenden Meilenstein erreicht. Nach zahllosen Tagen und Nächten harter Arbeit wurde das selbstentwickelte, neue intelligente Schöpfungsassistenzsystem von FiEE offiziell eingeführt ein strahlender neuer Stern, der neue Entwicklungspfade für die Kunst eröffnet und der gesamten Branche beispiellose Veränderungen und Chancen bringt.'
},
item2: {
title: 'Globale Strategie verbessert, FiEE erreicht strategische Zusammenarbeit mit über 30 internationalen Plattformen und schafft eine neue Matrix der künstlerischen Verbreitung',
desc: 'In einer Ära, in der globale kulturelle Verschmelzung mit noch nie dagewesener Geschwindigkeit voranschreitet, ist der Kunstbereich zur vordersten Front des kulturellen Austauschs und der Innovation geworden. Als führendes Unternehmen in der Kunstbranche stürzt sich FiEE mit scharfem strategischen Blick und entschlossener Tatkraft in diese Welle des globalen kulturellen Austauschs. Kürzlich gab FiEE mit Begeisterung bekannt, dass es eine tiefe strategische Zusammenarbeit mit TikTok, Instagram und über 30 weiteren führenden internationalen Social-Media-Plattformen erreicht hat und gleichzeitig das weitreichende "Kunst ohne Grenzen"-Programm startet, entschlossen, kulturelle Barrieren zwischen Regionen zu beseitigen.'
},
item3: {
title: 'Unterstützung für Künstler, FiEE veröffentlicht "KI × Kurzvideo"-Gesamtlösung',
desc: 'Für Künstler sind kreative Blockaden und geringe Effizienz zwei große Hindernisse auf ihrem Weg. Die Entwicklung eines ansprechenden Kurzvideo-Skripts erfordert oft enorme Zeit- und Energieaufwände; Künstler müssen nicht nur einzigartige Ideen ersinnen, sondern auch die Vorlieben des Publikums und Markttrends berücksichtigen. Die Verbreitung und Promotion eines fertigen Werks ist ebenfalls eine schwer zu überwindende Kluft.'
},
item4: {
title: 'FiEE startet 18-monatiges Kunst-KOL-Inkubationsprogramm, um zukünftige Stars im Kunstbereich zu fördern',
desc: 'In einer Zeit, in der die Kunstindustrie boomt, wird die Bedeutung hochwertiger Inhaltsschaffender immer deutlicher. Als Vorreiter in der Branche achtet FiEE stets auf die Talentförderung und Entwicklungen im Kunstbereich. Heute gab FiEE offiziell den Start eines sorgfältig geplanten 18-monatigen Kunst-KOL-Inkubationsprogramms bekannt, das darauf abzielt, eine Gruppe einflussreicher zukünftiger Stars im Kunstbereich zu fördern und die Innovationsentwicklung der gesamten Branche voranzutreiben.'
},
item5: {
title: 'Vielfältige Talente vereint, FiEE legt den Grundstein für die innovative Entwicklung der Kunst',
desc: 'Im goldenen Zeitalter des aktuellen Kunstbooms, in dem hundert Blumen blühen, ist Innovation zweifellos der Schlüssel, um die Branche kontinuierlich voranzutreiben und traditionelle Muster zu durchbrechen. Die Wurzeln liegen jedoch bei den Talenten, deren Bedeutung als Kernkraft der Innovation unbestreitbar ist. FiEE erkennt dies klar und sammelt mit offenem Geist und weitsichtiger Strategie Talente, indem es Betriebsexperten, technische Eliten und internationale Berater zusammenbringt.'
}
}
}
},
companyprofildetail: {
article1: {
title: 'Technologischer Durchbruch erzielt, führend in der technischen Revolution der Kunstschöpfung',
date: '7. Februar 2025, 12:00 Uhr',
content: [
'In einer Zeit, in der die digitale Welle den Kunstbereich mit noch nie dagewesener Kraft durchzieht, erlebt die künstlerische Schöpfung tiefgreifende Veränderungen. Mit unermüdlichem Einsatz und Innovationsgeist hat FiEE im Bereich der kreativen Technologien einen bahnbrechenden Meilenstein erreicht.',
'Nach zahllosen Tagen und Nächten harter Arbeit wurde das selbstentwickelte, neue intelligente Schöpfungsassistenzsystem von FiEE offiziell eingeführt ein strahlender neuer Stern, der neue Entwicklungspfade für die Kunst eröffnet und der gesamten Branche beispiellose Veränderungen und Chancen bringt.',
'Während des Entwicklungsprozesses stand das technische Team vor zahlreichen schwierigen Herausforderungen. Beim Sammeln riesiger Mengen an Kunstwerksdaten mussten Daten aus verschiedenen Epochen, Stilen und Typen gesammelt werden, darunter literarische Meisterwerke, filmische Klassiker und musikalische Werke aus aller Welt Quellen, die weit verstreut und in vielfältigen Formaten vorhanden waren, was die Sammlung äußerst schwierig machte. Bei der Organisation und Analyse war es entscheidend, die Genauigkeit und Vollständigkeit der Daten sicherzustellen und gleichzeitig verschiedene Datentypen zu kategorisieren und zu integrieren, um eine spätere tiefere Analyse zu ermöglichen.',
'Die Optimierung der künstlichen Intelligenz-Algorithmen war ebenfalls keine einfache Aufgabe; das Team musste die Algorithmusparameter kontinuierlich anpassen, um wertvolle Informationen präzise aus riesigen Datenmengen extrahieren zu können. Der Aufbau des Big-Data-Modells war komplex und musste Stabilität, Skalierbarkeit und Kompatibilität mit anderen Technologien berücksichtigen.',
'Die Teammitglieder durchforsteten umfangreiche nationale und internationale Materialien, von modernsten wissenschaftlichen Papieren bis hin zu Praxisbeispielen der Branche, und ließen keine mögliche Inspirationsquelle außer Acht. Sie führten unzählige Experimente und Simulationen durch; jedes Experiment konnte scheitern, doch sie gaben niemals auf und optimierten ihre Ansätze kontinuierlich.',
'Zum Beispiel wurde bei der Handhabung der Kompatibilität verschiedener Kunstwerksdaten ein innovativer multidimensionaler Datenklassifikationsalgorithmus eingesetzt. Dieser Algorithmus analysierte Daten aus mehreren Perspektiven Zeitdimension, Stildimension, Themen dimension und kategorisierte scheinbar chaotische Daten effektiv, um eine präzise Analyse und Nutzung sicherzustellen und eine solide Grundlage für die nachfolgende intelligente Schöpfungsunterstützung zu schaffen. Dieses intelligente Schöpfungsassistenzsystem integriert modernste Technologien wie künstliche Intelligenz und Big Data und bietet Künstlern umfassende, intelligente Unterstützung. Durch die tiefgehende Analyse riesiger Mengen an Kunstwerksdaten kann das System die aktuellen Trends und Hotspots der künstlerischen Schöpfung präzise erkennen.',
'In Bezug auf die Steigerung der Schöpfungseffizienz zeigt die künstliche Intelligenz des Systems beeindruckende Stärke. Während traditionelle Schöpfungsmethoden Monate für die Entwicklung eines Rahmens benötigen könnten, kann dieses System in nur wenigen Tagen einen vorläufigen Rahmen erstellen, den Künstler nur noch verfeinern müssen, was den Schöpfungszyklus erheblich verkürzt.',
'Der Leiter des technischen Entwicklungsteams konnte bei der Pressekonferenz seine Aufregung nicht verbergen: "Dieser technologische Durchbruch ist das Ergebnis der langjährigen harten Arbeit unseres Teams und ein kühner Versuch von FiEE, die Technologie der Kunstschöpfung zu revolutionieren. Wir wissen, dass in einer Zeit rapides technologischen Fortschritts nur kontinuierliche Innovation Künstlern mächtigere Werkzeuge bieten kann, um die Kunstschöpfung in Richtung hoher Qualität und Effizienz zu lenken."',
'Branchenexperten kommentierten: "Dieser technologische Durchbruch von FiEE bringt zweifellos neue Vitalität in den Bereich der Kunstschöpfung. Er steigert nicht nur die Schöpfungseffizienz, sondern eröffnet Künstlern auch völlig neue kreative Ansätze und bietet wertvolle Erfahrungen und Referenzen für die digitale Transformation der gesamten Branche."',
'Dieser technologische Durchbruch hat die Wettbewerbsfähigkeit von FiEE im Bereich der Kunsttechnologie erheblich gesteigert und das Unternehmen zu einem technischen Marktführer gemacht. In Zukunft wird FiEE weiterhin in die technologische Forschung investieren, Talente rekrutieren und Innovationen erforschen, um der digitalen Transformation der Kunstindustrie mehr und bessere technische Unterstützung zu bieten und die Kunstschöpfung zu neuen Höhen zu führen.'
]
},
article2: {
title: 'Globale Strategie verbessert, FiEE erreicht strategische Zusammenarbeit mit über 30 internationalen Plattformen und schafft eine neue Matrix der künstlerischen Verbreitung',
date: '10. Februar 2025, 10:30 Uhr',
content: [
'In einer Ära, in der globale kulturelle Verschmelzung mit noch nie dagewesener Geschwindigkeit voranschreitet, ist der Kunstbereich zur vordersten Front des kulturellen Austauschs und der Innovation geworden. Als führendes Unternehmen in der Kunstbranche stürzt sich FiEE mit scharfem strategischen Blick und entschlossener Tatkraft in diese Welle des globalen kulturellen Austauschs.',
'Kürzlich gab FiEE mit Begeisterung bekannt, dass es eine tiefe strategische Zusammenarbeit mit TikTok, Instagram und über 30 weiteren führenden internationalen Social-Media-Plattformen erreicht hat und gleichzeitig das weitreichende "Kunst ohne Grenzen"-Programm startet, entschlossen, kulturelle Barrieren zwischen Regionen zu beseitigen und Künstlern nachhaltig zu helfen, die weite Weltbühne zu betreten. Diese tiefgehende Zusammenarbeit von FiEE mit über 30 weltweit führenden Plattformen ist ein umfassendes und tiefgreifendes Fest der Ressourcenintegration und kollaborativen Innovation.',
'Im Bereich der sozialen Medien ist die Zusammenarbeit mit TikTok ein großes Highlight. TikTok zieht mit seinem einzigartigen Kurzvideo-Ökosystem und seinem leistungsstarken Algorithmus-Empfehlungssystem weltweit Milliarden von Nutzern an. Über diese Plattform kann FiEE die beeindruckenden Werke von Künstlern in hoch ansteckenden Kurzvideoformaten schnell und präzise an kunstinteressierte Nutzer weltweit weiterleiten, geografische Grenzen mühelos durchbrechen und weltweite Interaktion und Aufmerksamkeit hervorrufen.',
'Instagram bietet mit seiner exquisiten visuellen Präsentation und lebendigen sozialen Atmosphäre ein perfektes Schaufenster für verschiedene Kunstwerke. Hier können die Werke der Künstler auf höchstem Niveau präsentiert werden, massenhaft Aufmerksamkeit und Likes von Fans ernten, die Sichtbarkeit und den Einfluss der Werke erheblich steigern und die Schönheit der Kunst weltweit verbreiten. In spezialisierten Gemeinschaften bietet die Zusammenarbeit mit Plattformen wie ArtStation Künstlern aus verschiedenen Kunstbereichen einen professionellen und reinen Raum für Austausch und Präsentation.',
'Um sicherzustellen, dass das "Kunst ohne Grenzen"-Programm effizient und geordnet umgesetzt wird, hat FiEE eine Reihe detaillierter und praktikabler Strategien und Maßnahmen entwickelt. Im Bereich der Inhaltserstellung wird FiEE international bekannte Kunstexperten und Künstler einladen, Online- und Offline-Trainings sowie Führungskurse abzuhalten, um Künstlern zu helfen, die ästhetischen Vorlieben, kulturellen Bedürfnisse und populären Trends des internationalen Marktes tief zu verstehen und so die internationale Verbreitungskraft ihrer Werke zu steigern.',
'Im Bereich der Promotions- und Betriebsführung wird FiEE die Vorteile verschiedener Ressourcen bündeln und umfassende, mehrdimensionale Promotionspläne speziell für Künstler erstellen. Einerseits werden aktiv Online-Schöpfungswettbewerbe und andere Aktivitäten organisiert, um weltweite Aufmerksamkeit zu erregen und die Bekanntheit und den Einfluss der Künstler schnell zu steigern; andererseits werden Werbeanzeigen auf Social-Media-Plattformen, Themenmarketing und Kooperationen mit Influencern voll ausgeschöpft, um die Werke der Künstler präzise zu bewerben, den Verbreitungsbereich zu erweitern und mehr Menschen den Genuss hervorragender künstlerischer Schöpfungen zu ermöglichen.',
'Es ist absehbar, dass diese Zusammenarbeit für die Mehrheit der Künstler eine einmalige Entwicklungschance darstellt. Sie werden die Gelegenheit haben, tiefgehende Austausche und Zusammenarbeiten mit weltweit führenden Künstlern durchzuführen, Zugang zu den modernsten Schöpfungskonzepten und -techniken zu erhalten, ihren kreativen Horizont zu erweitern und ihr Schöpfungsniveau zu verbessern. Gleichzeitig können ihre Werke durch die vielfältige Plattformmatrix eine noch nie dagewesene Sichtbarkeit und Anerkennung erlangen, ihren kommerziellen Wert maximieren und breitere Entwicklungsmöglichkeiten für ihre künstlerische Karriere eröffnen.',
'Mit Blick in die Zukunft wird FiEE entschlossen die Zusammenarbeit mit internationalen Plattformen weiter vertiefen, das "Kunst ohne Grenzen"-Programm kontinuierlich optimieren und verbessern und weiterhin qualitativ hochwertigere, umfassendere und effizientere Dienstleistungen für Künstler anbieten, um ihnen zu helfen, auf der internationalen Bühne noch strahlender zu glänzen und mehr Weisheit und Kraft zur Förderung des globalen kulturellen Austauschs, der Integration und des Wohlstands beizutragen.'
]
},
article3: {
title: 'Unterstützung für Künstler, FiEE veröffentlicht "KI × Kurzvideo"-Gesamtlösung',
date: '14. Februar 2025, 12:30 Uhr',
content: [
'Für Künstler sind kreative Blockaden und geringe Effizienz zwei große Hindernisse auf ihrem Weg. Die Entwicklung eines ansprechenden Kurzvideo-Skripts erfordert oft enorme Zeit- und Energieaufwände; Künstler müssen nicht nur einzigartige Ideen ersinnen, sondern auch die Vorlieben des Publikums und Markttrends berücksichtigen.',
'Die Verbreitung und Promotion eines fertigen Werks ist ebenfalls eine schwer zu überwindende Kluft. In einer Ära der Informationsflut ist es eine Herausforderung, die eigenen Werke aus der Masse herausstechen zu lassen und präzise die Zielgruppe zu erreichen ein Problem, über das Künstler Tag und Nacht nachdenken.',
'Traditionelle Verbreitungsmethoden sind wie das Fischen mit einem Netz die Präzision lässt stark zu wünschen übrig, und viele qualitativ hochwertige künstlerische Inhalte gehen im Verbreitungsprozess verloren, ohne von wirklich interessierten Zielgruppen entdeckt zu werden. Zudem führt das Fehlen von Nutzerinteraktivität dazu, dass Künstler die Bedürfnisse der Zielgruppe nicht tiefgehend erforschen können, keine enge emotionale Verbindung zum Publikum aufbauen können und ihre Werke inmitten der Verbreitungswelle isoliert bleiben.',
'In einer Zeit rapide technologischen Fortschritts verändern künstliche Intelligenz und die Kurzvideo-Industrie das Muster der künstlerischen Schöpfung und Verbreitung mit beispielloser Geschwindigkeit.',
'Als innovativer Vorreiter im Kunstbereich hält FiEE mit der Zeit Schritt, erforscht aktiv die tiefe Integration von Technologie und Kunst und veröffentlicht heute offiziell die "KI × Kurzvideo"-Gesamtlösung, die eine revolutionäre Veränderung in der Kunstbranche einleitet.',
'Diese Lösung integriert mehrere modernste KI-Technologien und bietet zahlreiche bedeutende Vorteile. Im Bereich der Inhaltserstellung kann das KI-gestützte Skriptgenerierungstool basierend auf den vom Künstler eingegebenen Schlüsselinformationen wie Thema, Stil und Zielgruppe schnell kreative Skripte erstellen. Durch das Lernen aus einer Vielzahl hervorragender Kurzvideo-Skripte und künstlerischer Werke kann die KI nicht nur neuartige Story-Rahmen bieten, sondern auch präzise passende Handlungsstränge und Dialoge liefern, was den Schöpfungszyklus erheblich verkürzt.',
'Im Bereich der Inhaltsverbreitung und -betriebsführung macht die KI-basierte Big-Data-Analysetechnologie die Promotion von Kurzvideos präziser und effektiver. Durch die tiefgehende Analyse multidimensionaler Daten wie Browserverläufe, Like- und Kommentarverhalten sowie Abonnementlisten der Nutzer kann das System die Interessensvorlieben und Bedürfnisse der Nutzer präzise erkennen und die Werke der Künstler an wirklich interessierte Zielgruppen weiterleiten. Die Anwendungsszenarien dieser Lösung sind vielfältig und sowohl professionelle Künstler als auch Amateure mit künstlerischen Träumen können davon profitieren.',
'Professionelle Künstler können mithilfe der KI-Technologie kreative Blockaden überwinden, die Schöpfungseffizienz steigern und innovativere und einflussreichere Werke schaffen; Amateure können durch diese Lösung die Einstiegshürden für die Schöpfung senken, ihre kreativen Ideen mühelos umsetzen und die Freude am Schaffen genießen. Ein FiEE-Verantwortlicher erklärte: "Wir haben stets die Bedürfnisse und Schwierigkeiten der Künstler im Blick und hoffen, ihnen mit der "KI × Kurzvideo" -Gesamtlösung umfassende Unterstützung und Hilfe zu bieten. Dies ist nicht nur eine innovative Anwendung von Technologie, sondern auch eine bedeutende Praxis zur Förderung der Kunstbranche. Wir glauben, dass Künstler mit der Unterstützung der KI-Technologie mehr hervorragende Werke schaffen können und das Licht der Kunst eine noch breitere Welt erleuchten wird."',
'Mit Blick in die Zukunft wird FiEE weiterhin Entwicklungsressourcen einsetzen, die "KI × Kurzvideo"-Gesamtlösung kontinuierlich optimieren und verbessern, weitere Anwendungsszenarien der KI-Technologie im Kunstbereich erforschen, die Zusammenarbeit mit Branchenpartnern stärken und gemeinsam die Innovation in der künstlerischen Schöpfung und Verbreitung vorantreiben, um Künstlern eine qualitativ hochwertigere und effizientere Schöpfungsumgebung zu bieten und die Kunstbranche zu neuen Höhen zu führen.'
]
},
article4: {
title: 'FiEE startet 18-monatiges Kunst-KOL-Inkubationsprogramm, um zukünftige Stars im Kunstbereich zu fördern',
date: '19. Februar 2025, 12:00 Uhr',
content: [
'In einer Zeit, in der die Kunstindustrie boomt, wird die Bedeutung hochwertiger Inhaltsschaffender immer deutlicher. Als Vorreiter in der Branche achtet FiEE stets auf die Talentförderung und Entwicklungen im Kunstbereich.',
'Heute gab FiEE offiziell den Start eines sorgfältig geplanten 18-monatigen Kunst-KOL-Inkubationsprogramms bekannt, das darauf abzielt, eine Gruppe einflussreicher zukünftiger Stars im Kunstbereich zu fördern und die Innovationsentwicklung der gesamten Branche voranzutreiben. Mit dem Aufstieg sozialer Medien und der Veränderung der Inhaltsverbreitungsmethoden spielen KOLs eine Schlüsselrolle in der Verbreitung und Promotion im Kunstbereich. Sie sind nicht nur Schöpfer hochwertiger Inhalte, sondern auch die Brücke, die Kunstwerke mit einem breiten Publikum verbindet.',
'Jedoch ist die Entwicklung eines ausgereiften und weitreichend einflussreichen Kunst-KOLs kein einfacher Prozess und erfordert systematische Planung, professionelle Anleitung und ausreichende praktische Möglichkeiten. FiEE hat diesen Marktbedarf scharfsinnig erkannt, erhebliche Ressourcen investiert und dieses 18-monatige Inkubationsprogramm sorgfältig geplant. Dieses Inkubationsprogramm hat klare Ziele: potenzialreiche Künstler im Kunstbereich zu entdecken und zu fördern und ihnen zu helfen, zu national und international anerkannten und einflussreichen Kunst-KOLs heranzuwachsen.',
'Durch dieses Programm wird nicht nur den Künstlern ein breiter Entwicklungsraum geboten, sondern auch frisches Blut in den Kunstmarkt gebracht und die diversifizierte Verbreitung von Kunstwerken gefördert. Inhaltlich deckt das Programm mehrere Dimensionen ab und unterstützt das Wachstum der Künstler umfassend. Im Bereich der professionellen Schulung hat das Unternehmen bekannte Experten und Gelehrte aus verschiedenen Kunstbereichen eingeladen, um den Künstlern systematische und tiefgehende Kurse anzubieten.',
'Diese Kurse umfassen eine breite Palette an Inhalten, einschließlich der Verbesserung künstlerischer Schöpfungstechniken wie die Verwendung von Farben in der Malerei oder die Melodiegestaltung in der Musik, sowie Kenntnisse im Bereich des neuen Medienbetriebs wie Social-Media-Marketingstrategien und Fan-Interaktionstechniken sowie Kurse im Bereich des Geschäftsmanagements, die den Künstlern helfen, die Funktionsweise des Kunstmarktes zu verstehen und den kommerziellen Wert ihrer Werke zu realisieren. Gleichzeitig hat das Unternehmen jedem Künstler einen persönlichen Mentor zur Seite gestellt, der maßgeschneiderte Ratschläge und Unterstützung basierend auf den individuellen Merkmalen und Bedürfnissen der Künstler bietet, um eine personalisierte Anleitung zu gewährleisten.',
'Die Verbindung zu Ressourcen ist ebenfalls ein wichtiger Bestandteil des Inkubationsprogramms. Mit seiner jahrelangen Erfahrung in der Branche und einem umfangreichen Netzwerk an Partnerschaften baut FiEE Brücken zwischen Künstlern und Marken, Medien und Plattformen auf. Die Künstler haben die Möglichkeit, mit bekannten Marken zusammenzuarbeiten, um künstlerisch wertvolle Werbungen zu schaffen, mit Medien für Kunstreportagen und Programmproduktionen zu kooperieren und mit großen Kunstplattformen zusammenzuarbeiten, um ihre Werke zu veröffentlichen und ihre Verbreitungsreichweite zu erweitern.',
'Der Leiter des FiEE-Kunst-KOL-Inkubationsprojekts erklärte bei der Eröffnungszeremonie: "Wir sind überzeugt, dass jeder Teilnehmer des Inkubationsprogramms unbegrenztes Potenzial hat. Diese 18 Monate werden eine entscheidende Wachstumsphase für sie sein. FiEE wird ihnen volle Unterstützung und Hilfe bieten, damit sie auf ihrem Weg der künstlerischen Schöpfung immer weiter vorankommen und zu tragenden Säulen im Kunstbereich werden."',
'Mit Blick in die Zukunft wird die Umsetzung dieses 18-monatigen Kunst-KOL-Inkubationsprogramms voraussichtlich eine Gruppe von KOLs mit einzigartigem Stil und breitem Einfluss im Kunstbereich hervorbringen.',
'Sie werden durch ihre Werke und ihren Einfluss mehr Menschen dazu bringen, sich für Kunst zu interessieren, die Verbreitung und Innovation von Kunstwerken fördern und einen wichtigen Beitrag zur blühenden Entwicklung der Kunstindustrie leisten. FiEE wird auch weiterhin das Wachstum der Künstler im Auge behalten, das Inkubationsprogramm kontinuierlich optimieren und weitere effektive Modelle für die Talentförderung im Kunstbereich erkunden.'
]
},
article5: {
title: 'Vielfältige Talente vereint, FiEE legt den Grundstein für die innovative Entwicklung der Kunst',
date: '20. Februar 2025, 12:00 Uhr',
content: [
'Im goldenen Zeitalter des aktuellen Kunstbooms, in dem hundert Blumen blühen, ist Innovation zweifellos der Schlüssel, um die Branche kontinuierlich voranzutreiben und traditionelle Muster zu durchbrechen. Die Wurzeln liegen jedoch bei den Talenten, deren Bedeutung als Kernkraft der Innovation unbestreitbar ist.',
'FiEE erkennt dies klar und sammelt mit offenem Geist und weitsichtiger Strategie Talente, indem es Betriebsexperten, technische Eliten und internationale Berater zusammenbringt und erfolgreich ein Team mit hervorragender Fachkompetenz und vielfältiger Wissensstruktur aufbaut, das eine unzerstörbare Grundlage für die innovative Reise des Unternehmens im Kunstbereich schafft.',
'Das Team der Betriebsexperten ist sozusagen der Leuchtturm, der FiEE auf seinem stabilen Weg im Kunstbereich führt. Sie haben über viele Jahre im weiten Feld der Kunstindustrie gearbeitet, mit reicher praktischer Erfahrung und einem tiefen Verständnis des Marktes eine scharfe Fähigkeit entwickelt, Marktdynamiken zu erfassen, und ein präzises Gespür für die Vorlieben des Publikums erlangt.',
'Sie nutzen Big-Data-Analysen und Marktforschung vor Ort, um die kulturellen Hintergründe und Interessensvorlieben verschiedener Altersgruppen und Regionen tief zu untersuchen, Zielgruppen präzise zu positionieren und Künstlern höchst gezielte Schöpfungsrichtungen zu bieten. Sie analysieren gründlich aktuelle gesellschaftliche Trends und die spirituellen Bedürfnisse des Publikums, integrieren geschickt Elemente der Zeit und machen Werke sowohl tiefgründig als auch ansprechend. Durch einzigartige kreative Planung ziehen sie vom Projektstart an die Aufmerksamkeit des Publikums auf sich und legen eine solide Grundlage für den späteren Erfolg.',
'Mit der zunehmenden Verschmelzung von Technologie und Kunst werden technische Eliten zur starken Triebkraft für die innovative Entwicklung von FiEE.',
'Das Unternehmen rekrutiert aktiv Fachkräfte mit Kenntnissen in künstlicher Intelligenz, Big Data und virtueller Realität, um der künstlerischen Schöpfung technologische Vitalität einzuhauchen. Im Schöpfungsprozess nutzen diese technischen Eliten voll und ganz die Vorteile KI-gestützter Schöpfungswerkzeuge, um Künstlern kontinuierliche Inspiration zu bieten. Durch den Einsatz von Virtual Reality (VR) und Augmented Reality (AR) wird eine tiefe Interaktion zwischen Werken und Publikum ermöglicht, wodurch die Ausdrucksformen und der Verbreitungsbereich von Kunstwerken erheblich erweitert werden.',
'In der Ära der Globalisierung bringen internationale Beraterteams FiEE eine breite globale Perspektive und vielfältige kulturelle Blickwinkel. Sie kommen aus aller Welt und verfügen über tiefes Wissen über die Marktregeln, ästhetischen Vorlieben und kulturellen Unterschiede verschiedener Länder und Regionen. Im Prozess, lokale hervorragende Kunstwerke auf die Weltbühne zu bringen, spielen die internationalen Beraterteams eine Schlüsselrolle. Dies bringt nicht nur neue kreative Ansätze und Inspiration für lokale Künstler, sondern steigert auch den Einfluss von FiEE im internationalen Kunstbereich.',
'Genau wegen der Zusammenführung vielfältiger Talente wie Betriebsexperten, technischen Eliten und internationalen Beratern kann FiEE durch branchenübergreifende Zusammenarbeit und Ressourcenintegration traditionelle künstlerische Grenzen durchbrechen und neue künstlerische Ausdrucksformen erkunden. FiEE arbeitet umfassend mit Technologieunternehmen, Modemarken und Bildungseinrichtungen zusammen, verschmilzt Kunst tief mit Technologie, Mode und Bildung und schafft eine Reihe neuartiger künstlerischer Produkte und Dienstleistungen. Mit modernster Technologie und präzisem Marketing hat FiEE zahlreichen Kunstwerken geholfen, sowohl kommerziellen Wert als auch gesellschaftlichen Einfluss zu steigern und der blühenden Entwicklung der Kunst nachhaltige Dynamik verliehen.',
'Mit Blick in die Zukunft wird FiEE weiterhin an einer offenen und inklusiven Talentphilosophie festhalten, seine Talentstrategie kontinuierlich verbessern und mehr hervorragende Talente anziehen. Wir sind überzeugt, dass FiEE durch die gemeinsamen Anstrengungen vielfältiger Talente auf dem Weg der innovativen Entwicklung im Kunstbereich weiter voranschreiten und einen größeren Beitrag zur Förderung des globalen Kunstwohlstands leisten wird.'
]
}
},
businessintroduction: {
hero: {
title1: 'KI × Kurzvideo',
title2: 'Führend in eine neue kreative Welt',
desc: 'Durch die tiefe Integration modernster KI-Technologie mit den einzigartigen Vorteilen von Kurzvideoplattformen startet FiEE als Pionier eine Erkundungsreise, steht an der Spitze und führt den kreativen Bereich in eine noch nie dagewesene neue Welt.'
},
industry: {
label: 'Branchenstatus',
title: 'Kreative Krise, Kurzvideos entschlüsseln neues Wachstumspotenzial',
desc: 'Während der kreative Bereich in der Sackgasse gleicher Inhalte und erschöpfter Inspiration steckt, durchbrechen Kurzvideos mit ihrer einzigartigen immersiven Erfahrung und starken sozialen Viralität die Fesseln und bringen wie ein belebender Frühling neue Vitalität in die Branche, als starke Triebkraft für Wachstum.',
challenges: {
title: 'Herausforderungen des Kunstmarktes',
items: [
{
title: 'Künstlerischer Wert im Schatten',
desc: 'Mangelnde Markenbildung und Marktoperationsfähigkeiten lassen künstlerischen Wert im Dunkeln, schwer erkennbar und anerkannt vom breiten Publikum.'
},
{
title: 'Engpässe bei Verbreitungskanälen',
desc: 'Persönliche Social-Media-Plattformen und traditionelle Ausstellungsformate sind veraltet, unfähig, eine breite und qualitativ hochwertige Sichtbarkeit zu erreichen, was die Verbreitung stark einschränkt.'
},
{
title: 'Einförmige Werbung',
desc: 'Übermäßige Abhängigkeit von Offline-Ausstellungsräumen und einzelnen Messen führt zu einer engen Reichweite der Werbung, was zu einer einheitlichen und instabilen Einkommensstruktur führt.'
},
{
title: 'Einschränkungen traditionellen Marketings',
desc: 'Traditionelle Werbekanäle sind teuer, und Künstler können die finanziellen Belastungen kaum tragen, was den Umfang der Promotions- und Werbemaßnahmen stark einschränkt.'
}
]
},
opportunity: {
title: 'Kurzvideo-Social-Media: Starker Aufschwung, unbegrenzte Geschäftsmöglichkeiten',
desc: 'Derzeit erlebt der Kurzvideo-Markt ein explosives Wachstum, und die Werbeskala expandiert rasant. Als dynamischer Akteur im Bereich der Internetinhalte steigen Nutzerzahlen und Nutzungszeiten von Kurzvideos stetig an, eröffnen weite Möglichkeiten für Werbeplatzierungen und Monetarisierung, erhöhen den Anteil der Nutzerzeit und die Bindung der Nutzer und bergen unbegrenztes Potenzial und Chancen.'
}
},
solution: {
label: 'Branchenstatus',
title: 'Kreative Krise, Kurzvideos entschlüsseln neues Wachstumspotenzial',
features: {
precision: {
title: 'Präzise Verteilung startet die Fan-Wachstumsmaschine',
desc: 'Durch den Einsatz von Big-Data-Analysen und KI-Algorithmen analysieren wir tiefgehend Nutzerverhaltensdaten wie Surfgewohnheiten und Suchpräferenzen, erstellen präzise Nutzerprofile und leiten die Werke von Künstlern gezielt an potenzielle Zielgruppen weiter, verwandeln ungerichtete Nachfrager in treue Fans und legen die Grundlage für die Verbreitung des Einflusses der Künstler.'
},
monetization: {
title: 'Vielfältige Monetarisierung aktiviert die kommerzielle Wertkette',
desc: 'Um den kommerziellen Wert der künstlerischen Schöpfung zu erschließen, bauen wir ein Fan-Wirtschafts-Betriebssystem auf. Durch einen einfachen Spendenmechanismus können Fans ihre Wertschätzung für Künstler sofort ausdrücken; Verkaufskanäle für physische und digitale Werke erfüllen die Sammlungsbedürfnisse der Fans; Abonnementdienste bieten exklusive Inhalte und bevorzugte Teilnahmemöglichkeiten bei Veranstaltungen, um die Fan-Bindung zu stärken. Diese Wege fördern die Umwandlung von Fans in Konsumenten und steigern die Einnahmen sowohl für Künstler als auch für die Plattform.'
},
interaction: {
title: 'Interaktives Teilen schafft einen geschlossenen Kunst-Ökosystemkreis',
desc: 'Durch intelligente soziale Empfehlungstechnologie fördern wir tiefgehende Interaktionen und Austausch zwischen Fans, das Teilen von Einsichten und kreativer Inspiration, das Erkennen gegenseitiger potenzieller Bedürfnisse und die natürliche Ausbreitung der Fangemeinde. Gleichzeitig analysieren wir durch Daten die Merkmale und Bedürfnisse der Konsumentengruppen, erweitern gezielt Konsumentenkreise und erschließen neue Geschäftsmöglichkeiten. Dieser Mechanismus des interaktiven Teilens schafft ein nachhaltig entwickelndes Ökosystem der künstlerischen Schöpfung, steigert den Einfluss der Künstler kontinuierlich und ermöglicht dem Unternehmen stabiles Wachstum und Einnahmensteigerung, wodurch eine Win-Win-Situation entsteht.'
}
}
},
vision: {
label: 'Marktvision',
title: 'Entwurf eines neuen Bauplans für den Kunstmarkt',
desc: 'In den unvorhersehbaren Wellen der Kunst gestaltet FiEE mit Innovation als Pinsel und präziser Einsicht als Tinte die Zukunft der Kunstindustrie mit innovativem Denken und globaler Perspektive neu. Wir erschließen das Potenzial der Kunst tiefgehend, verschmelzen vielfältige kulturelle Elemente, durchbrechen traditionelle Barrieren, bauen Online-Verkehrsgemeinschaften auf, gestalten das Kunst-Ökosystem neu, beleben den Markt und führen den künstlerischen Wert in eine neue Richtung, um eine völlig neue Ära des Kunstmarktes einzuleiten.',
community: {
title: 'Aufbau einer Milliarden-Verkehrsgemeinschaft, Schaffung einer globalen Plattform für künstlerischen Austausch',
desc: 'Mit modernster Big-Data- und KI-Technologie bauen wir eine Gemeinschaft mit Milliarden an Traffic auf, die globale Kunstliebhaber vereint. Durch intelligente Algorithmen ermöglichen wir präzise Inhaltsempfehlungen und Interessenabgleich, fördern Austausch und Interaktion und schaffen eine effiziente Kommunikationsbrücke zwischen Künstlern und Fans, um die Grundlage für den Traffic des Kunst-Ökosystems zu legen.'
}
},
cooperation: {
title: 'Globale Zusammenarbeit erweitern, eine Karte der vielfältigen Integration zeichnen',
timeline: {
year2025: {
title: '2025',
desc: 'Zusammenarbeit mit 1500+ Kunstinstitutionen und Technologieunternehmen, Integration von Ressourcen und Vorantreiben von Kunst-Technologie-Fusionsprojekten.'
},
year2026: {
title: '2026',
desc: '5000+ globale Partner, Aufbau eines breiten Kooperationsnetzwerks, Erweiterung der geografischen Geschäftsabdeckung und Steigerung der internationalen Markenbekanntheit.'
},
year2027: {
title: '2027',
desc: '10000+ strategische Partner, Bildung einer stabilen globalen Allianz mit vollständiger Durchdringung der Kunstindustriekette und Realisierung von Ressourcenteilung.'
}
}
},
incubation: {
title: '18-monatige Inkubation von Kunst-KOLs ohne Vorkenntnisse',
subtitle: 'Entfesselung des kommerziellen Potenzials der Kunst',
desc: '18 Monate sind eine tiefgehende Entdeckung künstlerischen Potenzials und eine transformative Reise zur künstlerischen Kommerzialisierung. Von Zeichentechniken bis zur Ästhetikbildung, von Inhaltserstellung bis zum Traffic-Management umfassende Unterstützung. FiEE bietet maßgeschneiderte Wachstumspfade für Menschen ohne Vorkenntnisse, hilft Ihnen, die Brücke zwischen Kunst und Kommerz zu überqueren, ein trendsetzender Kunst-KOL zu werden und eine neue Reise mit unbegrenzten Möglichkeiten in der Kunst zu beginnen.',
features: {
fans: {
title: 'Fan-Wachstum',
desc: 'Bis 2027 werden durch präzise Marketingstrategien die Fangemeinden jedes Künstlers auf über 100.000 anwachsen, die Fan-Community wird 1 Milliarde erreichen, die Fangruppen der Künstler vergrößern und den Einfluss der Werke stärken.'
},
kol: {
title: 'KOL-Inkubation',
desc: 'Mit einer Milliarden-Traffic-Community und präziser Datenanalyse werden innerhalb von 18 Monaten gewöhnliche Künstler oder kommerzielle Marken zu international bekannten KOLs, die künstlerischen und kommerziellen Wert effizient umwandeln.'
}
}
},
exposure: {
title: 'Marktvision',
desc: 'Mit modernsten Verbreitungsstrategien zielen wir präzise auf das globale Publikum ab. Durch die tiefe Integration erstklassiger Medienressourcen erreichen wir eine exponentielle Sichtbarkeitssteigerung, formen umfassend globalen Markeneinfluss und führen künstlerische Trends auf die zentrale Weltbühne.',
timeline: {
year2025: {
title: '2025',
desc: 'Sichtbarkeit auf Social-Media-Plattformen übersteigt 500 Millionen+, durch Multi-Plattform-Verknüpfung und kreatives Content-Marketing wird die Sichtbarkeit von Marke und Künstlern gesteigert.'
},
year2026: {
title: '2026',
desc: 'Sichtbarkeit erreicht 1 Milliarde+, vertieft die Markenverbreitung und zieht mehr potenzielle Nutzer und Partner an.'
},
year2027: {
title: '2027',
desc: 'Erreicht über 5 Milliarden+ und durchbricht branchenübergreifende und kulturelle Grenzen, steigert den internationalen Markeneinfluss umfassend, fördert die tiefe Integration von Kunst und Technologie und gestaltet neue Trends in der Branchenentwicklung.'
}
}
}
},
investor: {
title: 'Investor Relations',
subtitle: 'Finanzstatus von Minim (NASDAQ: FIEE)',
latest_news: {
title: 'Aktuelle Nachrichten',
financial: {
title: 'Aktuelle Finanzdaten',
date: '29. März 2023',
content: 'Ergebnisse für das vierte Quartal 2022 und das Gesamtjahr 2022',
link: 'Ergebnisbericht'
},
events: {
title: 'Aktuelle Ereignisse',
date: '13. März 2024',
content: 'Minim gibt Vereinbarung zur Fusion mit e2Companies bekannt',
link: 'Fusionsvereinbarung - Endgültige Pressemitteilung 3. Dezember 2024'
},
stock: {
title: 'Aktienkurs',
content: 'FIEE-Kurs auf TradingView',
link: 'FIEE-Kurs'
}
},
financial_data: {
title: 'Finanzdaten',
years: {
year2023: '2023',
year2022: '2022',
year2021: '2021'
},
reports: {
profit_report: 'Gewinnbericht',
earnings_report: 'Ergebnisbericht',
earnings_call: 'Ergebnispräsentation',
sec_filings: '10-Q/10-K',
annual_report: 'Jahresbericht',
annual_meeting: 'Jahreshauptversammlung',
special_meeting: 'Außerordentliche Hauptversammlung',
proxy_statement: 'Vollmachtserklärung'
},
investor_guide: 'Leitfaden für die Kommunikation mit Investoren'
},
market_trends: {
title: 'Markttrends',
sec_description1: 'SEC-Einreichungen sind Dokumente, die bei der US-Börsenaufsichtsbehörde (SEC) eingereicht werden. Börsennotierte Unternehmen und bestimmte Insider sind verpflichtet, regelmäßig bei der SEC einzureichen. Diese Dokumente sind über die Online-Datenbank der SEC verfügbar.',
sec_description2: 'Durch die Auswahl unten verlassen Sie die Website von Minim. Minim gibt keine Zusicherungen bezüglich anderer Websites, die Sie über diese Seite aufrufen können. Wenn Sie eine Nicht-Minim-Website aufrufen, auch eine, die das Minim-Logo enthalten könnte, verstehen Sie bitte, dass diese unabhängig von Minim ist und Minim keinen Einfluss auf den Inhalt dieser Website hat. Darüber hinaus bedeutet ein Link zu einer Nicht-Minim-Website nicht, dass Minim den Inhalt oder die Nutzung dieser Website befürwortet oder dafür verantwortlich ist.',
view_all_sec: 'Alle SEC-Einreichungen anzeigen'
},
board_of_directors: {
title: 'Vorstand',
directors: {
patrick: {
name: 'Patrick Rivard',
position: 'Vorstandsmitglied,CFO bei U.S. Wealth Protection',
position1: 'Vorstandsmitglied',
position2: 'Partner & CFO bei U.S. Wealth Protection'
},
andrew: {
name: 'Andrew Papanikolaou',
position: 'Vorstandsmitglied, Finanzmanager bei Raytheon',
position1: 'Vorstandsmitglied',
position2: 'Finanzmanager bei Raytheon'
}
}
},
governance: {
title: 'Governance, Diversität und Ausschussrichtlinien',
documents: {
ethics_code: 'Ethikrichtlinie für leitende Finanzbeamte',
conflict_minerals: 'Konfliktmineralien-Erklärung',
business_conduct: 'Ethik- und Geschäftsverhaltenskodex',
audit_committee: 'Prüfungsausschussrichtlinie',
whistleblower: 'Hinweisgeberrichtlinie',
compensation_committee: 'Vergütungsausschussrichtlinie',
related_party: 'Richtlinie für Geschäfte mit Nahestehenden',
nomination_committee: 'Nominierungs- und Governance-Ausschuss',
diversity_matrix_2022: 'Diversitätsmatrix 2022',
diversity_matrix_2023: 'Diversitätsmatrix 2023'
}
}
},
investorhandbook: {
title: 'Mindestkommunikationsrichtlinien mit der Investorengemeinschaft',
authorized_speakers: {
title: 'Befugte Sprecher',
desc: 'Die folgenden Minim-Vertreter sind befugt, mit der Investorengemeinschaft, einschließlich Analysten, Börsenmaklern sowie privaten und institutionellen Aktionären, zu kommunizieren:',
items: [
'Präsident',
'Präsident und Chief Marketing Officer',
'Chief Financial Officer'
],
note: 'In diesem Leitfaden werden diese Vertreter als "Befugte IR-Kontakte" bezeichnet.'
},
quarterly_communications: {
title: 'Kommunikation und Besprechungen zum Quartalsende',
items: [
'1. Ruhefrist — Das Unternehmen beachtet eine "Ruhefrist", die vom Quartalsende bis zur Veröffentlichung der Ergebnisse reicht. Während dieser Zeit wird das Management des Unternehmens keine 1:1-Treffen mit Analysten oder Investoren durchführen. Auf Anfrage können jedoch sachliche öffentliche Informationen an Investoren bereitgestellt und von befugten Investor Relations-Kontakten analysiert werden.',
'2. Analystenmeetings/Telefonkonferenzen — Alle Analystenmeetings oder -anrufe, in denen vierteljährliche oder jährliche Finanz- und Geschäftsinformationen diskutiert werden, sollten gleichzeitig über das Internet oder eine Telefonkonferenz an alle interessierten Mitglieder der Öffentlichkeit übertragen werden. Eine angemessene Vorankündigung und gleichzeitige Übertragung der Besprechung sollte über eine Pressemitteilung oder andere Kommunikationsmethoden erfolgen, die der Verordnung FD entsprechen.',
'3. Ergebnis-Pressemitteilung — Die Ergebnis-Pressemitteilung wird zu Beginn oder vor der Besprechung oder Telefonkonferenz, wie von der Investor Relations-Abteilung und dem Chief Financial Officer festgelegt, gemäß den geltenden SEC- und NASDAQ-Regeln über den Nachrichtendienst veröffentlicht. Sie wird auch bei der SEC auf Formular 8-K eingereicht und auf der Unternehmenswebsite veröffentlicht.',
'4. Eine jährliche Leitlinie zum anfänglichen Abonnementumsatz und zum EPS-Bereich kann in der Ergebnis-Pressemitteilung bereitgestellt werden, und falls erforderlich, können Aktualisierungen der Leitlinie in jeder Quartals-Ergebnis-Pressemitteilung bereitgestellt werden. In der Regel wird das Unternehmen diese Leitlinie während des Quartals nicht aktualisieren oder zusätzliche Leitlinien bereitstellen, es sei denn, der VP Finance/CFO hält dies für notwendig, und dies nur in einem öffentlichen Forum, das der Verordnung FD entspricht.'
],
note: 'Minim-Vertreter (außer befugte Sprecher), die Anfragen von Medien, Marktprofis oder Aktionären erhalten, sollten solche Anfragen nicht beantworten, sondern den Anfragenden an einen befugten Sprecher verweisen. Minim-Vertreter, die den Investor Relations- und Marketingteams zugewiesen sind, können jedoch routinemäßige Anfragen zu öffentlichen Informationen gemäß den von befugten Sprechern festgelegten Richtlinien beantworten.'
}
}
}

View File

@ -16,7 +16,7 @@ export default {
home: "Home", home: "Home",
company: "Company Overview", company: "Company Overview",
businessintroduction: "Business Introduction", businessintroduction: "Business Introduction",
please_select: "Please Select", please_select: "Select Language",
confirm_select: "Confirm Selection", confirm_select: "Confirm Selection",
investor: "Investor Guide", investor: "Investor Guide",
}, },
@ -509,8 +509,24 @@ export default {
button: "Go", button: "Go",
}, },
download: "Download", download: "Download",
pdfDownload: "PDF Download",
pagination: {
displaying: "Displaying {start} - {end} of {total} results",
perPage: "{size}/page",
goto: "Goto",
},
}, },
}, },
stock_quote: {
title: "Stock Quote",
nasdaq: "NASDAQ: FIEE",
open: "Open",
change: "% Change",
volume: "Volume",
week_range: "52-Week Range",
days_range: "Day's Range",
market_cap: "Market Cap",
},
header_menu: { header_menu: {
corporate_information: { corporate_information: {
title: "Corporate Information", title: "Corporate Information",

225
src/locales/enc.js Normal file
View File

@ -0,0 +1,225 @@
export default {
language: {
zh: "Simplified Chinese",
zhTW: "Traditional Chinese",
en: "English",
ja: "Japanese",
},
home: {
nav: {
home: "Home",
company: "Company Overview",
businessintroduction: "Business Introduction",
please_select: "Select Language",
confirm_select: "Confirm Selection",
investor: "Investor Guide",
},
},
financialinformation: {
quarterlyreports: {
title: "Quarterly Reports",
search: {
placeholder: "Search",
button: "Go",
},
download: "Download",
pdfDownload: "PDF Download",
pagination: {
displaying: "Displaying {start} - {end} of {total} results",
perPage: "{size}/page",
goto: "Goto",
},
},
},
stock_quote: {
title: "Stock Quote",
nasdaq: "NASDAQ: FIEE",
open: "Open",
change: "% Change",
volume: "Volume",
week_range: "52-Week Range",
days_range: "Day's Range",
market_cap: "Market Cap",
},
historic_stock: {
title: "Historical Data",
range: "Range",
date: "Date",
open: "Open",
high: "High",
low: "Low",
close: "Close",
adj_close: "Adj. Close",
change: "Change",
volume: "Volume",
daily: "Daily",
weekly: "Weekly",
monthly: "Monthly",
quarterly: "Quarterly",
annual: "Annual",
duration: {
"3_months": "3 Months",
"6_months": "6 Months",
ytd: "YTD",
"1_year": "1 Year",
"5_years": "5 Years",
"10_years": "10 Years",
},
pagination: {
displaying: "Displaying {start} - {end} of {total} results",
perPage: "{size}/page",
goto: "Goto",
},
echarts: {
title: "FiEE, Inc. Stock Price History",
range: "Range",
price: "Price",
months: "m",
years: "Y",
year: "Y",
ytd: "YTD",
"1m": "1m",
"3m": "3m",
ytd_short: "YTD",
"1y": "1Y",
"5y": "5Y",
"10y": "10Y",
max: "Max",
start_time: "Start Time",
end_time: "End Time",
},
},
header_menu: {
corporate_information: {
title: "Corporate Information",
company_overview: "Company Overview",
business_introduction: "Business Introduction",
management: "Management",
board_of_directors: "Board of Directors",
committee_appointments: "Committee Appointments",
governance: "Governance",
corporate_video: "Corporate Video",
},
financial_information: {
title: "Financial Information",
sec_filings: "SEC Filings",
annual_reports: "Annual Reports",
quarterly_reports: "Quarterly Reports",
},
stock_information: {
title: "Stock Information",
stock_quote: "Stock Quote",
historic_stock_price: "Historic Stock Price",
investment_calculator: "Investment Calculator",
},
news_releases: {
title: "News Releases",
press_releases: "Press Releases",
events_calendar: "Events Calendar",
},
investor_resources: {
title: "Investor Resources",
ir_contacts: "IR Contacts",
email_alerts: "Email Alerts",
},
product_introduction: "Product Introduction",
},
press_releases: {
title: "Press Releases",
search: {
placeholder: "Search",
button: "Go",
},
},
events_calendar: {
title: "Events Calendar",
search: {
button: "Go",
},
},
product_intro: {
hero_line1: "More than just a tool——",
hero_line2: "Comprehensive growth solutions, ",
hero_line3: "providing a one-stop solution for content creation,",
hero_line4: "publishing, analysis, and monetization",
core_value_title: "Core Value",
core_value_text:
"The FIEE-SAAS platform is a one-stop content operation solution tailored for creators in the digital era. The platform utilizes intelligent distribution technology, A1 empowerment tools, and full-chain services,Assist you in efficiently reaching audiences on global mainstream platforms such as TikTok, YouTube, and Instagram, creating a KOL brand effect, unlocking content value, and achieving sustainable growth.",
features_title: "Product Features",
feature_sync: "One-click Synchronous Publishing",
feature_sync_desc:
"Synchronize graphic and video content to TikTok, YouTube, and Instagram platforms at once, saving time on repetitive operations.",
feature_schedule: "Intelligent Scheduled Publishing",
feature_schedule_desc:
"Plan the content release time in advance, support batch scheduling, and accurately grasp the optimal release time of each platform.",
feature_accounts: "Unified Management of Multiple Accounts",
feature_accounts_desc:
"Easily manage multiple accounts on one platform without the need for repeated login and switching, improving team collaboration efficiency.",
feature_library: "Cloud Content Library",
feature_library_desc:
"Safely store and manage all creative materials, access and use them anytime, anywhere, and support quick retrieval and reuse.",
feature_tracking: "Basic Data Tracking",
feature_tracking_desc:
"Visually view the content performance of various platforms, understand core data indicators, and provide a basis for optimizing strategies.",
solutions_title: "Value Added Solutions",
sol_kol: "KOL Brand Promotion Services",
sol_kol_desc:
"Efficiently connect high-quality business cooperation opportunities and complete the entire process management from order acceptance to publication within the platform.",
sol_content: "Professional Content Creation Support",
sol_content_desc:
"Connect professional shooting and post production teams for you, create high-quality \"art+story\" content, and strengthen IP influence.",
sol_ops: "Account Operation and Hosting Services",
sol_ops_desc:
"From 0 to 1 account positioning, follower growth strategy to monetization cycle, operation experts provide full cycle running and hosting services.",
advantages_title: "Our Advantages",
adv_time: "Time Saving",
adv_time_desc:
"Multi platform publishing efficiency improvement, allowing you to focus on content creation.",
adv_safe: "Safe and Reliable",
adv_safe_desc:
"Enterprise level data encryption and permission control ensure account and content security.",
adv_consistent: "Maintain Consistency",
adv_consistent_desc:
"Ensure that brand information is presented uniformly on all platforms.",
adv_data: "Data Driven",
adv_data_desc:
"Optimizing Content Strategies Based on Actual Performance.",
adv_easy: "Easy to Use",
adv_easy_desc:
"Intuitive interface design, no need for professional technical background.",
cta_title_line1: "Get customized ",
cta_title_line2: "solutions for free",
},
contacts: {
title: "Investor Contacts",
company_name: "FiEE Inc.",
investor_relations: "Investor Relations",
email_label: "Email:",
},
email_alerts: {
title: "E-Mail Alerts",
required_fields: "* Required Fields",
submitted_successfully: "Submitted successfully!",
submitted_info: "The information you submitted is as follows:",
form: {
first_name: "* First Name",
last_name: "* Last Name",
email: "* Email",
company: "* Company",
phone: "* Phone",
submit: "Submit",
},
submitted_data: {
first_name: "First Name",
last_name: "Last Name",
email: "Email",
company: "Company",
phone: "Phone",
not_filled: "Not filled in",
},
validation: {
complete_info: "Please fill in complete information",
field_length: "Field length cannot exceed 50 characters",
},
},
};

View File

@ -451,5 +451,30 @@ export default {
], ],
note: 'メディア、市場専門家、または株主からの問い合わせを受けたMinim代表者承認されたスピーカーを除くは、そのような問い合わせに応答せず、問い合わせ者を承認されたスピーカーに紹介する必要があります。ただし、Minimの投資家関係およびマーケティングチームに割り当てられたMinim代表者は、承認されたスピーカーが随時定めるガイドラインに従って、公開情報に関する日常的な問い合わせに応答することができます。' note: 'メディア、市場専門家、または株主からの問い合わせを受けたMinim代表者承認されたスピーカーを除くは、そのような問い合わせに応答せず、問い合わせ者を承認されたスピーカーに紹介する必要があります。ただし、Minimの投資家関係およびマーケティングチームに割り当てられたMinim代表者は、承認されたスピーカーが随時定めるガイドラインに従って、公開情報に関する日常的な問い合わせに応答することができます。'
} }
} },
financialinformation: {
quarterlyreports: {
title: '四半期報告書',
search: {
placeholder: '検索',
button: '検索',
},
pdfDownload: 'PDFダウンロード',
pagination: {
displaying: '{total}件中 {start} - {end}件を表示',
perPage: '{size}/ページ',
goto: 'ジャンプ',
},
},
},
stock_quote: {
title: '株価',
nasdaq: 'ナスダック: FIEE',
open: '始値',
change: '変動率',
volume: '出来高',
week_range: '52週レンジ',
days_range: '当日レンジ',
market_cap: '時価総額',
},
} }

223
src/locales/jac.js Normal file
View File

@ -0,0 +1,223 @@
export default {
language: {
zh: '簡体中国語',
zhTW: '繁体中国語',
en: '英語',
ja: '日本語',
},
home: {
nav: {
home: 'ホーム',
company: '会社概要',
businessintroduction: '業務紹介',
please_select: '選択してください',
confirm_select: '確認選択',
investor: '投資家ガイド',
},
},
financialinformation: {
quarterlyreports: {
title: '四半期報告書',
search: {
placeholder: '検索',
button: '検索',
},
download: "ダウンロード",
pdfDownload: 'PDFダウンロード',
pagination: {
displaying: '{total}件中 {start} - {end}件を表示',
perPage: '{size}/ページ',
goto: 'ジャンプ',
},
},
},
stock_quote: {
title: '株価',
nasdaq: 'ナスダック: FIEE',
open: '始値',
change: '変動率',
volume: '出来高',
week_range: '52週レンジ',
days_range: '当日レンジ',
market_cap: '時価総額',
},
historic_stock: {
title: '履歴データ',
range: '範囲',
date: '日付',
open: '始値',
high: '高値',
low: '安値',
close: '終値',
adj_close: '調整後終値',
change: '変動',
volume: '出来高',
daily: '毎日',
weekly: '毎週',
monthly: '毎月',
quarterly: '四半期ごと',
annual: '毎年',
duration: {
"3_months": "3ヶ月",
"6_months": "6ヶ月",
"ytd": "年初来",
"1_year": "1年",
"5_years": "5年",
"10_years": "10年",
},
pagination: {
displaying: "{total}件中 {start} - {end}件を表示",
perPage: "{size}/ページ",
goto: 'ジャンプ',
},
echarts: {
title: 'FiEE, Inc. 株価履歴',
range: '範囲',
price: '価格',
months: 'm',
years: 'Y',
year: 'Y',
ytd: 'YTD',
"1m": "1ヶ月",
"3m": "3ヶ月",
"ytd_short": "YTD",
"1y": "1年",
"5y": "5年",
"10y": "10年",
"max": "最大",
start_time: "開始時間",
end_time: "終了時間",
},
},
header_menu: {
corporate_information: {
title: "企業情報",
company_overview: "会社概要",
business_introduction: "事業紹介",
management: "経営陣",
board_of_directors: "取締役会",
committee_appointments: "委員会任命",
governance: "ガバナンス",
corporate_video: "企業ビデオ",
},
financial_information: {
title: "財務情報",
sec_filings: "SEC提出書類",
annual_reports: "年次報告書",
quarterly_reports: "四半期報告書",
},
stock_information: {
title: "株式情報",
stock_quote: "株価情報",
historic_stock_price: "過去の株価",
investment_calculator: "投資計算機",
},
news_releases: {
title: "ニュースリリース",
press_releases: "プレスリリース",
events_calendar: "イベントカレンダー",
},
investor_resources: {
title: "投資家向け情報",
ir_contacts: "IRお問い合わせ",
email_alerts: "メールアラート",
},
product_introduction: "製品紹介",
},
press_releases: {
title: "プレスリリース",
search: {
placeholder: "検索",
button: "検索",
},
},
events_calendar: {
title: "イベントカレンダー",
search: {
button: "検索",
},
},
product_intro: {
hero_line1: "単なるツールではなく——",
hero_line2: "包括的な成長ソリューション、",
hero_line3: "コンテンツ制作のワンストップソリューションを提供し、",
hero_line4: "配信・分析・マネタイズまで対応",
core_value_title: "中核価値",
core_value_text:
"FIEE-SAASプラットフォームは、デジタル時代のクリエイター向けに設計されたワンストップのコンテンツ運用ソリューションです。インテリジェント配信技術、AI支援ツール、フルチェーンサービスを活用し、TikTok、YouTube、Instagramなどの主要プラットフォームで効率的にオーディエンスにリーチし、KOLブランド効果を創出し、コンテンツ価値を解放し、持続的な成長を実現します。",
features_title: "製品機能",
feature_sync: "ワンクリック同時投稿",
feature_sync_desc:
"画像・動画コンテンツをTikTok、YouTube、Instagramに一括同期し、繰り返し作業の時間を削減します。",
feature_schedule: "インテリジェント予約投稿",
feature_schedule_desc:
"事前に投稿時間を計画し、バッチ予約に対応。各プラットフォームの最適な投稿時間を正確に把握。",
feature_accounts: "複数アカウントの一元管理",
feature_accounts_desc:
"1つのプラットフォームで複数アカウントを簡単に管理。ログイン・切替の手間を省き、チーム協業効率を向上。",
feature_library: "クラウドコンテンツライブラリ",
feature_library_desc:
"すべての制作素材を安全に保管・管理。いつでもどこでもアクセス・利用可能で、迅速な検索と再利用をサポート。",
feature_tracking: "基本データトラッキング",
feature_tracking_desc:
"各プラットフォームのコンテンツ実績を可視化し、コア指標を把握。最適化の根拠を提供。",
solutions_title: "付加価値ソリューション",
sol_kol: "KOLブランドプロモーション",
sol_kol_desc:
"高品質なビジネス協業機会を効率的にマッチングし、受注から公開までの全工程をプラットフォーム内で完結。",
sol_content: "プロフェッショナルなコンテンツ制作支援",
sol_content_desc:
"撮影・編集の専門チームと連携し、高品質な“アート+ストーリー”コンテンツを制作し、IP影響力を強化。",
sol_ops: "アカウント運用・代行サービス",
sol_ops_desc:
"0から1までのアカウント設計、フォロワー成長戦略から収益化サイクルまで、運用専門家が一貫して支援。",
advantages_title: "私たちの強み",
adv_time: "時間の節約",
adv_time_desc:
"マルチプラットフォーム同時投稿で効率アップ。制作に集中できます。",
adv_safe: "安全で信頼できる",
adv_safe_desc:
"エンタープライズレベルのデータ暗号化と権限管理でアカウントとコンテンツを保護。",
adv_consistent: "一貫性の維持",
adv_consistent_desc:
"すべてのプラットフォームでブランド情報を統一的に提示。",
adv_data: "データドリブン",
adv_data_desc: "実績に基づく戦略最適化。",
adv_easy: "使いやすさ",
adv_easy_desc: "直感的なUIで専門知識不要。",
cta_title_line1: "無料で",
cta_title_line2: "カスタマイズされたソリューションを",
},
contacts: {
title: "投資家連絡先",
company_name: "FiEE Inc.",
investor_relations: "投資家関係",
email_label: "メール:",
},
email_alerts: {
title: "メールアラート",
required_fields: "* 必須項目",
submitted_successfully: "送信が完了しました!",
submitted_info: "送信された情報は以下の通りです:",
form: {
first_name: "* 名",
last_name: "* 姓",
email: "* メールアドレス",
company: "* 会社名",
phone: "* 電話番号",
submit: "送信",
},
submitted_data: {
first_name: "名:",
last_name: "姓:",
email: "メールアドレス:",
company: "会社名:",
phone: "電話番号:",
not_filled: "未入力",
},
validation: {
complete_info: "完全な情報を入力してください",
field_length: "フィールドの長さは50文字を超えることはできません",
},
},
}

View File

@ -447,5 +447,30 @@ export default {
], ],
note: '收到媒體、市場專業人士或股東任何詢問的Minim代表授權發言人除外不得回覆此類詢問但應將提問者轉介給授權發言人。然而分配給Minim投資者關係和營銷團隊的Minim代表可以按照授權發言人不時制定的指導方針對公開信息的例行詢問作出回應。' note: '收到媒體、市場專業人士或股東任何詢問的Minim代表授權發言人除外不得回覆此類詢問但應將提問者轉介給授權發言人。然而分配給Minim投資者關係和營銷團隊的Minim代表可以按照授權發言人不時制定的指導方針對公開信息的例行詢問作出回應。'
} }
} },
financialinformation: {
quarterlyreports: {
title: '季度報告',
search: {
placeholder: '搜索',
button: '開始',
},
pdfDownload: 'PDF 下載',
pagination: {
displaying: '顯示第 {start} - {end} 條,共 {total} 條',
perPage: '{size}/頁',
goto: '前往',
},
},
},
stock_quote: {
title: '股票報價',
nasdaq: '納斯達克: FIEE',
open: '開盤',
change: '漲跌幅',
volume: '成交量',
week_range: '52週範圍',
days_range: '當日範圍',
market_cap: '市值',
},
} }

220
src/locales/zh-TWc.js Normal file
View File

@ -0,0 +1,220 @@
export default {
language: {
zh: '簡體中文',
zhTW: '繁體中文',
en: 'English',
ja: '日本語',
},
home: {
nav: {
home: '首頁',
company: '公司概況',
businessintroduction: '業務介紹',
please_select: '請選擇',
confirm_select: '確認選擇',
investor: '投資者指南',
},
},
financialinformation: {
quarterlyreports: {
title: '季度報告',
search: {
placeholder: '搜索',
button: '開始',
},
download: "下載",
pdfDownload: 'PDF 下載',
pagination: {
displaying: '顯示第 {start} - {end} 條,共 {total} 條',
perPage: '{size}/頁',
goto: '前往',
},
},
},
stock_quote: {
title: '股票報價',
nasdaq: '納斯達克: FIEE',
open: '開盤',
change: '漲跌幅',
volume: '成交量',
week_range: '52週範圍',
days_range: '當日範圍',
market_cap: '市值',
},
historic_stock: {
title: '歷史數據',
range: '範圍',
date: '日期',
open: '開盤',
high: '最高',
low: '最低',
close: '收盤',
adj_close: '調整後收盤',
change: '漲跌幅',
volume: '成交量',
daily: '每日',
weekly: '每週',
monthly: '每月',
quarterly: '每季度',
annual: '每年',
duration: {
"3_months": "3個月",
"6_months": "6個月",
"ytd": "年初至今",
"1_year": "1年",
"5_years": "5年",
"10_years": "10年",
},
pagination: {
displaying: '顯示第 {start} - {end} 條,共 {total} 條',
perPage: '{size}/頁',
goto: '前往',
},
echarts: {
title: 'FiEE, Inc. 股票歷史價格',
range: '範圍',
price: '價格',
months: '月',
years: '年',
year: '年',
ytd: '年初至今',
"1m": "1個月",
"3m": "3個月",
"ytd_short": "年初至今",
"1y": "1年",
"5y": "5年",
"10y": "10年",
"max": "最大",
start_time: "開始時間",
end_time: "結束時間",
},
},
header_menu: {
corporate_information: {
title: "公司信息",
company_overview: "公司概况",
business_introduction: "业务介绍",
management: "管理层",
board_of_directors: "董事会",
committee_appointments: "委员会任命",
governance: "公司治理",
corporate_video: "公司视频",
},
financial_information: {
title: "财务信息",
sec_filings: "SEC文件",
annual_reports: "年度报告",
quarterly_reports: "季度报告",
},
stock_information: {
title: "股票信息",
stock_quote: "股票报价",
historic_stock_price: "历史股价",
investment_calculator: "投资计算器",
},
news_releases: {
title: "新闻发布",
press_releases: "新闻稿",
events_calendar: "活动日历",
},
investor_resources: {
title: "投資者指南",
ir_contacts: "IR聯繫",
email_alerts: "郵件提醒",
},
product_introduction: "產品介紹",
},
press_releases: {
title: "新聞稿",
search: {
placeholder: "搜索",
button: "開始",
},
},
events_calendar: {
title: "活動日曆",
search: {
button: "開始",
},
},
product_intro: {
hero_line1: "不僅是一個工具——",
hero_line2: "全鏈路成長解決方案,",
hero_line3: "為內容創作提供一站式方案,",
hero_line4: "涵蓋發布、分析與變現",
core_value_title: "核心價值",
core_value_text:
"FIEE-SAAS 平台是一個為數位時代創作者量身打造的一站式內容營運解決方案。平台透過智慧分發技術、AI 賦能工具與全鏈服務,協助你高效觸達 TikTok、YouTube、Instagram 等全球主流平台受眾,打造 KOL 品牌效應,釋放內容價值,實現永續成長。",
features_title: "產品功能",
feature_sync: "一鍵同步發布",
feature_sync_desc:
"圖文與影片內容一鍵同步至 TikTok、YouTube、Instagram節省重複操作時間。",
feature_schedule: "智慧排程發布",
feature_schedule_desc:
"事先規劃發布時間,支援批量排程,精準掌握各平台最佳發布時段。",
feature_accounts: "多帳號統一管理",
feature_accounts_desc:
"在一個平台輕鬆管理多個帳號,無需反覆登入與切換,提升團隊協作效率。",
feature_library: "雲端素材庫",
feature_library_desc:
"安全儲存與管理所有創作素材,隨時隨地取用,支援快速檢索與再利用。",
feature_tracking: "基礎數據追蹤",
feature_tracking_desc:
"以視覺化方式查看各平台內容表現,掌握核心指標,為策略優化提供依據。",
solutions_title: "增值解決方案",
sol_kol: "KOL 品牌推廣服務",
sol_kol_desc:
"高效率對接優質商務合作機會,於平台內完成從接單到發布的全流程管理。",
sol_content: "專業內容創作支援",
sol_content_desc:
"為你對接專業拍攝與後製團隊,打造高品質「藝術+故事」內容,強化 IP 影響力。",
sol_ops: "帳號營運與代營運服務",
sol_ops_desc:
"從 0 到 1 的帳號定位、粉絲成長策略到變現循環,營運專家提供全週期營運與託管服務。",
advantages_title: "我們的優勢",
adv_time: "節省時間",
adv_time_desc: "多平台聯動提升發布效率,專注內容創作。",
adv_safe: "安全可靠",
adv_safe_desc: "企業級數據加密與權限控管,保障帳號與內容安全。",
adv_consistent: "維持一致性",
adv_consistent_desc: "確保品牌資訊在各平台一致呈現。",
adv_data: "數據驅動",
adv_data_desc: "根據實際表現優化內容策略。",
adv_easy: "易於使用",
adv_easy_desc: "直覺化介面設計,無需專業技術背景。",
cta_title_line1: "免費獲取",
cta_title_line2: "專屬客製化方案",
},
contacts: {
title: "投資者聯絡方式",
company_name: "FiEE Inc.",
investor_relations: "投資者關係",
email_label: "郵箱:",
},
email_alerts: {
title: "郵件提醒",
required_fields: "* 必填欄位",
submitted_successfully: "提交成功!",
submitted_info: "您提交的資訊如下:",
form: {
first_name: "* 名字",
last_name: "* 姓氏",
email: "* 郵箱",
company: "* 公司",
phone: "* 電話",
submit: "提交",
},
submitted_data: {
first_name: "名字:",
last_name: "姓氏:",
email: "郵箱:",
company: "公司:",
phone: "電話:",
not_filled: "未填寫",
},
validation: {
complete_info: "請填寫完整資訊",
field_length: "欄位長度不能超過50個字符",
},
},
}

View File

@ -448,9 +448,35 @@ export default {
'1.静默期——公司遵守"静默期"从季度末日期开始到收益发布时结束。在此期间企业管理层将不会与分析师或投资者进行1:1的会面。然而根据要求可向投资者提供基于事实的公共信息并由授权投资者关系联系人进行分析。', '1.静默期——公司遵守"静默期"从季度末日期开始到收益发布时结束。在此期间企业管理层将不会与分析师或投资者进行1:1的会面。然而根据要求可向投资者提供基于事实的公共信息并由授权投资者关系联系人进行分析。',
'2.分析师会议/电话会议——所有讨论季度、年度财务和业务信息的分析师会议或通话应通过互联网或电话会议同时向所有感兴趣的公众广播。会议的适当提前通知和同步广播应通过新闻稿或符合FD条例的其他通信方式进行。', '2.分析师会议/电话会议——所有讨论季度、年度财务和业务信息的分析师会议或通话应通过互联网或电话会议同时向所有感兴趣的公众广播。会议的适当提前通知和同步广播应通过新闻稿或符合FD条例的其他通信方式进行。',
'3.收益新闻稿——收益新闻稿将在投资者关系部和首席财务官确定的会议或电话会议开始时或之前按照适用的美国证券交易委员会和纳斯达克规则在新闻专线上发布以8-K表格的形式提供给美国证券交易会并发布在公司网站上。', '3.收益新闻稿——收益新闻稿将在投资者关系部和首席财务官确定的会议或电话会议开始时或之前按照适用的美国证券交易委员会和纳斯达克规则在新闻专线上发布以8-K表格的形式提供给美国证券交易会并发布在公司网站上。',
'4.有关年度首次认购收入和每股收益范围的指导意见,可在收益新闻稿中提供,如有必要,可在每个季度的收益新闻稿上提供对指导意见的修改。一般来说,公司不会在本季度更新本指南或提供额外指南,除非财务副总裁/首席财务官认为有必要并且只能根据FD条例在公开论坛上提供。' '4.有关年度首次认购收入和每股收益范围的指导意见,可在收益新闻稿中提供,如有必要,可在每个季度的收益新闻稿上提供对指导意见的修改。一般来说,公司不会在本季度更新本指南或提供额外指南,除非财务副总裁/首席财务官认为有必要并且只能根据FD条例在公开论坛上提供。',
'收到媒体、市场专业人士或股东任何询问的Minim代表授权发言人除外不得回复此类询问但应将提问者转介给授权发言人。然而分配给Minim投资者关系和营销团队的Minim代表可以按照授权发言人不时制定的指导方针对公开信息的例行询问作出回应。'
], ],
note: '收到媒体、市场专业人士或股东任何询问的Minim代表授权发言人除外不得回复此类询问但应将提问者转介给授权发言人。然而分配给Minim投资者关系和营销团队的Minim代表可以按照授权发言人不时制定的指导方针对公开信息的例行询问作出回应。' note: '收到媒体、市场专业人士或股东任何询问的Minim代表授权发言人除外不得回复此类询问但应将提问者转介给授权发言人。然而分配给Minim投资者关系和营销团队的Minim代表可以按照授权发言人不时制定的指导方针对公开信息的例行询问作出回应。'
} }
} },
financialinformation: {
quarterlyreports: {
title: '季度报告',
search: {
placeholder: '搜索',
button: '开始',
},
pdfDownload: 'PDF 下载',
pagination: {
displaying: '显示第 {start} - {end} 条,共 {total} 条',
perPage: '{size}/页',
goto: '前往',
},
},
},
stock_quote: {
title: '股票报价',
nasdaq: '纳斯达克: FIEE',
open: '开盘',
change: '涨跌幅',
volume: '成交量',
week_range: '52周范围',
days_range: '当日范围',
market_cap: '市值',
},
} }

220
src/locales/zhc.js Normal file
View File

@ -0,0 +1,220 @@
export default {
language: {
zh: '简体中文',
zhTW: '繁體中文',
en: 'English',
ja: '日本語',
},
home: {
nav: {
home: '首页',
company: '公司概况',
businessintroduction: '业务介绍',
please_select: '请选择',
confirm_select: '确认选择',
investor: '投资者关系',
},
},
financialinformation: {
quarterlyreports: {
title: '季度报告',
search: {
placeholder: '搜索',
button: '开始',
},
download: "下载",
pdfDownload: 'PDF 下载',
pagination: {
displaying: '显示第 {start} - {end} 条,共 {total} 条',
perPage: '{size}/页',
goto: '前往',
},
},
},
stock_quote: {
title: '股票报价',
nasdaq: '纳斯达克: FIEE',
open: '开盘',
change: '涨跌幅',
volume: '成交量',
week_range: '52周范围',
days_range: '当日范围',
market_cap: '市值',
},
historic_stock: {
title: '历史数据',
range: '范围',
date: '日期',
open: '开盘',
high: '最高',
low: '最低',
close: '收盘',
adj_close: '调整后收盘',
change: '涨跌幅',
volume: '成交量',
daily: '每日',
weekly: '每周',
monthly: '每月',
quarterly: '每季度',
annual: '每年',
duration: {
"3_months": '3个月',
"6_months": '6个月',
ytd: '年初至今',
"1_year": '1年',
"5_years": '5年',
"10_years": '10年',
},
pagination: {
displaying: '显示第 {start} - {end} 条,共 {total} 条',
perPage: '{size}/页',
goto: '前往',
},
echarts: {
title: 'FiEE, Inc. 股票历史价格',
range: '范围',
price: '价格',
months: '月',
years: '年',
year: '年',
ytd: '年初至今',
"1m": '1个月',
"3m": '3个月',
ytd_short: '年初至今',
"1y": '1年',
"5y": '5年',
"10y": '10年',
max: '最大',
start_time: '开始时间',
end_time: '结束时间',
},
},
header_menu: {
corporate_information: {
title: '公司信息',
company_overview: '公司概况',
business_introduction: '业务介绍',
management: '管理层',
board_of_directors: '董事会',
committee_appointments: '委员会任命',
governance: '公司治理',
corporate_video: '公司视频',
},
financial_information: {
title: '财务信息',
sec_filings: 'SEC文件',
annual_reports: '年度报告',
quarterly_reports: '季度报告',
},
stock_information: {
title: '股票信息',
stock_quote: '股票报价',
historic_stock_price: '历史股价',
investment_calculator: '投资计算器',
},
news_releases: {
title: '新闻发布',
press_releases: '新闻稿',
events_calendar: '活动日历',
},
investor_resources: {
title: '投资者资源',
ir_contacts: '投资者关系联系人',
email_alerts: '邮件提醒',
},
product_introduction: '产品介绍',
},
press_releases: {
title: "新闻稿",
search: {
placeholder: "搜索",
button: "搜索",
},
},
events_calendar: {
title: "活动日历",
search: {
button: "开始",
},
},
product_intro: {
hero_line1: "不止于工具——",
hero_line2: "全链路增长解决方案,",
hero_line3: "为内容创作提供一站式方案,",
hero_line4: "覆盖发布、分析与变现",
core_value_title: "核心价值",
core_value_text:
"FIEE-SAAS 平台是为数字时代创作者量身打造的一站式内容运营解决方案。平台通过智能分发技术、AI 赋能工具与全链路服务,助你高效触达 TikTok、YouTube、Instagram 等全球主流平台受众,打造 KOL 品牌效应,释放内容价值,实现可持续增长。",
features_title: "产品功能",
feature_sync: "一键同步发布",
feature_sync_desc:
"图文与视频内容一键同步至 TikTok、YouTube、Instagram节省重复操作时间。",
feature_schedule: "智能定时发布",
feature_schedule_desc:
"提前规划内容发布时间,支持批量排期,精准把握各平台最佳发布时间。",
feature_accounts: "多账号统一管理",
feature_accounts_desc:
"一个平台轻松管理多个账号,无需反复登录与切换,提升团队协同效率。",
feature_library: "云端素材库",
feature_library_desc:
"安全存储与管理全部创作素材,随时随地访问与使用,支持快速检索与复用。",
feature_tracking: "基础数据追踪",
feature_tracking_desc:
"可视化查看各平台内容表现,掌握核心数据指标,为策略优化提供依据。",
solutions_title: "增值解决方案",
sol_kol: "KOL 品牌推广服务",
sol_kol_desc:
"高效对接优质商务合作机会,在平台内完成从接单到发布的全流程管理。",
sol_content: "专业内容创作支持",
sol_content_desc:
"为你对接专业拍摄与后期团队,打造高质量“艺术+故事”内容,强化 IP 影响力。",
sol_ops: "账号运营与代运营服务",
sol_ops_desc:
"从 0 到 1 账号定位、粉丝增长策略到变现闭环,运营专家提供全周期运营与托管服务。",
advantages_title: "我们的优势",
adv_time: "节省时间",
adv_time_desc: "多平台联动提升发布效率,专注内容创作。",
adv_safe: "安全可靠",
adv_safe_desc: "企业级数据加密与权限控制,保障账号与内容安全。",
adv_consistent: "保持一致性",
adv_consistent_desc: "确保品牌信息在各平台统一呈现。",
adv_data: "数据驱动",
adv_data_desc: "基于实际表现优化内容策略。",
adv_easy: "易于使用",
adv_easy_desc: "直观的界面设计,无需专业技术背景。",
cta_title_line1: "免费获取",
cta_title_line2: "专属定制方案",
},
contacts: {
title: "投资者联系方式",
company_name: "FiEE Inc.",
investor_relations: "投资者关系",
email_label: "邮箱:",
},
email_alerts: {
title: "邮件提醒",
required_fields: "* 必填字段",
submitted_successfully: "提交成功!",
submitted_info: "您提交的信息如下:",
form: {
first_name: "* 名字",
last_name: "* 姓氏",
email: "* 邮箱",
company: "* 公司",
phone: "* 电话",
submit: "提交",
},
submitted_data: {
first_name: "名字:",
last_name: "姓氏:",
email: "邮箱:",
company: "公司:",
phone: "电话:",
not_filled: "未填写",
},
validation: {
complete_info: "请填写完整信息",
field_length: "字段长度不能超过50个字符",
},
},
}

View File

@ -10,7 +10,6 @@ export function useLanguage() {
{ label: t('language.zhTW'), value: 'zh-TW', key: 'zh-TW' }, { label: t('language.zhTW'), value: 'zh-TW', key: 'zh-TW' },
{ label: t('language.en'), value: 'en', key: 'en' }, { label: t('language.en'), value: 'en', key: 'en' },
{ label: t('language.ja'), value: 'ja', key: 'jp' }, { label: t('language.ja'), value: 'ja', key: 'jp' },
{ label: t('language.de'), value: 'de', key: 'de' }
]); ]);
// 当前选中的语言 // 当前选中的语言
@ -31,8 +30,7 @@ export function useLanguage() {
return 'jp'; return 'jp';
case 'en': case 'en':
return 'en'; return 'en';
case 'de':
return 'de';
default: default:
return 'cn'; return 'cn';
} }
@ -62,9 +60,7 @@ export function useLanguage() {
if (lang.includes('ja')) { if (lang.includes('ja')) {
return 'ja'; return 'ja';
} }
if (lang.includes('de')) {
return 'de';
}
return 'en'; return 'en';
}; };

View File

@ -1,6 +1,9 @@
<script setup> <script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
function copyEmail() { function copyEmail() {
navigator.clipboard.writeText("fiee@dlkadvisory.com"); navigator.clipboard.writeText("fiee@dlkadvisory.com");
@ -12,15 +15,15 @@ function copyEmail() {
<!-- Title Section --> <!-- Title Section -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="contact-title">Investor Contacts</div> <div class="contact-title">{{ t("contacts.title") }}</div>
</div> </div>
<!-- Card Section --> <!-- Card Section -->
<div class="contact-card"> <div class="contact-card">
<div class="logo-text">FiEE Inc.</div> <div class="logo-text">{{ t("contacts.company_name") }}</div>
<div class="relation-text">Investor Relations</div> <div class="relation-text">{{ t("contacts.investor_relations") }}</div>
<div class="email-section"> <div class="email-section">
<span>Email:</span> <span>{{ t("contacts.email_label") }}</span>
<span class="email-address" @click="copyEmail" <span class="email-address" @click="copyEmail"
>fiee@dlkadvisory.com</span >fiee@dlkadvisory.com</span
> >

View File

@ -1,6 +1,9 @@
<script setup> <script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
function copyEmail() { function copyEmail() {
navigator.clipboard.writeText("fiee@dlkadvisory.com"); navigator.clipboard.writeText("fiee@dlkadvisory.com");
@ -12,15 +15,15 @@ function copyEmail() {
<!-- Title Section --> <!-- Title Section -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="contact-title">Investor Contacts</div> <div class="contact-title">{{ t("contacts.title") }}</div>
</div> </div>
<!-- Card Section --> <!-- Card Section -->
<div class="contact-card"> <div class="contact-card">
<div class="logo-text">FiEE Inc.</div> <div class="logo-text">{{ t("contacts.company_name") }}</div>
<div class="relation-text">Investor Relations</div> <div class="relation-text">{{ t("contacts.investor_relations") }}</div>
<div class="email-section"> <div class="email-section">
<span>Email:</span> <span>{{ t("contacts.email_label") }}</span>
<span class="email-address" @click="copyEmail" <span class="email-address" @click="copyEmail"
>fiee@dlkadvisory.com</span >fiee@dlkadvisory.com</span
> >

View File

@ -1,6 +1,9 @@
<script setup> <script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
function copyEmail() { function copyEmail() {
navigator.clipboard.writeText("fiee@dlkadvisory.com"); navigator.clipboard.writeText("fiee@dlkadvisory.com");
@ -12,7 +15,7 @@ function copyEmail() {
<!-- Title Section --> <!-- Title Section -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="contact-title">Investor Contacts</div> <div class="contact-title">{{ t("contacts.title") }}</div>
</div> </div>
<!-- Card Section --> <!-- Card Section -->
@ -22,10 +25,10 @@ function copyEmail() {
src="@/assets/image/375/contacts-bg.png" src="@/assets/image/375/contacts-bg.png"
alt="" alt=""
/> />
<div class="logo-text">FiEE Inc.</div> <div class="logo-text">{{ t("contacts.company_name") }}</div>
<div class="relation-text">Investor Relations</div> <div class="relation-text">{{ t("contacts.investor_relations") }}</div>
<div class="email-section"> <div class="email-section">
<span>Email:</span> <span>{{ t("contacts.email_label") }}</span>
<span class="email-address" @click="copyEmail" <span class="email-address" @click="copyEmail"
>fiee@dlkadvisory.com</span >fiee@dlkadvisory.com</span
> >

View File

@ -1,6 +1,9 @@
<script setup> <script setup>
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
function copyEmail() { function copyEmail() {
navigator.clipboard.writeText("fiee@dlkadvisory.com"); navigator.clipboard.writeText("fiee@dlkadvisory.com");
@ -12,15 +15,15 @@ function copyEmail() {
<!-- Title Section --> <!-- Title Section -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="contact-title">Investor Contacts</div> <div class="contact-title">{{ t("contacts.title") }}</div>
</div> </div>
<!-- Card Section --> <!-- Card Section -->
<div class="contact-card"> <div class="contact-card">
<div class="logo-text">FiEE Inc.</div> <div class="logo-text">{{ t("contacts.company_name") }}</div>
<div class="relation-text">Investor Relations</div> <div class="relation-text">{{ t("contacts.investor_relations") }}</div>
<div class="email-section"> <div class="email-section">
<span>Email:</span> <span>{{ t("contacts.email_label") }}</span>
<span class="email-address" @click="copyEmail" <span class="email-address" @click="copyEmail"
>fiee@dlkadvisory.com</span >fiee@dlkadvisory.com</span
> >

View File

@ -1,7 +1,11 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
import { message } from "@/utils/message.js";
const { t } = useI18n();
const form = ref({ const form = ref({
firstName: "", firstName: "",
lastName: "", lastName: "",
@ -13,13 +17,21 @@ const submitted = ref(false);
async function handleSubmit(e) { async function handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const res = await axios.post( if (
"https://erpapi-out.szjixun.cn/api/stock/submit/data", Object.values(form.value).some((value) => value === "" || value === null)
form.value ) {
); message.warning(t("email_alerts.validation.complete_info"));
return;
}
if (Object.values(form.value).some((value) => value.length > 50)) {
message.warning(t("email_alerts.validation.field_length"));
return;
}
let url = "http://114.218.158.24:9020/api/fiee/emailalerts/submit";
// let url = 'https://erpapi-out.szjixun.cn/api/fiee/emailalerts/submit'
const res = await axios.post(url, form.value);
if (res.data.status === 0) { if (res.data.status === 0) {
submitted.value = true; submitted.value = true;
} else {
} }
} }
</script> </script>
@ -30,14 +42,14 @@ async function handleSubmit(e) {
<!-- 未提交 --> <!-- 未提交 -->
<div v-if="!submitted" class="title-section"> <div v-if="!submitted" class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title">E-Mail Alerts</div> <div class="title">{{ t("email_alerts.title") }}</div>
<div class="subtitle">* Required Fields</div> <div class="subtitle">{{ t("email_alerts.required_fields") }}</div>
</div> </div>
<!-- 已提交 --> <!-- 已提交 -->
<div v-else class="title-section mt-[60px]"> <div v-else class="title-section mt-[60px]">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title">Submitted successfully!</div> <div class="title">{{ t("email_alerts.submitted_successfully") }}</div>
<div class="subtitle">The information you submitted is as follows:</div> <div class="subtitle">{{ t("email_alerts.submitted_info") }}</div>
</div> </div>
<!-- Form Card --> <!-- Form Card -->
<div <div
@ -50,8 +62,11 @@ async function handleSubmit(e) {
<template v-if="!submitted"> <template v-if="!submitted">
<form class="form-content" @submit="handleSubmit"> <form class="form-content" @submit="handleSubmit">
<div class="form-group"> <div class="form-group">
<label for="firstName">* First Name</label> <label for="firstName">{{
t("email_alerts.form.first_name")
}}</label>
<input <input
v-no-space
id="firstName" id="firstName"
v-model="form.firstName" v-model="form.firstName"
type="text" type="text"
@ -59,46 +74,92 @@ async function handleSubmit(e) {
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastName">* Last Name</label> <label for="lastName">{{ t("email_alerts.form.last_name") }}</label>
<input id="lastName" v-model="form.lastName" type="text" required /> <input
v-no-space
id="lastName"
v-model="form.lastName"
type="text"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">* Email</label> <label for="email">{{ t("email_alerts.form.email") }}</label>
<input id="email" v-model="form.email" type="email" required /> <input
v-no-space
id="email"
v-model="form.email"
type="email"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="company">* Company</label> <label for="company">{{ t("email_alerts.form.company") }}</label>
<input id="company" v-model="form.company" type="text" required /> <input
v-no-space
id="company"
v-model="form.company"
type="text"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="phone">* Phone</label> <label for="phone">{{ t("email_alerts.form.phone") }}</label>
<input id="phone" v-model="form.phone" type="tel" required /> <input
v-no-space
id="phone"
v-model="form.phone"
type="tel"
required
/>
</div> </div>
<button type="submit" class="submit-btn">Submit</button> <button type="submit" class="submit-btn">
{{ t("email_alerts.form.submit") }}
</button>
</form> </form>
</template> </template>
<template v-else> <template v-else>
<div class="submitted-data"> <div class="submitted-data">
<div class="submitted-data-content"> <div class="submitted-data-content">
<div class="submitted-row"> <div class="submitted-row">
<span class="label">First Name</span> <span class="label">{{
<span class="value">{{ form.firstName || "Not filled in" }}</span> t("email_alerts.submitted_data.first_name")
}}</span>
<span class="value">{{
form.firstName || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Last Name</span> <span class="label">{{
<span class="value">{{ form.lastName || "Not filled in" }}</span> t("email_alerts.submitted_data.last_name")
}}</span>
<span class="value">{{
form.lastName || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Email</span> <span class="label">{{
<span class="value">{{ form.email || "Not filled in" }}</span> t("email_alerts.submitted_data.email")
}}</span>
<span class="value">{{
form.email || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Company</span> <span class="label">{{
<span class="value">{{ form.company || "Not filled in" }}</span> t("email_alerts.submitted_data.company")
}}</span>
<span class="value">{{
form.company || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Phone</span> <span class="label">{{
<span class="value">{{ form.phone || "Not filled in" }}</span> t("email_alerts.submitted_data.phone")
}}</span>
<span class="value">{{
form.phone || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,11 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
import { message } from "@/utils/message.js";
const { t } = useI18n();
const form = ref({ const form = ref({
firstName: "", firstName: "",
lastName: "", lastName: "",
@ -13,13 +17,21 @@ const submitted = ref(false);
async function handleSubmit(e) { async function handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const res = await axios.post( if (
"https://erpapi-out.szjixun.cn/api/stock/submit/data", Object.values(form.value).some((value) => value === "" || value === null)
form.value ) {
); message.warning(t("email_alerts.validation.complete_info"));
return;
}
if (Object.values(form.value).some((value) => value.length > 50)) {
message.warning(t("email_alerts.validation.field_length"));
return;
}
let url = "http://114.218.158.24:9020/api/fiee/emailalerts/submit";
// let url = 'https://erpapi-out.szjixun.cn/api/fiee/emailalerts/submit'
const res = await axios.post(url, form.value);
if (res.data.status === 0) { if (res.data.status === 0) {
submitted.value = true; submitted.value = true;
} else {
} }
} }
</script> </script>
@ -30,14 +42,14 @@ async function handleSubmit(e) {
<!-- 未提交 --> <!-- 未提交 -->
<div v-if="!submitted" class="title-section"> <div v-if="!submitted" class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title">E-Mail Alerts</div> <div class="title">{{ t("email_alerts.title") }}</div>
<div class="subtitle">* Required Fields</div> <div class="subtitle">{{ t("email_alerts.required_fields") }}</div>
</div> </div>
<!-- 已提交 --> <!-- 已提交 -->
<div v-else class="title-section"> <div v-else class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title">Submitted successfully!</div> <div class="title">{{ t("email_alerts.submitted_successfully") }}</div>
<div class="subtitle">The information you submitted is as follows:</div> <div class="subtitle">{{ t("email_alerts.submitted_info") }}</div>
</div> </div>
<!-- Form Card --> <!-- Form Card -->
<div <div
@ -50,55 +62,98 @@ async function handleSubmit(e) {
<template v-if="!submitted"> <template v-if="!submitted">
<form class="form-content" @submit="handleSubmit"> <form class="form-content" @submit="handleSubmit">
<div class="form-group"> <div class="form-group">
<label for="firstName">* First Name</label> <label for="firstName">{{
t("email_alerts.form.first_name")
}}</label>
<input <input
id="firstName" id="firstName"
v-no-space
v-model="form.firstName" v-model="form.firstName"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastName">* Last Name</label> <label for="lastName">{{ t("email_alerts.form.last_name") }}</label>
<input id="lastName" v-model="form.lastName" type="text" required /> <input
id="lastName"
v-no-space
v-model="form.lastName"
type="text"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">* Email</label> <label for="email">{{ t("email_alerts.form.email") }}</label>
<input id="email" v-model="form.email" type="email" required /> <input
id="email"
v-no-space
v-model="form.email"
type="email"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="company">* Company</label> <label for="company">{{ t("email_alerts.form.company") }}</label>
<input id="company" v-model="form.company" type="text" required /> <input
id="company"
v-no-space
v-model="form.company"
type="text"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="phone">* Phone</label> <label for="phone">{{ t("email_alerts.form.phone") }}</label>
<input id="phone" v-model="form.phone" type="tel" /> <input id="phone" v-no-space v-model="form.phone" type="tel" />
</div> </div>
<button type="submit" class="submit-btn">Submit</button> <button type="submit" class="submit-btn">
{{ t("email_alerts.form.submit") }}
</button>
</form> </form>
</template> </template>
<template v-else> <template v-else>
<div class="submitted-data"> <div class="submitted-data">
<div class="submitted-data-content"> <div class="submitted-data-content">
<div class="submitted-row"> <div class="submitted-row">
<span class="label">First Name</span> <span class="label">{{
<span class="value">{{ form.firstName || "Not filled in" }}</span> t("email_alerts.submitted_data.first_name")
}}</span>
<span class="value">{{
form.firstName || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Last Name</span> <span class="label">{{
<span class="value">{{ form.lastName || "Not filled in" }}</span> t("email_alerts.submitted_data.last_name")
}}</span>
<span class="value">{{
form.lastName || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Email</span> <span class="label">{{
<span class="value">{{ form.email || "Not filled in" }}</span> t("email_alerts.submitted_data.email")
}}</span>
<span class="value">{{
form.email || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Company</span> <span class="label">{{
<span class="value">{{ form.company || "Not filled in" }}</span> t("email_alerts.submitted_data.company")
}}</span>
<span class="value">{{
form.company || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Phone</span> <span class="label">{{
<span class="value">{{ form.phone || "Not filled in" }}</span> t("email_alerts.submitted_data.phone")
}}</span>
<span class="value">{{
form.phone || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,6 +1,10 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
import { message } from "@/utils/message.js";
const { t } = useI18n();
const form = ref({ const form = ref({
firstName: "", firstName: "",
lastName: "", lastName: "",
@ -13,140 +17,265 @@ const submitted = ref(false);
async function handleSubmit(e) { async function handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const res = await axios.post( if (
"https://erpapi-out.szjixun.cn/api/stock/submit/data", Object.values(form.value).some((value) => value === "" || value === null)
form.value ) {
); message.warning(t("email_alerts.validation.complete_info"));
return;
}
if (Object.values(form.value).some((value) => value.length > 50)) {
message.warning(t("email_alerts.validation.field_length"));
return;
}
let url = "http://114.218.158.24:9020/api/fiee/emailalerts/submit";
// let url = 'https://erpapi-out.szjixun.cn/api/fiee/emailalerts/submit'
const res = await axios.post(url, form.value);
if (res.data.status === 0) { if (res.data.status === 0) {
submitted.value = true; submitted.value = true;
} else {
} }
} }
</script> </script>
<template> <template>
<main <main class="email-alerts-375">
class="min-h-60vh flex flex-col items-center justify-center relative px-4 py-8" <!-- Header -->
> <template v-if="!submitted">
<!-- Card --> <div class="title-block">
<div <div class="title-bar"></div>
class="w-full max-w-90vw p-4 bg-white/95 rounded-2xl shadow-lg animate-bounce-in" <h2 class="title">{{ t("email_alerts.title") }}</h2>
> <p class="subtitle">{{ t("email_alerts.required_fields") }}</p>
<template v-if="!submitted"> </div>
<h2 <!-- Card -->
class="text-xl font-bold text-#ff7bac mb-2 text-center tracking-wide" <div class="card">
> <form class="form" @submit="handleSubmit">
E-Mail Alerts <div class="form-field">
</h2> <label>{{ t("email_alerts.form.first_name") }}</label>
<p class="text-xs text-gray-500 mb-4 text-center">* Required Fields</p> <input v-no-space v-model="form.firstName" type="text" />
<form class="flex flex-col gap-3" @submit="handleSubmit">
<div>
<label class="block text-gray-700 font-semibold mb-1 text-sm"
>* First Name</label
>
<input
v-model="form.firstName"
type="text"
class="w-full px-3 py-2 rounded-lg ring-8 ring-#ff7bac/20) transition-all duration-300 outline-none bg-white/90 border-none"
/>
</div> </div>
<div> <div class="form-field">
<label class="block text-gray-700 font-semibold mb-1 text-sm" <label>{{ t("email_alerts.form.last_name") }}</label>
>* Last Name</label <input v-no-space v-model="form.lastName" type="text" />
>
<input
v-model="form.lastName"
type="text"
class="w-full px-3 py-2 rounded-lg ring-8 ring-#ff7bac/20) transition-all duration-300 outline-none bg-white/90 border-none"
/>
</div> </div>
<div> <div class="form-field">
<label class="block text-gray-700 font-semibold mb-1 text-sm" <label>{{ t("email_alerts.form.email") }}</label>
>* Email</label <input v-no-space v-model="form.email" type="email" />
>
<input
v-model="form.email"
type="email"
class="w-full px-3 py-2 rounded-lg ring-8 ring-#ff7bac/20) transition-all duration-300 outline-none bg-white/90 border-none"
/>
</div> </div>
<div> <div class="form-field">
<label class="block text-gray-700 font-semibold mb-1 text-sm" <label>{{ t("email_alerts.form.company") }}</label>
>* Company</label <input v-no-space v-model="form.company" type="text" />
>
<input
v-model="form.company"
type="text"
class="w-full px-3 py-2 rounded-lg ring-8 ring-#ff7bac/20) transition-all duration-300 outline-none bg-white/90 border-none"
/>
</div> </div>
<div> <div class="form-field">
<label class="block text-gray-700 font-semibold mb-1 text-sm" <label>{{ t("email_alerts.form.phone") }}</label>
>Phone</label <input v-no-space v-model="form.phone" type="tel" />
>
<input
v-model="form.phone"
type="tel"
class="w-full px-3 py-2 rounded-lg ring-8 ring-#ff7bac/20) transition-all duration-300 outline-none bg-white/90 border-none"
/>
</div> </div>
<button <button type="submit" class="submit">
type="submit" {{ t("email_alerts.form.submit") }}
class="w-full py-3 rounded-xl text-white font-bold text-base active:scale-95 transition-all duration-200 animate-bounce-in animate-delay-200 mt-2 submit-btn"
>
Submit
</button> </button>
</form> </form>
</template> </div>
<template v-else> </template>
<div <template v-else>
class="flex flex-col items-center justify-center min-h-[200px] animate-bounce-in" <div class="success-block">
> <div class="title-block">
<span <div class="title-bar"></div>
class="i-mdi:check-circle-outline text-green-500 text-4xl mb-3" <h2 class="title">{{ t("email_alerts.submitted_successfully") }}</h2>
></span> <p class="subtitle">{{ t("email_alerts.submitted_info") }}</p>
<h2 class="text-lg font-bold text-#ff7bac mb-2"> </div>
Submitted successfully!
</h2> <div class="success-card">
<div class="text-gray-700 text-sm mb-3"> <div class="success-card-item">
The information you submitted is as follows: <div class="font-semibold success-card-label">
{{ t("email_alerts.submitted_data.first_name") }}
</div>
{{ form.firstName || t("email_alerts.submitted_data.not_filled") }}
</div> </div>
<div <div class="success-card-item">
class="w-full bg-white/90 rounded-xl shadow p-3 space-y-1 text-gray-800 text-sm" <div class="font-semibold success-card-label">
> {{ t("email_alerts.submitted_data.last_name") }}
<div>
<span class="font-semibold">First Name</span
>{{ form.firstName }}
</div> </div>
<div> {{ form.lastName || t("email_alerts.submitted_data.not_filled") }}
<span class="font-semibold">Last Name</span>{{ form.lastName }} </div>
<div class="success-card-item">
<div class="font-semibold success-card-label">
{{ t("email_alerts.submitted_data.email") }}
</div> </div>
<div> {{ form.email || t("email_alerts.submitted_data.not_filled") }}
<span class="font-semibold">Email</span>{{ form.email }} </div>
<div class="success-card-item">
<div class="font-semibold success-card-label">
{{ t("email_alerts.submitted_data.company") }}
</div> </div>
<div> {{ form.company || t("email_alerts.submitted_data.not_filled") }}
<span class="font-semibold">Company</span>{{ form.company }} </div>
</div> <div class="success-card-item">
<div> <div class="font-semibold success-card-label">
<span class="font-semibold">Phone</span {{ t("email_alerts.submitted_data.phone") }}
>{{ form.phone || "(Not filled)" }}
</div>
<div>
<span class="font-semibold">Alert Type</span
>{{
form.alertType === "all" ? "All Alerts" : "Customize Alerts"
}}
</div> </div>
{{ form.phone || t("email_alerts.submitted_data.not_filled") }}
</div> </div>
</div> </div>
</template> <div class="submitted-bg"></div>
</div> </div>
</template>
</main> </main>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
/* Keep mobile background simple */ /* 375*5.12px design exact *5.12px values from Figma */
.submit-btn { .email-alerts-375 {
background: linear-gradient(to right, #ff7bac, #00ffff); max-width: 343 * 5.12px;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
gap: 24 * 5.12px; /* spacing between header and card */
}
.title-block {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8 * 5.12px;
padding: 0 16 * 5.12px;
margin-top: 47 * 5.12px;
}
.title-bar {
width: 58 * 5.12px;
height: 7 * 5.12px;
background: #ff7bac; /* 主题色-粉色 */
}
.title {
margin: 0;
color: #000000; /* 标题色 */
font-family: "PingFang SC", Arial, Helvetica, sans-serif;
font-weight: 500;
font-size: 24 * 5.12px; /* 手机端主标题 */
line-height: 24 * 5.12px;
text-align: center;
}
.subtitle {
margin: 0;
width: 100%;
color: #455363; /* 正文色 */
font-family: "PingFang SC", Arial, Helvetica, sans-serif;
font-weight: 400;
font-size: 14 * 5.12px; /* 移动端正文 */
line-height: 14 * 5.12px;
letter-spacing: 0.48 * 5.12px;
text-align: center;
}
.card {
width: 100%;
background: #ffffff;
border-radius: 16 * 5.12px;
box-shadow: 0 * 5.12px 3 * 5.12px 14 * 5.12px 0 * 5.12px rgba(0, 0, 0, 0.16);
box-sizing: border-box;
padding: 24 * 5.12px;
display: flex;
align-items: center;
justify-content: center;
}
.form {
width: 100%;
display: flex;
flex-direction: column;
gap: 24 * 5.12px;
}
.form-field {
display: flex;
flex-direction: column;
gap: 8 * 5.12px;
}
.form-field > label {
color: #000000;
font-family: "PingFang SC", Arial, Helvetica, sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
line-height: 14 * 5.12px;
letter-spacing: 0.48 * 5.12px;
}
.form-field > input {
width: 100%;
height: 38 * 5.12px;
background: #ffffff;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 8 * 5.12px;
padding: 0 12 * 5.12px;
font-size: 14 * 5.12px;
outline: none;
}
.submit {
width: 100%;
height: 48 * 5.12px;
border: none;
border-radius: 8 * 5.12px;
background: #ff7bac;
color: #ffffff;
font-family: "PingFang SC", Arial, Helvetica, sans-serif;
font-weight: 500;
font-size: 20 * 5.12px; /* 手机端三级标题 */
line-height: 20 * 5.12px;
letter-spacing: 1.2 * 5.12px; /* 按钮字间距 */
}
/* success view simple alignment */
.success-block {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.success-title {
margin: 0 0 8 * 5.12px 0;
color: #ff7bac;
font-weight: 700;
font-size: 18 * 5.12px;
}
.success-subtitle {
margin-bottom: 12 * 5.12px;
color: #374151;
font-size: 14 * 5.12px;
}
.success-card {
width: 100%;
background: rgba(255, 255, 255, 0.9);
border-radius: 12 * 5.12px;
box-shadow: 0 1 * 5.12px 3 * 5.12px rgba(0, 0, 0, 0.08);
padding: 56 * 5.12px 38 * 5.12px;
min-height: 484 * 5.12px;
margin-top: 24 * 5.12px;
}
.submitted-bg {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 186 * 5.12px;
background-image: url("@/assets/image/375/email-alerts-submit.png");
background-repeat: no-repeat;
background-position: bottom;
background-size: 100%;
}
.success-card-item {
display: flex;
align-items: center;
margin-bottom: 16 * 5.12px;
}
.success-card-label {
width: 92 * 5.12px;
} }
</style> </style>

View File

@ -1,7 +1,11 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
import { message } from "@/utils/message.js";
const { t } = useI18n();
const form = ref({ const form = ref({
firstName: "", firstName: "",
lastName: "", lastName: "",
@ -13,13 +17,21 @@ const submitted = ref(true);
async function handleSubmit(e) { async function handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const res = await axios.post( if (
"https://erpapi-out.szjixun.cn/api/stock/submit/data", Object.values(form.value).some((value) => value === "" || value === null)
form.value ) {
); message.warning(t("email_alerts.validation.complete_info"));
return;
}
if (Object.values(form.value).some((value) => value.length > 50)) {
message.warning(t("email_alerts.validation.field_length"));
return;
}
let url = "http://114.218.158.24:9020/api/fiee/emailalerts/submit";
// let url = 'https://erpapi-out.szjixun.cn/api/fiee/emailalerts/submit'
const res = await axios.post(url, form.value);
if (res.data.status === 0) { if (res.data.status === 0) {
submitted.value = true; submitted.value = true;
} else {
} }
} }
</script> </script>
@ -30,14 +42,14 @@ async function handleSubmit(e) {
<!-- 未提交 --> <!-- 未提交 -->
<div v-if="!submitted" class="title-section"> <div v-if="!submitted" class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title">E-Mail Alerts</div> <div class="title">{{ t("email_alerts.title") }}</div>
<div class="subtitle">* Required Fields</div> <div class="subtitle">{{ t("email_alerts.required_fields") }}</div>
</div> </div>
<!-- 已提交 --> <!-- 已提交 -->
<div v-else class="title-section"> <div v-else class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title">Submitted successfully!</div> <div class="title">{{ t("email_alerts.submitted_successfully") }}</div>
<div class="subtitle">The information you submitted is as follows:</div> <div class="subtitle">{{ t("email_alerts.submitted_info") }}</div>
</div> </div>
<!-- Form Card --> <!-- Form Card -->
<div <div
@ -50,55 +62,98 @@ async function handleSubmit(e) {
<template v-if="!submitted"> <template v-if="!submitted">
<form class="form-content" @submit="handleSubmit"> <form class="form-content" @submit="handleSubmit">
<div class="form-group mt-[36px]"> <div class="form-group mt-[36px]">
<label for="firstName">* First Name</label> <label for="firstName">{{
t("email_alerts.form.first_name")
}}</label>
<input <input
id="firstName" id="firstName"
v-no-space
v-model="form.firstName" v-model="form.firstName"
type="text" type="text"
required required
/> />
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="lastName">* Last Name</label> <label for="lastName">{{ t("email_alerts.form.last_name") }}</label>
<input id="lastName" v-model="form.lastName" type="text" required /> <input
id="lastName"
v-no-space
v-model="form.lastName"
type="text"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="email">* Email</label> <label for="email">{{ t("email_alerts.form.email") }}</label>
<input id="email" v-model="form.email" type="email" required /> <input
id="email"
v-no-space
v-model="form.email"
type="email"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="company">* Company</label> <label for="company">{{ t("email_alerts.form.company") }}</label>
<input id="company" v-model="form.company" type="text" required /> <input
id="company"
v-no-space
v-model="form.company"
type="text"
required
/>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="phone">* Phone</label> <label for="phone">{{ t("email_alerts.form.phone") }}</label>
<input id="phone" v-model="form.phone" type="tel" /> <input id="phone" v-no-space v-model="form.phone" type="tel" />
</div> </div>
<button type="submit" class="submit-btn">Submit</button> <button type="submit" class="submit-btn">
{{ t("email_alerts.form.submit") }}
</button>
</form> </form>
</template> </template>
<template v-else> <template v-else>
<div class="submitted-data"> <div class="submitted-data">
<div class="submitted-data-content"> <div class="submitted-data-content">
<div class="submitted-row"> <div class="submitted-row">
<span class="label">First Name</span> <span class="label">{{
<span class="value">{{ form.firstName || "Not filled in" }}</span> t("email_alerts.submitted_data.first_name")
}}</span>
<span class="value">{{
form.firstName || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Last Name</span> <span class="label">{{
<span class="value">{{ form.lastName || "Not filled in" }}</span> t("email_alerts.submitted_data.last_name")
}}</span>
<span class="value">{{
form.lastName || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Email</span> <span class="label">{{
<span class="value">{{ form.email || "Not filled in" }}</span> t("email_alerts.submitted_data.email")
}}</span>
<span class="value">{{
form.email || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Company</span> <span class="label">{{
<span class="value">{{ form.company || "Not filled in" }}</span> t("email_alerts.submitted_data.company")
}}</span>
<span class="value">{{
form.company || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
<div class="submitted-row"> <div class="submitted-row">
<span class="label">Phone</span> <span class="label">{{
<span class="value">{{ form.phone || "Not filled in" }}</span> t("email_alerts.submitted_data.phone")
}}</span>
<span class="value">{{
form.phone || t("email_alerts.submitted_data.not_filled")
}}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -26,7 +26,7 @@
<!-- 报告列表 --> <!-- 报告列表 -->
<div class="reports-table"> <div class="reports-table">
<div class="reports-list"> <div class="reports-list">
<div v-for="(item, index) in pagedList" :key="index" class="table-row"> <div v-for="(item, index) in state.list" :key="index" class="table-row">
<div class="content"> <div class="content">
<div class="file-content"> <div class="file-content">
<div class="file-info"> <div class="file-info">
@ -36,8 +36,11 @@
<p class="file-description">{{ item.description }}</p> <p class="file-description">{{ item.description }}</p>
</div> </div>
<div class="download-section"> <div class="download-section">
<p class="download-text" @click="downloadPdf(item.url)"> <p
PDF Download class="download-text"
@click="downloadPdf(item.url, item.attachmentName)"
>
{{ t("financialinformation.quarterlyreports.pdfDownload") }}
</p> </p>
</div> </div>
</div> </div>
@ -49,8 +52,13 @@
<!-- 分页器 --> <!-- 分页器 -->
<div class="pagination-container"> <div class="pagination-container">
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.total }} results t("financialinformation.quarterlyreports.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.total,
})
}}
</div> </div>
<div class="pagination-controls"> <div class="pagination-controls">
<div class="pagination-buttons"> <div class="pagination-buttons">
@ -100,7 +108,11 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: state.pageSize,
})
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -119,12 +131,18 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="changePageSize(size)" @click="changePageSize(size)"
> >
{{ size }}/page {{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: size,
})
}}
</div> </div>
</div> </div>
</div> </div>
<div class="goto-section"> <div class="goto-section">
<span>Goto</span> <span>{{
t("financialinformation.quarterlyreports.pagination.goto")
}}</span>
<input <input
type="number" type="number"
v-model="state.gotoPage" v-model="state.gotoPage"
@ -141,9 +159,9 @@
<script setup> <script setup>
import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue"; import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf"; // import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf";
import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf"; // import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf";
import axios from "axios";
const { t } = useI18n(); const { t } = useI18n();
const searchQuery = ref(""); const searchQuery = ref("");
@ -152,45 +170,77 @@ const state = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
gotoPage: 1, gotoPage: 1,
listConfig: {
url: "http://114.218.158.24:9020/api/fiee/reports/quarterly/display",
// url: "https://erpapi.fiee.com/api/fiee/reports/quarterly/display",
params: {
filtrate: {
fileName: "", //
},
},
},
list: [ list: [
{ // {
title: "2025 Q2 Quarterly Report", // title: "2025 Q2 Quarterly Report",
description: "Second Quarter 2025 Financial Results", // description: "Second Quarter 2025 Financial Results",
url: quarterlyPdf2025Q2, // url: quarterlyPdf2025Q2,
}, // },
{ // {
title: "2025 Q1 Quarterly Report Amendment No.1", // title: "2025 Q1 Quarterly Report Amendment No.1",
description: "First Quarter 2025 Financial Results", // description: "First Quarter 2025 Financial Results",
url: quarterlyPdf2025Q3N, // url: quarterlyPdf2025Q3N,
}, // },
], ],
}); });
watch(searchQuery, (newVal) => {
if (newVal === "" || newVal === null) {
state.listConfig.params.filtrate.fileName = newVal;
state.currentPage = 1;
getListData();
}
});
const showPageSizeMenu = ref(false); const showPageSizeMenu = ref(false);
onMounted(() => { onMounted(() => {
document.addEventListener("click", handleClickOutside); document.addEventListener("click", handleClickOutside);
getListData();
}); });
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener("click", handleClickOutside); document.removeEventListener("click", handleClickOutside);
}); });
const filteredList = computed(() => { const getListData = async () => {
if (!searchQuery.value) return state.list; console.log(state.listConfig);
const query = searchQuery.value.toLowerCase(); const res = await axios.post(state.listConfig.url, state.listConfig.params);
return state.list.filter( console.log(res);
(item) => if (res.data.code === 0) {
item.title.toLowerCase().includes(query) || let resData = res.data.data.Item || [];
item.description.toLowerCase().includes(query) resData.forEach((item) => {
); item.title = item.fileName;
}); item.description = item.fileIntroduce;
item.url = item.attachment;
item.attachmentName = item.attachmentName;
});
state.list = resData;
state.total = res.data.data.total || 0;
}
};
// const filteredList = computed(() => {
// if (!searchQuery.value) return state.list;
// const query = searchQuery.value.toLowerCase();
// return state.list.filter(
// (item) =>
// item.title.toLowerCase().includes(query) ||
// item.description.toLowerCase().includes(query)
// );
// });
// //
const pagedList = computed(() => { // const pagedList = computed(() => {
const start = (state.currentPage - 1) * state.pageSize; // const start = (state.currentPage - 1) * state.pageSize;
const end = start + state.pageSize; // const end = start + state.pageSize;
return filteredList.value.slice(start, end); // return filteredList.value.slice(start, end);
}); // });
// //
const totalPages = computed(() => { const totalPages = computed(() => {
@ -207,13 +257,19 @@ const displayRange = computed(() => {
const handleSearch = () => { const handleSearch = () => {
// //
// console.log(":", searchQuery.value); state.listConfig.params.filtrate.fileName = searchQuery.value;
state.currentPage = 1;
getListData();
}; };
const downloadPdf = async (pdfResource, filename = "") => { const downloadPdf = async (pdfResource, filename = "") => {
try { try {
const isDev = import.meta.env.DEV;
const requestUrl = isDev
? "/pdf-proxy/" + pdfResource.split("//")[1].split("/").slice(1).join("/")
: pdfResource;
// PDF // PDF
const response = await fetch(pdfResource); const response = await fetch(requestUrl);
const blob = await response.blob(); const blob = await response.blob();
// Blob URL // Blob URL
@ -232,7 +288,7 @@ const downloadPdf = async (pdfResource, filename = "") => {
// Blob URL // Blob URL
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
} catch (error) { } catch (error) {
// console.error("PDF:", error); console.error("下载PDF文件失败:", error);
} }
}; };
@ -322,14 +378,14 @@ watch(
} }
); );
watch( // watch(
() => filteredList.value, // () => filteredList.value,
(newList) => { // (newList) => {
state.total = newList.length; // state.total = newList.length;
state.currentPage = 1; // state.currentPage = 1;
}, // },
{ immediate: true } // { immediate: true }
); // );
// //
const handleClickOutside = (event) => { const handleClickOutside = (event) => {

View File

@ -26,7 +26,7 @@
<!-- 报告列表 --> <!-- 报告列表 -->
<div class="reports-table"> <div class="reports-table">
<div class="reports-list"> <div class="reports-list">
<div v-for="(item, index) in pagedList" :key="index" class="table-row"> <div v-for="(item, index) in state.list" :key="index" class="table-row">
<div class="content"> <div class="content">
<div class="file-content"> <div class="file-content">
<div class="file-info"> <div class="file-info">
@ -36,8 +36,11 @@
<p class="file-description">{{ item.description }}</p> <p class="file-description">{{ item.description }}</p>
</div> </div>
<div class="download-section"> <div class="download-section">
<p class="download-text" @click="downloadPdf(item.url)"> <p
PDF Download class="download-text"
@click="downloadPdf(item.url, item.attachmentName)"
>
{{ t("financialinformation.quarterlyreports.pdfDownload") }}
</p> </p>
</div> </div>
</div> </div>
@ -49,8 +52,13 @@
<!-- 分页器 --> <!-- 分页器 -->
<div class="pagination-container"> <div class="pagination-container">
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.total }} results t("financialinformation.quarterlyreports.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.total,
})
}}
</div> </div>
<div class="pagination-controls"> <div class="pagination-controls">
<div class="pagination-buttons"> <div class="pagination-buttons">
@ -100,7 +108,11 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: state.pageSize,
})
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -119,12 +131,18 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="changePageSize(size)" @click="changePageSize(size)"
> >
{{ size }}/page {{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: size,
})
}}
</div> </div>
</div> </div>
</div> </div>
<div class="goto-section"> <div class="goto-section">
<span>Goto</span> <span>{{
t("financialinformation.quarterlyreports.pagination.goto")
}}</span>
<input <input
type="number" type="number"
v-model="state.gotoPage" v-model="state.gotoPage"
@ -141,9 +159,9 @@
<script setup> <script setup>
import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue"; import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf"; // import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf";
import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf"; // import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf";
import axios from "axios";
const { t } = useI18n(); const { t } = useI18n();
const searchQuery = ref(""); const searchQuery = ref("");
@ -152,45 +170,77 @@ const state = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
gotoPage: 1, gotoPage: 1,
listConfig: {
url: "http://114.218.158.24:9020/api/fiee/reports/quarterly/display",
// url: "https://erpapi.fiee.com/api/fiee/reports/quarterly/display",
params: {
filtrate: {
fileName: "", //
},
},
},
list: [ list: [
{ // {
title: "2025 Q2 Quarterly Report", // title: "2025 Q2 Quarterly Report",
description: "Second Quarter 2025 Financial Results", // description: "Second Quarter 2025 Financial Results",
url: quarterlyPdf2025Q2, // url: quarterlyPdf2025Q2,
}, // },
{ // {
title: "2025 Q1 Quarterly Report Amendment No.1", // title: "2025 Q1 Quarterly Report Amendment No.1",
description: "First Quarter 2025 Financial Results", // description: "First Quarter 2025 Financial Results",
url: quarterlyPdf2025Q3N, // url: quarterlyPdf2025Q3N,
}, // },
], ],
}); });
watch(searchQuery, (newVal) => {
if (newVal === "" || newVal === null) {
state.listConfig.params.filtrate.fileName = newVal;
state.currentPage = 1;
getListData();
}
});
const showPageSizeMenu = ref(false); const showPageSizeMenu = ref(false);
onMounted(() => { onMounted(() => {
document.addEventListener("click", handleClickOutside); document.addEventListener("click", handleClickOutside);
getListData();
}); });
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener("click", handleClickOutside); document.removeEventListener("click", handleClickOutside);
}); });
const filteredList = computed(() => { const getListData = async () => {
if (!searchQuery.value) return state.list; console.log(state.listConfig);
const query = searchQuery.value.toLowerCase(); const res = await axios.post(state.listConfig.url, state.listConfig.params);
return state.list.filter( console.log(res);
(item) => if (res.data.code === 0) {
item.title.toLowerCase().includes(query) || let resData = res.data.data.Item || [];
item.description.toLowerCase().includes(query) resData.forEach((item) => {
); item.title = item.fileName;
}); item.description = item.fileIntroduce;
item.url = item.attachment;
item.attachmentName = item.attachmentName;
});
state.list = resData;
state.total = res.data.data.total || 0;
}
};
// const filteredList = computed(() => {
// if (!searchQuery.value) return state.list;
// const query = searchQuery.value.toLowerCase();
// return state.list.filter(
// (item) =>
// item.title.toLowerCase().includes(query) ||
// item.description.toLowerCase().includes(query)
// );
// });
// //
const pagedList = computed(() => { // const pagedList = computed(() => {
const start = (state.currentPage - 1) * state.pageSize; // const start = (state.currentPage - 1) * state.pageSize;
const end = start + state.pageSize; // const end = start + state.pageSize;
return filteredList.value.slice(start, end); // return filteredList.value.slice(start, end);
}); // });
// //
const totalPages = computed(() => { const totalPages = computed(() => {
@ -207,13 +257,19 @@ const displayRange = computed(() => {
const handleSearch = () => { const handleSearch = () => {
// //
// console.log(":", searchQuery.value); state.listConfig.params.filtrate.fileName = searchQuery.value;
state.currentPage = 1;
getListData();
}; };
const downloadPdf = async (pdfResource, filename = "") => { const downloadPdf = async (pdfResource, filename = "") => {
try { try {
const isDev = import.meta.env.DEV;
const requestUrl = isDev
? "/pdf-proxy/" + pdfResource.split("//")[1].split("/").slice(1).join("/")
: pdfResource;
// PDF // PDF
const response = await fetch(pdfResource); const response = await fetch(requestUrl);
const blob = await response.blob(); const blob = await response.blob();
// Blob URL // Blob URL
@ -232,7 +288,7 @@ const downloadPdf = async (pdfResource, filename = "") => {
// Blob URL // Blob URL
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
} catch (error) { } catch (error) {
// console.error("PDF:", error); console.error("下载PDF文件失败:", error);
} }
}; };
@ -322,14 +378,14 @@ watch(
} }
); );
watch( // watch(
() => filteredList.value, // () => filteredList.value,
(newList) => { // (newList) => {
state.total = newList.length; // state.total = newList.length;
state.currentPage = 1; // state.currentPage = 1;
}, // },
{ immediate: true } // { immediate: true }
); // );
// //
const handleClickOutside = (event) => { const handleClickOutside = (event) => {

View File

@ -27,7 +27,7 @@
<!-- 报告列表 --> <!-- 报告列表 -->
<div class="reports-table"> <div class="reports-table">
<div class="reports-list"> <div class="reports-list">
<div v-for="(item, index) in pagedList" :key="index" class="table-row"> <div v-for="(item, index) in state.list" :key="index" class="table-row">
<div class="content"> <div class="content">
<div class="file-content"> <div class="file-content">
<div class="file-info"> <div class="file-info">
@ -35,8 +35,11 @@
<p class="file-title">{{ item.title }}</p> <p class="file-title">{{ item.title }}</p>
</div> </div>
<p class="file-description">{{ item.description }}</p> <p class="file-description">{{ item.description }}</p>
<p class="download-text" @click="downloadPdf(item.url)"> <p
PDF Download class="download-text"
@click="downloadPdf(item.url, item.attachmentName)"
>
{{ t("financialinformation.quarterlyreports.pdfDownload") }}
</p> </p>
</div> </div>
</div> </div>
@ -94,7 +97,11 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: state.pageSize,
})
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -113,23 +120,32 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="changePageSize(size)" @click="changePageSize(size)"
> >
{{ size }}/page {{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: size,
})
}}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.total }} results t("financialinformation.quarterlyreports.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.total,
})
}}
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue"; import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf"; // import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf";
import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf"; // import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf";
import axios from "axios";
const { t } = useI18n(); const { t } = useI18n();
const searchQuery = ref(""); const searchQuery = ref("");
@ -138,45 +154,77 @@ const state = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
gotoPage: 1, gotoPage: 1,
listConfig: {
url: "http://114.218.158.24:9020/api/fiee/reports/quarterly/display",
// url: "https://erpapi.fiee.com/api/fiee/reports/quarterly/display",
params: {
filtrate: {
fileName: "", //
},
},
},
list: [ list: [
{ // {
title: "2025 Q2 Quarterly Report", // title: "2025 Q2 Quarterly Report",
description: "Second Quarter 2025 Financial Results", // description: "Second Quarter 2025 Financial Results",
url: quarterlyPdf2025Q2, // url: quarterlyPdf2025Q2,
}, // },
{ // {
title: "2025 Q1 Quarterly Report Amendment No.1", // title: "2025 Q1 Quarterly Report Amendment No.1",
description: "First Quarter 2025 Financial Results", // description: "First Quarter 2025 Financial Results",
url: quarterlyPdf2025Q3N, // url: quarterlyPdf2025Q3N,
}, // },
], ],
}); });
watch(searchQuery, (newVal) => {
if (newVal === "" || newVal === null) {
state.listConfig.params.filtrate.fileName = newVal;
state.currentPage = 1;
getListData();
}
});
const showPageSizeMenu = ref(false); const showPageSizeMenu = ref(false);
onMounted(() => { onMounted(() => {
document.addEventListener("click", handleClickOutside); document.addEventListener("click", handleClickOutside);
getListData();
}); });
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener("click", handleClickOutside); document.removeEventListener("click", handleClickOutside);
}); });
const filteredList = computed(() => { const getListData = async () => {
if (!searchQuery.value) return state.list; console.log(state.listConfig);
const query = searchQuery.value.toLowerCase(); const res = await axios.post(state.listConfig.url, state.listConfig.params);
return state.list.filter( console.log(res);
(item) => if (res.data.code === 0) {
item.title.toLowerCase().includes(query) || let resData = res.data.data.Item || [];
item.description.toLowerCase().includes(query) resData.forEach((item) => {
); item.title = item.fileName;
}); item.description = item.fileIntroduce;
item.url = item.attachment;
item.attachmentName = item.attachmentName;
});
state.list = resData;
state.total = res.data.data.total || 0;
}
};
// const filteredList = computed(() => {
// if (!searchQuery.value) return state.list;
// const query = searchQuery.value.toLowerCase();
// return state.list.filter(
// (item) =>
// item.title.toLowerCase().includes(query) ||
// item.description.toLowerCase().includes(query)
// );
// });
// //
const pagedList = computed(() => { // const pagedList = computed(() => {
const start = (state.currentPage - 1) * state.pageSize; // const start = (state.currentPage - 1) * state.pageSize;
const end = start + state.pageSize; // const end = start + state.pageSize;
return filteredList.value.slice(start, end); // return filteredList.value.slice(start, end);
}); // });
// //
const totalPages = computed(() => { const totalPages = computed(() => {
@ -193,13 +241,19 @@ const displayRange = computed(() => {
const handleSearch = () => { const handleSearch = () => {
// //
// console.log(":", searchQuery.value); state.listConfig.params.filtrate.fileName = searchQuery.value;
state.currentPage = 1;
getListData();
}; };
const downloadPdf = async (pdfResource, filename = "") => { const downloadPdf = async (pdfResource, filename = "") => {
try { try {
const isDev = import.meta.env.DEV;
const requestUrl = isDev
? "/pdf-proxy/" + pdfResource.split("//")[1].split("/").slice(1).join("/")
: pdfResource;
// PDF // PDF
const response = await fetch(pdfResource); const response = await fetch(requestUrl);
const blob = await response.blob(); const blob = await response.blob();
// Blob URL // Blob URL
@ -218,7 +272,7 @@ const downloadPdf = async (pdfResource, filename = "") => {
// Blob URL // Blob URL
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
} catch (error) { } catch (error) {
// console.error("PDF:", error); console.error("下载PDF文件失败:", error);
} }
}; };
@ -308,14 +362,14 @@ watch(
} }
); );
watch( // watch(
() => filteredList.value, // () => filteredList.value,
(newList) => { // (newList) => {
state.total = newList.length; // state.total = newList.length;
state.currentPage = 1; // state.currentPage = 1;
}, // },
{ immediate: true } // { immediate: true }
); // );
// //
const handleClickOutside = (event) => { const handleClickOutside = (event) => {

View File

@ -26,7 +26,7 @@
<!-- 报告列表 --> <!-- 报告列表 -->
<div class="reports-table"> <div class="reports-table">
<div class="reports-list"> <div class="reports-list">
<div v-for="(item, index) in pagedList" :key="index" class="table-row"> <div v-for="(item, index) in state.list" :key="index" class="table-row">
<div class="content"> <div class="content">
<div class="file-content"> <div class="file-content">
<div class="file-info"> <div class="file-info">
@ -36,8 +36,11 @@
<p class="file-description">{{ item.description }}</p> <p class="file-description">{{ item.description }}</p>
</div> </div>
<div class="download-section"> <div class="download-section">
<p class="download-text" @click="downloadPdf(item.url)"> <p
PDF Download class="download-text"
@click="downloadPdf(item.url, item.attachmentName)"
>
{{ t("financialinformation.quarterlyreports.pdfDownload") }}
</p> </p>
</div> </div>
</div> </div>
@ -95,7 +98,11 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: state.pageSize,
})
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -114,12 +121,18 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="changePageSize(size)" @click="changePageSize(size)"
> >
{{ size }}/page {{
t("financialinformation.quarterlyreports.pagination.perPage", {
size: size,
})
}}
</div> </div>
</div> </div>
</div> </div>
<div class="goto-section"> <div class="goto-section">
<span>Goto</span> <span>{{
t("financialinformation.quarterlyreports.pagination.goto")
}}</span>
<input <input
type="number" type="number"
v-model="state.gotoPage" v-model="state.gotoPage"
@ -131,17 +144,22 @@
</div> </div>
</div> </div>
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.total }} results t("financialinformation.quarterlyreports.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.total,
})
}}
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue"; import { ref, watch, onMounted, onUnmounted, computed, reactive } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf"; // import quarterlyPdf2025Q2 from "@/assets/file/quarterly/10Q 2025-Q2.pdf";
import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf"; // import quarterlyPdf2025Q3N from "@/assets/file/quarterly/10Q 2025-Q1-No1.pdf";
import axios from "axios";
const { t } = useI18n(); const { t } = useI18n();
const searchQuery = ref(""); const searchQuery = ref("");
@ -150,45 +168,77 @@ const state = reactive({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
gotoPage: 1, gotoPage: 1,
listConfig: {
url: "http://114.218.158.24:9020/api/fiee/reports/quarterly/display",
// url: "https://erpapi.fiee.com/api/fiee/reports/quarterly/display",
params: {
filtrate: {
fileName: "", //
},
},
},
list: [ list: [
{ // {
title: "2025 Q2 Quarterly Report", // title: "2025 Q2 Quarterly Report",
description: "Second Quarter 2025 Financial Results", // description: "Second Quarter 2025 Financial Results",
url: quarterlyPdf2025Q2, // url: quarterlyPdf2025Q2,
}, // },
{ // {
title: "2025 Q1 Quarterly Report Amendment No.1", // title: "2025 Q1 Quarterly Report Amendment No.1",
description: "First Quarter 2025 Financial Results", // description: "First Quarter 2025 Financial Results",
url: quarterlyPdf2025Q3N, // url: quarterlyPdf2025Q3N,
}, // },
], ],
}); });
watch(searchQuery, (newVal) => {
if (newVal === "" || newVal === null) {
state.listConfig.params.filtrate.fileName = newVal;
state.currentPage = 1;
getListData();
}
});
const showPageSizeMenu = ref(false); const showPageSizeMenu = ref(false);
onMounted(() => { onMounted(() => {
document.addEventListener("click", handleClickOutside); document.addEventListener("click", handleClickOutside);
getListData();
}); });
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener("click", handleClickOutside); document.removeEventListener("click", handleClickOutside);
}); });
const filteredList = computed(() => { const getListData = async () => {
if (!searchQuery.value) return state.list; console.log(state.listConfig);
const query = searchQuery.value.toLowerCase(); const res = await axios.post(state.listConfig.url, state.listConfig.params);
return state.list.filter( console.log(res);
(item) => if (res.data.code === 0) {
item.title.toLowerCase().includes(query) || let resData = res.data.data.Item || [];
item.description.toLowerCase().includes(query) resData.forEach((item) => {
); item.title = item.fileName;
}); item.description = item.fileIntroduce;
item.url = item.attachment;
item.attachmentName = item.attachmentName;
});
state.list = resData;
state.total = res.data.data.total || 0;
}
};
// const filteredList = computed(() => {
// if (!searchQuery.value) return state.list;
// const query = searchQuery.value.toLowerCase();
// return state.list.filter(
// (item) =>
// item.title.toLowerCase().includes(query) ||
// item.description.toLowerCase().includes(query)
// );
// });
// //
const pagedList = computed(() => { // const pagedList = computed(() => {
const start = (state.currentPage - 1) * state.pageSize; // const start = (state.currentPage - 1) * state.pageSize;
const end = start + state.pageSize; // const end = start + state.pageSize;
return filteredList.value.slice(start, end); // return filteredList.value.slice(start, end);
}); // });
// //
const totalPages = computed(() => { const totalPages = computed(() => {
@ -205,13 +255,19 @@ const displayRange = computed(() => {
const handleSearch = () => { const handleSearch = () => {
// //
// console.log(":", searchQuery.value); state.listConfig.params.filtrate.fileName = searchQuery.value;
state.currentPage = 1;
getListData();
}; };
const downloadPdf = async (pdfResource, filename = "") => { const downloadPdf = async (pdfResource, filename = "") => {
try { try {
const isDev = import.meta.env.DEV;
const requestUrl = isDev
? "/pdf-proxy/" + pdfResource.split("//")[1].split("/").slice(1).join("/")
: pdfResource;
// PDF // PDF
const response = await fetch(pdfResource); const response = await fetch(requestUrl);
const blob = await response.blob(); const blob = await response.blob();
// Blob URL // Blob URL
@ -230,7 +286,7 @@ const downloadPdf = async (pdfResource, filename = "") => {
// Blob URL // Blob URL
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
} catch (error) { } catch (error) {
// console.error("PDF:", error); console.error("下载PDF文件失败:", error);
} }
}; };
@ -320,14 +376,14 @@ watch(
} }
); );
watch( // watch(
() => filteredList.value, // () => filteredList.value,
(newList) => { // (newList) => {
state.total = newList.length; // state.total = newList.length;
state.currentPage = 1; // state.currentPage = 1;
}, // },
{ immediate: true } // { immediate: true }
); // );
// //
const handleClickOutside = (event) => { const handleClickOutside = (event) => {

View File

@ -7,12 +7,12 @@
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="historic-title">Historical Data</div> <div class="historic-title">{{ t("historic_stock.title") }}</div>
</div> </div>
</div> </div>
<div class="filter-container"> <div class="filter-container">
<span class="range-label">Range</span> <span class="range-label">{{ t("historic_stock.range") }}</span>
<div <div
v-for="option in durationOptions" v-for="option in durationOptions"
:key="option.key" :key="option.key"
@ -20,13 +20,7 @@
:class="{ active: state.selectedDuration === option.key }" :class="{ active: state.selectedDuration === option.key }"
@click="handleDurationChange(option.key)" @click="handleDurationChange(option.key)"
> >
{{ {{ option.label }}
option.label
.replace(" Months", "m")
.replace(" Years", "Y")
.replace(" Year", "Y")
.replace(" to Date", "TD")
}}
</div> </div>
</div> </div>
<!-- reports-table from annualreports --> <!-- reports-table from annualreports -->
@ -85,8 +79,13 @@
<!-- pagination-container from annualreports --> <!-- pagination-container from annualreports -->
<div class="pagination-container"> <div class="pagination-container">
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.tableData.length }} results t("historic_stock.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.tableData.length,
})
}}
</div> </div>
<div class="pagination-controls"> <div class="pagination-controls">
<div class="pagination-buttons"> <div class="pagination-buttons">
@ -135,7 +134,9 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("historic_stock.pagination.perPage", { size: state.pageSize })
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -153,12 +154,12 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="handlePageSizeChange(size)" @click="handlePageSizeChange(size)"
> >
{{ size }}/page {{ t("historic_stock.pagination.perPage", { size: size }) }}
</div> </div>
</div> </div>
</div> </div>
<div class="goto-section"> <div class="goto-section">
<span>Goto</span> <span>{{ t("historic_stock.pagination.goto") }}</span>
<input <input
type="number" type="number"
v-model="state.gotoPage" v-model="state.gotoPage"
@ -187,24 +188,26 @@ import axios from "axios";
import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5"; import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5";
import defaultTableData from "../data"; import defaultTableData from "../data";
import customEcharts from "@/components/customEcharts/index.vue"; import customEcharts from "@/components/customEcharts/index.vue";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
// //
const periodOptions = [ const periodOptions = computed(() => [
{ label: "Daily", key: "Daily" }, { label: t("historic_stock.daily"), key: "Daily" },
{ label: "Weekly", key: "Weekly" }, { label: t("historic_stock.weekly"), key: "Weekly" },
{ label: "Monthly", key: "Monthly" }, { label: t("historic_stock.monthly"), key: "Monthly" },
{ label: "Quarterly", key: "Quarterly" }, { label: t("historic_stock.quarterly"), key: "Quarterly" },
{ label: "Annual", key: "Annual" }, { label: t("historic_stock.annual"), key: "Annual" },
]; ]);
const durationOptions = [ const durationOptions = computed(() => [
{ label: "3 Months", key: "3 Months" }, { label: t("historic_stock.duration.3_months"), key: "3 Months" },
{ label: "6 Months", key: "6 Months" }, { label: t("historic_stock.duration.6_months"), key: "6 Months" },
{ label: "YTD", key: "Year to Date" }, { label: t("historic_stock.duration.ytd"), key: "Year to Date" },
{ label: "1 Year", key: "1 Year" }, { label: t("historic_stock.duration.1_year"), key: "1 Year" },
{ label: "5 Years", key: "5 Years" }, { label: t("historic_stock.duration.5_years"), key: "5 Years" },
{ label: "10 Years", key: "10 Years" }, { label: t("historic_stock.duration.10_years"), key: "10 Years" },
]; ]);
// //
const pageSizeOptions = [ const pageSizeOptions = [
@ -239,51 +242,51 @@ const paginatedData = computed(() => {
}); });
// //
const columns = [ const columns = computed(() => [
{ {
title: "Date", title: t("historic_stock.date"),
key: "date", key: "date",
align: "left", align: "left",
fixed: "left", fixed: "left",
width: 150, width: 150,
}, },
{ {
title: "Open", title: t("historic_stock.open"),
key: "open", key: "open",
align: "center", align: "center",
}, },
{ {
title: "High", title: t("historic_stock.high"),
key: "high", key: "high",
align: "center", align: "center",
}, },
{ {
title: "Low", title: t("historic_stock.low"),
key: "low", key: "low",
align: "center", align: "center",
}, },
{ {
title: "Close", title: t("historic_stock.close"),
key: "close", key: "close",
align: "center", align: "center",
}, },
{ {
title: "Adj. Close", title: t("historic_stock.adj_close"),
key: "adjClose", key: "adjClose",
align: "center", align: "center",
width: 115, width: 115,
}, },
{ {
title: "Change", title: t("historic_stock.change"),
key: "change", key: "change",
align: "center", align: "center",
}, },
{ {
title: "Volume", title: t("historic_stock.volume"),
key: "volume", key: "volume",
align: "center", align: "center",
}, },
]; ]);
// //
const handlePeriodChange = (key) => { const handlePeriodChange = (key) => {
@ -585,6 +588,8 @@ const getPageData = async () => {
padding: 0 16px; padding: 0 16px;
margin-bottom: 32px; margin-bottom: 32px;
margin-top: 32px; margin-top: 32px;
flex-wrap: wrap;
gap: 10px;
.range-label { .range-label {
font-family: "PingFang SC", sans-serif; font-family: "PingFang SC", sans-serif;

View File

@ -7,12 +7,12 @@
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="historic-title">Historical Data</div> <div class="historic-title">{{ t("historic_stock.title") }}</div>
</div> </div>
</div> </div>
<div class="filter-container"> <div class="filter-container">
<span class="range-label">Range</span> <span class="range-label">{{ t("historic_stock.range") }}</span>
<div <div
v-for="option in durationOptions" v-for="option in durationOptions"
:key="option.key" :key="option.key"
@ -20,13 +20,7 @@
:class="{ active: state.selectedDuration === option.key }" :class="{ active: state.selectedDuration === option.key }"
@click="handleDurationChange(option.key)" @click="handleDurationChange(option.key)"
> >
{{ {{ option.label }}
option.label
.replace(" Months", "m")
.replace(" Years", "Y")
.replace(" Year", "Y")
.replace(" to Date", "TD")
}}
</div> </div>
</div> </div>
<!-- reports-table from annualreports --> <!-- reports-table from annualreports -->
@ -85,8 +79,13 @@
<!-- pagination-container from annualreports --> <!-- pagination-container from annualreports -->
<div class="pagination-container"> <div class="pagination-container">
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.tableData.length }} results t("historic_stock.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.tableData.length,
})
}}
</div> </div>
<div class="pagination-controls"> <div class="pagination-controls">
<div class="pagination-buttons"> <div class="pagination-buttons">
@ -135,7 +134,9 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("historic_stock.pagination.perPage", { size: state.pageSize })
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -153,12 +154,12 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="handlePageSizeChange(size)" @click="handlePageSizeChange(size)"
> >
{{ size }}/page {{ t("historic_stock.pagination.perPage", { size: size }) }}
</div> </div>
</div> </div>
</div> </div>
<div class="goto-section"> <div class="goto-section">
<span>Goto</span> <span>{{ t("historic_stock.pagination.goto") }}</span>
<input <input
type="number" type="number"
v-model="state.gotoPage" v-model="state.gotoPage"
@ -187,24 +188,26 @@ import axios from "axios";
import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5"; import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5";
import defaultTableData from "../data"; import defaultTableData from "../data";
import customEcharts from "@/components/customEcharts/index.vue"; import customEcharts from "@/components/customEcharts/index.vue";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
// //
const periodOptions = [ const periodOptions = computed(() => [
{ label: "Daily", key: "Daily" }, { label: t("historic_stock.daily"), key: "Daily" },
{ label: "Weekly", key: "Weekly" }, { label: t("historic_stock.weekly"), key: "Weekly" },
{ label: "Monthly", key: "Monthly" }, { label: t("historic_stock.monthly"), key: "Monthly" },
{ label: "Quarterly", key: "Quarterly" }, { label: t("historic_stock.quarterly"), key: "Quarterly" },
{ label: "Annual", key: "Annual" }, { label: t("historic_stock.annual"), key: "Annual" },
]; ]);
const durationOptions = [ const durationOptions = computed(() => [
{ label: "3 Months", key: "3 Months" }, { label: t("historic_stock.duration.3_months"), key: "3 Months" },
{ label: "6 Months", key: "6 Months" }, { label: t("historic_stock.duration.6_months"), key: "6 Months" },
{ label: "YTD", key: "Year to Date" }, { label: t("historic_stock.duration.ytd"), key: "Year to Date" },
{ label: "1 Year", key: "1 Year" }, { label: t("historic_stock.duration.1_year"), key: "1 Year" },
{ label: "5 Years", key: "5 Years" }, { label: t("historic_stock.duration.5_years"), key: "5 Years" },
{ label: "10 Years", key: "10 Years" }, { label: t("historic_stock.duration.10_years"), key: "10 Years" },
]; ]);
// //
const pageSizeOptions = [ const pageSizeOptions = [
@ -239,51 +242,51 @@ const paginatedData = computed(() => {
}); });
// //
const columns = [ const columns = computed(() => [
{ {
title: "Date", title: t("historic_stock.date"),
key: "date", key: "date",
align: "left", align: "left",
fixed: "left", fixed: "left",
width: 150, width: 150,
}, },
{ {
title: "Open", title: t("historic_stock.open"),
key: "open", key: "open",
align: "center", align: "center",
}, },
{ {
title: "High", title: t("historic_stock.high"),
key: "high", key: "high",
align: "center", align: "center",
}, },
{ {
title: "Low", title: t("historic_stock.low"),
key: "low", key: "low",
align: "center", align: "center",
}, },
{ {
title: "Close", title: t("historic_stock.close"),
key: "close", key: "close",
align: "center", align: "center",
}, },
{ {
title: "Adj. Close", title: t("historic_stock.adj_close"),
key: "adjClose", key: "adjClose",
align: "center", align: "center",
width: 115, width: 115,
}, },
{ {
title: "Change", title: t("historic_stock.change"),
key: "change", key: "change",
align: "center", align: "center",
}, },
{ {
title: "Volume", title: t("historic_stock.volume"),
key: "volume", key: "volume",
align: "center", align: "center",
}, },
]; ]);
// //
const handlePeriodChange = (key) => { const handlePeriodChange = (key) => {
@ -584,6 +587,8 @@ const getPageData = async () => {
padding: 0 16px; padding: 0 16px;
margin-bottom: 32px; margin-bottom: 32px;
margin-top: 32px; margin-top: 32px;
flex-wrap: wrap;
gap: 10px;
.range-label { .range-label {
font-family: "PingFang SC", sans-serif; font-family: "PingFang SC", sans-serif;

View File

@ -7,12 +7,12 @@
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title-text">Historical Data</div> <div class="title-text">{{ t("historic_stock.title") }}</div>
</div> </div>
</div> </div>
<div class="filter-container"> <div class="filter-container">
<span class="range-label">Range</span> <span class="range-label">{{ t("historic_stock.range") }}</span>
<div class="filter-row"> <div class="filter-row">
<div <div
v-for="option in durationOptions" v-for="option in durationOptions"
@ -21,13 +21,7 @@
:class="{ active: state.selectedDuration === option.key }" :class="{ active: state.selectedDuration === option.key }"
@click="handleDurationChange(option.key)" @click="handleDurationChange(option.key)"
> >
{{ {{ option.label }}
option.label
.replace(" Months", "m")
.replace(" Years", "Y")
.replace(" Year", "Y")
.replace(" to Date", "TD")
}}
</div> </div>
</div> </div>
</div> </div>
@ -135,7 +129,9 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("historic_stock.pagination.perPage", { size: state.pageSize })
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -153,15 +149,20 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="handlePageSizeChange(size)" @click="handlePageSizeChange(size)"
> >
{{ size }}/page {{ t("historic_stock.pagination.perPage", { size: size }) }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.tableData.length }} results t("historic_stock.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.tableData.length,
})
}}
</div> </div>
<!-- <div class="back-to-top-link"> <!-- <div class="back-to-top-link">
@ -180,24 +181,26 @@ import axios from "axios";
import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5"; import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5";
import defaultTableData from "../data"; import defaultTableData from "../data";
import customEcharts from "@/components/customEcharts/index.vue"; import customEcharts from "@/components/customEcharts/index.vue";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
// //
const periodOptions = [ const periodOptions = computed(() => [
{ label: "Daily", key: "Daily" }, { label: t("historic_stock.daily"), key: "Daily" },
{ label: "Weekly", key: "Weekly" }, { label: t("historic_stock.weekly"), key: "Weekly" },
{ label: "Monthly", key: "Monthly" }, { label: t("historic_stock.monthly"), key: "Monthly" },
{ label: "Quarterly", key: "Quarterly" }, { label: t("historic_stock.quarterly"), key: "Quarterly" },
{ label: "Annual", key: "Annual" }, { label: t("historic_stock.annual"), key: "Annual" },
]; ]);
const durationOptions = [ const durationOptions = computed(() => [
{ label: "3 Months", key: "3 Months" }, { label: t("historic_stock.duration.3_months"), key: "3 Months" },
{ label: "6 Months", key: "6 Months" }, { label: t("historic_stock.duration.6_months"), key: "6 Months" },
{ label: "YTD", key: "Year to Date" }, { label: t("historic_stock.duration.ytd"), key: "Year to Date" },
{ label: "1 Year", key: "1 Year" }, { label: t("historic_stock.duration.1_year"), key: "1 Year" },
{ label: "5 Years", key: "5 Years" }, { label: t("historic_stock.duration.5_years"), key: "5 Years" },
{ label: "10 Years", key: "10 Years" }, { label: t("historic_stock.duration.10_years"), key: "10 Years" },
]; ]);
// //
const pageSizeOptions = [ const pageSizeOptions = [
@ -232,57 +235,57 @@ const paginatedData = computed(() => {
}); });
// //
const columns = [ const columns = computed(() => [
{ {
title: "Date", title: t("historic_stock.date"),
key: "date", key: "date",
align: "left", align: "left",
fixed: "left", fixed: "left",
width: 152, width: 152,
}, },
{ {
title: "Open", title: t("historic_stock.open"),
key: "open", key: "open",
align: "center", align: "center",
width: 116, width: 116,
}, },
{ {
title: "High", title: t("historic_stock.high"),
key: "high", key: "high",
align: "center", align: "center",
width: 116, width: 116,
}, },
{ {
title: "Low", title: t("historic_stock.low"),
key: "low", key: "low",
align: "center", align: "center",
width: 116, width: 116,
}, },
{ {
title: "Close", title: t("historic_stock.close"),
key: "close", key: "close",
align: "center", align: "center",
width: 116, width: 116,
}, },
{ {
title: "Adj. Close", title: t("historic_stock.adj_close"),
key: "adjClose", key: "adjClose",
align: "center", align: "center",
width: 116, width: 116,
}, },
{ {
title: "Change", title: t("historic_stock.change"),
key: "change", key: "change",
align: "center", align: "center",
width: 116, width: 116,
}, },
{ {
title: "Volume", title: t("historic_stock.volume"),
key: "volume", key: "volume",
align: "center", align: "center",
width: 116, width: 116,
}, },
]; ]);
// //
const handlePeriodChange = (key) => { const handlePeriodChange = (key) => {
@ -557,6 +560,8 @@ const getPageData = async () => {
gap: 8 * 5.12px; gap: 8 * 5.12px;
padding: 0 16 * 5.12px; padding: 0 16 * 5.12px;
margin-bottom: 32 * 5.12px; margin-bottom: 32 * 5.12px;
flex-wrap: wrap;
gap: 10 * 5.12px;
.range-label { .range-label {
font-family: "PingFang SC", sans-serif; font-family: "PingFang SC", sans-serif;

View File

@ -7,12 +7,12 @@
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title-text">Historical Data</div> <div class="title-text">{{ t("historic_stock.title") }}</div>
</div> </div>
</div> </div>
<div class="filter-container"> <div class="filter-container">
<span class="range-label">Range</span> <span class="range-label">{{ t("historic_stock.range") }}</span>
<div <div
v-for="option in durationOptions" v-for="option in durationOptions"
:key="option.key" :key="option.key"
@ -20,13 +20,7 @@
:class="{ active: state.selectedDuration === option.key }" :class="{ active: state.selectedDuration === option.key }"
@click="handleDurationChange(option.key)" @click="handleDurationChange(option.key)"
> >
{{ {{ option.label }}
option.label
.replace(" Months", "m")
.replace(" Years", "Y")
.replace(" Year", "Y")
.replace(" to Date", "TD")
}}
</div> </div>
</div> </div>
<!-- reports-table from annualreports --> <!-- reports-table from annualreports -->
@ -133,7 +127,9 @@
</button> </button>
</div> </div>
<div class="page-size-selector" @click="togglePageSizeMenu"> <div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span> <span>{{
t("historic_stock.pagination.perPage", { size: state.pageSize })
}}</span>
<svg width="10" height="5" viewBox="0 0 10 5" fill="none"> <svg width="10" height="5" viewBox="0 0 10 5" fill="none">
<path <path
d="M1 1L5 4L9 1" d="M1 1L5 4L9 1"
@ -151,12 +147,12 @@
:class="{ active: state.pageSize === size }" :class="{ active: state.pageSize === size }"
@click="handlePageSizeChange(size)" @click="handlePageSizeChange(size)"
> >
{{ size }}/page {{ t("historic_stock.pagination.perPage", { size: size }) }}
</div> </div>
</div> </div>
</div> </div>
<div class="goto-section"> <div class="goto-section">
<span>Goto</span> <span>{{ t("historic_stock.pagination.goto") }}</span>
<input <input
type="number" type="number"
v-model="state.gotoPage" v-model="state.gotoPage"
@ -169,8 +165,13 @@
</div> </div>
</div> </div>
<div class="pagination-info"> <div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of {{
{{ state.tableData.length }} results t("historic_stock.pagination.displaying", {
start: displayRange.start,
end: displayRange.end,
total: state.tableData.length,
})
}}
</div> </div>
<!-- <div class="back-to-top-link"> <!-- <div class="back-to-top-link">
@ -189,24 +190,26 @@ import axios from "axios";
import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5"; import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5";
import defaultTableData from "../data"; import defaultTableData from "../data";
import customEcharts from "@/components/customEcharts/index.vue"; import customEcharts from "@/components/customEcharts/index.vue";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
// //
const periodOptions = [ const periodOptions = computed(() => [
{ label: "Daily", key: "Daily" }, { label: t("historic_stock.daily"), key: "Daily" },
{ label: "Weekly", key: "Weekly" }, { label: t("historic_stock.weekly"), key: "Weekly" },
{ label: "Monthly", key: "Monthly" }, { label: t("historic_stock.monthly"), key: "Monthly" },
{ label: "Quarterly", key: "Quarterly" }, { label: t("historic_stock.quarterly"), key: "Quarterly" },
{ label: "Annual", key: "Annual" }, { label: t("historic_stock.annual"), key: "Annual" },
]; ]);
const durationOptions = [ const durationOptions = computed(() => [
{ label: "3 Months", key: "3 Months" }, { label: t("historic_stock.duration.3_months"), key: "3 Months" },
{ label: "6 Months", key: "6 Months" }, { label: t("historic_stock.duration.6_months"), key: "6 Months" },
{ label: "YTD", key: "Year to Date" }, { label: t("historic_stock.duration.ytd"), key: "Year to Date" },
{ label: "1 Year", key: "1 Year" }, { label: t("historic_stock.duration.1_year"), key: "1 Year" },
{ label: "5 Years", key: "5 Years" }, { label: t("historic_stock.duration.5_years"), key: "5 Years" },
{ label: "10 Years", key: "10 Years" }, { label: t("historic_stock.duration.10_years"), key: "10 Years" },
]; ]);
// //
const pageSizeOptions = [ const pageSizeOptions = [
@ -241,51 +244,51 @@ const paginatedData = computed(() => {
}); });
// //
const columns = [ const columns = computed(() => [
{ {
title: "Date", title: t("historic_stock.date"),
key: "date", key: "date",
align: "left", align: "left",
fixed: "left", fixed: "left",
width: 150, width: 150,
}, },
{ {
title: "Open", title: t("historic_stock.open"),
key: "open", key: "open",
align: "center", align: "center",
}, },
{ {
title: "High", title: t("historic_stock.high"),
key: "high", key: "high",
align: "center", align: "center",
}, },
{ {
title: "Low", title: t("historic_stock.low"),
key: "low", key: "low",
align: "center", align: "center",
}, },
{ {
title: "Close", title: t("historic_stock.close"),
key: "close", key: "close",
align: "center", align: "center",
}, },
{ {
title: "Adj. Close", title: t("historic_stock.adj_close"),
key: "adjClose", key: "adjClose",
align: "center", align: "center",
width: 115, width: 115,
}, },
{ {
title: "Change", title: t("historic_stock.change"),
key: "change", key: "change",
align: "center", align: "center",
}, },
{ {
title: "Volume", title: t("historic_stock.volume"),
key: "volume", key: "volume",
align: "center", align: "center",
}, },
]; ]);
// //
const handlePeriodChange = (key) => { const handlePeriodChange = (key) => {
@ -586,6 +589,8 @@ const getPageData = async () => {
padding: 0 16 * 2.5px; padding: 0 16 * 2.5px;
margin-bottom: 32 * 2.5px; margin-bottom: 32 * 2.5px;
margin-top: 32 * 2.5px; margin-top: 32 * 2.5px;
flex-wrap: wrap;
gap: 10 * 2.5px;
.range-label { .range-label {
font-family: "PingFang SC", sans-serif; font-family: "PingFang SC", sans-serif;

View File

@ -8,9 +8,9 @@
</div> </div>
</div> </div>
<div class="search-container"> <div class="search-container">
<n-select <custom-select
:options="state.selectOptions" :options="state.selectOptions"
v-model:value="state.selectedValue" v-model="state.selectedValue"
class="search-select" class="search-select"
/> />
<input <input
@ -191,6 +191,7 @@
<script setup> <script setup>
import customDefaultPage from "@/components/customDefaultPage/index.vue"; import customDefaultPage from "@/components/customDefaultPage/index.vue";
import customSelect from "@/components/customSelect/index.vue";
import { import {
reactive, reactive,
onMounted, onMounted,
@ -200,7 +201,7 @@ import {
computed, computed,
onUnmounted, onUnmounted,
} from "vue"; } from "vue";
import { NSelect, NInput, NButton, NTooltip } from "naive-ui"; import { NTooltip } from "naive-ui";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
@ -480,7 +481,7 @@ const handleClickOutside = (event) => {
.search-input { .search-input {
flex: 1; flex: 1;
height: 46px; height: 46px;
padding: 7px; padding: 7px 12px;
border: 1px solid #e0e0e6; border: 1px solid #e0e0e6;
border-radius: 3px; border-radius: 3px;
font-family: "PingFang SC", sans-serif; font-family: "PingFang SC", sans-serif;
@ -489,32 +490,13 @@ const handleClickOutside = (event) => {
line-height: 1.375em; line-height: 1.375em;
letter-spacing: 0.03em; letter-spacing: 0.03em;
color: #455363; color: #455363;
box-sizing: border-box;
&::placeholder { &::placeholder {
color: #b6b6b6; color: #b6b6b6;
} }
} }
:deep(.n-input) {
.n-input__input {
padding: 4px 0;
// border: 1px solid #ccc;
border-radius: 4px;
}
}
:deep(.n-select) {
.n-select__input {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
}
}
:deep(.n-button) {
padding: 20px 16px;
border-radius: 4px;
}
.search-button { .search-button {
height: 46px; height: 46px;
padding: 7px 12px; padding: 7px 12px;
@ -529,6 +511,7 @@ const handleClickOutside = (event) => {
font-size: 16px; font-size: 16px;
line-height: 1.375em; line-height: 1.375em;
letter-spacing: 0.03em; letter-spacing: 0.03em;
box-sizing: border-box;
&:hover { &:hover {
background: #ff7bac; background: #ff7bac;
color: #fff; color: #fff;

View File

@ -8,9 +8,9 @@
</div> </div>
</div> </div>
<div class="search-container"> <div class="search-container">
<n-select <custom-select
:options="state.selectOptions" :options="state.selectOptions"
v-model:value="state.selectedValue" v-model="state.selectedValue"
class="search-select" class="search-select"
/> />
<input <input
@ -191,6 +191,7 @@
<script setup> <script setup>
import customDefaultPage from "@/components/customDefaultPage/index.vue"; import customDefaultPage from "@/components/customDefaultPage/index.vue";
import customSelect from "@/components/customSelect/index.vue";
import { import {
reactive, reactive,
onMounted, onMounted,
@ -200,7 +201,7 @@ import {
computed, computed,
onUnmounted, onUnmounted,
} from "vue"; } from "vue";
import { NSelect, NInput, NButton, NTooltip } from "naive-ui"; import { NTooltip } from "naive-ui";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
@ -470,7 +471,6 @@ const handleClickOutside = (event) => {
.search-select { .search-select {
width: 201px; width: 201px;
height: 34px;
} }
.search-input { .search-input {
@ -485,32 +485,13 @@ const handleClickOutside = (event) => {
line-height: 1.375em; line-height: 1.375em;
letter-spacing: 3%; letter-spacing: 3%;
color: #455363; color: #455363;
box-sizing: border-box;
&::placeholder { &::placeholder {
color: #b6b6b6; color: #b6b6b6;
} }
} }
:deep(.n-input) {
.n-input__input {
padding: 4px 0;
// border: 1px solid #ccc;
border-radius: 4px;
}
}
:deep(.n-select) {
.n-select__input {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
}
}
:deep(.n-button) {
padding: 20px 16px;
border-radius: 4px;
}
.search-button { .search-button {
height: 34px; height: 34px;
padding: 7px 12px; padding: 7px 12px;
@ -525,6 +506,7 @@ const handleClickOutside = (event) => {
font-size: 16px; font-size: 16px;
line-height: 1.375em; line-height: 1.375em;
letter-spacing: 3%; letter-spacing: 3%;
box-sizing: border-box;
&:hover { &:hover {
background: #ff7bac; background: #ff7bac;
color: #fff; color: #fff;

View File

@ -8,9 +8,9 @@
</div> </div>
</div> </div>
<div class="search-container"> <div class="search-container">
<n-select <custom-select
:options="state.selectOptions" :options="state.selectOptions"
v-model:value="state.selectedValue" v-model="state.selectedValue"
class="search-select" class="search-select"
/> />
<input <input
@ -191,6 +191,7 @@
<script setup> <script setup>
import customDefaultPage from "@/components/customDefaultPage/index.vue"; import customDefaultPage from "@/components/customDefaultPage/index.vue";
import customSelect from "@/components/customSelect/index.vue";
import { import {
reactive, reactive,
onMounted, onMounted,
@ -200,7 +201,7 @@ import {
computed, computed,
onUnmounted, onUnmounted,
} from "vue"; } from "vue";
import { NSelect, NInput, NButton, NTooltip } from "naive-ui"; import { NTooltip } from "naive-ui";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import axios from "axios"; import axios from "axios";
@ -470,7 +471,6 @@ const handleClickOutside = (event) => {
.search-select { .search-select {
width: 134 * 2.5px; width: 134 * 2.5px;
height: 34 * 2.5px;
} }
.search-input { .search-input {
@ -485,32 +485,13 @@ const handleClickOutside = (event) => {
line-height: 1.375em; line-height: 1.375em;
letter-spacing: 0.48 * 2.5px; letter-spacing: 0.48 * 2.5px;
color: #455363; color: #455363;
box-sizing: border-box;
&::placeholder { &::placeholder {
color: #b6b6b6; color: #b6b6b6;
} }
} }
:deep(.n-input) {
.n-input__input {
padding: 4 * 2.5px 0;
// border: 1*2.5px solid #ccc;
border-radius: 4 * 2.5px;
}
}
:deep(.n-select) {
.n-select__input {
padding: 8 * 2.5px 12 * 2.5px;
border: 1 * 2.5px solid #ccc;
border-radius: 4 * 2.5px;
}
}
:deep(.n-button) {
padding: 20 * 2.5px 16 * 2.5px;
border-radius: 4 * 2.5px;
}
.search-button { .search-button {
height: 34 * 2.5px; height: 34 * 2.5px;
padding: 7 * 2.5px 12 * 2.5px; padding: 7 * 2.5px 12 * 2.5px;
@ -525,6 +506,7 @@ const handleClickOutside = (event) => {
font-size: 14 * 2.5px; font-size: 14 * 2.5px;
line-height: 1.375em; line-height: 1.375em;
letter-spacing: 0.48 * 2.5px; letter-spacing: 0.48 * 2.5px;
box-sizing: border-box;
&:hover { &:hover {
background: #ff7bac; background: #ff7bac;
color: #fff; color: #fff;

View File

@ -1,34 +1,33 @@
<script setup></script> <script setup>
import { useI18n } from "vue-i18n";
const { t } = useI18n();
</script>
<template> <template>
<div class="page-container"> <div class="page-container">
<div class="grid-lines px-fixed"> <div class="grid-lines">
<div class="line solid line-1"></div> <div class="line solid line-1"></div>
<div class="line dashed line-2"></div> <div class="line dashed line-2"></div>
<div class="line dashed line-3"></div> <div class="line dashed line-3"></div>
<div class="line dashed line-4"></div> <div class="line dashed line-4"></div>
<div class="line solid line-5"></div> <div class="line solid line-5"></div>
</div> </div>
<section class="hero-section px-fixed relative"> <section class="hero-section relative">
<div class="hero-content"> <div class="hero-content">
<div class="hero-title"> <div class="hero-title">
More than just a tool<br /> {{ t("product_intro.hero_line1") }}<br />
Comprehensive growth solutions, <br /> {{ t("product_intro.hero_line2") }}<br />
providing a one-stop solution for content creation,<br /> {{ t("product_intro.hero_line3") }}<br />
publishing, analysis, and monetization {{ t("product_intro.hero_line4") }}
</div> </div>
</div> </div>
<div class="core-value-card px-fixed"> <div class="core-value-card">
<div class="card-content"> <div class="card-content">
<div class="card-title">Core Value</div> <div class="card-title">
{{ t("product_intro.core_value_title") }}
</div>
<div class="card-text"> <div class="card-text">
The FIEE-SAAS platform is a one-stop content operation solution {{ t("product_intro.core_value_text") }}
tailored for creators in the digital era. The platform utilizes
intelligent distribution technology, A1 empowerment tools, and
full-chain services,Assist you in efficiently reaching audiences on
global mainstream platforms such as TikTok, YouTube, and Instagram,
creating a KOL brand effect, unlocking content value, and achieving
sustainable growth.
</div> </div>
</div> </div>
</div> </div>
@ -39,71 +38,66 @@
/> />
</section> </section>
<section class="features-section px-fixed"> <section class="features-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Product Features</div> <div class="section-title">{{ t("product_intro.features_title") }}</div>
</div> </div>
<div class="features-list"> <div class="features-list">
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
One-click Synchronous Publishing {{ t("product_intro.feature_sync") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Synchronize graphic and video content to TikTok, YouTube, and {{ t("product_intro.feature_sync_desc") }}
Instagram platforms at once, saving time on repetitive operations.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Intelligent Scheduled Publishing {{ t("product_intro.feature_schedule") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Plan the content release time in advance, support batch scheduling, {{ t("product_intro.feature_schedule_desc") }}
and accurately grasp the optimal release time of each platform.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Unified Management of Multiple Accounts {{ t("product_intro.feature_accounts") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Easily manage multiple accounts on one platform without the need for {{ t("product_intro.feature_accounts_desc") }}
repeated login and switching, improving team collaboration
efficiency.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Cloud Content Library {{ t("product_intro.feature_library") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Safely store and manage all creative materials, access and use them {{ t("product_intro.feature_library_desc") }}
anytime, anywhere, and support quick retrieval and reuse.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Basic Data Tracking {{ t("product_intro.feature_tracking") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Visually view the content performance of various platforms, {{ t("product_intro.feature_tracking_desc") }}
understand core data indicators, and provide a basis for optimizing
strategies.
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section class="solutions-section px-fixed"> <section class="solutions-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Value Added Solutions</div> <div class="section-title">
{{ t("product_intro.solutions_title") }}
</div>
</div> </div>
<div class="solutions-content"> <div class="solutions-content">
<div class="solutions-list"> <div class="solutions-list">
@ -114,13 +108,11 @@
class="solution-icon" class="solution-icon"
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
KOL Brand Promotion Services {{ t("product_intro.sol_kol") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Efficiently connect high-quality business cooperation {{ t("product_intro.sol_kol_desc") }}
opportunities and complete the entire process management from
order acceptance to publication within the platform.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -130,13 +122,11 @@
class="solution-icon" class="solution-icon"
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Professional Content Creation Support {{ t("product_intro.sol_content") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Connect professional shooting and post production teams for you, {{ t("product_intro.sol_content_desc") }}
create high-quality "art+story" content, and strengthen IP
influence.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -146,13 +136,11 @@
class="solution-icon" class="solution-icon"
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Account Operation and Hosting Services {{ t("product_intro.sol_ops") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
From 0 to 1 account positioning, follower growth strategy to {{ t("product_intro.sol_ops_desc") }}
monetization cycle, operation experts provide full cycle running
and hosting services.
</div> </div>
</div> </div>
</div> </div>
@ -161,69 +149,66 @@
src="@/assets/image/1440/product-introduction-img1.png" src="@/assets/image/1440/product-introduction-img1.png"
alt="Value Added Solutions" alt="Value Added Solutions"
class="solution-image" class="solution-image"
style="width: 434px"
/> />
</div> </div>
</div> </div>
</section> </section>
<section class="advantages-section px-fixed"> <section class="advantages-section">
<div class="advantages-content px-fixed"> <div class="advantages-content">
<div class="advantages-header"> <div class="advantages-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title text-white">Our Advantages</div> <div class="section-title text-white">
{{ t("product_intro.advantages_title") }}
</div>
</div> </div>
<div style="width: 50%"> <div style="width: 50%">
<div class="advantages-list"> <div class="advantages-list">
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Time Saving {{ t("product_intro.adv_time") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Multi platform publishing efficiency improvement, allowing you {{ t("product_intro.adv_time_desc") }}
to focus on content creation.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Safe and Reliable {{ t("product_intro.adv_safe") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Enterprise level data encryption and permission control ensure {{ t("product_intro.adv_safe_desc") }}
account and content security.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Maintain Consistency {{ t("product_intro.adv_consistent") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Ensure that brand information is presented uniformly on all {{ t("product_intro.adv_consistent_desc") }}
platforms.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Data Driven {{ t("product_intro.adv_data") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Optimizing Content Strategies Based on Actual Performance. {{ t("product_intro.adv_data_desc") }}
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line px-fixed"></div> <div class="vertical-line"></div>
Easy to Use {{ t("product_intro.adv_easy") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Intuitive interface design, no need for professional technical {{ t("product_intro.adv_easy_desc") }}
background.
</div> </div>
</div> </div>
</div> </div>
@ -231,7 +216,7 @@
</div> </div>
</section> </section>
<section class="cta-section px-fixed"> <section class="cta-section">
<img <img
src="@/assets/image/1440/product-introduction-img5.png" src="@/assets/image/1440/product-introduction-img5.png"
alt="background" alt="background"
@ -252,8 +237,8 @@
/> />
</svg> </svg>
<div class="cta-title"> <div class="cta-title">
Get customized <br /> {{ t("product_intro.cta_title_line1") }}<br />
solutions for free {{ t("product_intro.cta_title_line2") }}
</div> </div>
</div> </div>
<div class="cta-qr-code"> <div class="cta-qr-code">
@ -267,7 +252,7 @@
</div> </div>
</template> </template>
<style scoped> <style scoped lang="scss">
.page-container { .page-container {
background-color: #fff; background-color: #fff;
font-family: "PingFang SC", sans-serif; font-family: "PingFang SC", sans-serif;
@ -275,7 +260,7 @@
position: relative; position: relative;
} }
.hero-section.px-fixed { .hero-section {
text-align: center; text-align: center;
position: relative; position: relative;
background-image: url("@/assets/image/1440/product-introduction-img3.png"); background-image: url("@/assets/image/1440/product-introduction-img3.png");
@ -290,28 +275,28 @@
} }
.hero-title { .hero-title {
font-size: 40px; font-size: 40 * 1.33px;
font-weight: 500; font-weight: 500;
line-height: 56px; line-height: 56 * 1.33px;
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
padding: 153px 0; padding: 153 * 1.33px 0;
color: #000; color: #000;
z-index: 2; z-index: 2;
} }
.hero-bg-img { .hero-bg-img {
position: absolute; position: absolute;
bottom: -204px; bottom: -204 * 1.33px;
left: 0; left: 0;
width: 100%; width: 100%;
z-index: 1; z-index: 1;
} }
.core-value-card.px-fixed { .core-value-card {
width: 932px; width: 932 * 1.33px;
padding: 40px 32px; padding: 40 * 1.33px 32 * 1.33px;
margin: 0 auto; margin: 0 auto;
background-color: #fff; background-color: #fff;
border-radius: 16px; border-radius: 16 * 1.33px;
box-shadow: 0px 3px 14px 0px rgba(0, 0, 0, 0.16); box-shadow: 0 * 1.33px 3 * 1.33px 14 * 1.33px 0 * 1.33px rgba(0, 0, 0, 0.16);
text-align: left; text-align: left;
z-index: 2; z-index: 2;
position: relative; position: relative;
@ -320,134 +305,134 @@
.card-content { .card-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 32px; gap: 32 * 1.33px;
} }
.card-title { .card-title {
font-size: 40px; font-size: 40 * 1.33px;
font-weight: 500; font-weight: 500;
line-height: 56px; line-height: 56 * 1.33px;
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
} }
.card-text { .card-text {
font-size: 16px; font-size: 16 * 1.33px;
line-height: 22px; line-height: 22 * 1.33px;
color: #455363; color: #455363;
letter-spacing: 0.48px; letter-spacing: 0.48 * 1.33px;
} }
.section-header { .section-header {
margin-bottom: 32px; margin-bottom: 32 * 1.33px;
padding: 0 16px; padding: 0 16 * 1.33px;
} }
.decorator-bar { .decorator-bar {
width: 58px; width: 58 * 1.33px;
height: 7px; height: 7 * 1.33px;
background-color: #ff7bac; background-color: #ff7bac;
margin-bottom: 16px; margin-bottom: 16 * 1.33px;
} }
.section-title { .section-title {
font-size: 40px; font-size: 40 * 1.33px;
font-weight: 500; font-weight: 500;
line-height: 56px; line-height: 56 * 1.33px;
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
color: #000; color: #000;
} }
.features-section.px-fixed { .features-section {
padding-top: 200px; padding-top: 102 * 1.33px;
width: 932px; width: 932 * 1.33px;
margin: 0 auto; margin: 0 auto;
} }
.features-list { .features-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 32px; gap: 24 * 1.33px;
} }
.feature-item { .feature-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16 * 1.33px;
} }
.feature-title { .feature-title {
font-size: 24px; font-size: 24 * 1.33px;
font-weight: 500; font-weight: 500;
line-height: 32px; line-height: 32 * 1.33px;
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 16px; gap: 16 * 1.33px;
} }
.feature-description { .feature-description {
font-size: 16px; font-size: 16 * 1.33px;
line-height: 22px; line-height: 22 * 1.33px;
color: #455363; color: #455363;
letter-spacing: 0.48px; letter-spacing: 0.48 * 1.33px;
padding: 0 16px; padding: 0 16 * 1.33px;
text-align: justify; text-align: justify;
font-feature-settings: "liga" off, "clig" off; font-feature-settings: "liga" off, "clig" off;
} }
.solutions-section.px-fixed { .solutions-section {
padding-top: 80px; padding-top: 80 * 1.33px;
width: 932px; width: 932 * 1.33px;
margin: 0 auto; margin: 0 auto;
} }
.solutions-content { .solutions-content {
display: flex; display: flex;
gap: 16px; gap: 16 * 1.33px;
align-items: center; align-items: center;
} }
.solutions-list { .solutions-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 24px; gap: 24 * 1.33px;
width: 466px; width: 466 * 1.33px;
} }
.solution-item { .solution-item {
text-align: left; text-align: left;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 14px; gap: 16 * 1.33px;
} }
.solution-icon { .solution-icon {
width: 92px; width: 76 * 1.33px;
height: 76px; height: 76 * 1.33px;
padding-left: 16px; margin-left: 16 * 1.33px;
} }
.solution-title { .solution-title {
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 24px; font-size: 24 * 1.33px;
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
line-height: 32px; /* 133.333% */ line-height: 32 * 1.33px; /* 133.333% */
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
display: flex; display: flex;
gap: 16px; gap: 16 * 1.33px;
align-items: flex-start; align-items: flex-start;
} }
.solution-description { .solution-description {
align-self: stretch; align-self: stretch;
color: #455363; color: #455363;
padding: 0 16px; padding: 0 16 * 1.33px;
text-align: justify; text-align: justify;
font-feature-settings: "liga" off, "clig" off; font-feature-settings: "liga" off, "clig" off;
/* 正文 */ /* 正文 */
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px; font-size: 16 * 1.33px;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
line-height: 22px; /* 137.5% */ line-height: 22 * 1.33px; /* 137.5% */
letter-spacing: 0.48px; letter-spacing: 0.48 * 1.33px;
} }
.solution-image-container { .solution-image-container {
width: 434px; width: 434 * 1.33px;
border-radius: 16px; border-radius: 16 * 1.33px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -457,12 +442,12 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
border-radius: 16px; border-radius: 16 * 1.33px;
} }
.advantages-section.px-fixed { .advantages-section {
margin-top: 80px; margin-top: 80 * 1.33px;
padding: 80px 0; padding: 80 * 1.33px 0;
background-image: url("@/assets/image/1440/product-introduction-img4.png"); background-image: url("@/assets/image/1440/product-introduction-img4.png");
background-size: cover; background-size: cover;
background-position: center; background-position: center;
@ -470,8 +455,8 @@
position: relative; position: relative;
} }
.advantages-content.px-fixed { .advantages-content {
width: 932px; width: 932 * 1.33px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -480,44 +465,44 @@
} }
.advantages-header { .advantages-header {
width: 466px; width: 466 * 1.33px;
padding: 0 16px; padding: 0 16 * 1.33px;
} }
.advantages-list { .advantages-list {
width: 466px; width: 466 * 1.33px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 32px; gap: 24 * 1.33px;
} }
.advantage-item { .advantage-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16 * 1.33px;
} }
.advantage-title { .advantage-title {
font-size: 24px; font-size: 24 * 1.33px;
font-weight: 500; font-weight: 500;
line-height: 32px; line-height: 32 * 1.33px;
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
display: flex; display: flex;
gap: 16px; gap: 16 * 1.33px;
align-items: flex-start; align-items: flex-start;
} }
.advantage-description { .advantage-description {
font-size: 16px; font-size: 16 * 1.33px;
line-height: 22px; line-height: 22 * 1.33px;
letter-spacing: 0.48px; letter-spacing: 0.48 * 1.33px;
opacity: 0.7; opacity: 0.7;
padding: 0 16px; padding: 0 16 * 1.33px;
} }
.text-white { .text-white {
color: #fff; color: #fff;
} }
.cta-section.px-fixed { .cta-section {
padding: 60px 0; padding: 80 * 1.33px 0;
position: relative; position: relative;
width: 932px; width: 932 * 1.33px;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
} }
@ -525,7 +510,7 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0 16px; padding: 0 16 * 1.33px;
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
@ -533,24 +518,24 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
height: 188px; height: 188 * 1.33px;
} }
.cta-arrow { .cta-arrow {
width: 60px; width: 60 * 1.33px;
height: 32px; height: 32 * 1.33px;
} }
.cta-title { .cta-title {
font-size: 40px; font-size: 40 * 1.33px;
font-weight: 500; font-weight: 500;
line-height: 56px; line-height: 56 * 1.33px;
letter-spacing: 1.2px; letter-spacing: 1.2 * 1.33px;
} }
.cta-qr-code { .cta-qr-code {
width: 188px; width: 188 * 1.33px;
height: 188px; height: 188 * 1.33px;
background-color: #90ffff; background-color: #90ffff;
border-radius: 16px; border-radius: 16 * 1.33px;
padding: 14px; padding: 14 * 1.33px;
} }
.cta-qr-code img { .cta-qr-code img {
width: 100%; width: 100%;
@ -559,27 +544,27 @@
} }
.cta-bg-img { .cta-bg-img {
position: absolute; position: absolute;
top: 80px; top: 0;
left: 355px; left: 201 * 1.33px;
width: 530px; width: 530 * 1.33px;
height: 268px; height: 268 * 1.33px;
opacity: 0.8; opacity: 0.8;
z-index: 0; z-index: 0;
} }
.vertical-line.px-fixed { .vertical-line {
width: 1px; width: 1 * 1.33px;
height: 16px; height: 16 * 1.33px;
background: #ff7bac; background: #ff7bac;
flex-shrink: 0; flex-shrink: 0;
margin-top: 6px; margin-top: 6 * 1.33px;
} }
.grid-lines.px-fixed { .grid-lines {
position: absolute; position: absolute;
top: 0; top: 0;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
width: 932px; width: 932 * 1.33px;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
z-index: 0; z-index: 0;
@ -592,26 +577,26 @@
} }
.grid-lines .line.solid { .grid-lines .line.solid {
width: 1px; width: 1 * 1.33px;
background-color: rgba(0, 0, 0, 0.08); background-color: rgba(0, 0, 0, 0.08);
} }
.grid-lines .line.dashed { .grid-lines .line.dashed {
width: 0; width: 0;
border-left: 1px dotted rgba(0, 0, 0, 0.12); border-left: 1 * 1.33px dotted rgba(0, 0, 0, 0.12);
} }
.grid-lines .line-1 { .grid-lines .line-1 {
left: 0; left: 0;
} }
.grid-lines .line-2 { .grid-lines .line-2 {
left: 310px; left: 234 * 1.33px;
} }
.grid-lines .line-3 { .grid-lines .line-3 {
left: 620px; left: 468 * 1.33px;
} }
.grid-lines .line-4 { .grid-lines .line-4 {
left: 930px; left: 702 * 1.33px;
} }
.grid-lines .line-5 { .grid-lines .line-5 {
right: 0; right: 0;

View File

@ -1,4 +1,7 @@
<script setup></script> <script setup>
import { useI18n } from "vue-i18n";
const { t } = useI18n();
</script>
<template> <template>
<div class="page-container"> <div class="page-container">
@ -12,23 +15,19 @@
<section class="hero-section relative"> <section class="hero-section relative">
<div class="hero-content"> <div class="hero-content">
<div class="hero-title"> <div class="hero-title">
More than just a tool<br /> {{ t("product_intro.hero_line1") }}<br />
Comprehensive growth solutions, <br /> {{ t("product_intro.hero_line2") }}<br />
providing a one-stop solution for content creation,<br /> {{ t("product_intro.hero_line3") }}<br />
publishing, analysis, and monetization {{ t("product_intro.hero_line4") }}
</div> </div>
</div> </div>
<div class="core-value-card"> <div class="core-value-card">
<div class="card-content"> <div class="card-content">
<div class="card-title">Core Value</div> <div class="card-title">
{{ t("product_intro.core_value_title") }}
</div>
<div class="card-text"> <div class="card-text">
The FIEE-SAAS platform is a one-stop content operation solution {{ t("product_intro.core_value_text") }}
tailored for creators in the digital era. The platform utilizes
intelligent distribution technology, A1 empowerment tools, and
full-chain services,Assist you in efficiently reaching audiences on
global mainstream platforms such as TikTok, YouTube, and Instagram,
creating a KOL brand effect, unlocking content value, and achieving
sustainable growth.
</div> </div>
</div> </div>
</div> </div>
@ -42,59 +41,52 @@
<section class="features-section"> <section class="features-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Product Features</div> <div class="section-title">{{ t("product_intro.features_title") }}</div>
</div> </div>
<div class="features-list"> <div class="features-list">
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
One-click Synchronous Publishing {{ t("product_intro.feature_sync") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Synchronize graphic and video content to TikTok, YouTube, and {{ t("product_intro.feature_sync_desc") }}
Instagram platforms at once, saving time on repetitive operations.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Intelligent Scheduled Publishing {{ t("product_intro.feature_schedule") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Plan the content release time in advance, support batch scheduling, {{ t("product_intro.feature_schedule_desc") }}
and accurately grasp the optimal release time of each platform.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Unified Management of Multiple Accounts {{ t("product_intro.feature_accounts") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Easily manage multiple accounts on one platform without the need for {{ t("product_intro.feature_accounts_desc") }}
repeated login and switching, improving team collaboration
efficiency.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Cloud Content Library {{ t("product_intro.feature_library") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Safely store and manage all creative materials, access and use them {{ t("product_intro.feature_library_desc") }}
anytime, anywhere, and support quick retrieval and reuse.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Basic Data Tracking {{ t("product_intro.feature_tracking") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Visually view the content performance of various platforms, {{ t("product_intro.feature_tracking_desc") }}
understand core data indicators, and provide a basis for optimizing
strategies.
</div> </div>
</div> </div>
</div> </div>
@ -103,7 +95,9 @@
<section class="solutions-section"> <section class="solutions-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Value Added Solutions</div> <div class="section-title">
{{ t("product_intro.solutions_title") }}
</div>
</div> </div>
<div class="solutions-content"> <div class="solutions-content">
<div class="solutions-list"> <div class="solutions-list">
@ -115,12 +109,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
KOL Brand Promotion Services {{ t("product_intro.sol_kol") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Efficiently connect high-quality business cooperation {{ t("product_intro.sol_kol_desc") }}
opportunities and complete the entire process management from
order acceptance to publication within the platform.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -131,12 +123,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Professional Content Creation Support {{ t("product_intro.sol_content") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Connect professional shooting and post production teams for you, {{ t("product_intro.sol_content_desc") }}
create high-quality "art+story" content, and strengthen IP
influence.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -147,12 +137,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Account Operation and Hosting Services {{ t("product_intro.sol_ops") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
From 0 to 1 account positioning, follower growth strategy to {{ t("product_intro.sol_ops_desc") }}
monetization cycle, operation experts provide full cycle running
and hosting services.
</div> </div>
</div> </div>
</div> </div>
@ -170,56 +158,54 @@
<div class="advantages-content"> <div class="advantages-content">
<div class="advantages-header"> <div class="advantages-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title text-white">Our Advantages</div> <div class="section-title text-white">
{{ t("product_intro.advantages_title") }}
</div>
</div> </div>
<div class="advantages-list"> <div class="advantages-list">
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Time Saving {{ t("product_intro.adv_time") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Multi platform publishing efficiency improvement, allowing you to {{ t("product_intro.adv_time_desc") }}
focus on content creation.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Safe and Reliable {{ t("product_intro.adv_safe") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Enterprise level data encryption and permission control ensure {{ t("product_intro.adv_safe_desc") }}
account and content security.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Maintain Consistency {{ t("product_intro.adv_consistent") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Ensure that brand information is presented uniformly on all {{ t("product_intro.adv_consistent_desc") }}
platforms.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Data Driven {{ t("product_intro.adv_data") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Optimizing Content Strategies Based on Actual Performance. {{ t("product_intro.adv_data_desc") }}
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Easy to Use {{ t("product_intro.adv_easy") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Intuitive interface design, no need for professional technical {{ t("product_intro.adv_easy_desc") }}
background.
</div> </div>
</div> </div>
</div> </div>
@ -247,8 +233,8 @@
/> />
</svg> </svg>
<div class="cta-title"> <div class="cta-title">
Get customized <br /> {{ t("product_intro.cta_title_line1") }}<br />
solutions for free {{ t("product_intro.cta_title_line2") }}
</div> </div>
</div> </div>
<div class="cta-qr-code"> <div class="cta-qr-code">

View File

@ -1,4 +1,7 @@
<script setup></script> <script setup>
import { useI18n } from "vue-i18n";
const { t } = useI18n();
</script>
<template> <template>
<div class="page-container"> <div class="page-container">
@ -9,23 +12,19 @@
<section class="hero-section relative"> <section class="hero-section relative">
<div class="hero-content"> <div class="hero-content">
<div class="hero-title"> <div class="hero-title">
More than just a tool<br /> {{ t("product_intro.hero_line1") }}<br />
Comprehensive growth <br />solutions, providing a one- <br />stop {{ t("product_intro.hero_line2") }}<br />
solution for content <br />creation, publishing, analysis,<br /> {{ t("product_intro.hero_line3") }}<br />
and monetization {{ t("product_intro.hero_line4") }}
</div> </div>
</div> </div>
<div class="core-value-card"> <div class="core-value-card">
<div class="card-content"> <div class="card-content">
<div class="card-title">Core Value</div> <div class="card-title">
{{ t("product_intro.core_value_title") }}
</div>
<div class="card-text"> <div class="card-text">
The FIEE-SAAS platform is a one-stop content operation solution {{ t("product_intro.core_value_text") }}
tailored for creators in the digital era. The platform utilizes
intelligent distribution technology, A1 empowerment tools, and
full-chain services,Assist you in efficiently reaching audiences on
global mainstream platforms such as TikTok, YouTube, and Instagram,
creating a KOL brand effect, unlocking content value, and achieving
sustainable growth.
</div> </div>
</div> </div>
</div> </div>
@ -39,59 +38,52 @@
<section class="features-section"> <section class="features-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Product Features</div> <div class="section-title">{{ t("product_intro.features_title") }}</div>
</div> </div>
<div class="features-list"> <div class="features-list">
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
One-click Synchronous Publishing {{ t("product_intro.feature_sync") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Synchronize graphic and video content to TikTok, YouTube, and {{ t("product_intro.feature_sync_desc") }}
Instagram platforms at once, saving time on repetitive operations.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Intelligent Scheduled Publishing {{ t("product_intro.feature_schedule") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Plan the content release time in advance, support batch scheduling, {{ t("product_intro.feature_schedule_desc") }}
and accurately grasp the optimal release time of each platform.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Unified Management of Multiple Accounts {{ t("product_intro.feature_accounts") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Easily manage multiple accounts on one platform without the need for {{ t("product_intro.feature_accounts_desc") }}
repeated login and switching, improving team collaboration
efficiency.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Cloud Content Library {{ t("product_intro.feature_library") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Safely store and manage all creative materials, access and use them {{ t("product_intro.feature_library_desc") }}
anytime, anywhere, and support quick retrieval and reuse.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Basic Data Tracking {{ t("product_intro.feature_tracking") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Visually view the content performance of various platforms, {{ t("product_intro.feature_tracking_desc") }}
understand core data indicators, and provide a basis for optimizing
strategies.
</div> </div>
</div> </div>
</div> </div>
@ -100,7 +92,9 @@
<section class="solutions-section"> <section class="solutions-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Value Added Solutions</div> <div class="section-title">
{{ t("product_intro.solutions_title") }}
</div>
</div> </div>
<div class="solutions-content"> <div class="solutions-content">
<div class="solution-image-container"> <div class="solution-image-container">
@ -119,12 +113,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
KOL Brand Promotion Services {{ t("product_intro.sol_kol") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Efficiently connect high-quality business cooperation {{ t("product_intro.sol_kol_desc") }}
opportunities and complete the entire process management from
order acceptance to publication within the platform.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -135,12 +127,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Professional Content Creation Support {{ t("product_intro.sol_content") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Connect professional shooting and post production teams for you, {{ t("product_intro.sol_content_desc") }}
create high-quality "art+story" content, and strengthen IP
influence.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -151,12 +141,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Account Operation and Hosting Services {{ t("product_intro.sol_ops") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
From 0 to 1 account positioning, follower growth strategy to {{ t("product_intro.sol_ops_desc") }}
monetization cycle, operation experts provide full cycle running
and hosting services.
</div> </div>
</div> </div>
</div> </div>
@ -167,56 +155,54 @@
<div class="advantages-content"> <div class="advantages-content">
<div class="advantages-header"> <div class="advantages-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title text-white">Our Advantages</div> <div class="section-title text-white">
{{ t("product_intro.advantages_title") }}
</div>
</div> </div>
<div class="advantages-list"> <div class="advantages-list">
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Time Saving {{ t("product_intro.adv_time") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Multi platform publishing efficiency improvement, allowing you to {{ t("product_intro.adv_time_desc") }}
focus on content creation.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Safe and Reliable {{ t("product_intro.adv_safe") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Enterprise level data encryption and permission control ensure {{ t("product_intro.adv_safe_desc") }}
account and content security.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Maintain Consistency {{ t("product_intro.adv_consistent") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Ensure that brand information is presented uniformly on all {{ t("product_intro.adv_consistent_desc") }}
platforms.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Data Driven {{ t("product_intro.adv_data") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Optimizing Content Strategies Based on Actual Performance. {{ t("product_intro.adv_data_desc") }}
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Easy to Use {{ t("product_intro.adv_easy") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Intuitive interface design, no need for professional technical {{ t("product_intro.adv_easy_desc") }}
background.
</div> </div>
</div> </div>
</div> </div>
@ -225,7 +211,11 @@
<section class="cta-section"> <section class="cta-section">
<div class="cta-content"> <div class="cta-content">
<div class="cta-title">Get customized<br />solutions for free</div> <div class="cta-title">
{{ t("product_intro.cta_title_line1") }}<br />{{
t("product_intro.cta_title_line2")
}}
</div>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="32" width="32"

View File

@ -1,4 +1,7 @@
<script setup></script> <script setup>
import { useI18n } from "vue-i18n";
const { t } = useI18n();
</script>
<template> <template>
<div class="page-container"> <div class="page-container">
@ -10,23 +13,19 @@
<section class="hero-section relative"> <section class="hero-section relative">
<div class="hero-content"> <div class="hero-content">
<div class="hero-title"> <div class="hero-title">
More than just a tool<br /> {{ t("product_intro.hero_line1") }}<br />
Comprehensive growth solutions, <br />providing a one-stop solution {{ t("product_intro.hero_line2") }}<br />
for <br />content creation, publishing, analysis, <br />and {{ t("product_intro.hero_line3") }}<br />
monetization {{ t("product_intro.hero_line4") }}
</div> </div>
</div> </div>
<div class="core-value-card"> <div class="core-value-card">
<div class="card-content"> <div class="card-content">
<div class="card-title">Core Value</div> <div class="card-title">
{{ t("product_intro.core_value_title") }}
</div>
<div class="card-text"> <div class="card-text">
The FIEE-SAAS platform is a one-stop content operation solution {{ t("product_intro.core_value_text") }}
tailored for creators in the digital era. The platform utilizes
intelligent distribution technology, A1 empowerment tools, and
full-chain services,Assist you in efficiently reaching audiences on
global mainstream platforms such as TikTok, YouTube, and Instagram,
creating a KOL brand effect, unlocking content value, and achieving
sustainable growth.
</div> </div>
</div> </div>
</div> </div>
@ -40,59 +39,52 @@
<section class="features-section"> <section class="features-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Product Features</div> <div class="section-title">{{ t("product_intro.features_title") }}</div>
</div> </div>
<div class="features-list"> <div class="features-list">
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
One-click Synchronous Publishing {{ t("product_intro.feature_sync") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Synchronize graphic and video content to TikTok, YouTube, and {{ t("product_intro.feature_sync_desc") }}
Instagram platforms at once, saving time on repetitive operations.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Intelligent Scheduled Publishing {{ t("product_intro.feature_schedule") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Plan the content release time in advance, support batch scheduling, {{ t("product_intro.feature_schedule_desc") }}
and accurately grasp the optimal release time of each platform.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Unified Management of Multiple Accounts {{ t("product_intro.feature_accounts") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Easily manage multiple accounts on one platform without the need for {{ t("product_intro.feature_accounts_desc") }}
repeated login and switching, improving team collaboration
efficiency.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Cloud Content Library {{ t("product_intro.feature_library") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Safely store and manage all creative materials, access and use them {{ t("product_intro.feature_library_desc") }}
anytime, anywhere, and support quick retrieval and reuse.
</div> </div>
</div> </div>
<div class="feature-item"> <div class="feature-item">
<div class="feature-title"> <div class="feature-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Basic Data Tracking {{ t("product_intro.feature_tracking") }}
</div> </div>
<div class="feature-description"> <div class="feature-description">
Visually view the content performance of various platforms, {{ t("product_intro.feature_tracking_desc") }}
understand core data indicators, and provide a basis for optimizing
strategies.
</div> </div>
</div> </div>
</div> </div>
@ -101,7 +93,9 @@
<section class="solutions-section"> <section class="solutions-section">
<div class="section-header"> <div class="section-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title">Value Added Solutions</div> <div class="section-title">
{{ t("product_intro.solutions_title") }}
</div>
</div> </div>
<div class="solutions-content"> <div class="solutions-content">
<div class="solution-image-container"> <div class="solution-image-container">
@ -120,12 +114,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
KOL Brand Promotion Services {{ t("product_intro.sol_kol") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Efficiently connect high-quality business cooperation {{ t("product_intro.sol_kol_desc") }}
opportunities and complete the entire process management from
order acceptance to publication within the platform.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -136,12 +128,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Professional Content Creation Support {{ t("product_intro.sol_content") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
Connect professional shooting and post production teams for you, {{ t("product_intro.sol_content_desc") }}
create high-quality "art+story" content, and strengthen IP
influence.
</div> </div>
</div> </div>
<div class="solution-item"> <div class="solution-item">
@ -152,12 +142,10 @@
/> />
<div class="solution-title"> <div class="solution-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Account Operation and Hosting Services {{ t("product_intro.sol_ops") }}
</div> </div>
<div class="solution-description"> <div class="solution-description">
From 0 to 1 account positioning, follower growth strategy to {{ t("product_intro.sol_ops_desc") }}
monetization cycle, operation experts provide full cycle running
and hosting services.
</div> </div>
</div> </div>
</div> </div>
@ -168,56 +156,54 @@
<div class="advantages-content"> <div class="advantages-content">
<div class="advantages-header"> <div class="advantages-header">
<div class="decorator-bar"></div> <div class="decorator-bar"></div>
<div class="section-title text-white">Our Advantages</div> <div class="section-title text-white">
{{ t("product_intro.advantages_title") }}
</div>
</div> </div>
<div class="advantages-list"> <div class="advantages-list">
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Time Saving {{ t("product_intro.adv_time") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Multi platform publishing efficiency improvement, allowing you to {{ t("product_intro.adv_time_desc") }}
focus on content creation.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Safe and Reliable {{ t("product_intro.adv_safe") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Enterprise level data encryption and permission control ensure {{ t("product_intro.adv_safe_desc") }}
account and content security.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Maintain Consistency {{ t("product_intro.adv_consistent") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Ensure that brand information is presented uniformly on all {{ t("product_intro.adv_consistent_desc") }}
platforms.
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Data Driven {{ t("product_intro.adv_data") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Optimizing Content Strategies Based on Actual Performance. {{ t("product_intro.adv_data_desc") }}
</div> </div>
</div> </div>
<div class="advantage-item"> <div class="advantage-item">
<div class="advantage-title"> <div class="advantage-title">
<div class="vertical-line"></div> <div class="vertical-line"></div>
Easy to Use {{ t("product_intro.adv_easy") }}
</div> </div>
<div class="advantage-description"> <div class="advantage-description">
Intuitive interface design, no need for professional technical {{ t("product_intro.adv_easy_desc") }}
background.
</div> </div>
</div> </div>
</div> </div>
@ -245,8 +231,8 @@
/> />
</svg> </svg>
<div class="cta-title"> <div class="cta-title">
Get customized <br /> {{ t("product_intro.cta_title_line1") }}<br />
solutions for free {{ t("product_intro.cta_title_line2") }}
</div> </div>
</div> </div>
<div class="cta-qr-code"> <div class="cta-qr-code">

View File

@ -1,5 +1,7 @@
<script setup> <script setup>
import { useStockQuote } from "@/store/stock-quote/index.js"; import { useStockQuote } from "@/store/stock-quote/index.js";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { getStockQuate, stockQuote, formatted } = useStockQuote(); const { getStockQuate, stockQuote, formatted } = useStockQuote();
getStockQuate(); getStockQuate();
</script> </script>
@ -10,21 +12,21 @@ getStockQuate();
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="stock-title">Stock Quote</div> <div class="stock-title">{{ t("stock_quote.title") }}</div>
</div> </div>
<section class="quote-layout"> <section class="quote-layout">
<div class="price-card"> <div class="price-card">
<div class="price-value">${{ stockQuote.price }}</div> <div class="price-value">${{ stockQuote.price }}</div>
<div class="price-market">NASDAQ: FIEE</div> <div class="price-market">{{ t("stock_quote.nasdaq") }}</div>
<div class="price-time">{{ formatted }}</div> <div class="price-time">{{ formatted }}</div>
</div> </div>
<div class="stats-table"> <div class="stats-table">
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Open</span> <span class="stat-title">{{ t("stock_quote.open") }}</span>
<span class="stat-value">{{ stockQuote.open }}</span> <span class="stat-value">{{ stockQuote.open }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">% Change</span> <span class="stat-title">{{ t("stock_quote.change") }}</span>
<span <span
class="stat-value" class="stat-value"
:class=" :class="
@ -41,19 +43,19 @@ getStockQuate();
</span> </span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Day's Range</span> <span class="stat-title">{{ t("stock_quote.days_range") }}</span>
<span class="stat-value">{{ stockQuote.daysRange }}</span> <span class="stat-value">{{ stockQuote.daysRange }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">52-Week Range</span> <span class="stat-title">{{ t("stock_quote.week_range") }}</span>
<span class="stat-value">{{ stockQuote.week52Range }}</span> <span class="stat-value">{{ stockQuote.week52Range }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Volume</span> <span class="stat-title">{{ t("stock_quote.volume") }}</span>
<span class="stat-value">{{ stockQuote.volume }}</span> <span class="stat-value">{{ stockQuote.volume }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Market Cap</span> <span class="stat-title">{{ t("stock_quote.market_cap") }}</span>
<span class="stat-value">{{ stockQuote.marketCap }}</span> <span class="stat-value">{{ stockQuote.marketCap }}</span>
</div> </div>
</div> </div>

View File

@ -1,5 +1,7 @@
<script setup> <script setup>
import { useStockQuote } from "@/store/stock-quote/index.js"; import { useStockQuote } from "@/store/stock-quote/index.js";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { getStockQuate, stockQuote, formatted } = useStockQuote(); const { getStockQuate, stockQuote, formatted } = useStockQuote();
console.log(stockQuote); console.log(stockQuote);
getStockQuate(); getStockQuate();
@ -11,21 +13,21 @@ getStockQuate();
<!-- 标题区域 --> <!-- 标题区域 -->
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="stock-title">Stock Quote</div> <div class="stock-title">{{ t("stock_quote.title") }}</div>
</div> </div>
<section class="quote-layout"> <section class="quote-layout">
<div class="price-card"> <div class="price-card">
<div class="price-value">${{ stockQuote.price }}</div> <div class="price-value">${{ stockQuote.price }}</div>
<div class="price-market">NASDAQ: FIEE</div> <div class="price-market">{{ t("stock_quote.nasdaq") }}</div>
<div class="price-time">{{ formatted }}</div> <div class="price-time">{{ formatted }}</div>
</div> </div>
<div class="stats-table"> <div class="stats-table">
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Open</span> <span class="stat-title">{{ t("stock_quote.open") }}</span>
<span class="stat-value">{{ stockQuote.open }}</span> <span class="stat-value">{{ stockQuote.open }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">% Change</span> <span class="stat-title">{{ t("stock_quote.change") }}</span>
<span <span
class="stat-value" class="stat-value"
:class=" :class="
@ -42,19 +44,19 @@ getStockQuate();
</span> </span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Volume</span> <span class="stat-title">{{ t("stock_quote.volume") }}</span>
<span class="stat-value">{{ stockQuote.volume }}</span> <span class="stat-value">{{ stockQuote.volume }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">52-Week Range</span> <span class="stat-title">{{ t("stock_quote.week_range") }}</span>
<span class="stat-value">{{ stockQuote.week52Range }}</span> <span class="stat-value">{{ stockQuote.week52Range }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Day's Range</span> <span class="stat-title">{{ t("stock_quote.days_range") }}</span>
<span class="stat-value">{{ stockQuote.daysRange }}</span> <span class="stat-value">{{ stockQuote.daysRange }}</span>
</div> </div>
<div class="stats-cell"> <div class="stats-cell">
<span class="stat-title">Market Cap</span> <span class="stat-title">{{ t("stock_quote.market_cap") }}</span>
<span class="stat-value">{{ stockQuote.marketCap }}</span> <span class="stat-value">{{ stockQuote.marketCap }}</span>
</div> </div>
</div> </div>

View File

@ -1,6 +1,8 @@
<script setup> <script setup>
import { onMounted } from "vue"; import { onMounted } from "vue";
import { useStockQuote } from "@/store/stock-quote/index.js"; import { useStockQuote } from "@/store/stock-quote/index.js";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { getStockQuate, stockQuote, formatted } = useStockQuote(); const { getStockQuate, stockQuote, formatted } = useStockQuote();
onMounted(() => { onMounted(() => {
getStockQuate(); getStockQuate();
@ -12,33 +14,35 @@ onMounted(() => {
<div class="content-wrapper"> <div class="content-wrapper">
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title-text">Stock Quote</div> <div class="title-text">{{ t("stock_quote.title") }}</div>
</div> </div>
<div class="data-section"> <div class="data-section">
<div class="price-card"> <div class="price-card">
<div class="price-value">${{ stockQuote.price }}</div> <div class="price-value">${{ stockQuote.price }}</div>
<div class="price-nasdaq">NASDAQ: FIEE</div> <div class="price-nasdaq">{{ t("stock_quote.nasdaq") }}</div>
<div class="price-date">{{ formatted }}</div> <div class="price-date">{{ formatted }}</div>
</div> </div>
<div class="details-grid"> <div class="details-grid">
<div class="details-column"> <div class="details-column">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Open</span> <span class="detail-label">{{ t("stock_quote.open") }}</span>
<span class="detail-value">{{ stockQuote.open }}</span> <span class="detail-value">{{ stockQuote.open }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Day's Range</span> <span class="detail-label">{{
t("stock_quote.days_range")
}}</span>
<span class="detail-value">{{ stockQuote.daysRange }}</span> <span class="detail-value">{{ stockQuote.daysRange }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Volume</span> <span class="detail-label">{{ t("stock_quote.volume") }}</span>
<span class="detail-value">{{ stockQuote.volume }}</span> <span class="detail-value">{{ stockQuote.volume }}</span>
</div> </div>
</div> </div>
<div class="details-column"> <div class="details-column">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">% Change</span> <span class="detail-label">{{ t("stock_quote.change") }}</span>
<span <span
class="detail-value" class="detail-value"
:class="{ :class="{
@ -50,11 +54,15 @@ onMounted(() => {
</span> </span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">52-Week Range</span> <span class="detail-label">{{
t("stock_quote.week_range")
}}</span>
<span class="detail-value">{{ stockQuote.week52Range }}</span> <span class="detail-value">{{ stockQuote.week52Range }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Market Cap</span> <span class="detail-label">{{
t("stock_quote.market_cap")
}}</span>
<span class="detail-value">{{ stockQuote.marketCap }}</span> <span class="detail-value">{{ stockQuote.marketCap }}</span>
</div> </div>
</div> </div>

View File

@ -2,6 +2,8 @@
import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui"; import { NCarousel, NDivider, NMarquee, NPopselect } from "naive-ui";
import { onUnmounted, ref, watch, onMounted, computed } from "vue"; import { onUnmounted, ref, watch, onMounted, computed } from "vue";
import { useStockQuote } from "@/store/stock-quote/index.js"; import { useStockQuote } from "@/store/stock-quote/index.js";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { getStockQuate, stockQuote, formatted } = useStockQuote(); const { getStockQuate, stockQuote, formatted } = useStockQuote();
getStockQuate(); getStockQuate();
</script> </script>
@ -11,33 +13,35 @@ getStockQuate();
<div class="content-wrapper"> <div class="content-wrapper">
<div class="title-section"> <div class="title-section">
<div class="title-decoration"></div> <div class="title-decoration"></div>
<div class="title-text">Stock Quote</div> <div class="title-text">{{ t("stock_quote.title") }}</div>
</div> </div>
<div class="data-section"> <div class="data-section">
<div class="price-card"> <div class="price-card">
<div class="price-value">${{ stockQuote.price }}</div> <div class="price-value">${{ stockQuote.price }}</div>
<div class="price-nasdaq">NASDAQ: FIEE</div> <div class="price-nasdaq">{{ t("stock_quote.nasdaq") }}</div>
<div class="price-date">{{ formatted }}</div> <div class="price-date">{{ formatted }}</div>
</div> </div>
<div class="details-grid"> <div class="details-grid">
<div class="details-column"> <div class="details-column">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Open</span> <span class="detail-label">{{ t("stock_quote.open") }}</span>
<span class="detail-value">{{ stockQuote.open }}</span> <span class="detail-value">{{ stockQuote.open }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Day's Range</span> <span class="detail-label">{{
t("stock_quote.days_range")
}}</span>
<span class="detail-value">{{ stockQuote.daysRange }}</span> <span class="detail-value">{{ stockQuote.daysRange }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Volume</span> <span class="detail-label">{{ t("stock_quote.volume") }}</span>
<span class="detail-value">{{ stockQuote.volume }}</span> <span class="detail-value">{{ stockQuote.volume }}</span>
</div> </div>
</div> </div>
<div class="details-column"> <div class="details-column">
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">% Change</span> <span class="detail-label">{{ t("stock_quote.change") }}</span>
<span <span
class="detail-value" class="detail-value"
:class="{ :class="{
@ -49,11 +53,15 @@ getStockQuate();
</span> </span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">52-Week Range</span> <span class="detail-label">{{
t("stock_quote.week_range")
}}</span>
<span class="detail-value">{{ stockQuote.week52Range }}</span> <span class="detail-value">{{ stockQuote.week52Range }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="detail-label">Market Cap</span> <span class="detail-label">{{
t("stock_quote.market_cap")
}}</span>
<span class="detail-value">{{ stockQuote.marketCap }}</span> <span class="detail-value">{{ stockQuote.marketCap }}</span>
</div> </div>
</div> </div>

View File

@ -21,6 +21,11 @@ export default defineConfig({
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/api') rewrite: (path) => path.replace(/^\/api/, '/api')
}, },
'/pdf-proxy': {
target: 'https://cdn-test.szjixun.cn',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/pdf-proxy/, '')
},
} }
}, },
plugins: [ plugins: [