Procházet zdrojové kódy

Merge branch 'dev_YWQ' into dev

dev_YWQ
HolyKnightIX před 1 rokem
rodič
revize
b89da41ca5
18 změnil soubory, kde provedl 5880 přidání a 5715 odebrání
  1. +1
    -1
      .env.development
  2. +1
    -1
      .env.production
  3. +0
    -6
      .env.staging
  4. +2
    -2
      package.json
  5. +23
    -0
      src/apis/login.js
  6. binární
      src/assets/video/loginVideo.mp4
  7. +8
    -1
      src/lang/package/en.ts
  8. +9
    -2
      src/lang/package/zh-cn.ts
  9. +9
    -8
      src/layout/components/Navbar.vue
  10. +47
    -47
      src/permission.ts
  11. +20
    -1
      src/router/index.ts
  12. +65
    -0
      src/utils/request.js
  13. +12
    -12
      src/utils/request.ts
  14. +130
    -72
      src/views/login/index.vue
  15. +2
    -2
      src/views/redirect/index.vue
  16. +15
    -0
      src/views/signSucceed/index.vue
  17. +3
    -5
      vite.config.ts
  18. +5533
    -5555
      yarn.lock

+ 1
- 1
.env.development Zobrazit soubor

@@ -5,4 +5,4 @@ NODE_ENV='development'

VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/dev-api'
VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'

+ 1
- 1
.env.production Zobrazit soubor

@@ -2,4 +2,4 @@

VITE_APP_TITLE = 'Metavatar-PC'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/prod-api'
VITE_APP_BASE_API = 'https://photo.metavatar.cc/C'

+ 0
- 6
.env.staging Zobrazit soubor

@@ -1,6 +0,0 @@
## 模拟环境
NODE_ENV='staging'

VITE_APP_TITLE = 'vue3-element-admin'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/prod--api'

+ 2
- 2
package.json Zobrazit soubor

@@ -27,7 +27,7 @@
"screenfull": "^6.0.0",
"vue": "^3.2.45",
"vue-i18n": "9",
"vue-router": "^4.1.6"
"vue-router": "^4.0.13"
},
"devDependencies": {
"@commitlint/cli": "^17.5.1",
@@ -71,4 +71,4 @@
"repository": "https://gitee.com/youlaiorg/vue3-element-admin.git",
"author": "有来开源组织",
"license": "MIT"
}
}

+ 23
- 0
src/apis/login.js Zobrazit soubor

@@ -0,0 +1,23 @@
import request from "@/utils/request.js";

const baseURL = import.meta.env.VITE_APP_BASE_API

/**
* @description 注册
* @params email,password
* @returns data
*/
export function register(data) {
return request({
url: `/api/user/doRegisterByEmail`,
method: 'post',
data
})
}

/**
* @description:获取图形验证码
* @return: 验证码地址
*/
export const getCodeImgUrl = baseURL + '/api/user/captcha.jpg'


binární
src/assets/video/loginVideo.mp4 Zobrazit soubor


+ 8
- 1
src/lang/package/en.ts Zobrazit soubor

@@ -7,11 +7,15 @@ export default {

// 登录页面国际化
login: {
title: "Metavatar-PC Sign in",
title: "Metavatar-PC",
username: "Username",
password: "Password",
login: "Sign in",
register: "Sign up",
loginBtn: "Sign in",
registerBtn: "Sign up",
verifyCode: "Verify Code",
signIn: "Already got an account? Sign in here",
signUp: "Don't have an account? Sign up here",
forgetPwd: "I forgot my password",
},
@@ -79,4 +83,7 @@ export default {
WeChatPay: "WeChatPay",
Alipay: "Alipay",
},
signSucceed: {
title: "Sign Succeed !",
},
};

+ 9
- 2
src/lang/package/zh-cn.ts Zobrazit soubor

@@ -7,11 +7,15 @@ export default {

// 登录页面国际化
login: {
title: "Metavatar-PC 登录",
title: "Metavatar-PC",
username: "用户名",
password: "密码",
login: "登 录",
login: "登录",
register: "注册",
loginBtn: "登 录",
registerBtn: "注 册",
verifyCode: "验证码",
signIn: "已有账号?现在登录!",
signUp: "没有账号?现在加入!",
forgetPwd: "忘记密码",
},
@@ -79,4 +83,7 @@ export default {
WeChatPay: "微信支付",
Alipay: "支付宝支付",
},
signSucceed: {
title: "注册成功!",
},
};

