2025-08-10 16:44:02 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="news-rank-list">
|
2025-09-04 16:22:13 +08:00
|
|
|
|
<u-skeleton rows="5" title loading style="margin-bottom: 30rpx" v-if="loading"></u-skeleton>
|
2025-08-10 16:44:02 +08:00
|
|
|
|
<view class="list" v-else>
|
2025-09-04 16:22:13 +08:00
|
|
|
|
<view v-for="(item, index) in rankListLocal" :key="index" class="news-item" @click="goDetail(item, 0)">
|
2025-08-10 16:44:02 +08:00
|
|
|
|
<view class="rank-tag">
|
|
|
|
|
|
<view v-if="index == 0" class="rank-text-top3">
|
|
|
|
|
|
<img src="@/assets/zixun/ranking_icon_1.png" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="index == 1" class="rank-text-top3">
|
|
|
|
|
|
<img src="@/assets/zixun/ranking_icon_2.png" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="index == 2" class="rank-text-top3">
|
|
|
|
|
|
<img src="@/assets/zixun/ranking_icon_3.png" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="rank-text" v-else>{{ index + 1 }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2025-09-04 16:22:13 +08:00
|
|
|
|
<view class="news-content" :style="{ filter: Session.get('token') ? '' : 'blur(5px)' }">
|
2025-08-10 16:44:02 +08:00
|
|
|
|
<text class="news-title">{{ item.title }}</text>
|
|
|
|
|
|
<text class="news-desc">{{ item.summary }}</text>
|
2025-09-12 10:40:08 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 两个标签 start -->
|
2025-09-13 15:05:05 +08:00
|
|
|
|
<view class="r_r_tags" v-if="hasTag">
|
2025-09-12 10:40:08 +08:00
|
|
|
|
<view style="display: flex; margin-top: 20rpx; overflow-x: auto; width: 95vw">
|
|
|
|
|
|
<view class="r_tags">
|
2026-02-04 14:04:02 +08:00
|
|
|
|
<view
|
|
|
|
|
|
class="tag"
|
|
|
|
|
|
style="background-color: #fff9ec; color: #ffb100"
|
|
|
|
|
|
v-for="(item, index) in item.conceptLabels"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
>{{ item }}</view
|
|
|
|
|
|
>
|
2025-09-12 10:40:08 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view style="display: flex; margin-top: 20rpx; overflow-x: auto; width: 100vw">
|
|
|
|
|
|
<view class="r_tags">
|
2026-02-04 14:04:02 +08:00
|
|
|
|
<view
|
|
|
|
|
|
class="tag"
|
|
|
|
|
|
style="background-color: #f5f8fe; color: #007aff"
|
|
|
|
|
|
v-for="(item, index) in item.industryLabels"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
>{{ item }}</view
|
|
|
|
|
|
>
|
2025-09-12 10:40:08 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<!-- 两个标签 end -->
|
|
|
|
|
|
|
2025-08-10 16:44:02 +08:00
|
|
|
|
<view class="news-meta">
|
|
|
|
|
|
<view>
|
2026-02-04 14:04:02 +08:00
|
|
|
|
<text class="source">中国证券报</text>
|
2025-09-17 14:52:18 +08:00
|
|
|
|
<!-- <text class="time">{{ dayjs(item.publish_time).format("YYYY-MM-DD HH:MM:ss") }}</text> -->
|
|
|
|
|
|
<!-- .format('YYYY-MM-DD HH:mm:ss'); -->
|
|
|
|
|
|
<text class="time">
|
|
|
|
|
|
{{ timeFormat(new Date(item.publish_time).getTime()) }}
|
|
|
|
|
|
</text>
|
2025-08-10 16:44:02 +08:00
|
|
|
|
</view>
|
2025-09-04 17:35:10 +08:00
|
|
|
|
<text class="score" v-if="needExp">
|
2025-08-10 16:44:02 +08:00
|
|
|
|
<text v-if="index < 3">资讯评分:</text>
|
2026-02-04 14:04:02 +08:00
|
|
|
|
{{ item.news_score }}</text
|
|
|
|
|
|
>
|
2025-08-10 16:44:02 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2025-09-04 16:22:13 +08:00
|
|
|
|
<view @click="isExp = !isExp" class="r_exp" v-if="needExp">
|
2025-08-10 16:44:02 +08:00
|
|
|
|
<text v-if="isExp">收起</text>
|
|
|
|
|
|
<text v-else>展开全部</text>
|
|
|
|
|
|
<img src="@/assets/zixun/up_icon.png" class="exp_up" v-if="isExp" />
|
|
|
|
|
|
<img src="@/assets/zixun/down_icon.png" class="exp_up" v-else />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-02-04 14:04:02 +08:00
|
|
|
|
<LoginPopup
|
|
|
|
|
|
:show="LoginShow"
|
|
|
|
|
|
@handlePopupClose="handlePopupClose"
|
|
|
|
|
|
@handlePopupSuccessCallback="handlePopupSuccessCallback"
|
|
|
|
|
|
@handlePopupErrorCallback="handlePopupErrorCallback"
|
|
|
|
|
|
/>
|
2025-08-10 16:44:02 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-09-17 14:52:18 +08:00
|
|
|
|
import { ref, watch, computed } from "vue";
|
2025-08-10 16:44:02 +08:00
|
|
|
|
import dayjs from "dayjs/esm/index";
|
|
|
|
|
|
import LoginPopup from "@/components/loginPopup/index.vue";
|
|
|
|
|
|
import { Session } from "@/utils/storage";
|
|
|
|
|
|
|
|
|
|
|
|
const isExp = ref(false);
|
|
|
|
|
|
const rankListLocal = ref([]);
|
|
|
|
|
|
const loading = ref(true);
|
|
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => isExp.value,
|
|
|
|
|
|
(newValue, oldValue) => {
|
|
|
|
|
|
rankListLocal.value = [];
|
|
|
|
|
|
props.newsList.forEach((item, index) => {
|
|
|
|
|
|
if (!isExp.value && index > 9) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-17 15:33:46 +08:00
|
|
|
|
let concept_label = "";
|
|
|
|
|
|
let industry_label = "";
|
|
|
|
|
|
try {
|
|
|
|
|
|
concept_label = JSON.parse(item.concept_label);
|
|
|
|
|
|
industry_label = JSON.parse(item.industry_label);
|
2026-02-04 14:04:02 +08:00
|
|
|
|
} catch (e) {}
|
2025-09-12 10:40:08 +08:00
|
|
|
|
rankListLocal.value.push({
|
|
|
|
|
|
...item,
|
2025-09-17 15:33:46 +08:00
|
|
|
|
conceptLabels: concept_label,
|
|
|
|
|
|
industryLabels: industry_label,
|
2025-09-12 10:40:08 +08:00
|
|
|
|
});
|
2025-08-10 16:44:02 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
loading.value = false;
|
2026-02-04 14:04:02 +08:00
|
|
|
|
},
|
2025-08-10 16:44:02 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => props.newsList,
|
|
|
|
|
|
(newValue, oldValue) => {
|
2025-09-04 17:35:10 +08:00
|
|
|
|
if (!props.needExp) {
|
|
|
|
|
|
isExp.value = true;
|
|
|
|
|
|
}
|
2025-08-10 16:44:02 +08:00
|
|
|
|
rankListLocal.value = [];
|
|
|
|
|
|
props.newsList.forEach((item, index) => {
|
|
|
|
|
|
if (!isExp.value && index > 9) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-17 15:33:46 +08:00
|
|
|
|
let concept_label = "";
|
|
|
|
|
|
let industry_label = "";
|
|
|
|
|
|
try {
|
|
|
|
|
|
concept_label = JSON.parse(item.concept_label);
|
|
|
|
|
|
industry_label = JSON.parse(item.industry_label);
|
2026-02-04 14:04:02 +08:00
|
|
|
|
} catch (e) {}
|
2025-08-10 16:44:02 +08:00
|
|
|
|
|
2025-09-12 10:40:08 +08:00
|
|
|
|
rankListLocal.value.push({
|
|
|
|
|
|
...item,
|
2025-09-17 15:33:46 +08:00
|
|
|
|
conceptLabels: concept_label,
|
|
|
|
|
|
industryLabels: industry_label,
|
2025-09-12 10:40:08 +08:00
|
|
|
|
});
|
2025-08-10 16:44:02 +08:00
|
|
|
|
});
|
|
|
|
|
|
loading.value = false;
|
2026-02-04 14:04:02 +08:00
|
|
|
|
},
|
2025-08-10 16:44:02 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 接收父组件传入的新闻列表
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
newsList: {
|
|
|
|
|
|
type: Array,
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
default: () => [],
|
|
|
|
|
|
},
|
2025-09-04 16:22:13 +08:00
|
|
|
|
needExp: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: true,
|
|
|
|
|
|
},
|
2025-09-13 15:05:05 +08:00
|
|
|
|
hasTag: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false,
|
|
|
|
|
|
},
|
2025-08-10 16:44:02 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const clickItem = ref({});
|
|
|
|
|
|
function goDetail(item) {
|
|
|
|
|
|
if (Session.get("token")) {
|
|
|
|
|
|
clickItem.value = item;
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
|
url: `/pages/detail/indexNewsInfo?id=${item.news_id}`,
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LoginShow.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const LoginShow = ref(false);
|
|
|
|
|
|
const isLoginStatus = ref();
|
|
|
|
|
|
// 关闭弹框
|
|
|
|
|
|
const handlePopupClose = () => {
|
|
|
|
|
|
LoginShow.value = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 登录成功之后的回调
|
|
|
|
|
|
const handlePopupSuccessCallback = () => {
|
|
|
|
|
|
isLoginStatus.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
// uni.navigateTo({
|
|
|
|
|
|
// url: `/pages/detail/indexNewsInfo?id=${clickItem.value.news_id}`,
|
|
|
|
|
|
// });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 登录失败之后的回调
|
|
|
|
|
|
const handlePopupErrorCallback = () => {
|
|
|
|
|
|
console.log("登录失败");
|
|
|
|
|
|
};
|
2025-09-17 14:52:18 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @description 格式化时间
|
|
|
|
|
|
* @param {String|Number} dateTime 需要格式化的时间戳
|
|
|
|
|
|
* @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
|
|
|
|
|
|
* @returns {string} 返回格式化后的字符串
|
|
|
|
|
|
*/
|
|
|
|
|
|
function timeFormat(dateTime = null, formatStr = "yyyy-mm-dd hh:MM:ss") {
|
|
|
|
|
|
let date;
|
|
|
|
|
|
// 若传入时间为假值,则取当前时间
|
|
|
|
|
|
if (!dateTime) {
|
|
|
|
|
|
date = new Date();
|
|
|
|
|
|
}
|
|
|
|
|
|
// 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容)
|
|
|
|
|
|
else if (/^\d{10}$/.test(dateTime.toString().trim())) {
|
|
|
|
|
|
date = new Date(dateTime * 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
// 若用户传入字符串格式时间戳,new Date无法解析,需做兼容
|
|
|
|
|
|
else if (typeof dateTime === "string" && /^\d+$/.test(dateTime.trim())) {
|
|
|
|
|
|
date = new Date(Number(dateTime));
|
|
|
|
|
|
}
|
|
|
|
|
|
// 其他都认为符合 RFC 2822 规范
|
|
|
|
|
|
else {
|
|
|
|
|
|
// 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
|
|
|
|
|
|
date = new Date(typeof dateTime === "string" ? dateTime.replace(/-/g, "/") : dateTime);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const timeSource = {
|
|
|
|
|
|
y: date.getFullYear().toString(), // 年
|
|
|
|
|
|
m: (date.getMonth() + 1).toString().padStart(2, "0"), // 月
|
|
|
|
|
|
d: date.getDate().toString().padStart(2, "0"), // 日
|
|
|
|
|
|
h: date.getHours().toString().padStart(2, "0"), // 时
|
|
|
|
|
|
M: date.getMinutes().toString().padStart(2, "0"), // 分
|
|
|
|
|
|
s: date.getSeconds().toString().padStart(2, "0"), // 秒
|
|
|
|
|
|
// 有其他格式化字符需求可以继续添加,必须转化成字符串
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (const key in timeSource) {
|
|
|
|
|
|
const [ret] = new RegExp(`${key}+`).exec(formatStr) || [];
|
|
|
|
|
|
if (ret) {
|
|
|
|
|
|
// 年可能只需展示两位
|
|
|
|
|
|
const beginIndex = key === "y" && ret.length === 2 ? 2 : 0;
|
|
|
|
|
|
formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return formatStr;
|
|
|
|
|
|
}
|
2025-08-10 16:44:02 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.news-rank-list {
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.list {
|
|
|
|
|
|
min-height: 700rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.news-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
|
border-bottom: 1px solid #f2f2f2;
|
|
|
|
|
|
padding-bottom: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-tag {
|
|
|
|
|
|
width: 30px;
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-text-top3 {
|
2026-02-04 14:04:02 +08:00
|
|
|
|
font-family:
|
|
|
|
|
|
PingFangSC,
|
|
|
|
|
|
PingFang SC;
|
2025-08-10 16:44:02 +08:00
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
line-height: 33rpx;
|
|
|
|
|
|
text-shadow: 0px 0px 2px #deb72b;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
|
|
|
|
|
|
img {
|
|
|
|
|
|
width: 33rpx;
|
|
|
|
|
|
height: 41rpx;
|
|
|
|
|
|
margin-top: -10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.rank-text {
|
2026-02-04 14:04:02 +08:00
|
|
|
|
font-family:
|
|
|
|
|
|
PingFangSC,
|
|
|
|
|
|
PingFang SC;
|
2025-08-10 16:44:02 +08:00
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #e98254;
|
|
|
|
|
|
margin-top: -20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.news-content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.news-title {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.news-desc {
|
2026-02-04 14:04:02 +08:00
|
|
|
|
font-family:
|
|
|
|
|
|
PingFangSC,
|
|
|
|
|
|
PingFang SC;
|
2025-08-10 16:44:02 +08:00
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
|
2025-11-06 11:24:23 +08:00
|
|
|
|
display: -webkit-box;
|
|
|
|
|
|
/* 设置为WebKit内核的弹性盒子模型 */
|
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
|
/* 垂直排列 */
|
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
|
/* 限制显示三行 */
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
/* 隐藏超出范围的内容 */
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
/* 使用省略号 */
|
2025-08-10 16:44:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.news-meta {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
margin-top: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.source,
|
|
|
|
|
|
.time,
|
|
|
|
|
|
.score {
|
|
|
|
|
|
// opacity: 0.8;
|
2026-02-04 14:04:02 +08:00
|
|
|
|
font-family:
|
|
|
|
|
|
PingFangSC,
|
|
|
|
|
|
PingFang SC;
|
2025-08-10 16:44:02 +08:00
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #999999;
|
|
|
|
|
|
line-height: 30rpx;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.time {
|
|
|
|
|
|
margin-left: 10rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.score {
|
2026-02-04 14:04:02 +08:00
|
|
|
|
font-family:
|
|
|
|
|
|
PingFangSC,
|
|
|
|
|
|
PingFang SC;
|
2025-08-10 16:44:02 +08:00
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #ffa800;
|
|
|
|
|
|
line-height: 30rpx;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.exp_up {
|
|
|
|
|
|
width: 21rpx;
|
|
|
|
|
|
height: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.r_exp {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10rpx;
|
|
|
|
|
|
}
|
2025-09-12 10:40:08 +08:00
|
|
|
|
|
|
|
|
|
|
.r_r_tags {
|
|
|
|
|
|
// margin-left: 20rpx;
|
|
|
|
|
|
width: 85vw;
|
|
|
|
|
|
overflow-x: scroll;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.r_tags {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 10rpx;
|
|
|
|
|
|
|
|
|
|
|
|
.tag {
|
2026-02-04 14:04:02 +08:00
|
|
|
|
font-family:
|
|
|
|
|
|
PingFangSC,
|
|
|
|
|
|
PingFang SC;
|
2025-09-12 10:40:08 +08:00
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #3f80fa;
|
|
|
|
|
|
|
|
|
|
|
|
padding: 5rpx 20rpx;
|
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
|
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-10 16:44:02 +08:00
|
|
|
|
</style>
|