Files
AIProd/pages/ToolDetail/index.vue

379 lines
8.9 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="normal-container" class="tool-detail">
<IntegratedLayout>
<div class="content">
<!--面包屑-->
<div class="bread-menu">
<span>Home</span>
<i class="el-icon-arrow-right"></i>
<span>{{ category_slug }}</span>
<i class="el-icon-arrow-right"></i>
<span class="crumbs gradient-color">{{ tool_detail.name || '' }}</span>
</div>
<!--标题-->
<p class="title-text">{{ tool_detail.name || '' }}</p>
<!--工具内容-->
<div class="tool-content">
<div class="left-content flex flex-col">
<div class="terms-item">
<div class="item-title">
<img src="/ToolDetail/icon_star.png" alt="">
<span>Score: </span>
</div>
<div class="item-content color-01">
{{ (tool_detail.rating || 0).toFixed(1) }}
</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">
{{ tool_detail.memo || '' }}
</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(tool_detail.updatedAt || '') }}</div>
</div>
<div class="tags">
<div class="tag-item" v-for="(it, index) in tagList" :key="index">{{ it.value }}</div>
</div>
</div>
<div class="right-content">
<div class="flex flex-top-right mb-20 mr-25">
<div class="flex gap-20">
<ThumbBtn
:like-count="tool_detail.likeCount || 0"
:id="tool_detail.id"
type="tool"
@like-success="refreshToolDetail"
/>
<CommentBtn :comment-count="commentCount" />
<a :href="tool_detail.url || ''" class="link-button">
<img src="/ToolDetail/icon_link.png" alt="" class="wh-16" />
<span>Visit website</span>
</a>
</div>
</div>
<div class="card-website-view">
<iframe :src="tool_detail.url || ''" class="scaled-iframe"></iframe>
</div>
</div>
</div>
<MyTabs v-model="activeName" @tab-click="handleClick">
<MyTabPane label="Product information" name="product">
<Product
:other-tools="other_tools"
:tool-id="tool_detail.id || 0"
:tool-slug="tool_slug || ''"
:category-slug="category_slug || ''"
:tool_content="tool_detail.description || ''"
:category-name="category_name || ''"
/>
</MyTabPane>
<MyTabPane label="Comment" name="comment"></MyTabPane>
</MyTabs>
<Comment comment-type="tool" :id="tool_detail.id" @update:commentCount="handleCommentCountUpdate" />
</div>
</IntegratedLayout>
</div>
</template>
<script>
import Product from "@/pages/ToolDetail/Product/index.vue";
import Comment from "@/pages/ToolDetail/Comment/index.vue";
import ThumbBtn from "@/pages/ToolDetail/components/ThumbBtn.vue";
import CommentBtn from "@/pages/ToolDetail/components/CommentBtn.vue";
import Rate from "@/components/Rate.vue";
import MyTabs from '@/components/MyTabs/MyTabs.vue';
import MyTabPane from '@/components/MyTabs/MyTabPane.vue';
export default {
components: {
Product,
Comment,
ThumbBtn,
CommentBtn,
Rate,
MyTabs,
MyTabPane,
},
data() {
return {
activeName: 'product',
tool_slug: '',
tool_detail: {},
commentCount: 0,
other_tools: [],
category_slug: '',
category_name: '',
}
},
mounted() {
this.tool_slug = this.$route.query.tool_slug;
this.category_slug = this.$route.query.category_slug;
this.category_name = this.$route.query.category_name || '';
this.getAsyncToolDetailData();
this.getAsyncOtherTools();
},
watch: {
'$route'(to, from) {
// 当路由参数发生变化时重新加载数据
if (to.query.tool_slug !== from.query.tool_slug) {
this.tool_slug = to.query.tool_slug;
this.category_slug = to.query.category_slug;
this.category_name = to.query.category_name || '';
this.resetAndReloadData();
}
}
},
methods: {
// 刷新工具详情数据
async refreshToolDetail() {
await this.getAsyncToolDetailData();
},
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}`;
},
// 获取详情数据
async getAsyncToolDetailData() {
if (this.tool_slug) {
const {data: res} = await this.$api.tool.getToolDetailBySlug(this.tool_slug);
const {code, data} = res;
if (code === 0 && data) {
this.tool_detail = {...data};
}
}
},
stringJsonToObject(str) {
// 将json字符串转为对象捕获错误当str为空或者转对象失败时默认返回空数组
try {
return JSON.parse(str);
} catch (e) {
console.error('Error parsing JSON string:', e);
return [];
}
},
handleCommentCountUpdate(count) {
this.commentCount = count;
},
handleClick() {},
// 添加重置和重新加载数据的方法
resetAndReloadData() {
// 重置数据
this.tool_detail = {};
this.commentCount = 0;
this.other_tools = [];
// 重新加载数据
this.getAsyncToolDetailData();
this.getAsyncOtherTools();
},
// 获取其他工具
async getAsyncOtherTools() {
if (!this.category_slug) {
return false;
}
const params = {categorySlug: this.category_slug, page: 1, limit: 9};
const {data: res} = await this.$api.tool.getToolsList(params);
const {code, data} = res;
if (code === 0 && data.list) {
this.other_tools = data.list;
}
},
},
computed: {
tagList() {
return this.stringJsonToObject(this.tool_detail.tags || '[]');
}
},
}
</script>
<style scoped lang="scss">
.gap-20 {
gap: 20px;
}
.mb-20 {
margin-bottom: 20px;
}
.mr-25 {
margin-right: 25px;
}
.color-01 {
color: #7B61FF;
}
.tool-detail {
flex: 1;
overflow-y: auto;
position: relative;
.content {
padding-top: 25px;
padding-bottom: 100px;
min-height: 100vh;
.bread-menu {
font-size: $mid-font-size;
font-family: 'Poppins-Medium';
font-weight: 600;
.crumbs {
font-family: 'Poppins-SemiBold';
font-weight: 600;
}
}
.title-text {
font-size: 34px;
color: #1E293B;
font-family: 'Poppins-SemiBold';
font-weight: 600;
margin-top: 16px;
margin-bottom: 20px;
}
.tool-content {
display: flex;
margin-top: 20px;
gap: 50px;
.left-content {
gap: 20px;
flex: 1;
.terms-item {
margin-bottom: 20px;
display: flex;
align-items: flex-start;
gap: 7px;
.item-title {
display: flex;
align-items: center;
gap: 8px;
color: #1E293B;
font-family: 'Poppins-Medium';
img {
width: 24px;
height: 24px;
}
}
.item-content {
font-family: 'Poppins-Regular';
color: #64748B;
}
}
.tags {
display: flex;
overflow-x: auto;
gap: 12px;
scrollbar-width: none;
-ms-overflow-style: none;
user-select: none;
flex-wrap: wrap;
width: 80%;
.tag-item {
flex-shrink: 0;
padding: 4px 12px;
border-radius: 12px;
@include gradient-border($linear-gradient-start, $linear-gradient-end);
}
}
}
.right-content {
width: 450px;
.card-website-view {
width: 100%;
height: 268px;
background-color: #FFFFFF;
border-radius: 16px;
box-shadow: 0 10px 30px 0 #0000000d;
padding: 19px 25px;
.scaled-iframe {
width: 1000%;
height: 1000%;
transform: scale(0.1);
transform-origin: 0 0;
pointer-events: none;
border: none;
}
}
.link-button {
width: 280px;
height: 44px;
background: $header-backgroungd;
border-radius: 12px;
margin: auto;
color: #fff;
font-family: 'Poppins-SemiBold';
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
&:hover {
opacity: 0.8;
}
&:active {
opacity: 0.8;
}
}
}
}
}
:deep(.el-tabs) {
.el-tabs__header {
.el-tabs__nav-wrap::after {
height: 4px !important;
background-color: #E2E8F0 !important;
}
.el-tabs__item {
font-weight: 700 !important;
font-size: 20px !important;
margin-bottom: 14px;
&.is-active {
background: linear-gradient(90deg, $linear-gradient-start, $linear-gradient-end);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: transparent;
}
}
.el-tabs__active-bar {
height: 4px !important;
background: linear-gradient(90deg, $linear-gradient-start, $linear-gradient-end) !important;
}
}
}
}
</style>