+ 9
- 8
src/layout/components/Navbar.vue Zobrazit soubor

@@ -123,14 +123,15 @@ function logout() {
confirmButtonText: confirm,
cancelButtonText: cancel,
}).then(() => {
userStore
.logout()
.then(() => {
tagsViewStore.delAllViews();
})
.then(() => {
router.push(`/login?redirect=${route.fullPath}`);
});
router.push(`/login`);
// userStore
// .logout()
// .then(() => {
// tagsViewStore.delAllViews();
// })
// .then(() => {
// router.push(`/login?redirect=${route.fullPath}`);
// });
});
}



+ 47
- 47
src/permission.ts Zobrazit soubor

@@ -11,51 +11,51 @@ const permissionStore = usePermissionStoreHook();
// 白名单路由
const whiteList = ["/login"];

router.beforeEach(async (to, from, next) => {
NProgress.start();
const hasToken = localStorage.getItem("accessToken");
if (hasToken) {
if (to.path === "/login") {
// 如果已登录,跳转首页
next({ path: "/" });
NProgress.done();
} else {
const userStore = useUserStoreHook();
const hasRoles = userStore.roles && userStore.roles.length > 0;
if (hasRoles) {
// 未匹配到任何路由,跳转404
if (to.matched.length === 0) {
from.name ? next({ name: from.name }) : next("/404");
} else {
next();
}
} else {
try {
const { roles } = await userStore.getInfo();
const accessRoutes = await permissionStore.generateRoutes(roles);
accessRoutes.forEach((route) => {
router.addRoute(route);
});
next({ ...to, replace: true });
} catch (error) {
// 移除 token 并跳转登录页
await userStore.resetToken();
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
}
} else {
// 未登录可以访问白名单页面
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
});
// router.beforeEach(async (to, from, next) => {
// NProgress.start();
// const hasToken = localStorage.getItem("accessToken");
// if (hasToken) {
// if (to.path === "/login") {
// // 如果已登录,跳转首页
// next({ path: "/" });
// NProgress.done();
// } else {
// const userStore = useUserStoreHook();
// const hasRoles = userStore.roles && userStore.roles.length > 0;
// if (hasRoles) {
// // 未匹配到任何路由,跳转404
// if (to.matched.length === 0) {
// from.name ? next({ name: from.name }) : next("/404");
// } else {
// next();
// }
// } else {
// try {
// const { roles } = await userStore.getInfo();
// const accessRoutes = await permissionStore.generateRoutes(roles);
// accessRoutes.forEach((route) => {
// router.addRoute(route);
// });
// next({ ...to, replace: true });
// } catch (error) {
// // 移除 token 并跳转登录页
// await userStore.resetToken();
// next(`/login?redirect=${to.path}`);
// NProgress.done();
// }
// }
// }
// } else {
// // 未登录可以访问白名单页面
// if (whiteList.indexOf(to.path) !== -1) {
// next();
// } else {
// next(`/login?redirect=${to.path}`);
// NProgress.done();
// }
// }
// });

router.afterEach(() => {
NProgress.done();
});
// router.afterEach(() => {
// NProgress.done();
// });

+ 20
- 1
src/router/index.ts Zobrazit soubor

@@ -78,7 +78,7 @@ export const constantRoutes: RouteRecordRaw[] = [
},
},
],
},// 修改密码
}, // 修改密码
{
path: "/",
component: Layout,
@@ -116,6 +116,25 @@ export const constantRoutes: RouteRecordRaw[] = [
},
],
},
// 商店
{
path: "/",
component: Layout,
redirect: "/signSucceed",
children: [
{
path: "signSucceed",
component: () => import("@/views/signSucceed/index.vue"),
name: "signSucceed",
meta: {
title: "signSucceed",
icon: "homepage",
affix: true,
name: "signSucceed.title",
},
},
],
},
// {
// path: "/dashboard",
// component: Layout,


+ 65
- 0
src/utils/request.js Zobrazit soubor

