实验台等

dev_202412
yujialong 8 months ago
parent 49b8141518
commit dc44e73e80
  1. 8
      public/index.html
  2. 3
      src/api/index.js
  3. BIN
      src/assets/img/my-school.png
  4. BIN
      src/assets/img/product/1.png
  5. BIN
      src/assets/img/product/2.png
  6. BIN
      src/assets/img/product/3.png
  7. BIN
      src/assets/img/product/4.png
  8. BIN
      src/assets/img/product/5.png
  9. BIN
      src/assets/img/product/bg1.png
  10. BIN
      src/assets/img/product/bg2.png
  11. 4
      src/layouts/header/index.vue
  12. 382
      src/pages/station/list/index.vue
  13. 65
      src/pages/station/preview/index.vue
  14. 671
      src/pages/station/product/index.vue
  15. 46
      src/router/modules/station.js

@ -3,21 +3,21 @@
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="keywords" content="智信云,教学,教育,在线编程" />
<meta name="keywords" content="职站,教学,教育,在线编程" />
<meta
name="description"
content="智信云是一家为高等院校提供实验课程数字化服务的国家级高新技术企业。以区块链、大数据、人工智能等前沿技术在行业领域的运用为支撑,将新技术与经济与管理人才培养深度融合,面向全国高等院校的经济、金融、大数据应用等相关专业提供科研创新、金课建设、实验实训教学软件、实践教学以及学生就业培训为一体的专业建设咨询与技术支持服务。"
content="职站是一款辅助院校教师开展虚拟仿真实验教学的智能云实践平台。平台采用了大数据,云计算等技术,为学校搭建信息化平台提供了基础,可助力院校实现教学智能化升级。职站平台设计遵循着极简、高效的理念,可帮助老师轻松开展实验教学,并支持自定义发布考核和练习,智能统计和检验学生的日常实训练习效果;老师还可以通过可视化图表报告直观查看学生实训成绩,评估教学成果。"
/>
<meta name="baidu-site-verification" content="code-TRfXe8xIkJ" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<!-- <meta name="viewport" content="width=device-width, user-scalable=yes, shrink-to-fit=no" /> -->
<link rel="stylesheet" href="//at.alicdn.com/t/font_830376_qzecyukz0s.css" />
<title></title>
<title>职站——为院校打造一站式虚拟仿真实训教学数智云平台</title>
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement('script');
hm.src = 'https://hm.baidu.com/hm.js?e4d7deeca2d6ea71d2bd5fa2365bc654';
hm.src = 'https://hm.baidu.com/hm.js?72fbad6ebf1d6c705117fe8fe0686a0e';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(hm, s);
})();

@ -47,6 +47,9 @@ export default {
maximumPracticeScoreList: 'occupationlab/occupationlab/achievement/maximumPracticeScoreList',
websiteProductList: `nakadai/mall/websiteProductList`,
getSandTableLastCache: `product/product/bank/operation/getSandTableLastCache`,
getSchoolCourseAuthority: `nakadai/nakadai/curriculum/getSchoolCourseAuthority`,
detailsOfGoods: `nakadai/mall/detailsOfGoods`,
requestRenewalNotice: `nakadai/nakadai/curriculum/requestRenewalNotice`,
// 课程笔记
addNote: `nakadai/curriculumNotes/addNote`,

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

@ -296,13 +296,13 @@ $height: 64px;
display: flex;
align-items: center;
font-size: 28px;
color: #568df2;
color: #333;
height: $height;
cursor: pointer;
img {
max-height: 50px;
margin-right: 10px;
margin-right: 20px;
}
}

