北斗启航科技教育平台登录系统
30
.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# authentication
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
51
index.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" class="w-h-full">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>北斗启航科技教育平台</title>
|
||||||
|
<style>
|
||||||
|
/* 禁止移动端左右滑动事件 */
|
||||||
|
html {
|
||||||
|
touch-action: none;
|
||||||
|
touch-action: pan-y;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="w-h-full">
|
||||||
|
<div id="app" class="w-h-full text-white"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
<script>
|
||||||
|
// 禁止移动端左右滑动事件
|
||||||
|
document.addEventListener("touchmove", function (e) {
|
||||||
|
e.preventDefalut()
|
||||||
|
}, {
|
||||||
|
passive: false
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var startX, startY;
|
||||||
|
document.addEventListener("touchstart", function (e) {
|
||||||
|
|
||||||
|
startX = e.targetTouches[0].pageX;
|
||||||
|
startY = e.targetTouches[0].pageY;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("touchmove", function (e) {
|
||||||
|
|
||||||
|
var moveX = e.targetTouches[0].pageX;
|
||||||
|
var moveY = e.targetTouches[0].pageY;
|
||||||
|
|
||||||
|
if (Math.abs(moveX - startX) > Math.abs(moveY - startY)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
passive: false
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
8
jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
174310
package-lock.json
generated
Normal file
39
package.json
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "authentication",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --port=7011",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview --port=7011"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"authentication": "file:",
|
||||||
|
"axios": "^1.6.8",
|
||||||
|
"core-js": "^3.37.0",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"element-plus": "^2.7.2",
|
||||||
|
"fingerprintjs2": "^2.1.4",
|
||||||
|
"jsencrypt": "^3.3.2",
|
||||||
|
"pinia": "^2.1.7",
|
||||||
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
|
"terser": "^5.31.6",
|
||||||
|
"vue": "^3.4.21",
|
||||||
|
"vue-i18n": "^9.13.1",
|
||||||
|
"vue-qr": "^4.0.9",
|
||||||
|
"vue-router": "^4.3.0",
|
||||||
|
"vue-vercode": "^1.0.6",
|
||||||
|
"vue3-slide-verify": "^1.1.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-legacy": "^5.4.1",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"postcss": "^8.4.38",
|
||||||
|
"tailwindcss": "^3.4.3",
|
||||||
|
"unplugin-auto-import": "^0.17.5",
|
||||||
|
"unplugin-vue-components": "^0.27.0",
|
||||||
|
"vite": "^5.2.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 72 KiB |
9
src/App.vue
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<RouterView />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { RouterView } from "vue-router";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
55
src/api/index.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import router from "../router"
|
||||||
|
|
||||||
|
// 添加请求拦截器
|
||||||
|
axios.interceptors.request.use(function (config) {
|
||||||
|
// 在发送请求之前做些什么
|
||||||
|
if (localStorage.token) {
|
||||||
|
if (config.headers) {
|
||||||
|
config.headers.authorization = localStorage.token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}, function (error) {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加响应拦截器
|
||||||
|
axios.interceptors.response.use(function (response) {
|
||||||
|
// 2xx 范围内的状态码都会触发该函数。
|
||||||
|
// 对响应数据做点什么
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
if ("token" in response.data) {
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
localStorage.setItem("token", response.data.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (response.data.code === 401 || response.data.code === 402) {
|
||||||
|
// alert(response.data.msg);
|
||||||
|
localStorage.clear();
|
||||||
|
router.push({ path: '/login' })
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}, function (error) {
|
||||||
|
// 超出 2xx 范围的状态码都会触发该函数。
|
||||||
|
// 对响应错误做点什么
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
get(url, query = {}) {
|
||||||
|
return axios.get(url, {
|
||||||
|
params: query
|
||||||
|
});
|
||||||
|
},
|
||||||
|
post(url, body) {
|
||||||
|
return axios.post(url, body);
|
||||||
|
},
|
||||||
|
put(url, body) {
|
||||||
|
return axios.put(url, body);
|
||||||
|
},
|
||||||
|
del(url) {
|
||||||
|
return axios.delete(url);
|
||||||
|
},
|
||||||
|
}
|
||||||
30
src/api/url.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
export default {
|
||||||
|
// B端用户登录
|
||||||
|
blogin: "/authentication/blogin",
|
||||||
|
// B端用户忘记密码
|
||||||
|
bforgotPassword: "/authentication/bforgotPassword",
|
||||||
|
// B端用户忘记账号
|
||||||
|
bforgotAccount: "/authentication/bforgotAccount",
|
||||||
|
// C端用户登录
|
||||||
|
clogin: "/authentication/clogin",
|
||||||
|
// C端用户注册
|
||||||
|
cregistered: "/authentication/cregistered",
|
||||||
|
// 检查昵称是否已被使用
|
||||||
|
diffName: "/authentication/diffName",
|
||||||
|
// 检查手机号是否已被使用
|
||||||
|
diffPhone: "/authentication/diffPhone",
|
||||||
|
// 检查邮箱是否已被使用
|
||||||
|
diffEmail: "/authentication/diffEmail",
|
||||||
|
// C端用户忘记密码
|
||||||
|
cforgotPassword: "/authentication/cforgotPassword",
|
||||||
|
// C端用户账号
|
||||||
|
cforgotAccount: "/authentication/cforgotAccount",
|
||||||
|
// 登录状态查询
|
||||||
|
loginStatus: "/authentication/loginStatus",
|
||||||
|
// 竞赛用户登录
|
||||||
|
tlogin: "/authentication/tlogin",
|
||||||
|
// 竞赛用户忘记密码
|
||||||
|
tforgotPassword: "/authentication/tforgotPassword",
|
||||||
|
// 竞赛用户忘记账号
|
||||||
|
tforgotAccount: "/authentication/tforgotAccount",
|
||||||
|
}
|
||||||
BIN
src/assets/favicon.ico
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
src/assets/img/BDThemeLesson.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/img/comprehensivePracticalActivities.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/img/ephemeris_search.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/interferenceSimulation.png
Normal file
|
After Width: | Height: | Size: 4.7 MiB |
BIN
src/assets/img/mapTheCampus.jpg
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
src/assets/img/nmea0183-thumbnail.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
src/assets/img/tdt-thumbnail.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
src/assets/img/trajectory.jpg
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
1
src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||||
|
After Width: | Height: | Size: 276 B |
9
src/assets/main.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.w-h-full {
|
||||||
|
@apply w-full h-full m-0 p-0 overflow-hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/assets/qrcode.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
19
src/main.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import './assets/main.css';
|
||||||
|
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
import ElementPlus from 'element-plus';
|
||||||
|
import 'element-plus/dist/index.css';
|
||||||
|
import { AES_Encrypt } from '@/utils/aes.js';
|
||||||
|
|
||||||
|
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
app.config.globalProperties.$AES_Encrypt = AES_Encrypt; //全局加密
|
||||||
|
app.use(createPinia());
|
||||||
|
app.use(ElementPlus);
|
||||||
|
app.use(router);
|
||||||
|
|
||||||
|
app.mount('#app');
|
||||||
59
src/router/index.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/blogin',
|
||||||
|
name: 'blogin',
|
||||||
|
component: () => import('../views/BSide/Login.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/bforgotPassword',
|
||||||
|
name: 'bforgotPassword',
|
||||||
|
component: () => import('../views/BSide/ForgotPassword.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/bforgotAccount',
|
||||||
|
name: 'bforgotAccount',
|
||||||
|
component: () => import('../views/BSide/ForgotAccount.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/clogin',
|
||||||
|
name: 'clogin',
|
||||||
|
component: () => import('../views/CSide/Login.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cregistered',
|
||||||
|
name: 'cregistered',
|
||||||
|
component: () => import('../views/CSide/Registered.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cforgotPassword',
|
||||||
|
name: 'cforgotPassword',
|
||||||
|
component: () => import('../views/CSide/ForgotPassword.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cforgotAccount',
|
||||||
|
name: 'cforgotAccount',
|
||||||
|
component: () => import('../views/CSide/ForgotAccount.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/tlogin',
|
||||||
|
name: 'tlogin',
|
||||||
|
component: () => import('../views/Contest/Login.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/tforgotPassword',
|
||||||
|
name: 'tforgotPassword',
|
||||||
|
component: () => import('../views/Contest/ForgotPassword.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/tforgotAccount',
|
||||||
|
name: 'tforgotAccount',
|
||||||
|
component: () => import('../views/Contest/ForgotAccount.vue')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
12
src/stores/counter.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useCounterStore = defineStore('counter', () => {
|
||||||
|
const count = ref(0)
|
||||||
|
const doubleCount = computed(() => count.value * 2)
|
||||||
|
function increment() {
|
||||||
|
count.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
return { count, doubleCount, increment }
|
||||||
|
})
|
||||||
11
src/utils/aes.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import CryptoJS from 'crypto-js'
|
||||||
|
|
||||||
|
const KEY = CryptoJS.enc.Utf8.parse('BDQH');
|
||||||
|
|
||||||
|
export const AES_Encrypt = (plaintext) => {
|
||||||
|
let ciphertext = CryptoJS.AES.encrypt(plaintext, KEY, {
|
||||||
|
mode: CryptoJS.mode.ECB,
|
||||||
|
padding: CryptoJS.pad.Pkcs7
|
||||||
|
}).toString();
|
||||||
|
return ciphertext;
|
||||||
|
}
|
||||||
17
src/utils/defense.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export const escapeHTML = (authHTML) => {
|
||||||
|
return authHTML.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/[/]/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const escapeSchemes = (schemes) => {
|
||||||
|
allowSchemes = ["http", "https"];
|
||||||
|
valid = isValid(getParameter(schemes), allowSchemes);
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const escapeEmbedJSON = (authJSON) => {
|
||||||
|
return authJSON.replace(/U+2028/g, '\u2028').replace(/U+2029/g, '\u2029').replace(/</g, '\u003c');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const input_xss = 'jaVasCript:'+'/*-/*`/*\`/*'+"'"+'/*"/**/'+'(/* */oNcliCk=alert() )'+'//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e';
|
||||||
|
|
||||||
|
export const url_xss = 'jaVasCript%3A%2F*-%2F*%60%2F*%60%2F*%27%2F*%22%2F**%2F(%2F*%20*%2FoNcliCk%3Dalert()%20)%2F%2F%250D%250A%250d%250a%2F%2F%3C%2FstYle%2F%3C%2FtitLe%2F%3C%2FteXtarEa%2F%3C%2FscRipt%2F--!%3E%3CsVg%2F%3CsVg%2FoNloAd%3Dalert()%2F%2F%3E%3E';
|
||||||
12
src/utils/pointless.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const pointless = (authtext) => {
|
||||||
|
// 定义一个包含所有非法值的数组
|
||||||
|
var invalidValues = ["", null, "null", "Null", NaN, "NaN", "nan", undefined, "undefined", " ", '\s'];
|
||||||
|
|
||||||
|
// 检查NaN,因为NaN不等于自身,需要特殊处理
|
||||||
|
if (isNaN(authtext)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查authtext是否在invalidValues数组中
|
||||||
|
return !invalidValues.includes(authtext);
|
||||||
|
}
|
||||||
47
src/views/BSide/ForgotAccount.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="border-2 border-rose-600 bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem;"> 公告 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 本系统暂不支持账号自主找回。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位用户请联系自身单位管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位管理员请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 个人用户请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 竞赛用户请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="backLogin">返回登录页面</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/blogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
47
src/views/BSide/ForgotPassword.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="border-2 border-rose-600 bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem;"> 公告 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 本系统暂不支持密码自主重置。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位用户请联系自身单位管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位管理员请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 个人用户请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 竞赛用户请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="backLogin">返回登录页面</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/blogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
284
src/views/BSide/Login.vue
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full justify-center items-center bg-sky-200">
|
||||||
|
<el-main>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
style="max-width: 600px"
|
||||||
|
:model="ruleForm"
|
||||||
|
status-icon
|
||||||
|
:rules="rules"
|
||||||
|
label-width="auto"
|
||||||
|
class="p-6 m-auto flex flex-col justify-center items-center bg-slate-100/70"
|
||||||
|
>
|
||||||
|
<el-form-item>
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.6rem;"> 北斗科技教育平台登录 </el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="用户名" prop="name">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
v-model="ruleForm.name"
|
||||||
|
@input="filter(ruleForm.name, 'name')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="密码" prop="password">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入密码"
|
||||||
|
v-model="ruleForm.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
maxlength="16"
|
||||||
|
autocomplete="off"
|
||||||
|
@input="filter(ruleForm.password, 'password')"
|
||||||
|
oncut="return false"
|
||||||
|
onpaste="return false"
|
||||||
|
oncopy="return false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<slide-verify
|
||||||
|
class="mx-1 font-bold"
|
||||||
|
ref="block"
|
||||||
|
:slider-text="text"
|
||||||
|
:accuracy="accuracy"
|
||||||
|
:imgs="imgs"
|
||||||
|
@again="onAgain"
|
||||||
|
@success="onSuccess"
|
||||||
|
@fail="onFail"
|
||||||
|
@refresh="onRefresh"
|
||||||
|
></slide-verify>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-text class="mx-1 font-bold" :style="fontColor"> {{ msg }} </el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div class="mx-2">
|
||||||
|
<el-link style="font-weight: bold;" type="primary" @click="forgotAccount">忘记账号?</el-link>
|
||||||
|
</div>
|
||||||
|
<div class="mx-2">
|
||||||
|
<el-link style="font-weight: bold;" type="primary" @click="forgotPassword">忘记密码?</el-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="submitForm(ruleFormRef)"> 登录 </el-button>
|
||||||
|
<el-button style="font-weight: bold;" type="warning" @click="resetForm(ruleFormRef)">清除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, getCurrentInstance, onMounted } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import router from "../../router";
|
||||||
|
import SlideVerify from "vue3-slide-verify";
|
||||||
|
import "vue3-slide-verify/dist/style.css";
|
||||||
|
import $http from "../../api/index";
|
||||||
|
import $url from "../../api/url";
|
||||||
|
import BDThemeLesson from "../../assets/img/BDThemeLesson.jpg";
|
||||||
|
import comprehensivePracticalActivities from "../../assets/img/comprehensivePracticalActivities.jpg";
|
||||||
|
import ephemerisSearch from "../../assets/img/ephemeris_search.jpg";
|
||||||
|
import interferenceSimulation from "../../assets/img/interferenceSimulation.png";
|
||||||
|
import mapTheCampus from "../../assets/img/mapTheCampus.jpg";
|
||||||
|
import nmea0183Thumbnail from "../../assets/img/nmea0183-thumbnail.jpg";
|
||||||
|
import tdtThumbnail from "../../assets/img/tdt-thumbnail.jpg";
|
||||||
|
import trajectory from "../../assets/img/trajectory.jpg";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
const msg = ref("请完成验证");
|
||||||
|
const fontColor = ref("color: red");
|
||||||
|
const block = ref();
|
||||||
|
const verify = ref(false);
|
||||||
|
let text = "向右滑动以验证->",
|
||||||
|
accuracy = 4,
|
||||||
|
imgs = [
|
||||||
|
BDThemeLesson,
|
||||||
|
comprehensivePracticalActivities,
|
||||||
|
ephemerisSearch,
|
||||||
|
interferenceSimulation,
|
||||||
|
mapTheCampus,
|
||||||
|
nmea0183Thumbnail,
|
||||||
|
tdtThumbnail,
|
||||||
|
trajectory,
|
||||||
|
];
|
||||||
|
|
||||||
|
const validateName = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入用户名"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePass = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入密码"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ruleForm = reactive({
|
||||||
|
name: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
name: [{ validator: validateName, trigger: "blur" }],
|
||||||
|
password: [{ validator: validatePass, trigger: "blur" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = (value, label) => {
|
||||||
|
if (/[=\s"'+-]/.test(value)) {
|
||||||
|
if (label == "name") {
|
||||||
|
ruleForm.name = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
if (label == "password") {
|
||||||
|
ruleForm.password = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAgain = () => {
|
||||||
|
msg.value = "检测到可能非人为操作!请重新验证";
|
||||||
|
fontColor.value = "color: yellow";
|
||||||
|
verify.value = false;
|
||||||
|
block.value?.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSuccess = (times) => {
|
||||||
|
msg.value = `验证通过, 耗时${(times / 1000).toFixed(1)}s`;
|
||||||
|
fontColor.value = "color: green";
|
||||||
|
verify.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFail = () => {
|
||||||
|
msg.value = "验证未通过,请重新验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
msg.value = "刷新成功,请完成验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotAccount = () => {
|
||||||
|
router.push({ path: "/bforgotAccount", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotPassword = () => {
|
||||||
|
router.push({ path: "/bforgotPassword", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
|
const timer = ref(false);
|
||||||
|
|
||||||
|
const timeCheck = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if(timer.value) {
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
}, 12000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginStatus = () => {
|
||||||
|
let getCode = window.location.href.indexOf("?");
|
||||||
|
if (
|
||||||
|
getCode !== "-1" &&
|
||||||
|
localStorage.data &&
|
||||||
|
localStorage.data !== "" &&
|
||||||
|
localStorage.data !== null &&
|
||||||
|
localStorage.data !== undefined &&
|
||||||
|
localStorage.data !== NaN
|
||||||
|
) {
|
||||||
|
let client_id = route.query.client_id;
|
||||||
|
const data = {
|
||||||
|
data: localStorage.data,
|
||||||
|
client_id
|
||||||
|
};
|
||||||
|
$http.post($url.loginStatus, data).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
timer.value = false;
|
||||||
|
localStorage.setItem("data", res.data.data);
|
||||||
|
if(
|
||||||
|
localStorage.data &&
|
||||||
|
localStorage.data !== "" &&
|
||||||
|
localStorage.data !== null &&
|
||||||
|
localStorage.data !== undefined &&
|
||||||
|
localStorage.data !== NaN
|
||||||
|
) {
|
||||||
|
window.location.href = res.data.redirect;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timer.value = true;
|
||||||
|
timeCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitForm = (formEl) => {
|
||||||
|
if (verify.value) {
|
||||||
|
block.value?.refresh();
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
let encryptForm = {
|
||||||
|
name: proxy.$AES_Encrypt(ruleForm.name),
|
||||||
|
password: proxy.$AES_Encrypt(ruleForm.password),
|
||||||
|
client_id: route.query.client_id,
|
||||||
|
};
|
||||||
|
$http.post($url.blogin, encryptForm).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: "登录成功!",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
localStorage.setItem("data", res.data.data);
|
||||||
|
window.location.href = res.data.redirect;
|
||||||
|
} else {
|
||||||
|
if(res.data.code === 444) {
|
||||||
|
ElMessage.error(res.data.msg);
|
||||||
|
} else {
|
||||||
|
ElMessage.error("登录失败!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ElMessage.error("提交失败,请先完善登录信息!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
block.value?.refresh();
|
||||||
|
ElMessage.error("验证未通过,请重新验证!");
|
||||||
|
msg.value = "验证未通过,请重新验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = (formEl) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('storage', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loginStatus();
|
||||||
|
}, 6000);
|
||||||
|
});
|
||||||
|
loginStatus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
47
src/views/CSide/ForgotAccount.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="border-2 border-rose-600 bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem;"> 公告 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 本系统暂不支持账号自主找回。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位用户请联系自身单位管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位管理员请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 个人用户请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 竞赛用户请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="backLogin">返回登录页面</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/clogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
47
src/views/CSide/ForgotPassword.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="border-2 border-rose-600 bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem;"> 公告 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 本系统暂不支持密码自主重置。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位用户请联系自身单位管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位管理员请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 个人用户请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 竞赛用户请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="backLogin">返回登录页面</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/clogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
290
src/views/CSide/Login.vue
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full justify-center items-center bg-sky-200">
|
||||||
|
<el-main>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
style="max-width: 600px"
|
||||||
|
:model="ruleForm"
|
||||||
|
status-icon
|
||||||
|
:rules="rules"
|
||||||
|
label-width="auto"
|
||||||
|
class="p-6 m-auto flex flex-col justify-center items-center bg-slate-100/70"
|
||||||
|
>
|
||||||
|
<el-form-item>
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.6rem;"> 北斗科技教育平台登录 </el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="用户名" prop="name">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
v-model="ruleForm.name"
|
||||||
|
@input="filter(ruleForm.name, 'name')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="密码" prop="password">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入密码"
|
||||||
|
v-model="ruleForm.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
maxlength="16"
|
||||||
|
autocomplete="off"
|
||||||
|
@input="filter(ruleForm.password, 'password')"
|
||||||
|
oncut="return false"
|
||||||
|
onpaste="return false"
|
||||||
|
oncopy="return false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<slide-verify
|
||||||
|
class="mx-1 font-bold"
|
||||||
|
ref="block"
|
||||||
|
:slider-text="text"
|
||||||
|
:accuracy="accuracy"
|
||||||
|
:imgs="imgs"
|
||||||
|
@again="onAgain"
|
||||||
|
@success="onSuccess"
|
||||||
|
@fail="onFail"
|
||||||
|
@refresh="onRefresh"
|
||||||
|
></slide-verify>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-text class="mx-1 font-bold" :style="fontColor"> {{ msg }} </el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div class="mx-2">
|
||||||
|
<el-link style="font-weight: bold;" type="primary" @click="forgotAccount">忘记账号?</el-link>
|
||||||
|
</div>
|
||||||
|
<div><el-link style="font-weight: bold;" type="primary" @click="forgotPassword">忘记密码?</el-link></div>
|
||||||
|
<div class="mx-2">
|
||||||
|
<el-link style="font-weight: bold;" type="primary" @click="registerAccount">注册账号</el-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="submitForm(ruleFormRef)"> 登录 </el-button>
|
||||||
|
<el-button style="font-weight: bold;" type="warning" @click="resetForm(ruleFormRef)">清除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, getCurrentInstance, onMounted, watchEffect } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import router from "../../router";
|
||||||
|
import SlideVerify from "vue3-slide-verify";
|
||||||
|
import "vue3-slide-verify/dist/style.css";
|
||||||
|
import $http from "../../api/index";
|
||||||
|
import $url from "../../api/url";
|
||||||
|
import BDThemeLesson from "../../assets/img/BDThemeLesson.jpg";
|
||||||
|
import comprehensivePracticalActivities from "../../assets/img/comprehensivePracticalActivities.jpg";
|
||||||
|
import ephemerisSearch from "../../assets/img/ephemeris_search.jpg";
|
||||||
|
import interferenceSimulation from "../../assets/img/interferenceSimulation.png";
|
||||||
|
import mapTheCampus from "../../assets/img/mapTheCampus.jpg";
|
||||||
|
import nmea0183Thumbnail from "../../assets/img/nmea0183-thumbnail.jpg";
|
||||||
|
import tdtThumbnail from "../../assets/img/tdt-thumbnail.jpg";
|
||||||
|
import trajectory from "../../assets/img/trajectory.jpg";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
const msg = ref("请完成验证");
|
||||||
|
const fontColor = ref("color: red");
|
||||||
|
const block = ref();
|
||||||
|
const verify = ref(false);
|
||||||
|
let text = "向右滑动以验证->",
|
||||||
|
accuracy = 4,
|
||||||
|
imgs = [
|
||||||
|
BDThemeLesson,
|
||||||
|
comprehensivePracticalActivities,
|
||||||
|
ephemerisSearch,
|
||||||
|
interferenceSimulation,
|
||||||
|
mapTheCampus,
|
||||||
|
nmea0183Thumbnail,
|
||||||
|
tdtThumbnail,
|
||||||
|
trajectory,
|
||||||
|
];
|
||||||
|
|
||||||
|
const validateName = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入用户名"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePass = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入密码"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ruleForm = reactive({
|
||||||
|
name: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
name: [{ validator: validateName, trigger: "blur" }],
|
||||||
|
password: [{ validator: validatePass, trigger: "blur" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = (value, label) => {
|
||||||
|
if (/[=\s"'+-]/.test(value)) {
|
||||||
|
if (label == "name") {
|
||||||
|
ruleForm.name = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
if (label == "password") {
|
||||||
|
ruleForm.password = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAgain = () => {
|
||||||
|
msg.value = "检测到可能非人为操作!请重新验证";
|
||||||
|
fontColor.value = "color: yellow";
|
||||||
|
verify.value = false;
|
||||||
|
block.value?.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSuccess = (times) => {
|
||||||
|
msg.value = `验证通过, 耗时${(times / 1000).toFixed(1)}s`;
|
||||||
|
fontColor.value = "color: green";
|
||||||
|
verify.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFail = () => {
|
||||||
|
msg.value = "验证未通过,请重新验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
msg.value = "刷新成功,请完成验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerAccount = () => {
|
||||||
|
router.push({ path: "/cregistered", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotAccount = () => {
|
||||||
|
router.push({ path: "/cforgotAccount", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotPassword = () => {
|
||||||
|
router.push({ path: "/cforgotPassword", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
|
const timer = ref(false);
|
||||||
|
|
||||||
|
const timeCheck = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if(timer.value) {
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
}, 12000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginStatus = () => {
|
||||||
|
let getCode = window.location.href.indexOf("?");
|
||||||
|
if (
|
||||||
|
getCode !== "-1" &&
|
||||||
|
localStorage.data &&
|
||||||
|
localStorage.data !== "" &&
|
||||||
|
localStorage.data !== null &&
|
||||||
|
localStorage.data !== undefined &&
|
||||||
|
localStorage.data !== NaN
|
||||||
|
) {
|
||||||
|
let client_id = route.query.client_id;
|
||||||
|
const data = {
|
||||||
|
data: localStorage.data,
|
||||||
|
client_id
|
||||||
|
};
|
||||||
|
$http.post($url.loginStatus, data).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
timer.value = false;
|
||||||
|
localStorage.setItem("data", res.data.data);
|
||||||
|
if(
|
||||||
|
localStorage.data &&
|
||||||
|
localStorage.data !== "" &&
|
||||||
|
localStorage.data !== null &&
|
||||||
|
localStorage.data !== undefined &&
|
||||||
|
localStorage.data !== NaN
|
||||||
|
) {
|
||||||
|
window.location.href = res.data.redirect;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timer.value = true;
|
||||||
|
timeCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitForm = (formEl) => {
|
||||||
|
loginStatus();
|
||||||
|
if (verify.value) {
|
||||||
|
block.value?.refresh();
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
let encryptForm = {
|
||||||
|
name: proxy.$AES_Encrypt(ruleForm.name),
|
||||||
|
password: proxy.$AES_Encrypt(ruleForm.password),
|
||||||
|
client_id: route.query.client_id,
|
||||||
|
};
|
||||||
|
$http.post($url.clogin, encryptForm).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: "登录成功!",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
localStorage.setItem("data", res.data.data);
|
||||||
|
window.location.href = res.data.redirect;
|
||||||
|
} else {
|
||||||
|
if(res.data.code === 444) {
|
||||||
|
ElMessage.error(res.data.msg);
|
||||||
|
} else {
|
||||||
|
ElMessage.error("登录失败!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ElMessage.error("提交失败,请先完善登录信息!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
block.value?.refresh();
|
||||||
|
ElMessage.error("验证未通过,请重新验证!");
|
||||||
|
msg.value = "验证未通过,请重新验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = (formEl) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('storage', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loginStatus();
|
||||||
|
}, 6000);
|
||||||
|
});
|
||||||
|
loginStatus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
383
src/views/CSide/Registered.vue
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem"> 个人账号注册 </el-text>
|
||||||
|
</div>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
status-icon
|
||||||
|
label-width="auto"
|
||||||
|
style="max-width: 600px; max-height: 80vh; overflow: auto"
|
||||||
|
class="p-10"
|
||||||
|
>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="用户名(必填)" prop="account_name">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
v-model="form.account_name"
|
||||||
|
@input="filter(form.account_name, 'account_name')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="密码(必填)" prop="password">
|
||||||
|
<el-text
|
||||||
|
style="color: rgb(240 70 70)"
|
||||||
|
class="mx-1 font-bold"
|
||||||
|
size="small"
|
||||||
|
type="info"
|
||||||
|
>
|
||||||
|
密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在8-16位之间
|
||||||
|
</el-text>
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入密码"
|
||||||
|
maxlength="16"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
autocomplete="off"
|
||||||
|
v-model="form.password"
|
||||||
|
@input="filter(form.password, 'password')"
|
||||||
|
oncut="return false"
|
||||||
|
onpaste="return false"
|
||||||
|
oncopy="return false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="确认密码" prop="checkPass">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请再次输入密码"
|
||||||
|
maxlength="16"
|
||||||
|
type="checkPass"
|
||||||
|
show-password
|
||||||
|
autocomplete="off"
|
||||||
|
v-model="form.checkPass"
|
||||||
|
@input="filter(form.checkPass, 'checkPass')"
|
||||||
|
oncut="return false"
|
||||||
|
onpaste="return false"
|
||||||
|
oncopy="return false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="姓名(选填)" prop="user_name">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
v-model="form.user_name"
|
||||||
|
@input="filter(form.user_name, 'user_name')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="电话号码(必填)" prop="phone">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入电话号码"
|
||||||
|
maxlength="11"
|
||||||
|
v-model="form.phone"
|
||||||
|
@input="filter(form.phone, 'phone')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="邮箱(选填)" prop="email">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
v-model="form.email"
|
||||||
|
@input="filter(form.email, 'email')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="性别(可选)">
|
||||||
|
<el-radio-group v-model="form.gender">
|
||||||
|
<el-radio value="1">男</el-radio>
|
||||||
|
<el-radio value="2">女</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="年龄(选填)" prop="age">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入年龄"
|
||||||
|
maxlength="2"
|
||||||
|
v-model.number="form.age"
|
||||||
|
@input="filter(form.age, 'age')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="通信地址(选填)" prop="address">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入通信地址"
|
||||||
|
v-model="form.address"
|
||||||
|
@input="filter(form.address, 'address')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="邮政编码(选填)" prop="post_code">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入邮政编码"
|
||||||
|
maxlength="6"
|
||||||
|
v-model="form.post_code"
|
||||||
|
@input="filter(form.post_code, 'post_code')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div class="w-full flex justify-center">
|
||||||
|
<el-button
|
||||||
|
style="font-weight: bold"
|
||||||
|
class="m-2"
|
||||||
|
type="primary"
|
||||||
|
@click="onSubmit(ruleFormRef)"
|
||||||
|
>注册</el-button
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
style="font-weight: bold"
|
||||||
|
class="m-2"
|
||||||
|
type="warning"
|
||||||
|
@click="dialogVisible = true"
|
||||||
|
>清除</el-button
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
style="font-weight: bold"
|
||||||
|
class="m-2"
|
||||||
|
type="info"
|
||||||
|
@click="backLogin"
|
||||||
|
>返回登录页面</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<el-dialog
|
||||||
|
style="font-weight: bold"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
title="警告"
|
||||||
|
width="300"
|
||||||
|
align-center
|
||||||
|
:before-close="handleClose"
|
||||||
|
>
|
||||||
|
<span>确认清空所有输入?</span>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button
|
||||||
|
style="font-weight: bold"
|
||||||
|
type="primary"
|
||||||
|
@click="onClear(ruleFormRef)"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
<el-button style="font-weight: bold" @click="dialogVisible = false"
|
||||||
|
>取消</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, getCurrentInstance } from "vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
import $http from "../../api/index";
|
||||||
|
import $url from "../../api/url";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
|
||||||
|
const dialogVisible = ref(false);
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
account_name: "",
|
||||||
|
password: "",
|
||||||
|
checkPass: "",
|
||||||
|
user_name: "",
|
||||||
|
phone: "",
|
||||||
|
email: "",
|
||||||
|
gender: "",
|
||||||
|
age: "",
|
||||||
|
address: "",
|
||||||
|
post_code: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = (value, label) => {
|
||||||
|
if (/[=\s"'+-]/.test(value)) {
|
||||||
|
if (form[label]) {
|
||||||
|
form[label] = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const passwordRegex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*]).{8,16}$/;
|
||||||
|
const phoneRegex = /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[0123456789]))\d{8}$/;
|
||||||
|
const emailRegex = /^([a-zA-Z0-9]+[|-|.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[|-|.]?)*[a-zA-Z0-9]+(.[a-zA-Z]{2,3})+$/;
|
||||||
|
const postCodeRegex = /^(0[1234567]|1[012356]|2[01234567]|3[0123456]|4[01234567]|5[1234567]|6[1234567]|7[012345]|8[013456])\d{4}$/;
|
||||||
|
|
||||||
|
const validateAccount = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入用户名"));
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
name: value,
|
||||||
|
};
|
||||||
|
$http.post($url.diffName, data).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
callback(new Error("该用户名已存在"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePass = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入密码"));
|
||||||
|
} else if (!passwordRegex.test(value)) {
|
||||||
|
callback(
|
||||||
|
new Error(
|
||||||
|
"密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在8-16位之间"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (form.checkPass !== "") {
|
||||||
|
if (!ruleFormRef.value) return;
|
||||||
|
ruleFormRef.value.validateField("checkPass");
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateCheck = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请再次输入密码"));
|
||||||
|
} else if (value !== form.password) {
|
||||||
|
callback(new Error("两次输入的密码不一致"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateName = (rule, value, callback) => {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePhone = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入电话号码"));
|
||||||
|
} else if (!phoneRegex.test(value)) {
|
||||||
|
callback(new Error("请输入正确的电话号码"));
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
phone: value,
|
||||||
|
};
|
||||||
|
$http.post($url.diffPhone, data).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
callback(new Error("该手机号已注册"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateEmail = (rule, value, callback) => {
|
||||||
|
if (value !== "" && !emailRegex.test(value)) {
|
||||||
|
callback(new Error("请输入正确的邮箱地址"));
|
||||||
|
} else if (value === "") {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
const data = {
|
||||||
|
email: value,
|
||||||
|
};
|
||||||
|
$http.post($url.diffEmail, data).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
callback(new Error("该邮箱已经注册"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateAge = (rule, value, callback) => {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateAddress = (rule, value, callback) => {
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateCode = (rule, value, callback) => {
|
||||||
|
if (value !== "" && !postCodeRegex.test(value)) {
|
||||||
|
callback(new Error("请输入正确的邮政编码"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
account_name: [{ required: true, validator: validateAccount, trigger: "blur" }],
|
||||||
|
password: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||||
|
checkPass: [{ required: true, validator: validateCheck, trigger: "blur" }],
|
||||||
|
user_name: [{ validator: validateName, trigger: "blur" }],
|
||||||
|
phone: [{ required: true, validator: validatePhone, trigger: "blur" }],
|
||||||
|
email: [
|
||||||
|
{ validator: validateEmail, trigger: "blur" },
|
||||||
|
{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] },
|
||||||
|
],
|
||||||
|
age: [
|
||||||
|
{ validator: validateAge, trigger: "blur" },
|
||||||
|
{ type: "number", message: "年龄必须为数字", trigger: ["blur", "change"] },
|
||||||
|
],
|
||||||
|
address: [{ validator: validateAddress, trigger: "blur" }],
|
||||||
|
post_code: [{ validator: validateCode, trigger: "blur" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (formEl) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
let encryptForm = {
|
||||||
|
account_name: proxy.$AES_Encrypt(form.account_name),
|
||||||
|
password: proxy.$AES_Encrypt(form.password),
|
||||||
|
user_name: form.user_name == "" ? "" : proxy.$AES_Encrypt(form.user_name),
|
||||||
|
phone: proxy.$AES_Encrypt(form.phone),
|
||||||
|
email: form.email == "" ? "" : proxy.$AES_Encrypt(form.email),
|
||||||
|
gender: form.gender == "" ? "" : proxy.$AES_Encrypt(form.gender),
|
||||||
|
age: form.age == "" ? "" : proxy.$AES_Encrypt(form.age + ""),
|
||||||
|
address: form.address == "" ? "" : proxy.$AES_Encrypt(form.address),
|
||||||
|
post_code: form.post_code == "" ? "" : proxy.$AES_Encrypt(form.post_code),
|
||||||
|
};
|
||||||
|
$http.post($url.cregistered, encryptForm).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: "注册成功!",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
router.push({ path: "/clogin", query: { client_id: client_id } });
|
||||||
|
} else {
|
||||||
|
ElMessage.error("注册失败!请重试!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ElMessage.error("注册失败!请重试!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClear = (formEl) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
dialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
dialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/clogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
47
src/views/Contest/ForgotAccount.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="border-2 border-rose-600 bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem;"> 公告 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 本系统暂不支持账号自主找回。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位用户请联系自身单位管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位管理员请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 个人用户请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 竞赛用户请联系网站管理员找回账号。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="backLogin">返回登录页面</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/tlogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
47
src/views/Contest/ForgotPassword.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full flex justify-center items-center bg-sky-200">
|
||||||
|
<div class="border-2 border-rose-600 bg-slate-100/70 p-6">
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.8rem;"> 公告 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 本系统暂不支持密码自主重置。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位用户请联系自身单位管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 单位管理员请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 个人用户请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center font-bold">
|
||||||
|
<el-text> 竞赛用户请联系网站管理员重置密码。 </el-text>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="backLogin">返回登录页面</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import router from "../../router";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const backLogin = () => {
|
||||||
|
router.push({ path: "/tlogin", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
284
src/views/Contest/Login.vue
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<el-container class="w-h-full justify-center items-center bg-sky-200">
|
||||||
|
<el-main>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
style="max-width: 600px"
|
||||||
|
:model="ruleForm"
|
||||||
|
status-icon
|
||||||
|
:rules="rules"
|
||||||
|
label-width="auto"
|
||||||
|
class="p-6 m-auto flex flex-col justify-center items-center bg-slate-100/70"
|
||||||
|
>
|
||||||
|
<el-form-item>
|
||||||
|
<el-text class="mx-1 font-bold" style="font-size: 1.6rem;"> 北斗导航定位通信综合实践赛服务系统登录 </el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="用户名" prop="name">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
v-model="ruleForm.name"
|
||||||
|
@input="filter(ruleForm.name, 'name')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item class="mx-1 font-bold" label="密码" prop="password">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
placeholder="请输入密码"
|
||||||
|
v-model="ruleForm.password"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
maxlength="16"
|
||||||
|
autocomplete="off"
|
||||||
|
@input="filter(ruleForm.password, 'password')"
|
||||||
|
oncut="return false"
|
||||||
|
onpaste="return false"
|
||||||
|
oncopy="return false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<slide-verify
|
||||||
|
class="mx-1 font-bold"
|
||||||
|
ref="block"
|
||||||
|
:slider-text="text"
|
||||||
|
:accuracy="accuracy"
|
||||||
|
:imgs="imgs"
|
||||||
|
@again="onAgain"
|
||||||
|
@success="onSuccess"
|
||||||
|
@fail="onFail"
|
||||||
|
@refresh="onRefresh"
|
||||||
|
></slide-verify>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-text class="mx-1 font-bold" :style="fontColor"> {{ msg }} </el-text>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div class="mx-2">
|
||||||
|
<el-link style="font-weight: bold;" type="primary" @click="forgotAccount">忘记账号?</el-link>
|
||||||
|
</div>
|
||||||
|
<div class="mx-2">
|
||||||
|
<el-link style="font-weight: bold;" type="primary" @click="forgotPassword">忘记密码?</el-link>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button style="font-weight: bold;" type="primary" @click="submitForm(ruleFormRef)"> 登录 </el-button>
|
||||||
|
<el-button style="font-weight: bold;" type="warning" @click="resetForm(ruleFormRef)">清除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, getCurrentInstance, onMounted } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import router from "../../router";
|
||||||
|
import SlideVerify from "vue3-slide-verify";
|
||||||
|
import "vue3-slide-verify/dist/style.css";
|
||||||
|
import $http from "../../api/index";
|
||||||
|
import $url from "../../api/url";
|
||||||
|
import BDThemeLesson from "../../assets/img/BDThemeLesson.jpg";
|
||||||
|
import comprehensivePracticalActivities from "../../assets/img/comprehensivePracticalActivities.jpg";
|
||||||
|
import ephemerisSearch from "../../assets/img/ephemeris_search.jpg";
|
||||||
|
import interferenceSimulation from "../../assets/img/interferenceSimulation.png";
|
||||||
|
import mapTheCampus from "../../assets/img/mapTheCampus.jpg";
|
||||||
|
import nmea0183Thumbnail from "../../assets/img/nmea0183-thumbnail.jpg";
|
||||||
|
import tdtThumbnail from "../../assets/img/tdt-thumbnail.jpg";
|
||||||
|
import trajectory from "../../assets/img/trajectory.jpg";
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const client_id = route.query.client_id;
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
const msg = ref("请完成验证");
|
||||||
|
const fontColor = ref("color: red");
|
||||||
|
const block = ref();
|
||||||
|
const verify = ref(false);
|
||||||
|
let text = "向右滑动以验证->",
|
||||||
|
accuracy = 4,
|
||||||
|
imgs = [
|
||||||
|
BDThemeLesson,
|
||||||
|
comprehensivePracticalActivities,
|
||||||
|
ephemerisSearch,
|
||||||
|
interferenceSimulation,
|
||||||
|
mapTheCampus,
|
||||||
|
nmea0183Thumbnail,
|
||||||
|
tdtThumbnail,
|
||||||
|
trajectory,
|
||||||
|
];
|
||||||
|
|
||||||
|
const validateName = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入用户名"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validatePass = (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("请输入密码"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ruleForm = reactive({
|
||||||
|
name: "",
|
||||||
|
password: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
name: [{ validator: validateName, trigger: "blur" }],
|
||||||
|
password: [{ validator: validatePass, trigger: "blur" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = (value, label) => {
|
||||||
|
if (/[=\s"'+-]/.test(value)) {
|
||||||
|
if (label == "name") {
|
||||||
|
ruleForm.name = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
if (label == "password") {
|
||||||
|
ruleForm.password = value.replace(/[=\s"'+-]/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAgain = () => {
|
||||||
|
msg.value = "检测到可能非人为操作!请重新验证";
|
||||||
|
fontColor.value = "color: yellow";
|
||||||
|
verify.value = false;
|
||||||
|
block.value?.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSuccess = (times) => {
|
||||||
|
msg.value = `验证通过, 耗时${(times / 1000).toFixed(1)}s`;
|
||||||
|
fontColor.value = "color: green";
|
||||||
|
verify.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFail = () => {
|
||||||
|
msg.value = "验证未通过,请重新验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
msg.value = "刷新成功,请完成验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotAccount = () => {
|
||||||
|
router.push({ path: "/tforgotAccount", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgotPassword = () => {
|
||||||
|
router.push({ path: "/tforgotPassword", query: { client_id: client_id } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
|
const timer = ref(false);
|
||||||
|
|
||||||
|
const timeCheck = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if(timer.value) {
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
}, 12000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginStatus = () => {
|
||||||
|
let getCode = window.location.href.indexOf("?");
|
||||||
|
if (
|
||||||
|
getCode !== "-1" &&
|
||||||
|
localStorage.data &&
|
||||||
|
localStorage.data !== "" &&
|
||||||
|
localStorage.data !== null &&
|
||||||
|
localStorage.data !== undefined &&
|
||||||
|
localStorage.data !== NaN
|
||||||
|
) {
|
||||||
|
let client_id = route.query.client_id;
|
||||||
|
const data = {
|
||||||
|
data: localStorage.data,
|
||||||
|
client_id
|
||||||
|
};
|
||||||
|
$http.post($url.loginStatus, data).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
timer.value = false;
|
||||||
|
localStorage.setItem("data", res.data.data);
|
||||||
|
if(
|
||||||
|
localStorage.data &&
|
||||||
|
localStorage.data !== "" &&
|
||||||
|
localStorage.data !== null &&
|
||||||
|
localStorage.data !== undefined &&
|
||||||
|
localStorage.data !== NaN
|
||||||
|
) {
|
||||||
|
window.location.href = res.data.redirect;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timer.value = true;
|
||||||
|
timeCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitForm = (formEl) => {
|
||||||
|
if (verify.value) {
|
||||||
|
block.value?.refresh();
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
let encryptForm = {
|
||||||
|
name: proxy.$AES_Encrypt(ruleForm.name),
|
||||||
|
password: proxy.$AES_Encrypt(ruleForm.password),
|
||||||
|
client_id: route.query.client_id,
|
||||||
|
};
|
||||||
|
$http.post($url.tlogin, encryptForm).then((res) => {
|
||||||
|
if (res.data.code === 200) {
|
||||||
|
ElMessage({
|
||||||
|
message: "登录成功!",
|
||||||
|
type: "success",
|
||||||
|
});
|
||||||
|
localStorage.setItem("data", res.data.data);
|
||||||
|
window.location.href = res.data.redirect;
|
||||||
|
} else {
|
||||||
|
if(res.data.code === 444) {
|
||||||
|
ElMessage.error(res.data.msg);
|
||||||
|
} else {
|
||||||
|
ElMessage.error("登录失败!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ElMessage.error("提交失败,请先完善登录信息!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
block.value?.refresh();
|
||||||
|
ElMessage.error("验证未通过,请重新验证!");
|
||||||
|
msg.value = "验证未通过,请重新验证";
|
||||||
|
fontColor.value = "color: red";
|
||||||
|
verify.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = (formEl) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('storage', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loginStatus();
|
||||||
|
}, 6000);
|
||||||
|
});
|
||||||
|
loginStatus();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
13
tailwind.config.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||||
|
darkMode: false, // or 'media' or 'class'
|
||||||
|
content: [],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
44
vite.config.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
import legacy from '@vitejs/plugin-legacy'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
base: './',
|
||||||
|
publicPath: './',
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
AutoImport({
|
||||||
|
resolvers: [ElementPlusResolver()],
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
resolvers: [ElementPlusResolver()],
|
||||||
|
}),
|
||||||
|
legacy({
|
||||||
|
target: ['ie>=11'],
|
||||||
|
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
//用来配置跨域
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 7001,
|
||||||
|
proxy: {
|
||||||
|
'/authentication': {
|
||||||
|
target: 'http://127.0.0.1:8011',//目标服务器地址
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/authentication/, '')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||