@@ -0,0 +1,65 @@
import axios from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'
ElMessage

/**
* @description:发送Axios请求
*/
const request = axios.create({
baseURL: '/api',
timeout: 5000
})

// 请求拦截器
request.interceptors.request.use(
function (config) {
// 给所有授权的请求提供token
const AccessToken = localStorage.getItem("AccessToken")
if (AccessToken) {
config.headers.token = AccessToken
}
return config
},
function (error) {
// 操作
return Promise.reject(error)
}
)

// 响应拦截器
request.interceptors.response.use(
function (response) {
let code = response.data.code
// 当token过期或损坏时
if (code == 1052) {
router.push("/login");
ElMessage.error("登录过期,请重新登录!");
return

// 弹出错误
} else if (code != 200) {
if (code == 1053) {
router.push("/login");
// Toast.fail("请您先登录!");
return

} else if (code == 500) {
ElMessage.error("系统异常,请稍后再试");

// 其他错误
} else {

return Promise.reject(response.data)
}
} else {
// 正常返回
return response.data
}

},
function (error) {
}
)

export default request

+ 12
- 12
src/utils/request.ts Zobrazit soubor

@@ -1,11 +1,11 @@
import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { useUserStoreHook } from '@/store/modules/user';
import axios, { InternalAxiosRequestConfig, AxiosResponse } from "axios";
import { useUserStoreHook } from "@/store/modules/user";

// 创建 axios 实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 50000,
headers: { 'Content-Type': 'application/json;charset=utf-8' }
headers: { "Content-Type": "application/json;charset=utf-8" },
});

