準備活動
由于不想,花很多精力在字元串的拼湊上,心裡還是更傾心vue的條件渲染,整體看起來思路更清晰,代碼更加整齊一緻,于是就開幹了。沒有用到npm,或者其他子產品化生産工具,就從最簡單的html頁面開始撸起。
首先頁面是這樣布局的:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL1kFVPJTQU5UeRpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZwpmLxQjN4ITN0UTMxIjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
經實際操作,發現熬點雲的播放器會跟vue起沖突。是以采用 iframe 方法,将其分離開,這就是我最開始的想法。
途中遇到的一些問題
1.vue跟奧點雲點播播放器不相容,播放器顯示不出來的問題
解決方法:用iframe架構引入,這樣兩者就不沖突了。
上代碼:
html部分:
<div id="app" v-cloak>
<div class="img-box" v-show="showPop" @click="closePop">
<img v-if="showType===1" :src="showImgSrc">
<audio v-else-if="showType===2" :src="showAudioUrl" controls="controls" controlsList="nodownload"
autoplay="autoplay">
您的浏覽器不支援 audio 标簽。
</audio>
<video v-else="showType===3" :src="showVideoUrl" controls="controls" controlsList="nodownload"
autoplay="autoplay">
您的浏覽器不支援 video 标簽。
</video>
</div>
<ul class="tabs">
<li class="li-tab" v-for="(item,index) in tab.tabsParam" @click="toggleTabs(index)"
:class="{active:index!=tab.nowIndex}">{{item}}</li>
</ul>
<div class="divTab scroll-first" v-show="tab.nowIndex===0">
<div class="line-box" v-if="lineShow">
<iframe frame style="width: 100%;height: 200px;"
:src="isAdvancedProducts?'pages/drawChart.html?sss=1':'pages/drawChart.html?sss=0'">
</iframe>
<img src="./images/up.png" @click="lineShow=false" style="width: 18px;" />
</div>
<div class="line-box" v-else="!lineShow">
<iframe frame style="width: 100%;height: 60px;" src="pages/drawChartMini.html">
</iframe>
<img src="./images/down.png" @click="lineShow=true" style="width: 18px;" />
</div>
<div id="middle">
<div class="reflash" @click="getLiveData()">點選重新整理</div>
<ul class="timeLine">
<li v-for="(item,keys) in livePart.livePartList">
<p>{{item.formatTime}} </p>
<div class="card" v-if="item.type===1">
<div class="text">{{item.text}}</div>
</div>
<div class="card" v-if="item.type===2">
<div v-if="item.text">
<div class="text">{{item.text}}</div>
</div>
<img class="img-btn" :key="keys" :src="item.imgUrl" @click="enlargeImg(keys)">
</div>
<div class="card" v-if="item.type===3">
<div class="text">
<div class="text-div"> {{item.text}} </div>
<a :href="item.articleUrl" target="_blank" rel="external nofollow" >
<div class="link-div">
<image :src="item.imgUrl" style="width: 40px;height:30px;float: left;">
</image>
<span style="line-height: 30px;font-weight: bolder">{{item.articleTitle}}</span>
</div>
</a>
</div>
</div>
<div class="card" v-if="item.type===4">
<div class="width-whole cardd">
<div class="text-center head1" :class="{'pink':item.benchMark=='否'}">買入政策</div>
<div class="body2">
<div class="td1 text-center">
<h3>{{item.strockName}}</h3>
<h5>{{item.strockCode}}</h5>
</div>
<div class="td2">
<div>成交價格:{{item.operationPrice}}</div>
<div>占用資金:{{item.occupancyPrice}}</div>
<div v-if="item.isPlus" class="red">加倉</div>
</div>
</div>
<div :key="keys" class="buttom text-center" @click="showConst(keys)"
v-if="!item.display">
<img class="open-img" src="./images/open.png">
</div>
<div class="buttom" v-if="item.display">
<div class="buttom-item">
<div class="width-middle">止盈價:{{item.fullPrice}}</div>
<div class="width-middle">止損價:{{item.lossPrice}}</div>
<div>操作理由:{{item.operationReason}}</div>
<div>投顧編号:{{item.adviserNumber}}</div>
</div>
</div>
</div>
</div>
<div class="card" v-if="item.type===5">
<div class="width-whole cardd">
<div class="text-center head2" :class="{'blue':item.benchMark=='否'}">賣出政策</div>
<div class="body2">
<div class="td1 text-center">
<h3>{{item.strockName}}</h3>
<h5>{{item.strockCode}}</h5>
</div>
<div class="td2">
<div>成交價格:{{item.operationPrice}}</div>
<div>賣出比例:{{item.sellingRatio}}</div>
<div v-if="item.isReduce" class="red">減倉</div>
<div v-else class="red" :class="{'green':item.profitRatio.charAt(0)==='-'}">
收益率:{{item.profitRatio}}</div>
</div>
</div>
<div :key="keys" class="buttom text-center" @click="showConst(keys)"
v-if="!item.display">
<img class="open-img" src="./images/open.png">
</div>
<div class="buttom" v-if="item.display">
<div class="buttom-item">
<div>剩餘持倉:{{item.remainingPositions}}</div>
<div>操作理由:{{item.operationReason}}</div>
<div>投顧編号:{{item.adviserNumber}}</div>
</div>
</div>
</div>
</div>
<div class="card" v-if="item.type===6">
<img class="img-btn" style="border: none;" :key="keys" src="./images/audio.png"
@click="showAudio(keys)">
</div>
<div class="card" v-if="item.type===7">
<img class="img-btn" :key="keys" :src="item.imgUrl" @click="showVideo(keys)">
</div>
</li>
<li>
<p>start</p>
</li>
<li class="bottom-tip" id="buttom1">
<a class="history-btn" href="./pages/history.html" target="_blank" rel="external nofollow" > 檢視曆史資訊</a>
</li>
</ul>
<div class="span-buttom">
服務到期時間:{{deadline}}
</div>
</div>
</div>
<div class="divTab scroll-second" v-show="tab.nowIndex===1">
<iframe frame class="chart-player" style="width: 100%;" :style="{'height':playerHeight}"
:src="isLiveTime?'pages/liveVideo.html':'pages/recordVideo.html'">
</iframe>
<ul class="chart-div">
<li v-for="(item,key) in interactionPart.chatList">
<!-- 使用者留言 -->
<div v-if="item.openid!=1" class="chat-card">
<img src="./images/user.jpg" class="user-img">
<div class="chat-content">
<span class="chart-nickname">{{item.nickname}}</span>
<span class="chart-time">{{item.formatTime}}</span>
<div class="chart-sentence"> {{item.chatContent}} </div>
<div v-if="item.replyStatus==1" style="position: relative;left: -10px;">
<img class="user-img" src="./images/shilei1.png">
<div class="chat-content">
<span class="chart-nickname">特訓營</span>
<span class="chart-time">{{item.replyFormatTime}}</span>
<div class="chart-sentence">{{item.replyContent}}</div>
</div>
</div>
</div>
</div>
<!-- S軍留言 -->
<div v-else class="chat-card">
<img :src="item.imgUrl" class="user-img">
<div class="chat-content">
<span class="chart-nickname">{{item.nickname}}</span>
<span class="chart-time">{{item.formatTime}}</span>
<div class="chart-sentence"> {{item.chatContent}} </div>
</div>
</div>
</li>
<li class="bottom-tip" id="buttom2">已經到最低了</li>
</ul>
<div class="input-buttom">
<img src="./images/send.png" class="send-img" />
<input v-model="interactionPart.submitSentence" @blur="viewDefault" placeholder="我也來說兩句。。。">
<span @click="submitBtn" :class="{'butt-active':interactionPart.active}">發送</span>
</div>
</div>
</div>
js部分:
let app = new Vue({
el: '#app',
data: {
openId: "",
userId: "",
userGoodsId: "",
userNickName: "",
nowTime: "", //目前時間
playerHeight: '', //視訊播放器高度
showPop: false, //彈窗是否顯示
showType: "1", //1為圖檔,2為音頻,3為小視訊
showImgSrc: '', //圖檔放大位址
showAudioUrl: '', //點選語音位址
showVideoUrl: '', //視訊播放器位址
lineShow: false, //折線圖是否顯示
isLiveTime: false, //是否直播時間
deadline: "----年--月--日", //服務到期時間
isAdvancedProducts: true, //是否為進階産品
tab: { //切換
tabsParam: ['盤中直播', '課堂互動'], //tab清單
nowIndex: 0, //預設第一個tab為激活狀态
},
livePart: { //盤中直播闆塊
stTime: "", //預存上一次的時間
livePartList: [],//資料格式很重要
},
interactionPart: { //聊天子產品
stTime: "", //預存上一次的時間
chatList: [],//資料格式很重要
submitSentence: '', //送出聊天内容
active: false,
},
timer: '', //定時器,做用于聊天室定時發送請求
},
created() { //尚未挂載,這裡初始化一些資料
var This = this;
window.addEventListener('resize', This.setPlayerSize);
This.getVideoType(); //判斷視訊類型
This.setPlayerSize(); //根據螢幕寬高度,設定布局
This.getAllData(); //擷取所有頁面資料
},
mounted() { //挂載成功,開始第一個業務邏輯
var This = this;
This.timer = setInterval(this.getChatData, 300000); //設定定時器
},
methods: {
goChatBottom: function () { //聊天跳轉到最底部
let anchor = this.$el.querySelector('#buttom2');
this.$nextTick(() => {
document.querySelector(".scroll-second").scrollTop = anchor.offsetTop;
});
},
getVideoType: function () { //判斷視訊類型
var This = this;
axios.post(`http://api..`)
.then(res => {
// console.log("類型:" + res.data.status);
var s = res.data.status;
if (s == 1) {
This.isLiveTime = true;
console.log("直播時間");
} else if (s == 0) {
This.isLiveTime = false;
console.log("錄播時間");
}
})
.catch(function (error) {
console.log("擷取類型失敗");
});
},
getAllData: function () { //頁面初始化,擷取所有資料
var This = this;
This.getLiveData(); //擷取直播室資料
This.getChatData(); //擷取聊天室資料
},
getLiveData: function () { //擷取新增的直播間内容,每次擷取,隻擷取新增的
var This = this;
This.getNowTime(); //擷取目前時間
let data = new FormData();
data.append("startTime", This.livePart.stTime);
data.append("endTime", This.nowTime);
data.append("goodsId", This.userGoodsId);
axios.post(`http://api..`, data)
.then(res => {
if (res.data.length == 0) {
alert("盤中直播已加載至最新");
} else {
This.livePart.livePartList = res.data.concat(This.livePart.livePartList);
// alert("加載成功");
}
This.livePart.stTime = This.nowTime;
})
.catch(function (error) {
// alert("操作失敗");
});
},
getChatData: function () { //擷取新增的聊天内容
var This = this;
This.getNowTime(); //擷取目前時間
let data = new FormData();
data.append("openid", This.openId);
data.append("startTime", This.interactionPart.stTime);
data.append("endTime", This.nowTime);
axios.post(`http://api..`, data)
.then(res => {
if (res.data.length == 0) {
console.log("暫無更多聊天資料");
} else {
This.interactionPart.chatList.push.apply(This.interactionPart.chatList, res
.data);
console.log("定時重新整理聊天内容成功");
This.goChatBottom();
}
This.interactionPart.stTime = This.nowTime;
})
.catch(function (error) {
// alert("操作失敗");
});
},
showConst: function (index) { //卡片展開點選事件送出
var This = this;
let data = new FormData();
data.append("userid", This.userId);
data.append("informationId", This.livePart.livePartList[index].id);
axios.post(`http://api..`, data)
.then(res => {
console.log("送出點選事件成功")
})
.catch(function (error) {
// alert("操作失敗");
});
this.livePart.livePartList[index].display = true;
},
toggleTabs: function (index) { //tab切換
this.tab.nowIndex = index;
},
setPlayerSize: function () { // 擷取浏覽器高度,按照視訊比例作計算
this.playerHeight = 183 / 320 * window.innerWidth + 'px';
},
submitBtn: function () { //送出聊天内容
var This = this;
if (This.interactionPart.submitSentence.trim()) {
axios.post(`http://api..`, {
"openid": This.openId,
"nickname": This.userNickName,
"chatContent": This.interactionPart.submitSentence,
})
.then(res => {
alert("送出成功");
This.getChatData();
})
.catch(function (error) {
alert("送出失敗");
});
console.log("重新整理成功");
This.interactionPart.submitSentence = "";
} else {
alert("不能送出空資料")
}
},
enlargeImg: function (index) { // 放大圖檔
var This = this;
this.showPop = true;
this.showType = 1;
this.showImgSrc = this.livePart.livePartList[index].imgUrl;
},
showAudio: function (index) { //展示音頻
var This = this;
This.showPop = true;
this.showType = 2;
this.showAudioUrl = This.livePart.livePartList[index].audioUrl
},
showVideo: function (index) { //展示視訊
var This = this;
This.showPop = true;
this.showType = 3;
this.showVideoUrl = This.livePart.livePartList[index].videoUrl
},
closePop: function () { //清空展示位址
var This = this;
this.showPop = false;
this.showImgSrc = '';
this.showAudioUrl = '';
this.showVideoUrl = '';
},
viewDefault: function () { //防止input框輸入完成後頁面上移
var This = this;
if (This.interactionPart.active)
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
},
getNowTime: function () { //擷取最新時間
var _this = this;
let yy = new Date().getFullYear();
let mm = new Date().getMonth() + 1;
let dd = new Date().getDate();
let hh = new Date().getHours();
let mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date()
.getMinutes();
let ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date()
.getSeconds();
_this.nowTime = yy + '-' + mm + '-' + dd + ' ' + hh + ':' + mf + ':' + ss;
},
},
watch: {
"interactionPart.submitSentence"(newVal, oldVal) { //監聽輸入框是否有内容
if (newVal) {
this.interactionPart.active = true
} else {
this.interactionPart.active = false
}
},
},
beforeDestroy() { //銷毀前,移除定時器
if (this.timer) {
clearInterval(this.timer); //消除定時器
}
},
destroyed() { //執行個體銷毀的同時,移除resize監聽器
window.removeEventListener('resize', this.setPlayerSize)
}
});
css樣式:
/* content */
/* 所有的樣式,都要考慮到響應式的問題,盡量适配多種終端。主要是手機 */
/* end content */
/* 公共部分 */
[v-cloak] {
display: none;
}
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: #000000;
}
#app {
width: 100%;
height: 100%;
}
.text-center {
text-align: center;
}
.width-whole {
width: 100%;
}
.bottom-tip {
text-align: center;
font-size: small;
color: gray;
display: block;
}
/* pops'style */
.img-box {
z-index: 999;
background-color: rgba(16, 16, 16, 0.71);
cursor: not-allowed;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: auto;
}
.img-box img {
width: 100%;
margin-top: 50%;
}
.img-box video {
position: absolute;
top: 50%;
margin-top: -50%;
width: 100%;
height: auto;
}
.img-box audio {
position: absolute;
top: 50%;
margin-top: -50%;
width: 100%;
}
/* tab切換 */
.active {
border-bottom: 2px solid rgb(255, 221, 221);
color: black !important;
}
.tabs {
width: 100%;
height: 40px;
line-height: 40px;
border-bottom: 2px solid red;
color: red;
position: sticky;
top: 0px;
background-color: #fff;
z-index: 2;
}
.li-tab {
width: 50%;
height: 100%;
display: inline-block;
text-align: center;
}
.divTab {
width: 100%;
height: 100%;
}
/* 盤中直播 */
.reflash {
position: fixed;
margin-top: 10px;
right: 40px;
color: white;
background-color: rgba(255, 0, 0, 0.68);
padding: 3px 10px;
border-radius: 20px;
transition: unset;
z-index: 5;
}
#middle {
width: 100%;
height: 100%;
overflow: auto;
z-index: 1;
background-color: #fff;
}
/* 上證指數 */
.line-box {
text-align: center;
position: sticky;
top: 42px;
background-color: #fff;
border-bottom: 8px solid #f4f4f4;
}
/* 直播時間軸 */
.timeLine {
margin-bottom: 15px;
margin-top: 15px;
}
.timeLine li {
background: url(../images/back1.png) repeat-y 20px 0;
padding-bottom: 10px;
width: 100%;
}
.timeLine li:after {
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.timeLine li:last-child {
background: none !important;
}
.timeLine li:nth-last-child(2) {
background: none !important;
}
.timeLine li p {
background: url(../images/time.png) no-repeat 10px 0;
font-size: 14px;
text-align: left;
padding-left: 34px;
padding-right: 10px;
color: #909090;
line-height: 20px;
}
.timeLine li .card {
padding: 10px 10px 10px 34px;
}
.timeLine li .text {
padding: 10px 15px;
border: 1px solid #c9c9c9;
border-radius: 8px;
line-height: 1.5rem;
color: #202020;
margin-bottom: 5px;
}
/* .timeLine li:nth-last-child(2) p {
background: url(../images/now.png) no-repeat 10px 0 !important;
font-size: 14px;
text-align: left;
padding-left: 34px;
padding-right: 10px;
color: #f73e3e;
line-height: 20px;
} */
.timeLine li:first-child p {
background: url(../images/now.png) no-repeat 10px 0 !important;
font-size: 14px;
text-align: left;
padding-left: 34px;
padding-right: 10px;
color: #f73e3e;
line-height: 20px;
}
/* 圖檔 */
.timeLine .card .img-btn {
max-width: 90%;
max-height: 200px;
border-radius: 8px;
border: 1px solid #c9c9c9;
}
.open-img {
width: 20px;
border: none !important;
padding-top: 5px;
}
.link-div {
background-color: rgb(255, 255, 255);
padding: 7px 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
background-image: url('../images/link2.png');
background-repeat: no-repeat;
background-position: right top;
background-color: #f0f0f0;
}
.text-div {
width: 100%;
height: 100%;
overflow-wrap: break-word;
}
/* 買入指令 */
.timeLine .cardd {
background-color: #faf8f8;
border-radius: 10px;
}
.pink {
background-color: rgb(255, 102, 102) !important;
}
.blue {
background-color: rgb(70, 151, 255) !important;
}
.timeLine .head1 {
background-color: rgb(255, 0, 64);
line-height: 30px;
color: #fff;
border-radius: 10px 10px 0 0;
}
.timeLine .buttom {
border-radius: 0 0 10px 10px;
display: block;
border-top: 1px solid #d2d2d2;
margin: 0 10px;
padding-bottom: 5px;
}
.timeLine .red {
color: red;
}
.timeLine .green {
color: green !important;
}
.timeLine .buttom-item {
background-color: #fff;
padding: 4px 18px;
}
.timeLine .body1 {
height: 70px;
padding-top: 5px;
}
.timeLine .td1 {
line-height: 30px;
width: 40%;
float: left;
}
.timeLine .td2 {
line-height: 35px;
float: left;
padding-left: 10px;
}
.width-middle {
width: 50%;
float: left;
}
.timeLine .head2 {
background-color: rgb(0, 112, 255);
;
line-height: 30px;
color: #fff;
border-radius: 10px 10px 0 0;
}
.timeLine .body2 {
height: 90px;
padding-top: 5px;
}
.timeLine .body2 .td2 {
line-height: 30px;
float: left;
padding-left: 10px;
}
.timeLine .body2 .td1 {
line-height: 40px;
width: 40%;
float: left;
border-right: 1px solid #e4e3e3;
}
.history-btn {
border: 1px solid #aaa;
border-radius: 20px;
padding: 5px 15px;
line-height: 18px;
}
/* 聊天 */
.chart-player {
position: sticky;
top: 42px;
height: 191.179px;
z-index: 2;
}
.chart-div {
margin-bottom: 53px;
}
.chart-div li {
list-style-type: none;
}
.chat-card {
padding: 0px 10px 20px 10px;
position: relative;
border-bottom: 1px solid #ededed;
}
.user-img {
float: left;
border-radius: 50%;
width: 45px;
padding: 5px;
}
.chat-content {
display: inline-block;
width: 75%;
}
.chart-time {
color: #aaa;
}
.chart-nickname {
font-size: 16px;
font-style: italic;
color: rgb(135, 135, 135);
line-height: 44px;
font-weight: bold;
}
.chart-sentence {
width: 90%;
height: 100%;
overflow-wrap: break-word;
padding: 5px 10px;
background-color: #f0f0f0;
color: #9e9e9e;
font-size: 18px;
text-align: justify;
min-height: 30px;
}
.send-img {
padding-left: 6px;
height: 18px;
padding-top: 8px;
}
.input-buttom {
position: fixed;
bottom: 0px;
height: 40px;
background-color: #fff;
width: 95%;
border: 1px solid #aaa;
margin: 5px;
border-radius: 8px;
}
.span-buttom {
position: fixed;
bottom: 0px;
height: 15px;
background-color: #eee;
width: 100%;
line-height: 15px;
font-size: small;
text-align: center;
color: red;
}
.input-buttom input {
width: 70%;
border: none;
height: 35px;
font-size: 16px;
background-color: transparent;
}
.input-buttom span {
color: #747474;
position: relative;
float: right;
padding-right: 12px;
line-height: 40px;
}
.butt-active {
color: rgb(255, 0, 0) !important;
font-weight: 300;
}
要注意的一點就是,異步請求是需要時間的,傳回成功後的邏輯務必寫在success的回調函數裡面,否則,會出現還沒有擷取到資料,就開始渲染,導緻渲染出錯。