短报文教学
This commit is contained in:
30
.gitignore
vendored
Normal file
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
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# gis-server
|
||||
|
||||
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
|
||||
```
|
||||
13
index.html
Normal file
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!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>
|
||||
</head>
|
||||
<body class="w-h-full">
|
||||
<div id="app" class="w-h-full text-white"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
jsconfig.json
Normal file
8
jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
7509
package-lock.json
generated
Normal file
7509
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
package.json
Normal file
36
package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "teaching-api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=7019",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^1.6.8",
|
||||
"core-js": "^3.37.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.7.2",
|
||||
"fingerprintjs2": "^2.1.4",
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"teaching-api": "file:",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.0",
|
||||
"zipson": "^0.2.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
11
src/App.vue
Normal file
11
src/App.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
import { RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
113
src/api/index.js
Normal file
113
src/api/index.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import axios from "axios";
|
||||
import $url from "./url";
|
||||
import router from "../router";
|
||||
import { encrypt } from "../utils/aes";
|
||||
|
||||
// 添加请求拦截器
|
||||
axios.interceptors.request.use(function (config) {
|
||||
// 在发送请求之前做些什么
|
||||
if (config.headers) {
|
||||
config.headers.uid = localStorage.data;
|
||||
if (localStorage.access_token && localStorage.access_token !== "" && localStorage.access_token !== null && localStorage.access_token !== undefined && localStorage.access_token !== NaN) {
|
||||
config.headers.authorization = localStorage.access_token;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}, function (error) {
|
||||
// 对请求错误做些什么
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 转到登录页面
|
||||
const toLogin = () => {
|
||||
localStorage.clear();
|
||||
const data = { origin_url: window.location.href };
|
||||
axios.post($url.toLogin, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
window.location.href = res.data.redirect;
|
||||
} else {
|
||||
router.push({ path: '/404page' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 添加响应拦截器
|
||||
let result = true;
|
||||
axios.interceptors.response.use(function (response) {
|
||||
// 2xx 范围内的状态码都会触发该函数。
|
||||
// 对响应数据做点什么
|
||||
if (response.data.code === 200) {
|
||||
if ("access_token" in response.data) {
|
||||
localStorage.setItem("access_token", response.data.access_token);
|
||||
}
|
||||
if ("refresh_token" in response.data) {
|
||||
localStorage.setItem("refresh_token", response.data.refresh_token);
|
||||
}
|
||||
if ("data" in response.data) {
|
||||
localStorage.setItem("data", response.data.data);
|
||||
}
|
||||
}
|
||||
if(response.data.code === 401 && result) {
|
||||
result = false;
|
||||
setTimeout(() => {
|
||||
result = true;
|
||||
}, 1000);
|
||||
let getCode = window.location.href.indexOf('?');
|
||||
if (getCode != '-1') {
|
||||
let code = window.location.href.substring(getCode + 1);
|
||||
const data = {
|
||||
code: code,
|
||||
type: encrypt('code')
|
||||
};
|
||||
axios.post($url.getToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("access_token", res.data.access_token);
|
||||
localStorage.setItem("refresh_token", res.data.refresh_token);
|
||||
window.location.href = res.data.redirect;
|
||||
} else {
|
||||
toLogin();
|
||||
}
|
||||
});
|
||||
} else if (localStorage.refresh_token && localStorage.refresh_token !== "" && localStorage.refresh_token !== null && localStorage.refresh_token !== undefined && localStorage.refresh_token !== NaN) {
|
||||
const data = {
|
||||
code: localStorage.refresh_token,
|
||||
type: localStorage.data
|
||||
};
|
||||
axios.post($url.getToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("access_token", res.data.access_token);
|
||||
localStorage.setItem("refresh_token", res.data.refresh_token);
|
||||
window.location.reload();
|
||||
} else {
|
||||
toLogin();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
toLogin();
|
||||
}
|
||||
}
|
||||
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);
|
||||
},
|
||||
}
|
||||
27
src/api/url.js
Normal file
27
src/api/url.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export default {
|
||||
// 用于确认哪个网页在进行登录
|
||||
toLogin: "/authentication/toLogin",
|
||||
// 退出登录
|
||||
unLogin: "/authentication/unLogin",
|
||||
// 用于获取token
|
||||
getToken: "/authentication/getToken",
|
||||
// 用于验证token
|
||||
verify: "/authentication/verify",
|
||||
// 获取用户基本信息
|
||||
getUser: "/authentication/getUser",
|
||||
// 修改用户基本信息
|
||||
editUser: "/authentication/editUser",
|
||||
// 修改密码
|
||||
changePassword: "/authentication/changePassword",
|
||||
// token状态查询接口
|
||||
tokenStatus: "/teachingApi/tokenStatus",
|
||||
// 用户获取短期token的接口
|
||||
getUserToken: "/teachingApi/getUserToken",
|
||||
// 用户延续短期token有效时间的接口
|
||||
lifeHold: "/teachingApi/lifeHold",
|
||||
// 教学接口
|
||||
// 获取最近发送的一条信息
|
||||
getMsg: "/teachingApi/getMsg",
|
||||
// 获取若干条历史消息
|
||||
getHistory: "/teachingApi/getHistory",
|
||||
}
|
||||
BIN
src/assets/favicon.ico
Normal file
BIN
src/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
1
src/assets/logo.svg
Normal file
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
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
BIN
src/assets/qrcode.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
173
src/components/changePassword.vue
Normal file
173
src/components/changePassword.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="密码管理" width="600" center align-center>
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
style="max-width: 600px"
|
||||
:model="ruleForm"
|
||||
:rules="rules"
|
||||
label-width="auto"
|
||||
size="default"
|
||||
status-icon
|
||||
>
|
||||
<el-form-item label="原密码" prop="oldPassword">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入原密码"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.oldPassword"
|
||||
@input="filter(ruleForm.oldPassword, 'oldPassword')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-text class="mx-1" size="small" type="info">
|
||||
密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在8-16位之间
|
||||
</el-text>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入新密码"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.newPassword"
|
||||
@input="filter(ruleForm.newPassword, 'newPassword')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认新密码" prop="checkPass">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请再次输入新密码"
|
||||
type="checkPass"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.checkPass"
|
||||
@input="filter(ruleForm.checkPass, 'checkPass')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
|
||||
<el-button type="primary" @click="changePassword(ruleFormRef)">
|
||||
提交修改
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import { encrypt } from "../utils/aes";
|
||||
|
||||
import $http from "../api/index";
|
||||
import $url from "../api/url";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const ruleFormRef = ref();
|
||||
const ruleForm = reactive({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
checkPass: "",
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const passwordRegex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*]).{8,16}$/;
|
||||
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入密码"));
|
||||
} else if (!passwordRegex.test(value)) {
|
||||
callback(
|
||||
new Error(
|
||||
"密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在8-16位之间"
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (ruleForm.checkPass !== "") {
|
||||
if (!ruleFormRef.value) return;
|
||||
ruleFormRef.value.validateField("checkPass");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateCheck = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请再次输入密码"));
|
||||
} else if (value !== ruleForm.newPassword) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||
checkPass: [{ required: true, validator: validateCheck, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const changePassword = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
let encryptForm = {
|
||||
oldPassword: encrypt(ruleForm.oldPassword),
|
||||
newPassword: encrypt(ruleForm.newPassword),
|
||||
};
|
||||
formEl.resetFields();
|
||||
const data = {
|
||||
passwordForm: encryptForm,
|
||||
uid: localStorage.data
|
||||
};
|
||||
$http
|
||||
.post($url.changePassword, data)
|
||||
.then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
} else {
|
||||
ElMessage.error("密码修改失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
284
src/components/userManager.vue
Normal file
284
src/components/userManager.vue
Normal file
@@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="账户管理" width="600" center align-center>
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
style="max-width: 600px"
|
||||
:model="ruleForm"
|
||||
:rules="rules"
|
||||
label-width="auto"
|
||||
size="default"
|
||||
status-icon
|
||||
>
|
||||
<el-form-item label="用户名" prop="account_name">
|
||||
<el-input
|
||||
:disabled="acabled"
|
||||
minlength="1"
|
||||
maxlength="20"
|
||||
clearable
|
||||
placeholder="请输入用户名"
|
||||
v-model="ruleForm.account_name"
|
||||
@input="filter(ruleForm.account_name, 'account_name')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="user_name">
|
||||
<el-input
|
||||
maxlength="20"
|
||||
clearable
|
||||
placeholder="请输入姓名"
|
||||
v-model="ruleForm.user_name"
|
||||
@input="filter(ruleForm.user_name, 'user_name')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="电话号码" prop="phone">
|
||||
<el-input
|
||||
minlength="11"
|
||||
maxlength="11"
|
||||
clearable
|
||||
placeholder="请输入电话号码"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="sideMark == 'C'" label="性别" prop="gender">
|
||||
<el-radio-group v-model="ruleForm.gender">
|
||||
<el-radio value="1">男</el-radio>
|
||||
<el-radio value="0">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="sideMark == 'C'" label="年龄" prop="age">
|
||||
<el-input
|
||||
maxlength="2"
|
||||
clearable
|
||||
placeholder="请输入年龄"
|
||||
v-model="ruleForm.age"
|
||||
@input="filter(ruleForm.age, 'age')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="sideMark == 'C'" label="邮箱号" prop="email">
|
||||
<el-input
|
||||
maxlength="254"
|
||||
clearable
|
||||
placeholder="请输入邮箱号"
|
||||
v-model="ruleForm.email"
|
||||
@input="filter(ruleForm.email, 'email')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="sideMark == 'C'" label="通信地址" prop="address">
|
||||
<el-input
|
||||
maxlength="300"
|
||||
clearable
|
||||
placeholder="请输入通信地址"
|
||||
v-model="ruleForm.address"
|
||||
@input="filter(ruleForm.address, 'address')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="sideMark == 'C'" label="邮政编码" prop="post_code">
|
||||
<el-input
|
||||
maxlength="6"
|
||||
clearable
|
||||
placeholder="请输入邮政编码"
|
||||
v-model="ruleForm.post_code"
|
||||
@input="filter(ruleForm.post_code, 'post_code')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="resetForm(ruleFormRef)">取消</el-button>
|
||||
<el-button type="primary" @click="editUser(ruleFormRef)"> 提交修改 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import userManagerActiveStore from "../stores/userManager";
|
||||
|
||||
import { encrypt, decrypt } from "../utils/aes";
|
||||
|
||||
import $http from "../api/index";
|
||||
import $url from "../api/url";
|
||||
|
||||
const phoneRegex = /^(?:13\d|14[579]|15\d|16[67]|17[0135678]|18\d|19[89])\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 userManagerActive = userManagerActiveStore();
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
msg: Object,
|
||||
});
|
||||
|
||||
let sideMark;
|
||||
const acabled = ref(true);
|
||||
const ruleFormRef = ref();
|
||||
const ruleForm = reactive({
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: "",
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
let filterValue;
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
filterValue = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
} else {
|
||||
filterValue = value;
|
||||
}
|
||||
if (label == "phone" || label == "age" || label == "post_code") {
|
||||
ruleForm[label] = filterValue.replace(/\D/g, "");
|
||||
} else {
|
||||
ruleForm[label] = filterValue;
|
||||
}
|
||||
};
|
||||
|
||||
const validatePhone = (rule, value, callback) => {
|
||||
if (value && !phoneRegex.test(value)) {
|
||||
callback(new Error("请输入正确的电话号码"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (value && !emailRegex.test(value)) {
|
||||
callback(new Error("请输入正确的邮箱地址"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateCode = (rule, value, callback) => {
|
||||
if (value && !postCodeRegex.test(value)) {
|
||||
callback(new Error("请输入正确的邮政编码"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
account_name: [{ required: false, message: "请输入用户名", trigger: "blur" }],
|
||||
user_name: [],
|
||||
phone: [
|
||||
{ required: false, message: "请输入手机号", trigger: "blur" },
|
||||
{ min: 11, max: 11, message: "输入长度应当为 11 位", trigger: "change" },
|
||||
{ validator: validatePhone },
|
||||
],
|
||||
});
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const emit = defineEmits(["resetData"]);
|
||||
|
||||
const reset = () => {
|
||||
ruleForm.account_name = props.msg.account_name;
|
||||
ruleForm.user_name = props.msg.user_name;
|
||||
ruleForm.phone = props.msg.phone;
|
||||
if (sideMark == "C") {
|
||||
ruleForm.gender = props.msg.gender ? "1" : "0";
|
||||
ruleForm.age = props.msg.age;
|
||||
ruleForm.email = props.msg.email;
|
||||
ruleForm.address = props.msg.address;
|
||||
ruleForm.post_code = props.msg.post_code;
|
||||
}
|
||||
};
|
||||
|
||||
const editUser = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
const data = {
|
||||
userMsg: ruleForm,
|
||||
uid: localStorage.data
|
||||
};
|
||||
$http
|
||||
.post($url.editUser, data)
|
||||
.then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
emit("resetData");
|
||||
} else {
|
||||
ElMessage.error("编辑用户信息失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible, reset });
|
||||
|
||||
watch(ruleForm, (value) => {
|
||||
if (dialogVisible.value) {
|
||||
userManagerActive.dialogVisible = dialogVisible.value;
|
||||
userManagerActive.ruleForm.account_name = encrypt(value.account_name);
|
||||
userManagerActive.ruleForm.user_name = encrypt(value.user_name);
|
||||
userManagerActive.ruleForm.phone = encrypt(value.phone);
|
||||
if (sideMark == "C") {
|
||||
userManagerActive.ruleForm.gender = encrypt(value.gender);
|
||||
userManagerActive.ruleForm.age = encrypt(value.age.toString());
|
||||
userManagerActive.ruleForm.email = encrypt(value.email);
|
||||
userManagerActive.ruleForm.address = encrypt(value.address);
|
||||
userManagerActive.ruleForm.post_code = encrypt(value.post_code);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
watch(dialogVisible, (value) => {
|
||||
if (!value) {
|
||||
userManagerActive.dialogVisible = value;
|
||||
userManagerActive.ruleForm = {};
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
dialogVisible.value = userManagerActive.dialogVisible;
|
||||
if (localStorage.data) {
|
||||
let decryptData = decrypt(localStorage.data);
|
||||
sideMark = decryptData[decryptData.length - 1];
|
||||
}
|
||||
if (sideMark == "C") {
|
||||
acabled.value = false;
|
||||
ruleForm.gender = "";
|
||||
ruleForm.age = "";
|
||||
ruleForm.email = "";
|
||||
ruleForm.address = "";
|
||||
ruleForm.post_code = "";
|
||||
rules.account_name[0].required = true;
|
||||
rules.phone[0].required = true;
|
||||
rules.age = [];
|
||||
rules.email = [{ validator: validateEmail }];
|
||||
rules.address = [];
|
||||
rules.post_code = [
|
||||
{ min: 6, max: 6, message: "输入长度应当为 6 位", trigger: "change" },
|
||||
{ validator: validateCode },
|
||||
];
|
||||
}
|
||||
if (userManagerActive.dialogVisible) {
|
||||
ruleForm.account_name = decrypt(userManagerActive.ruleForm.account_name);
|
||||
ruleForm.user_name = decrypt(userManagerActive.ruleForm.user_name);
|
||||
ruleForm.phone = decrypt(userManagerActive.ruleForm.phone);
|
||||
if (sideMark == "C") {
|
||||
ruleForm.gender = decrypt(userManagerActive.ruleForm.gender);
|
||||
ruleForm.age = decrypt(userManagerActive.ruleForm.age);
|
||||
ruleForm.email = decrypt(userManagerActive.ruleForm.email);
|
||||
ruleForm.address = decrypt(userManagerActive.ruleForm.address);
|
||||
ruleForm.post_code = decrypt(userManagerActive.ruleForm.post_code);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
16
src/main.js
Normal file
16
src/main.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import ElementPlus from 'element-plus'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(ElementPlus)
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
31
src/router/index.js
Normal file
31
src/router/index.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/main',
|
||||
name: 'main',
|
||||
component: () => import('../views/Main.vue')
|
||||
},
|
||||
{
|
||||
path: '/404page',
|
||||
name: '404',
|
||||
component: () => import('../views/404page.vue')
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.matched.length === 0) {
|
||||
if (to.path == "/") {
|
||||
router.push({ path: '/main' });
|
||||
} else {
|
||||
router.push({ path: '/404page' });
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router
|
||||
12
src/stores/counter.js
Normal file
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 }
|
||||
})
|
||||
35
src/stores/userManager.js
Normal file
35
src/stores/userManager.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const userManagerActiveStore = defineStore('userManager', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
dialogVisible: false,
|
||||
ruleForm: {
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: "",
|
||||
gender: "",
|
||||
age: "",
|
||||
email: "",
|
||||
address: "",
|
||||
post_code: "",
|
||||
},
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'userManagerActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["dialogVisible", "ruleForm"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
})
|
||||
|
||||
export default userManagerActiveStore
|
||||
21
src/utils/aes.js
Normal file
21
src/utils/aes.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
const KEY = CryptoJS.enc.Utf8.parse('BDQH');
|
||||
|
||||
export const encrypt = (plaintext) => {
|
||||
if(plaintext !== null && plaintext !== undefined && plaintext !== "") {
|
||||
return CryptoJS.AES.encrypt(plaintext, KEY, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString();
|
||||
}
|
||||
};
|
||||
|
||||
export const decrypt = (plaintext) => {
|
||||
if(plaintext !== null && plaintext !== undefined && !plaintext == "") {
|
||||
return CryptoJS.AES.decrypt(plaintext, KEY, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString(CryptoJS.enc.Utf8);
|
||||
}
|
||||
};
|
||||
21
src/views/404page.vue
Normal file
21
src/views/404page.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="common-layout w-h-full">
|
||||
<el-text class="mx-1"> 404 not found </el-text>
|
||||
<br />
|
||||
<el-text class="mx-1"> 您搜索的页面不存在 </el-text>
|
||||
<br />
|
||||
<el-button style="font-weight: bold;" type="primary" @click="backHome">返回首页</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const backHome = () => {
|
||||
router.push("/main");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
598
src/views/Main.vue
Normal file
598
src/views/Main.vue
Normal file
@@ -0,0 +1,598 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button type="primary" @click="tokenStatus">tokenStatus</el-button>
|
||||
<el-button type="primary" @click="getUserToken">getUserToken</el-button>
|
||||
<el-button type="primary" @click="lifeHold">lifeHold</el-button>
|
||||
<el-button type="primary" @click="getMsg">getMsg</el-button>
|
||||
<el-button type="primary" @click="getHistory">getHistory</el-button>
|
||||
<div style="font-size: 2rem" class="text-center flex-auto font-bold text-gray-800">
|
||||
北斗短报文应用教学
|
||||
</div>
|
||||
<div style="width: 100%" class="flex justify-end">
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
v-if="!loginMark"
|
||||
type="primary"
|
||||
@click="toLogin"
|
||||
>
|
||||
登录
|
||||
</el-button>
|
||||
</div>
|
||||
<el-dropdown
|
||||
style="width: 100%"
|
||||
class="text-center felx justify-end items-center px-6"
|
||||
v-if="loginMark"
|
||||
>
|
||||
<el-button style="font-weight: bold" type="success" size="small">
|
||||
账户名: {{ userMsg.account_name }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(56, 188, 248, 0.1)"
|
||||
@click="editUser"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="primary">
|
||||
账户管理
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(56, 188, 248, 0.1)"
|
||||
@click="changePassword"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="primary">
|
||||
密码管理
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(240, 70, 70, 0.1)"
|
||||
@click="unLogin"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="danger">
|
||||
退出登录
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<UserManager @resetData="resetData" ref="userManagerRef" :msg="userMsg" />
|
||||
<PasswordManager ref="passwordManagerRef" />
|
||||
</div>
|
||||
<div class="w-h-full bg-sky-500">
|
||||
<div class="px-6">
|
||||
<el-text
|
||||
v-if="!loginMark"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
您还未登录,无法使用本系统!请先进行登录!
|
||||
</el-text>
|
||||
<br />
|
||||
<el-button
|
||||
v-if="tokenType == 'none' ? loginMark : false"
|
||||
type="primary"
|
||||
@click="getUserToken"
|
||||
>
|
||||
获取临时 token
|
||||
</el-button>
|
||||
<br />
|
||||
<el-button
|
||||
v-if="tokenType == 'short' ? true : false"
|
||||
type="primary"
|
||||
@click="lifeHold"
|
||||
>
|
||||
延长临时 token 可用时间(仅一次)
|
||||
</el-button>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? false : true"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
您的 token 是: {{ token }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? false : tokenType == 'long' ? false : true"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
token 剩余有效时间: {{ formatTimeInSeconds(remainder) }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? false : tokenType == 'long' ? false : true"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
token 预计过期时间: {{ overdueAt }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? false : true"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
黑名单状态: {{ attacksMark ? "token 已被临时封禁" : "token 可以正常使用" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? false : attacksMark"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
token 剩余封禁时间: {{ formatTimeInSeconds(banAt / 1000) }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? false : attacksMark"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
token 预计解封时间: {{ UnblockingAt }}
|
||||
</el-text>
|
||||
</div>
|
||||
<div class="w-h-full bg-sky-200 px-6">
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? attacksMark : loginMark"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
教学接口使用示例:
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? attacksMark : loginMark"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
获取最近发送的一条信息:
|
||||
</el-text>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white"
|
||||
>http://xxxx/xxxx/getMsg?token=</el-text
|
||||
>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white">{{
|
||||
msgRuleForm.token
|
||||
}}</el-text>
|
||||
<el-form
|
||||
ref="msgRuleFormRef"
|
||||
style="max-width: 600px"
|
||||
:model="msgRuleForm"
|
||||
:rules="msgRules"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="token" prop="token">
|
||||
<el-input
|
||||
v-model.number="msgRuleForm.token"
|
||||
@input="filterAlphanumericChars('msg')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getMsg(msgRuleFormRef)"> 提交请求 </el-button>
|
||||
<el-button @click="resetForm(msgRuleFormRef)"> 清除 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white"
|
||||
>请求结果:
|
||||
</el-text>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white">{{
|
||||
msgResult
|
||||
}}</el-text>
|
||||
<br />
|
||||
<el-text
|
||||
v-if="tokenType == 'none' ? attacksMark : loginMark"
|
||||
style="font-size: 1.2rem"
|
||||
class="font-bold text-gray-800 text-white"
|
||||
>
|
||||
获取若干条历史消息的接口:
|
||||
</el-text>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white"
|
||||
>http://xxxx/xxxx/getHistory?token=</el-text
|
||||
>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white">{{
|
||||
historyRuleForm.token
|
||||
}}</el-text>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white"
|
||||
>&limit=</el-text
|
||||
>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white">{{
|
||||
historyRuleForm.limit
|
||||
}}</el-text>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white"
|
||||
>&offset=</el-text
|
||||
>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white">{{
|
||||
historyRuleForm.offset
|
||||
}}</el-text>
|
||||
<el-form
|
||||
ref="historyRuleFormRef"
|
||||
style="max-width: 600px"
|
||||
:model="historyRuleForm"
|
||||
:rules="historyRules"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="token" prop="token">
|
||||
<el-input
|
||||
v-model.number="historyRuleForm.token"
|
||||
@input="filterAlphanumericChars('history')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="limit" prop="limit">
|
||||
<el-input v-model.number="historyRuleForm.limit" />
|
||||
</el-form-item>
|
||||
<el-form-item label="offset" prop="offset">
|
||||
<el-input v-model.number="historyRuleForm.offset" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getHistory(historyRuleFormRef)">
|
||||
提交请求
|
||||
</el-button>
|
||||
<el-button @click="resetForm(historyRuleFormRef)"> 清除 </el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white"
|
||||
>请求结果:
|
||||
</el-text>
|
||||
<el-text style="font-size: 1.2rem" class="font-bold text-gray-800 text-white">{{
|
||||
historyResult
|
||||
}}</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import $http from "../api/index";
|
||||
import $url from "../api/url";
|
||||
|
||||
import { encrypt } from "../utils/aes";
|
||||
|
||||
import UserManager from "../components/userManager.vue";
|
||||
import PasswordManager from "../components/changePassword.vue";
|
||||
|
||||
const loginMark = ref(false);
|
||||
const userManagerRef = ref(null);
|
||||
const userMsg = ref({});
|
||||
const passwordManagerRef = ref(null);
|
||||
const tokenType = ref("none");
|
||||
const token = ref("none");
|
||||
const remainder = ref("none");
|
||||
const overdueAt = ref(0);
|
||||
const attacksMark = ref(false);
|
||||
const banAt = ref(0);
|
||||
const UnblockingAt = ref(0);
|
||||
|
||||
const msgRuleFormRef = ref(null);
|
||||
const historyRuleFormRef = ref(null);
|
||||
|
||||
const msgRuleForm = reactive({
|
||||
token: "",
|
||||
});
|
||||
|
||||
const historyRuleForm = reactive({
|
||||
token: "",
|
||||
limit: 10,
|
||||
offset: 1,
|
||||
});
|
||||
|
||||
const msgResult = ref();
|
||||
const historyResult = ref();
|
||||
|
||||
const filterAlphanumericChars = (str) => {
|
||||
if (str == "msg") {
|
||||
msgRuleForm.token = msgRuleForm.token.replace(/[^a-zA-Z0-9]/g, "");
|
||||
} else if (str == "history") {
|
||||
historyRuleForm.token = historyRuleForm.token.replace(/[^a-zA-Z0-9]/g, "");
|
||||
}
|
||||
};
|
||||
|
||||
const checkToken = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入已获取到的 token"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const checkLimit = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入一次请求要获取的数据量 limit"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const checkOffset = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入需要跳过的页数 offset"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const msgRules = reactive({
|
||||
token: [{ validator: checkToken, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const historyRules = reactive({
|
||||
token: [{ validator: checkToken, trigger: "blur" }],
|
||||
limit: [{ validator: checkLimit, trigger: "blur" }],
|
||||
offset: [{ validator: checkOffset, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
let timer = null,
|
||||
banTimer = null;
|
||||
|
||||
const formatTimeInSeconds = (seconds) => {
|
||||
const day = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
const pad = (num) => (num < 10 ? "0" + num : num);
|
||||
|
||||
return `${pad(day)}天${pad(hours)}小时${pad(minutes)}分钟${pad(secs)}秒`;
|
||||
};
|
||||
|
||||
const tokenStatus = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.tokenStatus, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
console.log(res.data);
|
||||
tokenType.value = res.data.type;
|
||||
if (res.data.type !== "none") {
|
||||
token.value = res.data.token;
|
||||
attacksMark.value = res.data.attacksMark;
|
||||
if (res.data.attacksMark) {
|
||||
banAt.value = res.data.banAt + 3600000 - new Date().getTime();
|
||||
clearInterval(banTimer);
|
||||
banTimer = setInterval(() => {
|
||||
banAt.value = banAt.value - 1000;
|
||||
}, 1000);
|
||||
UnblockingAt.value = moment
|
||||
.utc(new Date(res.data.banAt + 3600000))
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH:mm:ss");
|
||||
}
|
||||
if (res.data.type !== "long") {
|
||||
remainder.value = res.data.remainder;
|
||||
clearInterval(timer);
|
||||
timer = setInterval(() => {
|
||||
remainder.value = remainder.value - 1;
|
||||
}, 1000);
|
||||
overdueAt.value = moment
|
||||
.utc(new Date(new Date().getTime() + res.data.remainder * 1000))
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH:mm:ss");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(404);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getUserToken = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.getUserToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
console.log(res.data);
|
||||
tokenStatus();
|
||||
} else {
|
||||
console.log(404);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const lifeHold = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.lifeHold, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
console.log(res.data);
|
||||
tokenStatus();
|
||||
} else {
|
||||
console.log(404);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getMsg = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const data = {
|
||||
token: msgRuleForm.token,
|
||||
};
|
||||
$http.get($url.getMsg, data).then((res) => {
|
||||
console.log(res.data);
|
||||
msgResult.value = res.data;
|
||||
const code = res.data.code;
|
||||
if (code === 200) {
|
||||
console.log(200);
|
||||
} else if (code == "t401") {
|
||||
console.log(401);
|
||||
} else if (code == 403) {
|
||||
console.log(403);
|
||||
} else if (code == 500) {
|
||||
console.log(500);
|
||||
} else {
|
||||
console.log(404);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("请求提交失败");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getHistory = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
const data = {
|
||||
token: historyRuleForm.token,
|
||||
limit: historyRuleForm.limit,
|
||||
offset: historyRuleForm.offset,
|
||||
};
|
||||
$http.get($url.getHistory, data).then((res) => {
|
||||
console.log(res.data);
|
||||
historyResult.value = res.data;
|
||||
const code = res.data.code;
|
||||
if (code === 200) {
|
||||
console.log(200);
|
||||
} else if (code == "t401") {
|
||||
console.log(401);
|
||||
} else if (code == 403) {
|
||||
console.log(403);
|
||||
} else if (code == 500) {
|
||||
console.log(500);
|
||||
} else {
|
||||
console.log(404);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("请求提交失败");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const toLogin = () => {
|
||||
localStorage.clear();
|
||||
const data = {
|
||||
origin_url: window.location.href,
|
||||
};
|
||||
$http.post($url.toLogin, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
window.location.href = res.data.redirect;
|
||||
} else {
|
||||
router.push({ path: "/404page" });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const editUser = () => {
|
||||
if (userManagerRef.value) {
|
||||
userManagerRef.value.dialogVisible = true;
|
||||
userManagerRef.value.reset();
|
||||
}
|
||||
};
|
||||
|
||||
const changePassword = () => {
|
||||
if (passwordManagerRef.value) {
|
||||
passwordManagerRef.value.dialogVisible = true;
|
||||
}
|
||||
};
|
||||
|
||||
const unLogin = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
access_token: localStorage.access_token,
|
||||
refresh_token: localStorage.refresh_token,
|
||||
};
|
||||
$http.post($url.unLogin, data).then((res) => {
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
const resetData = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.getUser, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userMsg.value = res.data.userData;
|
||||
} else {
|
||||
ElMessage.error("获取用户信息失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let getCode = window.location.href.indexOf("?");
|
||||
if (getCode != "-1") {
|
||||
let code = window.location.href.substring(getCode + 1);
|
||||
const data = {
|
||||
code: code,
|
||||
type: encrypt("code"),
|
||||
};
|
||||
$http.post($url.getToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("access_token", res.data.access_token);
|
||||
localStorage.setItem("refresh_token", res.data.refresh_token);
|
||||
window.location.href = res.data.redirect;
|
||||
}
|
||||
});
|
||||
} else if (
|
||||
localStorage.data &&
|
||||
localStorage.data !== "" &&
|
||||
localStorage.data !== null &&
|
||||
localStorage.data !== undefined &&
|
||||
localStorage.data !== NaN &&
|
||||
localStorage.access_token &&
|
||||
localStorage.access_token !== "" &&
|
||||
localStorage.access_token !== null &&
|
||||
localStorage.access_token !== undefined &&
|
||||
localStorage.access_token !== NaN
|
||||
) {
|
||||
const data = {
|
||||
access_token: localStorage.access_token,
|
||||
data: localStorage.data,
|
||||
};
|
||||
$http.post($url.verify, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
loginMark.value = true;
|
||||
resetData();
|
||||
tokenStatus();
|
||||
}
|
||||
});
|
||||
} else if (
|
||||
localStorage.data &&
|
||||
localStorage.data !== "" &&
|
||||
localStorage.data !== null &&
|
||||
localStorage.data !== undefined &&
|
||||
localStorage.data !== NaN &&
|
||||
localStorage.refresh_token &&
|
||||
localStorage.refresh_token !== "" &&
|
||||
localStorage.refresh_token !== null &&
|
||||
localStorage.refresh_token !== undefined &&
|
||||
localStorage.refresh_token !== NaN
|
||||
) {
|
||||
const data = {
|
||||
code: localStorage.refresh_token,
|
||||
type: localStorage.data,
|
||||
};
|
||||
$http.post($url.getToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("access_token", res.data.access_token);
|
||||
localStorage.setItem("refresh_token", res.data.refresh_token);
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
13
tailwind.config.js
Normal file
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: [],
|
||||
}
|
||||
43
vite.config.js
Normal file
43
vite.config.js
Normal file
@@ -0,0 +1,43 @@
|
||||
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'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
publicPath: './',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server: {
|
||||
//用来配置跨域
|
||||
host: '127.0.0.1',
|
||||
port: 7009,
|
||||
proxy: {
|
||||
'/authentication': {
|
||||
target: 'http://127.0.0.1:8011',//鉴权服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/authentication/, '')
|
||||
},
|
||||
'/teachingApi': {
|
||||
target: 'http://127.0.0.1:8017',//资源服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/teachingApi/, '')
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user