// 请求拦截器
@@ -26,7 +26,7 @@ service.interceptors.request.use(
service.interceptors.response.use(
(response: AxiosResponse) => {
const { code, msg } = response.data;
if (code === '00000') {
if (code === "00000") {
return response.data;
}
// 响应数据为二进制流处理(Excel导出)
@@ -34,23 +34,23 @@ service.interceptors.response.use(
return response;
}

ElMessage.error(msg || '系统出错');
return Promise.reject(new Error(msg || 'Error'));
ElMessage.error(msg || "系统出错");
return Promise.reject(new Error(msg || "Error"));
},
(error: any) => {
if (error.response.data) {
const { code, msg } = error.response.data;
// token 过期,重新登录
if (code === 'A0230') {
ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', {
confirmButtonText: '确定',
type: 'warning'
if (code === "A0230") {
ElMessageBox.confirm("当前页面已失效,请重新登录", "提示", {
confirmButtonText: "确定",
type: "warning",
}).then(() => {
localStorage.clear();
window.location.href = '/';
window.location.href = "/";
});
} else {
ElMessage.error(msg || '系统出错');
ElMessage.error(msg || "系统出错");
}
}
return Promise.reject(error.message);


+ 130
- 72
src/views/login/index.vue Zobrazit soubor

@@ -2,7 +2,7 @@
<div class="login-container">
<div class="videoBox">
<video
src="../../assets/video/loginVideo.mp4"
src="https://video.metavatar.cc/sv/4aaaaa5e-18832dbb5e6/4aaaaa5e-18832dbb5e6.mp4"
muted
autoplay
loop
@@ -15,7 +15,9 @@
class="login-form"
>
<div class="flex text-white items-center py-4">
<span class="text-2xl flex-1 text-center">{{ $t("login.title") }}</span>
<span class="text-2xl flex-1 text-center">{{
$t("login.title") + " " + $t("login.login")
}}</span>
<lang-select style="color: #fff" />
</div>

@@ -45,7 +47,7 @@
<el-input
class="flex-1"
v-model="loginData.password"
placeholder="密码"
:placeholder="$t('login.password')"
:type="passwordVisible === false ? 'password' : 'input'"
size="large"
name="password"
@@ -70,13 +72,13 @@
v-model="loginData.verifyCode"
auto-complete="off"
:placeholder="$t('login.verifyCode')"
class="w-[60%]"
class="flex-1"
@keyup.enter="handleLogin"
/>

<div class="captcha">
<img :src="captchaBase64" @click="getCaptcha" />
</div>
<span class="captcha">
<img v-if="showCodeImg" :src="verifyCode" @click="getCode" />
</span>
</el-form-item>

<el-button
@@ -85,11 +87,11 @@
type="primary"
class="loginBtn"
@click.prevent="handleLogin"
>{{ $t("login.login") }}
>{{ $t("login.loginBtn") }}
</el-button>

<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'">
<div style="margin-bottom: 5px">
<div style="margin-bottom: 5px" @click="loginFlag">
<span class="join">{{ $t("login.signUp") }}</span>
</div>
<div>
@@ -105,7 +107,9 @@
class="login-form"
>
<div class="flex text-white items-center py-4">
<span class="text-2xl flex-1 text-center">{{ $t("login.title") }}</span>
<span class="text-2xl flex-1 text-center">{{
$t("login.title") + " " + $t("login.register")
}}</span>
<lang-select style="color: #fff" />
</div>

@@ -135,12 +139,12 @@
<el-input
class="flex-1"
v-model="loginData.password"
placeholder="密码"
:placeholder="$t('login.password')"
:type="passwordVisible === false ? 'password' : 'input'"
size="large"
name="password"
@keyup="checkCapslock"
@keyup.enter="handleLogin"
@keyup.enter="handleRegister"
/>
<span class="mr-2" @click="passwordVisible = !passwordVisible">
<svg-icon
@@ -152,7 +156,7 @@
</el-tooltip>

<!-- 验证码 -->
<el-form-item prop="verifyCode">
<el-form-item v-if="isLogin" prop="verifyCode">
<span class="p-2 text-white">
<svg-icon icon-class="verify_code" />
</span>
@@ -160,13 +164,13 @@
v-model="loginData.verifyCode"
auto-complete="off"
:placeholder="$t('login.verifyCode')"
class="w-[60%]"
@keyup.enter="handleLogin"
class="flex-1"
@keyup.enter="handleRegister"
/>

<div class="captcha">
<img :src="captchaBase64" @click="getCaptcha" />
</div>
<span class="captcha">
<img v-if="showCodeImg" :src="verifyCode" @click="getCode" />
</span>
</el-form-item>

<el-button
@@ -174,13 +178,13 @@
:loading="loading"
type="primary"
class="loginBtn"
@click.prevent="handleLogin"
>{{ $t("login.login") }}
@click.prevent="handleRegister"
>{{ $t("login.registerBtn") }}
</el-button>

<div :class="currentLan == 'zh-cn' ? 'bottomBtnCN' : 'bottomBtnEN'">
<div style="margin-bottom: 5px" @click="loginFlag">
<span class="join">{{ $t("login.signUp") }}</span>
<span class="join">{{ $t("login.signIn") }}</span>
</div>
<div>
<span class="join">{{ $t("login.forgetPwd") }}</span>
@@ -191,16 +195,23 @@
</template>

<script setup lang="ts">
import router from "@/router";
import LangSelect from "@/components/LangSelect/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
import { register, getCodeImgUrl } from "@/apis/login";

import axios from "axios";

// 状态管理依赖
import { useUserStore } from "@/store/modules/user";

// API依赖
import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
import { getCaptchaApi } from "@/api/auth";
import {
LocationQuery,
LocationQueryValue,
useRoute,
useRouter,
} from "vue-router";

import { LoginData } from "@/api/auth/types";

import { useI18n } from "vue-i18n";
@@ -210,7 +221,7 @@ const currentLan = ref(locale);

const userStore = useUserStore();
const route = useRoute();
const router = useRouter();
/**
* 按钮loading
*/
@@ -229,21 +240,23 @@ const passwordVisible = ref(false);
/**
* 验证码图片Base64字符串
*/
const captchaBase64 = ref();
const verifyCode = ref();

/**
* 登录/注册
*/
const isLogin = ref(true);

const showCodeImg = ref(true);
/**
* 登录表单引用
*/
const loginFormRef = ref(ElForm);

const loginData = ref<LoginData>({
username: "admin",
password: "123456",
const loginData = ref({
username: "",
password: "",
verifyCode: "",
});

const loginRules = {
@@ -256,7 +269,8 @@ const loginRules = {
* @description: 切换登录/注册状态
*/
function loginFlag() {
isLogin.value = !isLogin;
isLogin.value = !isLogin.value;
passwordVisible.value = !isLogin.value;
console.log(isLogin.value, "isLogin.value");
}

@@ -279,56 +293,100 @@ function checkCapslock(e: any) {
isCapslock.value = key && key.length === 1 && key >= "A" && key <= "Z";
}

/**
* 获取验证码
*/
function getCaptcha() {
getCaptchaApi().then(({ data }) => {
const { verifyCodeBase64, verifyCodeKey } = data;
loginData.value.verifyCodeKey = verifyCodeKey;
captchaBase64.value = verifyCodeBase64;
function handleLogin() {
router.push("/createVideo");
}

function handleRegister() {
const data = {
email: loginData.value.username,
password: loginData.value.password,
};
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,}))$/;

if (!data.email) {
ElMessage.error("邮箱不能为空!");
return;
}

if (!data.password) {
ElMessage.error("密码不能为空!");
return;
}

if (!emailReg.test(data.email)) {
ElMessage.error("邮箱格式错误!");
return;
}
console.log(data);
doRegister(data);
}

function getCode() {
showCodeImg.value = false;
axios({
url: getCodeImgUrl,
method: "get",
}).then(() => {
verifyCode.value = getCodeImgUrl;
showCodeImg.value = true;
});
}

/**
* 登录
* @description:注册
* @param {type} data
* @return: data
*/
function handleLogin() {
loginFormRef.value.validate((valid: boolean) => {
if (valid) {
loading.value = true;
userStore
.login(loginData.value)
.then(() => {
const query: LocationQuery = route.query;

const redirect = (query.redirect as LocationQueryValue) ?? "/";

const otherQueryParams = Object.keys(query).reduce(
(acc: any, cur: string) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
}
return acc;
},
{}
);

router.push({ path: redirect, query: otherQueryParams });
})
.catch(() => {
// 验证失败,重新生成验证码
getCaptcha();
})
.finally(() => {
loading.value = false;
});
}
});
async function doRegister(data: object) {
try {
const res = await register(data);
console.log(res, "res");
} catch (error) {
console.log(error);
}
}

// /**
// * 登录
// */
// function handleLogin() {
// loginFormRef.value.validate((valid: boolean) => {
// if (valid) {
// loading.value = true;
// userStore
// .login(loginData.value)
// .then(() => {
// const query: LocationQuery = route.query;

// const redirect = (query.redirect as LocationQueryValue) ?? "/";

// const otherQueryParams = Object.keys(query).reduce(
// (acc: any, cur: string) => {
// if (cur !== "redirect") {
// acc[cur] = query[cur];
// }
// return acc;
// },
// {}
// );

// router.push({ path: redirect, query: otherQueryParams });
// })
// .catch(() => {
// // 验证失败,重新生成验证码
// getCaptcha();
// })
// .finally(() => {
// loading.value = false;
// });
// }
// });
// }

onMounted(() => {
getCaptcha();
getCode();
});
</script>



+ 2
- 2
src/views/redirect/index.vue Zobrazit soubor

@@ -3,7 +3,7 @@
</template>

<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router';
import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();
@@ -11,5 +11,5 @@ const router = useRouter();
const { params, query } = route;
const { path } = params;

router.replace({ path: '/' + path, query });
router.replace({ path: "/" + path, query });
</script>

+ 15
- 0
src/views/signSucceed/index.vue Zobrazit soubor

@@ -0,0 +1,15 @@
<template>
<div />
</template>

<script setup lang="ts">
import { useRoute, useRouter } from "vue-router";

const route = useRoute();
const router = useRouter();

const { params, query } = route;
const { path } = params;

console.log(query.ticket, "ticket");
</script>

+ 3
- 5
vite.config.ts Zobrazit soubor

@@ -42,12 +42,10 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
open: true, // 运行是否自动打开浏览器
proxy: {
// 反向代理解决跨域
[env.VITE_APP_BASE_API]: {
target: "http://vapi.youlai.tech", // 线上接口地址
// target: 'http://localhost:8989', // 本地接口地址
"/api": {
target: env.VITE_APP_BASE_API, // 线上接口地址
changeOrigin: true,
rewrite: (path) =>
path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""), // 替换 /dev-api 为 target 接口地址
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},


+ 5533
- 5555
yarn.lock
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


Načítá se…
Zrušit
Uložit