瀏覽代碼

upload

master
HolyKnightIX 2 年之前
父節點
當前提交
8ee8af8b2d
共有 34 個文件被更改,包括 4302 次插入229 次删除
  1. 二進制
      src/assets/icon/icon-background.png
  2. 二進制
      src/assets/icon/icon-earphone.png
  3. 二進制
      src/assets/icon/icon-folder@2x.png
  4. 二進制
      src/assets/icon/icon-loginout.png
  5. 二進制
      src/assets/icon/icon-loginoutwhite.png
  6. 二進制
      src/assets/icon/icon-material.png
  7. 二進制
      src/assets/icon/icon-microphone.png
  8. 二進制
      src/assets/icon/icon-pencil.png
  9. 二進制
      src/assets/icon/icon-subtitle.png
  10. 二進制
      src/assets/icon/icon-upload@2x.png
  11. 二進制
      src/assets/icon/icon-yes.png
  12. 二進制
      src/assets/img/myVideo.png
  13. 二進制
      src/assets/web(1).rar
  14. +70
    -0
      src/assets/web(1)/web/css/client.css
  15. +503
    -0
      src/assets/web(1)/web/js/client.js
  16. +196
    -0
      src/assets/web(1)/web/user.html
  17. +91
    -0
      src/components/MyVideoItem.vue
  18. +11
    -5
      src/components/common/head.vue
  19. +1
    -1
      src/components/common/tail.vue
  20. +2
    -0
      src/main.js
  21. +18
    -0
      src/router/index.js
  22. +16
    -0
      src/styles/index.scss
  23. +284
    -0
      src/styles/reset.scss
  24. +3
    -2
      src/utils/request.js
  25. +100
    -0
      src/views/chat/chat.vue
  26. +441
    -0
      src/views/login/login copy.vue
  27. +309
    -158
      src/views/login/login.vue
  28. +312
    -0
      src/views/model/chooseModel copy.vue
  29. +55
    -34
      src/views/model/chooseModel.vue
  30. +649
    -0
      src/views/model/editModel.vue
  31. +98
    -0
      src/views/model/previewModel.vue
  32. +489
    -0
      src/views/myPage/myPage copy 3.vue
  33. +520
    -0
      src/views/myPage/myPage copy.vue
  34. +134
    -29
      src/views/myPage/myPage.vue

二進制
src/assets/icon/icon-background.png 查看文件

Before After
Width: 48  |  Height: 48  |  Size: 2.3 KiB

二進制
src/assets/icon/icon-earphone.png 查看文件

Before After
Width: 64  |  Height: 48  |  Size: 1.9 KiB

二進制
src/assets/icon/icon-folder@2x.png 查看文件

Before After
Width: 40  |  Height: 32  |  Size: 839 B

二進制
src/assets/icon/icon-loginout.png 查看文件

Before After
Width: 48  |  Height: 48  |  Size: 1.7 KiB

二進制
src/assets/icon/icon-loginoutwhite.png 查看文件

Before After
Width: 34  |  Height: 36  |  Size: 1.0 KiB

二進制
src/assets/icon/icon-material.png 查看文件

Before After
Width: 48  |  Height: 48  |  Size: 2.6 KiB

二進制
src/assets/icon/icon-microphone.png 查看文件

Before After
Width: 48  |  Height: 48  |  Size: 2.6 KiB

二進制
src/assets/icon/icon-pencil.png 查看文件

Before After
Width: 32  |  Height: 32  |  Size: 952 B

二進制
src/assets/icon/icon-subtitle.png 查看文件

Before After
Width: 48  |  Height: 48  |  Size: 2.8 KiB

二進制
src/assets/icon/icon-upload@2x.png 查看文件

Before After
Width: 40  |  Height: 32  |  Size: 461 B

二進制
src/assets/icon/icon-yes.png 查看文件

Before After
Width: 52  |  Height: 32  |  Size: 1.1 KiB

二進制
src/assets/img/myVideo.png 查看文件

Before After
Width: 24  |  Height: 18  |  Size: 814 B

二進制
src/assets/web(1).rar 查看文件


+ 70
- 0
src/assets/web(1)/web/css/client.css 查看文件

@@ -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;
}

+ 503
- 0
src/assets/web(1)/web/js/client.js 查看文件

@@ -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 = '';
}


+ 196
- 0
src/assets/web(1)/web/user.html 查看文件

@@ -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>

+ 91
- 0
src/components/MyVideoItem.vue 查看文件

@@ -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>

+ 11
- 5
src/components/common/head.vue 查看文件

