对接数据
This commit is contained in:
110
pages/Home/components/PopularToolList.vue
Normal file
110
pages/Home/components/PopularToolList.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
pop_tools: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getToolsAsyncData() {
|
||||
const {data: res} = await this.$api.tool.getToolsList({isHot: 1, page: 1, limit: 6});
|
||||
const {code, data} = res;
|
||||
if (code === 0 && data.list) {
|
||||
this.pop_tools = data.list;
|
||||
}
|
||||
},
|
||||
// 跳转工具详情页
|
||||
goToToolDetail(item) {
|
||||
if (item.slug && item.categorySlug) {
|
||||
this.recordToolClick(item);
|
||||
this.$router.push(`/detail?tool_slug=${item.slug}&category_slug=${item.categorySlug}`);
|
||||
}
|
||||
},
|
||||
// 记录工具点击次数
|
||||
async recordToolClick(item) {
|
||||
if (item.id) {
|
||||
await this.$api.tool.recordToolClickNum(item.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getToolsAsyncData();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="clearfix">
|
||||
<img src="/logo/hot.png" :style="{marginRight: '6px'}" alt=""/>
|
||||
Popular Tools
|
||||
</div>
|
||||
<div class="line" />
|
||||
<div class="pop-item">
|
||||
<div v-for="item in pop_tools" class="box" @click="goToToolDetail(item)">
|
||||
<div class="img-box">
|
||||
<img :src="item.iconUrl || ''" alt=""/>
|
||||
</div>
|
||||
<div class="tool-name">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.clearfix {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
font-size: $larg-font-size;
|
||||
font-weight: bold;
|
||||
font-family: 'Poppins-SemiBold', serif;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 15px 0 12px;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #E2E8F0;
|
||||
@include flex-center;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.15);
|
||||
cursor: pointer;
|
||||
@include gradient-border($linear-gradient-start, $linear-gradient-end);
|
||||
box-shadow: 0 5px 7px 0 #00000014;
|
||||
}
|
||||
}
|
||||
|
||||
.pop-item {
|
||||
display: grid;
|
||||
grid-auto-rows: 1fr;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.tool-name {
|
||||
font-family: 'Poppins-Medium', serif;
|
||||
color: #64748B;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
pages/Home/components/ScrollList.vue
Normal file
40
pages/Home/components/ScrollList.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<script>
|
||||
import BScroll from '@better-scroll/core';
|
||||
|
||||
export default {
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.bs = new BScroll(this.$refs.wrapper, {
|
||||
scrollX: true,
|
||||
scrollY: false,
|
||||
click: true,
|
||||
bounce: false
|
||||
})
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.bs && this.bs.destroy()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="wrapper" class="btn-wrapper">
|
||||
<div class="btn-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.btn-wrapper {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%
|
||||
}
|
||||
.btn-content {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
width: max-content;
|
||||
}
|
||||
</style>
|
||||
113
pages/Home/components/ToolItemCard.vue
Normal file
113
pages/Home/components/ToolItemCard.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
categorySlug: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
goToToolDetail() {
|
||||
if (this.config.slug && this.categorySlug) {
|
||||
this.recordToolClick(this.config);
|
||||
this.$router.push(`/detail?tool_slug=${this.config.slug}&category_slug=${this.categorySlug}`);
|
||||
}
|
||||
},
|
||||
// 记录点击次数
|
||||
async recordToolClick(item) {
|
||||
if (item.id) {
|
||||
await this.$api.tool.recordToolClickNum(item.id);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 检测文本是否包含有效的HTML标签
|
||||
* @param {string} text - 要检测的文本
|
||||
* @returns {boolean} - 是否包含有效的HTML标签
|
||||
*/
|
||||
containsHtml(text) {
|
||||
if (!text || typeof text !== 'string') {
|
||||
return false;
|
||||
}
|
||||
// 简单检测常见HTML标签格式,实际项目中可能需要更复杂的验证
|
||||
const htmlRegex = /<[^>]*>/;
|
||||
return htmlRegex.test(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card-caontainer" @click="goToToolDetail">
|
||||
<div class="title">
|
||||
<img :src="config.iconUrl || '/'" alt="" />
|
||||
<span style="font-size: 18px">
|
||||
{{ config.name || '' }}
|
||||
</span>
|
||||
</div>
|
||||
<!--<div class="text"-->
|
||||
<!-- v-if="config.memo && containsHtml(config.memo + '</p>')"-->
|
||||
<!-- v-html="config.memo"></div>-->
|
||||
<div class="text">
|
||||
{{ config.memo ? config.memo : '' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card-caontainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.05);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
border: 1px solid #E2E8F0;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.08);
|
||||
@include gradient-border($linear-gradient-start, $linear-gradient-end);
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
@include gradient-border($linear-gradient-start, $linear-gradient-end);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: $main-font-color;
|
||||
font-size: $big-font-size;
|
||||
font-weight: 600;
|
||||
font-family: 'Poppins-SemiBold', serif;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
color: $grey-color;
|
||||
font-family: 'Poppins-Regular', serif;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
78
pages/Home/components/ToolList.vue
Normal file
78
pages/Home/components/ToolList.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="list">
|
||||
<div v-for="item in list" class="tools" @click="checkTool(item)">
|
||||
<span class="tool-card" :class="item.active?'checkedBg':''">
|
||||
<span class="content">
|
||||
<img :src="''" alt="" />
|
||||
<span>{{ item.name }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['list'],
|
||||
data() {
|
||||
return {
|
||||
ischeck: 'check',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.list.forEach(item => {
|
||||
this.$set(item, 'active', false)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
checkTool(item) {
|
||||
if (item.active) {
|
||||
this.$set(item, 'active', false);
|
||||
} else {
|
||||
// 否则,先重置所有项,再激活当前项
|
||||
this.list.forEach(i => this.$set(i, 'active', false));
|
||||
this.$set(item, 'active', true);
|
||||
this.$emit('tool-selected', item.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr); // 4列布局
|
||||
gap: 20px; // 网格间距
|
||||
|
||||
.tools {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.tool-card {
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 10px 30px 0 rgba(0, 0, 0, 0.05);
|
||||
border-radius: 12px;
|
||||
padding: 10px 16px;
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
font-family: 'Poppins-SemiBold', serif;
|
||||
|
||||
.content {
|
||||
@include display-flex;
|
||||
|
||||
img {
|
||||
margin-right: 5px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tools .checkedBg {
|
||||
color: $white;
|
||||
background: linear-gradient(90deg, $linear-gradient-start 22%, $linear-gradient-end 73%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
142
pages/Home/components/Toolbar.vue
Normal file
142
pages/Home/components/Toolbar.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div :id="id">
|
||||
<div class="bar-list">
|
||||
<div class="top-box">
|
||||
<div class="title-wrap">
|
||||
<img :src="`/logo/${tool.img}_check.png`" alt="" />
|
||||
<span class="title-text gradient-color">
|
||||
{{tool.categoryName}}
|
||||
</span>
|
||||
</div>
|
||||
<div @click="goToViewMore" class="more pointer" v-if="!tool.tagList">
|
||||
View more<i class="el-icon-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tool.tagList && tool.tagList.length">
|
||||
<ScrollList>
|
||||
<div class="tags">
|
||||
<div class="tag-item" v-for="(item,index) in tool.tagList" :key="index">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</ScrollList>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div @click="goToViewMore" class="more pointer" v-if="tool.tagList && tool.tagList.length">
|
||||
View more<i class="el-icon-arrow-right"></i>
|
||||
</div>
|
||||
<div class="item-card" v-if="tool.tools && tool.tools.length">
|
||||
<div v-for="(item, index) in tool.tools" :key="index" style="min-height: 110px">
|
||||
<ToolItemCard :config="item" :categorySlug="categorySlug" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollList from "~/pages/Home/components/ScrollList.vue";
|
||||
import ToolItemCard from "~/pages/Home/components/ToolItemCard.vue";
|
||||
|
||||
export default {
|
||||
components: {ToolItemCard, ScrollList},
|
||||
props: {
|
||||
tool: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
categorySlug: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 查看更多
|
||||
goToViewMore() {
|
||||
if (this.categorySlug) {
|
||||
this.$router.push('/home/more?category_slug=' + this.categorySlug)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.bar-list {
|
||||
margin: 50px 0;
|
||||
}
|
||||
|
||||
.top-box {
|
||||
@include display-flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.title-text {
|
||||
font-weight: 600;
|
||||
font-size: $larg-font-size;
|
||||
font-family: 'Poppins-SemiBold', serif;
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-top: 27px;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
gap: 12px;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
user-select: none;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
flex-shrink: 0;
|
||||
padding: 10px;
|
||||
@include gradient-border($linear-gradient-start, $linear-gradient-end);
|
||||
/* 显示抓取手势 */
|
||||
font-family: 'Poppins-SemiBold', sans-serif;
|
||||
color: #64748B;
|
||||
font-weight: 600;
|
||||
|
||||
&:active {
|
||||
// cursor: grabbing;
|
||||
/* 抓取中状态 */
|
||||
}
|
||||
}
|
||||
|
||||
.more {
|
||||
display: block;
|
||||
text-align: right;
|
||||
color: $grey-color;
|
||||
font-size: $mid-font-size;
|
||||
font-family: 'Poppins-Regular', serif;
|
||||
|
||||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.item-card {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user