产品列表等

master
yujialong 1 year ago
parent 2857302ae7
commit 4188d35f66
  1. 16
      src/api/index.js
  2. BIN
      src/assets/images/exp/1.png
  3. BIN
      src/assets/images/exp/2.png
  4. BIN
      src/assets/images/exp/3.png
  5. BIN
      src/assets/images/exp/4.png
  6. BIN
      src/assets/images/exp/5.png
  7. BIN
      src/assets/images/exp/6.png
  8. BIN
      src/assets/images/exp/7.png
  9. BIN
      src/assets/images/exts/pdf.png
  10. BIN
      src/assets/images/exts/pic.png
  11. BIN
      src/assets/images/exts/ppt.png
  12. BIN
      src/assets/images/exts/txt.png
  13. BIN
      src/assets/images/exts/video.png
  14. BIN
      src/assets/images/exts/word.png
  15. BIN
      src/assets/images/hot.png
  16. BIN
      src/assets/images/my-school.png
  17. BIN
      src/assets/images/product/1.png
  18. BIN
      src/assets/images/product/2.png
  19. BIN
      src/assets/images/product/3.png
  20. BIN
      src/assets/images/product/4.png
  21. BIN
      src/assets/images/product/5.png
  22. BIN
      src/assets/images/product/bg1.png
  23. BIN
      src/assets/images/product/bg2.png
  24. BIN
      src/assets/images/search1.png
  25. BIN
      src/assets/images/type.png
  26. 842
      src/pages/product/list/index.vue
  27. 687
      src/pages/product/show/index.vue
  28. 27
      src/router/modules/product.js
  29. 1
      src/setting.js