@@ -5,15 +5,20 @@
<div class="userInfo">
<div class="nickName" @click="go('/myPage')">用户名</div>
<div class="userModel">
<img
<!-- <img
class="userImg"
src="../../assets/img/user_black.png"
@click="go('/myPage')"
/>
<img
/> -->
<!-- <img
class="userImg"
src="../../assets/img/loginOut.png"
@click="loginOut()"
/> -->
<img
class="userImg"
src="../../assets/icon/icon-loginout.png"
@click="loginOut()"
/>
</div>
</div>
@@ -100,14 +105,15 @@ export default {
font-weight: 600;
font-size: 13px;
margin-right: 15px;
margin-top: 3px;
}
.userModel {
position: relative;
top: 3px;
margin-right: 10px;
.userImg {
width: 30px;
height: 30px;
width: 24px;
height: 24px;
margin-right: 2px;
}
}


+ 1
- 1
src/components/common/tail.vue 查看文件

@@ -1,5 +1,5 @@
<template>
<div class="tailBox">
<div v-if="false" class="tailBox">
<!-- <div class="title">联系我们</div> -->
<!-- <div class="taillong">
<img src="../../assets/img/LOGO1.png" alt>


+ 2
- 0
src/main.js 查看文件

@@ -10,6 +10,8 @@ import 'lib-flexible'
import axios from "axios"
import ElementUI from 'element-ui'; //引入js
import 'element-ui/lib/theme-chalk/index.css';//引入css
import "@/styles/reset.scss"; // 引入清除默认样式
import "@/styles/index.scss"; // 引入全局样式

Vue.use(ElementUI);
Vue.use(Vant)


+ 18
- 0
src/router/index.js 查看文件

@@ -44,6 +44,24 @@ const routes = [
name: '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',


+ 16
- 0
src/styles/index.scss 查看文件

@@ -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;
}

+ 284
- 0
src/styles/reset.scss 查看文件

@@ -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;
}

+ 3
- 2
src/utils/request.js 查看文件

@@ -5,7 +5,9 @@ import { Toast } from 'vant'
// 基础地址
const baseURL = 'https://smapitest.malls.iformall.com/C'

// 创建axios实例
/**
* @description:发送Axios请求
*/
const request = axios.create({
baseURL,
timeout: 5000
@@ -56,7 +58,6 @@ request.interceptors.response.use(
return response.data
}


},
function (error) {
}


+ 100
- 0
src/views/chat/chat.vue 查看文件

@@ -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>

+ 441
- 0
src/views/login/login copy.vue 查看文件

@@ -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>

+ 309
- 158
src/views/login/login.vue 查看文件

@@ -1,4 +1,5 @@
<template>
<!-- 手机号13797188591 -->
<div :class="inPage ? 'page active' : 'page'">
<div id="top"></div>
<div class="logoBox" @click="go('/')">
@@ -6,214 +7,347 @@
<!-- <div class="logoTitle">metavatar</div> -->
</div>

<!-- 短信验证码登录 -->
<!-- 首页登录 loginType=1 -->
<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-input
v-model="form.phone"
placeholder="请输入您的手机号"
></el-input>
</el-form-item>
<el-form-item class="code" prop="code">
<!-- 密码 -->
<el-form-item class="code" prop="password">
<el-input
class="codeInput"
v-model="form.code"
maxlength="10"
clearable
v-model="form.code"
placeholder="请输入密码"
></el-input>
<div class="inputCode">请输入短信验证码</div>
</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-button class="submitBtn" @click="loginByPhoneCode"
>登录/注册</el-button
>
>登录/注册</el-button>
</el-form-item>
<!-- 没有账号 -->
<div>
<span>没有账号?</span>
<span class="greenColor" @click="loginType=5">立即注册</span>
</div>
</el-form>
</div>

<!-- 用户名密码登录 -->
<!-- 短信登录 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-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 class="code" prop="code">
<!-- 短信验证码 -->
<el-form-item class="code" prop="phoneCode">
<el-input
class="codeInput"
v-model="form.pwd"
maxlength="10"
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>
<div class="inputCode">请输入密码</div>
</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
class="codeInput"
maxlength="10"
clearable
v-model="form.code"
placeholder="请输入您的新密码"
>
</el-input>
</el-form-item>
<!-- 新密码2 -->
<el-form-item class="code" prop="newPassword2">
<el-input
maxlength="10"
clearable
></el-input>
<div class="inputCode">请输入验证码</div>
v-model="form.code"
placeholder="请再次确认您的新密码"
>
</el-input>
</el-form-item>

<span class="forgetPwd">忘记密码?</span>

<!-- 登录 -->
<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>
</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
class="codeInput"
v-model="formIII.inviteCode"
maxlength="10"
clearable
></el-input>
<div class="inputCode">请输入您的邀请码</div>
v-model="form.code"
placeholder="请输入您的密码"
>
</el-input>
</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-button class="submitBtn" @click="loginByPhoneCode"
>注册</el-button>
</el-form-item>

</el-form>
</div>
<!-- 弹出层 -->
<van-popup v-model="showDialog">内容</van-popup>
</div>
</template>

<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 {
getCodeByPhone,
doLoginByPhone,
doFindInviteCode,
doUpdateInviteCode,
} from "../../api/login";
doUpdateInviteCode
} from '../../api/login'

