产品分页和文章分类增加显示富文本模块;增加后台产品和文章详情预览页面

This commit is contained in:
hejin 2025-08-13 23:25:44 +08:00
parent 300019a7fc
commit f21acaa62a
4 changed files with 694 additions and 3 deletions

View File

@ -0,0 +1,217 @@
<template>
<!-- 新闻详情 -->
<div class="container article-page-info">
<div class="info-top">
<h2 class="article-page-title">{{ detais.title }}</h2>
<p class="article-page-date">{{ detais.createTime }}</p>
</div>
<div class="article-page-content">
<div class="rich-text" v-html="detais.content"></div>
<ContactFooter />
<div class="guide" :style="{'justify-content': prevarticle ? 'space-between' : 'flex-end'}">
<nuxt-link :to="`/article-page/${prevarticle.id}`" v-if="prevarticle">
{{ $t('common.prev') }}: {{ prevarticle.title }}
</nuxt-link>
<nuxt-link :to="`/article-page/${nextarticle.id}`" v-if="nextarticle">
{{ $t('common.next') }}: {{ nextarticle.title }}
</nuxt-link>
</div>
</div>
<!-- <div class="related-article-page mb-4">
<div class="pretty-header">
<h2>{{ $t('article.relatedarticle') }}</h2>
</div>
<nuxt-link
class="article-page-related-item box_shadow"
to="/"
v-for="item in 6"
:key="item"
>
<p class="text-ellipsis">he Importance Of Regular Maintenance For Your Flatbed Trailer</p>
<span>- Oct 25, 2024-</span>
</nuxt-link>
</div> -->
<div class="related-products" v-if="relatedProductList.length">
<div class="pretty-header">
<h2>{{ $t('news.relatedProducts') }}</h2>
</div>
<b-carousel
class="mt-3"
id="carousel-1"
:interval="4000"
controls
indicators
>
<b-carousel-slide v-for="item in relatedProductList" :key="item">
<template v-slot:img>
<ul class="row justify-content-around pl-0">
<li class="col-6 col-md-4 mb-4" v-for="son in item" :key="son.id">
<article class="product-card box_shadow">
<b-img
:src="son.mainPic"
fluid
:alt="son.title"
>
</b-img>
<div class="product-card-content">
<h4>{{ son.title }}</h4>
<div v-html="son.content"></div>
<!-- <p>{{ $t('common.truckModel') }}: ZZ4257N3247C1</p>
<p>{{ $t('common.dimension') }}: 6800x2490x3668</p>
<p>{{ $t('common.approachingAngle/Departure') }}: 16/70</p> -->
</div>
</article>
</li>
</ul>
</template>
</b-carousel-slide>
</b-carousel>
</div>
</div>
</template>
<script>
export default {
name: 'article-page-info',
data() {
return {
}
},
validate({query}) {
return query.private === '1BC0FFEC2294283E9BDB89E188FCA574' ? true : false
},
async asyncData({$axios, params }) {
let {
data:{
busiProdNew:detais,
next:nextarticle,
previous:prevarticle,
randomList
}
} = await $axios.get(`/web/prodNewsInfo?id=${params.id}`)
const chunkSize = 3;
const relatedProductList = [];
for (let i = 0; i < randomList.length; i += chunkSize) {
relatedProductList.push(randomList.slice(i, i + chunkSize));
}
return {
detais,
nextarticle,
prevarticle,
relatedProductList
}
},
head() {
return {
title: this.detais.title,
meta: [
{
hid: 'keywords',
name: 'keywords',
content: this.detais.prodKeyword
},
{
hid: 'description',
name: 'description',
content: this.detais.prodDescription
}
]
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.article-page-info {
.info-top {
padding-bottom: .5rem;
border-bottom: 1px solid #d8dde1;
.article-page-title {
margin: 1.5625rem 0;
margin-bottom: .9375rem;
color: #444444;
text-align: center;
font-size: 1.5rem;
}
.article-page-date {
color: #9e9e9e;
text-align: center;
font-size: .875rem;
}
}
.article-page-content {
margin-bottom: 2.6875rem;
.guide {
padding-top: .625rem;
display: flex;
align-items: center;
color: #515760;
border-top: 1px solid #d8dde1;
font-size: .875rem;
}
}
.related-article-page {
.news-related-item {
padding: 1.125rem 0;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #d8dde1;
font-size: .875rem;
p {
flex: 1;
color: #444444;
-webkit-line-clamp: 1;
}
span {
color: #9e9e9e;
}
}
}
.related-products {
#carousel-1 {
@mixin controls {
width: 2.875rem;
height: 2.875rem;
background-color: #7f7f7f;
background-size: 1.25rem;
}
::v-deep .carousel-control-prev-icon {
@include controls;
}
::v-deep .carousel-control-next-icon {
@include controls;
}
}
.product-card {
border: 1px solid #e6e8ec;
.product-card-content {
padding: 1.125rem 1rem;
border-top: .0625rem solid #e6e8ec;
font-size: .875rem;
h4 {
margin-bottom: .8125rem;
color: #212222;
font-size: .875rem;
}
p {
color: #878b90;
}
.view-details {
margin: 0 auto;
margin-top: 2rem;
}
}
}
}
}
</style>

View File

@ -0,0 +1,440 @@
<template>
<!-- 产品详情 -->
<div class="container products-info">
<div class="row pl-0">
<div class="products-preview col-md-5">
<div class="preview-img">
<no-ssr>
<vuePhotoZoomPro :url="currentImgUrl" :width="100" :scale="3"/>
</no-ssr>
</div>
<div class="products-scroll-box">
<no-ssr>
<swiper :options="swiperOptions">
<swiper-slide
v-for="src in productsImages"
:key="src"
class="products-img-item"
:class="src===currentImgUrl ? 'is-products-img-active' : ''"
>
<img @click="() => {currentImgUrl = src}" :src="src" :alt="prodDetail.title" />
</swiper-slide>
<div class="swiper-button-prev" slot="button-prev"></div>
<div class="swiper-button-next" slot="button-next"></div>
</swiper>
</no-ssr>
</div>
</div>
<article class="prod-details col-md-7">
<h2 class="products-title">
{{ prodDetail.title }}
</h2>
<div class="parameter" v-html="prodDetail.description"></div>
<div class="visit">
<a class="box_shadow" href="#form-target">
<img src="~assets/image/icon/email-highlight.png" :alt="$t('common.sendInquiry')" />
{{ $t('common.sendInquiry') }}
</a>
<a @click="chat" class="box_shadow" href="javascript:;">
<img src="~assets/image/icon/msg-highlight.png" :alt="$t('common.chatNow')" />
{{ $t('common.chatNow') }}
</a>
</div>
<ContactFooter />
</article>
</div>
<div class="mt-4">
<div class="pretty-header">
<h2>{{ $t('products.productDetails') }}</h2>
</div>
<div class="desc rich-text pt-3 pb-3" v-html="prodDetail.content"></div>
<div class="guide" :style="{'justify-content': prevNews ? 'space-between' : 'flex-end'}">
<nuxt-link :to="`/products/${prevNews.id}`" v-if="prevNews">
{{ $t('common.prev') }}: {{ prevNews.title }}
</nuxt-link>
<nuxt-link :to="`/products/${nextNews.id}`" v-if="nextNews">
{{ $t('common.next') }}: {{ nextNews.title }}
</nuxt-link>
</div>
</div>
<div class="mt-5">
<div class="pretty-header" id="form-target">
<h2>{{ $t('products.inquiry') }}</h2>
</div>
<div class="form-box box_shadow mt-3">
<b-form @submit="onSubmit">
<b-form-group
id="input-name"
label-for="input-name"
v-if="formConfig.name"
:invalid-feedback="$t('contactUs.name')"
>
<template v-solt:lable>
<div class="mb-2 d-flex align-items-center">
<b-img class="mr-1" src="~assets/image/icon/user.png" fluid :alt="$t('contactUs.name')"></b-img>
<h6 class="label-text">{{ $t('contactUs.name') }}</h6>
</div>
</template>
<b-form-input
id="input-name"
v-model="dataForm.name"
:required="formConfig.nameMust"
>
</b-form-input>
</b-form-group>
<b-form-group
id="input-title"
label-for="input-title"
v-if="formConfig.title"
:invalid-feedback="$t('contactUs.title')"
>
<template v-solt:lable>
<div class="mb-2 d-flex align-items-center">
<b-img class="mr-1" src="~assets/image/icon/title.png" fluid :alt="$t('contactUs.title')"></b-img>
<h6 class="label-text">{{ $t('contactUs.title') }}</h6>
</div>
</template>
<b-form-input
id="input-title"
v-model="dataForm.title"
:required="formConfig.titleMust"
>
</b-form-input>
</b-form-group>
<b-form-group
id="input-company"
label-for="input-company"
v-if="formConfig.company"
:invalid-feedback="$t('contactUs.company')"
>
<template v-solt:lable>
<div class="mb-2 d-flex align-items-center">
<b-img class="mr-1" src="~assets/image/icon/company.png" fluid :alt="$t('contactUs.company')"></b-img>
<h6 class="label-text">{{ $t('contactUs.company') }}</h6>
</div>
</template>
<b-form-input
id="input-company"
v-model="dataForm.company"
:required="formConfig.companyMust"
>
</b-form-input>
</b-form-group>
<b-form-group
id="input-mail"
label-for="input-mail"
v-if="formConfig.email"
:invalid-feedback="$t('contactUs.mail')"
>
<template v-solt:lable>
<div class="mb-2 d-flex align-items-center">
<b-img style="filter: brightness(0%);" class="mr-1" src="~assets/image/icon/email.png" fluid :alt="$t('contactUs.mail')"></b-img>
<h6 class="label-text">{{ $t('contactUs.mail') }}</h6>
</div>
</template>
<b-form-input
id="input-mail"
type="email"
v-model="dataForm.email"
:required="formConfig.emailMust"
>
</b-form-input>
</b-form-group>
<b-form-group
id="input-contactWay"
label-for="input-contactWay"
:invalid-feedback="$t('contactUs.contactWay')"
v-if="formConfig.tel"
>
<template v-solt:lable>
<div class="mb-2 d-flex align-items-center">
<b-img style="filter: brightness(0%);" class="mr-1" src="~assets/image/icon/labl-phone.png" fluid :alt="$t('contactUs.contactWay')"></b-img>
<h6 class="label-text">{{ $t('contactUs.contactWay') }}</h6>
</div>
</template>
<b-form-input
id="input-contactWay"
v-model="dataForm.tel"
:required="formConfig.telMust"
>
</b-form-input>
</b-form-group>
<b-form-group
id="input-desc"
label-for="input-desc"
:invalid-feedback="$t('contactUs.desc')"
>
<template v-solt:lable>
<div class="mb-2 d-flex align-items-center">
<b-img style="filter: brightness(0%);" class="mr-1" src="~assets/image/icon/msg.png" fluid :alt="$t('contactUs.desc')"></b-img>
<h6 class="label-text">{{ formConfig.content }}</h6>
</div>
</template>
<b-form-textarea
id="input-desc"
v-model="dataForm.content"
rows="3"
>
</b-form-textarea>
</b-form-group>
<div class="text-center">
<b-button class="w-25" type="submit" variant="primary" pill>
{{ $t('common.submit') }}
</b-button>
</div>
</b-form>
</div>
</div>
<!-- 聊天记录弹出框-->
<chat-form ref="chatFrom"></chat-form>
</div>
</template>
<script>
import vuePhotoZoomPro from 'vue-photo-zoom-pro';
import 'vue-photo-zoom-pro/dist/style/vue-photo-zoom-pro.css';
import chatForm from "../../products/chatForm.vue";
if (process.client) {
var { swiper, swiperSlide } = require("vue-awesome-swiper");
}
import "swiper/css/swiper.min.css";
export default {
name: 'products-info',
components: {
swiper,
swiperSlide,
vuePhotoZoomPro,
chatForm
},
data() {
return {
currentImgUrl: '',
dataForm: {
name: '',
title: '',
company: '',
email: '',
tel: '',
content: '',
equipment: this.$store.state.device
},
swiperOptions: {
slidesPerView :'auto',
spaceBetween: 10,
slidesPerGroupAuto:true,
grabCursor: true,
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
},
}
},
computed: {
isMob: () => {
return this.$store.state.device === '手机端' ? true : false
}
},
validate({query}) {
return query.private === 'CAC8F0807B8FB7EA72AF3596600F888A' ? true : false
},
async asyncData({$axios, params}) {
let {
data: {
busiProdNew: prodDetail,
pics,
next:nextNews,
previous:prevNews,
}
} = await $axios.get(`/web/prodNewsInfo?id=${params.id}`);
let picsArr = pics ? pics.split(',') : [];
let productsImages = [prodDetail.mainPic, ...picsArr];
let currentImgUrl = productsImages[0];
const { data: formConfig } = await $axios.get('/web/inquirySet')
return {
prodDetail,
currentImgUrl,
productsImages,
nextNews,
prevNews,
formConfig
}
},
head() {
return {
title: this.prodDetail.title,
meta: [
{
hid: 'keywords',
name: 'keywords',
content: this.prodDetail.prodKeyword
},
{
hid: 'description',
name: 'description',
content: this.prodDetail.prodDescription
}
]
}
},
methods: {
onSubmit(e) {
e.preventDefault()
this.$axios.post('/web/inquirySave', this.dataForm).then(() => {
this.$message.success(this.$t('common.submitSuccess'))
setTimeout(() => {
window.location.reload()
}, 1500)
})
},
/**打开聊天窗口,并建立连接*/
chat(){
this.$refs.chatFrom.show(this.prodDetail.id)
}
}
}
</script>
<style lang="scss" >
.is-products-img-active {
border: 1px solid #CC0001;
}
.swiper-button-next, .swiper-button-prev {
width: 1.1875rem;
height: 3.75rem;
top: 38%;
color: #ffffff;
background-color: rgba(198, 198, 198, 0.6);
opacity: 1 !important;
&::after{
font-size: unset;
}
}
.swiper-button-prev {
left: 0;
}
.swiper-button-next {
right: 0;
}
</style>
<style lang="scss" scoped>
.products-info {
padding-top: .9375rem;
.products-preview {
height: min-content;
.preview-img {
width: 100%;
max-height: 25.625rem;
border: 1px solid #d9dde0;
::v-deep .vue-photo-zoom-pro {
max-height: 25rem;
.img-region {
max-width: 100% !important;
max-height: 25rem;
}
}
}
.products-scroll-box {
width: 100%;
height: 6.25rem;
margin-top: .625rem;
.products-img-item {
width: 3.75rem !important;
height: 3.75rem !important;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
}
.prod-details {
.products-title {
width: 100%;
height: min-content;
min-height: 3.5rem;
margin-bottom: 1.4375rem;
padding: 0 1.4375rem;
display: flex;
align-items: center;
color: #fff;
background: url('~assets/image/backGroundImg/products-title.png') no-repeat;
background-size: 100% 100%;
font-family: 'SourceHanSansCN-Heavy';
font-size: 1rem;
}
.parameter {
height: min-content;
min-height: 17.5rem;
p {
color: #2d2d30;
font-size: .875rem;
}
}
.visit {
display: flex;
align-items: center;
a {
flex: 1;
height: 2.75rem;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
border-radius: 1.25rem;
font-size: .875rem;
&:first-child {
margin-right: .75rem;
background-color: #015fe8;
}
&:last-child {
background-color: #fc8815;
}
img {
width: 1rem;
height: 1rem;
margin-right: .5rem;
}
}
}
}
.guide {
padding-top: .625rem;
display: flex;
align-items: center;
color: #515760;
border-top: 1px solid #d8dde1;
font-size: .875rem;
}
.form-box {
padding: 2.125rem 7.8125rem;
border: 1px solid #ebeef0;
background: linear-gradient( 180deg, #FDFEFE 0%, #F5FBFF 100%);
border-radius: .5rem;
}
}
//
@media screen and (max-width:400px) {
.form-box {
padding: 1.25rem !important;
}
}
</style>

View File

@ -1,6 +1,7 @@
<template> <template>
<!-- 文章模块 --> <!-- 文章模块 -->
<section class="article-page-list"> <section class="article-page-list">
<div v-html="prodUp"></div>
<nuxt-link <nuxt-link
class="article-page-item box_shadow row mt-4" class="article-page-item box_shadow row mt-4"
:to="`/article-page/${item.id}?maxCatgId=${item.maxCatgId}&catgId=${item.catgId}`" :to="`/article-page/${item.id}?maxCatgId=${item.maxCatgId}&catgId=${item.catgId}`"
@ -24,7 +25,7 @@
</div> </div>
</article> </article>
</nuxt-link> </nuxt-link>
<div v-html="prodDown"></div>
<SeoPagination class="mt-4" :total="pordObj.total" /> <SeoPagination class="mt-4" :total="pordObj.total" />
</section> </section>
</template> </template>
@ -40,6 +41,14 @@ export default {
}, },
} }
}, },
computed: {
prodUp() {
return this.$store.state.device === '手机端' ? this.catgInfo.prodUpApp : this.catgInfo.prodUp
},
prodDown() {
return this.$store.state.device === '手机端' ? this.catgInfo.prodDownApp : this.catgInfo.prodDown
}
},
async asyncData({$axios,query}) { async asyncData({$axios,query}) {
let params = { let params = {
pageNum: query.pageNum || 1, pageNum: query.pageNum || 1,
@ -48,8 +57,17 @@ export default {
} }
let {data: pordObj} = await $axios('/web/newsPageList', {params}) let {data: pordObj} = await $axios('/web/newsPageList', {params})
let catgInfo = {}
if (params.catgId) {
var {data: catgInfoRes} = await $axios.get(`/web/categoryInfo?id=${query.catgId || query.maxCatgId ||''}`)
catgInfo = catgInfoRes
}
console.log(catgInfo);
return { return {
pordObj pordObj,
catgInfo
} }
}, },

View File

@ -1,6 +1,7 @@
<template> <template>
<!-- 产品列表 --> <!-- 产品列表 -->
<section class="products-list"> <section class="products-list">
<div v-html="prodUp"></div>
<ul class="row pl-0"> <ul class="row pl-0">
<li class="col-12 col-md-4" v-for="item in pordObj.records" :key="item.id"> <li class="col-12 col-md-4" v-for="item in pordObj.records" :key="item.id">
<nuxt-link <nuxt-link
@ -20,6 +21,7 @@
</nuxt-link> </nuxt-link>
</li> </li>
</ul> </ul>
<div v-html="prodDown"></div>
<SeoPagination class="mt-4" :total="pordObj.total" /> <SeoPagination class="mt-4" :total="pordObj.total" />
</section> </section>
</template> </template>
@ -35,17 +37,31 @@ export default {
}, },
} }
}, },
computed: {
prodUp() {
return this.$store.state.device === '手机端' ? this.catgInfo.prodUpApp : this.catgInfo.prodUp
},
prodDown() {
return this.$store.state.device === '手机端' ? this.catgInfo.prodDownApp : this.catgInfo.prodDown
}
},
async asyncData({$axios,query}) { async asyncData({$axios,query}) {
let params = { let params = {
pageNum: query.pageNum || 1, pageNum: query.pageNum || 1,
pageSize: 9, pageSize: 9,
catgId: query.catgId || query.maxCatgId ||'', catgId: query.catgId || query.maxCatgId ||'',
} }
let {data: pordObj} = await $axios('/web/prodPageList', {params}) let {data: pordObj} = await $axios.get('/web/prodPageList', {params})
let catgInfo = {}
if (params.catgId) {
var {data: catgInfoRes} = await $axios.get(`/web/categoryInfo?id=${query.catgId || query.maxCatgId ||''}`)
catgInfo = catgInfoRes
}
return { return {
pordObj, pordObj,
maxCatgId: query.maxCatgId || '', maxCatgId: query.maxCatgId || '',
catgInfo
} }
}, },
methods: { methods: {