@ -4,103 +4,134 @@
<div class="search">
<h6>创新实验智能教学</h6>
<div class="input">
<img src="@/assets/img/search.png"
alt="">
<input type="text"
placeholder="请输入关键词"
v-model="keyword">
<img src="@/assets/img/search.png" alt="">
<input type="text" placeholder="请输入关键词" v-model="keyword">
</div>
</div>
<div class="station">
<div class="inner">
<div class="tabs">
<a class="item"
v-for="(item, i) in tabs"
:key="i"
:class="{active: item.id == active}"
@click="tabChange(item)">{{ item.name }}</a>
<a class="item" v-for="(item, i) in tabs" :key="i" :class="{ active: item.classificationId === active }"
@click="tabChange(item)">{{ item.classificationName }}</a>
</div>
<div class="curs">
<template v-if="curriculumList.length">
<template v-for="(item,i) in curriculumList">
<div class="item"
:title="item.goodsName"
@click="goPreview(item)"
:key="i"
v-if="!keyword || item.goodsName.includes(keyword)">
<div v-if="isZj || isSq"
class="cover"
:style="{backgroundImage: 'url(/images/' + i + '.png)'}"></div>
<div v-else
class="cover"
:style="{backgroundImage: 'url(' + item.coverUrl + ')'}"></div>
<div class="bottom">
<p class="text"><span>{{ item.goodsName || item.curriculumName }}</span></p>
<a>进入实验</a>
</div>
<template v-if="products.length">
<div v-for="(item, i) in products" class="item" :title="item.goodsName" @click="toProduct(item)" :key="i">
<div v-if="isZj || isSq" class="cover" :style="{ backgroundImage: 'url(/images/' + i + '.png)' }"></div>
<div v-else class="cover" :style="{ backgroundImage: 'url(' + item.coverUrl + ')' }"></div>
<div class="bottom">
<p class="text"><span>{{ item.goodsName || item.curriculumName }}</span></p>
<a>{{ item.isInEffect ? '进入实验' : '续费' }}</a>
</div>
</template>
</div>
</template>
<div class="empty flex-1"
v-else>
<div class="empty flex-1" v-else>
<div>
<img src="@/assets/img/none.png"
alt="">
<img src="@/assets/img/none.png" alt="">
<p>暂无数据</p>
</div>
</div>
</div>
</div>
</div>
<!-- 选择链接 -->
<el-dialog title="请选择链接" :visible.sync="linkVisible" width="420px" center :close-on-click-modal="false">
<div class="buy">
<div v-for="(link, i) in links" :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 Setting from "@/setting";
import axios from "axios"
import Setting from '@/setting'
export default {
data () {
return {
isZj: location.host === '10.60.32.76', //
isSq: Setting.isSq,
keyword: this.$route.query.keyword || '',
active: +this.$route.query.active || 0,
tabs: [
{
id: 0,
name: '实验课程'
},
{
id: 1,
name: '最近使用'
}
],
curriculumList: []
active: +this.$route.query.active || '',
searchTimer: null,
tabs: [],
products: [],
linkVisible: false,
links: [],
}
},
watch: {
keyword: function () {
clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(this.getList, 500)
},
},
mounted () {
this.getschoolCourse();
this.getTab()
},
methods: {
getschoolCourse () { //
this.active ?
this.$post(this.api.recentUse, {
// tab
async getTab () {
const { data } = await this.$get(this.api.getSchoolCourseAuthority)
this.tabs = [
{
classificationId: '',
classificationName: '全部',
},
...data,
{
classificationId: 0,
classificationName: '最近使用',
},
]
this.getList()
},
//
async getList () {
// 使
if (this.active === 0) {
const { page } = await this.$post(this.api.recentUse, {
pageNum: 1,
pageSize: 100
}).then(({ page }) => {
this.curriculumList = page.records
}).catch(err => { }) :
this.$get(this.api.schoolCourse).then(res => {
this.curriculumList = res.data;
}).catch(err => { })
pageSize: 100,
goodsName: this.keyword,
})
this.curriculumList = page.records
} else {
const { data } = await this.$get(this.api.schoolCourse, {
authority: this.active,
goodsName: this.keyword,
})
this.products = data
}
},
goPreview (item) {
this.$router.push(`/station/preview?courseId=${item.cid}&curriculumName=${item.goodsName}&mallId=${item.mallId || ''}&keyword=${this.keyword}&active=${this.active}`);
toProduct (item) {
//
if (item.isInEffect) {
const links = item.nonAssociatedLinks
//
if (links && links.length) {
if (links.length === 1) {
window.open(links[0].url)
} else {
this.linkVisible = true
this.links = item.nonAssociatedLinks
}
} else {
this.$router.push(`/station/preview?courseId=${item.cid}&curriculumName=${item.goodsName}&mallId=${item.mallId || ''}&keyword=${this.keyword}&active=${this.active}`)
}
} else {
//
this.$router.push(`product?id=${item.mallId}`)
}
},
// tab
tabChange (item) {
this.active = item.id
this.getschoolCourse()
this.active = item.classificationId
this.getList()
},
}
};
@ -108,117 +139,142 @@ export default {
<style lang="scss" scoped>
.search {
position: relative;
padding: 100px 0 130px;
text-align: center;
background: url(../../../assets/img/station-bg.png) 0 0/100% 100% no-repeat;
h6 {
margin-bottom: 25px;
font-size: 26px;
color: #fff;
}
.input {
position: relative;
padding: 100px 0 130px;
text-align: center;
background: url(../../../assets/img/station-bg.png) 0 0/100% 100% no-repeat;
h6 {
margin-bottom: 25px;
font-size: 26px;
color: #fff;
}
.input {
position: relative;
width: 700px;
margin: 0 auto;
}
img {
position: absolute;
top: 19px;
left: 14px;
}
input {
width: 100%;
height: 62px;
line-height: 62px;
padding: 0 50px;
font-size: 18px;
color: #333;
border: 0;
outline: none;
border-radius: 4px;
}
width: 700px;
margin: 0 auto;
}
img {
position: absolute;
top: 19px;
left: 14px;
}
input {
width: 100%;
height: 62px;
line-height: 62px;
padding: 0 50px;
font-size: 18px;
color: #333;
border: 0;
outline: none;
border-radius: 4px;
}
}
.tabs {
display: flex;
justify-content: center;
align-items: center;
.item {
padding: 0 20px;
margin-right: 16px;
font-size: 17px;
text-align: center;
color: #333;
line-height: 50px;
border-bottom: 3px solid transparent;
cursor: pointer;
&.active {
color: #007eff;
border-color: #007eff;
}
display: flex;
justify-content: center;
align-items: center;
.item {
padding: 0 20px;
margin-right: 16px;
font-size: 17px;
text-align: center;
color: #333;
line-height: 50px;
border-bottom: 3px solid transparent;
cursor: pointer;
&.active {
color: #007eff;
border-color: #007eff;
}
}
}
.station {
min-height: calc(100vh - 520px);
background: url(../../../assets/img/station1.png) (top left) / auto no-repeat,
url(../../../assets/img/station2.png) bottom right/auto no-repeat;
.inner {
width: 1072px;
margin: 0 auto;
}
.curs {
min-height: calc(100vh - 520px);
background: url(../../../assets/img/station1.png) (top left) / auto no-repeat,
url(../../../assets/img/station2.png) bottom right/auto no-repeat;
.inner {
width: 1072px;
margin: 0 auto;
}
.curs {
display: flex;
flex-wrap: wrap;
padding-top: 60px;
.item {
position: relative;
padding: 0 10px;
margin: 0 10px 40px;
text-align: center;
background-color: #fff;
border-radius: 8px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
.cover {
width: 215px;
height: 118px;
margin-top: -20px;
border-radius: 12px;
background-size: 100% 100%;
background-position: 0 0;
background-repeat: no-repeat;
}
.bottom {
display: flex;
flex-wrap: wrap;
padding-top: 60px;
.item {
position: relative;
padding: 0 10px;
margin: 0 10px 40px;
text-align: center;
background-color: #fff;
border-radius: 8px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
.cover {
width: 215px;
height: 118px;
margin-top: -20px;
border-radius: 12px;
background-size: 100% 100%;
background-position: 0 0;
background-repeat: no-repeat;
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 5px;
}
.text {
display: inline-flex;
align-items: center;
width: 130px;
height: 40px;
text-align: left;
font-size: 12px;
line-height: 1.6;
overflow: hidden;
span {
@include mul-ellipsis(2);
}
}
a {
padding: 0 8px;
line-height: 28px;
font-size: 14px;
color: #7a7a7a;
border-radius: 20px;
border: 1px solid #dadada;
}
justify-content: space-between;
align-items: center;
padding: 10px 5px;
}
.text {
display: inline-flex;
align-items: center;
width: 130px;
height: 40px;
text-align: left;
font-size: 12px;
line-height: 1.6;
overflow: hidden;
span {
@include mul-ellipsis(2);
}
}
a {
padding: 0 8px;
line-height: 28px;
font-size: 14px;
color: #7a7a7a;
border-radius: 20px;
border: 1px solid #dadada;
}
}
}
}
.link-line {
margin: 5px 0;
.url {
color: #007eff;
text-decoration: underline;
}
}
</style>

@ -286,7 +286,7 @@
</template>
<script>
import util from "@/libs/util";
import Util from "@/libs/util";
import Setting from "@/setting";
import { mapState } from "vuex";
import pdf from "vue-pdf";
@ -381,7 +381,7 @@ export default {
archProject: '',
curReplyId: '',
maximumScores: [],
isAI: false,
third: '',
};
},
computed: {
@ -413,7 +413,7 @@ export default {
},
destroyed () {
//
util.local.get(Setting.tokenKey) && this.$post(this.api.playRecordSave, {
Util.local.get(Setting.tokenKey) && this.$post(this.api.playRecordSave, {
courseId: this.courseId,
courseType: 0,
playTime: Math.ceil((Date.now() - this.startTime) / 1000 / 60)
@ -519,11 +519,11 @@ export default {
//
submitNote () {
const form = this.noteForm
if (!form.noteName) return util.errorMsg('请输入笔记标题')
if (!form.noteContent) return util.errorMsg('请输入笔记内容')
if (!form.noteName) return Util.errorMsg('请输入笔记标题')
if (!form.noteContent) return Util.errorMsg('请输入笔记内容')
this.$post(this.api[form.noteId ? 'updateNote' : 'addNote'], form).then(res => {
this.getNote()
util.successMsg(form.noteId ? '修改成功' : '添加成功')
Util.successMsg(form.noteId ? '修改成功' : '添加成功')
this.showNoteAdd = false
this.noteForm = {
cid: this.courseId,
@ -538,7 +538,7 @@ export default {
type: "warning"
}).then(() => {
this.$post(`${this.api.deleteNotes}?noteId=${row.noteId}`).then(res => {
util.successMsg("删除成功");
Util.successMsg("删除成功");
this.getNote();
}).catch(res => { })
}).catch(() => { })
@ -598,7 +598,7 @@ export default {
type: "warning"
}).then(() => {
this.$post(`${this.api.deleteAComment}?commentId=${row.commentId}`).then(res => {
util.successMsg("删除成功");
Util.successMsg("删除成功");
this.getComment();
}).catch(res => { })
}).catch(() => { })
@ -606,7 +606,7 @@ export default {
//
submitComment (row, reply) {
const content = reply ? reply.replyContent : row ? row.replyContent : this.comment
if (!content) return util.errorMsg('请输入内容!')
if (!content) return Util.errorMsg('请输入内容!')
this.$post(this.api.addComment, {
mallId: this.mallId,
content,
@ -819,6 +819,15 @@ export default {
this.showProjectDia()
})
} else { // python
// python
const opened = +localStorage.getItem('opened')
if (opened) {
Util.errorMsg('Python系统限单页活跃,请切换至当前已有的活跃实验页面。', 5000)
return false
} else {
localStorage.setItem('opened', 1)
}
this.$get(this.api.getTheMostRecentlyRunProject, {
cid: this.courseId
}).then(({ data }) => {
@ -836,23 +845,23 @@ export default {
// python
toPython (projectId) {
const id = this.systemIds
let token = util.local.get(Setting.tokenKey);
util.cookies.set('assessmentId', '', -1)
util.cookies.set('startTime', '', -1)
util.cookies.set('stopTime', '', -1)
projectId ? util.cookies.set('projectId', projectId) : util.cookies.set('projectId', '', -1)
util.cookies.set('token', token)
util.cookies.set('courseId', this.courseId)
util.cookies.set('curriculumName', encodeURIComponent(this.curriculumName))
util.cookies.set('systemId', id)
util.cookies.set('mallId', this.mallId)
util.cookies.set('isAI', this.isAI)
util.cookies.set('isSubmit', '', -1)
util.cookies.set('fromManager', '', -1)
util.cookies.set('competitionId', '', -1)
util.cookies.set('language', '', -1)
util.cookies.set('className', '', -1)
util.cookies.set('loaded', '', -1)
let token = Util.local.get(Setting.tokenKey);
Util.cookies.set('assessmentId', '', -1)
Util.cookies.set('startTime', '', -1)
Util.cookies.set('stopTime', '', -1)
projectId ? Util.cookies.set('projectId', projectId) : Util.cookies.set('projectId', '', -1)
Util.cookies.set('token', token)
Util.cookies.set('courseId', this.courseId)
Util.cookies.set('curriculumName', encodeURIComponent(this.curriculumName))
Util.cookies.set('systemId', id)
Util.cookies.set('mallId', this.mallId)
Util.cookies.set('third', this.third) // python
Util.cookies.set('isSubmit', '', -1)
Util.cookies.set('fromManager', '', -1)
Util.cookies.set('competitionId', '', -1)
Util.cookies.set('language', '', -1)
Util.cookies.set('className', '', -1)
Util.cookies.set('loaded', '', -1)
// 8pythoncookiesystemId
location.href = process.env.NODE_ENV === 'development' ?
`http://${location.hostname}:8085/#/` :
@ -868,8 +877,8 @@ export default {
this.curProject = curProject.projectId
}
const { systemId } = curProject
let token = util.local.get(Setting.tokenKey)
this.isAI = curProject.type === 2
let token = Util.local.get(Setting.tokenKey)
this.third = curProject.type === 2 ? (curProject.systemId === 28 ? 'low' : 'ai') : ''
if (systemId == 11) {
//
location.href = `${Setting.systemPath}/#/index/list?curriculumName=${this.curriculumName}&token=${token}&cid=${this.courseId}&mallId=${this.mallId}&systemId=${this.systemIds}&projectId=${this.curProject}&assessmentId=&classId=&stopTime=&test=true`

@ -0,0 +1,671 @@
<template>
<div class="wrap">
<div class="inner">
<breadcrumb :routes.sync="routes" />
<div class="top">
<el-carousel class="pics" :interval="6000" height="278px" :arrow="form.pics.length > 1 ? 'hover' : 'never'"
:indicator-position="form.pics.length > 1 ? '' : 'none'">
<el-carousel-item v-for="(item, i) in form.pics" :key="i">
<img class="pic" :src="item" />
</el-carousel-item>
</el-carousel>
<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/img/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/img/product/2.png" alt=""> 适用场景
<div class="text">{{ form.mall.applicationScenario }}</div>
</div>
<div v-if="form.mall.matchingCourse" class="field">
<img src="@/assets/img/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/img/product/4.png" alt=""> 预计课时
<div class="text">{{ form.mall.courseHours }}</div>
</div>
<div v-if="form.goodsRes.typeName" class="field">
<img src="@/assets/img/product/5.png" alt=""> 产品类型
<div class="text">{{ form.goodsRes.typeName }}</div>
</div>
</div>
<button class="btn" @click="toRenew">续期</button>
</div>
</div>
<div class="course" id="part0">
<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">
<div class="des" v-html="form.mall.detailedIntroduction"></div>
<div v-if="isCourse" class="chapter" id="part1" 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">
<div class="val">
<img v-if="section.fileType === 'pptx'" src="@/assets/img/exts/ppt.png" alt="">
<img v-else-if="section.fileType === 'mp4'" src="@/assets/img/exts/video.png" alt="">
<img v-else-if="section.fileType === 'doc' || section.fileType === 'docx'"
src="@/assets/img/exts/word.png" alt="">
<img v-else-if="section.fileType === 'txt'" src="@/assets/img/exts/txt.png" alt="">
<img v-else-if="section.fileType === 'pdf'" src="@/assets/img/exts/pdf.png" alt="">
<img v-else src="@/assets/img/exts/pic.png" alt="">
{{ section.name }}
</div>
<i v-if="!form.goodsRes.logoOfOurSchool && j" class="icon el-icon-lock"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="products">
<h6>热门产品推荐</h6>
<ul class="product">
<li v-for="(item, i) in hots" :key="i">
<img :src="item.coverDrawing" alt="" />
<img v-if="item.logoOfOurSchool" class="my-school" src="@/assets/img/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 breadcrumb from '@/components/breadcrumb'
export default {
components: { breadcrumb },
data () {
return {
id: this.$route.query.id,
routes: [],
curTab: 0,
tabs: [
{
id: 0,
name: '详情介绍'
},
],
form: {
pics: [],
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
},
},
beforeRouteLeave (to, from, next) {
to.path !== '/product/list' && this.$store.commit('product/setParams', null)
next()
},
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 = ''
this.routes = [
{
name: '实验台',
path: `list?keyword=${this.$route.query.keyword || ''}&active=${this.$route.query.active || ''}`
},
{
name: e.mall.productName
}
]
this.$nextTick(() => {
this.getChapter()
this.height = document.querySelector('#fields').clientHeight + 'px'
})
}).catch(err => { })
},
//
async getChapter () {
if (this.isCourse) {
let res = await this.$get(this.api.curriculumChapter + '/' + this.form.mall.associatedProduct)
this.chapterList = res.chapterList
}
},
// tab
tabChange ({ id }) {
this.curTab = id
document.querySelector(`#part${id}`).scrollIntoView({
behavior: 'smooth'
})
},
//
toPreview (i, j) {
const bought = this.form.goodsRes.logoOfOurSchool
// ||
bought || (!bought && !j) ?
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://www.wjx.top/vm/wFCPCFp.aspx')
},
//
async toStation () {
if (this.isCourse) { //
await this.$post(`${this.api.addLearningRecord}?mallId=${this.id}`)
this.$router.push('/station/preview?courseId=' + this.form.mall.associatedProduct + '&curriculumName=' + this.form.mall.productName + '&mallId=' + this.id + '&admin=1')
} else if (this.withLink) { //
const links = this.form.mallNonAssociatedLinks
//
if (links.length === 1) {
window.open(links[0].url)
} else {
this.linkVisible = true
}
}
},
//
async toRenew () {
try {
await this.$confirm(`<p style="margin-bottom: 10px;">确认提交续期申请吗?</p><p style="font-size: 12px;">提交后,我们将立即处理您的续期请求。请耐心等待,我们会尽快完成续期流程。</p>`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'success',
closeOnClickModal: false,
dangerouslyUseHTMLString: true,
})
await this.$post(`${this.api.requestRenewalNotice}?mallId=${this.id}`)
} catch (e) { }
},
//
getHot () {
this.$post(this.api.websiteProductList, {
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 {
background: url(../../../assets/img/product/bg1.png) 0 373px no-repeat, url(../../../assets/img/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 {
width: 484px;
margin-right: 20px;
.pic {
width: 100%;
height: 100%;
border-radius: 8px;
}
}
.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 {
z-index: 100;
position: sticky;
top: 0;
display: flex;
padding: 10px 10px 20px;
background-color: #fff;
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: calc(100% - 294px);
padding: 20px 24px;
background-color: #fff;
border-radius: 10px;
}
}
.courses {
padding-top: 10px;
overflow: hidden;
/deep/.des {
div,
p,
span {
font-family: PingFang SC !important;
}
}
.chapters {
margin-top: 16px;
max-height: calc(100% - 53px);
overflow: auto;
}
.chapter {
padding-top: 60px;
margin-bottom: 20px;
}
.chapterName {
padding: 0 12px;
margin-bottom: 15px;
color: #333;
font-size: 14px;
font-weight: 600;
}
.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: 270px;
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: 155px;
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>

@ -5,25 +5,31 @@ const meta = {};
const pre = "station-";
export default {
path: "/station",
name: "station",
redirect: {
name: `${pre}list`
path: "/station",
name: "station",
redirect: {
name: `${pre}list`
},
meta,
component: BasicLayout,
children: [
{
name: `${pre}list`,
path: `list`,
component: () => import("@/pages/station/list"),
meta: { title: "实验台" }
},
meta,
component: BasicLayout,
children: [
{
name: `${pre}list`,
path: `list`,
component: () => import("@/pages/station/list"),
meta: { title: "实验台" }
},
{
name: `${pre}preview`,
path: `preview`,
component: () => import("@/pages/station/preview"),
meta: { title: "课程预览" }
}
]
{
name: `${pre}preview`,
path: `preview`,
component: () => import("@/pages/station/preview"),
meta: { title: "课程预览" }
},
{
name: `${pre}product`,
path: `product`,
component: () => import("@/pages/station/product"),
meta: { title: "产品详情" }
}
]
};
Loading…
Cancel
Save