| @@ -16,7 +16,7 @@ export function register(data) { | |||||
| } | } | ||||
| /** | /** | ||||
| * @description 登录 | |||||
| * @description 邮箱登录 | |||||
| * @params email,password,code | * @params email,password,code | ||||
| * @returns data | * @returns data | ||||
| */ | */ | ||||
| @@ -28,6 +28,21 @@ export function login(data) { | |||||
| }) | }) | ||||
| } | } | ||||
| /** | |||||
| * @description 手机+密码登录 | |||||
| * @params phone password code | |||||
| * @returns data | |||||
| */ | |||||
| export function loginByPhoneApi(data) { | |||||
| return request({ | |||||
| // url: `/api/user/loginByEmail`, | |||||
| url: `/api/user/login`, | |||||
| method: 'post', | |||||
| data | |||||
| }) | |||||
| } | |||||
| /** | /** | ||||
| /** | /** | ||||
| * @description:重新发送邮件 | * @description:重新发送邮件 | ||||
| @@ -87,7 +102,56 @@ export function resetPwdFormApi(data) { | |||||
| data | data | ||||
| }) | }) | ||||
| } | } | ||||
| /** | |||||
| * @description 手机注册 | |||||
| * @params email,password | |||||
| * @returns data | |||||
| */ | |||||
| export function registerByPhoneApi(data) { | |||||
| return request({ | |||||
| // url: `/api/user/doRegisterByEmail`, | |||||
| url: `/api/user/doRegisterByPhone`, | |||||
| method: 'post', | |||||
| data | |||||
| }) | |||||
| } | |||||
| /** | |||||
| * @description 获取手机验证码 | |||||
| * @param {number} phone | |||||
| * @returns data | |||||
| */ | |||||
| export function getSMSToLoginApi(phone) { | |||||
| return request({ | |||||
| url: `/api/user/sendLoginPhoneCode?phone=${phone}`, | |||||
| method: 'get' | |||||
| }) | |||||
| } | |||||
| /** | |||||
| * @description 短信验证码登录 | |||||
| * @param {object} loginParams (phone,code) | |||||
| * @returns data | |||||
| */ | |||||
| export function loginByMSMApi(loginParams) { | |||||
| return request({ | |||||
| url: `/api/user/doLoginByPhone`, | |||||
| method: 'post', | |||||
| data: loginParams | |||||
| }) | |||||
| } | |||||
| /**`/api/user/sendLoginPhoneCode?phone=${phone}` | |||||
| * @description 忘记密码,短信验证码和新密码 | |||||
| * @param {object} loginParams (phone,code,pwd) | |||||
| * @returns data | |||||
| */ | |||||
| export function doUpdPassApi(loginParams) { | |||||
| return request({ | |||||
| url: `/api/user/updPass`, | |||||
| method: 'post', | |||||
| data: loginParams | |||||
| }) | |||||
| } | |||||
| /** | /** | ||||
| * @description:获取图形验证码 | * @description:获取图形验证码 | ||||
| @@ -23,8 +23,10 @@ export default { | |||||
| loginBtn: "Sign In", | loginBtn: "Sign In", | ||||
| registerBtn: "Sign Up", | registerBtn: "Sign Up", | ||||
| verifyCode: "Verify Code", | verifyCode: "Verify Code", | ||||
| signIn: "Already got an account? Sign in here", | |||||
| signUp: "Don't have an account? Sign up here", | |||||
| // signIn: "Already got an account? Sign in here", | |||||
| // signUp: "Don't have an account? Sign up here", | |||||
| signIn: "Sign in here", | |||||
| signUp: "Sign up here", | |||||
| forgetPwd: "I forgot my password", | forgetPwd: "I forgot my password", | ||||
| forgetPwdTitle:'Forgot Password', | forgetPwdTitle:'Forgot Password', | ||||
| SendEmail:'Send an email/SMS', | SendEmail:'Send an email/SMS', | ||||
| @@ -101,7 +103,7 @@ export default { | |||||
| width:"Width", | width:"Width", | ||||
| FontColor:"Font Color", | FontColor:"Font Color", | ||||
| backgroundColor:"Background color", | backgroundColor:"Background color", | ||||
| SubtitlesTip:"Please edit text in copy area", | |||||
| SubtitlesTip:"Subtitles will be shown here", | |||||
| }, | }, | ||||
| // 我的视频 | // 我的视频 | ||||
| @@ -137,6 +139,7 @@ export default { | |||||
| upSharpnessTips:'If you are satisfied with the preview, you can click here to improve the video sharpness ~', | upSharpnessTips:'If you are satisfied with the preview, you can click here to improve the video sharpness ~', | ||||
| freeUserTip:'Free user-generated videos are limited to 1 minute', | freeUserTip:'Free user-generated videos are limited to 1 minute', | ||||
| hasNewVideo:'You have a new video status!', | hasNewVideo:'You have a new video status!', | ||||
| reset:"reset", | |||||
| }, | }, | ||||
| // 个人账户 | // 个人账户 | ||||
| @@ -23,8 +23,10 @@ export default { | |||||
| loginBtn: "登 录", | loginBtn: "登 录", | ||||
| registerBtn: "注 册", | registerBtn: "注 册", | ||||
| verifyCode: "验证码", | verifyCode: "验证码", | ||||
| signIn: "已有账号?现在登录!", | |||||
| signUp: "没有账号?现在加入!", | |||||
| // signIn: "已有账号?现在登录!", | |||||
| // signUp: "没有账号?现在加入!", | |||||
| signIn: "直接登录", | |||||
| signUp: "立即注册", | |||||
| forgetPwd: "忘记密码", | forgetPwd: "忘记密码", | ||||
| forgetPwdTitle:'忘记密码', | forgetPwdTitle:'忘记密码', | ||||
| SendEmail:'发送邮件/短信', | SendEmail:'发送邮件/短信', | ||||
| @@ -99,7 +101,7 @@ export default { | |||||
| width:"宽度", | width:"宽度", | ||||
| FontColor:"字体颜色", | FontColor:"字体颜色", | ||||
| backgroundColor:"背景颜色", | backgroundColor:"背景颜色", | ||||
| SubtitlesTip:"请在文案区编辑您的正文", | |||||
| SubtitlesTip:"字幕将在此处展示", | |||||
| }, | }, | ||||
| // 我的视频 | // 我的视频 | ||||
| @@ -135,6 +137,7 @@ export default { | |||||
| upSharpnessTips:'如果对预览效果满意,您可以点击此处来提升视频清晰度~', | upSharpnessTips:'如果对预览效果满意,您可以点击此处来提升视频清晰度~', | ||||
| freeUserTip:'免费用户生成视频时长限制为1分钟', | freeUserTip:'免费用户生成视频时长限制为1分钟', | ||||
| hasNewVideo:'您有新的视频状态!', | hasNewVideo:'您有新的视频状态!', | ||||
| reset:"重置", | |||||
| }, | }, | ||||
| // 个人账户 | // 个人账户 | ||||
| @@ -32,6 +32,7 @@ div { | |||||
| text-align: center; | text-align: center; | ||||
| border: 1px solid #000; | border: 1px solid #000; | ||||
| border-radius: 5px; | border-radius: 5px; | ||||
| cursor: pointer; | |||||
| } | } | ||||
| p { | p { | ||||
| @@ -36,10 +36,10 @@ | |||||
| width="30" | width="30" | ||||
| /></div> | /></div> | ||||
| <!-- X轴 --> | <!-- X轴 --> | ||||
| <div class="item" style="">{{$t('createVideo.XAxis')}}: <el-input | |||||
| <!-- <div class="item" style="">{{$t('createVideo.XAxis')}}: <el-input | |||||
| v-model="SubtitlesLeft" | v-model="SubtitlesLeft" | ||||
| width="30" | width="30" | ||||
| /></div> | |||||
| /></div> --> | |||||
| <!-- 单双行 --> | <!-- 单双行 --> | ||||
| <div v-show="false" class="item" >{{$t('createVideo.rows')}}: <el-switch | <div v-show="false" class="item" >{{$t('createVideo.rows')}}: <el-switch | ||||
| v-model="SubtitlesRow" | v-model="SubtitlesRow" | ||||
| @@ -79,7 +79,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <!-- 背景色 --> | <!-- 背景色 --> | ||||
| <div class="item"> <el-tooltip | |||||
| <!-- <div class="item"> <el-tooltip | |||||
| class="box-item" | class="box-item" | ||||
| effect="dark" | effect="dark" | ||||
| :content="$t('createVideo.backgroundColor')" | :content="$t('createVideo.backgroundColor')" | ||||
| @@ -93,21 +93,21 @@ | |||||
| <div v-for="item in colorList" :style="{backgroundColor: item}" class="selectColor-item" @click="SubtitlesBgc=item"></div> | <div v-for="item in colorList" :style="{backgroundColor: item}" class="selectColor-item" @click="SubtitlesBgc=item"></div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | |||||
| </div> --> | |||||
| </div> | </div> | ||||
| </el-row> | </el-row> | ||||
| <!-- 中间展示大图 --> | <!-- 中间展示大图 --> | ||||
| <div ref="canvasImgBox" class="bigImg inmiddle"> | <div ref="canvasImgBox" class="bigImg inmiddle"> | ||||
| <img id="canvasImg" :src="imgUrl" alt="" class="" /> | |||||
| <div id="SubtitlesBox" ref="SubtitlesBox" @mousedown="handleDrag" :style="SubtitlesStyle" v-if="SubtitlesSwitch" class="SubtitlesBox"> | |||||
| <img ref="ImgBox" id="canvasImg" :src="imgUrl" alt="" class="" /> | |||||
| <div id="SubtitlesBox" ref="SubtitlesBox" @mousedown="handleDrag" :style="SubtitlesStyle" v-if="SubtitlesSwitch" class="SubtitlesBox"> | |||||
| <div class="SubtitlesTip">{{$t('createVideo.SubtitlesTip')}}</div> | <div class="SubtitlesTip">{{$t('createVideo.SubtitlesTip')}}</div> | ||||
| <div v-show="SubtitlesRow" class="SubtitlesTip">{{$t('createVideo.SubtitlesTip')}}</div> | <div v-show="SubtitlesRow" class="SubtitlesTip">{{$t('createVideo.SubtitlesTip')}}</div> | ||||
| <div class="stretchBox box1"></div> | <div class="stretchBox box1"></div> | ||||
| <div class="stretchBox box2"></div> | <div class="stretchBox box2"></div> | ||||
| <div class="stretchBox box3"></div> | <div class="stretchBox box3"></div> | ||||
| <div class="stretchBox box4" @mousedown="startResize2" | |||||
| <div v-show="false" class="stretchBox box4" @mousedown="startResize2" | |||||
| ></div> | ></div> | ||||
| <div class="stretchBox box5" @mousedown="startResize" | |||||
| <div v-show="false" class="stretchBox box5" @mousedown="startResize" | |||||
| ></div> | ></div> | ||||
| <div class="stretchBox box6"></div> | <div class="stretchBox box6"></div> | ||||
| <div class="stretchBox box7"></div> | <div class="stretchBox box7"></div> | ||||
| @@ -1462,13 +1462,14 @@ async function uploadProgress(videoId, isSaveVideo) { | |||||
| subtitleParams:{ | subtitleParams:{ | ||||
| Fontname:SubtitlesFontFamily.value, //字体名字 | Fontname:SubtitlesFontFamily.value, //字体名字 | ||||
| Fontsize:SubtitlesSize.value, //字体大小 | Fontsize:SubtitlesSize.value, //字体大小 | ||||
| Boxwidth:SubtitlesWidth.value, //字幕宽 | |||||
| Boxheight:Number(SubtitlesSize.value+10), //字幕高 | |||||
| // Boxwidth:SubtitlesWidth.value, //字幕宽 | |||||
| // Boxheight:Number(SubtitlesSize.value+10), //字幕高 | |||||
| // Boxheight:SubtitlesBox.value.style.height, | // Boxheight:SubtitlesBox.value.style.height, | ||||
| Marginl:SubtitlesLeft.value, //左边距 | |||||
| Marginy:SubtitlesTop.value, //上边距 | |||||
| // Marginl:SubtitlesLeft.value, //左边距 | |||||
| Marginv:SubtitlesTop.value, //上边距 | |||||
| SecondaryColour:rgbaToBgra(SubtitlesColor.value), //字色 | SecondaryColour:rgbaToBgra(SubtitlesColor.value), //字色 | ||||
| OutlineColour:rgbaToBgra(SubtitlesBgc.value), //背景色 | |||||
| // OutlineColour:rgbaToBgra(SubtitlesBgc.value), //背景色 | |||||
| Aignment:6 | |||||
| } | } | ||||
| }; | }; | ||||
| try { | try { | ||||
| @@ -1546,13 +1547,14 @@ async function saveOrUpdate(index, isSaveVideo) { | |||||
| subtitleParams:JSON.stringify({ | subtitleParams:JSON.stringify({ | ||||
| Fontname:SubtitlesFontFamily.value, //字体名字 | Fontname:SubtitlesFontFamily.value, //字体名字 | ||||
| Fontsize:SubtitlesSize.value, //字体大小 | Fontsize:SubtitlesSize.value, //字体大小 | ||||
| Boxwidth:SubtitlesWidth.value, //字幕宽 | |||||
| Boxheight:Number(SubtitlesSize.value+10), //字幕高 | |||||
| // Boxwidth:SubtitlesWidth.value, //字幕宽 | |||||
| // Boxheight:Number(SubtitlesSize.value+10), //字幕高 | |||||
| // Boxheight:SubtitlesBox.value.style.height, | // Boxheight:SubtitlesBox.value.style.height, | ||||
| Marginl:SubtitlesLeft.value, //左边距 | |||||
| Marginy:SubtitlesTop.value, //上边距 | |||||
| // Marginl:SubtitlesLeft.value, //左边距 | |||||
| Marginv:SubtitlesTop.value, //上边距 | |||||
| SecondaryColour:rgbaToBgra(SubtitlesColor.value), //字色 | SecondaryColour:rgbaToBgra(SubtitlesColor.value), //字色 | ||||
| OutlineColour:rgbaToBgra(SubtitlesBgc.value), //背景色 | |||||
| // OutlineColour:rgbaToBgra(SubtitlesBgc.value), //背景色 | |||||
| Aignment:6 | |||||
| }) | }) | ||||
| }; | }; | ||||
| if (index == 1) { | if (index == 1) { | ||||
| @@ -1794,15 +1796,15 @@ async function findById(findId) { | |||||
| console.log(subtitleParamsToObj); | console.log(subtitleParamsToObj); | ||||
| SubtitlesFontFamily.value=subtitleParamsToObj.Fontname | SubtitlesFontFamily.value=subtitleParamsToObj.Fontname | ||||
| SubtitlesSize.value=subtitleParamsToObj.Fontsize | SubtitlesSize.value=subtitleParamsToObj.Fontsize | ||||
| SubtitlesWidth.value=subtitleParamsToObj.Boxwidth | |||||
| SubtitlesSize.value=Number(subtitleParamsToObj.Boxheight-10) | |||||
| // SubtitlesWidth.value=subtitleParamsToObj.Boxwidth | |||||
| // SubtitlesSize.value=Number(subtitleParamsToObj.Boxheight-10) | |||||
| // Boxheight:SubtitlesBox.value.style.height, | // Boxheight:SubtitlesBox.value.style.height, | ||||
| SubtitlesLeft.value=subtitleParamsToObj.Marginl | |||||
| SubtitlesTop.value=subtitleParamsToObj.Marginy | |||||
| // SubtitlesLeft.value=subtitleParamsToObj.Marginl | |||||
| SubtitlesTop.value=subtitleParamsToObj.Marginv | |||||
| SubtitlesColor.value=BgraToRgba(subtitleParamsToObj.SecondaryColour | SubtitlesColor.value=BgraToRgba(subtitleParamsToObj.SecondaryColour | ||||
| ) | ) | ||||
| SubtitlesBgc.value=BgraToRgba(subtitleParamsToObj.OutlineColour | |||||
| ) | |||||
| // SubtitlesBgc.value=BgraToRgba(subtitleParamsToObj.OutlineColour | |||||
| // ) | |||||
| } | } | ||||
| //#endregion ---------------------------- | //#endregion ---------------------------- | ||||
| @@ -2074,6 +2076,7 @@ function copyAnswerContent() { | |||||
| //#region 字幕部分 | //#region 字幕部分 | ||||
| const canvasImgBox = ref(null); // 图片外盒子dom | const canvasImgBox = ref(null); // 图片外盒子dom | ||||
| const ImgBox = ref(null); // 图片渲染dom | |||||
| const SubtitlesBox = ref(null); // 字幕盒子dom | const SubtitlesBox = ref(null); // 字幕盒子dom | ||||
| const SubtitlesSwitch = ref(false); // 字幕开关 | const SubtitlesSwitch = ref(false); // 字幕开关 | ||||
| const SubtitlesWidth = ref(250); // 字幕宽度 | const SubtitlesWidth = ref(250); // 字幕宽度 | ||||
| @@ -2085,11 +2088,11 @@ const canvasImgBox = ref(null); // 图片外盒子dom | |||||
| const isRotating = ref(false); //控制是否旋转 | const isRotating = ref(false); //控制是否旋转 | ||||
| const rotationStartAngle = ref(0); //旋转中间件 | const rotationStartAngle = ref(0); //旋转中间件 | ||||
| const SubtitlesAngle = ref(20); //旋转度数 | const SubtitlesAngle = ref(20); //旋转度数 | ||||
| const lastMouseAngle = ref(0); | |||||
| const initialRotation = ref(0); | |||||
| const imgWitdt=ref(0); //图片宽度 | |||||
| const imgHeight=ref(0); //图片高度 | |||||
| const SubtitlesColor = ref('#ff0000'); // 字幕字色 | const SubtitlesColor = ref('#ff0000'); // 字幕字色 | ||||
| // const SubtitlesColorBGRA = ref(rgbaToBgra(SubtitlesColor.value)); // 字幕字色 | // const SubtitlesColorBGRA = ref(rgbaToBgra(SubtitlesColor.value)); // 字幕字色 | ||||
| const SubtitlesBgc = ref('#ffffff'); // 字幕背景色 | |||||
| const SubtitlesBgc = ref('rgba(0,0,0,0)'); // 字幕背景色 | |||||
| // const SubtitlesBgcBGRA = ref(rgbaToBgra(SubtitlesBgc.value)); // 字幕背景色 | // const SubtitlesBgcBGRA = ref(rgbaToBgra(SubtitlesBgc.value)); // 字幕背景色 | ||||
| // 调色板 | // 调色板 | ||||
| const colorList = [ | const colorList = [ | ||||
| @@ -2162,14 +2165,17 @@ function BgraToRgba(hexColor) { | |||||
| const convertedColor = `#${red}${green}${blue}`; | const convertedColor = `#${red}${green}${blue}`; | ||||
| return convertedColor; | return convertedColor; | ||||
| } | } | ||||
| // watch( | |||||
| // () => SubtitlesColor, | |||||
| // (newValue, oldValue) => { | |||||
| // SubtitlesColorBGRA.value = ref(rgbaToBgra(SubtitlesColor.value)); // 字幕字色 | |||||
| // }, | |||||
| // { deep: true } | |||||
| // ); | |||||
| watch( | |||||
| () => imgUrl, | |||||
| async(newValue, oldValue) => { | |||||
| await nextTick() | |||||
| console.log(ImgBox.value.width); | |||||
| imgWitdt.value=ImgBox.value.width | |||||
| imgHeight.value=ImgBox.value.height | |||||
| SubtitlesWidth.value=imgWitdt.value*0.7 | |||||
| }, | |||||
| { deep: true } | |||||
| ); | |||||
| // watch( | // watch( | ||||
| // () => SubtitlesBgc, | // () => SubtitlesBgc, | ||||
| // (newValue, oldValue) => { | // (newValue, oldValue) => { | ||||
| @@ -2182,7 +2188,8 @@ function BgraToRgba(hexColor) { | |||||
| return { | return { | ||||
| width: SubtitlesWidth.value + 'px', | width: SubtitlesWidth.value + 'px', | ||||
| top: SubtitlesTop.value + 'px', | top: SubtitlesTop.value + 'px', | ||||
| left: SubtitlesLeft.value + 'px', | |||||
| // left: SubtitlesLeft.value + 'px', | |||||
| left: (imgWitdt.value/2)-(SubtitlesWidth.value/2) + 'px', | |||||
| fontSize: SubtitlesSize.value + 'px', | fontSize: SubtitlesSize.value + 'px', | ||||
| fontFamily: SubtitlesFontFamily.value, | fontFamily: SubtitlesFontFamily.value, | ||||
| color: SubtitlesColor.value, | color: SubtitlesColor.value, | ||||
| @@ -2206,6 +2213,12 @@ function BgraToRgba(hexColor) { | |||||
| const newY = event.clientY - offsetY; | const newY = event.clientY - offsetY; | ||||
| SubtitlesLeft.value = newX; | SubtitlesLeft.value = newX; | ||||
| SubtitlesTop.value = newY; | SubtitlesTop.value = newY; | ||||
| if(SubtitlesTop.value<0) { | |||||
| SubtitlesTop.value = 0; | |||||
| } | |||||
| if(SubtitlesTop.value>imgHeight.value-25) { | |||||
| SubtitlesTop.value = imgHeight.value-25 | |||||
| } | |||||
| }; | }; | ||||
| const onMouseUp = () => { | const onMouseUp = () => { | ||||
| @@ -9,7 +9,7 @@ | |||||
| loop | loop | ||||
| ></video> | ></video> | ||||
| </div> | </div> | ||||
| <!-- 正常登录 --> | |||||
| <!-- 正常登录 1--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 1" | v-if="isLogin == 1" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -126,7 +126,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </el-form> | </el-form> | ||||
| <!-- 注册账户 --> | |||||
| <!-- 注册账户 2--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 2" | v-if="isLogin == 2" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -240,7 +240,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </el-form> | </el-form> | ||||
| <!-- 忘记密码 --> | |||||
| <!-- 忘记密码 3--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 3" | v-if="isLogin == 3" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -308,7 +308,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </el-form> | </el-form> | ||||
| <!-- 重置密码 --> | |||||
| <!-- 重置密码 4--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 4" | v-if="isLogin == 4" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -429,7 +429,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </el-form> | </el-form> | ||||
| <!-- 忘记密码邮件返回 --> | |||||
| <!-- 忘记密码邮件返回 5--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 5" | v-if="isLogin == 5" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -559,7 +559,7 @@ | |||||
| name="email" | name="email" | ||||
| /> | /> | ||||
| <span class="captcha"> | <span class="captcha"> | ||||
| <div v-if="SMSTimeOut == 0" @click="" class="sendSMSBtn"> | |||||
| <div v-if="SMSTimeOut == 0" @click="getSMSToLogin" class="sendSMSBtn"> | |||||
| {{ $t("login.sendSMS") }} | {{ $t("login.sendSMS") }} | ||||
| </div> | </div> | ||||
| <div v-if="SMSTimeOut != 0" class="sendSMSBtn">{{ SMSTimeOut }}s</div> | <div v-if="SMSTimeOut != 0" class="sendSMSBtn">{{ SMSTimeOut }}s</div> | ||||
| @@ -598,7 +598,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </el-form> | </el-form> | ||||
| <!-- 忘记密码手机号 --> | |||||
| <!-- 忘记密码手机号 7--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 7" | v-if="isLogin == 7" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -633,7 +633,11 @@ | |||||
| name="password" | name="password" | ||||
| /> | /> | ||||
| <span class="captcha"> | <span class="captcha"> | ||||
| <div v-if="SMSTimeOut == 0" @click="" class="sendSMSBtn"> | |||||
| <div | |||||
| v-if="SMSTimeOut == 0" | |||||
| @click="getSMSToLogin(7)" | |||||
| class="sendSMSBtn" | |||||
| > | |||||
| {{ $t("login.sendSMS") }} | {{ $t("login.sendSMS") }} | ||||
| </div> | </div> | ||||
| <div v-if="SMSTimeOut != 0" class="sendSMSBtn">{{ SMSTimeOut }}s</div> | <div v-if="SMSTimeOut != 0" class="sendSMSBtn">{{ SMSTimeOut }}s</div> | ||||
| @@ -660,13 +664,13 @@ | |||||
| </span> | </span> | ||||
| </el-form-item> | </el-form-item> | ||||
| <el-form-item prop="confirmPassword4"> | |||||
| <el-form-item prop="confirmPassword7"> | |||||
| <span class="text-white"> | <span class="text-white"> | ||||
| <svg-icon icon-class="password" /> | <svg-icon icon-class="password" /> | ||||
| </span> | </span> | ||||
| <el-input | <el-input | ||||
| class="flex-1" | class="flex-1" | ||||
| v-model="loginData7.confirmPassword4" | |||||
| v-model="loginData7.confirmPassword7" | |||||
| :placeholder="$t('login.confirmNewPwd')" | :placeholder="$t('login.confirmNewPwd')" | ||||
| :type="passwordVisible === false ? 'password' : 'input'" | :type="passwordVisible === false ? 'password' : 'input'" | ||||
| size="large" | size="large" | ||||
| @@ -685,7 +689,7 @@ | |||||
| :loading="loading" | :loading="loading" | ||||
| type="primary" | type="primary" | ||||
| class="loginBtn" | class="loginBtn" | ||||
| @click.prevent="resetPwdForm" | |||||
| @click.prevent="handleResetPwdByPhone" | |||||
| > | > | ||||
| <el-icon | <el-icon | ||||
| v-if="isRegistering" | v-if="isRegistering" | ||||
| @@ -710,16 +714,16 @@ | |||||
| <div style="margin-bottom: 5px" @click="loginFlag(1)"> | <div style="margin-bottom: 5px" @click="loginFlag(1)"> | ||||
| <span class="join">{{ $t("login.signIn") }}</span> | <span class="join">{{ $t("login.signIn") }}</span> | ||||
| </div> | </div> | ||||
| <div> | |||||
| <!-- <div> | |||||
| <span | <span | ||||
| class="join" | class="join" | ||||
| @click="router.push({ path: 'createVideo', params: {} })" | @click="router.push({ path: 'createVideo', params: {} })" | ||||
| >{{ $t("login.gohome") }}</span | >{{ $t("login.gohome") }}</span | ||||
| > | > | ||||
| </div> | |||||
| </div> --> | |||||
| </div> | </div> | ||||
| </el-form> | </el-form> | ||||
| <!-- 手机号登录 --> | |||||
| <!-- 手机号登录 8--> | |||||
| <el-form | <el-form | ||||
| v-if="isLogin == 8" | v-if="isLogin == 8" | ||||
| ref="loginFormRef" | ref="loginFormRef" | ||||
| @@ -729,7 +733,7 @@ | |||||
| > | > | ||||
| <div class="flex text-white items-center"> | <div class="flex text-white items-center"> | ||||
| <span class="flex-1 text-center">{{ | <span class="flex-1 text-center">{{ | ||||
| $t("login.title") + " " + $t("login.register") | |||||
| $t("login.title") + " " + $t("login.login") | |||||
| }}</span> | }}</span> | ||||
| <el-tooltip | <el-tooltip | ||||
| class="box-item" | class="box-item" | ||||
| @@ -766,7 +770,11 @@ | |||||
| name="email" | name="email" | ||||
| /> | /> | ||||
| <span class="captcha"> | <span class="captcha"> | ||||
| <div v-if="SMSTimeOut == 0" @click="" class="sendSMSBtn"> | |||||
| <div | |||||
| v-if="SMSTimeOut == 0" | |||||
| @click="getSMSToLogin(8)" | |||||
| class="sendSMSBtn" | |||||
| > | |||||
| {{ $t("login.sendSMS") }} | {{ $t("login.sendSMS") }} | ||||
| </div> | </div> | ||||
| <div v-if="SMSTimeOut != 0" class="sendSMSBtn">{{ SMSTimeOut }}s</div> | <div v-if="SMSTimeOut != 0" class="sendSMSBtn">{{ SMSTimeOut }}s</div> | ||||
| @@ -778,7 +786,7 @@ | |||||
| :loading="loading" | :loading="loading" | ||||
| type="primary" | type="primary" | ||||
| class="loginBtn" | class="loginBtn" | ||||
| @click.prevent="handleRegister" | |||||
| @click.prevent="handleLoginByPhone" | |||||
| > | > | ||||
| <el-icon | <el-icon | ||||
| v-if="isRegistering" | v-if="isRegistering" | ||||
| @@ -796,7 +804,7 @@ | |||||
| ></path> | ></path> | ||||
| </svg> | </svg> | ||||
| </el-icon> | </el-icon> | ||||
| {{ $t("login.registerBtn") }} | |||||
| {{ $t("login.loginBtn") }} | |||||
| </el-button> | </el-button> | ||||
| <div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'"> | <div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'"> | ||||
| @@ -819,6 +827,11 @@ import { | |||||
| sendUpdPwdEmailApi, | sendUpdPwdEmailApi, | ||||
| resetPwdEmailApi, | resetPwdEmailApi, | ||||
| resetPwdFormApi, | resetPwdFormApi, | ||||
| loginByPhoneApi, | |||||
| registerByPhoneApi, | |||||
| getSMSToLoginApi, | |||||
| loginByMSMApi, | |||||
| doUpdPassApi, | |||||
| } from "@/apis/login"; | } from "@/apis/login"; | ||||
| import axios from "axios"; | import axios from "axios"; | ||||
| // 状态管理依赖 | // 状态管理依赖 | ||||
| @@ -897,7 +910,7 @@ const loginData6 = ref({ | |||||
| const loginData7 = ref({ | const loginData7 = ref({ | ||||
| SMScode: "", | SMScode: "", | ||||
| password: "", | password: "", | ||||
| confirmPassword4: "", | |||||
| confirmPassword7: "", | |||||
| }); | }); | ||||
| const loginData8 = ref({ | const loginData8 = ref({ | ||||
| phone: "", | phone: "", | ||||
| @@ -933,6 +946,9 @@ const loginRules = ref({ | |||||
| confirmPassword4: [ | confirmPassword4: [ | ||||
| { required: true, trigger: "blur", validator: confirmPasswordValidator4 }, | { required: true, trigger: "blur", validator: confirmPasswordValidator4 }, | ||||
| ], | ], | ||||
| confirmPassword7: [ | |||||
| { required: true, trigger: "blur", validator: confirmPasswordValidator7 }, | |||||
| ], | |||||
| newPwd: [{ required: true, trigger: "blur", validator: passwordValidator }], | newPwd: [{ required: true, trigger: "blur", validator: passwordValidator }], | ||||
| confirmNewPwd: [ | confirmNewPwd: [ | ||||
| { required: true, trigger: "blur", validator: confirmPasswordValidator5 }, | { required: true, trigger: "blur", validator: confirmPasswordValidator5 }, | ||||
| @@ -968,13 +984,23 @@ function loginFlag(type: number) { | |||||
| * @param:email,password | * @param:email,password | ||||
| */ | */ | ||||
| function saveInfo(data: any) { | function saveInfo(data: any) { | ||||
| const userInfo = JSON.stringify({ | |||||
| email: data.email, | |||||
| password: data.password, | |||||
| }); | |||||
| // 将用户信息加密 | |||||
| const enCodeUserInfo = encodeURI(userInfo); | |||||
| localStorage.setItem("userInfo", enCodeUserInfo); | |||||
| if (data.email) { | |||||
| const userInfo = JSON.stringify({ | |||||
| email: data.email, | |||||
| password: data.password, | |||||
| }); | |||||
| // 将用户信息加密 | |||||
| const enCodeUserInfo = encodeURI(userInfo); | |||||
| localStorage.setItem("userInfo", enCodeUserInfo); | |||||
| } else if (data.phone) { | |||||
| const userInfo = JSON.stringify({ | |||||
| email: data.phone, | |||||
| password: data.password, | |||||
| }); | |||||
| // 将用户信息加密 | |||||
| const enCodeUserInfo = encodeURI(userInfo); | |||||
| localStorage.setItem("userInfo", enCodeUserInfo); | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -1086,6 +1112,24 @@ function confirmPasswordValidator4(rule: any, value: any, callback: any) { | |||||
| callback(); | callback(); | ||||
| } | } | ||||
| } | } | ||||
| function confirmPasswordValidator7(rule: any, value: any, callback: any) { | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" | |||||
| ? "确认密码不能为空!" | |||||
| : "The Confirm password can not be empty"; | |||||
| const msg2 = | |||||
| currentLan.value == "zh-cn" | |||||
| ? "两次输入密码不一致!" | |||||
| : "Confirm Passward entered should match"; | |||||
| if (!value) { | |||||
| callback(new Error(msg)); | |||||
| } else if (value !== loginData7.value.password) { | |||||
| callback(new Error(msg2)); | |||||
| } else { | |||||
| callback(); | |||||
| } | |||||
| } | |||||
| function confirmPasswordValidator5(rule: any, value: any, callback: any) { | function confirmPasswordValidator5(rule: any, value: any, callback: any) { | ||||
| const msg = | const msg = | ||||
| currentLan.value == "zh-cn" | currentLan.value == "zh-cn" | ||||
| @@ -1115,8 +1159,62 @@ function handleLogin() { | |||||
| password: loginData.value.password, | password: loginData.value.password, | ||||
| code: loginData.value.verifyCode, | code: loginData.value.verifyCode, | ||||
| }; | }; | ||||
| const emailReg = | |||||
| /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; | |||||
| const phoneReg = /^[1][3-9]\d{9}$/; | |||||
| // 如果是邮箱 | |||||
| if (emailReg.test(data.email)) { | |||||
| isLogining.value = true; | |||||
| doLogin(data); | |||||
| // 如果是手机 | |||||
| } else if (phoneReg.test(data.email)) { | |||||
| const data2 = { | |||||
| phone: loginData.value.email, | |||||
| password: loginData.value.password, | |||||
| code: loginData.value.verifyCode, | |||||
| }; | |||||
| isRegistering.value = true; | |||||
| doLoginByPhone(data2); | |||||
| } else { | |||||
| } | |||||
| } else { | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" | |||||
| ? "请检查信息格式!" | |||||
| : "Please check the format !"; | |||||
| ElMessage.error(msg); | |||||
| return; | |||||
| } | |||||
| }); | |||||
| } | |||||
| //#region 手机号+密码登录 | |||||
| function doLoginByPhone(data: any) { | |||||
| loginFormRef.value.validate(async (val: any) => { | |||||
| if (val) { | |||||
| isLogining.value = true; | isLogining.value = true; | ||||
| doLogin(data); | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" ? "登录成功!" : "Welcome to Metavatar!"; | |||||
| await loginByPhoneApi(data) | |||||
| .then((res: any) => { | |||||
| console.log(res, "res"); | |||||
| localStorage.setItem("AccessToken", res.data.token); | |||||
| ElMessage.success(msg); | |||||
| router.push("/createVideo"); | |||||
| // 注册成功将账号密码保存到本地 | |||||
| saveInfo(data); | |||||
| isLogining.value = false; | |||||
| userInfoPinia.getUserInfo(); //保存邮箱到pinia | |||||
| }) | |||||
| .catch((err: any) => { | |||||
| console.log(err, "err"); | |||||
| // 登录失败刷新验证码 | |||||
| // getCode(); | |||||
| ElMessage.error(err.message); | |||||
| isLogining.value = false; | |||||
| if (err.code == 2112) { | |||||
| // reSendEmail(data.email); | |||||
| } | |||||
| }); | |||||
| } else { | } else { | ||||
| const msg = | const msg = | ||||
| currentLan.value == "zh-cn" | currentLan.value == "zh-cn" | ||||
| @@ -1127,7 +1225,6 @@ function handleLogin() { | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| // 注册行为 | // 注册行为 | ||||
| function handleRegister() { | function handleRegister() { | ||||
| loginFormRef.value.validate((val: any) => { | loginFormRef.value.validate((val: any) => { | ||||
| @@ -1145,8 +1242,12 @@ function handleRegister() { | |||||
| doRegister(data); | doRegister(data); | ||||
| // 如果是手机 | // 如果是手机 | ||||
| } else if (phoneReg.test(data.email)) { | } else if (phoneReg.test(data.email)) { | ||||
| const data2 = { | |||||
| phone: loginData2.value.email, | |||||
| password: loginData2.value.password, | |||||
| }; | |||||
| isRegistering.value = true; | isRegistering.value = true; | ||||
| doRegisterByPhone(data); | |||||
| doRegisterByPhone(data2); | |||||
| } else { | } else { | ||||
| } | } | ||||
| } else { | } else { | ||||
| @@ -1160,6 +1261,93 @@ function handleRegister() { | |||||
| }); | }); | ||||
| } | } | ||||
| //#region 手机号+SMS登录 | |||||
| function handleLoginByPhone() { | |||||
| loginFormRef.value.validate(async (val: any) => { | |||||
| if (val) { | |||||
| const data = { | |||||
| phone: loginData8.value.phone, | |||||
| code: loginData8.value.SMScode, | |||||
| }; | |||||
| isLogining.value = true; | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" ? "登录成功!" : "Welcome to Metavatar!"; | |||||
| await loginByMSMApi(data) | |||||
| .then((res: any) => { | |||||
| console.log(res, "res"); | |||||
| localStorage.setItem("AccessToken", res.data.token); | |||||
| ElMessage.success(msg); | |||||
| router.push("/createVideo"); | |||||
| // 注册成功将账号密码保存到本地 | |||||
| saveInfo(data); | |||||
| isLogining.value = false; | |||||
| userInfoPinia.getUserInfo(); //保存邮箱到pinia | |||||
| }) | |||||
| .catch((err: any) => { | |||||
| console.log(err, "err"); | |||||
| // 登录失败刷新验证码 | |||||
| // getCode(); | |||||
| ElMessage.error(err.message); | |||||
| isLogining.value = false; | |||||
| if (err.code == 2112) { | |||||
| // reSendEmail(data.email); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" | |||||
| ? "请检查信息格式!" | |||||
| : "Please check the format !"; | |||||
| ElMessage.error(msg); | |||||
| return; | |||||
| } | |||||
| }); | |||||
| } | |||||
| //#endregion | |||||
| //#region 手机号忘记密码 | |||||
| function handleResetPwdByPhone() { | |||||
| loginFormRef.value.validate(async (val: any) => { | |||||
| if (val) { | |||||
| const data = { | |||||
| phone: loginData3.value.email, | |||||
| code: loginData7.value.SMScode, | |||||
| pwd: loginData7.value.password, | |||||
| }; | |||||
| isLogining.value = true; | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" | |||||
| ? "密码修改成功!" | |||||
| : "Password changed successfully!"; | |||||
| await doUpdPassApi(data) | |||||
| .then((res: any) => { | |||||
| console.log(res, "res"); | |||||
| isLogin.value = 1; | |||||
| ElMessage.success(msg); | |||||
| }) | |||||
| .catch((err: any) => { | |||||
| console.log(err, "err"); | |||||
| // 登录失败刷新验证码 | |||||
| // getCode(); | |||||
| ElMessage.error(err.message); | |||||
| isLogining.value = false; | |||||
| if (err.code == 2112) { | |||||
| // reSendEmail(data.email); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| const msg = | |||||
| currentLan.value == "zh-cn" | |||||
| ? "请检查信息格式!" | |||||
| : "Please check the format !"; | |||||
| ElMessage.error(msg); | |||||
| return; | |||||
| } | |||||
| }); | |||||
| } | |||||
| //#endregion | |||||
| //#region 忘记密码 表单3 | //#region 忘记密码 表单3 | ||||
| function sendEmailforgetPwd() { | function sendEmailforgetPwd() { | ||||
| const emailReg = | const emailReg = | ||||
| @@ -1167,6 +1355,7 @@ function sendEmailforgetPwd() { | |||||
| const phoneReg = /^[1][3-9]\d{9}$/; | const phoneReg = /^[1][3-9]\d{9}$/; | ||||
| if (phoneReg.test(loginData3.value.email)) { | if (phoneReg.test(loginData3.value.email)) { | ||||
| isLogin.value = 7; | isLogin.value = 7; | ||||
| getSMSToLogin(3); | |||||
| return; | return; | ||||
| } else if (emailReg.test(loginData3.value.email)) { | } else if (emailReg.test(loginData3.value.email)) { | ||||
| const msg = | const msg = | ||||
| @@ -1303,14 +1492,18 @@ async function doRegister(data: object) { | |||||
| ElMessage.error(err.message); | ElMessage.error(err.message); | ||||
| }); | }); | ||||
| } | } | ||||
| // 注册时发送手机验证码后 | |||||
| const SMSTimeOut = ref(0); //倒计时 | |||||
| // 手机注册注册时 | |||||
| async function doRegisterByPhone(data: object) { | async function doRegisterByPhone(data: object) { | ||||
| // const msg = | |||||
| // currentLan.value == "zh-cn" | |||||
| // ? "确认短信验证码已发送至您的手机,请注意查收" | |||||
| // : "An SMS has been send to you, Please pay attention to confirm"; | |||||
| const msg = | const msg = | ||||
| currentLan.value == "zh-cn" | currentLan.value == "zh-cn" | ||||
| ? "确认短信验证码已发送至您的手机,请注意查收" | |||||
| : "An SMS has been send to you, Please pay attention to confirm"; | |||||
| await register(data) | |||||
| ? "注册成功!请登录" | |||||
| : "Registered successfully! Please log in"; | |||||
| await registerByPhoneApi(data) | |||||
| .then((res) => { | .then((res) => { | ||||
| SMSTimeOut.value = 60; | SMSTimeOut.value = 60; | ||||
| const timer = setInterval(() => { | const timer = setInterval(() => { | ||||
| @@ -1322,7 +1515,7 @@ async function doRegisterByPhone(data: object) { | |||||
| }, 1000); | }, 1000); | ||||
| console.log(res, "res"); | console.log(res, "res"); | ||||
| isRegistering.value = false; | isRegistering.value = false; | ||||
| isLogin.value = 6; | |||||
| isLogin.value = 1; | |||||
| ElMessage.success(msg); | ElMessage.success(msg); | ||||
| // 注册成功将账号密码保存到本地 | // 注册成功将账号密码保存到本地 | ||||
| saveInfo(data); | saveInfo(data); | ||||
| @@ -1335,6 +1528,48 @@ async function doRegisterByPhone(data: object) { | |||||
| ElMessage.error(err.message); | ElMessage.error(err.message); | ||||
| }); | }); | ||||
| } | } | ||||
| // 发送手机验证码 | |||||
| const SMSTimeOut = ref(0); //倒计时 | |||||
| async function getSMSToLogin(type: any) { | |||||
| let phoneReg = /^[1][3-9]\d{9}$/; | |||||
| const myPhone = ref(""); | |||||
| if (type == 8) { | |||||
| myPhone.value = loginData8.value.phone; | |||||
| } | |||||
| if (type == 3) { | |||||
| myPhone.value = loginData3.value.email; | |||||
| } | |||||
| let phoneValide = phoneReg.test(myPhone.value); | |||||
| if (!phoneValide) { | |||||
| ElMessage.error( | |||||
| currentLan.value == "zh-cn" | |||||
| ? "请输入正确的手机号!" | |||||
| : "Please enter the correct phone number!" | |||||
| ); | |||||
| return; | |||||
| } | |||||
| try { | |||||
| await getSMSToLoginApi(Number(myPhone.value)).then((res) => { | |||||
| ElMessage.success( | |||||
| currentLan.value == "zh-cn" | |||||
| ? "短信验证码发送成功!" | |||||
| : "SMS verification code sent successfully!" | |||||
| ); | |||||
| SMSTimeOut.value = 60; | |||||
| const timer = setInterval(() => { | |||||
| if (SMSTimeOut.value != 0) { | |||||
| SMSTimeOut.value--; | |||||
| } else { | |||||
| clearInterval(timer); | |||||
| } | |||||
| }, 1000); | |||||
| }); | |||||
| } catch (error: any) { | |||||
| ElMessage.error(error.message); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * @description:登录 | * @description:登录 | ||||
| * @param {Object} data | * @param {Object} data | ||||
| @@ -2,8 +2,9 @@ | |||||
| <div class="page"> | <div class="page"> | ||||
| <!-- 搜索框 --> | <!-- 搜索框 --> | ||||
| <div class="top"> | <div class="top"> | ||||
| <div class="flex justify-between" style="width: 300px"> | |||||
| <div class="search"> | |||||
| <el-input | <el-input | ||||
| style="width: 300px" | |||||
| v-model="title" | v-model="title" | ||||
| :placeholder="$t('myCreating.search')" | :placeholder="$t('myCreating.search')" | ||||
| @keyup.enter="searchVideoList" | @keyup.enter="searchVideoList" | ||||
| @@ -11,6 +12,10 @@ | |||||
| <el-icon class="icon-edit" @click="searchVideoList" | <el-icon class="icon-edit" @click="searchVideoList" | ||||
| ><i-ep-search | ><i-ep-search | ||||
| /></el-icon> | /></el-icon> | ||||
| <!-- 重置按钮 --> | |||||
| <div v-show="title" class="resetSearch" @click="resetSearch"> | |||||
| {{ $t("myCreating.reset") }} | |||||
| </div> | |||||
| </div> | </div> | ||||
| <!-- 更新视频提示 --> | <!-- 更新视频提示 --> | ||||
| <div class="hasNewVideoTips" :class="hasNewCreate ? '' : 'hasNew'"> | <div class="hasNewVideoTips" :class="hasNewCreate ? '' : 'hasNew'"> | ||||
| @@ -326,6 +331,11 @@ function handleDownload(itemVideoPlayUrl) { | |||||
| function goCreate() { | function goCreate() { | ||||
| router.push("/createVideo"); | router.push("/createVideo"); | ||||
| } | } | ||||
| function resetSearch(params) { | |||||
| title.value = ""; | |||||
| searchVideoList(); | |||||
| } | |||||
| // 搜索 | // 搜索 | ||||
| function searchVideoList() { | function searchVideoList() { | ||||
| getPhotoVideoList(pageNum.value, pageSize.value, title.value, true); | getPhotoVideoList(pageNum.value, pageSize.value, title.value, true); | ||||
| @@ -511,6 +521,28 @@ onMounted(() => { | |||||
| .top { | .top { | ||||
| position: relative; | position: relative; | ||||
| margin-bottom: 35px; | margin-bottom: 35px; | ||||
| .search { | |||||
| display: flex; | |||||
| justify-content: flex-start; | |||||
| .resetSearch { | |||||
| margin-left: 10px; | |||||
| padding: 0 10px; | |||||
| height: 30px; | |||||
| width: 100px; | |||||
| text-align: center; | |||||
| line-height: 30px; | |||||
| background-color: #000; | |||||
| border: 1px solid #000; | |||||
| border-radius: 15px; | |||||
| cursor: pointer; | |||||
| transition: all 0.3s; | |||||
| color: #fff; | |||||
| &:hover { | |||||
| background-color: #fff; | |||||
| color: #000; | |||||
| } | |||||
| } | |||||
| } | |||||
| .hasNewVideoTips { | .hasNewVideoTips { | ||||
| position: absolute; | position: absolute; | ||||
| top: 0; | top: 0; | ||||