fix historic-stock 375,press-releases 375

This commit is contained in:
yuanshan 2025-10-13 17:17:21 +08:00
parent ad91c54d8d
commit 4f59eb52e1
4 changed files with 2239 additions and 679 deletions

View File

@ -0,0 +1,397 @@
<template>
<div class="picker-mask" @click.self="emit('close')">
<div class="picker-panel">
<div class="picker-title">
Select Time
<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="picker-columns"
:style="{ height: wheelViewportHeight + 'px' }"
>
<div class="center-lines" :style="{ height: wheelItemHeight + 'px' }">
<div class="line"></div>
<div class="line"></div>
</div>
<div
class="picker-col year-col"
ref="yearColRef"
@scroll.passive="(e) => handleWheelScroll('year', e)"
@wheel.prevent="(e) => handleWheelStep('year', e)"
:style="{
scrollPaddingTop: wheelCenterPad + 'px',
scrollPaddingBottom: wheelCenterPad + 'px',
}"
>
<div :style="{ height: wheelCenterPad + 'px' }"></div>
<div
v-for="y in years"
:key="'y' + y"
class="picker-item"
:class="{ active: y === localYear }"
:style="{
height: wheelItemHeight + 'px',
}"
@click="localYear = y"
>
{{ y }}
</div>
<div :style="{ height: wheelCenterPad + 'px' }"></div>
</div>
<div
class="picker-col month-col"
ref="monthColRef"
@scroll.passive="(e) => handleWheelScroll('month', e)"
@wheel.prevent="(e) => handleWheelStep('month', e)"
:style="{
scrollPaddingTop: wheelCenterPad + 'px',
scrollPaddingBottom: wheelCenterPad + 'px',
}"
>
<div :style="{ height: wheelCenterPad + 'px' }"></div>
<div
v-for="m in months"
:key="'m' + m"
class="picker-item"
:class="{ active: m === localMonth }"
:style="{
height: wheelItemHeight + 'px',
}"
@click="localMonth = m"
>
{{ m }}
</div>
<div :style="{ height: wheelCenterPad + 'px' }"></div>
</div>
<div
class="picker-col day-col"
ref="dayColRef"
@scroll.passive="(e) => handleWheelScroll('day', e)"
@wheel.prevent="(e) => handleWheelStep('day', e)"
:style="{
scrollPaddingTop: wheelCenterPad + 'px',
scrollPaddingBottom: wheelCenterPad + 'px',
}"
>
<div :style="{ height: wheelCenterPad + 'px' }"></div>
<div
v-for="d in daysInMonth(localYear, localMonth)"
:key="'d' + d"
class="picker-item"
:class="{ active: d === localDay }"
:style="{
height: wheelItemHeight + 'px',
}"
@click="localDay = d"
>
{{ d }}
</div>
<div :style="{ height: wheelCenterPad + 'px' }"></div>
</div>
</div>
<div class="picker-actions">
<button class="picker-confirm" @click="confirm">
Confirm Selection
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch, nextTick } from "vue";
const props = defineProps({
modelValue: { type: Object, required: true }, // { year, month, day }
minYear: { type: Number, default: 2009 },
maxYear: { type: Number, default: new Date().getFullYear() },
});
const emit = defineEmits(["update:modelValue", "close", "confirm"]);
const wheelItemBase = 8 * 5.12;
const wheelItemHeight = Math.round(wheelItemBase); // use integer px to avoid fractional rounding
const wheelViewportHeight = wheelItemHeight * 5;
const wheelCenterPad = (wheelViewportHeight - wheelItemHeight) / 2;
const isUserScrolling = ref(false);
const years = computed(() =>
Array.from(
{ length: props.maxYear - props.minYear + 1 },
(_, i) => props.minYear + i
)
);
const months = Array.from({ length: 12 }, (_, i) => i + 1);
const localYear = ref(props.modelValue.year);
const localMonth = ref(props.modelValue.month);
const localDay = ref(props.modelValue.day);
watch(
() => props.modelValue,
(v) => {
localYear.value = v.year;
localMonth.value = v.month;
localDay.value = v.day;
nextTick(syncWheelPositions);
}
);
const yearColRef = ref(null);
const monthColRef = ref(null);
const dayColRef = ref(null);
function daysInMonth(year, month) {
return new Date(year, month, 0).getDate();
}
watch([localYear, localMonth], () => {
const dim = daysInMonth(localYear.value, localMonth.value);
if (localDay.value > dim) localDay.value = dim;
if (!isUserScrolling.value) {
nextTick(syncWheelPositions);
}
});
function syncWheelPositions() {
const yearIdx = years.value.indexOf(localYear.value);
const monthIdx = localMonth.value - 1;
const dayIdx = localDay.value - 1;
if (yearColRef.value) yearColRef.value.scrollTop = yearIdx * wheelItemHeight;
if (monthColRef.value)
monthColRef.value.scrollTop = monthIdx * wheelItemHeight;
if (dayColRef.value) dayColRef.value.scrollTop = dayIdx * wheelItemHeight;
}
const scrollTimers = { year: null, month: null, day: null };
const isAnimating = { year: false, month: false, day: false };
function getCenteredIndex(el) {
if (!el) return 0;
const raw = (el.scrollTop - wheelCenterPad) / wheelItemHeight;
return Math.round(raw);
}
function clamp(n, min, max) {
return Math.max(min, Math.min(n, max));
}
function getCenteredIndexByRect(el) {
if (!el) return 0;
const items = el.querySelectorAll(".picker-item");
if (!items || items.length === 0) return 0;
const containerRect = el.getBoundingClientRect();
const centerY = containerRect.top + containerRect.height / 2;
let nearestIdx = 0;
let nearestDist = Number.POSITIVE_INFINITY;
for (let i = 0; i < items.length; i++) {
const r = items[i].getBoundingClientRect();
const mid = r.top + r.height / 2;
const dist = Math.abs(mid - centerY);
if (dist < nearestDist) {
nearestDist = dist;
nearestIdx = i;
}
}
return nearestIdx;
}
function handleWheelStep(type, e) {
const refMap = { year: yearColRef, month: monthColRef, day: dayColRef };
const el = refMap[type].value;
if (!el || isAnimating[type]) return;
const direction = e.deltaY > 0 ? 1 : -1;
let idx = getCenteredIndexByRect(el);
if (type === "year") {
idx = clamp(idx + direction, 0, years.value.length - 1);
localYear.value = years.value[idx];
} else if (type === "month") {
idx = clamp(idx + direction, 0, 11);
localMonth.value = idx + 1;
} else {
const dim = daysInMonth(localYear.value, localMonth.value);
idx = clamp(idx + direction, 0, dim - 1);
localDay.value = idx + 1;
}
isAnimating[type] = true;
isUserScrolling.value = true;
el.scrollTo({ top: idx * wheelItemHeight, behavior: "smooth" });
setTimeout(() => {
isAnimating[type] = false;
isUserScrolling.value = false;
}, 180);
}
function handleWheelScroll(type, e) {
clearTimeout(scrollTimers[type]);
isUserScrolling.value = true;
scrollTimers[type] = setTimeout(() => {
const el = e.target;
const idx = getCenteredIndexByRect(el);
if (type === "year") {
localYear.value = years.value[clamp(idx, 0, years.value.length - 1)];
} else if (type === "month") {
localMonth.value = clamp(idx + 1, 1, 12);
} else if (type === "day") {
const dim = daysInMonth(localYear.value, localMonth.value);
localDay.value = clamp(idx + 1, 1, dim);
}
isUserScrolling.value = false;
}, 120);
}
function confirm() {
// 线 DOM
if (yearColRef.value) {
const idx = clamp(
getCenteredIndexByRect(yearColRef.value),
0,
years.value.length - 1
);
localYear.value = years.value[idx];
}
if (monthColRef.value) {
const idx = clamp(getCenteredIndexByRect(monthColRef.value) + 1, 1, 12);
localMonth.value = idx;
}
if (dayColRef.value) {
const dim = daysInMonth(localYear.value, localMonth.value);
const idx = clamp(getCenteredIndexByRect(dayColRef.value) + 1, 1, dim);
localDay.value = idx;
}
//
const payload = {
year: localYear.value,
month: localMonth.value,
day: localDay.value,
};
emit("update:modelValue", payload);
nextTick(() => emit("confirm", payload));
}
nextTick(syncWheelPositions);
</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: 311 * 5.12px;
background: #fff;
border-radius: 8 * 5.12px;
box-shadow: 0 3 * 5.12px 14 * 5.12px rgba(0, 0, 0, 0.16);
padding: 16 * 5.12px;
}
.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;
}
.picker-columns {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
column-gap: 32 * 5.12px;
align-items: stretch;
justify-items: stretch;
padding: 0;
}
.center-lines {
pointer-events: none;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
z-index: 0;
}
.center-lines .line {
position: absolute;
left: 16 * 5.12px; /* align with design padding */
right: 16 * 5.12px;
height: 1px; /* crisp hairline */
background: #ededed;
}
.center-lines .line:first-child {
top: 0;
}
.center-lines .line:last-child {
bottom: 0;
}
.picker-col {
position: relative;
overflow-y: auto;
z-index: 1; /* paint above guide lines */
scroll-snap-type: y mandatory;
overscroll-behavior: contain;
}
.picker-col::-webkit-scrollbar {
width: 0;
height: 0;
}
.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;
scroll-snap-align: center;
scroll-snap-stop: always;
}
.year-col .picker-item {
justify-content: flex-start;
padding-left: 16 * 5.12px;
}
.month-col .picker-item {
justify-content: center;
}
.day-col .picker-item {
justify-content: flex-end;
padding-right: 16 * 5.12px;
}
.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>