Vue.use(Toast);
Vue.use(Toast)
export default {
data() {
return {
codeInfo: "获取验证码",
showDialog:false,//弹出层状态
countNum: 60,// 倒计时
countdownState:false,//倒计时状态
codeInfo: '获取验证码',
inPage: false,
loginType: 1,
loginType: 1, //显示页面字符
// 短信验证码登录
form: {
phone: "",
code: "",
phone: '',
password: '',
newPassword1:'',
newPassword2:'',
phoneCode:'',
code: '',
yzCode: ''
},
rules: {
phone: [
{
required: true,
trigger: "blur",
message: "请先输手机号",
trigger: 'blur',
message: '请先输手机号'
},
{
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}$/,
let phone = /^1[35789]\d{9}$/;
let phone = /^1[35789]\d{9}$/
if (value && !phone.test(value)) {
callback("抱歉,请输入正确的手机号");
callback('抱歉,请输入正确的手机号')
}
callback(errors);
},
},
],
callback(errors)
}
}
]
},
// 账号密码登录
formII: {
phone: "",
pwd: "",
code: "",
phone: '',
pwd: '',
code: ''
},
rulesII: {
phone: [
{
required: true,
trigger: "blur",
message: "请先输手机号",
trigger: 'blur',
message: '请先输手机号'
},
{
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}$/,
let phone = /^1[35789]\d{9}$/;
let phone = /^1[35789]\d{9}$/
if (value && !phone.test(value)) {
callback("抱歉,请输入正确的手机号");
callback('抱歉,请输入正确的手机号')
}
callback(errors);
},
},
],
callback(errors)
}
}
]
},
// 短信登录
// 忘记密码
// 重设密码
// 注册
// 邀请码
formIII: {
inviteCode: "",
},
};
inviteCode: ''
}
}
},

computed: {
...mapState({
characters: (state) => state.publicObj.characters,
}),
characters: (state) => state.publicObj.characters
})
},

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) {
this.$router.push(url);
this.$router.push(url)
},

// 跳过填写邀请码
skip() {
this.$router.push("/myPage");
this.$router.push('/myPage')
},

// 获取手机短信验证码
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) {
Toast.fail("请输入正确的手机号!");
Toast.fail('请输入正确的手机号!')
} else {
this.getPhoneCode(phoneValue);
this.getPhoneCode(phoneValue)
}
},

@@ -221,17 +355,17 @@ export default {
loginByPhoneCode() {
const data = {
phone: this.form.phone,
code: this.form.code,
};
this.LoginByPhone(data);
code: this.form.code
}
this.LoginByPhone(data)
},

// 提交邀请码
submitInviteCode() {
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) {
try {
const res = await getCodeByPhone(phone);
Toast.success("短信验证码发送成功!");
this.codeInfo = 60;
const res = await getCodeByPhone(phone)
Toast.success('短信验证码发送成功!')
this.codeInfo = 60
const timer = setInterval(() => {
if (this.codeInfo != 1) {
this.codeInfo--;
this.codeInfo--
} else {
clearInterval(timer);
this.codeInfo = "获取验证码";
clearInterval(timer)
this.codeInfo = '获取验证码'
}
}, 1000);
}, 1000)
} 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) {
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置入本地存储
localStorage.setItem("AccessToken", token);
this.checkInviteCode();
localStorage.setItem('AccessToken', token)
this.checkInviteCode()
} 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() {
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) {
this.loginType = 3;
this.loginType = 3
// 有邀请码直接前往我的页面
} else if (status == 1) {
Toast.success("登录成功!");
this.$router.push("/myPage");
Toast.success('登录成功!')
this.$router.push('/myPage')
}
} 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) {
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) {
console.log(error, "error");
Toast.fail(error.message);
console.log(error, 'error')
Toast.fail(error.message)
}
},
}
},
created() {
// 手机号记忆回填
const phoneValue = localStorage.getItem("phoneValue");
this.form.phone = phoneValue ? phoneValue : "";
const phoneValue = localStorage.getItem('phoneValue')
this.form.phone = phoneValue ? phoneValue : ''
},
mounted() {
this.inPage = true;
scrollToID("top");
},
};
this.inPage = true
scrollToID('top')
}
}
</script>