@ -1,3 +1,6 @@
import Setting from '@/setting'
const host = Setting.huoranApi
export default { export default {
newlyPublishedArticles: `iasf/sysContent/newlyPublishedArticles`, newlyPublishedArticles: `iasf/sysContent/newlyPublishedArticles`,
listWithTree: `iasf/sysColumn/listWithTree`, listWithTree: `iasf/sysColumn/listWithTree`,
@ -18,4 +21,17 @@ export default {
siteSearchArticles: `iasf/sysContent/siteSearchArticles`, siteSearchArticles: `iasf/sysContent/siteSearchArticles`,
oneLevelChecksThemAll: `iasf/sysColumn/oneLevelChecksThemAll`, oneLevelChecksThemAll: `iasf/sysColumn/oneLevelChecksThemAll`,
queryArticlesByColumnType: `iasf/sysColumn/queryArticlesByColumnType`, queryArticlesByColumnType: `iasf/sysColumn/queryArticlesByColumnType`,
listMarketing: `nakadai/nakadai/mall/marketing/promotion/pagingQueryList`,
// 产品中心
productCategoryList: `${host}nakadai/productClassification/productCategoryList`,
productTypeList: `${host}nakadai/productType/productTypeList`,
supplierList: `${host}nakadai/supplier/supplierList`,
tagsList: `${host}nakadai/tags/tagsList`,
listOfGoods: `${host}nakadai/mall/listOfGoods`,
detailsOfGoods: `${host}nakadai/mall/detailsOfGoods`,
addLearningRecord: `${host}nakadai/mallCourseLearningRecord/addLearningRecord`,
subjectCategoryCited: `${host}nakadai/nakadai/subject/subjectCategoryCited`,
goodsSchemeClassification: `${host}nakadai/mall/goodsSchemeClassification`,
schemeList: `${host}nakadai/nakadai/partner/schemeManagement/schemeList`,
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -0,0 +1,842 @@
<template>
<div class="wrap">
<el-carousel :interval="6000"
:height="carouselHeight">
<template v-for="(item, i) in banners">
<el-carousel-item :key="i">
<div :class="['banner-item', {'cursor-pointer': item.url}]"
@click="openLink(item)">
<img :src="item.banner"
alt=""
:style="{height: carouselHeight}">
<!-- <img src="https://huoran.oss-cn-shenzhen.aliyuncs.com/20230726/png/1684091617063493632.png"
alt=""
:style="{height: carouselHeight}"> -->
<div class="texts">
<h6>{{ item.title }}</h6>
<p class="sub">{{ item.subheading }}</p>
</div>
</div>
</el-carousel-item>
</template>
</el-carousel>
<div class="inner-wrap">
<div class="inner">
<div class="type-wrap">
<div>
<img src="@/assets/images/hot.png"
alt="">
<img class="m-l-5 m-r-10"
src="@/assets/images/type.png"
alt="">
<ul class="tab">
<li v-for="(tab, i) in tabs"
:key="i"
:class="{active: curTab === tab.id}"
@click="tabChange(tab)">{{ tab.name }}</li>
</ul>
</div>
<div class="search">
<img class="icon"
src="@/assets/images/search.png"
alt="">
<input type="text"
placeholder="请输入产品名称"
v-model="form.productName">
</div>
</div>
<div class="filter">
<dl v-if="curTab == 1"
style="align-items: center;">
<dt>学科专业</dt>
<div class="vals">
<dd :class="{active: categoryId === ''}"
@click="catetoryClick('')">全部</dd>
<dd :class="{active: categoryId === 1}"
style="margin-right: 20px"
@click="catetoryClick(1)">不限</dd>
<div v-for="(item, i) in category"
:key="i"
:class="['category-item', {active: item.disciplineId == categoryId}]">
<span class="name"
@click="nameClick(item, i)">{{ item.val.length ? item.name : item.categoryName }}</span>
<el-cascader :class="{active: item.disciplineId == categoryId}"
:ref="'category' + i"
v-model="item.val"
:options="item.list"
:props="{ checkStrictly: true }"
placeholder=""
@change="id => categoryChange(id, item, i)"></el-cascader>
</div>
</div>
</dl>
<dl v-if="curTab == 3">
<dt>产品标签</dt>
<div class="vals">
<dd :class="{active: form.tagId === ''}"
@click="filterChange('', 'tagId')">全部</dd>
<dd v-for="(item, i) in labels"
:key="i"
:class="{active: form.tagId === item.tagsId}"
@click="filterChange(item.tagsId, 'tagId')">{{ item.tagsName }}</dd>
</div>
</dl>
<template v-if="curTab == 4">
<dl>
<dt>方案分类</dt>
<div class="vals">
<dd :class="{active: form.productClassification === ''}"
@click="filterChange('', 'productClassification')">全部</dd>
<dd v-for="(item, i) in classifications"
:key="i"
:class="{active: form.productClassification === item.id}"
@click="filterChange(item.id, 'productClassification')">{{ item.classificationName }}</dd>
</div>
</dl>
<dl>
<dt>方案名称</dt>
<div class="vals">
<dd :class="{active: form.websiteMallId === ''}"
@click="filterChange('', 'websiteMallId')">全部</dd>
<dd v-for="(item, i) in schemes"
:key="i"
:class="{active: form.websiteMallId === item.id}"
@click="filterChange(item.id, 'websiteMallId')">{{ item.title }}</dd>
</div>
</dl>
</template>
<dl>
<dt>产品类型</dt>
<div class="vals">
<dd :class="{active: form.productType === ''}"
@click="filterChange('', 'productType')">全部</dd>
<dd v-for="(item, i) in types"
:key="i"
:class="{active: form.productType === item.typeId}"
@click="filterChange(item.typeId, 'productType')">{{ item.typeName }}</dd>
</div>
</dl>
<dl>
<dt>购买状态</dt>
<dd v-for="(item, i) in status"
:key="i"
:class="{active: form.purchaseStatus === item.id}"
@click="filterChange(item.id, 'purchaseStatus')">{{ item.name }}</dd>
</dl>
</div>
<div class="filter m-t-20">
<dl>
<dd v-for="(item, i) in sorts"
:key="i"
:class="{active: form.sort === item.id}"
@click="filterChange(item.id, 'sort')">{{ item.name }}</dd>
<dd :class="{active: form.sort === 2 || form.sort === 5}"
@click="sort">发布时间</dd>
<span class="caret"
@click="sort">
<i :class="['asc', {active: form.sort === 2}]"></i>
<i :class="['desc', {active: form.sort === 5}]"></i>
</span>
</dl>
</div>
<div class="courses">
<template v-if="list.length">
<ul>
<li v-for="(item, i) in list"
:key="i"
@click="toDetail(item.mallId)">
<img :src="item.coverDrawing"
alt="" />
<img v-if="item.logoOfOurSchool"
class="my-school"
src="@/assets/images/my-school.png"
alt="">
<div class="texts">
<el-tooltip effect="dark"
:visible-arrow="false"
:content="item.productName"
placement="bottom">
<div class="title">{{ item.productName }}</div>
</el-tooltip>
<div class="desc"
v-html="item.productIntroduction"></div>
<div class="tags">
<el-tooltip v-if="item.tagsName"
class="item"
effect="dark"
:visible-arrow="false"
:content="item.tagsName"
placement="bottom">
<div>
<el-tag v-for="(tag, i) in item.tagsName.split(',')"
:key="i"
class="tag">{{ tag }}</el-tag>
</div>
</el-tooltip>
</div>
<div :class="['metas']">
<el-tag v-if="item.selected"
type="danger"
effect="dark">
官方精选
</el-tag>
<el-tag v-if="item.typeName"
effect="dark">
{{ item.typeName }}
</el-tag>
<div v-if="item.isCourse"
class="meta">{{ item.learningCount }}人学过</div>
</div>
</div>
</li>
</ul>
<div class="pagination">
<el-pagination background
layout="total, prev, pager, next"
:page-size="pageSize"
:total="total"
@current-change="handleCurrentChange"
:current-page="page"></el-pagination>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script>
import { Loading } from "element-ui";
import Setting from "@/setting";
export default {
data () {
const that = this
return {
carouselHeight: '533px',
banners: [],
timer: null,
curTab: '',
tabs: [
{
id: '',
name: '全部'
},
{
id: 1,
name: '学科专业'
},
{
id: 2,
name: '官方精选'
},
{
id: 3,
name: '热门标签'
},
{
id: 4,
name: '解决方案'
},
],
form: {
isShelves: 0,
hotTag: 1,
productType: '',
productName: '',
purchaseStatus: '',
sort: 0,
tagId: '',
productClassification: '',
websiteMallId: ''
},
categoryId: '',
professionalCategoryId: '',
professionalId: '',
category: [],
labels: [],
subjectList: [], //
professionalClassList: [], //
professionalList: [], //
categoryName: '',
professionalCategoryName: '',
professionalName: '',
types: [],
classifications: [],
status: [
{
id: '',
name: '全部'
},
{
id: 1,
name: '已购买'
},
{
id: 2,
name: '未购买'
},
],
sorts: [
{
id: 0,
name: '综合排序'
},
{
id: 1,
name: '热销排行'
},
],
list: [],
keyword: '',
total: 0,
page: 1,
pageSize: 12,
searchTimer: null,
loadIns: null
};
},
watch: {
'form.productName': function (val) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.initData();
}, 500);
}
},
mounted () {
this.carouselHeight = parseInt(window.innerWidth / 3.6) + 'px'
// this.getBanner()
this.getSubject()
this.getLabel()
this.getClassification()
this.getSchemes()
this.getClass()
this.initData()
},
methods: {
// banner
getBanner () {
this.$post(this.api.listMarketing, {
pageNum: 1,
pageSize: 1000,
isOpen: 0
}).then(({ page }) => {
this.banners = page.records
}).catch(res => { })
},
//
getData () {
this.loadIns = Loading.service()
this.$post(this.api.listOfGoods, {
...this.form,
pageNum: this.page,
pageSize: this.pageSize,
categoryId: this.categoryId,
professionalCategoryId: this.categoryId ? this.professionalCategoryId : '',
professionalId: this.categoryId ? this.professionalId : '',
}).then(({ page }) => {
const list = page.records
list && list.forEach(e => {
//
const el = document.createElement('div')
el.innerHTML = e.productIntroduction
e.productIntroduction = el.innerText
const cid = e.classificationId
e.isCourse = (cid === 1 || cid === 2) && !e.isAssociatedProduct
})
this.list = list ? list : []
this.total = page.total
this.loadIns.close()
}).catch(res => {
this.loadIns.close()
})
},
initData () {
this.page = 1;
this.getData();
},
//
getLabel () {
this.$get(this.api.tagsList).then(res => {
this.labels = res.tagsList
}).catch(err => { })
},
//
getClassification () {
this.$get(this.api.goodsSchemeClassification).then(({ data }) => {
this.classifications = data
}).catch(err => { })
},
//
getSchemes () {
this.$post(this.api.schemeList, {
pageNum: 1,
pageSize: 10000,
querySource: 3, //(3. 4.)
}).then(({ data }) => {
this.schemes = data.records
}).catch(err => { })
},
//
async getSubject () {
//
this.$get(this.api.subjectCategoryCited).then(({ list }) => {
const result = []
const promises = []
list = list.filter(e => e.disciplineId != 1)
list.map((e, i) => {
//
e.professionalClasses.map(e => {
e.value = e.professionalClassId
e.label = e.professionalClassName
//
e.professionals.map(e => {
e.value = e.professionalId
e.label = e.professionalName
})
e.children = e.professionals
})
result.push({
val: [],
disciplineId: e.disciplineId,
name: e.disciplineName,
categoryName: e.disciplineName,
list: e.professionalClasses
})
})
Promise.all(promises).then(_ => {
console.log("🚀 ~ file: index.vue:378 ~ this.$get ~ result:", result)
this.category = result
})
}).catch(err => { })
},
nameClick (item, i) {
this.categoryId = item.disciplineId
this.professionalCategoryId = ''
this.professionalId = ''
this.$refs['category' + i][0].toggleDropDownVisible()
this.clearCategory()
this.initData()
},
//
clearCategory () {
const list = this.category
list.map(e => {
e.val = []
})
},
categoryChange (val, item, i) {
const name = this.$refs['category' + i][0].getCheckedNodes()[0].pathLabels
console.log("🚀 ~ file: index.vue:431 ~ categoryChange ~ val, item:", val, item, name)
item.name = item.categoryName + '/' + name.join('/')
this.clearCategory()
item.val = val
this.categoryId = item.disciplineId
this.professionalCategoryId = val[0] || ''
this.professionalId = val[1] || ''
this.initData()
},
// name
handleCategoryName () {
if (this.subjectList.length) {
const id = this.categoryId
const list = this.subjectList
if (list.length) {
if (id === '' || id === 1) {
this.categoryName = list[0].disciplineName
} else {
const item = list.find(e => e.disciplineId == id)
this.categoryName = item ? item.disciplineName : ''
}
}
}
if (this.professionalClassList.length) {
const id = this.professionalCategoryId
const list = this.professionalClassList
if (list.length) {
if (id === '' || id === 1) {
this.professionalCategoryName = list[0].professionalClassName
} else {
const item = list.find(e => e.professionalClassId == id)
this.professionalCategoryName = item ? item.professionalClassName : ''
}
}
}
if (this.professionalList.length) {
const id = this.professionalId
const list = this.professionalList
if (list.length) {
if (id === '' || id === 1) {
this.professionalName = list[0].professionalName
} else {
const item = list.find(e => e.professionalId == id)
this.professionalName = item ? item.professionalName : ''
}
}
}
},
//
catetoryClick (id) {
this.categoryId = id
this.professionalCategoryId = id
this.professionalId = id
this.clearCategory()
this.initData()
},
//
getClass () {
this.$get(this.api.productTypeList).then(res => {
this.types = res.typeList
}).catch(err => { })
},
// banner
openLink (item) {
item.url && window.open(item.url)
},
// tab
tabChange ({ id }) {
this.curTab = id
this.form.hotTag = 1
this.form.selection = ''
this.form.tagId = ''
this.form.productClassification = ''
this.form.websiteMallId = ''
if (id === 3) {
this.form.hotTag = 2
} else if (id === 2) {
this.form.selection = 1
}
this.initData()
},
//
filterChange (id, prop) {
this.form[prop] = id
this.initData()
},
//
sort () {
this.form.sort = this.form.sort === 2 ? 5 : 2
this.initData()
},
handleCurrentChange (val) {
this.page = val;
this.getData();
},
//
toDetail (id) {
this.$router.push(`show?id=${id}`);
}
}
};
</script>
<style lang="scss" scoped>
.wrap {
margin: -24px;
.banner-item {
position: relative;
img {
width: 100%;
height: 100%;
}
.texts {
position: absolute;
top: 50%;
left: 188px;
color: #fff;
transform: translateY(-50%);
}
h6 {
margin-bottom: 25px;
font-size: 44px;
}
.sub {
font-size: 22px;
}
}
.inner-wrap {
padding: 18px 0;
background: url(../../../assets/images/product/bg1.png) 0 159px no-repeat,
url(../../../assets/images/product/bg2.png) bottom right no-repeat;
background-color: #f3f6fa;
}
.inner {
width: 1146px;
margin: 0 auto;
}
.type-wrap {
display: flex;
justify-content: space-between;
margin-bottom: 18px;
.left {
display: inline-flex;
}
.tab {
display: inline-flex;
li {
position: relative;
margin: 0 20px;
font-size: 18px;
line-height: 25px;
color: #0b1d30;
cursor: pointer;
&:after {
content: '';
position: absolute;
bottom: -10px;
left: 50%;
width: 53px;
height: 4px;
transform: translateX(-50%);
}
&.active:after {
background-color: #9278ff;
}
}
}
}
.search {
position: relative;
display: flex;
align-items: center;
width: 410px;
height: 48px;
padding: 0 18px;
background-color: #fff;
border-radius: 31px;
input {
height: 40px;
margin-left: 7px;
font-size: 14px;
color: #333;
border: 0;
outline: none !important;
}
}
.filter {
padding: 5px 30px;
background-color: #fff;
border-radius: 10px;
dl {
position: relative;
display: flex;
flex-wrap: wrap;
margin: 20px 0;
dt {
min-width: 60px;
padding: 5px 0;
margin-right: 30px;
color: #666;
font-size: 14px;
font-weight: 600;
white-space: nowrap;
}
dd {
padding: 5px 15px;
color: #333;
font-size: 14px;
white-space: nowrap;
cursor: pointer;
&.active {
font-weight: 600;
color: #9278ff;
}
}
.category {
margin: 5px 10px 0;
}
/deep/.category-item {
display: inline-flex;
align-items: center;
margin-right: 20px;
.name {
position: relative;
font-size: 14px;
color: #333;
cursor: pointer;
& + .el-cascader {
width: 30px;
}
}
&.active {
.name {
color: #9278ff;
}
}
}
/deep/.el-cascader {
width: auto;
.el-input {
.el-input__inner {
font-size: 14px;
color: #333;
border: 0;
}
}
&.active {
.el-input .el-input__inner {
color: #9278ff;
}
}
}
}
.vals {
display: inline-flex;
flex-wrap: wrap;
align-items: center;
width: 920px;
}
.caret {
position: absolute;
top: 4px;
left: 250px;
width: 20px;
height: 20px;
cursor: pointer;
i {
position: absolute;
width: 0;
height: 0;
border: 5px solid transparent;
}
.asc {
top: 0;
border-bottom-color: #c0c4cc;
&.active {
border-bottom-color: #409eff;
}
}
.desc {
top: 12px;
border-top-color: #c0c4cc;
&.active {
border-top-color: #409eff;
}
}
}
}
.courses {
position: relative;
margin-top: 24px;
ul {
position: relative;
display: flex;
flex-wrap: wrap;
}
li {
position: relative;
width: calc((100% - 66px) / 4);
min-height: 250px;
margin: 0 22px 22px 0;
cursor: pointer;
border-radius: 10px;
background-color: #fff;
transition: all 0.3s;
overflow: hidden;
&:nth-child(4n) {
margin-right: 0;
}
img {
width: 100%;
height: 140px;
transition: 0.3s;
}
.my-school {
position: absolute;
top: 0;
right: 0;
width: 57px;
height: 22px;
}
.texts {
padding: 10px;
}
.title {
margin-bottom: 10px;
color: #0b1d30;
font-size: 14px;
font-weight: 600;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.desc {
min-height: 34px;
color: #757f92;
font-size: 12px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.tags {
height: 24px;
margin-top: 10px;
overflow: hidden;
}
.tag {
margin-right: 8px;
color: #9278ff;
background-color: #f9f9f9;
border: 0;
}
.type {
display: inline-block;
padding: 4px 11px;
font-size: 12px;
color: #666;
border: 1px solid #dadada;
border-radius: 20px;
}
.metas {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
&.not-selected {
justify-content: flex-end;
}
}
.meta {
display: inline-flex;
align-items: center;
color: #b5bfd5;
font-size: 12px;
.icon {
width: 14px;
max-height: 14px;
margin-right: 3px;
}
}
&:hover {
box-shadow: 0px 5px 12px 4px rgba(142, 123, 253, 0.09), 0px 3px 6px 0px rgba(142, 123, 253, 0.12),
0px 1px 2px -2px rgba(142, 123, 253, 0.16);
img {
transform: scale(1.05);
}
}
}
}
}
@media (max-width: 1500px) {
.wrap {
.banner-item {
h6 {
font-size: 35px;
}
.sub {
font-size: 18px;
}
}
}
}
</style>

@ -0,0 +1,687 @@
<template>
<div class="wrap">
<el-card shadow="hover"
class="m-b-20">
<div class="flex-between">
<el-page-header @back="$router.push('list')"
:content="form.mall.productName"></el-page-header>
</div>
</el-card>
<div class="inner">
<div class="top">
<div class="pics"
v-if="form.mall.coverDrawing">
<el-image :class="['cover', {full: !form.interfaceDiagrams}]"
:src="form.mall.coverDrawing"
:preview-src-list="form.pics">
</el-image>
<div v-if="form.interfaceDiagrams"
class="interface">
<div class="pic"
v-for="(pic, i) in form.interfaceDiagrams"
:key="i">
<el-image :src="pic"
:preview-src-list="form.pics">
</el-image>
</div>
</div>
</div>
<div class="right"
id="fields">
<h6>{{ form.mall.productName }}</h6>
<div v-if="isCourse"
class="meta">
<span class="val">{{ form.numberOfExperimentalItems }}</span> 个实验项目&emsp;&emsp;
已有 <span class="val">{{ form.goodsRes.learningCount }}</span>人学过
</div>
<div class="des"
v-html="form.mall.productIntroduction"></div>
<div class="fields">
<div class="field">
<img src="@/assets/images/product/1.png"
alt=""> 适用专业
<el-tooltip class="text"
effect="dark"
:visible-arrow="false"
:content="form.goodsRes.professionalName"
placement="bottom">
<div>
{{ form.goodsRes.professionalName }}
</div>
</el-tooltip>
</div>
<div v-if="form.mall.applicationScenario"
class="field">
<img src="@/assets/images/product/2.png"
alt=""> 适用场景
<div class="text">{{ form.mall.applicationScenario }}</div>
</div>
<div v-if="form.mall.matchingCourse"
class="field">
<img src="@/assets/images/product/3.png"
alt=""> 匹配课程
<el-tooltip class="text"
effect="dark"
:visible-arrow="false"
:content="form.mall.matchingCourse"
placement="bottom">
<div class="text">
{{ form.mall.matchingCourse }}
</div>
</el-tooltip>
</div>
<div v-if="form.mall.courseHours"
class="field">
<img src="@/assets/images/product/4.png"
alt=""> 预计课时
<div class="text">{{ form.mall.courseHours }}</div>
</div>
<div v-if="form.goodsRes.typeName"
class="field">
<img src="@/assets/images/product/5.png"
alt=""> 产品类型
<div class="text">{{ form.goodsRes.typeName }}</div>
</div>
</div>
<button v-if="!form.goodsRes.logoOfOurSchool"
class="btn"
@click="toTrail">试用体验</button>
<button v-else-if="isCourse || withLink"
class="btn entry"
@click="toStation">进入{{ isDataforward ? '系统' : '实验' }}</button>
<button v-else-if="isValueModule"
class="btn entry"
@click="toSystem">进入系统</button>
</div>
</div>
<div class="course">
<div class="detail">
<ul class="tab">
<li v-for="(tab, i) in tabs"
:key="i"
:class="{active: curTab === tab.id}"
@click="tabChange(tab)">{{ tab.name }}</li>
</ul>
<div class="courses">
<template v-if="!curTab">
<div class="des"
v-html="form.mall.detailedIntroduction"></div>
</template>
<template v-else>
<div class="chapter"
v-for="(item, i) in chapterList"
:key="i">
<div class="chapterName">{{ item.name }}</div>
<div class="section"
v-if="item.subsectionList.length">
<div v-for="(section, j) in item.subsectionList"
:key="j"
@click="toPreview(i, j)">
<div class="sectionName"
:title="section.name"
:class="{active: curLink === `${item.name}${section.name}`}">
<div class="val">
<img v-if="section.fileType === 'pptx'"
src="@/assets/images/exts/ppt.png"
alt="">
<img v-else-if="section.fileType === 'mp4'"
src="@/assets/images/exts/video.png"
alt="">
<img v-else-if="section.fileType === 'doc' || section.fileType === 'docx'"
src="@/assets/images/exts/word.png"
alt="">
<img v-else-if="section.fileType === 'txt'"
src="@/assets/images/exts/txt.png"
alt="">
<img v-else-if="section.fileType === 'pdf'"
src="@/assets/images/exts/pdf.png"
alt="">
<img v-else
src="@/assets/images/exts/pic.png"
alt="">
{{ section.name }}
</div>
<i v-if="!form.goodsRes.logoOfOurSchool"
class="icon el-icon-lock"></i>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
<div class="products">
<h6>热门产品推荐</h6>
<ul class="product">
<li v-for="(item, i) in hots"
:key="i"
@click="toDetail(item.mallId)">
<img :src="item.coverDrawing"
alt="" />
<img v-if="item.logoOfOurSchool"
class="my-school"
src="@/assets/images/my-school.png"
alt="">
<div class="texts">
<div class="title">{{ item.productName }}</div>
<div :class="['desc', {'not-tag': !item.tagsName}]"
v-html="item.productIntroduction"></div>
<div v-if="item.tagsName"
class="tags">
<el-tag v-for="(tag, i) in item.tagsName.split(',')"
:key="i"
class="tag">{{ tag }}</el-tag>
</div>
<div class="metas">
<el-tag v-if="item.selected"
type="danger"
effect="dark">
官方精选
</el-tag>
<div v-if="item.isCourse"
class="meta">{{ item.learningCount }}人学过</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<!-- 选择链接 -->
<el-dialog title="请选择链接"
:visible.sync="linkVisible"
width="420px"
center
:close-on-click-modal="false">
<div v-if="withLink"
class="buy">
<div v-for="(link, i) in form.mallNonAssociatedLinks"
:key="i"
class="link-line">
{{ link.urlName }}
<a class="url"
:href="link.url"
target="_blank">{{ link.url }}</a>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import Util from "@/libs/util"
import Setting from "@/setting"
export default {
data () {
return {
id: this.$route.query.id,
curTab: 0,
tabs: [
{
id: 0,
name: '详情介绍'
},
],
form: {
classificationIds: [],
mall: {
coverDrawing: ''
},
goodsRes: {}
},
chapterList: [],
hots: [],
linkVisible: false,
height: ''
};
},
computed: {
//
withLink () {
const links = this.form.mallNonAssociatedLinks
const id = this.form.classificationIds
return (this.form.mall.isAssociatedProduct || (id && id[0] == 5)) && links && links.length
},
//
isCourse () {
let id = this.form.classificationIds
if (id) {
id = id[0]
const isCourse = (id == 1 || id == 2) && this.form.mall.isAssociatedProduct === 0
this.tabs = [
{
id: 0,
name: '详情介绍'
},
]
isCourse && this.tabs.push({
id: 1,
name: '课程目录'
})
return isCourse
}
return false
},
//
isValueModule () {
let id = this.form.classificationIds
if (id) return id[0] == 3
return false
},
//
isDataforward () {
let id = this.form.classificationIds
if (id) return id[0] == 5
return false
},
},
mounted () {
this.getData()
this.getHot()
},
methods: {
//
getData () {
this.$get(`${this.api.detailsOfGoods}?mallId=${this.id}`).then(res => {
const e = res.orderDetails
e.pics = [e.mall.coverDrawing]
if (e.mall.interfaceDiagram) {
const pics = e.mall.interfaceDiagram.split(',').slice(0, 3)
e.pics.push(...pics)
e.interfaceDiagrams = pics
}
this.form = e
this.chapterList = ''
e.mall.associatedProduct && this.getChapter()
this.$nextTick(() => {
this.height = document.querySelector('#fields').clientHeight + 'px'
})
}).catch(err => { })
},
//
async getChapter () {
let res = await this.$get(this.api.curriculumChapter + '/' + this.form.mall.associatedProduct)
this.chapterList = res.chapterList
},
// tab
tabChange ({ id }) {
this.curTab = id
},
//
toPreview (i, j) {
this.form.goodsRes.logoOfOurSchool ?
this.$router.push(`/station/preview?courseId=${this.form.mall.associatedProduct}&curriculumName=${this.form.mall.productName}&mallId=${this.id}&chapter=${i}&section=${j}&admin=1`) :
this.toTrail()
},
// /
studySection (item) {
item.whetherToStudyOrNot ?
this.$post(`${this.api.deleteLearningProgress}?id=${item.learningProgressId}`).then(res => {
this.getChapter()
}).catch(res => { }) :
this.$post(this.api.saveLearningProgress, {
cid: this.form.mall.associatedProduct,
projectId: item.projectId,
}).then(res => {
this.getChapter()
}).catch(res => { })
},
//
toTrail () {
window.open('https://f.wps.cn/g/0sJLI4NA/')
},
//
toStation () {
if (this.isCourse) { //
this.$post(`${this.api.addLearningRecord}?mallId=${this.id}`).then(res => {
this.$router.push('/station/preview?courseId=' + this.form.mall.associatedProduct + '&curriculumName=' + this.form.mall.productName + '&mallId=' + this.id)
}).catch(res => { })
} else if (this.withLink) { //
this.linkVisible = true
}
},
//
toSystem () {
this.$router.push(`/match`)
},
//
getHot () {
this.$post(this.api.listOfGoods, {
hotTag: 1,
sort: 1,
pageNum: 1,
pageSize: 2,
}).then(({ page }) => {
const list = page.records
list.forEach(e => {
//
const el = document.createElement('div')
el.innerHTML = e.productIntroduction
e.productIntroduction = el.innerText
const cid = e.classificationId
e.isCourse = (cid === 1 || cid === 2) && !e.isAssociatedProduct
})
this.hots = list
}).catch(res => { })
},
//
toDetail (id) {
this.$router.push(`show?id=${id}`)
this.id = id
this.getData()
},
}
};
</script>
<style lang="scss" scoped>
.wrap {
padding: 24px;
margin: -24px;
background: url(../../../assets/images/product/bg1.png) 0 373px no-repeat,
url(../../../assets/images/product/bg2.png) bottom right no-repeat;
background-color: #f3f6fa;
.inner {
width: 1146px;
margin: 0 auto;
}
.top {
display: flex;
padding: 24px;
background-color: #fff;
border-radius: 10px;
.pics {
display: flex;
flex-wrap: wrap;
width: 502px;
margin-right: 20px;
.cover {
width: 370px;
height: 278px;
margin-right: 10px;
border-radius: 8px;
overflow: hidden;
&.full {
width: 100%;
}
}
.pic {
width: 114px;
height: 86px;
margin-bottom: 10px;
border-radius: 8px;
overflow: hidden;
.el-image {
width: 100%;
height: 100%;
}
&:last-child {
margin-bottom: 0;
}
}
}
.right {
width: 592px;
overflow: hidden;
}
h6 {
font-size: 24px;
font-weight: 600;
color: #2e2d31;
}
.meta {
margin: 10px 0;
font-size: 12px;
color: #2e2d31;
.val {
color: #007eff;
}
}
.des {
margin-bottom: 15px;
font-size: 14px;
color: #666;
line-height: 20px;
@include mul-ellipsis(3);
}
.field {
display: inline-flex;
align-items: center;
margin: 0 20px 10px 0;
font-size: 14px;
color: #2e2d31;
&:last-child {
margin-right: 0;
}
.label {
color: #333;
}
.text {
max-width: 160px;
@include ellipsis;
}
img {
margin-right: 5px;
}
}
.fields {
display: flex;
// justify-content: space-between;
flex-wrap: wrap;
height: 60px;
margin: 10px 0 10px;
overflow: hidden;
}
.btn {
width: 119px;
height: 46px;
color: #fff;
background: #64c25a;
border-radius: 6px;
border: 0;
cursor: pointer;
&:hover {
opacity: 0.9;
}
&:first-child {
margin-left: 11px;
}
}
.entry {
background: #007eff;
}
}
.tab {
display: inline-flex;
margin-left: 10px;
li {
position: relative;
margin-right: 20px;
font-size: 18px;
line-height: 25px;
color: #0b1d30;
cursor: pointer;
&:after {
content: '';
position: absolute;
bottom: -10px;
left: 50%;
width: 53px;
height: 4px;
transform: translateX(-50%);
}
&.active:after {
background-color: #007eff;
}
}
}
.course {
display: flex;
margin-top: 20px;
.detail {
width: 835px;
padding: 20px 24px;
background-color: #fff;
border-radius: 10px;
overflow: hidden;
}
}
.courses {
margin-top: 40px;
/deep/.des {
div,
p,
span {
font-family: PingFangSC-Regular !important;
}
}
.chapters {
margin-top: 16px;
max-height: calc(100% - 53px);
overflow: auto;
}
.chapter {
margin-bottom: 20px;
}
.chapterName {
padding: 0 12px;
margin-bottom: 15px;
color: #333;
font-size: 14px;
}
.sectionName {
position: relative;
display: flex;
justify-content: space-between;
padding: 12px;
font-size: 14px;
color: #666;
cursor: pointer;
border-radius: 8px;
@include ellipsis;
img {
margin-right: 8px;
}
&:hover {
color: #007eff;
background: #f6fbff;
}
.val {
display: inline-flex;
align-items: center;
}
.icon {
font-size: 18px;
color: #666;
}
}
}
.products {
width: 295px;
margin-left: 24px;
& > h6 {
font-size: 14px;
color: #0b1d30;
}
}
.product {
margin-top: 11px;
li {
position: relative;
margin-bottom: 15px;
cursor: pointer;
border-radius: 10px;
background-color: #fff;
transition: all 0.3s;
overflow: hidden;
img {
width: 100%;
height: 140px;
transition: 0.3s;
}
.my-school {
position: absolute;
top: 0;
right: 0;
width: 57px;
height: 22px;
}
.texts {
padding: 10px;
}
.title {
margin-bottom: 10px;
color: #0b1d30;
font-size: 14px;
font-weight: 600;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.desc {
min-height: 34px;
color: #757f92;
font-size: 12px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
&.not-tag {
-webkit-line-clamp: 3;
}
}
.tags {
margin-top: 10px;
}
.tag {
margin-right: 8px;
color: #007eff;
background-color: #f9f9f9;
border: 0;
}
.type {
display: inline-block;
padding: 4px 11px;
font-size: 12px;
color: #666;
border: 1px solid #dadada;
border-radius: 20px;
}
.metas {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
}
.meta {
display: inline-flex;
align-items: center;
color: #b5bfd5;
font-size: 12px;
.icon {
width: 14px;
max-height: 14px;
margin-right: 3px;
}
}
&:hover {
box-shadow: 0px 5px 12px 4px rgba(142, 123, 253, 0.09), 0px 3px 6px 0px rgba(142, 123, 253, 0.12),
0px 1px 2px -2px rgba(142, 123, 253, 0.16);
img {
transform: scale(1.05);
}
}
}
}
}
.link-line {
margin: 5px 0;
.url {
color: #007eff;
text-decoration: underline;
}
}
</style>

@ -0,0 +1,27 @@
import BasicLayout from "@/layouts/home";
const meta = {};
const pre = "product-";
export default {
path: "/product",
name: "product",
redirect: {
name: `${pre}list`
},
meta,
component: BasicLayout,
children: [
{
path: `list`,
component: () => import("@/pages/product/list"),
meta: { title: "产品中心" }
},
{
path: `show`,
component: () => import("@/pages/product/show"),
meta: { title: "产品详情" }
},
]
};

@ -15,6 +15,7 @@ const Setting = {
titleSuffix: '深圳或然科技官网', // 网页标题的后缀 titleSuffix: '深圳或然科技官网', // 网页标题的后缀
routerMode: "hash", // 路由模式,可选值为 history 或 hash routerMode: "hash", // 路由模式,可选值为 history 或 hash
apiBaseURL: host, // 接口请求地址 apiBaseURL: host, // 接口请求地址
huoranApi: `https://www.occupationlab.com/`,
autoLogoutTime: 3600000, // 长时间未操作,自动退出登录时间 autoLogoutTime: 3600000, // 长时间未操作,自动退出登录时间
modalDuration: 3, // 接口请求返回错误时,弹窗的持续时间,单位:秒 modalDuration: 3, // 接口请求返回错误时,弹窗的持续时间,单位:秒
errorModalType: "Message", // 接口请求返回错误时,弹窗的类型,可选值为 Message 或 Notice errorModalType: "Message", // 接口请求返回错误时,弹窗的类型,可选值为 Message 或 Notice

Loading…
Cancel
Save