File diff suppressed because it is too large Load Diff

View File

@ -1,94 +1,184 @@
<template>
<div class="historic-data-container" style="margin-bottom: 40px">
<div class="historic-data-container">
<div class="echarts-container">
<customEcharts></customEcharts>
</div>
<div class="header mt-[80px]">
<div class="title">Historical Data</div>
<div class="filter-container">
<!-- <n-dropdown
trigger="click"
:options="periodOptions"
@select="handlePeriodChange"
:value="state.selectedPeriod"
>
<n-button>
{{ state.selectedPeriod }}
<n-icon><chevron-down-outline /></n-icon>
</n-button>
</n-dropdown> -->
<n-dropdown
trigger="click"
:options="durationOptions"
@select="handleDurationChange"
:value="state.selectedDuration"
>
<n-button>
{{ state.selectedDuration }}
<n-icon><chevron-down-outline /></n-icon>
</n-button>
</n-dropdown>
<div class="header">
<!-- 标题区域 -->
<div class="title-section">
<div class="title-decoration"></div>
<div class="title-text">Historical Data</div>
</div>
</div>
<n-data-table
:columns="columns"
:data="paginatedData"
:bordered="false"
:single-line="false"
:scroll-x="600"
/>
<div class="filter-container">
<span class="range-label">Range</span>
<div class="filter-row">
<div
v-for="option in durationOptions"
:key="option.key"
class="filter-option"
:class="{ active: state.selectedDuration === option.key }"
@click="handleDurationChange(option.key)"
>
{{
option.label
.replace(" Months", "m")
.replace(" Years", "Y")
.replace(" Year", "Y")
.replace(" to Date", "TD")
}}
</div>
</div>
</div>
<!-- reports-table from annualreports -->
<div class="reports-table">
<div class="table-container">
<div class="table-header">
<div
class="column"
v-for="col in columns"
:key="col.key"
:style="{
width: col.width ? col.width + 'px' : 'auto',
flex: col.width ? '0 0 ' + col.width + 'px' : '1 0 116px',
'text-align': col.align,
}"
>
{{ col.title }}
</div>
</div>
<div class="reports-list">
<div
class="table-row"
v-for="(row, index) in paginatedData"
:key="index"
>
<div
class="column"
v-for="col in columns"
:key="col.key"
:style="{
width: col.width ? col.width + 'px' : 'auto',
flex: col.width ? '0 0 ' + col.width + 'px' : '1 0 116px',
'text-align': col.align,
}"
>
<span
v-if="col.key === 'change'"
:style="{
color:
parseFloat(row.change) < 0
? '#cf3050'
: parseFloat(row.change) > 0
? '#18a058'
: '',
}"
>
{{ row[col.key] }}
</span>
<span v-else>
{{ row[col.key] }}
</span>
</div>
</div>
</div>
</div>
</div>
<!-- pagination-container from annualreports -->
<div class="pagination-container">
<n-button class="page-btn prev-btn" @click="handlePrevPage">
<n-icon><chevron-back-outline /></n-icon>
</n-button>
<div class="pagination-controls">
<div class="pagination-buttons">
<button
class="page-btn prev-btn"
:disabled="state.currentPage === 1"
@click="goToPrevPage"
>
<svg width="5" height="9" viewBox="0 0 5 9" fill="none">
<path
d="M4 1L1 4.5L4 8"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
<div class="page-info mr-[40px]">
{{ state.currentPage }} of {{ totalPages }}
</div>
<template v-for="page in getVisiblePages()" :key="page">
<button
v-if="page !== '...'"
class="page-btn"
:class="{ active: page === state.currentPage }"
@click="goToPage(page)"
>
{{ page }}
</button>
<button v-else class="page-btn disabled" disabled>...</button>
</template>
<div class="right-controls">
<n-dropdown
trigger="click"
:options="pageSizeOptions"
@select="handlePageSizeChange"
>
<n-button class="rows-dropdown">
{{ state.pageSize }} Rows
<n-icon><chevron-down-outline /></n-icon>
</n-button>
</n-dropdown>
<n-button class="page-btn next-btn" @click="handleNextPage">
<n-icon><chevron-forward-outline /></n-icon>
</n-button>
<button
class="page-btn next-btn"
:disabled="state.currentPage === totalPages"
@click="goToNextPage"
>
<svg width="5" height="9" viewBox="0 0 5 9" fill="none">
<path
d="M1 1L4 4.5L1 8"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</div>
<div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span>
<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 v-if="showPageSizeMenu" class="page-size-menu">
<div
v-for="size in [10, 50, 100, 500, 1000]"
:key="size"
class="page-size-option"
:class="{ active: state.pageSize === size }"
@click="handlePageSizeChange(size)"
>
{{ size }}/page
</div>
</div>
</div>
</div>
</div>
<div class="pagination-info">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of
{{ state.tableData.length }} results
</div>
<div class="back-to-top-link">
<!-- <div class="back-to-top-link">
<a href="#" @click.prevent="scrollToTop">
Back to Top
<n-icon><arrow-up-outline /></n-icon>
</a>
</div>
</div> -->
</div>
</template>
<script setup>
import { NDataTable, NButton, NDropdown, NIcon } from "naive-ui";
import { reactive, onMounted, h, computed } from "vue";
import { NDropdown, NIcon } from "naive-ui";
import { reactive, onMounted, h, computed, ref, watch, onUnmounted } from "vue";
import axios from "axios";
import {
ChevronDownOutline,
ChevronBackOutline,
ChevronForwardOutline,
ArrowUpOutline,
} from "@vicons/ionicons5";
import { ChevronDownOutline, ArrowUpOutline } from "@vicons/ionicons5";
import defaultTableData from "../data";
// console.log('defaultTableData', defaultTableData)
import customEcharts from "@/components/customEcharts/index.vue";
//
@ -103,15 +193,15 @@ const periodOptions = [
const durationOptions = [
{ label: "3 Months", key: "3 Months" },
{ label: "6 Months", key: "6 Months" },
{ label: "Year to Date", key: "Year to Date" },
{ label: "YTD", key: "Year to Date" },
{ label: "1 Year", key: "1 Year" },
{ label: "5 Years", key: "5 Years" },
{ label: "10 Years", key: "10 Years" },
// { label: 'Full History', key: 'Full History', disabled: true },
];
//
const pageSizeOptions = [
{ label: "10", key: 10 },
{ label: "50", key: 50 },
{ label: "100", key: 100 },
{ label: "500", key: 500 },
@ -123,9 +213,12 @@ const state = reactive({
selectedDuration: "6 Months",
tableData: [],
currentPage: 1,
pageSize: 50,
pageSize: 10,
gotoPage: 1,
});
const showPageSizeMenu = ref(false);
//
const totalPages = computed(() => {
return Math.ceil(state.tableData.length / state.pageSize);
@ -145,47 +238,49 @@ const columns = [
key: "date",
align: "left",
fixed: "left",
width: 150,
width: 152,
},
{
title: "Open",
key: "open",
align: "center",
width: 116,
},
{
title: "High",
key: "high",
align: "center",
width: 116,
},
{
title: "Low",
key: "low",
align: "center",
width: 116,
},
{
title: "Close",
key: "close",
align: "center",
width: 116,
},
{
title: "Adj. Close",
key: "adjClose",
align: "center",
width: 116,
},
{
title: "Change",
key: "change",
align: "center",
render(row) {
const value = parseFloat(row.change);
const color = value < 0 ? "#ff4d4f" : value > 0 ? "#52c41a" : "";
return h("span", { style: { color } }, row.change);
},
width: 116,
},
{
title: "Volume",
key: "volume",
align: "center",
width: 116,
},
];
@ -213,26 +308,62 @@ const handleDurationChange = (key) => {
getPageData();
};
const displayRange = computed(() => {
const start = (state.currentPage - 1) * state.pageSize + 1;
const end = Math.min(
state.currentPage * state.pageSize,
state.tableData.length
);
return { start, end };
});
const goToPage = (page) => {
if (page >= 1 && page <= totalPages.value) {
state.currentPage = page;
}
};
const goToPrevPage = () => {
if (state.currentPage > 1) {
state.currentPage--;
}
};
const goToNextPage = () => {
if (state.currentPage < totalPages.value) {
state.currentPage++;
}
};
//
const handlePrevPage = () => {
if (state.currentPage === 1) {
return;
}
state.currentPage--;
};
const handleNextPage = () => {
if (state.currentPage >= totalPages.value) {
return;
}
state.currentPage++;
};
const handlePageSizeChange = (size) => {
state.pageSize = size;
state.currentPage = 1; //
};
const handleGoto = () => {
const page = parseInt(state.gotoPage);
if (page >= 1 && page <= totalPages.value) {
state.currentPage = page;
}
};
const togglePageSizeMenu = () => {
showPageSizeMenu.value = !showPageSizeMenu.value;
};
const getVisiblePages = () => {
const total = totalPages.value;
if (total <= 4) {
const pages = [];
for (let i = 1; i <= total; i++) {
pages.push(i);
}
return pages;
}
return [1, 2, "...", total];
};
//
const scrollToTop = () => {
//
@ -248,8 +379,32 @@ const scrollToTop = () => {
};
onMounted(() => {
getPageData();
document.addEventListener("click", handleClickOutside);
});
onUnmounted(() => {
document.removeEventListener("click", handleClickOutside);
});
const handleClickOutside = (event) => {
if (!event.target.closest(".page-size-selector")) {
showPageSizeMenu.value = false;
}
};
watch(
() => state.pageSize,
() => {
state.currentPage = 1;
}
);
watch(
() => state.currentPage,
(newPage) => {
state.gotoPage = newPage;
}
);
const getPageDefaultData = async () => {
try {
let url =
@ -341,24 +496,25 @@ const getPageData = async () => {
String(fromDate.getMonth() + 1).padStart(2, "0") +
"-" +
String(fromDate.getDate()).padStart(2, "0");
// let url = `https://stockanalysis.com/api/symbol/a/OTC-MINM/history?period=${state.selectedPeriod}&range=${range}`
let url =
"https://common.szjixun.cn/api/stock/history/list?from=" +
finalFromDate +
"&to=" +
toDate;
const res = await axios.get(url);
// console.error(res)
if (res.status === 200) {
if (res.data.status === 0) {
// "Nov 26, 2024"
let resultData = res.data.data.map((item) => {
return {
date: new Date(item.date).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
}),
date: new Date(item.date.replace(/-/g, "/")).toLocaleDateString(
"en-US",
{
month: "short",
day: "numeric",
year: "numeric",
}
),
open: item.open != null ? Number(item.open).toFixed(2) : "",
high: item.high != null ? Number(item.high).toFixed(2) : "",
low: item.low != null ? Number(item.low).toFixed(2) : "",
@ -379,82 +535,83 @@ const getPageData = async () => {
<style scoped lang="scss">
.historic-data-container {
padding: 80px;
width: 343 * 5.12px;
margin: 0 auto;
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
.title {
font-size: 80px;
font-size: 40 * 2.5 * 5.12px;
font-weight: bold;
margin: 0;
}
.filter-container {
display: flex;
gap: 40px;
}
}
.pagination-container {
.filter-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 60px;
padding: 10px 16px;
border-radius: 4px;
background-color: #ffffff;
flex-direction: column;
align-items: flex-start;
gap: 8 * 5.12px;
padding: 0 16 * 5.12px;
margin-bottom: 32 * 5.12px;
.page-btn {
display: flex;
align-items: center;
gap: 5px;
padding: 6px 12px;
font-size: 92px;
&.prev-btn {
margin-right: auto;
}
&.next-btn {
margin-left: 10px;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.range-label {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
letter-spacing: 0.48 * 5.12px;
color: #455363;
}
.page-info {
font-size: 72px;
color: #374151;
.filter-row {
width: 311 * 5.12px;
display: flex;
flex-wrap: wrap;
column-gap: 16 * 5.12px;
row-gap: 8 * 5.12px;
}
.right-controls {
.filter-option {
display: flex;
align-items: center;
justify-content: center;
height: 34 * 5.12px;
border-radius: 3 * 5.12px;
background-color: #efefef;
cursor: pointer;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #000000;
transition: all 0.2s ease;
width: 93 * 5.12px;
.rows-dropdown {
font-size: 72px;
&:hover {
background-color: #e0e0e0;
}
&.active {
background-color: #ff7bac;
color: #ffffff;
}
}
}
.back-to-top-link {
display: flex;
justify-content: center;
margin-top: 56px;
margin-top: 16 * 5.12px;
a {
display: flex;
align-items: center;
gap: 5px;
gap: 5 * 5.12px;
color: #2563eb;
font-size: 92px;
font-size: 20 * 5.12px;
font-weight: bold;
text-decoration: none;
@ -463,11 +620,300 @@ const getPageData = async () => {
}
}
}
}
:deep(.n-data-table) {
.n-data-table-td {
padding: 12px 8px;
}
.reports-table {
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);
padding: 16 * 5.12px;
}
.table-container {
width: 100%;
overflow-x: auto;
}
.table-container::-webkit-scrollbar {
height: 8 * 5.12px;
}
.table-container::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4 * 5.12px;
}
.table-container::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 4 * 5.12px;
}
.table-container::-webkit-scrollbar-thumb:hover {
background: #fff0f5;
cursor: pointer;
}
.column {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
letter-spacing: 0.48 * 5.12px;
color: #455363;
padding: 16 * 5.12px;
position: relative;
font-variant-numeric: tabular-nums; /* 让数字等宽对齐 */
}
.table-header {
display: flex;
border-radius: 8 * 5.12px;
margin-bottom: 4 * 5.12px;
align-items: center;
position: sticky;
top: 0;
z-index: 2;
.column {
background: #fff0f5;
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
line-height: 1.4;
letter-spacing: 0.48 * 5.12px;
color: #000000;
}
}
.table-row {
display: flex;
align-items: center;
position: relative;
border-radius: 8 * 5.12px;
&:hover .column {
background: #fff8fb;
}
// &:last-child {
// border-bottom: none;
// }
&:nth-child(even) {
margin: 4 * 5.12px 0;
}
}
.reports-list {
display: flex;
flex-direction: column;
gap: 4 * 5.12px;
}
.table-row .column:not(:last-child)::after {
content: "";
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
height: 40 * 5.12px;
border-right: 1 * 5.12px dashed #e0e0e6;
}
.table-header .column:first-child {
border-radius: 8 * 5.12px 0 0 8 * 5.12px;
}
.table-header .column:last-child,
.table-row .column:last-child {
border-radius: 0 8 * 5.12px 8 * 5.12px 0;
}
.table-row .column:first-child {
background: #ffffff;
}
.table-row:hover .column:first-child {
background: #fff8fb;
}
//
.pagination-container {
display: flex;
align-items: center;
margin-top: 16 * 5.12px;
justify-content: flex-end;
padding: 0 4 * 5.12px;
}
.pagination-info {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #455363;
text-align: right;
margin-top: 16 * 5.12px;
margin-bottom: 16 * 5.12px;
padding: 0 4 * 5.12px;
}
.pagination-controls {
display: flex;
align-items: center;
gap: 8 * 5.12px;
}
.pagination-buttons {
display: flex;
align-items: center;
gap: 8 * 5.12px;
}
.page-btn {
display: flex;
align-items: center;
justify-content: center;
width: 28 * 5.12px;
height: 28 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
background: #ffffff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #455363;
cursor: pointer;
transition: all 0.2s ease;
&:hover:not(:disabled) {
border-color: #ff7bac;
color: #ff7bac;
}
&.active {
border-color: #ff7bac;
color: #ff7bac;
background: #fff0f5;
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
&.disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
.page-size-selector {
position: relative;
display: flex;
align-items: center;
gap: 18 * 5.12px;
padding: 4 * 5.12px 12 * 5.12px;
height: 28 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
background: #ffffff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #455363;
cursor: pointer;
&:hover {
border-color: #ff7bac;
}
}
.page-size-menu {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
background: #ffffff;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
box-shadow: 0 2 * 5.12px 8 * 5.12px rgba(0, 0, 0, 0.1);
z-index: 1000;
margin-bottom: 5 * 5.12px;
}
.page-size-option {
padding: 8 * 5.12px 12 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #455363;
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background: #fff0f5;
}
&.active {
background: #fff0f5;
color: #ff7bac;
}
}
.goto-section {
display: flex;
align-items: center;
gap: 8 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #455363;
margin-right: 16 * 5.12px;
}
.goto-input {
width: 60 * 5.12px;
height: 28 * 5.12px;
padding: 4 * 5.12px 12 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4;
color: #455363;
text-align: center;
&:focus {
outline: none;
border-color: #ff7bac;
}
}
.title-section {
display: flex;
flex-direction: column;
gap: 16 * 5.12px;
margin-bottom: 32 * 5.12px;
margin-top: 43 * 5.12px;
padding: 0 16 * 5.12px;
}
.title-decoration {
width: 58 * 5.12px;
height: 7 * 5.12px;
background: #ff7bac;
margin: auto 0;
margin-top: 0;
}
.title-text {
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 24 * 5.12px;
line-height: 1.4;
letter-spacing: 0.03em;
color: #000000;
}
</style>

View File

@ -1,59 +1,80 @@
<template>
<div class="press-releases-page">
<n-infinite-scroll :distance="0" @load="doLoadMore">
<main class="p-[80px] mx-auto" style="max-width: 100vw; min-width: 285px">
<div class="title mb-[24px]">
{{ t("press_releases.title") }}
</div>
<div class="search-container">
<n-select
:options="state.selectOptions"
v-model:value="state.selectedValue"
class="search-select"
:font-size="72"
/>
<n-input
v-model:value="state.inputValue"
type="text"
:placeholder="t('press_releases.search.placeholder')"
class="search-input"
clearable
:font-size="72"
/>
<n-button @click="handleSearch" class="search-button" :font-size="72">
{{ t("press_releases.search.button") }}
</n-button>
</div>
<div v-for="(item, idx) in state.filterNewsData" :key="idx">
<div class="news-item mt-[10px]">
<div class="news-item-date">{{ item.date }}</div>
<div
class="news-item-title text-[#0078d7] cursor-pointer"
style="
word-break: break-word;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
"
@click="handleNewClick(item)"
>
{{ item.title }}
<div class="title-section">
<div class="title-decoration"></div>
<div class="title">
{{ t("press_releases.title") }}
</div>
</div>
<div class="search-container">
<n-select
:options="state.selectOptions"
v-model:value="state.selectedValue"
class="search-select"
/>
<input
v-model="state.inputValue"
type="text"
:placeholder="t('press_releases.search.placeholder')"
class="search-input"
/>
<button @click="handleSearch" class="search-button">
{{ t("press_releases.search.button") }}
</button>
</div>
<div class="reports-list">
<div
v-for="(item, idx) in state.filterNewsData"
:key="idx"
class="news-item table-row"
>
<div class="content">
<div class="file-content">
<div class="file-info">
<div class="vertical-line"></div>
<div
class="news-item-title text-[#000] cursor-pointer"
style="
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
"
@click="handleNewClick(item)"
>
{{ item.title }}
</div>
<svg
class="arrow-icon"
width="7"
height="14"
viewBox="0 0 7 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@click="handleNewClick(item)"
>
<path
d="M1 1L6 7L1 13"
stroke="#FF7BAC"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<n-tooltip
trigger="click"
:disabled="!item.showTooltip"
width="trigger"
>
<n-tooltip trigger="hover" :disabled="true" width="trigger">
<template #trigger>
<div
:ref="(el) => setTitleRef(el, idx)"
class="news-item-content"
class="news-item-content file-description"
style="
word-break: break-word;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
@ -66,17 +87,118 @@
{{ item.summary }}
</div>
</n-tooltip>
<div class="download-section">
<div class="news-item-date">{{ item.date }}</div>
</div>
</div>
</div>
</main>
</n-infinite-scroll>
<div class="separator-line"></div>
</div>
</div>
<!-- 分页器 -->
<div class="pagination-container" v-if="state.total > 0">
<div class="pagination-controls">
<div class="pagination-buttons">
<button
class="page-btn prev-btn"
:disabled="state.currentPage === 1"
@click="goToPrevPage"
>
<svg width="5" height="9" viewBox="0 0 5 9" fill="none">
<path
d="M4 1L1 4.5L4 8"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
<template v-for="page in getVisiblePages()" :key="page">
<button
v-if="page !== '...'"
class="page-btn"
:class="{ active: page === state.currentPage }"
@click="goToPage(page)"
>
{{ page }}
</button>
<button v-else class="page-btn disabled" disabled>...</button>
</template>
<button
class="page-btn next-btn"
:disabled="state.currentPage === totalPages"
@click="goToNextPage"
>
<svg width="5" height="9" viewBox="0 0 5 9" fill="none">
<path
d="M1 1L4 5L1 8"
stroke="#455363"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
</div>
<div class="page-size-selector" @click="togglePageSizeMenu">
<span>{{ state.pageSize }}/page</span>
<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 v-if="showPageSizeMenu" class="page-size-menu">
<div
v-for="size in [10, 20, 50]"
:key="size"
class="page-size-option"
:class="{ active: state.pageSize === size }"
@click="changePageSize(size)"
>
{{ size }}/page
</div>
</div>
</div>
<div class="goto-section">
<span>Goto</span>
<input
type="number"
v-model="state.gotoPage"
class="goto-input"
:min="1"
:max="totalPages"
@keyup.enter="handleGoto"
/>
</div>
</div>
</div>
<div class="pagination-info" v-if="state.total > 0">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of
{{ state.total }} results
</div>
</div>
</template>
<script setup>
import customDefaultPage from "@/components/customDefaultPage/index.vue";
import { reactive, onMounted, watch, nextTick, ref } from "vue";
import { NSelect, NInput, NButton, NInfiniteScroll, NTooltip } from "naive-ui";
import {
reactive,
onMounted,
watch,
nextTick,
ref,
computed,
onUnmounted,
} from "vue";
import { NSelect, NInput, NButton, NTooltip } from "naive-ui";
import { useI18n } from "vue-i18n";
import axios from "axios";
@ -97,32 +219,16 @@ const state = reactive({
}),
], //
inputValue: "", //
newsData: [
{
date: "June 3, 2025",
title: "FiEE, Inc. seized market opportunities through 2025 Osaka Expo",
content:
"Hong Kong, 3 June 2025 — FiEE, Inc. (NASDAQ:FIEE) (“FiEE, Inc.” or the “Company”), a technology company integrating IoT, connectivity and AI to redefine brand management solutions in the digital era, is pleased to announce significant business updates....",
},
{
date: "June 2, 2025",
title: "FiEE, Inc. Closes Its First Day of Trading on NASDAQ",
content:
"Hong Kong, 2 June 2025 — FiEE, Inc. (NASDAQ:FIEE) (“FiEE, Inc.” or the “Company”), a technology company integrating IoT, connectivity and AI to redefine brand management solutions in the digital era, commenced...",
},
{
date: "May 30, 2025",
title: "FiEE, Inc. Announces Reinitiation of Trading on Nasdaq",
content:
"Hong Kong, May 30, 2025 — FiEE, Inc. (“FiEE, Inc.” or the “Company”), a technology company integrating IoT, connectivity and AI to redefine brand management solutions...",
},
],
filterNewsData: [],
loading: false, //
hasMore: true, //
currentPage: 1, //
pageSize: 10,
total: 0,
gotoPage: 1,
});
const showPageSizeMenu = ref(false);
const titleRefs = ref([]);
const setTitleRef = (el, idx) => {
@ -142,14 +248,18 @@ const checkAllTitleOverflow = () => {
};
onMounted(() => {
// state.filterNewsData = state.newsData;
getPressReleasesDisplay();
document.addEventListener("click", handleClickOutside);
nextTick(() => {
checkAllTitleOverflow();
});
});
onUnmounted(() => {
document.removeEventListener("click", handleClickOutside);
});
watch(
() => state.filterNewsData,
() => {
@ -162,93 +272,68 @@ watch(
//
const getPressReleasesDisplay = () => {
state.loading = true;
let url = "https://erpapi.fiee.com/api/fiee/pressreleases/display";
let params = {
query: state.inputValue,
page: state.currentPage,
pageSize: 10,
pageSize: state.pageSize,
timeStart: state.selectedValue
? state.selectedValue === "all_years"
? null
: new Date(state.selectedValue).getTime()
: null,
};
// console.log(params)
axios.post(url, params).then((res) => {
// console.log(res)
if (res.status === 200) {
if (res.data.status === 0) {
res.data.data?.data?.forEach((item) => {
item.date = new Date(item.createdAt).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
axios
.post(url, params)
.then((res) => {
if (res.status === 200) {
if (res.data.status === 0) {
res.data.data?.data?.forEach((item) => {
item.date = new Date(item.createdAt).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
});
});
if (state.currentPage === 1) {
state.filterNewsData = res.data.data?.data || [];
} else {
state.filterNewsData = [
...state.filterNewsData,
...(res.data.data?.data || []),
];
}
if (state.filterNewsData.length < (res.data.data?.total || 0)) {
state.hasMore = true;
} else {
state.hasMore = false;
state.total = res.data.data?.total || 0;
}
}
}
});
};
const handleFilter = () => {
//
let filteredData = [...state.newsData];
//
if (state.selectedValue !== "all_years") {
filteredData = filteredData.filter((item) => {
// "May 30, 2025"
const dateMatch = item.date.match(/\b\d{4}\b/);
if (dateMatch) {
const year = dateMatch[0];
return year === state.selectedValue;
}
return false;
})
.finally(() => {
state.loading = false;
});
}
// title content
if (state.inputValue && state.inputValue.trim() !== "") {
const searchText = state.inputValue.toLowerCase().trim();
filteredData = filteredData.filter((item) => {
const titleMatch = item.title.toLowerCase().includes(searchText);
const contentMatch = item.content.toLowerCase().includes(searchText);
return titleMatch || contentMatch;
});
}
state.filterNewsData = filteredData;
};
// watcher
watch(
() => [state.selectedValue, state.inputValue],
() => {
// handleFilter();
state.currentPage = 1;
getPressReleasesDisplay();
}
);
watch(
() => state.pageSize,
() => {
state.currentPage = 1;
getPressReleasesDisplay();
}
);
watch(
() => state.currentPage,
(newPage) => {
state.gotoPage = newPage;
getPressReleasesDisplay();
}
);
const handleSearch = () => {
//
// handleFilter();
state.currentPage = 1;
getPressReleasesDisplay();
// console.log(":", state.filterNewsData);
};
const handleNewClick = (item) => {
@ -260,102 +345,468 @@ const handleNewClick = (item) => {
});
};
//
const doLoadMore = () => {
if (!state.hasMore || state.loading) {
return;
const totalPages = computed(() => {
return Math.ceil(state.total / state.pageSize) || 1;
});
const displayRange = computed(() => {
if (state.total === 0) return { start: 0, end: 0 };
const start = (state.currentPage - 1) * state.pageSize + 1;
const end = Math.min(state.currentPage * state.pageSize, state.total);
return { start, end };
});
const goToPage = (page) => {
if (page >= 1 && page <= totalPages.value) {
state.currentPage = page;
}
};
const goToPrevPage = () => {
if (state.currentPage > 1) {
state.currentPage--;
}
};
const goToNextPage = () => {
if (state.currentPage < totalPages.value) {
state.currentPage++;
}
};
const changePageSize = (size) => {
state.pageSize = size;
};
const handleGoto = () => {
const page = parseInt(state.gotoPage);
if (page >= 1 && page <= totalPages.value) {
state.currentPage = page;
}
};
const togglePageSizeMenu = () => {
showPageSizeMenu.value = !showPageSizeMenu.value;
};
const getVisiblePages = () => {
const current = state.currentPage;
const total = totalPages.value;
const pages = [];
if (total <= 7) {
for (let i = 1; i <= total; i++) {
pages.push(i);
}
} else {
pages.push(1);
if (current <= 4) {
for (let i = 2; i <= 5; i++) {
pages.push(i);
}
pages.push("...");
pages.push(total);
} else if (current >= total - 3) {
pages.push("...");
for (let i = total - 4; i <= total; i++) {
pages.push(i);
}
} else {
pages.push("...");
for (let i = current - 1; i <= current + 1; i++) {
pages.push(i);
}
pages.push("...");
pages.push(total);
}
}
return pages;
};
//
const handleClickOutside = (event) => {
if (!event.target.closest || !event.target.closest(".page-size-selector")) {
showPageSizeMenu.value = false;
}
// console.log('')
state.loading = true;
state.currentPage++;
getPressReleasesDisplay().finally(() => {
state.loading = false;
});
};
</script>
<style scoped lang="scss">
.press-releases-page {
width: 343 * 5.12px;
margin: 0 auto;
background: #fff;
}
.title-section {
display: flex;
flex-direction: column;
gap: 16 * 5.12px;
padding: 0 16 * 5.12px;
}
.title-decoration {
width: 58 * 5.12px;
height: 7 * 5.12px;
background: #ff7bac;
margin-top: 43 * 5.12px;
}
.title {
font-size: 113px;
font-weight: bold;
color: #333;
text-align: center;
margin-top: 8px;
font-size: 32 * 5.12px;
color: #000;
}
.search-container {
margin-bottom: 24px;
margin-top: 32 * 5.12px;
margin-bottom: 20 * 5.12px;
display: flex;
flex-direction: row;
flex-direction: column;
align-items: center;
background-color: #f6f7f9;
border-radius: 8px;
padding: 8px;
gap: 16px;
justify-content: flex-start;
gap: 16 * 5.12px;
padding: 0 16 * 5.12px;
}
.search-select {
width: 1000px;
:deep(.n-base-selection) {
padding: 4px 0;
}
width: 100%;
height: 34 * 5.12px;
}
.search-input {
width: 100%;
height: 34 * 5.12px;
padding: 7 * 5.12px 12 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.375em;
letter-spacing: 0.48 * 5.12px;
color: #455363;
&::placeholder {
color: #b6b6b6;
}
}
:deep(.n-input) {
.n-input__input {
padding: 4px 0;
border-radius: 4px;
padding: 4 * 5.12px 0;
// border: 1*5.12px solid #ccc;
border-radius: 4 * 5.12px;
}
}
:deep(.n-select) {
.n-select__input {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 8 * 5.12px 12 * 5.12px;
border: 1 * 5.12px solid #ccc;
border-radius: 4 * 5.12px;
}
}
:deep(.n-button) {
width: 260px;
padding: 20px 16px;
border-radius: 4px;
}
.news-item {
padding: 16px;
border-bottom: 1px solid #eee;
margin-bottom: 16px;
}
.news-item-date {
font-size: 72px;
color: #666;
margin-bottom: 8px;
}
.news-item-title {
font-size: 92px;
font-weight: bold;
margin-bottom: 8px;
line-height: 1.4;
}
.news-item-content {
font-size: 72px;
color: #333;
line-height: 1.6;
padding: 20 * 5.12px 16 * 5.12px;
border-radius: 4 * 5.12px;
}
.search-button {
width: 100%;
height: 34 * 5.12px;
padding: 7 * 5.12px 12 * 5.12px;
background: #ff7bac;
color: #fff;
border: none;
border-radius: 3 * 5.12px;
cursor: pointer;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.375em;
letter-spacing: 0.48 * 5.12px;
&:hover {
background: #ff7bac;
color: #fff;
}
}
.reports-list {
display: flex;
flex-direction: column;
gap: 4 * 5.12px;
background: #fff;
width: 100%;
padding: 0 16 * 5.12px;
margin-top: 40 * 5.12px;
}
.table-row {
display: flex;
flex-direction: column;
position: relative;
border-radius: 8 * 5.12px;
// &:last-child {
// .separator-line {
// display: none;
// }
// }
}
.content {
flex: 1;
display: flex;
flex-direction: column;
gap: 16 * 5.12px;
}
.table-row .content {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 0;
&:hover {
background: #fff8fb;
}
}
.file-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.file-info {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16 * 5.12px;
flex: 1;
}
.news-item-title {
font-family: "PingFang SC", sans-serif;
font-weight: 500;
font-size: 14 * 5.12px;
line-height: 1.375em;
letter-spacing: 0.48 * 5.12px;
color: #000000;
}
.arrow-icon {
margin-left: auto;
flex-shrink: 0;
cursor: pointer;
}
.vertical-line {
width: 1 * 5.12px;
height: 20 * 5.12px;
background: #ff7bac;
flex-shrink: 0;
}
.file-description {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.375em;
letter-spacing: 0.48 * 5.12px;
color: #455363;
margin: 0;
padding: 0;
margin-top: 8 * 5.12px;
}
.news-item-date {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.375em;
letter-spacing: 0.48 * 5.12px;
color: #455363;
}
.download-section {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
padding: 4 * 5.12px 0 * 5.12px;
}
.separator-line {
width: 100%;
height: 1 * 5.12px;
background: repeating-linear-gradient(
to right,
#e6eaee 0 * 5.12px,
#e6eaee 2 * 5.12px,
transparent 2 * 5.12px,
transparent 4 * 5.12px
);
margin-top: 16 * 5.12px;
}
//
.pagination-container {
display: flex;
align-items: center;
margin: 40 * 5.12px 0;
justify-content: flex-end;
padding: 0 16 * 5.12px;
flex-wrap: wrap;
gap: 16 * 5.12px;
}
.pagination-info {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.4375em;
color: #455363;
text-align: right;
margin-bottom: 30 * 5.12px;
padding: 0 16 * 5.12px;
width: 100%;
}
.pagination-controls {
display: flex;
align-items: center;
gap: 8 * 5.12px;
flex-wrap: wrap;
justify-content: flex-end;
}
.pagination-buttons {
display: flex;
align-items: center;
gap: 8 * 5.12px;
}
.page-btn {
display: flex;
align-items: center;
justify-content: center;
width: 28 * 5.12px;
height: 28 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
background: #ffffff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #455363;
cursor: pointer;
transition: all 0.2s ease;
&:hover:not(:disabled) {
border-color: #ff7bac;
color: #ff7bac;
}
&.active {
border-color: #ff7bac;
color: #ff7bac;
background: #fff0f5;
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
&.disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
.page-size-selector {
position: relative;
display: flex;
align-items: center;
gap: 18 * 5.12px;
padding: 4 * 5.12px 12 * 5.12px;
height: 28 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
background: #ffffff;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #455363;
cursor: pointer;
&:hover {
border-color: #ff7bac;
}
}
.page-size-menu {
position: absolute;
bottom: 100%;
left: 0;
right: 0;
background: #ffffff;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
box-shadow: 0 2 * 5.12px 8 * 5.12px rgba(0, 0, 0, 0.1);
z-index: 1000;
margin-bottom: 2 * 5.12px;
}
.page-size-option {
padding: 8 * 5.12px 12 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #455363;
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background: #fff0f5;
}
&.active {
background: #fff0f5;
color: #ff7bac;
}
}
.goto-section {
display: flex;
align-items: center;
gap: 8 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #455363;
}
.goto-input {
width: 60 * 5.12px;
height: 28 * 5.12px;
padding: 4 * 5.12px 12 * 5.12px;
border: 1 * 5.12px solid #e0e0e6;
border-radius: 3 * 5.12px;
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #455363;
text-align: center;
&:focus {
outline: none;
border-color: #ff7bac;
}
}
</style>