feat(ETF): 新增ETF详情页面及接口

- 添加fetchEtfDetail接口用于获取ETF详情数据
- 创建indexEtfInfo页面展示ETF详情信息
- 在indexEtf页面添加跳转到详情页功能
- 调整.env.development中的API地址配置
- 优化indexEtf页面样式和加载逻辑
This commit is contained in:
34701892@qq.com 2025-11-20 18:38:16 +08:00
parent 2b20a82484
commit 6dcc946e1c
5 changed files with 301 additions and 20 deletions

View File

@ -2,8 +2,8 @@
ENV = development
# 本地环境接口地址
VITE_API_URL = http://123.60.153.169:8040/apih5
# VITE_API_URL = http://4155gf93ll13.vicp.fun/apih5
# VITE_API_URL = http://123.60.153.169:8040/apih5
VITE_API_URL = http://4155gf93ll13.vicp.fun/apih5
# VITE_API_URL = http://123.60.79.143:8041/apih5
# VITE_API_URL =http://localhost:8040/apih5
# VITE_API_URL = https://cankao.cs.com.cn/apih5

View File

@ -85,3 +85,8 @@ export const doLogout = (data: any) => {
export const getEtfIndexList = (data: any) => {
return Request.post("/news/etfIndexList", data);
};
// 获取ETF详情
export const fetchEtfDetail = (data: any) => {
return Request.post("/news/etfList", data);
};

View File

@ -25,6 +25,9 @@
{
"path": "pages/realtimeInfo/indexEtf"
},
{
"path": "pages/realtimeInfo/indexEtfInfo"
},
{
"path": "pages/sreachReq/index"
},

View File

@ -55,7 +55,8 @@
<text>{{ item.summary }}</text>
</view>
<view class="item_etf">
<view v-for="etf in item.etfs" :key="etf.code" class="item_etf_item">
<view v-for="etf in item.etfs" :key="etf.code" class="item_etf_item" @click.stop="goEtfDetail(etf)">
<view></view>
<text>{{ etf.name }}</text>
<div class="btn-play"></div>
</view>
@ -95,22 +96,22 @@ import { onReachBottom } from "@dcloudio/uni-app";
import { getEtfIndexList } from "@/api/index";
const listData = ref([
{
day: "2026-10-26",
list: [
{
time: "11:30:56",
title: "刚果延长禁令后,中国钴价和库存飙升刚果延长禁令后,中国钴价和库存飙升",
summary: "刚果民主共和国将钴出口禁令延长至9月导致全球约四分之三的钴供应受到影响。这一消息引…",
etfs: [
{
name: "医药 ETF",
code: "string",
},
],
},
],
},
// {
// day: "2026-10-26",
// list: [
// {
// time: "11:30:56",
// title: "",
// summary: "9",
// etfs: [
// {
// name: " ETF",
// code: "string",
// },
// ],
// },
// ],
// },
]);
const form = ref({
@ -118,10 +119,15 @@ const form = ref({
keyword: null,
});
async function getData() {
uni.showLoading({
title: "加载中",
});
let { code, data } = await getEtfIndexList({
...form.value,
size: 20,
});
uni.hideLoading();
if (code == 200) {
if (form.value.page == 1) {
listData.value = [];
@ -173,6 +179,13 @@ function goDetail(item, index) {
}
}
function goEtfDetail(item) {
console.log("🚀 ~ goEtfDetail ~ item:", item);
uni.navigateTo({
url: `/pages/realtimeInfo/indexEtfInfo?name=${item.name}&code=${item.code}`,
});
}
function onSreach() {
if (Session.get("token")) {
form.value.page = 1;
@ -487,15 +500,19 @@ onMounted(async () => {
margin-top: 10rpx;
gap: 20rpx;
margin-top: 30rpx;
margin-bottom: 30rpx;
.item_etf_item {
// width: 168rpx;
width: calc(50% - 60rpx);
height: 48rpx;
background: #edf1f5;
border-radius: 125rpx;
padding: 5rpx 25rpx;
display: flex;
align-items: center;
text-align: center;
justify-content: space-between;
text {
font-family: PingFangSC, PingFang SC;

View File

@ -0,0 +1,256 @@
<template>
<div class="index-etf-info">
<!-- 头部 -->
<view class="vipHeader">
<view class="flashBack" @click="back">
<u-icon size="20" name="arrow-left" color="#000"></u-icon>
</view>
<view style="display: flex; flex-direction: column">
<view class="title">{{ name }}</view>
<view class="title_sub">{{ code }}</view>
</view>
<view style="width: 5%"></view>
</view>
<view style="width: 100vw; height: 10rpx; background-color: #f5f5f5"></view>
<!-- 指数ETF实时信息展示区域 -->
<view class="r_list">
<view v-for="(item, index) in etfInfoList" :key="index" class="news-item" @click="goDetail(item, 0)">
<view class="news-content">
<text class="news-title">{{ item.title }}</text>
<text class="news-desc">{{ item.summary }}</text>
<view class="news-meta">
<view>
<text class="time">
{{ timeFormat(new Date(item.publish_time).getTime()) }}
</text>
</view>
<text class="time">中国证券报</text>
</view>
</view>
</view>
</view>
</div>
</template>
<script setup>
import { onLoad } from "@dcloudio/uni-app";
import { ref } from "vue";
import { fetchEtfDetail } from "@/api/index";
// 使 Vue3 <script setup>
//
const etfInfoList = ref([]);
async function getEtfInfo() {
uni.showLoading({
title: "加载中",
});
let { code, data } = await fetchEtfDetail({
etfName: name.value,
});
uni.hideLoading();
if (code === 200) {
console.log(data);
etfInfoList.value = data.list;
}
}
/**
* @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/Webkitnew 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;
}
function goDetail(item, index) {
uni.navigateTo({
url: `/pages/detail/indexNewsInfo?id=${item.id}`,
});
}
const name = ref("");
const code = ref("");
onLoad((option) => {
console.log(option.name);
name.value = option.name;
code.value = option.code;
getEtfInfo();
});
</script>
<style scoped lang="scss">
.news-item {
display: flex;
align-items: flex-start;
margin-bottom: 15px;
border-bottom: 1px solid #f2f2f2;
padding-bottom: 10px;
}
.news-content {
flex: 1;
display: flex;
flex-direction: column;
}
.r_list {
padding: 30rpx;
width: calc(100vw - 60rpx);
}
.news-title {
font-family: PingFangSC, PingFang SC;
font-weight: bold;
font-size: 30rpx;
color: #222222;
line-height: 42rpx;
text-align: left;
font-style: normal;
margin-bottom: 5px;
line-height: 1.2;
width: calc(100vw - 60rpx);
display: inline-block;
/* 或 block */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.news-desc {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #333333;
text-align: left;
font-style: normal;
display: -webkit-box;
/* 设置为WebKit内核的弹性盒子模型 */
-webkit-box-orient: vertical;
/* 垂直排列 */
-webkit-line-clamp: 2;
/* 限制显示三行 */
overflow: hidden;
/* 隐藏超出范围的内容 */
text-overflow: ellipsis;
/* 使用省略号 */
width: calc(100vw - 60rpx);
margin-top: 10rpx;
}
.news-meta {
display: flex;
justify-content: space-between;
font-size: 12px;
color: #999;
margin-top: 26rpx;
}
.source,
.time,
.score {
// opacity: 0.8;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 22rpx;
color: #999999;
line-height: 30rpx;
text-align: left;
font-style: normal;
}
.time {
// margin-left: 10rpx;
}
.score {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 22rpx;
color: #ffa800;
line-height: 30rpx;
text-align: center;
font-style: normal;
}
.vipHeader {
width: 100vw;
height: 120rpx;
background-color: white;
box-sizing: border-box;
padding: 32rpx 32rpx 6rpx 40rpx;
display: flex;
justify-content: space-between;
// position: sticky;
// top: 0;
// z-index: 9999;
.flashBack {
height: 100%;
display: flex;
align-items: center;
}
.title {
font-size: 32rpx;
color: #000;
font-weight: bold;
left: 0;
}
.title_sub {
font-size: 24rpx;
color: #999;
font-weight: 400;
left: 0;
}
}
</style>