fiee-official-website/src/views/press-releases/size375/index.vue
2025-10-14 09:42:08 +08:00

850 lines
20 KiB
Vue

<template>
<div class="press-releases-page">
<div class="title-section">
<div class="title-decoration"></div>
<div class="title">
{{ t("press_releases.title") }}
</div>
</div>
<div class="search-container">
<div class="search-select" @click="openYearPicker">
<div class="search-select-label">Year</div>
<div class="search-select-icon">
<span class="selected-year-label">{{ selectedYearLabel }}</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>
<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="hover" :disabled="true" width="trigger">
<template #trigger>
<div
:ref="(el) => setTitleRef(el, idx)"
class="news-item-content file-description"
style="
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
"
>
{{ item.summary }}
</div>
</template>
<div slot="content">
{{ item.summary }}
</div>
</n-tooltip>
<div class="download-section">
<div class="news-item-date">{{ item.date }}</div>
</div>
</div>
</div>
<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>
</div>
<div class="pagination-info" v-if="state.total > 0">
Displaying {{ displayRange.start }} - {{ displayRange.end }} of
{{ state.total }} results
</div>
</div>
<year-wheel-picker
v-if="state.showYearPicker"
v-model="state.selectedValue"
:options="state.selectOptions"
@close="closeYearPicker"
@confirm="onYearConfirm"
></year-wheel-picker>
</template>
<script setup>
import customDefaultPage from "@/components/customDefaultPage/index.vue";
import YearWheelPicker from "@/components/YearWheelPicker.vue";
import {
reactive,
onMounted,
watch,
nextTick,
ref,
computed,
onUnmounted,
} from "vue";
import { NInput, NButton, NTooltip } from "naive-ui";
import { useI18n } from "vue-i18n";
import axios from "axios";
import { useRouter } from "vue-router";
const router = useRouter();
const { t } = useI18n();
const state = reactive({
selectedValue: "all_years", //选中值
selectOptions: [
{
label: "All Years",
value: "all_years",
},
...Array.from({ length: 2025 - 1990 + 1 }, (_, i) => {
const year = 2025 - i;
return { label: String(year), value: String(year) };
}),
], //下拉选项
inputValue: "", //输入值
filterNewsData: [],
loading: false, //是否正在加载数据
currentPage: 1, //当前页码
pageSize: 10,
total: 0,
gotoPage: 1,
showYearPicker: false,
});
const showPageSizeMenu = ref(false);
const titleRefs = ref([]);
const selectedYearLabel = computed(() => {
const option = state.selectOptions.find(
(opt) => opt.value === state.selectedValue
);
return option ? option.label : "";
});
const openYearPicker = () => {
state.showYearPicker = true;
};
const closeYearPicker = () => {
state.showYearPicker = false;
};
const onYearConfirm = (year) => {
state.selectedValue = year;
closeYearPicker();
};
const setTitleRef = (el, idx) => {
if (el) titleRefs.value[idx] = el;
};
const checkAllTitleOverflow = () => {
state.filterNewsData.forEach((item, idx) => {
const el = titleRefs.value[idx];
if (!el) {
item.showTooltip = false;
return;
}
item.showTooltip =
el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;
});
};
onMounted(() => {
getPressReleasesDisplay();
document.addEventListener("click", handleClickOutside);
nextTick(() => {
checkAllTitleOverflow();
});
});
onUnmounted(() => {
document.removeEventListener("click", handleClickOutside);
});
watch(
() => state.filterNewsData,
() => {
nextTick(() => {
checkAllTitleOverflow();
});
},
{ deep: true }
);
// 获取新闻列表
const getPressReleasesDisplay = () => {
state.loading = true;
let url = "https://erpapi.fiee.com/api/fiee/pressreleases/display";
let params = {
query: state.inputValue,
page: state.currentPage,
pageSize: state.pageSize,
timeStart: state.selectedValue
? state.selectedValue === "all_years"
? null
: new Date(state.selectedValue).getTime()
: null,
};
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",
});
});
state.filterNewsData = res.data.data?.data || [];
state.total = res.data.data?.total || 0;
}
}
})
.finally(() => {
state.loading = false;
});
};
// 添加 watcher 来实现自动筛选
watch(
() => [state.selectedValue, state.inputValue],
() => {
state.currentPage = 1;
getPressReleasesDisplay();
}
);
watch(
() => state.pageSize,
() => {
state.currentPage = 1;
getPressReleasesDisplay();
}
);
watch(
() => state.currentPage,
(newPage) => {
state.gotoPage = newPage;
getPressReleasesDisplay();
}
);
const handleSearch = () => {
state.currentPage = 1;
getPressReleasesDisplay();
};
const handleNewClick = (item) => {
router.push({
path: "/news",
query: {
id: item.id,
},
});
};
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 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 handleClickOutside = (event) => {
if (!event.target.closest || !event.target.closest(".page-size-selector")) {
showPageSizeMenu.value = 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: 32 * 5.12px;
color: #000;
}
.search-container {
margin-top: 32 * 5.12px;
margin-bottom: 20 * 5.12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
flex-flow: wrap;
gap: 16 * 5.12px;
padding: 0 16 * 5.12px;
}
.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;
}
.search-select-icon {
display: flex;
align-items: center;
}
.search-input {
width: 191 * 5.12px;
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: 4 * 5.12px 0;
// border: 1*5.12px solid #ccc;
border-radius: 4 * 5.12px;
}
}
:deep(.n-select) {
.n-select__input {
padding: 8 * 5.12px 12 * 5.12px;
border: 1 * 5.12px solid #ccc;
border-radius: 4 * 5.12px;
}
}
:deep(.n-button) {
padding: 20 * 5.12px 16 * 5.12px;
border-radius: 4 * 5.12px;
}
.search-button {
width: 104 * 5.12px;
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%;
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-right: 16 * 5.12px;
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: 8 * 5.12px 0;
padding: 0 16 * 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: 0 16 * 5.12px;
margin-bottom: 8 * 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: 16 * 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;
}
}
.selected-year-label {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #000;
margin-right: 16 * 5.12px;
}
.search-select-label {
font-family: "PingFang SC", sans-serif;
font-weight: 400;
font-size: 14 * 5.12px;
line-height: 1.428em;
color: #000;
}
</style>