feat: 重构拓展阅读功能,优化组件数据传递和用户权限控制

This commit is contained in:
傅光孟 2026-02-06 17:10:10 +08:00
parent b0a3e38657
commit f9c0f69cd6
7 changed files with 213 additions and 79 deletions

View File

@ -42,6 +42,6 @@ export const getListByTag = (data: any) => {
}; };
// 拓展阅读新闻 // 拓展阅读新闻
export const getListByNewsExtend = (news_id: string) => { export const getDetailFurtherReadData = (news_id: string) => {
return request.get(`/news/detail/${news_id}/furtherReadings`); return request.get(`/news/detail/${news_id}/furtherReadings`);
}; };

View File

@ -1,7 +1,9 @@
<template> <template>
<view class="page-container"> <view class="page-container">
<view class="main"> <view class="main">
<view class="title" :class="{ mohu: !userStore.isLogin }">{{ props.data.title }}</view> <view class="title" :class="{ mohu: !userStore.isLogin }">{{
props.data.title
}}</view>
<view class="author"> <view class="author">
<view class="name" :class="{ mohu: !userStore.isLogin }"> <view class="name" :class="{ mohu: !userStore.isLogin }">
<text class="text">来源:</text> <text class="text">来源:</text>
@ -15,7 +17,9 @@
<text class="text">编辑:</text> <text class="text">编辑:</text>
<text class="text">{{ props.data.editor }}</text> <text class="text">{{ props.data.editor }}</text>
</view> --> </view> -->
<view class="time" :class="{ mohu: !userStore.isLogin }">{{ props.data.publishTime }}</view> <view class="time" :class="{ mohu: !userStore.isLogin }">{{
props.data.publishTime
}}</view>
</view> </view>
<!-- 两个标签 start --> <!-- 两个标签 start -->
@ -104,9 +108,28 @@
</view> </view>
<!-- 关联个股 --> <!-- 关联个股 -->
<view class="r_etf" style="margin-top: 20rpx" v-if="intoType == 'etf'"> <view
class="r_etf"
style="margin-top: 20rpx"
v-if="data.stocks && data.stocks.length > 0"
>
<text class="etf_title">关联个股</text> <text class="etf_title">关联个股</text>
<view class="etfs"> <view class="etfs">
<template>
<view v-for="(item, index) in data.stocks" class="stock_item" :key="index">
<view class="name">{{ item.name }}</view>
<view class="code">{{ item.code }}</view>
<!-- 暂无涨跌数据保留样式 -->
<!-- <view>
<view class="name">{{ item.name }}</view>
<view class="code">{{ item.code }}</view>
</view>
<view>
<view class="percent up">-0.28%</view>
</view> -->
</view>
</template>
<template v-if="intoType == 'etf'">
<view <view
v-for="(item, index) in data.etfs" v-for="(item, index) in data.etfs"
class="etf_item" class="etf_item"
@ -116,38 +139,25 @@
<view class="name">{{ item.name }}</view> <view class="name">{{ item.name }}</view>
<view class="code">{{ item.code }}</view> <view class="code">{{ item.code }}</view>
</view> </view>
<view </template>
v-for="(item, index) in data.etfs"
class="stock_item"
:key="index"
@click="goEtfDetail(item)"
>
<view>
<view class="name">{{ item.name }}</view>
<view class="code">{{ item.code }}</view>
</view>
<view>
<view class="percent up">-0.28%</view>
</view>
</view>
</view> </view>
</view> </view>
<!-- 拓展阅读 --> <!-- 拓展阅读 -->
<view class="more-news" style="margin-top: 20rpx" v-if="extendData.length > 0"> <view class="more-news" style="margin-top: 20rpx" v-if="furtherReadData.length > 0">
<text class="more-news-title">拓展阅读</text> <text class="more-news-title">拓展阅读</text>
<view class="more-news-list"> <view class="more-news-list">
<view <view
class="news" class="news"
v-for="item in extendData" v-for="item in furtherReadData"
:ke="item.id" :ke="item.id"
@click="goNewsDetail(item)" @click="goNewsDetail(item)"
> >
<view class="title">{{ item.title }}</view> <view class="title">{{ item.title }}</view>
</view> </view>
</view> </view>
<view class="more-btn"> <view class="more-btn" v-if="!isMore">
<view class="text" @click="getMoreExtendNews">查看更多</view> <view class="text" @click="getMoreNews">查看更多</view>
</view> </view>
</view> </view>
@ -177,13 +187,13 @@ import { Session } from "@/utils/storage";
import zhaiyaoImg from "../../assets/zixun/zhaiyao_icon.png"; import zhaiyaoImg from "../../assets/zixun/zhaiyao_icon.png";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
const isLogin = ref(Session.get("token")); const emit = defineEmits(["getFurtherReadData"]);
const props = defineProps({ const props = defineProps({
data: { data: {
type: Object, type: Object,
default: () => {}, default: () => {},
}, },
extendData: { furtherReadData: {
type: Array, type: Array,
default: () => [], default: () => [],
}, },
@ -203,7 +213,7 @@ const tagList1 = ref([
const userStore = useUserStore(); const userStore = useUserStore();
// //
const LoginShow = ref(true); const LoginShow = ref(false);
// //
const handleShowLogin = () => { const handleShowLogin = () => {
@ -247,7 +257,7 @@ onLoad((option) => {
intoType.value = option?.intoType || null; intoType.value = option?.intoType || null;
if (!userStore.isLogin) { if (!userStore.isLogin) {
LoginShow.value = true; // LoginShow.value = true;
} }
}); });
@ -314,8 +324,10 @@ const goNewsDetail = (item: any) => {
}; };
// //
const getMoreExtendNews = (item: any) => { const isMore = ref(false);
console.log("output >>>>> 更多"); const getMoreNews = () => {
isMore.value = true;
emit("getFurtherReadData");
}; };
</script> </script>

View File

@ -11,7 +11,11 @@
</view> </view>
<!-- 文章正文 start --> <!-- 文章正文 start -->
<Article :data="data" :extendData="extendList" /> <Article
:data="data"
:furtherReadData="furtherReadData"
@getFurtherReadData="getFurtherReadData"
/>
<!-- 分割 --> <!-- 分割 -->
<view class="line"></view> <view class="line"></view>
@ -36,7 +40,12 @@
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app"; import { onLoad, onShow } from "@dcloudio/uni-app";
import { onReachBottom } from "@dcloudio/uni-app"; import { onReachBottom } from "@dcloudio/uni-app";
import { fetchArticleDetail, fetchArticleLike, fetchArticleFavorate, getListByNewsExtend } from "@/api/detail"; import {
fetchArticleDetail,
fetchArticleLike,
fetchArticleFavorate,
getDetailFurtherReadData,
} from "@/api/detail";
import { getNewsList } from "@/api"; import { getNewsList } from "@/api";
import Article from "@/components/article/indexNewsInfo.vue"; import Article from "@/components/article/indexNewsInfo.vue";
import Column from "@/components/column/index.vue"; import Column from "@/components/column/index.vue";
@ -50,6 +59,7 @@ const data = ref<any>({});
const newType = ref<any>(""); const newType = ref<any>("");
const columnList = ref<any>([]); const columnList = ref<any>([]);
const columnName = ref(""); const columnName = ref("");
const furtherReadData = ref<any[]>([]);
onReachBottom(() => { onReachBottom(() => {
console.log("🚀 ~ onReachBottom ~ onReachBottom:"); console.log("🚀 ~ onReachBottom ~ onReachBottom:");
@ -66,6 +76,7 @@ const newList = async (columnId: number) => {
if (res.code === 200) { if (res.code === 200) {
// console.log(res.data) // console.log(res.data)
columnList.value = res.data; columnList.value = res.data;
furtherReadData.value = res.data.furtherReadings || [];
} }
}; };
@ -191,21 +202,16 @@ const jumpDetail = (item: any) => {
}); });
}; };
// id // id
const news_id = ref('') const news_id = ref("");
const extendList = ref([]) // const getFurtherReadData = () => {
const getExtendList = ()=> { getDetailFurtherReadData(news_id.value).then((res: any) => {
furtherReadData.value = [...furtherReadData.value, ...res.data];
getListByNewsExtend(news_id.value).then((res: any) => {
console.log('output >>>>> res',res);
}); });
} };
onLoad(async (option: any) => { onLoad(async (option: any) => {
news_id.value = option.id news_id.value = option.id;
aplus_queue.push({ aplus_queue.push({
action: "aplus.sendPV", action: "aplus.sendPV",
arguments: [{ is_auto: false }], // arguments: [{ is_auto: false }], //
@ -230,9 +236,6 @@ onLoad(async (option: any) => {
id: option.id, id: option.id,
}); });
//
getExtendList();
if (res.code === 200) { if (res.code === 200) {
data.value = res.data; data.value = res.data;

View File

@ -1,25 +1,82 @@
<template> <template>
<view class="hot-section"> <view class="hot-section">
<view class="hot-top"> <view class="hot-top" @click="navigateTo">
<view class="title">风口概念</view> <view class="title">风口概念</view>
<view class="time">近一个月热门</view> <view class="time">近一个月热门</view>
</view> </view>
<view class="tag-box"> <view class="tag-box">
<view class="tag tag-active">医药生物</view> <view v-for="(item, index) in topConceptList_d" :key="index" class="tag">
<view class="tag">医药生物</view> {{ item.content }}
<view class="tag">医药生物</view> </view>
<view class="tag">医药生物</view>
</view> </view>
<view class="tag-content"> <view class="tag-content">
<view class="content" <view class="content" @click="goDetail(oneData)">{{ oneData.title }} </view>
>据称中国科技巨头赴海外训练AI模型以获取英伟达芯片据称中国科技巨头赴海外训练AI模型以获取英伟达芯片据称中国科技巨头赴海外训练AI模型以获取英伟达芯片
</view>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { getListByTag } from "@/api/detail";
import { getTopConcept_d } from "@/api/newsInfo";
import { useUserStore } from "@/stores/user";
import { computed, onMounted, ref } from "vue";
const emit = defineEmits(["onShow"]);
const active = ref(0);
const topConceptList = ref([]);
const oneData = ref({});
const userStore = useUserStore();
const topConceptList_d = computed(() => {
return topConceptList.value.slice(0, 3);
});
const handleClickTag = (index: number, item: any) => {
active.value = index;
getList(item);
};
// top10
async function getTopConcept_dFn() {
topConceptList.value = await getTopConcept_d({});
}
async function getList() {
const name = topConceptList.value[active.value].content;
//
let { code, data } = await getListByTag({ name });
if (code == 200) {
oneData.value = data[0];
}
}
//
function goDetail(item: any) {
if (userStore.isLogin) {
uni.navigateTo({
url: `/pages/detail/indexNewsInfo?id=${item.id}`,
});
} else {
emit("onShow");
}
}
//
function navigateTo() {
if (userStore.isLogin) {
uni.navigateTo({
url: `/pages/concept/index`,
});
} else {
emit("onShow");
}
}
onMounted(async () => {
// top10
await getTopConcept_dFn();
await getList();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -26,31 +26,31 @@ const menus = reactive([
{ {
name: "海外先机", name: "海外先机",
icon: MENUICON1, icon: MENUICON1,
auth: "menu1", // auth: "menu1",
path: "/pages/foreign/index", path: "/pages/foreign/index",
}, },
{ {
name: "编辑精选", name: "编辑精选",
icon: MENUICON2, icon: MENUICON2,
auth: "menu2", // auth: "menu2",
path: "/pages/recommend/index", path: "/pages/recommend/index",
}, },
{ {
name: "宏观知微", name: "宏观知微",
icon: MENUICON3, icon: MENUICON3,
auth: "menu3", // auth: "menu3",
path: "/pages/macroscopic/index", path: "/pages/macroscopic/index",
}, },
{ {
name: "热门行业", name: "热门行业",
icon: MENUICON4, icon: MENUICON4,
auth: "menu4", // auth: "menu4",
path: "/pages/industry/index", path: "/pages/industry/index",
}, },
{ {
name: "风口概念", name: "风口概念",
icon: MENUICON5, icon: MENUICON5,
auth: "menu5", // auth: "menu5",
path: "/pages/concept/index", path: "/pages/concept/index",
}, },
]); ]);
@ -67,11 +67,13 @@ const goto = (path: string, auth: string) => {
return; return;
} }
if (userInfos.value.auth.includes(auth)) { if (userInfos.value.accountType === 1) {
//
uni.navigateTo({ uni.navigateTo({
url: path, url: path,
}); });
} else { } else {
//
uni.showToast({ uni.showToast({
title: "无权限访问", title: "无权限访问",
icon: "none", icon: "none",

View File

@ -1,37 +1,93 @@
<template> <template>
<view class="hot-section"> <view class="hot-section">
<view class="hot-top"> <view class="hot-top" @click="navigateTo">
<view class="title">热门行业</view> <view class="title">热门行业</view>
<view class="time">近一个月热门</view> <view class="time">近一个月热门</view>
</view> </view>
<view class="tag-box"> <view class="tag-box">
<view class="tag tag-active">医药生物</view> <view
<view class="tag">医药生物</view> v-for="(item, index) in industryList_d"
<view class="tag">医药生物</view> :class="['tag', { 'tag-active': active === index }]"
<view class="tag">医药生物</view> @click="handleClickTag(index, item)"
>{{ item.content }}</view
>
</view> </view>
<view class="tag-content"> <view class="tag-content" @click="goDetail(oneData)">
<view class="title"> <view class="title">
<image class="icon-hot" src="@/assets/images/page/icon_hot@2x.png"></image> <image class="icon-hot" src="@/assets/images/page/icon_hot@2x.png"></image>
<view class="name"> <view class="name">
<text class="text" <text class="text">{{ oneData.title }}</text>
>行业龙头最新财报行业龙头最新财报行业龙头最新财报行业龙头最新财报</text
>
</view> </view>
</view> </view>
<view class="content"> <view class="content">
英伟达发布2026财年三季度业绩及四季度指引均超市场预期收入方场预期收入方面三亿美元 {{ oneData.summary }}
英伟达发布2026财年三季度业绩及四季度指引均超市场预期收入方场预期收入方面三亿美元
英伟达发布2026财年三季度业绩及四季度指引均超市场预期收入方场预期收入方面三亿美元
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { getListByTagIndustry } from "@/api/detail";
const active = ref(0); import { getTopIndustry_d } from "@/api/newsInfo";
import { useUserStore } from "@/stores/user";
import { computed, onMounted, ref } from "vue";
const emit = defineEmits(["onShow"]);
const active = ref(0);
const industryList = ref([]);
const oneData = ref({});
const userStore = useUserStore();
const industryList_d = computed(() => {
return industryList.value.slice(0, 4);
});
const handleClickTag = (index: number, item: any) => {
active.value = index;
getList(item);
};
// top10
async function getTopIndustry_dFn() {
industryList.value = await getTopIndustry_d({});
}
async function getList() {
const name = industryList.value[active.value].content;
//
let { code, data } = await getListByTagIndustry({ name });
if (code == 200) {
oneData.value = data[0];
}
}
//
function goDetail(item: any) {
if (userStore.isLogin) {
uni.navigateTo({
url: `/pages/detail/indexNewsInfo?id=${item.id}`,
});
} else {
emit("onShow");
}
}
//
function navigateTo() {
if (userStore.isLogin) {
uni.navigateTo({
url: `/pages/industry/index`,
});
} else {
emit("onShow");
}
}
onMounted(async () => {
// top10
await getTopIndustry_dFn();
await getList();
});
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -88,7 +144,9 @@ const active = ref(0);
font-weight: 500; font-weight: 500;
font-size: 28rpx; font-size: 28rpx;
color: #333333; color: #333333;
line-height: 40rpx; text-align: center;
line-height: 36rpx;
padding: 0 10rpx;
box-sizing: border-box; box-sizing: border-box;
} }

View File

@ -9,6 +9,7 @@ type IUserInfos = {
name?: string; name?: string;
auth: string[]; auth: string[];
role: string[]; role: string[];
accountType: number;
}; };
const storeSetup = () => { const storeSetup = () => {
@ -16,8 +17,9 @@ const storeSetup = () => {
const userInfos = ref<IUserInfos>({ const userInfos = ref<IUserInfos>({
phone: "", phone: "",
name: "", name: "",
auth: [], auth: [], // 首页导航菜单权限存在, 暂不使用
role: [], role: [], // 角色,暂不使用
accountType: 0, // 账号类型0:测试 1:正式
}); });
const setUserInfos = (payload: Partial<IUserInfos>) => { const setUserInfos = (payload: Partial<IUserInfos>) => {