后期修改完善,上线版本

This commit is contained in:
2025-11-12 18:11:11 +08:00
parent c54f9c9976
commit 8f57683dd5
98 changed files with 2110 additions and 867 deletions

View File

@ -1,12 +1,12 @@
<template>
<div @click="goToFinanceDetail">
<div>
<div class="finance-item flex">
<div class="dot-container">
<div class="dot"></div>
</div>
<div>
<div class="title">2025-2-01</div>
<div class="content-text">Koah参与$ 500万美元种子轮融资领投Forerunner 参与投资South Park Commons </div>
<div class="title">{{ item.date || '' }}</div>
<div class="content-text">{{ item.info || '' }}</div>
</div>
</div>
<div class="diver-line"></div>
@ -27,13 +27,6 @@ export default {
return {
}
},
methods: {
goToFinanceDetail() {
this.$router.push({
path: '/finance-detail',
})
}
}
}
</script>
@ -60,13 +53,18 @@ export default {
}
.title {
color: #aebadee6;
font-family: 'Poppins-Medium', serif;
font-family: 'Poppins-Medium';
margin-bottom: 12px;
}
.content-text {
color: #64748B;
font-family: 'Poppins-Medium', serif;
font-family: 'Poppins-Medium';
font-size: 20px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="item flex items-center">
<div class="item flex items-center" @click="gotoLink(item.value)" @mouseenter="isHovered = true" @mouseleave="isHovered = false">
<div class="icon flex items-center justify-center">
<img :src="item.iconUrl || ''" alt="" />
<img :src="getImageSrc()" alt="" />
</div>
<div class="text">{{ item.name || '' }}</div>
<div class="text">{{ item.key || '' }}</div>
</div>
</template>
@ -19,12 +19,30 @@ export default {
},
data() {
return {
isHovered: false
}
},
methods: {}
methods: {
// 跳转链接
gotoLink(url) {
if (!url) {
return false;
}
window.open(url, '_blank');
},
// 获取图标路径
getImageSrc() {
if (this.isHovered && this.item.key) {
return `/launches/link/icon_${this.item.key}_selected.png`;
}
return this.item.key ? `/launches/link/icon_${this.item.key}.png` : '';
}
}
}
</script>
<style scoped lang="scss">
.item {
border-radius: 12px;
@ -33,11 +51,13 @@ export default {
margin-bottom: 6px;
color: #64748B;
font-size: 18px;
font-family: 'Poppins-Medium', serif;
font-family: 'Poppins-Medium';
cursor: pointer;
.icon {
width: 24px;
height: 24px;
border-radius: 4px;
padding: 5px;
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.08);
img {
width: 14px;

View File

@ -1,16 +1,16 @@
<template>
<div class="item">
<div class="item" @click="goToDetail">
<div class="flex items-center">
<img class="icon" :src="item.iconUrl || ''" alt="" />
<img class="icon" :src="item.coverImage || ''" alt="" />
<div class="flex-1 flex flex-col justify-between">
<div class="title">{{ item.title || '' }}</div>
<div class="flex items-center" style="gap: 30px">
<div class="flex items-center data" style="gap: 12px">
<img alt="" src="/launches/detail/icon_thumb.png" style="width: 24px; height: 24px;" />
<div class="flex items-center gap-30">
<div class="flex items-center data gap-12">
<img alt="" src="/launches/detail/icon_thumb.png" class="wh-24" />
<div>{{ item.likeCount || 0 }}</div>
</div>
<div class="flex items-center data" style="gap: 12px">
<img alt="" src="/launches/detail/icon_star.png" style="width: 24px; height: 24px;" />
<div class="flex items-center data gap-12">
<img alt="" src="/launches/detail/icon_star.png" class="wh-24" />
<div>{{ item.rating || 0 }}</div>
</div>
</div>
@ -32,12 +32,30 @@ export default {
return {
}
},
methods: {},
methods: {
goToDetail() {
if (this.item && this.item.slug) {
this.$router.push(`/launches/detail?news_slug=${this.item.slug}`);
}
}
},
}
</script>
<style scoped lang="scss">
.gap-30 {
gap: 30px;
}
.item {
cursor: pointer;
transition: all 0.3s ease;
max-width: 330px;
overflow: hidden;
&:hover {
opacity: 0.8;
}
.icon {
width: 70px;
height: 70px;
@ -45,19 +63,26 @@ export default {
margin-right: 20px;
}
.title {
font-family: 'Poppins-SemiBold', serif;
flex: 1;
font-family: 'Poppins-SemiBold';
font-size: 24px;
color: #1E293B;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.data {
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
color: #64748B;
}
.summary {
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
color: #64748B;
margin-top: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
</style>

View File

@ -2,86 +2,83 @@
<div id="normal-container">
<IntegratedLayout>
<div class="content">
<div class="title-text">{{ news_detail.title || '' }}</div>
<div class="rate-box">
<Rate v-model="news_detail.rating" readonly />
<div class="flex" style="gap: 20px">
<div class="bread-menu">
<span>AI Launches</span>
<i class="el-icon-arrow-right"></i>
<span class="crumbs gradient-color">{{newsDetail.title || ''}}</span>
</div>
<div class="title-text">{{ newsDetail.title || '' }}</div>
<div class="flex mt-24">
<div class="flex-1">
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/icon_star.png" alt="">
<span>Score: </span>
<div class="score color-01">{{ (newsDetail.rating || 0).toFixed(1) }}</div>
</div>
</div>
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/icon_note.png" alt="">
<span>Introduction: </span>
</div>
<div class="item-content">
{{ newsDetail.summary || '' }}
</div>
</div>
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/icon_clock.png" alt="">
<span>Data update: </span>
</div>
<div class="item-content">{{ formatPublishTime(newsDetail.publishTime || '') }}</div>
</div>
<div class="tags">
<div class="tag-item" v-for="(it, index) in tagList" :key="index">{{ it }}</div>
</div>
</div>
<div class="flex gap-20">
<ThumbBtn :like-count="newsDetail.likeCount || 0" :id="newsDetail.id || 0" type="article" @like-success="refreshToolDetail" />
<CommentBtn :comment-count="commentCount" />
</div>
</div>
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/icon_note.png" alt="">
<span>Introduction: </span>
</div>
<div class="item-content">
{{ news_detail.summary || '' }}
</div>
</div>
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/icon_clock.png" alt="">
<span>Data update: </span>
</div>
<div class="item-content">{{ news_detail.publishTime || '' }}</div>
</div>
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/like_icon.png" alt="">
<span>Like: </span>
</div>
<div class="item-content">{{ news_detail.likeCount || 0 }}</div>
</div>
<div class="tags">
<div class="tag-item" v-for="(it, index) in tagList" :key="index">{{ it }}</div>
</div>
<div class="diver"></div>
<div class="container flex">
<div class="left-content flex-1 flex flex-col">
<!--<div class="sketch flex justify-between">-->
<!-- <div class="flex items-center" style="gap: 8px">-->
<!-- <img style="width: 18px; height: 18px" src="/launches/detail/icon_fly.png" alt="" />-->
<!-- <div class="text">This is the <span style="color: #7B61FF">6</span> release of Sketch</div>-->
<!-- </div>-->
<!-- <div class="flex items-center more">-->
<!-- <div>View more</div>-->
<!-- <img src="/launches/detail/icon_arrow.png" alt="" />-->
<!-- </div>-->
<!--</div>-->
<div class="card flex-1">
<div class="article-content">
<div v-html="news_detail.content || ''"></div>
<div v-html="newsDetail.content || ''"></div>
</div>
</div>
</div>
<div class="right-content">
<div class="card">
<div class="flex items-center" style="margin-top: 20px">
<div class="flex items-center mt-20">
<img alt="" src="/launches/detail/icon_hourse.png" class="icon" />
<div class="content-title">Company Information</div>
</div>
<div class="diver-line"></div>
<ProjectItem :item="{name: 'Sketch.com', iconUrl: '/launches/web/sketch.png'}" />
<ProjectItem :item="{name: 'Github.com', iconUrl: '/launches/web/github.png'}" />
<ProjectItem :item="{name: 'Facebook.com', iconUrl: '/launches/web/facebook.png'}" />
<ProjectItem :item="{name: 'Instagram.com', iconUrl: '/launches/web/instagram.png'}" />
<ProjectItem :item="{name: 'Twitter.com', iconUrl: '/launches/web/twitter.png'}" />
<div class="more flex items-center justify-between" style="margin-top: 20px">
<div>A total of 8 projects were released</div>
<img src="/launches/detail/icon_arrow.png" alt="" />
<div v-if="newsDetail.extra && Array.isArray(newsDetail.extra.socialLinks)" class="mt-30">
<ProjectItem v-for="(item, index) in newsDetail.extra.socialLinks" :key="index" :item="item" />
</div>
<!--<div class="more flex items-center justify-between mt-20">-->
<!-- <div>A total of 8 projects were released</div>-->
<!-- <img src="/launches/detail/icon_arrow.png" alt="" />-->
<!--</div>-->
</div>
<div class="card" style="margin-top: 20px">
<div class="flex items-center" style="margin-top: 20px">
<div class="card mt-20">
<div class="flex items-center mt-20">
<img alt="" src="/launches/detail/icon_finance.png" class="icon" />
<div class="content-title">Special Financing</div>
</div>
<div class="diver-line"></div>
<FinanceItem />
<FinanceItem />
<FinanceItem />
<div class="flex justify-end" style="margin-top: 30px">
<div class="more flex items-center justify-between" style="gap: 14px">
<div class="scroll">
<div v-if="newsDetail.extra && Array.isArray(newsDetail.extra.financing)">
<FinanceItem v-for="(item, index) in newsDetail.extra.financing" :key="index" :item="item" :slug="news_slug" />
</div>
</div>
<div class="flex justify-end mt-30">
<div class="more flex items-center justify-between gap-14" @click="goToFinanceDetail">
<div>View more</div>
<img src="/launches/detail/icon_arrow.png" alt="" />
</div>
@ -97,29 +94,14 @@
</div>
</div>
<div class="card">
<div class="flex-between-center list">
<RelatedTool :item="{
title: 'Figma',
summary: 'Easily create highly interactive prototypes',
likeCount: 123,
rating: 4.5
}" />
<RelatedTool :item="{
title: 'Figma',
summary: 'Easily create highly interactive prototypes',
likeCount: 123,
rating: 4.5
}" />
<RelatedTool :item="{
title: 'Figma',
summary: 'Easily create highly interactive prototypes',
likeCount: 123,
rating: 4.5
}" />
<div class="list">
<div class="item" v-for="(item, index) in otherList" :key="index">
<RelatedTool :item="item" />
</div>
</div>
</div>
</div>
<Comment comment-type="article" :id="news_detail.id" @update:commentCount="handleCommentCountUpdate" />
<Comment comment-type="article" :id="newsDetail.id" @update:commentCount="handleCommentCountUpdate" />
</div>
</IntegratedLayout>
</div>
@ -131,25 +113,59 @@ import Comment from "@/pages/ToolDetail/Comment/index.vue";
import RelatedTool from "@/pages/Launches/Detail/RelatedTool.vue";
import ProjectItem from "@/pages/Launches/Detail/ProjectItem.vue";
import FinanceItem from "@/pages/Launches/Detail/FinanceItem.vue";
import ThumbBtn from "@/pages/ToolDetail/components/ThumbBtn.vue";
export default {
components: {FinanceItem, ProjectItem, RelatedTool, CommentBtn, Comment},
components: {ThumbBtn, FinanceItem, ProjectItem, RelatedTool, CommentBtn, Comment},
data() {
return {
news_detail: {},
newsDetail: {},
commentCount: 0,
news_slug: '',
otherList: [],
}
},
methods: {
// 刷新工具详情数据
refreshToolDetail() {
if (this.news_slug) {
this.getNewsDetail(this.news_slug);
}
},
// 获取新闻详情
async getNewsDetail(newsSlug) {
const {data: res} = await this.$api.article.getArticleDetail(newsSlug);
const {code, data} = res;
if (code === 0 && data) {
this.news_detail = {...data};
this.newsDetail = {...data, extra: this.stringJsonToObject(data.extra || '[]')};
// 处理socialLinks
if (this.newsDetail.extra && Array.isArray(this.newsDetail.extra.socialLinks)) {
const socialLinks = this.newsDetail.extra.socialLinks;
// 查找Website选项的索引
const websiteIndex = socialLinks.findIndex(item => item && item.key === 'Website');
// 如果找到Website选项且不在第一个位置则移到第一个位置
if (websiteIndex !== -1 && websiteIndex !== 0) {
const websiteItem = socialLinks.splice(websiteIndex, 1)[0];
socialLinks.unshift(websiteItem);
}
}
}
},
formatPublishTime(timeString) {
if (!timeString) return '';
const date = new Date(timeString);
const months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];
const month = months[date.getMonth()];
const day = String(date.getDate()).padStart(2, '0');
const year = date.getFullYear();
return `${month} ${day} ${year}`;
},
stringJsonToObject(str) {
// 将json字符串转为对象捕获错误当str为空或者转对象失败时默认返回空数组
try {
@ -161,63 +177,116 @@ export default {
},
goToViewMore() {
// 返回上一页
this.$router.go(-1);
this.$router.push('/launches');
},
handleCommentCountUpdate(count) {
this.commentCount = count;
},
goToFinanceDetail() {
if (!this.news_slug) {
return false;
}
this.$router.push('/finance-detail?news_slug=' + this.news_slug)
},
async getArticleListData() {
const params = {page: 1, limit: 4, articleType: 'launches'};
const {data: res} = await this.$api.article.getArticleList(params);
const {code, data} = res;
if (code === 0 && data.list && Array.isArray(data.list)) {
let processedList = [...data.list];
// 判断列表中是否有与当前详情id一致的项
if (this.newsDetail && this.newsDetail.id) {
const currentIdIndex = processedList.findIndex(item => item && item.id === this.newsDetail.id);
if (currentIdIndex !== -1) {
// 有一致的id删除该项
processedList.splice(currentIdIndex, 1);
} else if (processedList.length > 0) {
// 没有一致的id删除最后一个项
processedList = processedList.slice(0, -1);
}
}
this.otherList = processedList;
}
},
async onLoad() {
this.news_slug = this.$route.query.news_slug;
if (this.news_slug) {
await this.getNewsDetail(this.news_slug);
await this.getArticleListData();
}
}
},
watch: {
'$route'(to, from) {
// 当路由参数发生变化时重新加载数据
if (to.query.news_slug !== from.query.news_slug) {
this.news_slug = to.query.news_slug;
if (this.news_slug) {
this.getNewsDetail(this.news_slug);
}
this.onLoad();
}
}
},
mounted() {
this.news_slug = this.$route.query.news_slug;
if (this.news_slug) {
this.getNewsDetail(this.news_slug);
}
this.onLoad();
},
computed: {
tagList() {
if (!this.news_detail.tags) {
if (!this.newsDetail.tags) {
return [];
}
return this.stringJsonToObject(this.news_detail.tags || '[]');
return this.stringJsonToObject(this.newsDetail.tags || '[]');
}
},
}
</script>
<style scoped lang="scss">
.mt-24 {
margin-top: 24px;
}
.color-01 {
color: #7B61FF;
}
.mt-20 {
margin-top: 20px;
}
.mt-30 {
margin-top: 30px;
}
.card {
padding: 20px;
background-color: #FFFFFF;
border-radius: 12px;
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.08);
}
.scroll {
max-height: 500px; overflow-y: auto; overflow-x: hidden;
}
.content {
padding-top: 100px;
padding-top: 25px;
padding-bottom: 100px;
.bread-menu {
font-size: $mid-font-size;
font-family: 'Poppins-Medium';
margin-bottom: 25px;
.crumbs {
font-family: 'Poppins-SemiBold';
font-weight: 600;
}
}
.diver {
margin-top: 20px;
margin-top: 30px;
border-top: 4px solid #E2E8F0;
margin-bottom: 20px;
margin-bottom: 30px;
}
.title-text {
font-size: 34px;
color: #1E293B;
font-family: 'Poppins-SemiBold', serif;
font-family: 'Poppins-SemiBold';
font-weight: 600;
margin-top: 80px;
}
.rate-box {
@ -244,7 +313,7 @@ export default {
align-items: center;
gap: 8px;
color: #1E293B;
font-family: 'Poppins-Medium', serif;
font-family: 'Poppins-Medium';
img {
width: 24px;
@ -253,7 +322,7 @@ export default {
}
.item-content {
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
color: #64748B;
}
}
@ -269,7 +338,7 @@ export default {
width: 80%;
.tag-item {
font-family: 'Poppins-Medium', serif;
font-family: 'Poppins-Medium';
flex-shrink: 0;
padding: 4px 12px;
border-radius: 12px;
@ -290,7 +359,7 @@ export default {
.content-title {
margin-left: 6px;
font-family: 'Poppins-SemiBold', serif;
font-family: 'Poppins-SemiBold';
font-size: 24px;
color: #1E293B;
font-weight: 600;
@ -306,9 +375,13 @@ export default {
border-radius: 6px;
padding: 8px 15px;
border: 1px solid #e2e8f0;
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
color: #64748B;
&:active {
opacity: 0.8;
}
img {
width: 16px;
height: 16px;
@ -322,7 +395,7 @@ export default {
background-color: #F5F4FF;
border-radius: 12px;
.text {
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
color: #64748B;
font-size: 18px;
}
@ -332,7 +405,7 @@ export default {
border-radius: 6px;
padding: 8px 15px;
border: 1px solid #e2e8f0;
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
color: #64748B;
img {
@ -344,13 +417,13 @@ export default {
}
}
.related {
margin-top: 60px;
margin-top: 40px;
.related-title {
.title {
margin-bottom: 20px;
font-size: 30px;
color: #1E293B;
font-family: 'Poppins-SemiBold', serif;
font-family: 'Poppins-SemiBold';
font-weight: 600;
&::before {
content: '';
@ -367,12 +440,19 @@ export default {
text-align: right;
color: $grey-color;
font-size: $mid-font-size;
font-family: 'Poppins-Regular', serif;
font-family: 'Poppins-Regular';
}
}
.list {
gap: 60px;
margin: 40px 10px 40px 10px;
display: grid;
grid-template-columns: repeat(3, 1fr);
.item {
width: 100%;
overflow: hidden;
min-height: 110px;
}
}
}
}