<style lang="scss" scoped>
.greenColor {
color: #5ed5c8;
}
.page {
opacity: 0;
transition: all 0.5s; // global-transition
@@ -332,8 +469,8 @@ export default {
.logoBox {
// margin-top: 10px;
margin-left: 5px;
margin-top: 10px;
margin-bottom: 30px;
margin-top: 25px;
margin-bottom: 25px;
overflow: hidden;
cursor: pointer;
text-align: center;
@@ -356,20 +493,14 @@ export default {
font-weight: 600;
margin-bottom: 40px;
}
.inBoxEnd {
display: flex;
justify-content: end !important;
}
.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;
@@ -390,18 +521,34 @@ export default {
color: #0068b7;
}
}

.morePage {
display: flex;
justify-content: space-between;
margin-top: -9px;
}
.submitBtn {
position: relative;
right: 10px;
float: right;
// right: 10px;
// float: right;
color: #fff;
font-size: 15px;
line-height: 15px;
border: none;
margin-top: 25px;
// margin-bottom: 7.5px;
padding: 9px;
background-color: #0068b7;
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 {
top: 10px;
}
@@ -436,6 +583,10 @@ export default {
}
::v-deep .el-input__inner {
height: 42px;
border: 1px solid #0068b7;
border: 0px;
background-color: #f6f6f6;
}
::v-deep .el-form-item {
margin-bottom: 25px;
}
</style>

+ 312
- 0
src/views/model/chooseModel copy.vue 查看文件

@@ -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>

+ 55
- 34
src/views/model/chooseModel.vue 查看文件

@@ -2,14 +2,15 @@
<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> -->
<!-- 顶部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 == 0 ? 'active' : ''" @click="chooseType(0)">
全部模板
@@ -21,8 +22,8 @@
男性模板
</div>
</div>
</div>
</div> -->
<!-- 模板列表 -->
<div class="choose">
<div class="modeList">
<div
@@ -67,6 +68,7 @@ export default {
type: 0,
currentId: "",
modeList: [], // 模板列表
tabList: ['全部','男性','女性','原P','武装直升机','粥P','二刺猿']
};
},
computed: {
@@ -77,7 +79,11 @@ export default {

methods: {
...mapActions(["setCharacters"]),

//点击tab栏选择模板列表
chooseType1(name, title) {
this.type = this.tabList.findIndex(item => item==title)
this.loadModeList(this.type);
},
globalWatcher(e) {
console.log(e, "e");
},
@@ -90,10 +96,10 @@ export default {
this.phase = num;
},

chooseType(num) {
this.type = num;
this.loadModeList(num);
},
// chooseType(num) {
// this.type = num;
// this.loadModeList(num);
// },

selectModel(id) {
if (this.currentId != id) {
@@ -115,13 +121,20 @@ export default {
Toast.fail(error.message);
}
},
// 根据id获取模板详情
async getModeDetail(id) {
try {
const res = await getModeDetailById(id);
console.log(res, "根据id获取模板详情");
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) {
console.log(error, "error");
Toast.fail(error.message);
@@ -132,13 +145,13 @@ export default {
try {
const res = await saveOrUpdateUserVideo(data);
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) {
console.log(error, "error");
Toast.fail(error.message);
@@ -173,6 +186,10 @@ export default {
</script>

<style lang="scss" scoped>
::v-deep .van-tab--active {
font-size: 17px;
font-weight: 700;
}
.page {
transform: translateX(30%);
opacity: 0;
@@ -215,9 +232,9 @@ export default {
}
.typeOutSide {
position: sticky;
top: 60px;
top: 0px;
z-index: 99;
.type {
display: flex;
justify-content: space-between;
@@ -245,6 +262,7 @@ export default {
}

.choose {
padding-top: 20px;
margin-bottom: 40px;

.modeList {
@@ -253,24 +271,27 @@ export default {
align-items: center;
flex-wrap: wrap;
margin: auto;
padding: 0 10px;
padding: 0 23px;

.model {
position: relative;
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;
opacity: 0.9;
img {
width: 150px;
height: 220px;
border-radius: 8px;
width: 100%;
height: 273px;
}
.modelName {
margin-top: 4px;
font-size: 13px;
margin-top: 10px;
font-weight: 700;
font-size: 16px;
}
.setPaper {
position: absolute;


+ 649
- 0
src/views/model/editModel.vue 查看文件

@@ -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>&nbsp;&nbsp;
<span>视频时长:&nbsp;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>

+ 98
- 0
src/views/model/previewModel.vue 查看文件

@@ -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>

+ 489
- 0
src/views/myPage/myPage copy 3.vue 查看文件

@@ -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>

+ 520
- 0
src/views/myPage/myPage copy.vue 查看文件

@@ -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>

+ 134
- 29
src/views/myPage/myPage.vue 查看文件

@@ -2,25 +2,34 @@
<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">我的视频作品</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
v-if="videoList.length > 0"
class="videoModel"
:key="index"
@click="goDetail(item)"
>
<div class="videoPrv">
>
<!-- 左侧图片 -->
<div class="videoPrv" @click="goDetail(item)">
<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="videoInfo" @click="goDetail(item)">
<div class="videoName">
{{ item.title || "暂无" }}
</div>
@@ -36,13 +45,31 @@
}}
</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" />&nbsp;&nbsp;&nbsp;编辑</div>
<div @click="goDelete(item.id)"><i class="el-icon-delete" />&nbsp;&nbsp;&nbsp; 删除</div>
</van-popup>

<!-- <van-dialog class="dialog"
v-model="clickItem==item?showTrue:showFalse">
<div @click="closeDialog"><i class="el-icon-edit" />&nbsp;&nbsp;&nbsp;编辑</div>
<div><i class="el-icon-delete" />&nbsp;&nbsp;&nbsp; 删除</div>
</van-dialog> -->
</div>
<!-- 无数据处理 -->
<div v-if="videoList.length <= 0" class="noWork">
您还没有视频作品
</div>
</div>

<template #right>
<!-- 右滑 -->
<!-- <template #right>
<van-button
square
text="删除"
@@ -50,17 +77,21 @@
class="delete-button"
@click="goDelete(item.id)"
/>
</template>
</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="goAdd" @click="checkInviteCode">+ 创作新作品</div> -->
<div class="goNewAdd" @click="checkInviteCode">
<i class="el-icon-plus"></i>
</div>

<div
class="messageCover"
@@ -84,7 +115,7 @@
import Vue from "vue";
import { Toast, Dialog } from "vant";
import { mapState, mapActions } from "vuex";
import HeadTop from "./../../components/common/head.vue";
import HeadTop from "@/components/common/head.vue";
// 工具
import {
bytesToSize,
@@ -107,6 +138,10 @@ export default {
},
data() {
return {
showPopup:false,// 编辑和删除显示隐藏
clickItem: -1,// 弹出层显示隐藏
showTrue: true,// 弹出层显示隐藏
showFalse: false,// 弹出层显示隐藏
inPage: false,
isShowMessageCover: false,
videoList: [],
@@ -124,7 +159,14 @@ export default {

methods: {
...mapActions(["setCharacters"]),

closeDialog() {
Dialog.close
},
// 点击显示编辑删除
showDialog(id) {
this.showPopup=true
this.clickItem=id
},
go(url) {
this.$router.push(url);
},
@@ -193,14 +235,14 @@ export default {
});
}
},
// 删除作品
goDelete(id) {
Dialog.confirm({
title: "删除作品",
message: "您确定要删除该作品吗?",
})
.then(() => {
this.deleteVideoById(id);
.then(async() => {
await this.deleteVideoById(id);
this.loadVideoList(this.currentPage, this.pageSize);
})
.catch(() => {
@@ -326,26 +368,33 @@ export default {
}
}
.showIMG {
padding: 10px;
width: 100%;
height: 130px;
height: 140px;
border-radius: 16px;
}
.item {
padding-top: 15px;
padding-bottom: 15px;
padding: 15px 10px;
.title {
font-size: 15px;
font-weight: 600;
margin-left: 15px;
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;
margin-bottom: 20px;
.videoPrv {
width: 150px;
height: 100px;
@@ -354,6 +403,7 @@ export default {
position: absolute;
width: 150px;
height: 100px;
border-radius: 6px;
}
.cover {
position: absolute;
@@ -362,22 +412,63 @@ export default {
line-height: 100px;
background-color: #00000034;
text-align: center;
border-radius: 6px;
color: #fff;
}
}
.videoInfo {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 10px;
flex: 1;
margin-left: 12px;
.videoName {
font-size: 16px;
font-weight: 600;
}
.createTime {
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 {
position: absolute;
right: 10px;
@@ -428,6 +519,20 @@ export default {
margin-top: 25px;
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 {
position: fixed;
top: 0;


Loading…
取消
儲存