| @@ -0,0 +1,70 @@ | |||||
| * { | |||||
| margin: 0; | |||||
| padding: 0; | |||||
| } | |||||
| html, body { | |||||
| height: 100%; | |||||
| } | |||||
| button { | |||||
| padding: 1px 6px; | |||||
| } | |||||
| video { | |||||
| background: #222; | |||||
| margin: 0 0 0 0; | |||||
| width: 320px; | |||||
| height: 240px; | |||||
| } | |||||
| .container { | |||||
| display: flex; | |||||
| height: 100%; | |||||
| flex-direction: column; | |||||
| } | |||||
| .top { | |||||
| margin: 0.5em; | |||||
| } | |||||
| .middle { | |||||
| flex: 1; | |||||
| overflow: auto; | |||||
| margin: 0 0.5em; | |||||
| } | |||||
| .log { | |||||
| overflow: scroll; | |||||
| width: 30em; | |||||
| height: 100%; | |||||
| background-color: #eeeeee; | |||||
| float: left; | |||||
| } | |||||
| .preview { | |||||
| width: auto; | |||||
| height: 100%; | |||||
| float: left; | |||||
| } | |||||
| .local { | |||||
| margin: 0.5em 0.5em; | |||||
| float: left; | |||||
| } | |||||
| .remote { | |||||
| margin: 0.5em 0.5em 0.5em 0; | |||||
| float: left; | |||||
| } | |||||
| .sdp { | |||||
| width: 320px; | |||||
| height: 200px; | |||||
| margin: 0; | |||||
| resize: none; | |||||
| } | |||||
| .bottom { | |||||
| margin: 0.5em; | |||||
| } | |||||
| @@ -0,0 +1,503 @@ | |||||
| 'use strict' | |||||
| //本地视频预览窗口 | |||||
| var localVideo = document.querySelector('video#localvideo'); | |||||
| //远端视频预览窗口 | |||||
| var remoteVideo = document.querySelector('video#remotevideo'); | |||||
| //查看Offer文本窗口 | |||||
| var offer = document.querySelector('textarea#offer'); | |||||
| //查看Answer文本窗口 | |||||
| var answer = document.querySelector('textarea#answer'); | |||||
| var pcConfig = { | |||||
| 'iceServers': [{ | |||||
| //TURN服务器地址 | |||||
| 'urls': 'turn:43.143.227.135:3478', | |||||
| //TURN服务器用户名 | |||||
| 'username': "ghb", | |||||
| //TURN服务器密码 | |||||
| 'credential': "moonshine" | |||||
| }], | |||||
| //默认使用relay方式传输数据 | |||||
| "iceTransportPolicy": "relay", | |||||
| "iceCandidatePoolSize": "0" | |||||
| }; | |||||
| //websocket连接 | |||||
| var conn; | |||||
| //会话描述 | |||||
| var session; | |||||
| //本地视频流 | |||||
| var localStream = null; | |||||
| //远端视频流 | |||||
| var remoteStream = null; | |||||
| //PeerConnection | |||||
| var pc = null; | |||||
| //offer描述 | |||||
| var offerdesc = null; | |||||
| /** | |||||
| * 功能: 判断此浏览器是在PC端,还是移动端。 | |||||
| * 返回值: false, 说明当前操作系统是移动端 | |||||
| * true, 说明当前的操作系统是PC端。 | |||||
| */ | |||||
| function IsPC() { | |||||
| var userAgentInfo = navigator.userAgent; | |||||
| var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; | |||||
| var flag = true; | |||||
| for (var v = 0; v < Agents.length; v++) { | |||||
| if (userAgentInfo.indexOf(Agents[v]) > 0) { | |||||
| flag = false; | |||||
| break; | |||||
| } | |||||
| } | |||||
| return flag; | |||||
| } | |||||
| /** | |||||
| * 功能: 判断是Android端还是iOS端。 | |||||
| * 返回值: true,说明是Android端 | |||||
| * false,说明是iOS端。 | |||||
| */ | |||||
| function IsAndroid() { | |||||
| var u = navigator.userAgent, app = navigator, appVersion; | |||||
| var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; | |||||
| var isIOS = !!u.match(/\(i[^;]+;( U;) ? CPU.+Mac OS X/); | |||||
| if (isAndroid) { | |||||
| //这个是Android系统 | |||||
| return true; | |||||
| } | |||||
| if (isIOS) { | |||||
| //这个是iOS系统 | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 功能: 向对端发消息 | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function sendMessage(roomid, data) { | |||||
| console.log('send message to other end', roomid, data); | |||||
| if (!socket) { | |||||
| console.log('socket is null'); | |||||
| } | |||||
| socket.emit('message', roomid, data); | |||||
| } | |||||
| /** | |||||
| *功能: 与信令服务器建立socket.io连接;并根据信令更新状态机。 | |||||
| *返回值: 无 | |||||
| */ | |||||
| function conn() { | |||||
| //连接信令服务器 | |||||
| socket = io.connect(); | |||||
| //joined'消息处理函数 | |||||
| socket.on('joined', (roomid, id) => { | |||||
| console.log('receive joined message!', roomid, id); | |||||
| //状态机变更为joined' | |||||
| state = 'joined'; | |||||
| /** | |||||
| * 如果是Mesh方案,第一个人不该在这里创建 | |||||
| * peerConnection,而是要等到所有端都收到一个'otherjoin'消息时再创建 | |||||
| */ | |||||
| //创建PeerConnection 并绑定音视频轨 | |||||
| createPeerConnection(); | |||||
| bindTracks(); | |||||
| //设置button状态 | |||||
| btnConn.disabled = true; | |||||
| btnLeave.disabled = false; | |||||
| console.log('receive joined message, state = ', state); | |||||
| }); | |||||
| //otherjoin消息处理函数 | |||||
| socket.on('other_join', (roomid) => { | |||||
| console.log('receive joined message:', roomid, state); | |||||
| //如果是多人,每加入一个人都要创建一个新的 PeerConnection | |||||
| if (state === 'joined_unbind') { | |||||
| createPeerConnection(); | |||||
| bindTracks(); | |||||
| } | |||||
| //状态机变更为 joined_conn | |||||
| state = 'joined_conn'; | |||||
| //开始“呼叫”对方 | |||||
| call1(); | |||||
| console.log('receive other_join message, state = ', state); | |||||
| }); | |||||
| //full消息处理函数 | |||||
| socket.on('full', (roomid, id) => { | |||||
| console.log('receive full message', roomid, id); | |||||
| //关闭gocket.io连接 | |||||
| socket.disconnect(); | |||||
| //挂断“呼叫” | |||||
| hangup(); | |||||
| //关闭本地媒体 | |||||
| closeLocalMedia(); | |||||
| //状态机变更为 leaved | |||||
| state = 'leaved'; | |||||
| console.log('receive full message, state = ', state); | |||||
| alert('the room is full!'); | |||||
| }); | |||||
| //leaved消息处理函数 | |||||
| socket.on('left', (roomid, id) => { | |||||
| console.log('receive leaved message', roomid, id); | |||||
| //状态机变更为leaved | |||||
| state = 'leaved' | |||||
| //关闭socket.io连接 | |||||
| socket.disconnect(); | |||||
| console.log('receive leaved message, state = ', state); | |||||
| //改变button状态 | |||||
| btnConn.disabled = false; | |||||
| btnLeave.disabled = true; | |||||
| }); | |||||
| //bye消息处理函数 | |||||
| socket.on('bye', (room, id) => { | |||||
| console.log('receive bye message', roomid, id); | |||||
| /** | |||||
| * 当是Mesh方案时,应该带上当前房间的用户数, | |||||
| * 如果当前房间用户数不小于 2,则不用修改状态, | |||||
| * 并且关闭的应该是对应用户的PeerConnection。 | |||||
| * 在客户端应该维护一张PeerConnection表,它是 | |||||
| * key:value的格式,key=userid,value=peerconnection | |||||
| */ | |||||
| //状态机变更为 joined_unbind | |||||
| state = 'joined_unbind'; | |||||
| //挂断“呼叫” | |||||
| hangup(); | |||||
| offer.value = ''; | |||||
| answer.value = ''; | |||||
| console.log('receive bye message, state=', state); | |||||
| }); | |||||
| //socket.io连接断开处理函数 | |||||
| socket.on('disconnect', (socket) => { | |||||
| console.log('receive disconnect message!', roomid); | |||||
| if (!(state === 'leaved')) { | |||||
| //挂断“呼叫” | |||||
| hangup(); | |||||
| //关闭本地媒体 | |||||
| closeLocalMedia(); | |||||
| } | |||||
| //状态机变更为 leaved | |||||
| state = 'leaved'; | |||||
| }); | |||||
| //收到对端消息处理函数 | |||||
| socket.on('message', (roomid, data) => { | |||||
| console.log('receive message!', roomid, data); | |||||
| if( data === null || data === undefined) { | |||||
| console.error('the message is invalid!'); | |||||
| return; | |||||
| } | |||||
| //如果收到的SDP是offer | |||||
| if (data.hasOwnProperty('type') && data.type === 'offer') { | |||||
| offer.value = data.sdp; | |||||
| //进行媒体协商 | |||||
| pc.setRemoteDescription(new RTCSessionDescription(data)); | |||||
| //创建answer | |||||
| pc.createAnswer() | |||||
| .then(getAnswer) | |||||
| .catch(handleAnswerError); | |||||
| //如果收到的SDP是answer | |||||
| } else if (data.hasOwnProperty('type') && data.type == 'answer') { | |||||
| answer.value = data.sdp; | |||||
| //进行媒体协商 | |||||
| pc.setRemoteDescription(new RTCSessionDescription(data)); | |||||
| //如果收到的是Candidate消息 | |||||
| } else if (data.hasOwnProperty('type') && data.type === 'candidate') { | |||||
| var candidate = new RTCIceCandidate({ | |||||
| sdpMLineIndex: data.label, | |||||
| candidate: data.candidate | |||||
| }); | |||||
| //将远端Candidate消息添加到PeerConnection中 | |||||
| pc.addIceCandidate(candidate); | |||||
| } else { | |||||
| console.log('the message is invalid!', data); | |||||
| } | |||||
| }); | |||||
| //从url中获取roomid | |||||
| roomid = getQueryVariable('room'); | |||||
| //发送'join'消息 | |||||
| socket.emit('join', roomid); | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * 功能: 打开音视频设备成功时的回调函数 | |||||
| * 返回值: 永远为true | |||||
| */ | |||||
| function getMediaStream(stream) { | |||||
| //将从设备上获取到的音视频track添加到localStream中 | |||||
| if (localStream) { | |||||
| stream.getAudioTracks().forEach((track) => { | |||||
| localStream.addTrack(track); | |||||
| stream.removeTrack(track); | |||||
| }); | |||||
| } else { | |||||
| localStream = stream; | |||||
| } | |||||
| //本地视频标签与本地流绑定 | |||||
| localVideo.srcObject = localStream; | |||||
| //创建PeerConnection 并绑定音视频轨 | |||||
| createPeerConnection(); | |||||
| bindTracks(); | |||||
| } | |||||
| /** | |||||
| * 功能: 错误处理函数 | |||||
| * | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function handleError(err) { | |||||
| console.error('Failed to get Media Stream!', err); | |||||
| } | |||||
| /** | |||||
| *功能: 打开音视频设备 | |||||
| *返回值: 无 | |||||
| */ | |||||
| function startLocalMedia() { | |||||
| if (!navigator.mediaDevices || | |||||
| !navigator.mediaDevices.getUserMedia) { | |||||
| console.error('the getUserMedia is not supported!'); | |||||
| return; | |||||
| } else { | |||||
| var constraints; | |||||
| constraints = { | |||||
| video: true, | |||||
| audio: { | |||||
| echoCancellation: true, | |||||
| noiseSuppression: true, | |||||
| autoGainControl: true | |||||
| } | |||||
| }; | |||||
| navigator.mediaDevices.getUserMedia(constraints) | |||||
| .then(getMediaStream) | |||||
| .catch(handleError); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 功能: 获得远端媒体流 | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function getRemoteStream(e) { | |||||
| //存放远端视频流 | |||||
| remoteStream = e.streams[0]; | |||||
| //远端视频标签与远端视频流绑定 | |||||
| remoteVideo.srcObject = e.streams[0]; | |||||
| } | |||||
| /** | |||||
| * 功能: 处理Offer错误 | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function handleOfferError(err) { | |||||
| console.error('Failed to create offer:', err); | |||||
| } | |||||
| /** | |||||
| *功能:处理Answer错误 | |||||
| *返回值:无 | |||||
| */ | |||||
| function handleAnswerError(err) { | |||||
| console.error('Failed to create answer;', err); | |||||
| } | |||||
| /** | |||||
| *功能:获取Answer SDP 描述符的回调函数 | |||||
| * | |||||
| *返回值:无 | |||||
| */ | |||||
| function getAnswer(desc) { | |||||
| //设置Answer | |||||
| pc.setLocalDescription(desc); | |||||
| //将Answer显示出来 | |||||
| answer.value = desc.sdp; | |||||
| //将AnswerSDP发送给对端 | |||||
| let req = new Object; | |||||
| req.cmd = 'CMD_WEBRTC'; | |||||
| req.data = desc; | |||||
| let str = JSON.stringify(req); | |||||
| console.log(str); | |||||
| conn.send(str); | |||||
| } | |||||
| /** | |||||
| *功能:获取Offer SDP 描述符的回调函数 | |||||
| *返回值:无 | |||||
| */ | |||||
| function getOffer(desc) { | |||||
| //设置Offer | |||||
| pc.setLocalDescription(desc); | |||||
| //将Offer显示出来 | |||||
| offer.value = desc.sdp; | |||||
| offerdesc = desc; | |||||
| console.log(offerdesc) | |||||
| //将 OfferSDP发送给对端 | |||||
| let req = new Object; | |||||
| req.cmd = 'CMD_WEBRTC'; | |||||
| req.data = desc; | |||||
| let str = JSON.stringify(req); | |||||
| console.log(str); | |||||
| conn.send(str); | |||||
| } | |||||
| /** | |||||
| * 功能: 创建PeerConnection对象 | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function createPeerConnection() { | |||||
| /* | |||||
| * 如果是多人的话,在这里要创建一个新的连接 | |||||
| * 新创建好的要放到一个映射表中 | |||||
| */ | |||||
| //key=userid, value=peerconnection | |||||
| console.log('create RTCPeerConnection!'); | |||||
| if (!pc) { | |||||
| //创建PeerConnection对象 | |||||
| pc = new RTCPeerConnection(pcConfig); | |||||
| //当收集到Candidate后 | |||||
| pc.onicecandidate = (event) => { | |||||
| if (event.candidate) { | |||||
| console.log("candidate" + JSON.stringify(event.candidate.toJSON())); | |||||
| //将Candidate发送给对端 | |||||
| let req = new Object; | |||||
| req.cmd = 'CMD_WEBRTC'; | |||||
| req.data = { | |||||
| type: 'candidate', | |||||
| label: event.candidate.sdpMLineIndex, | |||||
| id: event.candidate.sdpMid, | |||||
| candidate: event.candidate.candidate | |||||
| }; | |||||
| let str = JSON.stringify(req); | |||||
| console.log(str); | |||||
| conn.send(str); | |||||
| } else { | |||||
| console.log('this is the end candidate'); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 当PeerConnection对象收到远端音视频流时 | |||||
| * 触发ontrack事件,并回调getRemoteStream函数 | |||||
| */ | |||||
| pc.ontrack = getRemoteStream; | |||||
| } else { | |||||
| console.log('the pc have be created!'); | |||||
| } | |||||
| return; | |||||
| } | |||||
| /** | |||||
| * 功能: 将音视频track绑定到PeerConnection对象中 | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function bindTracks() { | |||||
| console.log('bind tracks into RTCPeerConnection!'); | |||||
| if (pc === null && localStream === undefined) { | |||||
| console.error('pc is null or undefined!'); | |||||
| return; | |||||
| } | |||||
| if (localStream === null && localStream === undefined) { | |||||
| console.error('localstream is null or undefined!'); | |||||
| return; | |||||
| } | |||||
| //将本地音视频流中所有的track添加到PeerConnection对象中 | |||||
| localStream.getTracks().forEach((track)=>{ | |||||
| pc.addTrack(track, localStream); | |||||
| }); | |||||
| } | |||||
| /** | |||||
| * 功能: 开启“呼叫” | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function videoCall() { | |||||
| var offerOptions = { | |||||
| offerToReceiveAudio: 1, | |||||
| offerToReceiveVideo: 1 | |||||
| }; | |||||
| /** | |||||
| * 创建Offer, | |||||
| * 如果成功,则回调getoffer()方法 | |||||
| * 如果失败,则回调handleofferError()方法 | |||||
| */ | |||||
| pc.createOffer(offerOptions) | |||||
| .then(getOffer) | |||||
| .catch(handleOfferError); | |||||
| } | |||||
| /** | |||||
| * 功能: 挂断“呼叫” | |||||
| * 返回值: 无 | |||||
| */ | |||||
| function hangup() { | |||||
| if (!pc) { | |||||
| return; | |||||
| } | |||||
| //发送“挂断”消息 | |||||
| let r = new Object; | |||||
| r.cmd = "CMD_HANGUP"; | |||||
| let str = JSON.stringify(r); | |||||
| conn.send(str); | |||||
| /* | |||||
| offerdesc = null; | |||||
| //将PeerConnection连接关掉 | |||||
| pc.close(); | |||||
| pc = null; | |||||
| */ | |||||
| } | |||||
| /** | |||||
| * 功能:关闭本地媒体 | |||||
| * 返回值:无 | |||||
| */ | |||||
| function closeLocalMedia() { | |||||
| if (!(localStream === null || localStream === undefined)) { | |||||
| //遍历每个track,并将其关闭 | |||||
| localStream.getTracks().forEach((track) => { | |||||
| track.stop(); | |||||
| }); | |||||
| } | |||||
| localStream = null; | |||||
| } | |||||
| /** | |||||
| *功能:离开房间 | |||||
| *返回值:无 | |||||
| */ | |||||
| function leave() { | |||||
| //向信令服务器发送leave消息 | |||||
| socket.emit('leave', roomid); | |||||
| //挂断“呼叫” | |||||
| hangup(); | |||||
| //关闭本地媒体 | |||||
| closeLocalMedia(); | |||||
| offer.value = ''; | |||||
| answer.value = ''; | |||||
| } | |||||
| @@ -0,0 +1,196 @@ | |||||
| <!DOCTYPE html> | |||||
| <html> | |||||
| <head> | |||||
| <title>用户页面</title> | |||||
| <link href="./css/client.css" rel="stylesheet"/> | |||||
| <script type="text/javascript"> | |||||
| var username; | |||||
| var msg; | |||||
| var log; | |||||
| window.onload = function () { | |||||
| username = document.getElementById("username"); | |||||
| msg = document.getElementById("msg"); | |||||
| log = document.getElementById("log"); | |||||
| if (window["WebSocket"]) { | |||||
| // 连接服务器 | |||||
| conn = new WebSocket("wss://" + document.location.host + "/webrtc"); | |||||
| conn.onclose = function (evt) { | |||||
| appendLog("连接已断开."); | |||||
| }; | |||||
| conn.onmessage = function (e) { | |||||
| res = JSON.parse(e.data) | |||||
| if (res.cmd == 'CMD_USER_LOGIN_ACK') { | |||||
| if (res.error === 0) { | |||||
| appendLog("登录成功。"); | |||||
| // 启动本地音视频 | |||||
| startLocalMedia(); | |||||
| } else { | |||||
| appendLog("登录失败,error: " + res.error); | |||||
| } | |||||
| } else if (res.cmd === 'CMD_CALL_ACK') { | |||||
| if (res.error === 0) | |||||
| appendLog("呼叫成功。"); | |||||
| else | |||||
| appendLog("呼叫失败,error: " + res.error); | |||||
| } else if (res.cmd === 'CMD_SESSION_BEGIN') { | |||||
| session = new Object; | |||||
| session.staffer = res.staffer; | |||||
| session.user = res.user; | |||||
| session.iceServers = res.iceServers | |||||
| appendLog("开始由 " + res.staffer + " 为您服务"); | |||||
| console.log(session); | |||||
| if (!pc) { | |||||
| createPeerConnection(); | |||||
| bindTracks(); | |||||
| } | |||||
| videoCall(); | |||||
| } else if (res.cmd === 'CMD_SESSION_END') { | |||||
| if (res.error === 0) | |||||
| appendLog("会话已结束。"); | |||||
| else | |||||
| appendLog("会话异常结束,error: " + res.error); | |||||
| if (pc != null) { | |||||
| pc.close(); | |||||
| pc = null; | |||||
| offerdesc = null; | |||||
| offer.value = ""; | |||||
| answer.value = ""; | |||||
| } | |||||
| } else if (res.cmd === 'CMD_CHAT') { | |||||
| appendLog(session.staffer + " 说:" + res.text); | |||||
| } else if (res.cmd === 'CMD_WEBRTC') { | |||||
| //如果收到的SDP是offer | |||||
| if (res.data.hasOwnProperty('type') && res.data.type === 'offer') { | |||||
| offer.value = res.data.sdp; | |||||
| //进行媒体协商 | |||||
| pc.setRemoteDescription(new RTCSessionDescription(res.data)); | |||||
| //创建answer | |||||
| pc.createAnswer() | |||||
| .then(getAnswer) | |||||
| .catch(handleAnswerError); | |||||
| //如果收到的SDP是answer | |||||
| } else if (res.data.hasOwnProperty('type') && res.data.type == 'answer') { | |||||
| answer.value = res.data.sdp; | |||||
| //进行媒体协商 | |||||
| pc.setRemoteDescription(new RTCSessionDescription(res.data)); | |||||
| //如果收到的是Candidate消息 | |||||
| } else if (res.data.hasOwnProperty('type') && res.data.type === 'candidate') { | |||||
| var candidate = new RTCIceCandidate({ | |||||
| sdpMLineIndex: res.data.label, | |||||
| candidate: res.data.candidate | |||||
| }); | |||||
| //将远端Candidate消息添加到PeerConnection中 | |||||
| pc.addIceCandidate(candidate); | |||||
| } | |||||
| } | |||||
| }; | |||||
| } else { | |||||
| appendLog("Your browser does not support WebSockets."); | |||||
| } | |||||
| }; | |||||
| function appendLog(text) { | |||||
| let item = document.createElement("div"); | |||||
| item.innerText = text; | |||||
| let doScroll = log.scrollTop > log.scrollHeight - log.clientHeight - 1; | |||||
| log.appendChild(item); | |||||
| if (doScroll) { | |||||
| log.scrollTop = log.scrollHeight - log.clientHeight; | |||||
| } | |||||
| } | |||||
| function login() { | |||||
| let req = new Object; | |||||
| req.cmd = "CMD_USER_LOGIN"; | |||||
| req.username = username.value; | |||||
| req.password = "123456"; | |||||
| req.agent = "browser"; | |||||
| let str = JSON.stringify(req); | |||||
| console.log(str); | |||||
| conn.send(str); | |||||
| } | |||||
| function chat() { | |||||
| if (!conn) { | |||||
| return false; | |||||
| } | |||||
| if (!msg.value) { | |||||
| return false; | |||||
| } | |||||
| appendLog("我说:" + msg.value); | |||||
| let req = new Object; | |||||
| req.cmd = "CMD_CHAT"; | |||||
| req.text = msg.value; | |||||
| let str = JSON.stringify(req); | |||||
| console.log(str); | |||||
| conn.send(str); | |||||
| msg.value = ""; | |||||
| return false; | |||||
| } | |||||
| function call() { | |||||
| let req = new Object; | |||||
| req.cmd = "CMD_CALL"; | |||||
| req.caller = username.value; | |||||
| req.callee = ""; | |||||
| let str = JSON.stringify(req); | |||||
| console.log(str); | |||||
| conn.send(str); | |||||
| } | |||||
| </script> | |||||
| </head> | |||||
| <body> | |||||
| <div class="container"> | |||||
| <div class="top"> | |||||
| <label>用户账号</label> | |||||
| <input id="username"></input> | |||||
| <button id="login" onclick="login()">登录</button> | |||||
| <button id="call" onclick="call()">呼叫</button> | |||||
| <button id="call" onclick="hangup()">挂断</button> | |||||
| </div> | |||||
| <div class="middle"> | |||||
| <div class="log" id="log"></div> | |||||
| <div class="preview"> | |||||
| <div class="local"> | |||||
| <p>Local:</p> | |||||
| <video id="localvideo" autoplay playsinline muted></video> | |||||
| <p>Offer SDP:</p> | |||||
| <textarea class="sdp" id="offer"></textarea> | |||||
| </div> | |||||
| <div class="remote"> | |||||
| <p>Remote:</p> | |||||
| <video id="remotevideo" autoplay playsinline></video> | |||||
| <p>Answer SDP:</p> | |||||
| <textarea class="sdp" id="answer"></textarea> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="bottom"> | |||||
| <textarea id="msg" cols="70" rows="6" style="overflow: visible; resize: none"></textarea> | |||||
| <button id="send" style="vertical-align: top;" onclick="chat()">发送</button> | |||||
| </div> | |||||
| </div> | |||||
| <!-- | |||||
| 引用的JavaScript脚本库: | |||||
| socket.io.js:用于连接信令服务器 | |||||
| adapter-latest.js:用于浏览器适配Chrome,Firefox... | |||||
| client.js:WebRTC客户端代码 | |||||
| --> | |||||
| <script src="js/adapter-latest.js"></script> | |||||
| <script src="js/client.js"></script> | |||||
| </body> | |||||
| </html> | |||||
| @@ -0,0 +1,91 @@ | |||||
| <template> | |||||
| <div> | |||||
| <van-swipe-cell v-for="(item, index) in videoList"> | |||||
| <div class="itemList"> | |||||
| <div | |||||
| v-if="videoList.length > 0" | |||||
| class="videoModel" | |||||
| :key="index" | |||||
| @click="$parent.goDetail(item)" | |||||
| > | |||||
| <!-- 左侧图片 --> | |||||
| <div class="videoPrv"> | |||||
| <img src="@/assets/img/BG@2x.png" /> | |||||
| <div v-if="item.videoStatus == 0" class="cover">草稿</div> | |||||
| <div v-if="item.videoStatus == 1" class="cover">视频生成中</div> | |||||
| <div v-if="item.videoStatus == 3" class="cover">视频生成失败</div> | |||||
| <!-- <div v-if="item.videoStatus == ?" class="cover">排队中</div> --> | |||||
| </div> | |||||
| <!-- 中间文字 --> | |||||
| <div class="videoInfo"> | |||||
| <div class="videoName"> | |||||
| {{ item.title || "暂无" }} | |||||
| </div> | |||||
| <div class="createTime"> | |||||
| 视频时长:{{ $parent.formatSeconds(item.videoTime) }} | |||||
| </div> | |||||
| <div class="createTime"> | |||||
| 视频容量:{{ $parent.bytesToSize(item.videoSize) }} | |||||
| </div> | |||||
| <div class="createTime"> | |||||
| 创建时间:{{ | |||||
| $parent.changeTime(item.createDate, "YYYY-MM-DD hh:mm:ss") | |||||
| }} | |||||
| </div> | |||||
| </div> | |||||
| <!-- 右侧更多 --> | |||||
| <div class="showMore" @click.stop="showDialog=!showDialog"> | |||||
| <div></div> | |||||
| <div></div> | |||||
| <div></div> | |||||
| </div> | |||||
| <van-dialog v-model="showDialog" title="标题" show-cancel-button> | |||||
| 123123123123 | |||||
| </van-dialog> | |||||
| </div> | |||||
| <div v-if="videoList.length <= 0" class="noWork"> | |||||
| 您还没有视频作品 | |||||
| </div> | |||||
| </div> | |||||
| <template #right> | |||||
| <van-button | |||||
| square | |||||
| text="删除" | |||||
| type="danger" | |||||
| class="delete-button" | |||||
| @click="$parent.goDelete(item.id)" | |||||
| /> | |||||
| </template> | |||||
| </van-swipe-cell> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| export default { | |||||
| name: 'SrcMyVideoItem', | |||||
| props: { | |||||
| videoList: { | |||||
| type: Array, | |||||
| required: true | |||||
| } | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| showDialog:false | |||||
| }; | |||||
| }, | |||||
| mounted() { | |||||
| }, | |||||
| methods: { | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| </style> | |||||
| @@ -5,15 +5,20 @@ | |||||
| <div class="userInfo"> | <div class="userInfo"> | ||||
| <div class="nickName" @click="go('/myPage')">用户名</div> | <div class="nickName" @click="go('/myPage')">用户名</div> | ||||
| <div class="userModel"> | <div class="userModel"> | ||||
| <img | |||||
| <!-- <img | |||||
| class="userImg" | class="userImg" | ||||
| src="../../assets/img/user_black.png" | src="../../assets/img/user_black.png" | ||||
| @click="go('/myPage')" | @click="go('/myPage')" | ||||
| /> | |||||
| <img | |||||
| /> --> | |||||
| <!-- <img | |||||
| class="userImg" | class="userImg" | ||||
| src="../../assets/img/loginOut.png" | src="../../assets/img/loginOut.png" | ||||
| @click="loginOut()" | @click="loginOut()" | ||||
| /> --> | |||||
| <img | |||||
| class="userImg" | |||||
| src="../../assets/icon/icon-loginout.png" | |||||
| @click="loginOut()" | |||||
| /> | /> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -100,14 +105,15 @@ export default { | |||||
| font-weight: 600; | font-weight: 600; | ||||
| font-size: 13px; | font-size: 13px; | ||||
| margin-right: 15px; | margin-right: 15px; | ||||
| margin-top: 3px; | |||||
| } | } | ||||
| .userModel { | .userModel { | ||||
| position: relative; | position: relative; | ||||
| top: 3px; | top: 3px; | ||||
| margin-right: 10px; | margin-right: 10px; | ||||
| .userImg { | .userImg { | ||||
| width: 30px; | |||||
| height: 30px; | |||||
| width: 24px; | |||||
| height: 24px; | |||||
| margin-right: 2px; | margin-right: 2px; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,5 +1,5 @@ | |||||
| <template> | <template> | ||||
| <div class="tailBox"> | |||||
| <div v-if="false" class="tailBox"> | |||||
| <!-- <div class="title">联系我们</div> --> | <!-- <div class="title">联系我们</div> --> | ||||
| <!-- <div class="taillong"> | <!-- <div class="taillong"> | ||||
| <img src="../../assets/img/LOGO1.png" alt> | <img src="../../assets/img/LOGO1.png" alt> | ||||
| @@ -10,6 +10,8 @@ import 'lib-flexible' | |||||
| import axios from "axios" | import axios from "axios" | ||||
| import ElementUI from 'element-ui'; //引入js | import ElementUI from 'element-ui'; //引入js | ||||
| import 'element-ui/lib/theme-chalk/index.css';//引入css | import 'element-ui/lib/theme-chalk/index.css';//引入css | ||||
| import "@/styles/reset.scss"; // 引入清除默认样式 | |||||
| import "@/styles/index.scss"; // 引入全局样式 | |||||
| Vue.use(ElementUI); | Vue.use(ElementUI); | ||||
| Vue.use(Vant) | Vue.use(Vant) | ||||
| @@ -44,6 +44,24 @@ const routes = [ | |||||
| name: 'chooseModel', | name: 'chooseModel', | ||||
| component: () => import("../views/model/chooseModel"), | component: () => import("../views/model/chooseModel"), | ||||
| }, | }, | ||||
| // 模板预览new | |||||
| { | |||||
| path: '/previewModel', | |||||
| name: 'previewModel', | |||||
| component: () => import("../views/model/previewModel"), | |||||
| }, | |||||
| // 编辑模板new | |||||
| { | |||||
| path: '/editModel', | |||||
| name: 'editModel', | |||||
| component: () => import("../views/model/editModel"), | |||||
| }, | |||||
| // 聊天首页new | |||||
| { | |||||
| path: '/chat', | |||||
| name: 'chat', | |||||
| component: () => import("@/views/chat/chat"), | |||||
| }, | |||||
| // 录入文案 | // 录入文案 | ||||
| { | { | ||||
| path: '/getPaper', | path: '/getPaper', | ||||
| @@ -0,0 +1,16 @@ | |||||
| .floatRight { | |||||
| float: right; | |||||
| } | |||||
| // 淡化文字 | |||||
| .desalinate { | |||||
| font-size: 12px; | |||||
| font-weight: 400; | |||||
| color: #666; | |||||
| } | |||||
| .redWord { | |||||
| font-weight: normal; | |||||
| color: red; | |||||
| } | |||||
| img { | |||||
| vertical-align: middle; | |||||
| } | |||||
| @@ -0,0 +1,284 @@ | |||||
| /*! | |||||
| * ress.css • v4.0.0 | |||||
| * MIT License | |||||
| * github.com/filipelinhares/ress | |||||
| */ | |||||
| /* # ================================================================= | |||||
| # Global selectors | |||||
| # ================================================================= */ | |||||
| html { | |||||
| box-sizing: border-box; | |||||
| -webkit-text-size-adjust: 100%; /* Prevent adjustments of font size after orientation changes in iOS */ | |||||
| word-break: normal; | |||||
| -moz-tab-size: 4; | |||||
| tab-size: 4; | |||||
| } | |||||
| *, | |||||
| ::before, | |||||
| ::after { | |||||
| box-sizing: inherit; | |||||
| background-repeat: no-repeat; /* Set `background-repeat: no-repeat` to all elements and pseudo elements */ | |||||
| } | |||||
| ::before, | |||||
| ::after { | |||||
| text-decoration: inherit; /* Inherit text-decoration and vertical align to ::before and ::after pseudo elements */ | |||||
| vertical-align: inherit; | |||||
| } | |||||
| * { | |||||
| padding: 0; /* Reset `padding` and `margin` of all elements */ | |||||
| margin: 0; | |||||
| } | |||||
| /* # ================================================================= | |||||
| # General elements | |||||
| # ================================================================= */ | |||||
| hr { | |||||
| height: 0; /* Add the correct box sizing in Firefox */ | |||||
| overflow: visible; /* Show the overflow in Edge and IE */ | |||||
| color: inherit; /* Correct border color in Firefox. */ | |||||
| } | |||||
| details, | |||||
| main { | |||||
| display: block; /* Render the `main` element consistently in IE. */ | |||||
| } | |||||
| summary { | |||||
| display: list-item; /* Add the correct display in all browsers */ | |||||
| } | |||||
| small { | |||||
| font-size: 80%; /* Set font-size to 80% in `small` elements */ | |||||
| } | |||||
| [hidden] { | |||||
| display: none; /* Add the correct display in IE */ | |||||
| } | |||||
| abbr[title] { | |||||
| /* Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari */ | |||||
| text-decoration: underline; | |||||
| text-decoration: underline dotted; | |||||
| border-bottom: none; /* Remove the bottom border in Chrome 57 */ | |||||
| } | |||||
| a { | |||||
| background-color: transparent; /* Remove the gray background on active links in IE 10 */ | |||||
| } | |||||
| a:active, | |||||
| a:hover { | |||||
| outline-width: 0; /* Remove the outline when hovering in all browsers */ | |||||
| } | |||||
| code, | |||||
| kbd, | |||||
| pre, | |||||
| samp { | |||||
| font-family: monospace, monospace; /* Specify the font family of code elements */ | |||||
| } | |||||
| pre { | |||||
| font-size: 1em; /* Correct the odd `em` font sizing in all browsers */ | |||||
| } | |||||
| b, | |||||
| strong { | |||||
| font-weight: bolder; /* Add the correct font weight in Chrome, Edge, and Safari */ | |||||
| } | |||||
| /* https://gist.github.com/unruthless/413930 */ | |||||
| sub, | |||||
| sup { | |||||
| position: relative; | |||||
| font-size: 75%; | |||||
| line-height: 0; | |||||
| vertical-align: baseline; | |||||
| } | |||||
| sub { | |||||
| bottom: -.25em; | |||||
| } | |||||
| sup { | |||||
| top: -.5em; | |||||
| } | |||||
| table { | |||||
| text-indent: 0; /* Remove text indentation in Chrome, Edge, and Safari */ | |||||
| border-color: inherit; /* Correct border color in all Chrome, Edge, and Safari. */ | |||||
| } | |||||
| /* # ================================================================= | |||||
| # Forms | |||||
| # ================================================================= */ | |||||
| input { | |||||
| border-radius: 0; | |||||
| } | |||||
| /* Replace pointer cursor in disabled elements */ | |||||
| [disabled] { | |||||
| cursor: default; | |||||
| } | |||||
| [type='number']::-webkit-inner-spin-button, | |||||
| [type='number']::-webkit-outer-spin-button { | |||||
| height: auto; /* Correct the cursor style of increment and decrement buttons in Chrome */ | |||||
| } | |||||
| [type='search'] { | |||||
| -webkit-appearance: textfield; /* Correct the odd appearance in Chrome and Safari */ | |||||
| outline-offset: -2px; /* Correct the outline style in Safari */ | |||||
| } | |||||
| [type='search']::-webkit-search-decoration { | |||||
| -webkit-appearance: none; /* Remove the inner padding in Chrome and Safari on macOS */ | |||||
| } | |||||
| textarea { | |||||
| overflow: auto; /* Internet Explorer 11+ */ | |||||
| resize: vertical; /* Specify textarea resizability */ | |||||
| } | |||||
| button, | |||||
| input, | |||||
| optgroup, | |||||
| select, | |||||
| textarea { | |||||
| font: inherit; /* Specify font inheritance of form elements */ | |||||
| } | |||||
| optgroup { | |||||
| font-weight: bold; /* Restore the font weight unset by the previous rule */ | |||||
| } | |||||
| button { | |||||
| overflow: visible; /* Address `overflow` set to `hidden` in IE 8/9/10/11 */ | |||||
| } | |||||
| button, | |||||
| select { | |||||
| text-transform: none; /* Firefox 40+, Internet Explorer 11- */ | |||||
| } | |||||
| /* Apply cursor pointer to button elements */ | |||||
| button, | |||||
| [type='button'], | |||||
| [type='reset'], | |||||
| [type='submit'], | |||||
| [role='button'] { | |||||
| color: inherit; | |||||
| cursor: pointer; | |||||
| } | |||||
| /* Remove inner padding and border in Firefox 4+ */ | |||||
| button::-moz-focus-inner, | |||||
| [type='button']::-moz-focus-inner, | |||||
| [type='reset']::-moz-focus-inner, | |||||
| [type='submit']::-moz-focus-inner { | |||||
| padding: 0; | |||||
| border-style: none; | |||||
| } | |||||
| /* Replace focus style removed in the border reset above */ | |||||
| button:-moz-focusring, | |||||
| [type='button']::-moz-focus-inner, | |||||
| [type='reset']::-moz-focus-inner, | |||||
| [type='submit']::-moz-focus-inner { | |||||
| outline: 1px dotted ButtonText; | |||||
| } | |||||
| button, | |||||
| html [type='button'], /* Prevent a WebKit bug where (2) destroys native `audio` and `video`controls in Android 4 */ | |||||
| [type='reset'], | |||||
| [type='submit'] { | |||||
| -webkit-appearance: button; /* Correct the inability to style clickable types in iOS */ | |||||
| } | |||||
| /* Remove the default button styling in all browsers */ | |||||
| button, | |||||
| input, | |||||
| select, | |||||
| textarea { | |||||
| background-color: transparent; | |||||
| border-style: none; | |||||
| } | |||||
| a:focus, | |||||
| button:focus, | |||||
| input:focus, | |||||
| select:focus, | |||||
| textarea:focus { | |||||
| outline-width: 0; | |||||
| } | |||||
| /* Style select like a standard input */ | |||||
| select { | |||||
| -moz-appearance: none; /* Firefox 36+ */ | |||||
| -webkit-appearance: none; /* Chrome 41+ */ | |||||
| } | |||||
| select::-ms-expand { | |||||
| display: none; /* Internet Explorer 11+ */ | |||||
| } | |||||
| select::-ms-value { | |||||
| color: currentColor; /* Internet Explorer 11+ */ | |||||
| } | |||||
| legend { | |||||
| display: table; /* Correct the text wrapping in Edge and IE */ | |||||
| max-width: 100%; /* Correct the text wrapping in Edge and IE */ | |||||
| max-width: 100%; /* Correct the text wrapping in Edge 18- and IE */ | |||||
| color: inherit; /* Correct the color inheritance from `fieldset` elements in IE */ | |||||
| white-space: normal; /* Correct the text wrapping in Edge and IE */ | |||||
| border: 0; /* Correct `color` not being inherited in IE 8/9/10/11 */ | |||||
| } | |||||
| ::-webkit-file-upload-button { | |||||
| font: inherit; /* Change font properties to `inherit` in Chrome and Safari */ | |||||
| color: inherit; | |||||
| /* Correct the inability to style clickable types in iOS and Safari */ | |||||
| -webkit-appearance: button; | |||||
| } | |||||
| /* # ================================================================= | |||||
| # Specify media element style | |||||
| # ================================================================= */ | |||||
| img { | |||||
| border-style: none; /* Remove border when inside `a` element in IE 8/9/10 */ | |||||
| } | |||||
| /* Add the correct vertical alignment in Chrome, Firefox, and Opera */ | |||||
| progress { | |||||
| vertical-align: baseline; | |||||
| } | |||||
| /* # ================================================================= | |||||
| # Accessibility | |||||
| # ================================================================= */ | |||||
| /* Specify the progress cursor of updating elements */ | |||||
| [aria-busy='true'] { | |||||
| cursor: progress; | |||||
| } | |||||
| /* Specify the pointer cursor of trigger elements */ | |||||
| [aria-controls] { | |||||
| cursor: pointer; | |||||
| } | |||||
| /* Specify the unstyled cursor of disabled, not-editable, or otherwise inoperable elements */ | |||||
| [aria-disabled='true'] { | |||||
| cursor: default; | |||||
| } | |||||
| @@ -5,7 +5,9 @@ import { Toast } from 'vant' | |||||
| // 基础地址 | // 基础地址 | ||||
| const baseURL = 'https://smapitest.malls.iformall.com/C' | const baseURL = 'https://smapitest.malls.iformall.com/C' | ||||
| // 创建axios实例 | |||||
| /** | |||||
| * @description:发送Axios请求 | |||||
| */ | |||||
| const request = axios.create({ | const request = axios.create({ | ||||
| baseURL, | baseURL, | ||||
| timeout: 5000 | timeout: 5000 | ||||
| @@ -56,7 +58,6 @@ request.interceptors.response.use( | |||||
| return response.data | return response.data | ||||
| } | } | ||||
| }, | }, | ||||
| function (error) { | function (error) { | ||||
| } | } | ||||
| @@ -0,0 +1,100 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <!-- 主体内容 --> | |||||
| <div class="contnet"> | |||||
| <!-- 头部 --> | |||||
| <div class="head"> | |||||
| <img src="../../assets/icon/icon-loginout.png" alt=""> | |||||
| <div class="head-exit"> | |||||
| <img src="../../assets/icon/icon-loginoutwhite.png" alt=""> | |||||
| 退出聊天 | |||||
| </div> | |||||
| </div> | |||||
| <!-- 显示区 --> | |||||
| <div class="showBody"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| // 组件 | |||||
| import Vue from "vue"; | |||||
| import { Toast } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| // 工具 | |||||
| import { scrollToID } from "../../utils"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| data() { | |||||
| return { | |||||
| inPage: false, | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| }, | |||||
| created() {}, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| transform: translateX(30%); | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| &.active { | |||||
| opacity: 1; | |||||
| transform: translateX(0); | |||||
| } | |||||
| } | |||||
| .contnet { | |||||
| .head { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| padding: 4px 10px; | |||||
| >img { | |||||
| width: 36px; | |||||
| height: 36px; | |||||
| } | |||||
| &-exit { | |||||
| margin-top: 4px; | |||||
| width: 97px; | |||||
| height: 28px; | |||||
| text-align: center; | |||||
| line-height: 28px; | |||||
| background: #24A9FF; | |||||
| color: #fff; | |||||
| box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.2); | |||||
| border-radius: 33px; | |||||
| >img { | |||||
| margin-top: -2px; | |||||
| margin-right: 4px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .showBody { | |||||
| width: 100%; | |||||
| height: calc(100vh - 44px) ; | |||||
| background-color: red; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| @@ -0,0 +1,441 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <div class="logoBox" @click="go('/')"> | |||||
| <img src="../../assets/img/logoh.png" alt class="logo" /> | |||||
| <!-- <div class="logoTitle">metavatar</div> --> | |||||
| </div> | |||||
| <!-- 短信验证码登录 --> | |||||
| <div class="loginBox" v-if="loginType == 1"> | |||||
| <div class="welcome">登录邃芒</div> | |||||
| <el-form ref="form" :rules="rules" :model="form"> | |||||
| <el-form-item class="phone" prop="phone"> | |||||
| <el-input class="phoneInput" v-model="form.phone"></el-input> | |||||
| <div class="inputPhone">请输入您的手机号</div> | |||||
| <el-button class="getCode" type="text" @click="getCode">{{ | |||||
| codeInfo | |||||
| }}</el-button> | |||||
| </el-form-item> | |||||
| <el-form-item class="code" prop="code"> | |||||
| <el-input | |||||
| class="codeInput" | |||||
| v-model="form.code" | |||||
| maxlength="10" | |||||
| clearable | |||||
| ></el-input> | |||||
| <div class="inputCode">请输入短信验证码</div> | |||||
| </el-form-item> | |||||
| <el-form-item> | |||||
| <el-button class="submitBtn" @click="loginByPhoneCode" | |||||
| >登录/注册</el-button | |||||
| > | |||||
| </el-form-item> | |||||
| </el-form> | |||||
| </div> | |||||
| <!-- 用户名密码登录 --> | |||||
| <div class="loginBox" v-if="loginType == 2"> | |||||
| <div class="welcome">登录邃芒</div> | |||||
| <el-form ref="form" :rules="rulesII" :model="formII"> | |||||
| <el-form-item class="phone" prop="phone"> | |||||
| <el-input class="phoneInput" v-model="formII.phone"></el-input> | |||||
| <div class="inputPhone">请输入您的手机号</div> | |||||
| <!-- <el-button class="getCode" type="text" @click="getCode">{{ | |||||
| codeInfo | |||||
| }}</el-button> --> | |||||
| </el-form-item> | |||||
| <el-form-item class="code" prop="code"> | |||||
| <el-input | |||||
| class="codeInput" | |||||
| v-model="form.pwd" | |||||
| maxlength="10" | |||||
| clearable | |||||
| ></el-input> | |||||
| <div class="inputCode">请输入密码</div> | |||||
| </el-form-item> | |||||
| <el-form-item class="code" prop="code"> | |||||
| <el-input | |||||
| class="codeInput" | |||||
| v-model="form.code" | |||||
| maxlength="10" | |||||
| clearable | |||||
| ></el-input> | |||||
| <div class="inputCode">请输入验证码</div> | |||||
| </el-form-item> | |||||
| <span class="forgetPwd">忘记密码?</span> | |||||
| <el-form-item> | |||||
| <el-button class="submitBtn inside" @click="loginByPhoneCode" | |||||
| >登录/注册</el-button | |||||
| > | |||||
| </el-form-item> | |||||
| </el-form> | |||||
| </div> | |||||
| <!-- 用户名密码登录 --> | |||||
| <div class="loginBox" v-if="loginType == 3"> | |||||
| <div class="welcome">登录邃芒</div> | |||||
| <el-form ref="form" :model="formIII"> | |||||
| <el-form-item class="code" prop="code"> | |||||
| <el-input | |||||
| class="codeInput" | |||||
| v-model="formIII.inviteCode" | |||||
| maxlength="10" | |||||
| clearable | |||||
| ></el-input> | |||||
| <div class="inputCode">请输入您的邀请码</div> | |||||
| </el-form-item> | |||||
| <el-form-item> | |||||
| <el-button class="skipBtn skip" @click="skip">跳过</el-button> | |||||
| <el-button class="skipBtn submit" @click="submitInviteCode" | |||||
| >确定</el-button | |||||
| > | |||||
| </el-form-item> | |||||
| </el-form> | |||||
| </div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| // 组件 | |||||
| import Vue from "vue"; | |||||
| import { Toast } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| // 工具 | |||||
| import { scrollToID } from "../../utils"; | |||||
| // 接口 | |||||
| import { | |||||
| getCodeByPhone, | |||||
| doLoginByPhone, | |||||
| doFindInviteCode, | |||||
| doUpdateInviteCode, | |||||
| } from "../../api/login"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| data() { | |||||
| return { | |||||
| codeInfo: "获取验证码", | |||||
| inPage: false, | |||||
| loginType: 1, | |||||
| // 短信验证码登录 | |||||
| form: { | |||||
| phone: "", | |||||
| code: "", | |||||
| }, | |||||
| rules: { | |||||
| phone: [ | |||||
| { | |||||
| required: true, | |||||
| trigger: "blur", | |||||
| message: "请先输手机号", | |||||
| }, | |||||
| { | |||||
| validator(rule, value, callback, source, options) { | |||||
| var errors = []; | |||||
| // var mobile = /^1[0|1|2|3|4|5|6|7|8|9]\d{9}$/, | |||||
| let phone = /^1[35789]\d{9}$/; | |||||
| if (value && !phone.test(value)) { | |||||
| callback("抱歉,请输入正确的手机号"); | |||||
| } | |||||
| callback(errors); | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| // 账号密码登录 | |||||
| formII: { | |||||
| phone: "", | |||||
| pwd: "", | |||||
| code: "", | |||||
| }, | |||||
| rulesII: { | |||||
| phone: [ | |||||
| { | |||||
| required: true, | |||||
| trigger: "blur", | |||||
| message: "请先输手机号", | |||||
| }, | |||||
| { | |||||
| validator(rule, value, callback, source, options) { | |||||
| var errors = []; | |||||
| // var mobile = /^1[0|1|2|3|4|5|6|7|8|9]\d{9}$/, | |||||
| let phone = /^1[35789]\d{9}$/; | |||||
| if (value && !phone.test(value)) { | |||||
| callback("抱歉,请输入正确的手机号"); | |||||
| } | |||||
| callback(errors); | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| // 邀请码 | |||||
| formIII: { | |||||
| inviteCode: "", | |||||
| }, | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| // 跳过填写邀请码 | |||||
| skip() { | |||||
| this.$router.push("/myPage"); | |||||
| }, | |||||
| // 获取手机短信验证码 | |||||
| getCode() { | |||||
| if (this.codeInfo != "获取验证码") { | |||||
| Toast.fail("请求过于频繁,请稍后再试"); | |||||
| return; | |||||
| } | |||||
| const phoneValue = this.form.phone; | |||||
| // 将手机号置入本地存储 | |||||
| localStorage.setItem("phoneValue", phoneValue); | |||||
| let phoneReg = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/; | |||||
| let phoneValide = phoneReg.test(phoneValue); | |||||
| if (!phoneValide) { | |||||
| Toast.fail("请输入正确的手机号!"); | |||||
| } else { | |||||
| this.getPhoneCode(phoneValue); | |||||
| } | |||||
| }, | |||||
| // 手机验证码登录 | |||||
| loginByPhoneCode() { | |||||
| const data = { | |||||
| phone: this.form.phone, | |||||
| code: this.form.code, | |||||
| }; | |||||
| this.LoginByPhone(data); | |||||
| }, | |||||
| // 提交邀请码 | |||||
| submitInviteCode() { | |||||
| const data = { | |||||
| inviteCode: this.formIII.inviteCode, | |||||
| }; | |||||
| this.updateInviteCode(data); | |||||
| }, | |||||
| /** | |||||
| * @description:获取手机验证码 | |||||
| */ | |||||
| async getPhoneCode(phone) { | |||||
| try { | |||||
| const res = await getCodeByPhone(phone); | |||||
| Toast.success("短信验证码发送成功!"); | |||||
| this.codeInfo = 60; | |||||
| const timer = setInterval(() => { | |||||
| if (this.codeInfo != 1) { | |||||
| this.codeInfo--; | |||||
| } else { | |||||
| clearInterval(timer); | |||||
| this.codeInfo = "获取验证码"; | |||||
| } | |||||
| }, 1000); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description:短信验证码登录 | |||||
| */ | |||||
| async LoginByPhone(loginParams) { | |||||
| try { | |||||
| const res = await doLoginByPhone(loginParams); | |||||
| console.log(res, "登录数据"); | |||||
| const token = res.data.token; | |||||
| // 将token置入本地存储 | |||||
| localStorage.setItem("AccessToken", token); | |||||
| this.checkInviteCode(); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description:查询用户邀请码状态 | |||||
| */ | |||||
| async checkInviteCode() { | |||||
| try { | |||||
| const res = await doFindInviteCode(); | |||||
| console.log(res, "查询用户邀请码数据"); | |||||
| const status = res.data.status; | |||||
| // 当没有邀请码时,切换输入验证码页面 | |||||
| if (status == 0) { | |||||
| this.loginType = 3; | |||||
| // 有邀请码直接前往我的页面 | |||||
| } else if (status == 1) { | |||||
| Toast.success("登录成功!"); | |||||
| this.$router.push("/myPage"); | |||||
| } | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description:更新邀请码 | |||||
| */ | |||||
| async updateInviteCode(inviteCodeData) { | |||||
| try { | |||||
| const res = await doUpdateInviteCode(inviteCodeData); | |||||
| console.log(res, "更新邀请码数据"); | |||||
| Toast.success("登录成功!"); | |||||
| this.$router.push("/myPage"); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| }, | |||||
| created() { | |||||
| // 手机号记忆回填 | |||||
| const phoneValue = localStorage.getItem("phoneValue"); | |||||
| this.form.phone = phoneValue ? phoneValue : ""; | |||||
| }, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| &.active { | |||||
| opacity: 1; | |||||
| } | |||||
| .logoBox { | |||||
| // margin-top: 10px; | |||||
| margin-left: 5px; | |||||
| margin-top: 10px; | |||||
| margin-bottom: 30px; | |||||
| overflow: hidden; | |||||
| cursor: pointer; | |||||
| text-align: center; | |||||
| .logo { | |||||
| width: 240px; | |||||
| height: 70px; | |||||
| } | |||||
| } | |||||
| .loginBox { | |||||
| height: 500px; | |||||
| // background: linear-gradient(140deg, #6e60e9, #2867d4, #1ec2ae); | |||||
| margin: auto; | |||||
| text-align: center; | |||||
| padding-left: 30px; | |||||
| padding-right: 30px; | |||||
| .welcome { | |||||
| font-size: 18px; | |||||
| color: #000; | |||||
| font-weight: 600; | |||||
| margin-bottom: 40px; | |||||
| } | |||||
| .phone { | |||||
| position: relative; | |||||
| margin-bottom: 25px; | |||||
| .inputPhone { | |||||
| position: absolute; | |||||
| background-color: #fff; | |||||
| top: -5px; | |||||
| left: 12px; | |||||
| font-size: 12px; | |||||
| height: 10px; | |||||
| line-height: 10px; | |||||
| color: #0068b7; | |||||
| } | |||||
| .getCode { | |||||
| position: absolute; | |||||
| top: 1px; | |||||
| right: 10px; | |||||
| color: #0068b7; | |||||
| } | |||||
| } | |||||
| .code { | |||||
| position: relative; | |||||
| .inputCode { | |||||
| position: absolute; | |||||
| background-color: #fff; | |||||
| top: -5px; | |||||
| left: 12px; | |||||
| font-size: 12px; | |||||
| height: 10px; | |||||
| line-height: 10px; | |||||
| color: #0068b7; | |||||
| } | |||||
| } | |||||
| .submitBtn { | |||||
| position: relative; | |||||
| right: 10px; | |||||
| float: right; | |||||
| color: #fff; | |||||
| font-size: 15px; | |||||
| line-height: 15px; | |||||
| border: none; | |||||
| padding: 9px; | |||||
| background-color: #0068b7; | |||||
| border-radius: 5px; | |||||
| &.inside { | |||||
| top: 10px; | |||||
| } | |||||
| } | |||||
| .skipBtn { | |||||
| position: relative; | |||||
| top: 10px; | |||||
| left: 70px; | |||||
| right: 10px; | |||||
| width: 78px; | |||||
| height: 30px; | |||||
| color: #fff; | |||||
| font-size: 15px; | |||||
| line-height: 15px; | |||||
| border: none; | |||||
| padding: 9px; | |||||
| background-color: #0068b7; | |||||
| border-radius: 3px; | |||||
| margin-top: 160px; | |||||
| &.skip { | |||||
| background-color: #a0a0a0; | |||||
| } | |||||
| } | |||||
| .forgetPwd { | |||||
| position: relative; | |||||
| top: -10px; | |||||
| float: left; | |||||
| font-size: 12px; | |||||
| color: #0068b7; | |||||
| } | |||||
| } | |||||
| } | |||||
| ::v-deep .el-input__inner { | |||||
| height: 42px; | |||||
| border: 1px solid #0068b7; | |||||
| } | |||||
| </style> | |||||
| @@ -1,4 +1,5 @@ | |||||
| <template> | <template> | ||||
| <!-- 手机号13797188591 --> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | <div :class="inPage ? 'page active' : 'page'"> | ||||
| <div id="top"></div> | <div id="top"></div> | ||||
| <div class="logoBox" @click="go('/')"> | <div class="logoBox" @click="go('/')"> | ||||
| @@ -6,214 +7,347 @@ | |||||
| <!-- <div class="logoTitle">metavatar</div> --> | <!-- <div class="logoTitle">metavatar</div> --> | ||||
| </div> | </div> | ||||
| <!-- 短信验证码登录 --> | |||||
| <!-- 首页登录 loginType=1 --> | |||||
| <div class="loginBox" v-if="loginType == 1"> | <div class="loginBox" v-if="loginType == 1"> | ||||
| <div class="welcome">登录邃芒</div> | |||||
| <el-form ref="form" :rules="rules" :model="form"> | <el-form ref="form" :rules="rules" :model="form"> | ||||
| <!-- 手机号 --> | |||||
| <el-form-item class="phone" prop="phone"> | <el-form-item class="phone" prop="phone"> | ||||
| <el-input class="phoneInput" v-model="form.phone"></el-input> | |||||
| <div class="inputPhone">请输入您的手机号</div> | |||||
| <el-button class="getCode" type="text" @click="getCode">{{ | |||||
| codeInfo | |||||
| }}</el-button> | |||||
| <el-input | |||||
| v-model="form.phone" | |||||
| placeholder="请输入您的手机号" | |||||
| ></el-input> | |||||
| </el-form-item> | </el-form-item> | ||||
| <el-form-item class="code" prop="code"> | |||||
| <!-- 密码 --> | |||||
| <el-form-item class="code" prop="password"> | |||||
| <el-input | <el-input | ||||
| class="codeInput" | |||||
| v-model="form.code" | |||||
| maxlength="10" | maxlength="10" | ||||
| clearable | clearable | ||||
| v-model="form.code" | |||||
| placeholder="请输入密码" | |||||
| ></el-input> | ></el-input> | ||||
| <div class="inputCode">请输入短信验证码</div> | |||||
| </el-form-item> | </el-form-item> | ||||
| <!-- 随机验证码 --> | |||||
| <el-form-item class="yzCode" prop="yzCode"> | |||||
| <el-col :span="12"> | |||||
| <el-input v-model="form.code" placeholder="请输入验证码"></el-input> | |||||
| </el-col> | |||||
| </el-form-item> | |||||
| <!-- 短信登录和忘记密码 --> | |||||
| <div class="morePage greenColor"> | |||||
| <span @click="loginType=2">短信登录</span> | |||||
| <span @click="loginType=3">忘记密码?</span> | |||||
| </div> | |||||
| <!-- 登录 --> | |||||
| <el-form-item> | <el-form-item> | ||||
| <el-button class="submitBtn" @click="loginByPhoneCode" | <el-button class="submitBtn" @click="loginByPhoneCode" | ||||
| >登录/注册</el-button | |||||
| > | |||||
| >登录/注册</el-button> | |||||
| </el-form-item> | </el-form-item> | ||||
| <!-- 没有账号 --> | |||||
| <div> | |||||
| <span>没有账号?</span> | |||||
| <span class="greenColor" @click="loginType=5">立即注册</span> | |||||
| </div> | |||||
| </el-form> | </el-form> | ||||
| </div> | </div> | ||||
| <!-- 用户名密码登录 --> | |||||
| <!-- 短信登录 loginType=2 --> | |||||
| <div class="loginBox" v-if="loginType == 2"> | <div class="loginBox" v-if="loginType == 2"> | ||||
| <div class="welcome">登录邃芒</div> | |||||
| <el-form ref="form" :rules="rulesII" :model="formII"> | |||||
| <el-form ref="form" :rules="rules" :model="form"> | |||||
| <!-- 手机号 --> | |||||
| <el-form-item class="phone" prop="phone"> | <el-form-item class="phone" prop="phone"> | ||||
| <el-input class="phoneInput" v-model="formII.phone"></el-input> | |||||
| <div class="inputPhone">请输入您的手机号</div> | |||||
| <!-- <el-button class="getCode" type="text" @click="getCode">{{ | |||||
| codeInfo | |||||
| }}</el-button> --> | |||||
| <el-input | |||||
| v-model="form.phone" | |||||
| placeholder="请输入您的手机号" | |||||
| ></el-input> | |||||
| </el-form-item> | </el-form-item> | ||||
| <el-form-item class="code" prop="code"> | |||||
| <!-- 短信验证码 --> | |||||
| <el-form-item class="code" prop="phoneCode"> | |||||
| <el-input | <el-input | ||||
| class="codeInput" | |||||
| v-model="form.pwd" | |||||
| maxlength="10" | maxlength="10" | ||||
| clearable | clearable | ||||
| v-model="form.code" | |||||
| placeholder="请输入短信验证码" | |||||
| > | |||||
| <div v-if="!countdownState" @click="triggerCountdown" class="greenColor" slot="suffix" type="primary">获取验证码</div> | |||||
| <div v-else slot="suffix" type="primary">验证码已发送{{ countNum }}s</div> | |||||
| </el-input> | |||||
| </el-form-item> | |||||
| <!-- 短信登录和忘记密码 --> | |||||
| <div class="morePage greenColor inBoxEnd"> | |||||
| <span @click="loginType=1">账号登录</span> | |||||
| </div> | |||||
| <!-- 登录 --> | |||||
| <el-form-item> | |||||
| <el-button class="submitBtn" @click="loginByPhoneCode" | |||||
| >登录</el-button> | |||||
| </el-form-item> | |||||
| <!-- 没有账号 --> | |||||
| <div> | |||||
| <span>没有账号?</span> | |||||
| <span class="greenColor" @click="loginType=5">立即注册</span> | |||||
| </div> | |||||
| </el-form> | |||||
| </div> | |||||
| <!-- 忘记密码 loginType=3--> | |||||
| <div class="loginBox" v-if="loginType == 3"> | |||||
| <el-form ref="form" :rules="rules" :model="form"> | |||||
| <!-- 手机号 --> | |||||
| <el-form-item class="phone" prop="phone"> | |||||
| <el-input | |||||
| v-model="form.phone" | |||||
| placeholder="请输入您的手机号" | |||||
| ></el-input> | ></el-input> | ||||
| <div class="inputCode">请输入密码</div> | |||||
| </el-form-item> | </el-form-item> | ||||
| <!-- 随机验证码 --> | |||||
| <el-form-item class="code" prop="yzCode"> | |||||
| <el-col :span="12"> | |||||
| <el-input | |||||
| maxlength="10" | |||||
| clearable | |||||
| v-model="form.code" | |||||
| placeholder="请输入验证码" | |||||
| ></el-input> | |||||
| </el-col> | |||||
| </el-form-item> | |||||
| <!-- 登录 --> | |||||
| <el-form-item> | |||||
| <el-button class="submitBtn" @click="loginByPhoneCode" | |||||
| >获取短信验证码</el-button> | |||||
| </el-form-item> | |||||
| </el-form> | |||||
| </div> | |||||
| <el-form-item class="code" prop="code"> | |||||
| <!-- 重设密码 loginType=4 --> | |||||
| <div class="loginBox" v-if="loginType == 4"> | |||||
| <el-form ref="form" :rules="rules" :model="form"> | |||||
| <!-- 短信验证码 --> | |||||
| <el-form-item class="phone" prop="phoneCode"> | |||||
| <el-input | |||||
| v-model="form.phone" | |||||
| placeholder="请输入短信验证码" | |||||
| ></el-input> | |||||
| </el-form-item> | |||||
| <!-- 新密码1 --> | |||||
| <el-form-item class="code" prop="newPassword1"> | |||||
| <el-input | <el-input | ||||
| class="codeInput" | |||||
| maxlength="10" | |||||
| clearable | |||||
| v-model="form.code" | v-model="form.code" | ||||
| placeholder="请输入您的新密码" | |||||
| > | |||||
| </el-input> | |||||
| </el-form-item> | |||||
| <!-- 新密码2 --> | |||||
| <el-form-item class="code" prop="newPassword2"> | |||||
| <el-input | |||||
| maxlength="10" | maxlength="10" | ||||
| clearable | clearable | ||||
| ></el-input> | |||||
| <div class="inputCode">请输入验证码</div> | |||||
| v-model="form.code" | |||||
| placeholder="请再次确认您的新密码" | |||||
| > | |||||
| </el-input> | |||||
| </el-form-item> | </el-form-item> | ||||
| <span class="forgetPwd">忘记密码?</span> | |||||
| <!-- 登录 --> | |||||
| <el-form-item> | <el-form-item> | ||||
| <el-button class="submitBtn inside" @click="loginByPhoneCode" | |||||
| >登录/注册</el-button | |||||
| > | |||||
| <el-button class="submitBtn" @click="loginByPhoneCode" | |||||
| >保存密码</el-button> | |||||
| </el-form-item> | </el-form-item> | ||||
| </el-form> | </el-form> | ||||
| </div> | </div> | ||||
| <!-- 用户名密码登录 --> | |||||
| <div class="loginBox" v-if="loginType == 3"> | |||||
| <div class="welcome">登录邃芒</div> | |||||
| <el-form ref="form" :model="formIII"> | |||||
| <el-form-item class="code" prop="code"> | |||||
| <!-- 注册 loginType=5 --> | |||||
| <div class="loginBox" v-if="loginType == 5"> | |||||
| <el-form ref="form" :rules="rules" :model="form"> | |||||
| <!-- 短信验证码 --> | |||||
| <el-form-item class="phone" prop="phone"> | |||||
| <el-input | |||||
| v-model="form.phone" | |||||
| placeholder="请输入您的手机号" | |||||
| ></el-input> | |||||
| </el-form-item> | |||||
| <!-- 新密码1 --> | |||||
| <el-form-item class="code" prop="newPassword1"> | |||||
| <el-input | <el-input | ||||
| class="codeInput" | |||||
| v-model="formIII.inviteCode" | |||||
| maxlength="10" | maxlength="10" | ||||
| clearable | clearable | ||||
| ></el-input> | |||||
| <div class="inputCode">请输入您的邀请码</div> | |||||
| v-model="form.code" | |||||
| placeholder="请输入您的密码" | |||||
| > | |||||
| </el-input> | |||||
| </el-form-item> | </el-form-item> | ||||
| <el-form-item> | |||||
| <el-button class="skipBtn skip" @click="skip">跳过</el-button> | |||||
| <el-button class="skipBtn submit" @click="submitInviteCode" | |||||
| >确定</el-button | |||||
| <!-- 新密码2 --> | |||||
| <el-form-item class="code" prop="newPassword2"> | |||||
| <el-input | |||||
| maxlength="10" | |||||
| clearable | |||||
| v-model="form.code" | |||||
| placeholder="请再次确认您的密码" | |||||
| > | > | ||||
| </el-input> | |||||
| </el-form-item> | </el-form-item> | ||||
| <!-- 注册 --> | |||||
| <el-form-item> | |||||
| <el-button class="submitBtn" @click="loginByPhoneCode" | |||||
| >注册</el-button> | |||||
| </el-form-item> | |||||
| </el-form> | </el-form> | ||||
| </div> | </div> | ||||
| <!-- 弹出层 --> | |||||
| <van-popup v-model="showDialog">内容</van-popup> | |||||
| </div> | </div> | ||||
| </template> | </template> | ||||
| <script> | <script> | ||||
| // 组件 | // 组件 | ||||
| import Vue from "vue"; | |||||
| import { Toast } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| import Vue from 'vue' | |||||
| import { Toast } from 'vant' | |||||
| import { mapState, mapActions } from 'vuex' | |||||
| // 工具 | // 工具 | ||||
| import { scrollToID } from "../../utils"; | |||||
| import { scrollToID } from '../../utils' | |||||
| // 接口 | // 接口 | ||||
| import { | import { | ||||
| getCodeByPhone, | getCodeByPhone, | ||||
| doLoginByPhone, | doLoginByPhone, | ||||
| doFindInviteCode, | doFindInviteCode, | ||||
| doUpdateInviteCode, | |||||
| } from "../../api/login"; | |||||
| doUpdateInviteCode | |||||
| } from '../../api/login' | |||||
| Vue.use(Toast); | |||||
| Vue.use(Toast) | |||||
| export default { | export default { | ||||
| data() { | data() { | ||||
| return { | return { | ||||
| codeInfo: "获取验证码", | |||||
| showDialog:false,//弹出层状态 | |||||
| countNum: 60,// 倒计时 | |||||
| countdownState:false,//倒计时状态 | |||||
| codeInfo: '获取验证码', | |||||
| inPage: false, | inPage: false, | ||||
| loginType: 1, | |||||
| loginType: 1, //显示页面字符 | |||||
| // 短信验证码登录 | // 短信验证码登录 | ||||
| form: { | form: { | ||||
| phone: "", | |||||
| code: "", | |||||
| phone: '', | |||||
| password: '', | |||||
| newPassword1:'', | |||||
| newPassword2:'', | |||||
| phoneCode:'', | |||||
| code: '', | |||||
| yzCode: '' | |||||
| }, | }, | ||||
| rules: { | rules: { | ||||
| phone: [ | phone: [ | ||||
| { | { | ||||
| required: true, | required: true, | ||||
| trigger: "blur", | |||||
| message: "请先输手机号", | |||||
| trigger: 'blur', | |||||
| message: '请先输手机号' | |||||
| }, | }, | ||||
| { | { | ||||
| validator(rule, value, callback, source, options) { | validator(rule, value, callback, source, options) { | ||||
| var errors = []; | |||||
| var errors = [] | |||||
| // var mobile = /^1[0|1|2|3|4|5|6|7|8|9]\d{9}$/, | // var mobile = /^1[0|1|2|3|4|5|6|7|8|9]\d{9}$/, | ||||
| let phone = /^1[35789]\d{9}$/; | |||||
| let phone = /^1[35789]\d{9}$/ | |||||
| if (value && !phone.test(value)) { | if (value && !phone.test(value)) { | ||||
| callback("抱歉,请输入正确的手机号"); | |||||
| callback('抱歉,请输入正确的手机号') | |||||
| } | } | ||||
| callback(errors); | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| callback(errors) | |||||
| } | |||||
| } | |||||
| ] | |||||
| }, | }, | ||||
| // 账号密码登录 | // 账号密码登录 | ||||
| formII: { | formII: { | ||||
| phone: "", | |||||
| pwd: "", | |||||
| code: "", | |||||
| phone: '', | |||||
| pwd: '', | |||||
| code: '' | |||||
| }, | }, | ||||
| rulesII: { | rulesII: { | ||||
| phone: [ | phone: [ | ||||
| { | { | ||||
| required: true, | required: true, | ||||
| trigger: "blur", | |||||
| message: "请先输手机号", | |||||
| trigger: 'blur', | |||||
| message: '请先输手机号' | |||||
| }, | }, | ||||
| { | { | ||||
| validator(rule, value, callback, source, options) { | validator(rule, value, callback, source, options) { | ||||
| var errors = []; | |||||
| var errors = [] | |||||
| // var mobile = /^1[0|1|2|3|4|5|6|7|8|9]\d{9}$/, | // var mobile = /^1[0|1|2|3|4|5|6|7|8|9]\d{9}$/, | ||||
| let phone = /^1[35789]\d{9}$/; | |||||
| let phone = /^1[35789]\d{9}$/ | |||||
| if (value && !phone.test(value)) { | if (value && !phone.test(value)) { | ||||
| callback("抱歉,请输入正确的手机号"); | |||||
| callback('抱歉,请输入正确的手机号') | |||||
| } | } | ||||
| callback(errors); | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| callback(errors) | |||||
| } | |||||
| } | |||||
| ] | |||||
| }, | }, | ||||
| // 短信登录 | |||||
| // 忘记密码 | |||||
| // 重设密码 | |||||
| // 注册 | |||||
| // 邀请码 | // 邀请码 | ||||
| formIII: { | formIII: { | ||||
| inviteCode: "", | |||||
| }, | |||||
| }; | |||||
| inviteCode: '' | |||||
| } | |||||
| } | |||||
| }, | }, | ||||
| computed: { | computed: { | ||||
| ...mapState({ | ...mapState({ | ||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| characters: (state) => state.publicObj.characters | |||||
| }) | |||||
| }, | }, | ||||
| methods: { | methods: { | ||||
| ...mapActions(["setCharacters"]), | |||||
| ...mapActions(['setCharacters']), | |||||
| // 发送短信验证码 | |||||
| triggerCountdown() { | |||||
| //调用接口 | |||||
| this.getCode() | |||||
| // 切换文字 | |||||
| this.countdownState = !this.countdownState; | |||||
| // 定时器 | |||||
| let intervalBtn = setInterval(() => { | |||||
| // 倒数自减 | |||||
| this.countNum--; | |||||
| if(this.countNum < 0) { | |||||
| // 清除定时器 | |||||
| clearInterval(intervalBtn) | |||||
| // 更改状态 | |||||
| this.countdownState =! this.countdownState; | |||||
| // 重置倒计时状态 | |||||
| this.countNum = 60; | |||||
| }; | |||||
| // 倒计时 | |||||
| }, 1000); | |||||
| }, | |||||
| go(url) { | go(url) { | ||||
| this.$router.push(url); | |||||
| this.$router.push(url) | |||||
| }, | }, | ||||
| // 跳过填写邀请码 | // 跳过填写邀请码 | ||||
| skip() { | skip() { | ||||
| this.$router.push("/myPage"); | |||||
| this.$router.push('/myPage') | |||||
| }, | }, | ||||
| // 获取手机短信验证码 | // 获取手机短信验证码 | ||||
| getCode() { | getCode() { | ||||
| if (this.codeInfo != "获取验证码") { | |||||
| Toast.fail("请求过于频繁,请稍后再试"); | |||||
| return; | |||||
| if (this.codeInfo != '获取验证码') { | |||||
| Toast.fail('请求过于频繁,请稍后再试') | |||||
| return | |||||
| } | } | ||||
| const phoneValue = this.form.phone; | |||||
| const phoneValue = this.form.phone | |||||
| // 将手机号置入本地存储 | // 将手机号置入本地存储 | ||||
| localStorage.setItem("phoneValue", phoneValue); | |||||
| let phoneReg = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/; | |||||
| let phoneValide = phoneReg.test(phoneValue); | |||||
| localStorage.setItem('phoneValue', phoneValue) | |||||
| let phoneReg = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/ | |||||
| let phoneValide = phoneReg.test(phoneValue) | |||||
| if (!phoneValide) { | if (!phoneValide) { | ||||
| Toast.fail("请输入正确的手机号!"); | |||||
| Toast.fail('请输入正确的手机号!') | |||||
| } else { | } else { | ||||
| this.getPhoneCode(phoneValue); | |||||
| this.getPhoneCode(phoneValue) | |||||
| } | } | ||||
| }, | }, | ||||
| @@ -221,17 +355,17 @@ export default { | |||||
| loginByPhoneCode() { | loginByPhoneCode() { | ||||
| const data = { | const data = { | ||||
| phone: this.form.phone, | phone: this.form.phone, | ||||
| code: this.form.code, | |||||
| }; | |||||
| this.LoginByPhone(data); | |||||
| code: this.form.code | |||||
| } | |||||
| this.LoginByPhone(data) | |||||
| }, | }, | ||||
| // 提交邀请码 | // 提交邀请码 | ||||
| submitInviteCode() { | submitInviteCode() { | ||||
| const data = { | const data = { | ||||
| inviteCode: this.formIII.inviteCode, | |||||
| }; | |||||
| this.updateInviteCode(data); | |||||
| inviteCode: this.formIII.inviteCode | |||||
| } | |||||
| this.updateInviteCode(data) | |||||
| }, | }, | ||||
| /** | /** | ||||
| @@ -239,20 +373,20 @@ export default { | |||||
| */ | */ | ||||
| async getPhoneCode(phone) { | async getPhoneCode(phone) { | ||||
| try { | try { | ||||
| const res = await getCodeByPhone(phone); | |||||
| Toast.success("短信验证码发送成功!"); | |||||
| this.codeInfo = 60; | |||||
| const res = await getCodeByPhone(phone) | |||||
| Toast.success('短信验证码发送成功!') | |||||
| this.codeInfo = 60 | |||||
| const timer = setInterval(() => { | const timer = setInterval(() => { | ||||
| if (this.codeInfo != 1) { | if (this.codeInfo != 1) { | ||||
| this.codeInfo--; | |||||
| this.codeInfo-- | |||||
| } else { | } else { | ||||
| clearInterval(timer); | |||||
| this.codeInfo = "获取验证码"; | |||||
| clearInterval(timer) | |||||
| this.codeInfo = '获取验证码' | |||||
| } | } | ||||
| }, 1000); | |||||
| }, 1000) | |||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| console.log(error, 'error') | |||||
| Toast.fail(error.message) | |||||
| } | } | ||||
| }, | }, | ||||
| @@ -261,15 +395,15 @@ export default { | |||||
| */ | */ | ||||
| async LoginByPhone(loginParams) { | async LoginByPhone(loginParams) { | ||||
| try { | try { | ||||
| const res = await doLoginByPhone(loginParams); | |||||
| console.log(res, "登录数据"); | |||||
| const token = res.data.token; | |||||
| const res = await doLoginByPhone(loginParams) | |||||
| console.log(res, '登录数据') | |||||
| const token = res.data.token | |||||
| // 将token置入本地存储 | // 将token置入本地存储 | ||||
| localStorage.setItem("AccessToken", token); | |||||
| this.checkInviteCode(); | |||||
| localStorage.setItem('AccessToken', token) | |||||
| this.checkInviteCode() | |||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| console.log(error, 'error') | |||||
| Toast.fail(error.message) | |||||
| } | } | ||||
| }, | }, | ||||
| @@ -278,20 +412,20 @@ export default { | |||||
| */ | */ | ||||
| async checkInviteCode() { | async checkInviteCode() { | ||||
| try { | try { | ||||
| const res = await doFindInviteCode(); | |||||
| console.log(res, "查询用户邀请码数据"); | |||||
| const status = res.data.status; | |||||
| const res = await doFindInviteCode() | |||||
| console.log(res, '查询用户邀请码数据') | |||||
| const status = res.data.status | |||||
| // 当没有邀请码时,切换输入验证码页面 | // 当没有邀请码时,切换输入验证码页面 | ||||
| if (status == 0) { | if (status == 0) { | ||||
| this.loginType = 3; | |||||
| this.loginType = 3 | |||||
| // 有邀请码直接前往我的页面 | // 有邀请码直接前往我的页面 | ||||
| } else if (status == 1) { | } else if (status == 1) { | ||||
| Toast.success("登录成功!"); | |||||
| this.$router.push("/myPage"); | |||||
| Toast.success('登录成功!') | |||||
| this.$router.push('/myPage') | |||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| console.log(error, 'error') | |||||
| Toast.fail(error.message) | |||||
| } | } | ||||
| }, | }, | ||||
| @@ -300,29 +434,32 @@ export default { | |||||
| */ | */ | ||||
| async updateInviteCode(inviteCodeData) { | async updateInviteCode(inviteCodeData) { | ||||
| try { | try { | ||||
| const res = await doUpdateInviteCode(inviteCodeData); | |||||
| console.log(res, "更新邀请码数据"); | |||||
| Toast.success("登录成功!"); | |||||
| this.$router.push("/myPage"); | |||||
| const res = await doUpdateInviteCode(inviteCodeData) | |||||
| console.log(res, '更新邀请码数据') | |||||
| Toast.success('登录成功!') | |||||
| this.$router.push('/myPage') | |||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| console.log(error, 'error') | |||||
| Toast.fail(error.message) | |||||
| } | } | ||||
| }, | |||||
| } | |||||
| }, | }, | ||||
| created() { | created() { | ||||
| // 手机号记忆回填 | // 手机号记忆回填 | ||||
| const phoneValue = localStorage.getItem("phoneValue"); | |||||
| this.form.phone = phoneValue ? phoneValue : ""; | |||||
| const phoneValue = localStorage.getItem('phoneValue') | |||||
| this.form.phone = phoneValue ? phoneValue : '' | |||||
| }, | }, | ||||
| mounted() { | mounted() { | ||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| }; | |||||
| this.inPage = true | |||||
| scrollToID('top') | |||||
| } | |||||
| } | |||||
| </script> | </script> | ||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||
| .greenColor { | |||||
| color: #5ed5c8; | |||||
| } | |||||
| .page { | .page { | ||||
| opacity: 0; | opacity: 0; | ||||
| transition: all 0.5s; // global-transition | transition: all 0.5s; // global-transition | ||||
| @@ -332,8 +469,8 @@ export default { | |||||
| .logoBox { | .logoBox { | ||||
| // margin-top: 10px; | // margin-top: 10px; | ||||
| margin-left: 5px; | margin-left: 5px; | ||||
| margin-top: 10px; | |||||
| margin-bottom: 30px; | |||||
| margin-top: 25px; | |||||
| margin-bottom: 25px; | |||||
| overflow: hidden; | overflow: hidden; | ||||
| cursor: pointer; | cursor: pointer; | ||||
| text-align: center; | text-align: center; | ||||
| @@ -356,20 +493,14 @@ export default { | |||||
| font-weight: 600; | font-weight: 600; | ||||
| margin-bottom: 40px; | margin-bottom: 40px; | ||||
| } | } | ||||
| .inBoxEnd { | |||||
| display: flex; | |||||
| justify-content: end !important; | |||||
| } | |||||
| .phone { | .phone { | ||||
| position: relative; | position: relative; | ||||
| margin-bottom: 25px; | margin-bottom: 25px; | ||||
| .inputPhone { | |||||
| position: absolute; | |||||
| background-color: #fff; | |||||
| top: -5px; | |||||
| left: 12px; | |||||
| font-size: 12px; | |||||
| height: 10px; | |||||
| line-height: 10px; | |||||
| color: #0068b7; | |||||
| } | |||||
| .getCode { | .getCode { | ||||
| position: absolute; | position: absolute; | ||||
| top: 1px; | top: 1px; | ||||
| @@ -390,18 +521,34 @@ export default { | |||||
| color: #0068b7; | color: #0068b7; | ||||
| } | } | ||||
| } | } | ||||
| .morePage { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-top: -9px; | |||||
| } | |||||
| .submitBtn { | .submitBtn { | ||||
| position: relative; | position: relative; | ||||
| right: 10px; | |||||
| float: right; | |||||
| // right: 10px; | |||||
| // float: right; | |||||
| color: #fff; | color: #fff; | ||||
| font-size: 15px; | font-size: 15px; | ||||
| line-height: 15px; | line-height: 15px; | ||||
| border: none; | border: none; | ||||
| margin-top: 25px; | |||||
| // margin-bottom: 7.5px; | |||||
| padding: 9px; | padding: 9px; | ||||
| background-color: #0068b7; | background-color: #0068b7; | ||||
| border-radius: 5px; | border-radius: 5px; | ||||
| background-image: linear-gradient( | |||||
| 316deg, | |||||
| rgba(110, 228, 215, 1) 0, | |||||
| rgba(55, 196, 241, 1) 31.975823%, | |||||
| rgba(164, 154, 252, 1) 100% | |||||
| ); | |||||
| border-radius: 25px; | |||||
| width: 315px; | |||||
| height: 50px; | |||||
| // padding: 12px 138px 12px 138px; | |||||
| &.inside { | &.inside { | ||||
| top: 10px; | top: 10px; | ||||
| } | } | ||||
| @@ -436,6 +583,10 @@ export default { | |||||
| } | } | ||||
| ::v-deep .el-input__inner { | ::v-deep .el-input__inner { | ||||
| height: 42px; | height: 42px; | ||||
| border: 1px solid #0068b7; | |||||
| border: 0px; | |||||
| background-color: #f6f6f6; | |||||
| } | |||||
| ::v-deep .el-form-item { | |||||
| margin-bottom: 25px; | |||||
| } | } | ||||
| </style> | </style> | ||||
| @@ -0,0 +1,312 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <HeadTop /> | |||||
| <!-- <div class="chooseModel"> | |||||
| <div :class="phase == 1 ? 'active' : ''">选择模板</div> | |||||
| <div :class="phase == 2 ? 'active' : ''">录入文案</div> | |||||
| <div :class="phase == 3 ? 'active' : ''">合成视频</div> | |||||
| </div> --> | |||||
| <div class="title">选择模板</div> | |||||
| <div class="typeOutSide"> | |||||
| <div class="type"> | |||||
| <div :class="type == 0 ? 'active' : ''" @click="chooseType(0)"> | |||||
| 全部模板 | |||||
| </div> | |||||
| <div :class="type == 2 ? 'active' : ''" @click="chooseType(2)"> | |||||
| 女性模板 | |||||
| </div> | |||||
| <div :class="type == 1 ? 'active' : ''" @click="chooseType(1)"> | |||||
| 男性模板 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="choose"> | |||||
| <div class="modeList"> | |||||
| <div | |||||
| class="model" | |||||
| :class="currentId == item.id ? 'active' : ''" | |||||
| v-if="modeList.length >= 1" | |||||
| v-for="(item, index) in modeList" | |||||
| :key="index" | |||||
| @click="selectModel(item.id)" | |||||
| > | |||||
| <img :src="item.coverImg" alt="" /> | |||||
| <div class="modelName">{{ item.title }}</div> | |||||
| <div class="setPaper">录入文案</div> | |||||
| </div> | |||||
| <div class="alt" v-if="modeList.length == 0">暂无可用模板</div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="blank" v-if="modeList.length <= 2"></div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| import Vue from "vue"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| import HeadTop from "./../../components/common/head.vue"; | |||||
| import { Toast } from "vant"; | |||||
| import { scrollToID } from "../../utils"; | |||||
| import { | |||||
| getModeList, | |||||
| getModeDetailById, | |||||
| saveOrUpdateUserVideo, | |||||
| } from "../../api/chooseModel"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| components: { | |||||
| HeadTop, | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| inPage: false, | |||||
| type: 0, | |||||
| currentId: "", | |||||
| modeList: [], // 模板列表 | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| globalWatcher(e) { | |||||
| console.log(e, "e"); | |||||
| }, | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| chooseModel(num) { | |||||
| this.phase = num; | |||||
| }, | |||||
| chooseType(num) { | |||||
| this.type = num; | |||||
| this.loadModeList(num); | |||||
| }, | |||||
| selectModel(id) { | |||||
| if (this.currentId != id) { | |||||
| this.currentId = id; | |||||
| } else { | |||||
| this.getModeDetail(id); | |||||
| } | |||||
| }, | |||||
| // 获取模板列表 | |||||
| async loadModeList(sex) { | |||||
| try { | |||||
| sex = sex == 0 ? "" : sex; | |||||
| const res = await getModeList(sex); | |||||
| console.log(res, "获取模板列表"); | |||||
| this.modeList = res.data.list; | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| async getModeDetail(id) { | |||||
| try { | |||||
| const res = await getModeDetailById(id); | |||||
| console.log(res, "根据id获取模板详情"); | |||||
| const modelDetail = res.data; | |||||
| this.goSetPaperWork(modelDetail); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| async saveToSetPaper(data, modelDetail) { | |||||
| try { | |||||
| const res = await saveOrUpdateUserVideo(data); | |||||
| console.log(res, "保存或修改用户作品"); | |||||
| this.$router.push({ | |||||
| path: "/getPaper", | |||||
| query: { | |||||
| modelDetail: JSON.stringify(modelDetail), | |||||
| saveID: res.data.id, | |||||
| }, | |||||
| }); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description:根据模板详情跳转至选择文案 | |||||
| * @param {type}modelDetail | |||||
| * @event:Go page => setPaperWork | |||||
| */ | |||||
| goSetPaperWork(modelDetail) { | |||||
| const data = { | |||||
| // id: "", | |||||
| type: 1, | |||||
| personMouldId: modelDetail.id, //模板id | |||||
| personMouldSmId: modelDetail.mouldSmId, //模板标识 | |||||
| // voiceMouldId: "", | |||||
| // voiceMouldId: "", | |||||
| }; | |||||
| this.saveToSetPaper(data, modelDetail); | |||||
| }, | |||||
| }, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| created() { | |||||
| this.loadModeList(2); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| transform: translateX(30%); | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| padding-bottom: 25px; | |||||
| &.active { | |||||
| opacity: 1; | |||||
| transform: translateX(0); | |||||
| } | |||||
| .chooseModel { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| width: 350px; | |||||
| height: 32px; | |||||
| margin: auto; | |||||
| margin-top: 15px; | |||||
| margin-bottom: 15px; | |||||
| div { | |||||
| width: 33.33%; | |||||
| height: 100%; | |||||
| line-height: 32px; | |||||
| text-align: center; | |||||
| border: 1px solid #46464661; | |||||
| &.active { | |||||
| background-color: #00ccff; | |||||
| color: #fff; | |||||
| } | |||||
| } | |||||
| } | |||||
| .title { | |||||
| text-align: center; | |||||
| width: 70px; | |||||
| font-size: 16px; | |||||
| font-weight: 600; | |||||
| border-bottom: 3px solid #0068b7; | |||||
| padding-bottom: 7px; | |||||
| margin: 10px auto; | |||||
| margin-bottom: 25px; | |||||
| } | |||||
| .typeOutSide { | |||||
| position: sticky; | |||||
| top: 60px; | |||||
| z-index: 99; | |||||
| .type { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| padding: 0 25px; | |||||
| margin-bottom: 20px; | |||||
| div { | |||||
| width: 94px; | |||||
| height: 35px; | |||||
| line-height: 35px; | |||||
| color: #fff; | |||||
| border: 1px solid #bfbfbf; | |||||
| background-color: #bfbfbf; | |||||
| border-radius: 5px; | |||||
| text-align: center; | |||||
| transition: all 0.3s; | |||||
| &.active { | |||||
| color: #fff; | |||||
| border: 1px solid #0068b7; | |||||
| background-color: #0068b7; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| .choose { | |||||
| margin-bottom: 40px; | |||||
| .modeList { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| flex-wrap: wrap; | |||||
| margin: auto; | |||||
| padding: 0 10px; | |||||
| .model { | |||||
| position: relative; | |||||
| text-align: left; | |||||
| margin-bottom: 25px; | |||||
| border: 1px solid #00000028; | |||||
| padding: 8px; | |||||
| border-radius: 8px; | |||||
| transition: all 0.3s; | |||||
| opacity: 0.9; | |||||
| img { | |||||
| width: 150px; | |||||
| height: 220px; | |||||
| } | |||||
| .modelName { | |||||
| margin-top: 4px; | |||||
| font-size: 13px; | |||||
| } | |||||
| .setPaper { | |||||
| position: absolute; | |||||
| top: 80%; | |||||
| left: 50%; | |||||
| transform: translate(-50%, -50%); | |||||
| color: #fff; | |||||
| font-size: 14px; | |||||
| font-weight: 600; | |||||
| background-color: #00000042; | |||||
| padding: 3px 5px; | |||||
| border-radius: 5px; | |||||
| transition: all 0.3s; | |||||
| opacity: 0; | |||||
| } | |||||
| &.active { | |||||
| transform: translateY(-3px); | |||||
| box-shadow: #60606082 0px 2px 2px 1px; | |||||
| opacity: 1; | |||||
| .setPaper { | |||||
| opacity: 1; | |||||
| } | |||||
| } | |||||
| } | |||||
| .alt { | |||||
| font-size: 18px; | |||||
| margin: 80px auto; | |||||
| height: 450px; | |||||
| line-height: 450px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .blank { | |||||
| width: 1px; | |||||
| height: 1px; | |||||
| margin-bottom: 240px; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| @@ -2,14 +2,15 @@ | |||||
| <div :class="inPage ? 'page active' : 'page'"> | <div :class="inPage ? 'page active' : 'page'"> | ||||
| <div id="top"></div> | <div id="top"></div> | ||||
| <HeadTop /> | <HeadTop /> | ||||
| <!-- <div class="chooseModel"> | |||||
| <div :class="phase == 1 ? 'active' : ''">选择模板</div> | |||||
| <div :class="phase == 2 ? 'active' : ''">录入文案</div> | |||||
| <div :class="phase == 3 ? 'active' : ''">合成视频</div> | |||||
| </div> --> | |||||
| <!-- 顶部tab栏 --> | |||||
| <van-tabs @click="chooseType1" class="typeOutSide" animated color="#7264F5" line-width=46 line-height=6 title-inactive-color="#666" title-active-color="#333"> | |||||
| <van-tab v-for="item in tabList" :key="item" :title="item"> | |||||
| </van-tab> | |||||
| </van-tabs> | |||||
| <div class="title">选择模板</div> | |||||
| <div class="typeOutSide"> | |||||
| <!-- <div class="title">选择模板</div> --> | |||||
| <!-- 老版吸顶tab栏 --> | |||||
| <!-- <div class="typeOutSide"> | |||||
| <div class="type"> | <div class="type"> | ||||
| <div :class="type == 0 ? 'active' : ''" @click="chooseType(0)"> | <div :class="type == 0 ? 'active' : ''" @click="chooseType(0)"> | ||||
| 全部模板 | 全部模板 | ||||
| @@ -21,8 +22,8 @@ | |||||
| 男性模板 | 男性模板 | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | |||||
| </div> --> | |||||
| <!-- 模板列表 --> | |||||
| <div class="choose"> | <div class="choose"> | ||||
| <div class="modeList"> | <div class="modeList"> | ||||
| <div | <div | ||||
| @@ -67,6 +68,7 @@ export default { | |||||
| type: 0, | type: 0, | ||||
| currentId: "", | currentId: "", | ||||
| modeList: [], // 模板列表 | modeList: [], // 模板列表 | ||||
| tabList: ['全部','男性','女性','原P','武装直升机','粥P','二刺猿'] | |||||
| }; | }; | ||||
| }, | }, | ||||
| computed: { | computed: { | ||||
| @@ -77,7 +79,11 @@ export default { | |||||
| methods: { | methods: { | ||||
| ...mapActions(["setCharacters"]), | ...mapActions(["setCharacters"]), | ||||
| //点击tab栏选择模板列表 | |||||
| chooseType1(name, title) { | |||||
| this.type = this.tabList.findIndex(item => item==title) | |||||
| this.loadModeList(this.type); | |||||
| }, | |||||
| globalWatcher(e) { | globalWatcher(e) { | ||||
| console.log(e, "e"); | console.log(e, "e"); | ||||
| }, | }, | ||||
| @@ -90,10 +96,10 @@ export default { | |||||
| this.phase = num; | this.phase = num; | ||||
| }, | }, | ||||
| chooseType(num) { | |||||
| this.type = num; | |||||
| this.loadModeList(num); | |||||
| }, | |||||
| // chooseType(num) { | |||||
| // this.type = num; | |||||
| // this.loadModeList(num); | |||||
| // }, | |||||
| selectModel(id) { | selectModel(id) { | ||||
| if (this.currentId != id) { | if (this.currentId != id) { | ||||
| @@ -115,13 +121,20 @@ export default { | |||||
| Toast.fail(error.message); | Toast.fail(error.message); | ||||
| } | } | ||||
| }, | }, | ||||
| // 根据id获取模板详情 | |||||
| async getModeDetail(id) { | async getModeDetail(id) { | ||||
| try { | try { | ||||
| const res = await getModeDetailById(id); | const res = await getModeDetailById(id); | ||||
| console.log(res, "根据id获取模板详情"); | console.log(res, "根据id获取模板详情"); | ||||
| const modelDetail = res.data; | const modelDetail = res.data; | ||||
| this.goSetPaperWork(modelDetail); | |||||
| this.$router.push({ | |||||
| path: '/previewModel', | |||||
| query: { | |||||
| imgUrl: res.data.coverImg, | |||||
| modelId:res.data.id } | |||||
| }) | |||||
| // this.goSetPaperWork(modelDetail); | |||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error, "error"); | console.log(error, "error"); | ||||
| Toast.fail(error.message); | Toast.fail(error.message); | ||||
| @@ -132,13 +145,13 @@ export default { | |||||
| try { | try { | ||||
| const res = await saveOrUpdateUserVideo(data); | const res = await saveOrUpdateUserVideo(data); | ||||
| console.log(res, "保存或修改用户作品"); | console.log(res, "保存或修改用户作品"); | ||||
| this.$router.push({ | |||||
| path: "/getPaper", | |||||
| query: { | |||||
| modelDetail: JSON.stringify(modelDetail), | |||||
| saveID: res.data.id, | |||||
| }, | |||||
| }); | |||||
| // this.$router.push({ | |||||
| // path: "/getPaper", | |||||
| // query: { | |||||
| // modelDetail: JSON.stringify(modelDetail), | |||||
| // saveID: res.data.id, | |||||
| // }, | |||||
| // }); | |||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error, "error"); | console.log(error, "error"); | ||||
| Toast.fail(error.message); | Toast.fail(error.message); | ||||
| @@ -173,6 +186,10 @@ export default { | |||||
| </script> | </script> | ||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||
| ::v-deep .van-tab--active { | |||||
| font-size: 17px; | |||||
| font-weight: 700; | |||||
| } | |||||
| .page { | .page { | ||||
| transform: translateX(30%); | transform: translateX(30%); | ||||
| opacity: 0; | opacity: 0; | ||||
| @@ -215,9 +232,9 @@ export default { | |||||
| } | } | ||||
| .typeOutSide { | .typeOutSide { | ||||
| position: sticky; | position: sticky; | ||||
| top: 60px; | |||||
| top: 0px; | |||||
| z-index: 99; | z-index: 99; | ||||
| .type { | .type { | ||||
| display: flex; | display: flex; | ||||
| justify-content: space-between; | justify-content: space-between; | ||||
| @@ -245,6 +262,7 @@ export default { | |||||
| } | } | ||||
| .choose { | .choose { | ||||
| padding-top: 20px; | |||||
| margin-bottom: 40px; | margin-bottom: 40px; | ||||
| .modeList { | .modeList { | ||||
| @@ -253,24 +271,27 @@ export default { | |||||
| align-items: center; | align-items: center; | ||||
| flex-wrap: wrap; | flex-wrap: wrap; | ||||
| margin: auto; | margin: auto; | ||||
| padding: 0 10px; | |||||
| padding: 0 23px; | |||||
| .model { | .model { | ||||
| position: relative; | position: relative; | ||||
| text-align: left; | text-align: left; | ||||
| margin-bottom: 25px; | |||||
| border: 1px solid #00000028; | |||||
| padding: 8px; | |||||
| border-radius: 8px; | |||||
| margin-bottom: 15px; | |||||
| // border: 1px solid #00000028; | |||||
| // padding: 8px; | |||||
| width: 154px; | |||||
| height: 305px; | |||||
| transition: all 0.3s; | transition: all 0.3s; | ||||
| opacity: 0.9; | opacity: 0.9; | ||||
| img { | img { | ||||
| width: 150px; | |||||
| height: 220px; | |||||
| border-radius: 8px; | |||||
| width: 100%; | |||||
| height: 273px; | |||||
| } | } | ||||
| .modelName { | .modelName { | ||||
| margin-top: 4px; | |||||
| font-size: 13px; | |||||
| margin-top: 10px; | |||||
| font-weight: 700; | |||||
| font-size: 16px; | |||||
| } | } | ||||
| .setPaper { | .setPaper { | ||||
| position: absolute; | position: absolute; | ||||
| @@ -0,0 +1,649 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <HeadTop /> | |||||
| <!-- 中间内容区 --> | |||||
| <div class="content"> | |||||
| <!-- 展示图片 --> | |||||
| <div v-if="imgUrl" class="shouModelImg"> | |||||
| <img :src="imgUrl" alt=""> | |||||
| <!-- 字幕 --> | |||||
| <div v-html="textareaContent" v-show="showSubtitle" class="shouModelImg-subtitle"></div> | |||||
| </div> | |||||
| <!-- 文字 --> | |||||
| <div class="addText"> | |||||
| <div class="addText-title">添加文案</div> | |||||
| <div class="desalinate"> | |||||
| <span>已录入{{textareaContent.length}}字</span> | |||||
| <span>视频时长: 00:16</span> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 标题框 --> | |||||
| <div class="textInput"> | |||||
| <div class="textInput-in"> | |||||
| <!-- 标题 --> | |||||
| <el-input v-model="inputTitle" placeholder="请输入标题"> | |||||
| <template slot="append"> | |||||
| <img src="../../assets/icon/icon-pencil.png" alt=""> | |||||
| </template> | |||||
| </el-input> | |||||
| <div class="divider" /> | |||||
| <!-- 语音内容 --> | |||||
| <el-input rows="4" type="textarea" v-model="textareaContent"></el-input> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 播报声音 --> | |||||
| <div class="addText"> | |||||
| <div class="addText-title">播报声音<span class="redWord">(必选)</span></div> | |||||
| <div> | |||||
| 英语_女性_安娜 | |||||
| </div> | |||||
| </div> | |||||
| <!-- 合成按钮 --> | |||||
| <div class="makeVideo" @click="showSynthesisDialog=true">立即制作</div> | |||||
| <!-- 上移下移 --> | |||||
| <div class="upAndBottomBtn"> | |||||
| <div class="moveLayer"> | |||||
| <div class="btnContent"> | |||||
| <img src="https://lanhu.oss-cn-beijing.aliyuncs.com/SketchPngf9bff6080bf29c6886ad614d479aebfb5fc41429c11c0bea78ca695a0f0e7388" alt=""> | |||||
| 上移一层 | |||||
| </div> | |||||
| </div> | |||||
| <div class="moveLayer below"> | |||||
| <div class="btnContent active"> | |||||
| <img src="https://lanhu.oss-cn-beijing.aliyuncs.com/SketchPngde1a7c84fc47b26095186ff9ee7ec07d7fe7bcd2cde90f3178f4edb8cdb08bcd" alt=""> | |||||
| 下移一层 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 底部导航栏 --> | |||||
| <div class="bottom"> | |||||
| <van-tabbar class="bottom-tab"> | |||||
| <van-tabbar-item @click="clickBottomTab(1)"> | |||||
| <span>更改声音</span> | |||||
| <template #icon="props"> | |||||
| <img src="../../assets/icon/icon-microphone.png" /> | |||||
| </template> | |||||
| </van-tabbar-item> | |||||
| <van-tabbar-item @click="clickBottomTab(2)"> | |||||
| <span>更改背景</span> | |||||
| <template #icon="props"> | |||||
| <img src="../../assets/icon/icon-background.png" /> | |||||
| </template> | |||||
| </van-tabbar-item> | |||||
| <van-tabbar-item @click="clickBottomTab(3)"> | |||||
| <span>添加素材</span> | |||||
| <template #icon="props"> | |||||
| <img src="../../assets/icon/icon-material.png" /> | |||||
| </template> | |||||
| </van-tabbar-item> | |||||
| <van-tabbar-item @click="clickBottomTab(4)"> | |||||
| <span v-show="showSubtitle">取消字幕</span> | |||||
| <span v-show="!showSubtitle">添加字幕</span> | |||||
| <template #icon="props"> | |||||
| <img src="../../assets/icon/icon-subtitle.png" /> | |||||
| </template> | |||||
| </van-tabbar-item> | |||||
| </van-tabbar> | |||||
| </div> | |||||
| <!-- 合成视频进度条 --> | |||||
| <div v-if="showSynthesisDialog" class="synthesisDialog"> | |||||
| <div class="synthesisDialog-in"> | |||||
| <el-progress color="#7264F5" define-back-color="#f0effe" stroke-width="12" class="progress" :percentage="26"></el-progress> | |||||
| <div class="synthesisDialog-text">正在生成视频,请耐心等待!</div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 点击tab栏弹出框 --> | |||||
| <van-action-sheet class="tabSheet" v-model="showTabSheet" > | |||||
| <!-- 声音 --> | |||||
| <div v-show="bottomTabActive==1"> | |||||
| <!-- 顶部文字 --> | |||||
| <div class="tabSheet-title"> | |||||
| <div><img class="img" src="../../assets/icon/icon-microphone.png" alt=""> 更改声音</div> | |||||
| <div><img class="yesImg" src="../../assets/icon/icon-yes.png" alt=""></div> | |||||
| </div> | |||||
| <!-- 主要内容 --> | |||||
| <div class="tabSheet-content"> | |||||
| <!-- tab栏 --> | |||||
| <van-tabs color="#7264F5" line-width=46 line-height=6 title-inactive-color="#666" title-active-color="#333" v-model="soundListAction" swipeable> | |||||
| <van-tab v-for="(item,index) in soundTabList" :title="item" :key="index"> | |||||
| <!-- 展示页 --> | |||||
| <div class="list"> | |||||
| <div @click="soundItemListAction=index" class="listItem" :class="{ 'active': soundItemListAction ===index }" v-for="(item,index) in soundItemList" :key="index"> | |||||
| <img src="../../assets/icon/icon-earphone.png" alt=""> | |||||
| <div >{{ item }}</div> | |||||
| </div> | |||||
| </div> | |||||
| </van-tab> | |||||
| </van-tabs> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 背景 --> | |||||
| <div v-show="bottomTabActive==2"> | |||||
| <!-- 顶部文字 --> | |||||
| <div class="tabSheet-title"> | |||||
| <div><img class="img" src="../../assets/icon/icon-background.png" alt=""> 更换背景</div> | |||||
| <div><img class="yesImg" src="../../assets/icon/icon-yes.png" alt=""></div> | |||||
| </div> | |||||
| <!-- 主要内容 --> | |||||
| <div class="tabSheet-content"> | |||||
| <!-- tab栏 --> | |||||
| <div class="myTab"> | |||||
| <div @click="bjAction=0" class="myTab-item" :class="{'active':bjAction==0}"><img src="../../assets/icon/icon-folder@2x.png" alt=""> 背景库</div> | |||||
| <div @click="bjAction=1" class="myTab-item" :class="{'active':bjAction==1}"><img src="../../assets/icon/icon-upload@2x.png" alt="">我上传的</div> | |||||
| </div> | |||||
| <!-- 展示 --> | |||||
| <div class="myTab-show"> | |||||
| <div class="myTab-show-title">图片<i></i></div> | |||||
| <div class="myTab-show-list"> | |||||
| <!-- 上传 --> | |||||
| <div v-if="bjAction==1" class="myTab-show-list-upload"> | |||||
| <img src="../../assets/icon/icon-upload@2x.png" alt=""> | |||||
| <input type="file"> | |||||
| </div> | |||||
| <!-- 图片列表 --> | |||||
| <div @click="bjImgAction=index" class="myTab-show-list-item" v-for="(item,index) in 7" :key="index" > | |||||
| <div :class=" {'myTab-show-list-active':bjImgAction==index}"> | |||||
| <div class="whiteBox"> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 删除上传素材 --> | |||||
| <div class="icon-error" v-if="bjAction==1&&bjImgAction==index"> | |||||
| × | |||||
| </div> | |||||
| <!-- 中间图片 --> | |||||
| <img src="" alt=""> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 素材 --> | |||||
| <div v-show="bottomTabActive==3"> | |||||
| <!-- 顶部文字 --> | |||||
| <div class="tabSheet-title"> | |||||
| <div><img class="img" src="../../assets/icon/icon-material.png" alt=""> 更换素材</div> | |||||
| <div><img class="yesImg" src="../../assets/icon/icon-yes.png" alt=""></div> | |||||
| </div> | |||||
| <!-- 主要内容 --> | |||||
| <div class="tabSheet-content"> | |||||
| <!-- tab栏 --> | |||||
| <div class="myTab"> | |||||
| <div @click="scAction=0" class="myTab-item" :class="{'active':scAction==0}"><img src="../../assets/icon/icon-folder@2x.png" alt=""> 背景库</div> | |||||
| <div @click="scAction=1" class="myTab-item" :class="{'active':scAction==1}"><img src="../../assets/icon/icon-upload@2x.png" alt="">我上传的</div> | |||||
| </div> | |||||
| <!-- 展示 --> | |||||
| <div class="myTab-show"> | |||||
| <div class="myTab-show-title">图片<i></i></div> | |||||
| <div class="myTab-show-list"> | |||||
| <!-- 上传 --> | |||||
| <div v-if="scAction==1" class="myTab-show-list-upload"> | |||||
| <img src="../../assets/icon/icon-upload@2x.png" alt=""> | |||||
| <input type="file"> | |||||
| </div> | |||||
| <!-- 图片列表 --> | |||||
| <div @click="scImgAction=index" class="myTab-show-list-item" v-for="(item,index) in 7" :key="index" > | |||||
| <div :class=" {'myTab-show-list-active':scImgAction==index}"> | |||||
| <div class="whiteBox"> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 删除上传素材 --> | |||||
| <div class="icon-error" v-if="scAction==1&&scImgAction==index"> | |||||
| × | |||||
| </div> | |||||
| <!-- 中间图片 --> | |||||
| <img src="" alt=""> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </van-action-sheet> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| // 组件 | |||||
| import Vue from "vue"; | |||||
| import { Toast } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| import HeadTop from "./../../components/common/head.vue"; | |||||
| // 工具 | |||||
| import { scrollToID } from "../../utils"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| components: { | |||||
| HeadTop, | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| inPage: false, | |||||
| imgUrl:null, | |||||
| modelId: null, | |||||
| inputTitle: '', | |||||
| textareaContent: '', // 输入框内容 | |||||
| bottomTabActive: 2,// 底部导航高亮 | |||||
| showSubtitle: true, // 是否展示字幕 | |||||
| showSynthesisDialog: false,//合成进度条窗口显示隐藏 | |||||
| showTabSheet: false, //tab栏弹出框显隐 | |||||
| soundTabList: ['男生', '女生', '御姐', '萝莉'], // 声音tab栏标题 | |||||
| soundListAction:-1,//声音tab栏标题高亮 | |||||
| soundItemList: ['普通话', '英语', '日语', '日语', '日语', '日语', '日语'],// 声音展示列表 | |||||
| soundItemListAction:-1,// 声音展示列表高亮 | |||||
| bjAction:-1,// 背景:素材库和上传高亮 | |||||
| bjImgAction:-1,// 选中背景图高亮 | |||||
| scAction:-1,// 素材:素材库和上传高亮 | |||||
| scImgAction:-1,// 选中素材图高亮 | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| // 点击底部tab栏 | |||||
| clickBottomTab(index) { | |||||
| if (index != 4) { | |||||
| this.bottomTabActive = index | |||||
| console.log(this.bottomTabActive); | |||||
| this.showTabSheet=!this.showTabSheet | |||||
| } else { | |||||
| //字幕切换 | |||||
| this.showSubtitle=!this.showSubtitle | |||||
| } | |||||
| }, | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| }, | |||||
| created() { | |||||
| this.imgUrl=this.$route.query.imgUrl | |||||
| this.modelId=this.$route.query.modelId | |||||
| }, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| // transform会导致其子元素固定定位失效 | |||||
| // transform: translateX(30%); | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| &.active { | |||||
| opacity: 1; | |||||
| // transform: translateX(0); | |||||
| } | |||||
| } | |||||
| .content { | |||||
| position: relative; | |||||
| padding: 0 10px; | |||||
| padding-bottom: 50px; | |||||
| .shouModelImg { | |||||
| display: flex; | |||||
| position: relative; | |||||
| justify-content: center ; | |||||
| width: 100%; | |||||
| img { | |||||
| width: 200px; | |||||
| height: 351px; | |||||
| border-radius: 12px; | |||||
| }; | |||||
| &-subtitle { | |||||
| position: absolute; | |||||
| top: 220px; | |||||
| left: 50%; | |||||
| transform: translateX(-50%); | |||||
| padding: 2px 6px; | |||||
| width: 160px; | |||||
| height: 24px; | |||||
| line-height: 20px; | |||||
| background: rgba(0,0,0,0.5); | |||||
| font-size: 14px; | |||||
| color: #fff; | |||||
| border-radius: 3px; | |||||
| } | |||||
| } | |||||
| .addText { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-top: 20px; | |||||
| &-title { | |||||
| font-size: 16px; | |||||
| font-weight: 600; | |||||
| color: #000; | |||||
| } | |||||
| } | |||||
| .textInput { | |||||
| margin-top: 10px; | |||||
| padding: 1px; | |||||
| width: 100%; | |||||
| background: linear-gradient(225deg, rgba(81, 217, 202, 1), rgba(114, 100, 245, 1)); | |||||
| border-radius: 6px; | |||||
| &-in { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| background: #fff; | |||||
| border-radius: 6px; | |||||
| ::v-deep .el-input-group__append { | |||||
| padding: 0; | |||||
| } | |||||
| img { | |||||
| float: right; | |||||
| width: 16px; | |||||
| height: 16px; | |||||
| margin: 10px; | |||||
| } | |||||
| } | |||||
| .divider { | |||||
| transform: scaleY (0.25); | |||||
| width: 100%; | |||||
| height: 1px; | |||||
| border-top: 1px solid; | |||||
| border-image: linear-gradient(225deg, rgba(81, 217, 202, 1), rgba(114, 100, 245, 1)) 1 1; | |||||
| } | |||||
| ::v-deep .el-input__inner { | |||||
| border: none; | |||||
| background-color: #faf9fe; | |||||
| } | |||||
| ::v-deep .el-textarea__inner { | |||||
| border: none; | |||||
| background-color: #faf9fe; | |||||
| } | |||||
| } | |||||
| .makeVideo { | |||||
| margin: 15px 20px 0; | |||||
| height: 50px; | |||||
| text-align: center; | |||||
| font-size: 20px; | |||||
| line-height: 50px; | |||||
| color: #fff; | |||||
| background: linear-gradient(316deg, #6EE4D7 0%, #37C4F1 32%, #A49AFC 100%); | |||||
| border-radius: 25px; | |||||
| } | |||||
| .upAndBottomBtn { | |||||
| .moveLayer { | |||||
| position: absolute; | |||||
| display: flex; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| top: 20%; | |||||
| left: 290px; | |||||
| padding-left: 10px; | |||||
| width: 80px; | |||||
| height: 22px; | |||||
| font-size: 10px; | |||||
| line-height: 28px; | |||||
| text-align: center; | |||||
| box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.1); | |||||
| background-color: rgba(255, 255, 255, 1); | |||||
| border-radius: 11px; | |||||
| } | |||||
| img { | |||||
| vertical-align: middle; | |||||
| width: 10px; | |||||
| height: 11px; | |||||
| margin: 1px 0 2px 0; | |||||
| } | |||||
| .below { | |||||
| top: 25%; | |||||
| } | |||||
| .active { | |||||
| opacity: 0.5; | |||||
| } | |||||
| } | |||||
| } | |||||
| .bottom { | |||||
| ::v-deep .van-tabbar-item { | |||||
| color: rgba(51, 51, 51, 1)!important; | |||||
| font-size: 10px !important; | |||||
| } | |||||
| } | |||||
| .synthesisDialog { | |||||
| position: fixed; | |||||
| top: 0; | |||||
| left: 0; | |||||
| width: 100vw; | |||||
| height: 110vh; | |||||
| z-index: 99999; | |||||
| color:#333; | |||||
| background: rgba(0,0,0,0.5); | |||||
| &-in { | |||||
| position: absolute; | |||||
| top: 40%; | |||||
| left: 50%; | |||||
| transform: translate(-50%); | |||||
| padding: 0 15px; | |||||
| padding-top: 40px; | |||||
| width: 315px; | |||||
| height: 95px; | |||||
| background-color: #fff; | |||||
| border-radius: 12px; | |||||
| .progress { | |||||
| width: 300px; | |||||
| ::v-deep .el-progress__text { | |||||
| margin-left: 15px; | |||||
| } | |||||
| } | |||||
| } | |||||
| &-text { | |||||
| margin-top: 10px; | |||||
| font-size: 12px; | |||||
| } | |||||
| } | |||||
| .tabSheet { | |||||
| height: 280px; | |||||
| padding: 10px; | |||||
| padding-bottom: 0px; | |||||
| background-color: #f9f9f9; | |||||
| &-title { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| height: 40px; | |||||
| font-size: 13px; | |||||
| line-height: 40px; | |||||
| background-color: #fff; | |||||
| .img { | |||||
| margin-top: -3px; | |||||
| margin-right: 3px; | |||||
| width: 16px; | |||||
| height: 16px; | |||||
| } | |||||
| .yesImg { | |||||
| margin-top: -13px; | |||||
| width: 24px; | |||||
| height: 16px; | |||||
| } | |||||
| } | |||||
| &-content { | |||||
| height: 220px; | |||||
| background-color: #f9f9f9; | |||||
| ::v-deep .van-tab{ | |||||
| background-color: #f9f9f9; | |||||
| } | |||||
| ::v-deep .van-tab--active { | |||||
| font-size: 17px; | |||||
| font-weight: 700; | |||||
| } | |||||
| .list { | |||||
| height: 170px; | |||||
| overflow-y: scroll; | |||||
| // scrollbar-width: none; | |||||
| // -ms-overflow-style: none; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| flex-wrap: wrap; | |||||
| margin-top: 10px; | |||||
| .listItem { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| padding: 12px; | |||||
| margin-bottom: 10px; | |||||
| width: 170px; | |||||
| height: 50px; | |||||
| overflow: hidden; | |||||
| background: rgba(186,186,189,0.5); | |||||
| border-radius: 24px; | |||||
| color: #fff; | |||||
| img { | |||||
| width: 32px; | |||||
| } | |||||
| >div { | |||||
| flex: 1; | |||||
| margin-left: 12px; | |||||
| } | |||||
| } | |||||
| .active { | |||||
| background: #7264F5; | |||||
| } | |||||
| } | |||||
| .list::-webkit-scrollbar { | |||||
| width:0; | |||||
| } | |||||
| } | |||||
| .myTab { | |||||
| display: flex; | |||||
| padding: 5px; | |||||
| width: 100%; | |||||
| height: 40px; | |||||
| background-color: #EEEEEE; | |||||
| border-radius: 6px; | |||||
| &-item { | |||||
| width: 50%; | |||||
| height: 30px; | |||||
| font-size: 14px; | |||||
| line-height: 30px; | |||||
| text-align: center; | |||||
| background-color: #EEEEEE; | |||||
| border-radius: 6px; | |||||
| img { | |||||
| width: 20px; | |||||
| height: 16px; | |||||
| margin-right: 8px; | |||||
| } | |||||
| } | |||||
| .active { | |||||
| background-color: #fff; | |||||
| } | |||||
| &-show { | |||||
| width: 100%; | |||||
| height: 190px; | |||||
| &-title { | |||||
| font-size: 16px; | |||||
| i { | |||||
| display: block; | |||||
| margin-top: 4px; | |||||
| margin-left: 3px; | |||||
| width: 23px; | |||||
| height: 3px; | |||||
| background: #7264F5; | |||||
| border-radius: 2px; | |||||
| } | |||||
| } | |||||
| &-list { | |||||
| height: 145px; | |||||
| padding-left: 4px; | |||||
| overflow-y: scroll; | |||||
| display: flex; | |||||
| justify-content: start; | |||||
| flex-wrap: wrap; | |||||
| &-list::-webkit-scrollbar { | |||||
| width:0; | |||||
| } | |||||
| &-item { | |||||
| position: relative; | |||||
| margin-right: 9px; | |||||
| margin-top: 10px; | |||||
| width: 80px; | |||||
| height: 120px; | |||||
| z-index: 9; | |||||
| img { | |||||
| background: red; | |||||
| width: 80px; | |||||
| height: 120px; | |||||
| border-radius: 12px; | |||||
| } | |||||
| border-radius: 10px; | |||||
| .icon-error { | |||||
| float: left; | |||||
| position: absolute; | |||||
| top: 5px; | |||||
| right: 5px; | |||||
| z-index: 99999; | |||||
| display: block!important; | |||||
| float: right; | |||||
| width: 12px; | |||||
| height: 12px; | |||||
| line-height: 12px; | |||||
| text-align: center; | |||||
| color: #fff; | |||||
| background: #666666; | |||||
| border-radius: 6px; | |||||
| } | |||||
| &:nth-child(4n) { | |||||
| margin-right: 0px!important; | |||||
| } | |||||
| } | |||||
| &-active { | |||||
| position: absolute; | |||||
| top: -3px; | |||||
| left: -3px; | |||||
| padding: 1px; | |||||
| z-index: -1; | |||||
| width: 86px; | |||||
| height: 126px; | |||||
| border-radius: 12px; | |||||
| background: linear-gradient(225deg, rgba(81, 217, 202, 1), rgba(114, 100, 245, 1)); | |||||
| .whiteBox { | |||||
| transition: all 0.5s; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| background-color: #fff; | |||||
| border-radius: 12px; | |||||
| } | |||||
| } | |||||
| &-upload { | |||||
| position: relative; | |||||
| margin-right: 10px; | |||||
| margin-top: 10px; | |||||
| width: 80px; | |||||
| height: 120px; | |||||
| background: #ddd; | |||||
| border-radius: 10px; | |||||
| img { | |||||
| background-color: #fff; | |||||
| position: absolute; | |||||
| top: 50%; | |||||
| left: 50%; | |||||
| transform: translate(-50%,-50%); | |||||
| } | |||||
| input { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| opacity: 0; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| </style> | |||||
| @@ -0,0 +1,98 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <HeadTop /> | |||||
| <!-- 展示图片 --> | |||||
| <div v-if="imgUrl" class="shouModelImg"> | |||||
| <img :src="imgUrl" alt=""> | |||||
| </div> | |||||
| <div class="makeMpdel" @click="handleMakeMpdel">立即制作</div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| // 组件 | |||||
| import Vue from "vue"; | |||||
| import { Toast } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| // 工具 | |||||
| import { scrollToID } from "../../utils"; | |||||
| import HeadTop from "./../../components/common/head.vue"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| components: { | |||||
| HeadTop, | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| inPage: false, | |||||
| imgUrl: null, | |||||
| modelId:null | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| // 点击立即制作 | |||||
| handleMakeMpdel() { | |||||
| this.$router.push({ | |||||
| path: '/editModel', | |||||
| query: { | |||||
| imgUrl: this.imgUrl, | |||||
| modelId: this.modelId | |||||
| } | |||||
| }) | |||||
| }, | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| }, | |||||
| created() { | |||||
| this.imgUrl=this.$route.query.imgUrl | |||||
| this.modelId=this.$route.query.modelId | |||||
| }, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| transform: translateX(30%); | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| &.active { | |||||
| opacity: 1; | |||||
| transform: translateX(0); | |||||
| } | |||||
| } | |||||
| .shouModelImg { | |||||
| padding: 20px 30px; | |||||
| img { | |||||
| width: 315px; | |||||
| height: 518px; | |||||
| border-radius: 10px; | |||||
| } | |||||
| } | |||||
| .makeMpdel { | |||||
| margin-left: 30px; | |||||
| width: 315px; | |||||
| height: 50px; | |||||
| text-align: center; | |||||
| font-size: 20px; | |||||
| line-height: 50px; | |||||
| color: #fff; | |||||
| background: linear-gradient(316deg, #6EE4D7 0%, #37C4F1 32%, #A49AFC 100%); | |||||
| border-radius: 25px; | |||||
| } | |||||
| </style> | |||||
| @@ -0,0 +1,489 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <HeadTop /> | |||||
| <!-- 顶部图 --> | |||||
| <img class="showIMG" src="../../assets/img/BG-Page.png" alt="" /> | |||||
| <!-- 展示列表 --> | |||||
| <div class="item"> | |||||
| <div class="title"> | |||||
| <img src="../../assets/img/myVideo.png" alt=""> | |||||
| 我的视频作品 | |||||
| <span class="floatRight" style="float: right;">草稿箱</span> | |||||
| </div> | |||||
| <!-- 具体内容 --> | |||||
| <MyVideoItem :videoList="videoList"/> | |||||
| </div> | |||||
| <!-- 分页 --> | |||||
| <van-pagination | |||||
| v-model="currentPage" | |||||
| :total-items="total" | |||||
| :items-per-page="4" | |||||
| @change="pageChange" | |||||
| /> | |||||
| <div class="goAdd" @click="checkInviteCode">+ 创作新作品</div> | |||||
| <div | |||||
| class="messageCover" | |||||
| :class="isShowMessageCover ? 'active' : ''" | |||||
| @click="exitCover" | |||||
| > | |||||
| <div class="message" id="message"> | |||||
| <div class="connect" id="message">请添加微信与我们联系</div> | |||||
| <div class="qrCode" id="message"> | |||||
| <img src="../../assets/img/SuiMangQRCode.png" alt="" /> | |||||
| </div> | |||||
| <div class="phoneNum" id="message">或致电13260039498</div> | |||||
| <div class="clickToClose">点击空白处关闭</div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| // 组件 | |||||
| import Vue from "vue"; | |||||
| import { Toast, Dialog } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| import HeadTop from "./../../components/common/head.vue"; | |||||
| import MyVideoItem from "./../../components/MyVideoItem.vue"; | |||||
| // 工具 | |||||
| import { | |||||
| bytesToSize, | |||||
| timestampToTime, | |||||
| scrollToID, | |||||
| formatSeconds, | |||||
| } from "../../utils"; | |||||
| // 接口 | |||||
| import { | |||||
| getVideoList, | |||||
| doGetVideoDetialById, | |||||
| doFindInviteCode, | |||||
| deleteVideoById, | |||||
| } from "../../api/myPage"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| components: { | |||||
| HeadTop, | |||||
| MyVideoItem | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| showDialog: true,// 弹出层显示隐藏 | |||||
| inPage: false, | |||||
| isShowMessageCover: false, | |||||
| videoList: [], | |||||
| currentPage: 1, | |||||
| pageSize: 4, | |||||
| prePage: "", | |||||
| total: "", | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| changeTime(timestamp, form) { | |||||
| return timestampToTime(timestamp, form); | |||||
| }, | |||||
| pageChange() { | |||||
| this.loadVideoList(this.currentPage, this.pageSize); | |||||
| }, | |||||
| // 点击空白区域退出提示 | |||||
| exitCover(e) { | |||||
| if (e.srcElement.id != "message") { | |||||
| this.isShowMessageCover = false; | |||||
| } | |||||
| }, | |||||
| // 秒转化时分秒 | |||||
| formatSeconds(seconds) { | |||||
| if (seconds) { | |||||
| return formatSeconds(seconds); | |||||
| } else { | |||||
| return "无"; | |||||
| } | |||||
| }, | |||||
| // 将bytes转化 | |||||
| bytesToSize(bytes) { | |||||
| if (bytes) { | |||||
| return bytesToSize(bytes); | |||||
| } else { | |||||
| return "无"; | |||||
| } | |||||
| }, | |||||
| // 查看详情 | |||||
| goDetail(item) { | |||||
| const status = item.videoStatus; | |||||
| if (status == 0) { | |||||
| this.$router.push({ | |||||
| path: "/getPaper", | |||||
| query: { | |||||
| id: item.id, | |||||
| }, | |||||
| }); | |||||
| } else if (status == 1) { | |||||
| Toast.fail("视频生成中"); | |||||
| return; | |||||
| } else if (status == 2) { | |||||
| const url = item.videoPathUri + item.videoPath; | |||||
| this.$router.push({ | |||||
| path: "/downloadVideo", | |||||
| query: { | |||||
| videoId: item.videoId, | |||||
| videoUrl: url, | |||||
| }, | |||||
| }); | |||||
| } else if (status == 3) { | |||||
| this.$router.push({ | |||||
| path: "/getPaper", | |||||
| query: { | |||||
| id: item.id, | |||||
| }, | |||||
| }); | |||||
| } | |||||
| }, | |||||
| goDelete(id) { | |||||
| Dialog.confirm({ | |||||
| title: "删除作品", | |||||
| message: "您确定要删除该作品吗?", | |||||
| }) | |||||
| .then(() => { | |||||
| this.deleteVideoById(id); | |||||
| this.loadVideoList(this.currentPage, this.pageSize); | |||||
| }) | |||||
| .catch(() => { | |||||
| return; | |||||
| }); | |||||
| }, | |||||
| /** | |||||
| * @description:获取作品列表 | |||||
| */ | |||||
| async loadVideoList(pageNum, pageSize) { | |||||
| try { | |||||
| const res = await getVideoList(pageNum, pageSize); | |||||
| console.log(res, "获取作品列表"); | |||||
| this.videoList = res.data.list; | |||||
| this.total = res.data.total; | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description 通过视频id获取视频详情 | |||||
| */ | |||||
| async getVideoDetialById(id) { | |||||
| try { | |||||
| const res = await doGetVideoDetialById(id); | |||||
| console.log(res, "通过视频id获取视频详情"); | |||||
| return res.data; | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description 根据id删除作品 | |||||
| */ | |||||
| async deleteVideoById(id) { | |||||
| try { | |||||
| const res = await deleteVideoById(id); | |||||
| console.log(res, "根据id删除作品"); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description:查询用户邀请码状态 | |||||
| */ | |||||
| async checkInviteCode() { | |||||
| try { | |||||
| const res = await doFindInviteCode(); | |||||
| console.log(res, "查询用户邀请码数据"); | |||||
| const status = res.data.status; | |||||
| // 当没有邀请码时 | |||||
| if (status == 0) { | |||||
| scrollToID("top"); | |||||
| this.isShowMessageCover = true; | |||||
| } else if (status == 1) { | |||||
| this.goCreateNew(); | |||||
| } | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| goCreateNew() { | |||||
| this.$router.push("/chooseModel"); | |||||
| }, | |||||
| }, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| created() { | |||||
| this.loadVideoList(1, this.pageSize); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| transform: translateX(30%); | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| padding-bottom: 25px; | |||||
| &.active { | |||||
| opacity: 1; | |||||
| transform: translateX(0); | |||||
| } | |||||
| .logoBox { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| overflow: hidden; | |||||
| cursor: pointer; | |||||
| background-color: #606060; | |||||
| .logo { | |||||
| float: left; | |||||
| width: 140px; | |||||
| height: 40px; | |||||
| } | |||||
| .userInfo { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| color: #fff; | |||||
| .userImg { | |||||
| float: right; | |||||
| width: 30px; | |||||
| height: 30px; | |||||
| margin-top: 3px; | |||||
| margin-right: 10px; | |||||
| margin-left: 15px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .showIMG { | |||||
| padding: 10px; | |||||
| width: 100%; | |||||
| height: 140px; | |||||
| border-radius: 16px; | |||||
| } | |||||
| .item { | |||||
| padding: 15px 10px; | |||||
| .title { | |||||
| font-size: 16px; | |||||
| font-weight: 500; | |||||
| // margin-left: 15px; | |||||
| margin-bottom: 20px; | |||||
| img { | |||||
| margin-top: -5px; | |||||
| vertical-align: middle; | |||||
| width: 24px; | |||||
| height: 18px; | |||||
| background-size: 100% 100%; | |||||
| } | |||||
| } | |||||
| ::v-deep .itemList { | |||||
| .videoModel { | |||||
| position: relative; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-bottom: 20px; | |||||
| .videoPrv { | |||||
| width: 150px; | |||||
| height: 100px; | |||||
| // background-color: #979797; | |||||
| img { | |||||
| position: absolute; | |||||
| width: 150px; | |||||
| height: 100px; | |||||
| border-radius: 6px; | |||||
| } | |||||
| .cover { | |||||
| position: absolute; | |||||
| width: 150px; | |||||
| height: 100px; | |||||
| line-height: 100px; | |||||
| background-color: #00000034; | |||||
| text-align: center; | |||||
| border-radius: 6px; | |||||
| color: #fff; | |||||
| } | |||||
| } | |||||
| .videoInfo { | |||||
| flex: 1; | |||||
| margin-left: 12px; | |||||
| .videoName { | |||||
| font-size: 16px; | |||||
| font-weight: 600; | |||||
| } | |||||
| .createTime { | |||||
| font-size: 11px; | |||||
| color: #999999; | |||||
| margin-top: 10px; | |||||
| } | |||||
| } | |||||
| .showMore { | |||||
| width: 20px; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| // margin-left: 10px; | |||||
| div { | |||||
| width: 4px; | |||||
| height: 4px; | |||||
| background-color: #51D9CA; | |||||
| } | |||||
| } | |||||
| .tag { | |||||
| position: absolute; | |||||
| right: 10px; | |||||
| top: 10px; | |||||
| padding: 1px 5px; | |||||
| border-radius: 5px; | |||||
| &.text { | |||||
| color: #fff; | |||||
| background-color: #a2a2a290; | |||||
| } | |||||
| &.generating { | |||||
| background-color: #ffa600c2; | |||||
| } | |||||
| &.finish { | |||||
| color: #ffffff; | |||||
| background-color: #00ff88; | |||||
| } | |||||
| &.fail { | |||||
| color: #ffffff; | |||||
| background-color: #ff0000c3; | |||||
| } | |||||
| } | |||||
| } | |||||
| .noWork { | |||||
| width: 100%; | |||||
| height: 450px; | |||||
| line-height: 450px; | |||||
| font-size: 14px; | |||||
| font-weight: 600; | |||||
| text-align: center; | |||||
| } | |||||
| } | |||||
| } | |||||
| .goAdd { | |||||
| position: sticky; | |||||
| bottom: 25px; | |||||
| left: 10px; | |||||
| width: 300px; | |||||
| height: 42px; | |||||
| line-height: 42px; | |||||
| font-size: 15px; | |||||
| color: #fff; | |||||
| text-align: center; | |||||
| transform: translateX(-50%); | |||||
| background-color: #0068b7; | |||||
| border-radius: 5px; | |||||
| box-shadow: #60606082 1px 1px 1px 1px; | |||||
| margin-top: 25px; | |||||
| margin-left: 190px; | |||||
| } | |||||
| .messageCover { | |||||
| position: fixed; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| background-color: #00000091; | |||||
| transition: all 0.3s; | |||||
| opacity: 0; | |||||
| z-index: -1; | |||||
| .message { | |||||
| position: sticky; | |||||
| top: 48%; | |||||
| transform: translate(-50%, -50%); | |||||
| width: 272px; | |||||
| height: 290px; | |||||
| border-radius: 5px; | |||||
| background-color: #fff; | |||||
| text-align: center; | |||||
| transition: all 0.3s; | |||||
| margin-left: 50%; | |||||
| padding: 10px; | |||||
| .connect { | |||||
| font-size: 12px; | |||||
| margin: 19px 0 10px 0; | |||||
| } | |||||
| .qrCode { | |||||
| width: 203px; | |||||
| height: 213px; | |||||
| background-color: #ffffff; | |||||
| border-radius: 5px; | |||||
| border: solid 1px #595959; | |||||
| margin: auto; | |||||
| img { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| } | |||||
| .phoneNum { | |||||
| float: left; | |||||
| font-size: 11px; | |||||
| margin-top: 8px; | |||||
| margin-left: 34px; | |||||
| } | |||||
| .close { | |||||
| position: absolute; | |||||
| top: 8px; | |||||
| right: 15px; | |||||
| font-size: 18px; | |||||
| color: #000; | |||||
| } | |||||
| .clickToClose { | |||||
| position: absolute; | |||||
| font-size: 14px; | |||||
| color: #fff; | |||||
| left: 50%; | |||||
| bottom: -20%; | |||||
| transform: translateX(-50%); | |||||
| } | |||||
| } | |||||
| &.active { | |||||
| z-index: 100; | |||||
| opacity: 1; | |||||
| } | |||||
| } | |||||
| .delete-button { | |||||
| position: relative; | |||||
| left: 1%; | |||||
| height: 100%; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| @@ -0,0 +1,520 @@ | |||||
| <template> | |||||
| <div :class="inPage ? 'page active' : 'page'"> | |||||
| <div id="top"></div> | |||||
| <HeadTop /> | |||||
| <!-- 顶部图 --> | |||||
| <img class="showIMG" src="../../assets/img/BG-Page.png" alt="" /> | |||||
| <!-- 展示列表 --> | |||||
| <div class="item"> | |||||
| <div class="title"> | |||||
| <img src="../../assets/img/myVideo.png" alt=""> | |||||
| 我的视频作品 | |||||
| </div> | |||||
| <van-swipe-cell v-for="(item, index) in videoList"> | |||||
| <div class="itemList"> | |||||
| <div | |||||
| v-if="videoList.length > 0" | |||||
| class="videoModel" | |||||
| :key="index" | |||||
| @click="goDetail(item)" | |||||
| > | |||||
| <div class="videoPrv"> | |||||
| <img src="../../assets/img/BG@2x.png" /> | |||||
| <div v-if="item.videoStatus == 0" class="cover">草稿</div> | |||||
| <div v-if="item.videoStatus == 1" class="cover">视频生成中</div> | |||||
| <div v-if="item.videoStatus == 3" class="cover">视频生成失败</div> | |||||
| <!-- <div v-if="item.videoStatus == ?" class="cover">排队中</div> --> | |||||
| </div> | |||||
| <div class="videoInfo"> | |||||
| <div class="videoName"> | |||||
| {{ item.title || "暂无" }} | |||||
| </div> | |||||
| <div class="createTime"> | |||||
| 视频时长:{{ formatSeconds(item.videoTime) }} | |||||
| </div> | |||||
| <div class="createTime"> | |||||
| 视频容量:{{ bytesToSize(item.videoSize) }} | |||||
| </div> | |||||
| <div class="createTime"> | |||||
| 创建时间:{{ | |||||
| changeTime(item.createDate, "YYYY-MM-DD hh:mm:ss") | |||||
| }} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div v-if="videoList.length <= 0" class="noWork"> | |||||
| 您还没有视频作品 | |||||
| </div> | |||||
| </div> | |||||
| <template #right> | |||||
| <van-button | |||||
| square | |||||
| text="删除" | |||||
| type="danger" | |||||
| class="delete-button" | |||||
| @click="goDelete(item.id)" | |||||
| /> | |||||
| </template> | |||||
| </van-swipe-cell> | |||||
| </div> | |||||
| <!-- 分页 --> | |||||
| <van-pagination | |||||
| v-model="currentPage" | |||||
| :total-items="total" | |||||
| :items-per-page="4" | |||||
| @change="pageChange" | |||||
| /> | |||||
| <div class="goAdd" @click="checkInviteCode">+ 创作新作品</div> | |||||
| <div | |||||
| class="messageCover" | |||||
| :class="isShowMessageCover ? 'active' : ''" | |||||
| @click="exitCover" | |||||
| > | |||||
| <div class="message" id="message"> | |||||
| <div class="connect" id="message">请添加微信与我们联系</div> | |||||
| <div class="qrCode" id="message"> | |||||
| <img src="../../assets/img/SuiMangQRCode.png" alt="" /> | |||||
| </div> | |||||
| <div class="phoneNum" id="message">或致电13260039498</div> | |||||
| <div class="clickToClose">点击空白处关闭</div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| // 组件 | |||||
| import Vue from "vue"; | |||||
| import { Toast, Dialog } from "vant"; | |||||
| import { mapState, mapActions } from "vuex"; | |||||
| import HeadTop from "./../../components/common/head.vue"; | |||||
| // 工具 | |||||
| import { | |||||
| bytesToSize, | |||||
| timestampToTime, | |||||
| scrollToID, | |||||
| formatSeconds, | |||||
| } from "../../utils"; | |||||
| // 接口 | |||||
| import { | |||||
| getVideoList, | |||||
| doGetVideoDetialById, | |||||
| doFindInviteCode, | |||||
| deleteVideoById, | |||||
| } from "../../api/myPage"; | |||||
| Vue.use(Toast); | |||||
| export default { | |||||
| components: { | |||||
| HeadTop, | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| inPage: false, | |||||
| isShowMessageCover: false, | |||||
| videoList: [], | |||||
| currentPage: 1, | |||||
| pageSize: 4, | |||||
| prePage: "", | |||||
| total: "", | |||||
| }; | |||||
| }, | |||||
| computed: { | |||||
| ...mapState({ | |||||
| characters: (state) => state.publicObj.characters, | |||||
| }), | |||||
| }, | |||||
| methods: { | |||||
| ...mapActions(["setCharacters"]), | |||||
| go(url) { | |||||
| this.$router.push(url); | |||||
| }, | |||||
| changeTime(timestamp, form) { | |||||
| return timestampToTime(timestamp, form); | |||||
| }, | |||||
| pageChange() { | |||||
| this.loadVideoList(this.currentPage, this.pageSize); | |||||
| }, | |||||
| // 点击空白区域退出提示 | |||||
| exitCover(e) { | |||||
| if (e.srcElement.id != "message") { | |||||
| this.isShowMessageCover = false; | |||||
| } | |||||
| }, | |||||
| // 秒转化时分秒 | |||||
| formatSeconds(seconds) { | |||||
| if (seconds) { | |||||
| return formatSeconds(seconds); | |||||
| } else { | |||||
| return "无"; | |||||
| } | |||||
| }, | |||||
| // 将bytes转化 | |||||
| bytesToSize(bytes) { | |||||
| if (bytes) { | |||||
| return bytesToSize(bytes); | |||||
| } else { | |||||
| return "无"; | |||||
| } | |||||
| }, | |||||
| // 查看详情 | |||||
| goDetail(item) { | |||||
| const status = item.videoStatus; | |||||
| if (status == 0) { | |||||
| this.$router.push({ | |||||
| path: "/getPaper", | |||||
| query: { | |||||
| id: item.id, | |||||
| }, | |||||
| }); | |||||
| } else if (status == 1) { | |||||
| Toast.fail("视频生成中"); | |||||
| return; | |||||
| } else if (status == 2) { | |||||
| const url = item.videoPathUri + item.videoPath; | |||||
| this.$router.push({ | |||||
| path: "/downloadVideo", | |||||
| query: { | |||||
| videoId: item.videoId, | |||||
| videoUrl: url, | |||||
| }, | |||||
| }); | |||||
| } else if (status == 3) { | |||||
| this.$router.push({ | |||||
| path: "/getPaper", | |||||
| query: { | |||||
| id: item.id, | |||||
| }, | |||||
| }); | |||||
| } | |||||
| }, | |||||
| goDelete(id) { | |||||
| Dialog.confirm({ | |||||
| title: "删除作品", | |||||
| message: "您确定要删除该作品吗?", | |||||
| }) | |||||
| .then(() => { | |||||
| this.deleteVideoById(id); | |||||
| this.loadVideoList(this.currentPage, this.pageSize); | |||||
| }) | |||||
| .catch(() => { | |||||
| return; | |||||
| }); | |||||
| }, | |||||
| /** | |||||
| * @description:获取作品列表 | |||||
| */ | |||||
| async loadVideoList(pageNum, pageSize) { | |||||
| try { | |||||
| const res = await getVideoList(pageNum, pageSize); | |||||
| console.log(res, "获取作品列表"); | |||||
| this.videoList = res.data.list; | |||||
| this.total = res.data.total; | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description 通过视频id获取视频详情 | |||||
| */ | |||||
| async getVideoDetialById(id) { | |||||
| try { | |||||
| const res = await doGetVideoDetialById(id); | |||||
| console.log(res, "通过视频id获取视频详情"); | |||||
| return res.data; | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description 根据id删除作品 | |||||
| */ | |||||
| async deleteVideoById(id) { | |||||
| try { | |||||
| const res = await deleteVideoById(id); | |||||
| console.log(res, "根据id删除作品"); | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| /** | |||||
| * @description:查询用户邀请码状态 | |||||
| */ | |||||
| async checkInviteCode() { | |||||
| try { | |||||
| const res = await doFindInviteCode(); | |||||
| console.log(res, "查询用户邀请码数据"); | |||||
| const status = res.data.status; | |||||
| // 当没有邀请码时 | |||||
| if (status == 0) { | |||||
| scrollToID("top"); | |||||
| this.isShowMessageCover = true; | |||||
| } else if (status == 1) { | |||||
| this.goCreateNew(); | |||||
| } | |||||
| } catch (error) { | |||||
| console.log(error, "error"); | |||||
| Toast.fail(error.message); | |||||
| } | |||||
| }, | |||||
| goCreateNew() { | |||||
| this.$router.push("/chooseModel"); | |||||
| }, | |||||
| }, | |||||
| mounted() { | |||||
| this.inPage = true; | |||||
| scrollToID("top"); | |||||
| }, | |||||
| created() { | |||||
| this.loadVideoList(1, this.pageSize); | |||||
| }, | |||||
| }; | |||||
| </script> | |||||
| <style lang="scss" scoped> | |||||
| .page { | |||||
| transform: translateX(30%); | |||||
| opacity: 0; | |||||
| transition: all 0.5s; // global-transition | |||||
| padding-bottom: 25px; | |||||
| &.active { | |||||
| opacity: 1; | |||||
| transform: translateX(0); | |||||
| } | |||||
| .logoBox { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| overflow: hidden; | |||||
| cursor: pointer; | |||||
| background-color: #606060; | |||||
| .logo { | |||||
| float: left; | |||||
| width: 140px; | |||||
| height: 40px; | |||||
| } | |||||
| .userInfo { | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| color: #fff; | |||||
| .userImg { | |||||
| float: right; | |||||
| width: 30px; | |||||
| height: 30px; | |||||
| margin-top: 3px; | |||||
| margin-right: 10px; | |||||
| margin-left: 15px; | |||||
| } | |||||
| } | |||||
| } | |||||
| .showIMG { | |||||
| padding: 10px; | |||||
| width: 355px; | |||||
| height: 130px; | |||||
| border-radius: 16px; | |||||
| } | |||||
| .item { | |||||
| padding-top: 15px; | |||||
| padding-bottom: 15px; | |||||
| padding-left: 10px; | |||||
| padding-right: 10px; | |||||
| .title { | |||||
| font-size: 16px; | |||||
| font-weight: 500; | |||||
| // margin-left: 15px; | |||||
| margin-bottom: 20px; | |||||
| img { | |||||
| margin-top: -5px; | |||||
| vertical-align: middle; | |||||
| width: 24px; | |||||
| height: 18px; | |||||
| background-size: 100% 100%; | |||||
| } | |||||
| } | |||||
| .itemList { | |||||
| .videoModel { | |||||
| position: relative; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| margin-bottom: 25px; | |||||
| border-bottom: 1px solid #00000010; | |||||
| padding: 0 20px 25px 25px; | |||||
| .videoPrv { | |||||
| width: 150px; | |||||
| height: 100px; | |||||
| // background-color: #979797; | |||||
| img { | |||||
| position: absolute; | |||||
| width: 150px; | |||||
| height: 100px; | |||||
| } | |||||
| .cover { | |||||
| position: absolute; | |||||
| width: 150px; | |||||
| height: 100px; | |||||
| line-height: 100px; | |||||
| background-color: #00000034; | |||||
| text-align: center; | |||||
| color: #fff; | |||||
| } | |||||
| } | |||||
| .videoInfo { | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| justify-content: space-between; | |||||
| margin-left: 10px; | |||||
| .videoName { | |||||
| font-size: 16px; | |||||
| font-weight: 600; | |||||
| } | |||||
| .createTime { | |||||
| font-size: 11px; | |||||
| } | |||||
| } | |||||
| .tag { | |||||
| position: absolute; | |||||
| right: 10px; | |||||
| top: 10px; | |||||
| padding: 1px 5px; | |||||
| border-radius: 5px; | |||||
| &.text { | |||||
| color: #fff; | |||||
| background-color: #a2a2a290; | |||||
| } | |||||
| &.generating { | |||||
| background-color: #ffa600c2; | |||||
| } | |||||
| &.finish { | |||||
| color: #ffffff; | |||||
| background-color: #00ff88; | |||||
| } | |||||
| &.fail { | |||||
| color: #ffffff; | |||||
| background-color: #ff0000c3; | |||||
| } | |||||
| } | |||||
| } | |||||
| .noWork { | |||||
| width: 100%; | |||||
| height: 450px; | |||||
| line-height: 450px; | |||||
| font-size: 14px; | |||||
| font-weight: 600; | |||||
| text-align: center; | |||||
| } | |||||
| } | |||||
| } | |||||
| .goAdd { | |||||
| position: sticky; | |||||
| bottom: 25px; | |||||
| left: 10px; | |||||
| width: 300px; | |||||
| height: 42px; | |||||
| line-height: 42px; | |||||
| font-size: 15px; | |||||
| color: #fff; | |||||
| text-align: center; | |||||
| transform: translateX(-50%); | |||||
| background-color: #0068b7; | |||||
| border-radius: 5px; | |||||
| box-shadow: #60606082 1px 1px 1px 1px; | |||||
| margin-top: 25px; | |||||
| margin-left: 190px; | |||||
| } | |||||
| .messageCover { | |||||
| position: fixed; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| background-color: #00000091; | |||||
| transition: all 0.3s; | |||||
| opacity: 0; | |||||
| z-index: -1; | |||||
| .message { | |||||
| position: sticky; | |||||
| top: 48%; | |||||
| transform: translate(-50%, -50%); | |||||
| width: 272px; | |||||
| height: 290px; | |||||
| border-radius: 5px; | |||||
| background-color: #fff; | |||||
| text-align: center; | |||||
| transition: all 0.3s; | |||||
| margin-left: 50%; | |||||
| padding: 10px; | |||||
| .connect { | |||||
| font-size: 12px; | |||||
| margin: 19px 0 10px 0; | |||||
| } | |||||
| .qrCode { | |||||
| width: 203px; | |||||
| height: 213px; | |||||
| background-color: #ffffff; | |||||
| border-radius: 5px; | |||||
| border: solid 1px #595959; | |||||
| margin: auto; | |||||
| img { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| } | |||||
| .phoneNum { | |||||
| float: left; | |||||
| font-size: 11px; | |||||
| margin-top: 8px; | |||||
| margin-left: 34px; | |||||
| } | |||||
| .close { | |||||
| position: absolute; | |||||
| top: 8px; | |||||
| right: 15px; | |||||
| font-size: 18px; | |||||
| color: #000; | |||||
| } | |||||
| .clickToClose { | |||||
| position: absolute; | |||||
| font-size: 14px; | |||||
| color: #fff; | |||||
| left: 50%; | |||||
| bottom: -20%; | |||||
| transform: translateX(-50%); | |||||
| } | |||||
| } | |||||
| &.active { | |||||
| z-index: 100; | |||||
| opacity: 1; | |||||
| } | |||||
| } | |||||
| .delete-button { | |||||
| position: relative; | |||||
| left: 1%; | |||||
| height: 100%; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| @@ -2,25 +2,34 @@ | |||||
| <div :class="inPage ? 'page active' : 'page'"> | <div :class="inPage ? 'page active' : 'page'"> | ||||
| <div id="top"></div> | <div id="top"></div> | ||||
| <HeadTop /> | <HeadTop /> | ||||
| <!-- 顶部图 --> | |||||
| <img class="showIMG" src="../../assets/img/BG-Page.png" alt="" /> | <img class="showIMG" src="../../assets/img/BG-Page.png" alt="" /> | ||||
| <!-- 展示列表 --> | |||||
| <div class="item"> | <div class="item"> | ||||
| <div class="title">我的视频作品</div> | |||||
| <van-swipe-cell v-for="(item, index) in videoList"> | |||||
| <div class="title"> | |||||
| <img src="../../assets/img/myVideo.png" alt=""> | |||||
| 我的视频作品 | |||||
| <span class="floatRight" style="float: right;">草稿箱</span> | |||||
| </div> | |||||
| <!-- 具体内容 --> | |||||
| <van-swipe-cell v-for="(item, index) in videoList" :key="item.id?item.id:index"> | |||||
| <div class="itemList"> | <div class="itemList"> | ||||
| <!-- 每一项 --> | |||||
| <div | <div | ||||
| v-if="videoList.length > 0" | v-if="videoList.length > 0" | ||||
| class="videoModel" | class="videoModel" | ||||
| :key="index" | :key="index" | ||||
| @click="goDetail(item)" | |||||
| > | |||||
| <div class="videoPrv"> | |||||
| > | |||||
| <!-- 左侧图片 --> | |||||
| <div class="videoPrv" @click="goDetail(item)"> | |||||
| <img src="../../assets/img/BG@2x.png" /> | <img src="../../assets/img/BG@2x.png" /> | ||||
| <div v-if="item.videoStatus == 0" class="cover">草稿</div> | <div v-if="item.videoStatus == 0" class="cover">草稿</div> | ||||
| <div v-if="item.videoStatus == 1" class="cover">视频生成中</div> | <div v-if="item.videoStatus == 1" class="cover">视频生成中</div> | ||||
| <div v-if="item.videoStatus == 3" class="cover">视频生成失败</div> | <div v-if="item.videoStatus == 3" class="cover">视频生成失败</div> | ||||
| <!-- <div v-if="item.videoStatus == ?" class="cover">排队中</div> --> | <!-- <div v-if="item.videoStatus == ?" class="cover">排队中</div> --> | ||||
| </div> | </div> | ||||
| <div class="videoInfo"> | |||||
| <!-- 中间文字 --> | |||||
| <div class="videoInfo" @click="goDetail(item)"> | |||||
| <div class="videoName"> | <div class="videoName"> | ||||
| {{ item.title || "暂无" }} | {{ item.title || "暂无" }} | ||||
| </div> | </div> | ||||
| @@ -36,13 +45,31 @@ | |||||
| }} | }} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <!-- 右侧更多 --> | |||||
| <div class="showMore" @click.stop="showDialog(item.id)"> | |||||
| <div></div> | |||||
| <div></div> | |||||
| <div></div> | |||||
| </div> | |||||
| <!-- 弹出层 --> | |||||
| <van-popup v-if="clickItem==item.id" v-model="showPopup"> | |||||
| <div @click="closeDialog"><i class="el-icon-edit" /> 编辑</div> | |||||
| <div @click="goDelete(item.id)"><i class="el-icon-delete" /> 删除</div> | |||||
| </van-popup> | |||||
| <!-- <van-dialog class="dialog" | |||||
| v-model="clickItem==item?showTrue:showFalse"> | |||||
| <div @click="closeDialog"><i class="el-icon-edit" /> 编辑</div> | |||||
| <div><i class="el-icon-delete" /> 删除</div> | |||||
| </van-dialog> --> | |||||
| </div> | </div> | ||||
| <!-- 无数据处理 --> | |||||
| <div v-if="videoList.length <= 0" class="noWork"> | <div v-if="videoList.length <= 0" class="noWork"> | ||||
| 您还没有视频作品 | 您还没有视频作品 | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <template #right> | |||||
| <!-- 右滑 --> | |||||
| <!-- <template #right> | |||||
| <van-button | <van-button | ||||
| square | square | ||||
| text="删除" | text="删除" | ||||
| @@ -50,17 +77,21 @@ | |||||
| class="delete-button" | class="delete-button" | ||||
| @click="goDelete(item.id)" | @click="goDelete(item.id)" | ||||
| /> | /> | ||||
| </template> | |||||
| </template> --> | |||||
| </van-swipe-cell> | </van-swipe-cell> | ||||
| </div> | </div> | ||||
| <!-- 分页 --> | |||||
| <van-pagination | <van-pagination | ||||
| v-model="currentPage" | v-model="currentPage" | ||||
| :total-items="total" | :total-items="total" | ||||
| :items-per-page="4" | :items-per-page="4" | ||||
| @change="pageChange" | @change="pageChange" | ||||
| /> | /> | ||||
| <div class="goAdd" @click="checkInviteCode">+ 创作新作品</div> | |||||
| <!-- 创建新作品 --> | |||||
| <!-- <div class="goAdd" @click="checkInviteCode">+ 创作新作品</div> --> | |||||
| <div class="goNewAdd" @click="checkInviteCode"> | |||||
| <i class="el-icon-plus"></i> | |||||
| </div> | |||||
| <div | <div | ||||
| class="messageCover" | class="messageCover" | ||||
| @@ -84,7 +115,7 @@ | |||||
| import Vue from "vue"; | import Vue from "vue"; | ||||
| import { Toast, Dialog } from "vant"; | import { Toast, Dialog } from "vant"; | ||||
| import { mapState, mapActions } from "vuex"; | import { mapState, mapActions } from "vuex"; | ||||
| import HeadTop from "./../../components/common/head.vue"; | |||||
| import HeadTop from "@/components/common/head.vue"; | |||||
| // 工具 | // 工具 | ||||
| import { | import { | ||||
| bytesToSize, | bytesToSize, | ||||
| @@ -107,6 +138,10 @@ export default { | |||||
| }, | }, | ||||
| data() { | data() { | ||||
| return { | return { | ||||
| showPopup:false,// 编辑和删除显示隐藏 | |||||
| clickItem: -1,// 弹出层显示隐藏 | |||||
| showTrue: true,// 弹出层显示隐藏 | |||||
| showFalse: false,// 弹出层显示隐藏 | |||||
| inPage: false, | inPage: false, | ||||
| isShowMessageCover: false, | isShowMessageCover: false, | ||||
| videoList: [], | videoList: [], | ||||
| @@ -124,7 +159,14 @@ export default { | |||||
| methods: { | methods: { | ||||
| ...mapActions(["setCharacters"]), | ...mapActions(["setCharacters"]), | ||||
| closeDialog() { | |||||
| Dialog.close | |||||
| }, | |||||
| // 点击显示编辑删除 | |||||
| showDialog(id) { | |||||
| this.showPopup=true | |||||
| this.clickItem=id | |||||
| }, | |||||
| go(url) { | go(url) { | ||||
| this.$router.push(url); | this.$router.push(url); | ||||
| }, | }, | ||||
| @@ -193,14 +235,14 @@ export default { | |||||
| }); | }); | ||||
| } | } | ||||
| }, | }, | ||||
| // 删除作品 | |||||
| goDelete(id) { | goDelete(id) { | ||||
| Dialog.confirm({ | Dialog.confirm({ | ||||
| title: "删除作品", | title: "删除作品", | ||||
| message: "您确定要删除该作品吗?", | message: "您确定要删除该作品吗?", | ||||
| }) | }) | ||||
| .then(() => { | |||||
| this.deleteVideoById(id); | |||||
| .then(async() => { | |||||
| await this.deleteVideoById(id); | |||||
| this.loadVideoList(this.currentPage, this.pageSize); | this.loadVideoList(this.currentPage, this.pageSize); | ||||
| }) | }) | ||||
| .catch(() => { | .catch(() => { | ||||
| @@ -326,26 +368,33 @@ export default { | |||||
| } | } | ||||
| } | } | ||||
| .showIMG { | .showIMG { | ||||
| padding: 10px; | |||||
| width: 100%; | width: 100%; | ||||
| height: 130px; | |||||
| height: 140px; | |||||
| border-radius: 16px; | |||||
| } | } | ||||
| .item { | .item { | ||||
| padding-top: 15px; | |||||
| padding-bottom: 15px; | |||||
| padding: 15px 10px; | |||||
| .title { | .title { | ||||
| font-size: 15px; | |||||
| font-weight: 600; | |||||
| margin-left: 15px; | |||||
| font-size: 16px; | |||||
| font-weight: 500; | |||||
| // margin-left: 15px; | |||||
| margin-bottom: 20px; | margin-bottom: 20px; | ||||
| img { | |||||
| margin-top: -5px; | |||||
| vertical-align: middle; | |||||
| width: 24px; | |||||
| height: 18px; | |||||
| background-size: 100% 100%; | |||||
| } | |||||
| } | } | ||||
| .itemList { | .itemList { | ||||
| .videoModel { | .videoModel { | ||||
| position: relative; | position: relative; | ||||
| display: flex; | display: flex; | ||||
| justify-content: space-between; | justify-content: space-between; | ||||
| margin-bottom: 25px; | |||||
| border-bottom: 1px solid #00000010; | |||||
| padding: 0 20px 25px 25px; | |||||
| margin-bottom: 20px; | |||||
| .videoPrv { | .videoPrv { | ||||
| width: 150px; | width: 150px; | ||||
| height: 100px; | height: 100px; | ||||
| @@ -354,6 +403,7 @@ export default { | |||||
| position: absolute; | position: absolute; | ||||
| width: 150px; | width: 150px; | ||||
| height: 100px; | height: 100px; | ||||
| border-radius: 6px; | |||||
| } | } | ||||
| .cover { | .cover { | ||||
| position: absolute; | position: absolute; | ||||
| @@ -362,22 +412,63 @@ export default { | |||||
| line-height: 100px; | line-height: 100px; | ||||
| background-color: #00000034; | background-color: #00000034; | ||||
| text-align: center; | text-align: center; | ||||
| border-radius: 6px; | |||||
| color: #fff; | color: #fff; | ||||
| } | } | ||||
| } | } | ||||
| .videoInfo { | .videoInfo { | ||||
| display: flex; | |||||
| flex-direction: column; | |||||
| justify-content: space-between; | |||||
| margin-left: 10px; | |||||
| flex: 1; | |||||
| margin-left: 12px; | |||||
| .videoName { | .videoName { | ||||
| font-size: 16px; | font-size: 16px; | ||||
| font-weight: 600; | font-weight: 600; | ||||
| } | } | ||||
| .createTime { | .createTime { | ||||
| font-size: 11px; | font-size: 11px; | ||||
| color: #999999; | |||||
| margin-top: 5px; | |||||
| } | |||||
| } | |||||
| .showMore { | |||||
| width: 20px; | |||||
| display: flex; | |||||
| justify-content: space-between; | |||||
| align-items: center; | |||||
| // margin-left: 10px; | |||||
| div { | |||||
| width: 4px; | |||||
| height: 4px; | |||||
| background-color: #51D9CA; | |||||
| } | |||||
| } | |||||
| .dialog { | |||||
| i { | |||||
| margin-left: -10px; | |||||
| } | } | ||||
| } | } | ||||
| // 弹出层样式 | |||||
| ::v-deep .van-popup { | |||||
| display: flex; | |||||
| flex-wrap: wrap; | |||||
| align-items: center; | |||||
| justify-content: center; | |||||
| top: 70%; | |||||
| left: 290px; | |||||
| padding-left: 10px; | |||||
| width: 80px; | |||||
| height: 60px; | |||||
| border-radius: 12px; | |||||
| font-size: 14px; | |||||
| line-height: 28px; | |||||
| text-align: center; | |||||
| box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.1); | |||||
| } | |||||
| ::v-deep .van-overlay { | |||||
| background-color: rgba(0,0,0,0); | |||||
| } | |||||
| ::v-deep .van-button { | |||||
| display: none; | |||||
| } | |||||
| .tag { | .tag { | ||||
| position: absolute; | position: absolute; | ||||
| right: 10px; | right: 10px; | ||||
| @@ -428,6 +519,20 @@ export default { | |||||
| margin-top: 25px; | margin-top: 25px; | ||||
| margin-left: 190px; | margin-left: 190px; | ||||
| } | } | ||||
| .goNewAdd { | |||||
| position: sticky; | |||||
| left: 300px; | |||||
| bottom: 25px; | |||||
| width: 60px; | |||||
| height: 60px; | |||||
| border-radius: 50%; | |||||
| font-size: 40px; | |||||
| line-height: 60px; | |||||
| text-align: center; | |||||
| color: #ffffff; | |||||
| background: linear-gradient(316deg, #6EE4D7 0%, #37C4F1 32%, #A49AFC 100%); | |||||
| box-shadow: 0px 2px 6px 0px rgba(114,100,245,0.25); | |||||
| } | |||||
| .messageCover { | .messageCover { | ||||
| position: fixed; | position: fixed; | ||||
| top: 0; | top: 0; | ||||