北斗应用技术培训报名系统
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 @@
|
||||
# open-map-assistant
|
||||
|
||||
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
|
||||
```
|
||||
49
index.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="w-h-full">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/src/assets/bdjy.png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>北斗应用技术培训报名系统</title>
|
||||
<style>
|
||||
/* 当屏幕宽度大于或等于3000px时 */
|
||||
@media (min-width: 3000px) {
|
||||
html {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度在1920px到2999px之间时 */
|
||||
@media (min-width: 1920px) and (max-width: 2999px) {
|
||||
html {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度在750px到1919px之间时 */
|
||||
@media (min-width: 750px) and (max-width: 1919px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度在600px到749px之间时 */
|
||||
@media (min-width: 600px) and (max-width: 749px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度小于600px时 */
|
||||
@media (max-width: 599px) {
|
||||
html {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
</body>
|
||||
</html>
|
||||
8
jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
6813
package-lock.json
generated
Normal file
37
package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "bd-training-exam",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=7021",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^1.6.8",
|
||||
"bd-training-exam": "file:",
|
||||
"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",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue3-slide-verify": "^1.1.5",
|
||||
"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
@@ -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 @@
|
||||
<script setup>
|
||||
import { RouterView } from "vue-router";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
129
src/api/index.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import axios from "axios";
|
||||
import $url from "./url";
|
||||
import router from "../router";
|
||||
import { encrypt } from "../utils/aes";
|
||||
|
||||
// 添加请求拦截器
|
||||
axios.interceptors.request.use(function (config) {
|
||||
// 在发送请求之前做些什么
|
||||
if (localStorage.access_token && localStorage.access_token !== "" && localStorage.access_token !== null && localStorage.access_token !== undefined && localStorage.access_token !== NaN) {
|
||||
if (config.headers) {
|
||||
config.headers.authorization = localStorage.access_token;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}, function (error) {
|
||||
// 对请求错误做些什么
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 转到登录页面
|
||||
const toLogin = () => {
|
||||
// localStorage.clear();
|
||||
router.push({ path: '/initialService' });
|
||||
};
|
||||
|
||||
// 添加响应拦截器
|
||||
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 === 404) {
|
||||
// localStorage.clear();
|
||||
// //转到404页面
|
||||
// router.push({ path: '/404page' });
|
||||
// }
|
||||
// if (response.data.code === 402) {
|
||||
// toLogin();
|
||||
// }
|
||||
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.loginCode && localStorage.loginCode !== "" && localStorage.loginCode !== null && localStorage.loginCode !== undefined && localStorage.loginCode !== NaN) {
|
||||
const data = {
|
||||
code: localStorage.loginCode,
|
||||
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);
|
||||
localStorage.removeItem('loginCode');
|
||||
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);
|
||||
},
|
||||
}
|
||||
53
src/api/url.js
Normal file
@@ -0,0 +1,53 @@
|
||||
export default {
|
||||
// 退出登录
|
||||
unLogin: "/authentication/unLogin",
|
||||
// 登录请求接口
|
||||
elogin: "/authentication/elogin",
|
||||
// 获取登录状态
|
||||
loginStatus: "/authentication/loginStatus",
|
||||
// 用于获取token
|
||||
getToken: "/authentication/getToken",
|
||||
// 用于验证token
|
||||
verify: "/authentication/verify",
|
||||
// 获取用户基本信息
|
||||
getUser: "/authentication/getUser",
|
||||
// 修改用户基本信息
|
||||
editUser: "/authentication/editUser",
|
||||
// 修改密码
|
||||
changePassword: "/authentication/changePassword",
|
||||
// 向手机发送短信
|
||||
sendSMS: "/authentication/sendSMS",
|
||||
// 校验短信验证码
|
||||
checkCode: "/authentication/checkCode",
|
||||
|
||||
// 系统获取考试信息接口
|
||||
examMsg: "/bdTrainingExam/examMsg",
|
||||
// 比较身份证号
|
||||
diffIdentityCard: "/bdTrainingExam/diffIdentityCard",
|
||||
// 比较邮箱
|
||||
diffEmail: "/bdTrainingExam/diffEmail",
|
||||
// 比较电话号码
|
||||
diffPhone: "/bdTrainingExam/diffPhone",
|
||||
// 用户注册接口
|
||||
registered: "/bdTrainingExam/registered",
|
||||
// 信息完整性,合法性校验
|
||||
checkInfo: "/bdTrainingExam/checkInfo",
|
||||
// 用户照片是否存在判断
|
||||
checkPhoto: "/bdTrainingExam/checkPhoto",
|
||||
// 身份证二要素核验
|
||||
CNTEV: "/bdTrainingExam/CNTEV",
|
||||
// 用户上传照片接口
|
||||
uploadPhoto: "/bdTrainingExam/uploadPhoto",
|
||||
// 用户找回密码接口
|
||||
resetPassword: "/bdTrainingExam/resetPassword",
|
||||
// 用户报名接口
|
||||
application: "/bdTrainingExam/application",
|
||||
// 用户取消报名接口
|
||||
cancelApplication: "/bdTrainingExam/cancelApplication",
|
||||
// 用户查看所有考试接口
|
||||
examList: "/bdTrainingExam/examList",
|
||||
// 用户查看考试结果接口
|
||||
gradesList: "/bdTrainingExam/gradesList",
|
||||
// 用户查看报名历史接口
|
||||
applicationList: "/bdTrainingExam/applicationList",
|
||||
}
|
||||
BIN
src/assets/bdjy.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src/assets/bdjyLogo.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
src/assets/bdqhLogo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/bg.jpg
Normal file
|
After Width: | Height: | Size: 760 KiB |
BIN
src/assets/certificate.jpg
Normal file
|
After Width: | Height: | Size: 928 KiB |
BIN
src/assets/default.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/exam.jpg
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
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/correctExample.png
Normal file
|
After Width: | Height: | Size: 843 B |
BIN
src/assets/img/ephemeris_search.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/errorsExample.png
Normal file
|
After Width: | Height: | Size: 5.8 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/know.png
Normal file
|
After Width: | Height: | Size: 157 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/pay.jpg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
src/assets/qrcode.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
src/assets/training.jpg
Normal file
|
After Width: | Height: | Size: 125 KiB |
539
src/components/features/applicationList.vue
Normal file
@@ -0,0 +1,539 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<el-card>
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<TabelTools @showTools="showTools" :dialogObj="applicationMsgActive" />
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
height="47rem"
|
||||
max-height="47rem"
|
||||
table-layout="auto"
|
||||
scrollbar-always-on
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column min-width="100" column-key="exam_uid">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchExamUidKeyRef"
|
||||
holderText="输入考试编号以搜索相关数据"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试编号 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_uid }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="180" column-key="exam_name">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchExamNameKeyRef"
|
||||
holderText="输入考试名称以搜索相关数据"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试名称 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.examination.exam_name }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" column-key="status">
|
||||
<template #header>
|
||||
<SelectSearch
|
||||
v-if="dialogSearchVisible"
|
||||
ref="StatusSelectSearchRef"
|
||||
holderText="选择报名状态以搜索相关数据"
|
||||
:options="statusOptions"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 报名状态 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(statusOptions, scope.row.status)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
{{ statusText[scope.row.status] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="140" column-key="created_at">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="DateTimePickerRef"
|
||||
DateTimePicker="applicationMsgDateTimePicker"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 报名时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortDateTimeRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.created_at }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="140" column-key="application_end">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="ApplicationEndPickerRef"
|
||||
DateTimePicker="applicationMsgDateTimePicker"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 报名结束时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortApplicationEndRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.application_end }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="140" column-key="exam_start">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="ExamStartPickerRef"
|
||||
DateTimePicker="gradesMsgDateTimePicker"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试开始时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortExamStartRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_start }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column min-width="140" column-key="exam_end">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="ExamEndPickerRef"
|
||||
DateTimePicker="gradesMsgDateTimePicker"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试结束时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortExamEndRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_end }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 表格 -->
|
||||
<Pagination
|
||||
ref="PaginationRef"
|
||||
:active="applicationMsgActive"
|
||||
@sizeChangeBack="changePage"
|
||||
@search="applicationMsg"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted, nextTick } from "vue";
|
||||
|
||||
import TabelTools from "../operate/tabelTools.vue";
|
||||
import SearchKey from "../search/searchKey.vue";
|
||||
import SortSearch from "../search/sortSearch.vue";
|
||||
import SelectSearch from "../search/selectSearch.vue";
|
||||
import DateTimePicker from "../search/dateTimePicker.vue";
|
||||
import Pagination from "../search/pagination.vue";
|
||||
|
||||
import applicationMsgActiveStore from "../../stores/applicationMsg";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { judgment } from "../../utils/judgment.js";
|
||||
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
|
||||
|
||||
const applicationMsgActive = applicationMsgActiveStore();
|
||||
|
||||
const SearchExamUidKeyRef = ref(null);
|
||||
const SearchExamNameKeyRef = ref(null);
|
||||
const StatusSelectSearchRef = ref(null);
|
||||
const SortDateTimeRef = ref(null);
|
||||
const DateTimePickerRef = ref(null);
|
||||
const SortExamStartRef = ref(null);
|
||||
const SortApplicationEndRef = ref(null);
|
||||
const SortExamEndRef = ref(null);
|
||||
const ApplicationEndPickerRef = ref(null);
|
||||
const ExamStartPickerRef = ref(null);
|
||||
const ExamEndPickerRef = ref(null);
|
||||
const PaginationRef = ref(null);
|
||||
const dialogSearchVisible = ref(false);
|
||||
const dialogSortVisible = ref(false);
|
||||
const userTableRef = ref();
|
||||
const offsetOff = ref(true);
|
||||
const pageCount = ref(0);
|
||||
|
||||
const searchOptions = reactive({
|
||||
exam_uid: "",
|
||||
exam_name: "",
|
||||
status: "",
|
||||
created_at: "",
|
||||
application_end: "",
|
||||
exam_start: "",
|
||||
exam_end: "",
|
||||
});
|
||||
|
||||
const sortOptions = reactive({
|
||||
created_at: false,
|
||||
application_end: false,
|
||||
exam_start: false,
|
||||
exam_end: false,
|
||||
});
|
||||
|
||||
const tableData = ref([]);
|
||||
|
||||
const statusText = ["报名审核中", "报名成功", "报名失败", "取消报名"];
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: "0",
|
||||
label: "报名审核中",
|
||||
style: "color: rgb(56, 188, 248)",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "报名成功",
|
||||
style: "color: rgb(16, 184, 130)",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "报名失败",
|
||||
style: "color: rgb(240, 70, 70)",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "取消报名",
|
||||
style: "color: rgb(255, 200, 0)",
|
||||
},
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
{
|
||||
value: "0",
|
||||
label: "报名审核中",
|
||||
style:
|
||||
"color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "报名成功",
|
||||
style:
|
||||
"color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "报名失败",
|
||||
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "3",
|
||||
label: "取消报名",
|
||||
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
|
||||
},
|
||||
];
|
||||
|
||||
let applicationData = reactive({});
|
||||
let applicationDataClone = reactive({});
|
||||
|
||||
const styleShunt = (options, data) => {
|
||||
let nowStyle;
|
||||
options.forEach((item) => {
|
||||
if (data == item.value) {
|
||||
nowStyle = item.style;
|
||||
}
|
||||
});
|
||||
return nowStyle;
|
||||
};
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
dialogSearchVisible.value = !dialogSearchVisible.value;
|
||||
applicationMsgActive.dialogSearchVisible = dialogSearchVisible.value;
|
||||
break;
|
||||
case "sort":
|
||||
dialogSortVisible.value = !dialogSortVisible.value;
|
||||
applicationMsgActive.dialogSortVisible = dialogSortVisible.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const applicationMsg = () => {
|
||||
judgment(searchOptions, "exam_uid", SearchExamUidKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_name", SearchExamNameKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "status", StatusSelectSearchRef.value, "searchMsg");
|
||||
judgment(searchOptions, "created_at", DateTimePickerRef.value, "searchMsg");
|
||||
judgment(searchOptions, "application_end", ApplicationEndPickerRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_start", ExamStartPickerRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_end", ExamEndPickerRef.value, "searchMsg");
|
||||
judgment(sortOptions, "created_at", SortDateTimeRef.value, "searchMsg");
|
||||
judgment(sortOptions, "application_end", SortApplicationEndRef.value, "searchMsg");
|
||||
judgment(sortOptions, "exam_start", SortExamStartRef.value, "searchMsg");
|
||||
judgment(sortOptions, "exam_end", SortExamEndRef.value, "searchMsg");
|
||||
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
|
||||
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
|
||||
PaginationRef.value.handleCurrentChange(1);
|
||||
offsetOff.value = false;
|
||||
}
|
||||
} else {
|
||||
offsetOff.value = true;
|
||||
}
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
offset: PaginationRef.value.offset,
|
||||
limit: PaginationRef.value.limit,
|
||||
searchOptions: searchOptions,
|
||||
sortOptions: sortOptions,
|
||||
};
|
||||
$http.post($url.applicationList, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
tableData.value = res.data.applicationMsg;
|
||||
tableData.value.forEach((item) => {
|
||||
item.origin_time = item.created_at;
|
||||
item.created_at = moment
|
||||
.utc(item.created_at)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
item.origin_applicationEnd = item.examination.application_end;
|
||||
item.application_end = moment
|
||||
.utc(item.examination.application_end)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
item.origin_start = item.examination.exam_start;
|
||||
item.exam_start = moment
|
||||
.utc(item.examination.exam_start)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
item.origin_end = item.examination.exam_end;
|
||||
item.exam_end = moment
|
||||
.utc(item.examination.exam_end)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
});
|
||||
PaginationRef.value.dataCount = res.data.count;
|
||||
applicationMsgActive.dataCount = PaginationRef.value.dataCount;
|
||||
pageCount.value = Math.ceil(
|
||||
PaginationRef.value.dataCount / PaginationRef.value.limit
|
||||
);
|
||||
applicationMsgActive.pageCount = pageCount.value;
|
||||
if (!applicationData[PaginationRef.value.offset]) return;
|
||||
applicationData[PaginationRef.value.offset].forEach((element) => {
|
||||
tableData.value.forEach((item) => {
|
||||
if (item.created_at == element.created_at) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
if (item.application_end == element.application_end) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
if (item.exam_start == element.exam_start) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
if (item.exam_end == element.exam_end) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const changePage = (val) => {
|
||||
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
|
||||
applicationData = {};
|
||||
for (let i = 0; i < pageCount.value; i++) {
|
||||
applicationData[i + 1] = [];
|
||||
for (const key in applicationDataClone) {
|
||||
applicationDataClone[key].forEach((element) => {
|
||||
// 判断数据在第几页
|
||||
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
|
||||
applicationData[i + 1].push(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectionChange = (val) => {
|
||||
applicationData[PaginationRef.value.offset] = val;
|
||||
tableData.value.forEach((item, index) => {
|
||||
applicationData[PaginationRef.value.offset].forEach((element) => {
|
||||
if (item.created_at == element.created_at) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
if (item.application_end == element.application_end) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
if (item.exam_start == element.exam_start) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
if (item.exam_end == element.exam_end) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
applicationDataClone = JSON.parse(JSON.stringify(applicationData));
|
||||
};
|
||||
|
||||
defineExpose({ applicationMsg });
|
||||
|
||||
watch(searchOptions, (value) => {
|
||||
applicationMsgActive.searchOptions = value;
|
||||
});
|
||||
|
||||
watch(sortOptions, (value) => {
|
||||
applicationMsgActive.sortOptions = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
pageCount.value = applicationMsgActive.pageCount;
|
||||
dialogSearchVisible.value = applicationMsgActive.dialogSearchVisible;
|
||||
dialogSortVisible.value = applicationMsgActive.dialogSortVisible;
|
||||
|
||||
if (dialogSearchVisible.value) {
|
||||
searchOptions.exam_uid = applicationMsgActive.searchOptions.exam_uid;
|
||||
searchOptions.exam_name = applicationMsgActive.searchOptions.exam_name;
|
||||
searchOptions.status = applicationMsgActive.searchOptions.status;
|
||||
searchOptions.created_at = applicationMsgActive.searchOptions.created_at;
|
||||
searchOptions.application_end = applicationMsgActive.searchOptions.application_end;
|
||||
searchOptions.exam_start = gradesMsgActive.searchOptions.exam_start;
|
||||
searchOptions.exam_end = gradesMsgActive.searchOptions.exam_end;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
sortOptions.created_at = applicationMsgActive.sortOptions.created_at;
|
||||
sortOptions.application_end = applicationMsgActive.sortOptions.application_end;
|
||||
sortOptions.exam_start = gradesMsgActive.sortOptions.exam_start;
|
||||
sortOptions.exam_end = gradesMsgActive.sortOptions.exam_end;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
applicationMsg();
|
||||
}, 200);
|
||||
|
||||
setTimeout(() => {
|
||||
if (dialogSearchVisible.value) {
|
||||
SearchExamUidKeyRef.value.searchMsg = applicationMsgActive.searchOptions.exam_uid;
|
||||
SearchExamNameKeyRef.value.searchMsg = applicationMsgActive.searchOptions.exam_name;
|
||||
StatusSelectSearchRef.value.searchMsg = applicationMsgActive.searchOptions.status;
|
||||
DateTimePickerRef.value.created_at = applicationMsgActive.searchOptions.created_at;
|
||||
ApplicationEndPickerRef.value.application_end =
|
||||
applicationMsgActive.searchOptions.application_end;
|
||||
ExamStartPickerRef.value.exam_start = gradesMsgActive.searchOptions.exam_start;
|
||||
ExamEndPickerRef.value.exam_end = gradesMsgActive.searchOptions.exam_end;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
SortDateTimeRef.value.searchMsg = applicationMsgActive.sortOptions.created_at;
|
||||
SortApplicationEndRef.value.searchMsg =
|
||||
applicationMsgActive.sortOptions.application_end;
|
||||
SortExamStartRef.value.searchMsg = gradesMsgActive.sortOptions.exam_start;
|
||||
SortExamEndRef.value.searchMsg = gradesMsgActive.sortOptions.exam_end;
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
752
src/components/features/examList.vue
Normal file
@@ -0,0 +1,752 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<el-card>
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<TabelTools @showTools="showTools" :dialogObj="examMsgActive" />
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
height="47rem"
|
||||
max-height="47rem"
|
||||
table-layout="auto"
|
||||
scrollbar-always-on
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<TabelSelect
|
||||
@applicationDialog="applicationDialog"
|
||||
@cancelApplicationDialog="cancelApplicationDialog"
|
||||
/>
|
||||
<el-table-column min-width="100" column-key="uid">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchUidKeyRef"
|
||||
holderText="输入考试编号以搜索相关数据"
|
||||
@search="examMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试编号 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.uid }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="180" column-key="exam_name">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchExamNameKeyRef"
|
||||
holderText="输入考试名称以搜索相关数据"
|
||||
@search="examMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试名称 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_name }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" column-key="status">
|
||||
<template #header>
|
||||
<SelectSearch
|
||||
v-if="dialogSearchVisible"
|
||||
ref="StatusSelectSearchRef"
|
||||
holderText="选择编码类型以搜索相关数据"
|
||||
:options="statusOptions"
|
||||
@search="examMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试状态 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(statusOptions, scope.row.status)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
{{ statusText[scope.row.status] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="140" column-key="application_end">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="DateTimePickerRef"
|
||||
DateTimePicker="examMsgDateTimePicker"
|
||||
@search="examMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 报名结束时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortDateTimeRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="examMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.application_end }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="140" column-key="exam_start">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="DateTimePickerRef"
|
||||
DateTimePicker="examMsgDateTimePicker"
|
||||
@search="examMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试开始时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortDateTimeRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="examMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_start }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="140" column-key="exam_end">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="DateTimePickerRef"
|
||||
DateTimePicker="examMsgDateTimePicker"
|
||||
@search="examMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试结束时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortDateTimeRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="examMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_end }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 表格 -->
|
||||
<Pagination
|
||||
ref="PaginationRef"
|
||||
:active="examMsgActive"
|
||||
@sizeChangeBack="changePage"
|
||||
@search="examMsg"
|
||||
/>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="个人信息验证"
|
||||
width="600"
|
||||
center
|
||||
align-center
|
||||
>
|
||||
<div
|
||||
:style="getPhoto ? 'color:rgb(16, 184, 130)' : 'color:rgb(240, 70, 70)'"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ getPhoto ? "照片已上传!" : "请先上传照片!" }}
|
||||
<el-icon v-if="getPhoto" color="rgb(16, 184, 130)"><Select /></el-icon>
|
||||
<el-icon v-if="!getPhoto" color="rgb(240, 70, 70)"><CloseBold /></el-icon>
|
||||
</div>
|
||||
<br />
|
||||
<div
|
||||
:style="
|
||||
getInfo == '1'
|
||||
? 'color:rgb(16, 184, 130)'
|
||||
: getInfo == '0'
|
||||
? 'color:rgb(56, 188, 248)'
|
||||
: 'color:rgb(240, 70, 70)'
|
||||
"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{
|
||||
getInfo == "1"
|
||||
? "身份证二要素核验通过!"
|
||||
: getInfo == "0"
|
||||
? "身份证二要素核验中!"
|
||||
: "身份证二要素核验未通过!"
|
||||
}}
|
||||
<el-icon v-if="getInfo == '0'" color="rgb(56, 188, 248)"><Loading /></el-icon>
|
||||
<el-icon v-if="getInfo == '1'" color="rgb(16, 184, 130)"><Select /></el-icon>
|
||||
<el-icon v-if="getInfo == '2'" color="rgb(240, 70, 70)"><CloseBold /></el-icon>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="resetForm">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="getPhoto && getInfo != '1'"
|
||||
@click="requestOperate"
|
||||
>
|
||||
确认报名
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="cancelDialogVisible"
|
||||
title="取消报名"
|
||||
width="600"
|
||||
center
|
||||
align-center
|
||||
>
|
||||
<div
|
||||
style="color: rgb(240, 70, 70)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
您将要取消{{
|
||||
cancelMsg
|
||||
}}进行的“北斗应用技术培训考试“报名,请尽快选择新的考试场次。
|
||||
<br />
|
||||
确定要取消报名?
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="resetForm">返回</el-button>
|
||||
<el-button type="primary" :disabled="timeSwitch" @click="cancelRequestOperate">
|
||||
{{ timeSwitch ? `取消报名${time}秒)` : "取消报名" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="confirmDialogVisible"
|
||||
title="报名已提交"
|
||||
width="600"
|
||||
center
|
||||
align-center
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<div
|
||||
style="color: rgb(10, 10, 10)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
恭喜,您已提交于{{ applicationMsg }}进行的“北斗应用技术培训考试“报名。
|
||||
<br />
|
||||
请您尽快完成缴费,缴费金额和汇款信息请详见首页的注意事项。
|
||||
<br />
|
||||
若您已经完成缴费,请忽略此提示。
|
||||
<br />
|
||||
报名审核人员将会在2个工作日内完成您的报名审核。
|
||||
<br />
|
||||
请按时参与培训和考试,并预祝您取得好成绩!
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" :disabled="timeSwitch" @click="confirmOperate">
|
||||
{{
|
||||
timeSwitch
|
||||
? `我已阅读并确认注意事项(${time}秒)`
|
||||
: "我已阅读并确认注意事项"
|
||||
}}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted, nextTick, getCurrentInstance } from "vue";
|
||||
import { Select, CloseBold, Loading } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import TabelTools from "../operate/tabelTools.vue";
|
||||
import TabelSelect from "../operate/tabelSelect.vue";
|
||||
import SearchKey from "../search/searchKey.vue";
|
||||
import SortSearch from "../search/sortSearch.vue";
|
||||
import SelectSearch from "../search/selectSearch.vue";
|
||||
import DateTimePicker from "../search/dateTimePicker.vue";
|
||||
import Pagination from "../search/pagination.vue";
|
||||
|
||||
import { useCommunicationStore } from "../../stores/communicationStore";
|
||||
import examMsgActiveStore from "../../stores/examMsg";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { judgment } from "../../utils/judgment.js";
|
||||
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const communicationStore = useCommunicationStore();
|
||||
const examMsgActive = examMsgActiveStore();
|
||||
|
||||
const SearchUidKeyRef = ref(null);
|
||||
const SearchExamNameKeyRef = ref(null);
|
||||
const SortApplicationEndRef = ref(null);
|
||||
const SortExamStartRef = ref(null);
|
||||
const SortExamEndRef = ref(null);
|
||||
const StatusSelectSearchRef = ref(null);
|
||||
const ApplicationEndPickerRef = ref(null);
|
||||
const ExamStartPickerRef = ref(null);
|
||||
const ExamEndPickerRef = ref(null);
|
||||
const PaginationRef = ref(null);
|
||||
const dialogSearchVisible = ref(false);
|
||||
const dialogSortVisible = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const confirmDialogVisible = ref(false);
|
||||
const applicationMsg = ref("");
|
||||
const cancelDialogVisible = ref(false);
|
||||
const cancelMsg = ref("");
|
||||
const timeSwitch = ref(false);
|
||||
const time = ref(5);
|
||||
const getPhoto = ref(false);
|
||||
const getInfo = ref("0");
|
||||
const userTableRef = ref();
|
||||
const offsetOff = ref(true);
|
||||
const pageCount = ref(0);
|
||||
|
||||
const searchOptions = reactive({
|
||||
uid: "",
|
||||
exam_name: "",
|
||||
status: "",
|
||||
application_end: "",
|
||||
exam_start: "",
|
||||
exam_end: "",
|
||||
});
|
||||
|
||||
const sortOptions = reactive({
|
||||
application_end: false,
|
||||
exam_start: false,
|
||||
exam_end: false,
|
||||
});
|
||||
|
||||
const tableData = ref([]);
|
||||
|
||||
const statusText = ["即将进行", "正在进行", "已经结束"];
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: "0",
|
||||
label: "即将进行",
|
||||
style: "color: rgb(16, 184, 130)",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "正在进行",
|
||||
style: "color: rgb(56, 188, 248)",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "已经结束",
|
||||
style: "color: rgb(240, 70, 70)",
|
||||
},
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
{
|
||||
value: "0",
|
||||
label: "即将进行",
|
||||
style:
|
||||
"color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "正在进行",
|
||||
style:
|
||||
"color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "已经结束",
|
||||
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
|
||||
},
|
||||
];
|
||||
|
||||
let applicationData = reactive({});
|
||||
let examData = reactive({});
|
||||
let examDataClone = reactive({});
|
||||
|
||||
const styleShunt = (options, data) => {
|
||||
let nowStyle;
|
||||
options.forEach((item) => {
|
||||
if (data == item.value) {
|
||||
nowStyle = item.style;
|
||||
}
|
||||
});
|
||||
return nowStyle;
|
||||
};
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
dialogSearchVisible.value = !dialogSearchVisible.value;
|
||||
examMsgActive.dialogSearchVisible = dialogSearchVisible.value;
|
||||
break;
|
||||
case "sort":
|
||||
dialogSortVisible.value = !dialogSortVisible.value;
|
||||
examMsgActive.dialogSortVisible = dialogSortVisible.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const examMsg = () => {
|
||||
judgment(searchOptions, "uid", SearchUidKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_name", SearchExamNameKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "status", StatusSelectSearchRef.value, "searchMsg");
|
||||
judgment(searchOptions, "application_end", ApplicationEndPickerRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_start", ExamStartPickerRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_end", ExamEndPickerRef.value, "searchMsg");
|
||||
judgment(sortOptions, "application_end", SortApplicationEndRef.value, "searchMsg");
|
||||
judgment(sortOptions, "exam_start", SortExamStartRef.value, "searchMsg");
|
||||
judgment(sortOptions, "exam_end", SortExamEndRef.value, "searchMsg");
|
||||
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
|
||||
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
|
||||
PaginationRef.value.handleCurrentChange(1);
|
||||
offsetOff.value = false;
|
||||
}
|
||||
} else {
|
||||
offsetOff.value = true;
|
||||
}
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
offset: PaginationRef.value.offset,
|
||||
limit: PaginationRef.value.limit,
|
||||
searchOptions: searchOptions,
|
||||
sortOptions: sortOptions,
|
||||
};
|
||||
$http.post($url.examList, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
setTimeout(() => {
|
||||
window.addEventListener("beforeunload", () => {
|
||||
localStorage.clear();
|
||||
});
|
||||
}, 6000);
|
||||
tableData.value = res.data.examMsg;
|
||||
tableData.value.forEach((item) => {
|
||||
item.origin_applicationEnd = item.application_end;
|
||||
item.application_end = moment
|
||||
.utc(item.application_end)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
item.origin_examStart = item.exam_start;
|
||||
item.exam_start = moment
|
||||
.utc(item.exam_start)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
item.origin_examEnd = item.exam_end;
|
||||
item.exam_end = moment
|
||||
.utc(item.exam_end)
|
||||
.add(8, "hours")
|
||||
.format("YYYY年MM月DD日HH时mm分");
|
||||
});
|
||||
PaginationRef.value.dataCount = res.data.count;
|
||||
examMsgActive.dataCount = PaginationRef.value.dataCount;
|
||||
pageCount.value = Math.ceil(
|
||||
PaginationRef.value.dataCount / PaginationRef.value.limit
|
||||
);
|
||||
examMsgActive.pageCount = pageCount.value;
|
||||
if (!examData[PaginationRef.value.offset]) return;
|
||||
examData[PaginationRef.value.offset].forEach((element) => {
|
||||
tableData.value.forEach((item) => {
|
||||
if (item.application_end == element.application_end) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
if (item.exam_start == element.exam_start) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
if (item.exam_end == element.exam_end) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const changePage = (val) => {
|
||||
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
|
||||
examData = {};
|
||||
for (let i = 0; i < pageCount.value; i++) {
|
||||
examData[i + 1] = [];
|
||||
for (const key in examDataClone) {
|
||||
examDataClone[key].forEach((element) => {
|
||||
// 判断数据在第几页
|
||||
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
|
||||
examData[i + 1].push(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectionChange = (val) => {
|
||||
examData[PaginationRef.value.offset] = val;
|
||||
tableData.value.forEach((item, index) => {
|
||||
examData[PaginationRef.value.offset].forEach((element) => {
|
||||
if (item.application_end == element.application_end) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
if (item.exam_start == element.exam_start) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
if (item.exam_end == element.exam_end) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
examDataClone = JSON.parse(JSON.stringify(examData));
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
dialogVisible.value = false;
|
||||
cancelDialogVisible.value = false;
|
||||
};
|
||||
|
||||
const timeInterval = () => {
|
||||
timeSwitch.value = true;
|
||||
const tiktok = setInterval(() => {
|
||||
if (time.value > 0 && timeSwitch.value) {
|
||||
time.value--;
|
||||
} else {
|
||||
timeSwitch.value = false;
|
||||
time.value = 5;
|
||||
}
|
||||
}, 1000);
|
||||
setTimeout(() => {
|
||||
clearInterval(tiktok);
|
||||
}, 6000);
|
||||
};
|
||||
|
||||
const requestOperate = () => {
|
||||
if (getPhoto.value && getInfo.value == "1") {
|
||||
const data = {
|
||||
uid: proxy.$AES_Encrypt(applicationData.uid),
|
||||
exam_name: proxy.$AES_Encrypt(applicationData.exam_name),
|
||||
};
|
||||
$http.post($url.application, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userTableRef.value.clearSelection();
|
||||
examData = {};
|
||||
examDataClone = {};
|
||||
ElMessage({
|
||||
message: "操作成功!",
|
||||
type: "success",
|
||||
});
|
||||
examMsg();
|
||||
dialogVisible.value = false;
|
||||
confirmDialogVisible.value = true;
|
||||
timeInterval();
|
||||
} else {
|
||||
ElMessage.error("操作失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!getPhoto.value) {
|
||||
ElMessage.error("请先上传照片!");
|
||||
}
|
||||
if (!getInfo.value) {
|
||||
ElMessage.error("需要通过身份证二要素核验!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const confirmOperate = () => {
|
||||
confirmDialogVisible.value = false;
|
||||
};
|
||||
|
||||
const cancelRequestOperate = () => {
|
||||
const data = {
|
||||
uid: proxy.$AES_Encrypt(applicationData.uid),
|
||||
};
|
||||
$http.post($url.cancelApplication, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userTableRef.value.clearSelection();
|
||||
examData = {};
|
||||
examDataClone = {};
|
||||
ElMessage({
|
||||
message: "操作成功!",
|
||||
type: "success",
|
||||
});
|
||||
examMsg();
|
||||
cancelDialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error("操作失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const checkPhoto = () => {
|
||||
$http.post($url.checkPhoto).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "照片已上传!",
|
||||
type: "success",
|
||||
});
|
||||
getPhoto.value = true;
|
||||
} else {
|
||||
ElMessage.error("请先上传照片!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const checkInfo = () => {
|
||||
$http.post($url.checkInfo).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "身份证二要素核验成功!",
|
||||
type: "success",
|
||||
});
|
||||
communicationStore.setTrigger(true);
|
||||
communicationStore.setEditSwitch(true);
|
||||
getInfo.value = "1";
|
||||
} else if (res.data.code === 303) {
|
||||
ElMessage({
|
||||
message: "身份证二要素核验中!",
|
||||
type: "warning",
|
||||
});
|
||||
communicationStore.setTrigger(false);
|
||||
communicationStore.setEditSwitch(false);
|
||||
getInfo.value = "0";
|
||||
setTimeout(() => {
|
||||
checkInfo();
|
||||
}, 6000);
|
||||
} else {
|
||||
communicationStore.setTrigger(true);
|
||||
communicationStore.setEditSwitch(true);
|
||||
ElMessage.error("身份证二要素核验失败!");
|
||||
getInfo.value = "2";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const applicationDialog = (singleMsg) => {
|
||||
applicationMsg.value = singleMsg.data.exam_name.slice(
|
||||
10,
|
||||
singleMsg.data.exam_name.length
|
||||
);
|
||||
dialogVisible.value = true;
|
||||
applicationData = singleMsg.data;
|
||||
checkPhoto();
|
||||
checkInfo();
|
||||
};
|
||||
|
||||
const cancelApplicationDialog = (singleMsg) => {
|
||||
cancelMsg.value = singleMsg.data.exam_name.slice(10, singleMsg.data.exam_name.length);
|
||||
cancelDialogVisible.value = true;
|
||||
timeInterval();
|
||||
applicationData = singleMsg.data;
|
||||
};
|
||||
|
||||
defineExpose({ examMsg });
|
||||
|
||||
watch(searchOptions, (value) => {
|
||||
examMsgActive.searchOptions = value;
|
||||
});
|
||||
|
||||
watch(sortOptions, (value) => {
|
||||
examMsgActive.sortOptions = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
pageCount.value = examMsgActive.pageCount;
|
||||
dialogSearchVisible.value = examMsgActive.dialogSearchVisible;
|
||||
dialogSortVisible.value = examMsgActive.dialogSortVisible;
|
||||
|
||||
if (dialogSearchVisible.value) {
|
||||
searchOptions.uid = examMsgActive.searchOptions.uid;
|
||||
searchOptions.exam_name = examMsgActive.searchOptions.exam_name;
|
||||
searchOptions.status = examMsgActive.searchOptions.status;
|
||||
searchOptions.application_end = examMsgActive.searchOptions.application_end;
|
||||
searchOptions.exam_start = examMsgActive.searchOptions.exam_start;
|
||||
searchOptions.exam_end = examMsgActive.searchOptions.exam_end;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
sortOptions.application_end = examMsgActive.sortOptions.application_end;
|
||||
sortOptions.exam_start = examMsgActive.sortOptions.exam_start;
|
||||
sortOptions.exam_end = examMsgActive.sortOptions.exam_end;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
examMsg();
|
||||
}, 200);
|
||||
|
||||
setTimeout(() => {
|
||||
if (dialogSearchVisible.value) {
|
||||
SearchUidKeyRef.value.searchMsg = examMsgActive.searchOptions.uid;
|
||||
SearchExamNameKeyRef.value.searchMsg = examMsgActive.searchOptions.exam_name;
|
||||
StatusSelectSearchRef.value.searchMsg = examMsgActive.searchOptions.status;
|
||||
ApplicationEndPickerRef.value.application_end =
|
||||
examMsgActive.searchOptions.application_end;
|
||||
ExamStartPickerRef.value.exam_start = examMsgActive.searchOptions.exam_start;
|
||||
ExamEndPickerRef.value.exam_end = examMsgActive.searchOptions.exam_end;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
SortApplicationEndRef.value.searchMsg = examMsgActive.sortOptions.application_end;
|
||||
SortExamStartRef.value.searchMsg = examMsgActive.sortOptions.exam_start;
|
||||
SortExamEndRef.value.searchMsg = examMsgActive.sortOptions.exam_end;
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
388
src/components/features/gradesList.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<el-card>
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<TabelTools @showTools="showTools" :dialogObj="gradesMsgActive" />
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
height="47rem"
|
||||
max-height="47rem"
|
||||
table-layout="auto"
|
||||
scrollbar-always-on
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column min-width="100" column-key="exam_uid">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchExamUidKeyRef"
|
||||
holderText="输入考试编号以搜索相关数据"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试编号 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.exam_uid }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="180" column-key="exam_name">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchExamNameKeyRef"
|
||||
holderText="输入考试名称以搜索相关数据"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试名称 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.examination.exam_name }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column min-width="100" column-key="status">
|
||||
<template #header>
|
||||
<SelectSearch
|
||||
v-if="dialogSearchVisible"
|
||||
ref="StatusSelectSearchRef"
|
||||
holderText="选择参与状态以搜索相关数据"
|
||||
:options="statusOptions"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 参与状态 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(statusOptions, scope.row.status)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
{{ statusText[scope.row.status] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<!-- <el-table-column min-width="100" column-key="grades">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchGradesKeyRef"
|
||||
holderText="输入考试成绩以搜索相关数据"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试成绩 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortGradesRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.status)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.grades }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column min-width="120" column-key="pass_type">
|
||||
<template #header>
|
||||
<SelectSearch
|
||||
v-if="dialogSearchVisible"
|
||||
ref="PassTypeSelectSearchRef"
|
||||
holderText="选择考试结果以搜索相关数据"
|
||||
:options="passTypeOptions"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 考试结果 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(passTypeOptions, scope.row.pass_type)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
{{ passTypeText[scope.row.pass_type] }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 表格 -->
|
||||
<Pagination
|
||||
ref="PaginationRef"
|
||||
:active="gradesMsgActive"
|
||||
@sizeChangeBack="changePage"
|
||||
@search="gradesMsg"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted, nextTick } from "vue";
|
||||
|
||||
import TabelTools from "../operate/tabelTools.vue";
|
||||
import SearchKey from "../search/searchKey.vue";
|
||||
import SortSearch from "../search/sortSearch.vue";
|
||||
import SelectSearch from "../search/selectSearch.vue";
|
||||
import Pagination from "../search/pagination.vue";
|
||||
|
||||
import gradesMsgActiveStore from "../../stores/gradesMsg";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { judgment } from "../../utils/judgment.js";
|
||||
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
|
||||
|
||||
const gradesMsgActive = gradesMsgActiveStore();
|
||||
|
||||
const SearchExamUidKeyRef = ref(null);
|
||||
const SearchExamNameKeyRef = ref(null);
|
||||
const SearchGradesKeyRef = ref(null);
|
||||
const SortGradesRef = ref(null);
|
||||
const StatusSelectSearchRef = ref(null);
|
||||
const PassTypeSelectSearchRef = ref(null);
|
||||
const PaginationRef = ref(null);
|
||||
const dialogSearchVisible = ref(false);
|
||||
const dialogSortVisible = ref(false);
|
||||
const userTableRef = ref();
|
||||
const offsetOff = ref(true);
|
||||
const pageCount = ref(0);
|
||||
|
||||
const searchOptions = reactive({
|
||||
exam_uid: "",
|
||||
exam_name: "",
|
||||
status: "",
|
||||
grades: "",
|
||||
pass_type: "",
|
||||
});
|
||||
|
||||
const sortOptions = reactive({
|
||||
grades: false,
|
||||
});
|
||||
|
||||
const tableData = ref([]);
|
||||
|
||||
const statusText = ["旷考", "正常参加", "考试作弊"];
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: "0",
|
||||
label: "旷考",
|
||||
style: "color: rgb(255, 200, 0)",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "正常参加",
|
||||
style: "color: rgb(16, 184, 130)",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "考试作弊",
|
||||
style: "color: rgb(240, 70, 70)",
|
||||
},
|
||||
];
|
||||
|
||||
const statusOptions = [
|
||||
{
|
||||
value: "0",
|
||||
label: "旷考",
|
||||
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "正常参加",
|
||||
style:
|
||||
"color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "考试作弊",
|
||||
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
|
||||
},
|
||||
];
|
||||
|
||||
const passTypeText = ["成绩不及格", "成绩及格", "无成绩"];
|
||||
|
||||
const passTypeOptions = [
|
||||
{
|
||||
value: "0",
|
||||
label: "成绩不及格",
|
||||
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "成绩及格",
|
||||
style:
|
||||
"color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
|
||||
},
|
||||
{
|
||||
value: "2",
|
||||
label: "无成绩",
|
||||
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
|
||||
},
|
||||
];
|
||||
|
||||
let gradesData = reactive({});
|
||||
let gradesDataClone = reactive({});
|
||||
|
||||
const styleShunt = (options, data) => {
|
||||
let nowStyle;
|
||||
options.forEach((item) => {
|
||||
if (data == item.value) {
|
||||
nowStyle = item.style;
|
||||
}
|
||||
});
|
||||
return nowStyle;
|
||||
};
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
dialogSearchVisible.value = !dialogSearchVisible.value;
|
||||
gradesMsgActive.dialogSearchVisible = dialogSearchVisible.value;
|
||||
break;
|
||||
case "sort":
|
||||
dialogSortVisible.value = !dialogSortVisible.value;
|
||||
gradesMsgActive.dialogSortVisible = dialogSortVisible.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const gradesMsg = () => {
|
||||
judgment(searchOptions, "exam_uid", SearchExamUidKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "exam_name", SearchExamNameKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "status", StatusSelectSearchRef.value, "searchMsg");
|
||||
judgment(searchOptions, "grades", SearchGradesKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "pass_type", PassTypeSelectSearchRef.value, "searchMsg");
|
||||
judgment(sortOptions, "grades", SortGradesRef.value, "searchMsg");
|
||||
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
|
||||
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
|
||||
PaginationRef.value.handleCurrentChange(1);
|
||||
offsetOff.value = false;
|
||||
}
|
||||
} else {
|
||||
offsetOff.value = true;
|
||||
}
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
offset: PaginationRef.value.offset,
|
||||
limit: PaginationRef.value.limit,
|
||||
searchOptions: searchOptions,
|
||||
sortOptions: sortOptions,
|
||||
};
|
||||
$http.post($url.gradesList, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
tableData.value = res.data.gradesMsg;
|
||||
PaginationRef.value.dataCount = res.data.count;
|
||||
gradesMsgActive.dataCount = PaginationRef.value.dataCount;
|
||||
pageCount.value = Math.ceil(
|
||||
PaginationRef.value.dataCount / PaginationRef.value.limit
|
||||
);
|
||||
gradesMsgActive.pageCount = pageCount.value;
|
||||
if (!gradesData[PaginationRef.value.offset]) return;
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const changePage = (val) => {
|
||||
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
|
||||
gradesData = {};
|
||||
for (let i = 0; i < pageCount.value; i++) {
|
||||
gradesData[i + 1] = [];
|
||||
for (const key in gradesDataClone) {
|
||||
gradesDataClone[key].forEach((element) => {
|
||||
// 判断数据在第几页
|
||||
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
|
||||
gradesData[i + 1].push(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectionChange = (val) => {
|
||||
gradesData[PaginationRef.value.offset] = val;
|
||||
gradesDataClone = JSON.parse(JSON.stringify(gradesData));
|
||||
};
|
||||
|
||||
defineExpose({ gradesMsg });
|
||||
|
||||
watch(searchOptions, (value) => {
|
||||
gradesMsgActive.searchOptions = value;
|
||||
});
|
||||
|
||||
watch(sortOptions, (value) => {
|
||||
gradesMsgActive.sortOptions = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
pageCount.value = gradesMsgActive.pageCount;
|
||||
dialogSearchVisible.value = gradesMsgActive.dialogSearchVisible;
|
||||
dialogSortVisible.value = gradesMsgActive.dialogSortVisible;
|
||||
|
||||
if (dialogSearchVisible.value) {
|
||||
searchOptions.exam_uid = gradesMsgActive.searchOptions.exam_uid;
|
||||
searchOptions.exam_name = gradesMsgActive.searchOptions.exam_name;
|
||||
searchOptions.status = gradesMsgActive.searchOptions.status;
|
||||
searchOptions.grades = gradesMsgActive.searchOptions.grades;
|
||||
searchOptions.pass_type = gradesMsgActive.searchOptions.pass_type;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
sortOptions.grades = gradesMsgActive.sortOptions.grades;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
gradesMsg();
|
||||
}, 200);
|
||||
|
||||
setTimeout(() => {
|
||||
if (dialogSearchVisible.value) {
|
||||
SearchExamUidKeyRef.value.searchMsg = gradesMsgActive.searchOptions.exam_uid;
|
||||
SearchExamNameKeyRef.value.searchMsg = gradesMsgActive.searchOptions.exam_name;
|
||||
StatusSelectSearchRef.value.searchMsg = gradesMsgActive.searchOptions.status;
|
||||
SearchGradesKeyRef.value.searchMsg = gradesMsgActive.searchOptions.grades;
|
||||
PassTypeSelectSearchRef.value.searchMsg = gradesMsgActive.searchOptions.pass_type;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
SortGradesRef.value.searchMsg = gradesMsgActive.sortOptions.grades;
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
181
src/components/initialService/login.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<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.25rem">
|
||||
北斗应用技术培训报名系统登录
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="手机号码" prop="phone">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入手机号码"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
/>
|
||||
</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>
|
||||
<SlideVerify ref="slideVerifyRef" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div>
|
||||
<el-link style="font-weight: bold" type="primary" @click="resetPassword"
|
||||
>忘记密码?</el-link
|
||||
>
|
||||
</div>
|
||||
<div class="mx-2">
|
||||
<el-link style="font-weight: bold" type="primary" @click="registered"
|
||||
>注册账号</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>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, getCurrentInstance } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import SlideVerify from "../tools/slideVerify.vue";
|
||||
|
||||
const emit = defineEmits(["changePage"]);
|
||||
|
||||
const ruleFormRef = ref();
|
||||
const slideVerifyRef = ref();
|
||||
|
||||
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 resetPassword = () => {
|
||||
emit("changePage", "resetPassword");
|
||||
};
|
||||
|
||||
const registered = () => {
|
||||
emit("changePage", "registered");
|
||||
};
|
||||
|
||||
const validatePhone = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入手机号码"));
|
||||
} else if (!phoneRegex.test(value)) {
|
||||
callback(new Error("手机号码格式错误, 请重新输入!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入密码"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const ruleForm = reactive({
|
||||
phone: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
phone: [{ required: true, validator: validatePhone, trigger: "blur" }],
|
||||
password: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (label == "phone") {
|
||||
ruleForm.phone = value.replace(/\D/g, "");
|
||||
}
|
||||
if (label == "password") {
|
||||
ruleForm.password = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
};
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const submitForm = (formEl) => {
|
||||
if (slideVerifyRef.value.verify) {
|
||||
slideVerifyRef.value.block.value?.refresh();
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
let encryptForm = {
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
password: proxy.$AES_Encrypt(ruleForm.password),
|
||||
};
|
||||
$http.post($url.elogin, encryptForm).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "登录成功!",
|
||||
type: "success",
|
||||
});
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("loginCode", res.data.loginCode);
|
||||
window.location.href = window.location.href.replace(
|
||||
"/initialService",
|
||||
"/main"
|
||||
);
|
||||
} else {
|
||||
if (res.data.code === 444) {
|
||||
ElMessage.error(res.data.msg);
|
||||
} else {
|
||||
ElMessage.error("登录失败!");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败,请先完善登录信息!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
slideVerifyRef.value.block.refresh();
|
||||
ElMessage.error("验证未通过,请重新验证!");
|
||||
slideVerifyRef.value.msg = "验证未通过,请重新验证";
|
||||
slideVerifyRef.value.fontColor = "color: red";
|
||||
slideVerifyRef.value.verify = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
465
src/components/initialService/registered.vue
Normal file
@@ -0,0 +1,465 @@
|
||||
<template>
|
||||
<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.25rem">
|
||||
北斗应用技术培训报名系统注册
|
||||
</el-text>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="手机号码" prop="phone">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="11"
|
||||
placeholder="请输入手机号码"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
ref="phoneInputRef"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-text class="mx-1" size="small" type="warning">
|
||||
密码格式:密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在8-16位之间
|
||||
</el-text>
|
||||
<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"
|
||||
ref="passwordInputRef"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label=" " prop="again">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请再次输入密码"
|
||||
v-model="ruleForm.again"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
@input="filter(ruleForm.again, 'again')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
ref="againInputRef"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-text class="mx-1" size="small" type="warning">
|
||||
*请认真检查确保您的身份证号码和真实姓名输入正确无误,错误的身份信息将影响考试报名*
|
||||
</el-text>
|
||||
<el-form-item class="mx-1 font-bold" label="真实姓名" prop="user_name">
|
||||
<el-input
|
||||
clearable
|
||||
minlength="2"
|
||||
maxlength="10"
|
||||
placeholder="请输入真实姓名"
|
||||
v-model="ruleForm.user_name"
|
||||
ref="userNameInputRef"
|
||||
@input="filter(ruleForm.user_name, 'user_name')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="身份证号" prop="identity_card">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="18"
|
||||
placeholder="请输入身份证号"
|
||||
v-model="ruleForm.identity_card"
|
||||
ref="identityCardInputRef"
|
||||
@input="filter(ruleForm.identity_card, 'identity_card')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="工作单位" prop="workplace">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="60"
|
||||
placeholder="请输入工作单位"
|
||||
v-model="ruleForm.workplace"
|
||||
ref="workplaceInputRef"
|
||||
@input="filter(ruleForm.workplace, 'workplace')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="通讯地址" prop="address">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="120"
|
||||
placeholder="请输入通讯地址"
|
||||
v-model="ruleForm.address"
|
||||
ref="addressInputRef"
|
||||
@input="filter(ruleForm.address, 'address')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="电子邮箱号码" prop="email">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="60"
|
||||
placeholder="请输入电子邮箱号码"
|
||||
v-model="ruleForm.email"
|
||||
ref="emailInputRef"
|
||||
@input="filter(ruleForm.email, 'email')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="推荐人信息" prop="referrers">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="60"
|
||||
placeholder="如有推荐人请填写"
|
||||
v-model="ruleForm.referrers"
|
||||
ref="referrersInputRef"
|
||||
@input="filter(ruleForm.referrers, 'referrers')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="验证码" prop="code">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="6"
|
||||
placeholder="请输入短信验证码"
|
||||
v-model="ruleForm.code"
|
||||
@input="filter(ruleForm.code, 'code')"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
:style="
|
||||
codeText
|
||||
? 'font-weight: bold; color: rgb(34 197 94 / var(--tw-bg-opacity, 1))'
|
||||
: 'font-weight: bold; color: rgb(56 189 248 / var(--tw-bg-opacity, 1));'
|
||||
"
|
||||
type="warning"
|
||||
:disabled="timeSwitch"
|
||||
@click="sendVerify"
|
||||
>
|
||||
{{ codeText ? "发送验证码" : `重新发送(${time}秒)` }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div>
|
||||
<el-link style="font-weight: bold" type="primary" @click="login">登录</el-link>
|
||||
</div>
|
||||
<div class="mx-2">
|
||||
<el-link style="font-weight: bold" type="primary" @click="resetPassword"
|
||||
>忘记密码?</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>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, getCurrentInstance } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const emit = defineEmits(["changePage"]);
|
||||
|
||||
const ruleFormRef = ref();
|
||||
const phoneInputRef = ref(null);
|
||||
const passwordInputRef = ref(null);
|
||||
const againInputRef = ref(null);
|
||||
const userNameInputRef = ref(null);
|
||||
const identityCardInputRef = ref(null);
|
||||
const workplaceInputRef = ref(null);
|
||||
const addressInputRef = ref(null);
|
||||
const emailInputRef = ref(null);
|
||||
const timeSwitch = ref(false);
|
||||
const codeText = ref(true);
|
||||
const time = ref(59);
|
||||
|
||||
const identityCardRegex = /^[1-9](?:\d{5})(?:19|20)?\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}(?:\d|X|x)$/;
|
||||
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 passwordRegex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*]).{8,16}$/;
|
||||
|
||||
const login = () => {
|
||||
emit("changePage", "login");
|
||||
};
|
||||
|
||||
const resetPassword = () => {
|
||||
emit("changePage", "resetPassword");
|
||||
};
|
||||
|
||||
const timeInterval = () => {
|
||||
const tiktok = setInterval(() => {
|
||||
if (time.value > 0 && timeSwitch.value) {
|
||||
time.value--;
|
||||
} else {
|
||||
timeSwitch.value = true;
|
||||
codeText.value = false;
|
||||
time.value = 59;
|
||||
}
|
||||
}, 1000);
|
||||
setTimeout(() => {
|
||||
clearInterval(tiktok);
|
||||
timeSwitch.value = false;
|
||||
codeText.value = true;
|
||||
}, 60000);
|
||||
};
|
||||
|
||||
const validateCode = (rule, value, callback) => {
|
||||
if (ruleForm.phone == "") {
|
||||
callback(new Error("请先输入手机号码"));
|
||||
} else if (!phoneRegex.test(ruleForm.phone)) {
|
||||
callback(new Error("手机号码格式错误, 请重新输入!"));
|
||||
} else if (value === "") {
|
||||
callback(new Error("请输入短信验证码"));
|
||||
} else {
|
||||
const data = {
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
code: proxy.$AES_Encrypt(value),
|
||||
};
|
||||
$http.post($url.checkCode, 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("密码格式不正确,请重新设置!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateAgain = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请再次输入密码"));
|
||||
} else if (value != ruleForm.password) {
|
||||
callback(new Error("两次输入的密码不一致!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateIdentityCard = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入身份证号"));
|
||||
} else if (!identityCardRegex.test(value)) {
|
||||
callback(new Error("身份证格式不正确,请重新输入!"));
|
||||
} else {
|
||||
const data = {
|
||||
identity_card: proxy.$AES_Encrypt(value),
|
||||
type: proxy.$AES_Encrypt("register"),
|
||||
data: proxy.$AES_Encrypt("null"),
|
||||
};
|
||||
$http.post($url.diffIdentityCard, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("该身份证号已被注册"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const validatePhone = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
timeSwitch.value = true;
|
||||
codeText.value = true;
|
||||
callback(new Error("请输入手机号码"));
|
||||
} else if (!phoneRegex.test(value)) {
|
||||
timeSwitch.value = true;
|
||||
codeText.value = true;
|
||||
callback(new Error("手机号码格式错误, 请重新输入!"));
|
||||
} else {
|
||||
const data = {
|
||||
phone: proxy.$AES_Encrypt(value),
|
||||
type: proxy.$AES_Encrypt("register"),
|
||||
data: proxy.$AES_Encrypt("null"),
|
||||
};
|
||||
$http.post($url.diffPhone, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
timeSwitch.value = false;
|
||||
callback();
|
||||
} else {
|
||||
timeSwitch.value = true;
|
||||
codeText.value = true;
|
||||
callback(new Error("该手机号码已被注册"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入电子邮箱号码"));
|
||||
} else if (!emailRegex.test(value)) {
|
||||
callback(new Error("邮箱格式不正确,请重新输入!"));
|
||||
} else {
|
||||
const data = {
|
||||
email: proxy.$AES_Encrypt(value),
|
||||
type: proxy.$AES_Encrypt("register"),
|
||||
data: proxy.$AES_Encrypt("null"),
|
||||
};
|
||||
$http.post($url.diffEmail, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("该电子邮箱号码已被注册"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const sendVerify = () => {
|
||||
let inputRef = [
|
||||
phoneInputRef,
|
||||
passwordInputRef,
|
||||
againInputRef,
|
||||
userNameInputRef,
|
||||
identityCardInputRef,
|
||||
workplaceInputRef,
|
||||
addressInputRef,
|
||||
emailInputRef,
|
||||
];
|
||||
inputRef.forEach((item) => {
|
||||
if (item.value) {
|
||||
item.value.focus();
|
||||
item.value.blur();
|
||||
}
|
||||
});
|
||||
if (ruleForm.phone != "" && /^1[3-9]\d{9}$/.test(ruleForm.phone) && !timeSwitch.value) {
|
||||
timeInterval();
|
||||
const data = {
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
};
|
||||
$http.post($url.sendSMS, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "发送短信验证码成功!",
|
||||
type: "success",
|
||||
});
|
||||
} else {
|
||||
if (res.data.code === 333) {
|
||||
ElMessage.error(res.data.msg);
|
||||
} else {
|
||||
ElMessage.error("发送短信验证码失败!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const ruleForm = reactive({
|
||||
code: "",
|
||||
password: "",
|
||||
again: "",
|
||||
user_name: "",
|
||||
identity_card: "",
|
||||
workplace: "",
|
||||
address: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
referrers: "",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
code: [{ required: true, validator: validateCode, trigger: "blur" }],
|
||||
password: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||
again: [{ validator: validateAgain, trigger: "blur" }],
|
||||
user_name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
|
||||
identity_card: [{ required: true, validator: validateIdentityCard, trigger: "blur" }],
|
||||
workplace: {
|
||||
required: true,
|
||||
message: "请输入工作单位",
|
||||
trigger: "blur",
|
||||
},
|
||||
address: { required: true, message: "请输入通讯地址", trigger: "blur" },
|
||||
phone: [{ required: true, validator: validatePhone, trigger: "blur" }],
|
||||
email: [
|
||||
{ required: true, validator: validateEmail, trigger: "blur" },
|
||||
{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] },
|
||||
],
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (label == "phone") {
|
||||
ruleForm.phone = value.replace(/\D/g, "");
|
||||
} else if (label == "code") {
|
||||
ruleForm.code = value.replace(/\D/g, "");
|
||||
} else {
|
||||
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
};
|
||||
|
||||
const submitForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
let encryptForm = {
|
||||
password: proxy.$AES_Encrypt(ruleForm.password),
|
||||
user_name: proxy.$AES_Encrypt(ruleForm.user_name),
|
||||
identity_card: proxy.$AES_Encrypt(ruleForm.identity_card),
|
||||
workplace: proxy.$AES_Encrypt(ruleForm.workplace),
|
||||
address: proxy.$AES_Encrypt(ruleForm.address),
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
email: proxy.$AES_Encrypt(ruleForm.email),
|
||||
referrers:
|
||||
ruleForm.referrers == "" ? null : proxy.$AES_Encrypt(ruleForm.referrers),
|
||||
};
|
||||
$http.post($url.registered, encryptForm).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "注册成功!",
|
||||
type: "success",
|
||||
});
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("loginCode", res.data.loginCode);
|
||||
emit("changePage", "login");
|
||||
} else {
|
||||
if (res.data.code === 333) {
|
||||
ElMessage.error(res.data.msg);
|
||||
} else {
|
||||
ElMessage.error("注册失败!");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败,请先完善信息!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
290
src/components/initialService/resetPassword.vue
Normal file
@@ -0,0 +1,290 @@
|
||||
<template>
|
||||
<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.25rem"> 密码找回 </el-text>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="手机号码" prop="phone">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="11"
|
||||
placeholder="请输入手机号码"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
ref="phoneInputRef"
|
||||
>
|
||||
<template #append>
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
type="warning"
|
||||
:disabled="timeSwitch"
|
||||
@click="sendVerify"
|
||||
>
|
||||
{{ timeSwitch ? `重新发送(${time}秒)` : "发送验证码" }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="验证码" prop="code">
|
||||
<el-input
|
||||
clearable
|
||||
maxlength="6"
|
||||
placeholder="请输入短信验证码"
|
||||
v-model="ruleForm.code"
|
||||
@input="filter(ruleForm.code, 'code')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-text class="mx-1" size="small" type="danger">
|
||||
密码格式:密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在8-16位之间
|
||||
</el-text>
|
||||
<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 class="mx-1 font-bold" label=" " prop="again">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请再次输入新密码"
|
||||
v-model="ruleForm.again"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
@input="filter(ruleForm.again, 'again')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div class="mx-2">
|
||||
<el-link style="font-weight: bold" type="primary" @click="login">登录</el-link>
|
||||
</div>
|
||||
<div>
|
||||
<el-link style="font-weight: bold" type="primary" @click="registered"
|
||||
>注册账号</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>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, getCurrentInstance } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const emit = defineEmits(["changePage"]);
|
||||
|
||||
const ruleFormRef = ref();
|
||||
const phoneInputRef = ref(null);
|
||||
const timeSwitch = ref(false);
|
||||
const time = ref(59);
|
||||
|
||||
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 login = () => {
|
||||
emit("changePage", "login");
|
||||
};
|
||||
|
||||
const registered = () => {
|
||||
emit("changePage", "registered");
|
||||
};
|
||||
|
||||
const timeInterval = () => {
|
||||
timeSwitch.value = true;
|
||||
const tiktok = setInterval(() => {
|
||||
if (time.value > 0 && timeSwitch.value) {
|
||||
time.value--;
|
||||
} else {
|
||||
timeSwitch.value = false;
|
||||
time.value = 59;
|
||||
}
|
||||
}, 1000);
|
||||
setTimeout(() => {
|
||||
clearInterval(tiktok);
|
||||
}, 60000);
|
||||
};
|
||||
|
||||
const validatePhone = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入手机号码"));
|
||||
} else if (!phoneRegex.test(value)) {
|
||||
callback(new Error("手机号码格式错误, 请重新输入!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateCode = (rule, value, callback) => {
|
||||
if (ruleForm.phone == "") {
|
||||
callback(new Error("请先输入手机号码"));
|
||||
} else if (!/^1[3-9]\d{9}$/.test(ruleForm.phone)) {
|
||||
callback(new Error("手机号码格式错误, 请重新输入!"));
|
||||
} else if (value === "") {
|
||||
callback(new Error("请输入短信验证码"));
|
||||
} else {
|
||||
const data = {
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
code: proxy.$AES_Encrypt(value),
|
||||
};
|
||||
$http.post($url.checkCode, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("验证码错误"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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("密码格式不正确,请重新设置"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateAgain = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请再次输入新密码"));
|
||||
} else if (value != ruleForm.password) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const sendVerify = () => {
|
||||
if (phoneInputRef.value) {
|
||||
phoneInputRef.value.focus();
|
||||
phoneInputRef.value.blur();
|
||||
}
|
||||
if (ruleForm.phone != "" && /^1[3-9]\d{9}$/.test(ruleForm.phone) && !timeSwitch.value) {
|
||||
timeInterval();
|
||||
const data = {
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
};
|
||||
$http.post($url.sendSMS, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "发送短信验证码成功!",
|
||||
type: "success",
|
||||
});
|
||||
} else {
|
||||
if (res.data.code === 333) {
|
||||
ElMessage.error(res.data.msg);
|
||||
} else {
|
||||
ElMessage.error("发送短信验证码失败!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const ruleForm = reactive({
|
||||
phone: "",
|
||||
code: "",
|
||||
password: "",
|
||||
again: "",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
phone: [{ required: true, validator: validatePhone, trigger: "blur" }],
|
||||
code: [{ required: true, validator: validateCode, trigger: "blur" }],
|
||||
password: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||
again: [{ validator: validateAgain, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (label == "phone") {
|
||||
ruleForm.phone = value.replace(/\D/g, "");
|
||||
}
|
||||
if (label == "code") {
|
||||
ruleForm.code = value.replace(/\D/g, "");
|
||||
}
|
||||
if (label == "password") {
|
||||
ruleForm.password = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
if (label == "again") {
|
||||
ruleForm.again = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
};
|
||||
|
||||
const submitForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
let encryptForm = {
|
||||
phone: proxy.$AES_Encrypt(ruleForm.phone),
|
||||
password: proxy.$AES_Encrypt(ruleForm.password),
|
||||
};
|
||||
$http.post($url.resetPassword, encryptForm).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "修改密码成功!",
|
||||
type: "success",
|
||||
});
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("loginCode", res.data.loginCode);
|
||||
emit("changePage", "login");
|
||||
} else {
|
||||
if (res.data.code === 333) {
|
||||
ElMessage.error(res.data.msg);
|
||||
} else {
|
||||
ElMessage.error("修改密码失败!");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败,请先完善信息!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
91
src/components/operate/tabelSelect.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<el-table-column min-width="100" width="100" :column-key="报名状态">
|
||||
<template #header>
|
||||
<div>
|
||||
<el-text class="mx-1"> 报名状态 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div class="flex justify-center items-center text-base">
|
||||
<el-button
|
||||
v-if="
|
||||
(scope.row.status == '0' && scope.row.applicationStatus == '2') ||
|
||||
(scope.row.status == '0' && scope.row.applicationStatus == '3') ||
|
||||
(scope.row.status == '0' && scope.row.applicationStatus == '4')
|
||||
"
|
||||
style="font-weight: bold"
|
||||
type="primary"
|
||||
size="default"
|
||||
plain="true"
|
||||
:disabled="
|
||||
new Date() < new Date(scope.row.origin_applicationEnd) &&
|
||||
scope.row.applicationCheck == 0 &&
|
||||
!scope.row.applicationCheckSwitch
|
||||
? false
|
||||
: true
|
||||
"
|
||||
@click="applicationDialog(scope.$index, scope.row)"
|
||||
>
|
||||
报名
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="flex justify-center items-center text-base">
|
||||
<el-button
|
||||
v-if="
|
||||
(scope.row.status == '0' && scope.row.applicationStatus == '0') ||
|
||||
(scope.row.status == '0' && scope.row.applicationStatus == '1')
|
||||
"
|
||||
style="font-weight: bold"
|
||||
type="danger"
|
||||
size="default"
|
||||
plain="true"
|
||||
@click="cancelApplicationDialog(scope.$index, scope.row)"
|
||||
>
|
||||
取消报名
|
||||
</el-button>
|
||||
</div>
|
||||
<div
|
||||
v-if="scope.row.status != '0' && scope.row.applicationStatus == '1'"
|
||||
style="color: rgb(16, 184, 130); background-color: rgba(16, 184, 130, 0.1)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
报名成功
|
||||
</div>
|
||||
<div
|
||||
v-if="scope.row.status != '0' && scope.row.applicationStatus == '2'"
|
||||
style="color: rgb(240, 70, 70); background-color: rgba(240, 70, 70, 0.1)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
报名失败
|
||||
</div>
|
||||
<div
|
||||
v-if="scope.row.status != '0' && scope.row.applicationStatus == '3'"
|
||||
style="color: rgb(255, 200, 0); background-color: rgba(255, 200, 0, 0.1)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
报名取消
|
||||
</div>
|
||||
<div
|
||||
v-if="scope.row.status != '0' && scope.row.applicationStatus == '4'"
|
||||
style="color: rgb(56, 188, 248); background-color: rgba(56, 188, 248, 0.1)"
|
||||
class="flex justify-center items-center text-base"
|
||||
>
|
||||
未参与
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(["applicationDialog", "cancelApplicationDialog"]);
|
||||
|
||||
const applicationDialog = (index, row) => {
|
||||
emit("applicationDialog", { data: row });
|
||||
};
|
||||
|
||||
const cancelApplicationDialog = (index, row) => {
|
||||
emit("cancelApplicationDialog", { data: row });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
90
src/components/operate/tabelTools.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-full h-8 flex flex-col justify-center items-center relative top-0 right-0"
|
||||
>
|
||||
<div class="absolute top-0 left-0" style="font-size: 1rem">
|
||||
<el-dropdown>
|
||||
<el-button style="font-weight: bold" type="warning" size="small" plain>
|
||||
表格工具
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :style="searchBgColor" @click="showTools('search')">
|
||||
<el-link style="font-weight: bold" :underline="false" type="success">
|
||||
搜索工具
|
||||
<el-icon v-show="showSearch" class="el-icon--right">
|
||||
<View />
|
||||
</el-icon>
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :style="sortBgColor" @click="showTools('sort')">
|
||||
<el-link style="font-weight: bold" :underline="false" type="success">
|
||||
排序工具
|
||||
<el-icon v-show="showSort" class="el-icon--right">
|
||||
<View />
|
||||
</el-icon>
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { ArrowDown, View } from "@element-plus/icons-vue";
|
||||
|
||||
const showSearch = ref(false);
|
||||
const searchBgColor = ref("background-color: rgba(16, 184, 130, 0.1);");
|
||||
const showSort = ref(false);
|
||||
const sortBgColor = ref("background-color: rgba(16, 184, 130, 0.1);");
|
||||
|
||||
const props = defineProps({
|
||||
dialogObj: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["showTools"]);
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
emit("showTools", "search");
|
||||
showSearch.value = !showSearch.value;
|
||||
break;
|
||||
case "sort":
|
||||
emit("showTools", "sort");
|
||||
showSort.value = !showSort.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
watch(showSearch, (value) => {
|
||||
if (value) {
|
||||
searchBgColor.value = "background-color: rgba(16, 184, 130, 0.2);";
|
||||
} else {
|
||||
searchBgColor.value = "background-color: rgba(16, 184, 130, 0.1);";
|
||||
}
|
||||
});
|
||||
|
||||
watch(showSort, (value) => {
|
||||
if (value) {
|
||||
sortBgColor.value = "background-color: rgba(16, 184, 130, 0.2);";
|
||||
} else {
|
||||
sortBgColor.value = "background-color: rgba(16, 184, 130, 0.1);";
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
showSearch.value = props.dialogObj.dialogSearchVisible;
|
||||
showSort.value = props.dialogObj.dialogSortVisible;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
301
src/components/operate/userMsgCard.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<div class="h-full flex flex-wrap justify-center items-center" style="min-width: 520px">
|
||||
<el-card
|
||||
v-if="loginMark"
|
||||
shadow="always"
|
||||
style="width: 520px; background-color: #f0f0ff"
|
||||
>
|
||||
<div class="text-center bg-white py-5">
|
||||
<el-text style="font-weight: bold; font-size: 1.25rem"> 个人信息 </el-text>
|
||||
</div>
|
||||
<div class="flex justify-evenly items-center gap-5 bg-white px-5">
|
||||
<div>
|
||||
<img :key="photoKey" :src="userPhotoUrl" style="width: 115px; height: 165px" />
|
||||
</div>
|
||||
<div>
|
||||
<el-text style="font-weight: bold"> 姓名: </el-text>
|
||||
<el-text> {{ userMsg.user_name }} </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 身份证号码: </el-text>
|
||||
<el-text> {{ userMsg.identity_card }} </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 工作单位: </el-text>
|
||||
<el-text> {{ userMsg.workplace == "" ? "无" : userMsg.workplace }} </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 通讯地址: </el-text>
|
||||
<el-text> {{ userMsg.address == "" ? "无" : userMsg.address }} </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 电话号码: </el-text>
|
||||
<el-text> {{ userMsg.phone }} </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 电子邮箱号码: </el-text>
|
||||
<el-text> {{ userMsg.email }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
<UploadPhoto ref="uploadPhotoRef" @updateKey="updateKey" />
|
||||
<UserManager @resetData="resetData" ref="userManagerRef" :msg="userMsg" />
|
||||
<PasswordManager ref="passwordManagerRef" />
|
||||
<div class="flex justify-evenly bg-white py-5">
|
||||
<el-button style="font-weight: bold" type="primary" @click="uploadPhoto">
|
||||
上传照片
|
||||
</el-button>
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
type="primary"
|
||||
:disabled="editSwitch"
|
||||
@click="editUser"
|
||||
>
|
||||
修改信息
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold" type="primary" @click="changePassword">
|
||||
修改密码
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card
|
||||
v-if="loginMark"
|
||||
shadow="always"
|
||||
style="width: 520px; background-color: #f0f0ff"
|
||||
>
|
||||
<div class="text-center bg-white py-5">
|
||||
<el-text style="font-weight: bold; font-size: 1.25rem"> 注意事项 </el-text>
|
||||
</div>
|
||||
<div class="flex justify-evenly items-center gap-5 px-5 pb-5 bg-white">
|
||||
<div>
|
||||
<el-text style="font-weight: bold">
|
||||
1.考试报名提交前请先完成个人信息认证,确保信息完整和准确
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
2.个人信息核验通过后,选择考试的场次,完成考试报名
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
3.请在报名完成后,尽快缴纳培训费,费用为
|
||||
</el-text>
|
||||
<el-text style="font-weight: bold; color: red"> 1500元/人 </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
4.缴费完成后,审核人员将会在2个工作日内完成您的报名审核
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
5.报名审核完成后,将开通培训账号,自行登录培训平台进行学习
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 6.缴费方式为 </el-text>
|
||||
<el-text style="font-weight: bold; color: red"> 扫码缴费 </el-text>
|
||||
<el-text style="font-weight: bold"> 或 </el-text>
|
||||
<el-text style="font-weight: bold; color: red"> 银行转账 </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 点此按钮 </el-text>
|
||||
<el-text style="font-weight: bold; color: red"> 扫码缴费 </el-text>
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="payFee"
|
||||
>
|
||||
缴费
|
||||
</el-button>
|
||||
<br />
|
||||
<el-text style="font-weight: bold; color: red"> 银行转账 </el-text>
|
||||
<el-text style="font-weight: bold"> 请用 </el-text>
|
||||
<el-text style="font-weight: bold; color: red"> 报名者本人银行卡 </el-text>
|
||||
<el-text style="font-weight: bold"> 汇款,汇款账号为: </el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
账户名称:北京北斗教仪科技有限公司
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
开户银行:中信银行北京宝盛广场支行
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
行号:302100011882
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
账号:8110701013303001673
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold; color: red">
|
||||
汇款备注:本人姓名+考试编号
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold; color: red">
|
||||
【如已经在线下缴费,请勿再进行转账,避免重复】
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
7.如需更改考试场次,请先取消之前报名场次,再选择新的考试时间
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold">
|
||||
8.培训账号开通后,视为完整报名,将不进行退费
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text style="font-weight: bold"> 9.咨询电话:4001665286转2 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<Pay ref="payRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { encrypt } from "../../utils/aes";
|
||||
|
||||
import { useCommunicationStore } from "../../stores/communicationStore";
|
||||
|
||||
import UploadPhoto from "../tools/uploadPhoto.vue";
|
||||
import UserManager from "../tools/userManager.vue";
|
||||
import PasswordManager from "../tools/changePassword.vue";
|
||||
import Pay from "../tools/pay.vue";
|
||||
|
||||
import defaultPhoto from "../../assets/default.png";
|
||||
|
||||
const communicationStore = useCommunicationStore();
|
||||
|
||||
const editSwitch = ref(false);
|
||||
const loginMark = ref(false);
|
||||
const photoKey = ref(0);
|
||||
const uploadPhotoRef = ref(null);
|
||||
const userPhotoUrl = ref("#");
|
||||
const userManagerRef = ref(null);
|
||||
const userMsg = ref({});
|
||||
const passwordManagerRef = ref(null);
|
||||
const payRef = ref(null);
|
||||
|
||||
const uploadPhoto = () => {
|
||||
if (uploadPhotoRef.value) {
|
||||
uploadPhotoRef.value.dialogVisible = true;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => communicationStore.editSwitch,
|
||||
(newValue) => {
|
||||
editSwitch.value = !newValue;
|
||||
}
|
||||
);
|
||||
|
||||
const editUser = () => {
|
||||
if (userManagerRef.value) {
|
||||
userManagerRef.value.dialogVisible = true;
|
||||
userManagerRef.value.reset();
|
||||
}
|
||||
};
|
||||
|
||||
const changePassword = () => {
|
||||
if (passwordManagerRef.value) {
|
||||
passwordManagerRef.value.dialogVisible = true;
|
||||
}
|
||||
};
|
||||
|
||||
const resetData = () => {
|
||||
$http.post($url.getUser, {}).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userMsg.value = res.data.userData;
|
||||
if (res.data.userData.status == 0) {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: "您的账号正在使用初始密码,请尽快进行更改!",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
if (res.data.userData.status == 2) {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: "您的账号已被封禁,请联系管理员进行申诉!",
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
ElMessage.error("获取用户信息失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getPhotoUrl = () => {
|
||||
$http.post($url.checkPhoto, {}).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userPhotoUrl.value = res.data.photoUrl;
|
||||
} else {
|
||||
ElMessage.error("获取用户照片失败!");
|
||||
userPhotoUrl.value = defaultPhoto;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateKey = () => {
|
||||
photoKey.value += 1;
|
||||
getPhotoUrl();
|
||||
};
|
||||
|
||||
const payFee = () => {
|
||||
payRef.value.dialogVisible = true;
|
||||
payRef.value.know = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
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();
|
||||
getPhotoUrl();
|
||||
}
|
||||
});
|
||||
} 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>
|
||||
161
src/components/operate/userTabel.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="h-full bg-slate-400 flex flex-col" style="min-width: 520px">
|
||||
<el-menu
|
||||
class="flex font-bold"
|
||||
style="background-color: rgb(148 163 184 / var(--tw-bg-opacity, 1))"
|
||||
:default-active="activeIndex"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item
|
||||
index="1"
|
||||
:style="activeIndex == '1' ? activeStyle : indexStyle"
|
||||
class="border-x-4 border-slate-300"
|
||||
>
|
||||
考试信息
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="2"
|
||||
:style="activeIndex == '2' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
报名记录
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="3"
|
||||
:style="activeIndex == '3' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
我的成绩
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="4"
|
||||
:style="activeIndex == '4' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
培训授权
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="5"
|
||||
:style="activeIndex == '5' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
培训考试
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<div class="flex-grow">
|
||||
<el-card>
|
||||
<div v-show="activeIndex === '1'">
|
||||
<ExamList ref="ExamListRef" />
|
||||
</div>
|
||||
<div v-show="activeIndex === '2'">
|
||||
<ApplicationList ref="ApplicationListRef" />
|
||||
</div>
|
||||
<div v-show="activeIndex === '3'">
|
||||
<GradesList ref="GradesListRef" />
|
||||
</div>
|
||||
<div v-show="activeIndex === '4'" style="height: 55rem" class="flex items-center">
|
||||
<div>
|
||||
<img
|
||||
style="width: 88%"
|
||||
src="../../assets/certificate.jpg"
|
||||
alt=""
|
||||
class="mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-show="activeIndex === '5'"
|
||||
style="height: 55rem"
|
||||
class="flex flex-wrap justify-center"
|
||||
>
|
||||
<a
|
||||
href="https://apphhz10f2w7582.h5.xiaoeknow.com/p/course/ecourse/course_2un90Qw1MS5wzgHxrQFe8kvt7wA?shareBind=true"
|
||||
class="h-1/2"
|
||||
>
|
||||
<img style="height: 100%" src="../../assets/training.jpg" alt="" />
|
||||
</a>
|
||||
<div class="h-1/2 relative left-0 top-0">
|
||||
<img style="height: 100%" src="../../assets/exam.jpg" alt="" />
|
||||
<div class="w-full absolute left-0 bottom-20 flex justify-center">
|
||||
<el-button
|
||||
size="large"
|
||||
color="#ffe5cc"
|
||||
@click="urlTo('https://kaoshixing.com/s/CPkEvi')"
|
||||
>
|
||||
<el-text style="font-weight: bold">进入考试平台【</el-text>
|
||||
<el-text style="font-weight: bold; color: red">模拟考试</el-text>
|
||||
<el-text style="font-weight: bold">】</el-text>
|
||||
</el-button>
|
||||
<el-button
|
||||
size="large"
|
||||
color="#ffe5cc"
|
||||
@click="urlTo('https://kaoshixing.com/s/CS4pF6')"
|
||||
>
|
||||
<el-text style="font-weight: bold">进入考试平台【</el-text>
|
||||
<el-text style="font-weight: bold; color: red">正式考试</el-text>
|
||||
<el-text style="font-weight: bold">】</el-text>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
|
||||
import ApplicationList from "../features/applicationList.vue";
|
||||
import ExamList from "../features/examList.vue";
|
||||
import GradesList from "../features/gradesList.vue";
|
||||
|
||||
import examMsgActiveStore from "../../stores/examMsg";
|
||||
|
||||
const examMsgActive = examMsgActiveStore();
|
||||
|
||||
const activeIndex = ref("1");
|
||||
const indexStyle = "background-color: rgb(241 245 249);";
|
||||
const activeStyle = "background-color: rgb(207 250 254);";
|
||||
const ExamListRef = ref(null);
|
||||
const ApplicationListRef = ref(null);
|
||||
const GradesListRef = ref(null);
|
||||
|
||||
const handleSelect = (key, keyPath) => {
|
||||
activeIndex.value = key;
|
||||
switch (key) {
|
||||
case "1":
|
||||
ExamListRef.value.examMsg();
|
||||
break;
|
||||
|
||||
case "2":
|
||||
ApplicationListRef.value.applicationMsg();
|
||||
break;
|
||||
|
||||
case "3":
|
||||
GradesListRef.value.gradesMsg();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const urlTo = (url) => {
|
||||
window.location.href = url;
|
||||
};
|
||||
|
||||
watch(activeIndex, (value) => {
|
||||
examMsgActive.activeIndex = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
activeIndex.value = examMsgActive.activeIndex;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
116
src/components/pageStructure/header.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div
|
||||
class="h-full p-5 flex justify-between items-center"
|
||||
style="background-color: #232d3b"
|
||||
>
|
||||
<div class="flex justify-between items-center">
|
||||
<a href="http://www.beidouin.com"
|
||||
><img style="width: 150px" :src="bdjyLogo" alt=""
|
||||
/></a>
|
||||
</div>
|
||||
<div
|
||||
style="font-size: 2.4rem"
|
||||
class="text-center flex-auto font-bold text-gray-800 text-white"
|
||||
>
|
||||
<div>北斗应用技术培训报名系统</div>
|
||||
<div style="font-size: 1rem">人社部能建中心职业技能培训项目</div>
|
||||
</div>
|
||||
<div v-if="!loginMark" style="width: 320px" class="flex justify-end">
|
||||
<el-button style="font-weight: bold" type="primary" @click="toLogin">
|
||||
登录
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="loginMark" style="width: 320px" class="flex justify-end">
|
||||
<el-button style="font-weight: bold" type="danger" @click="unLogin">
|
||||
退出登录
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { encrypt } from "../../utils/aes";
|
||||
|
||||
import bdqhLogo from "../../assets/bdqhLogo.png";
|
||||
import bdjyLogo from "../../assets/bdjyLogo.png";
|
||||
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const loginMark = ref(false);
|
||||
|
||||
const toLogin = () => {
|
||||
router.push({ path: "/initialService" });
|
||||
};
|
||||
|
||||
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.href = "http://cert.beidouin.com/";
|
||||
// window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
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;
|
||||
}
|
||||
});
|
||||
} 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>
|
||||
17
src/components/pageStructure/main.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="scroll-container w-h-full flex justify-evenly items-center">
|
||||
<UserMsgCard />
|
||||
<UserTabel />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import UserMsgCard from "../operate/userMsgCard.vue";
|
||||
import UserTabel from "../operate/userTabel.vue";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.scroll-container {
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
94
src/components/search/dateTimePicker.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-date-picker
|
||||
v-model="searchMsg"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
range-separator="到"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
@change="search"
|
||||
style="width: 100%; font-size: 1rem"
|
||||
:popper-class="DateTimePicker"
|
||||
@visible-change="handleVisibleChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
DateTimePicker: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
}
|
||||
|
||||
const handleVisibleChange = () => {
|
||||
setTimeout(() => {
|
||||
let changeNode = window.document.getElementsByClassName(props.DateTimePicker)[0];
|
||||
let Triangles = changeNode.children[1];
|
||||
Triangles.style.display = "none";
|
||||
let elppNode = changeNode.children[0];
|
||||
elppNode.style.width = "30rem";
|
||||
elppNode.style.backgroundColor = "#aaffff";
|
||||
let elppbwNode = elppNode.children[0];
|
||||
elppbwNode.style.width = "30rem";
|
||||
let elppfNode = elppNode.children[1];
|
||||
elppfNode.style.width = "30rem";
|
||||
elppfNode.style.backgroundColor = "#aaffff";
|
||||
let elppbNode = elppbwNode.children[0];
|
||||
elppbNode.style.width = "30rem";
|
||||
let elppbCNode = elppbNode.children;
|
||||
elppbCNode[0].style.width = "30rem";
|
||||
let leftDateSelectNode = elppbCNode[0].children[0].children[1];
|
||||
let leftTimeSelectNode = leftDateSelectNode.children[0];
|
||||
leftTimeSelectNode.addEventListener("click", function () {
|
||||
let leftTimeTableNode = leftDateSelectNode.children[1];
|
||||
leftTimeTableNode.style.width = "10rem";
|
||||
leftTimeTableNode.style.fontSize = "1rem";
|
||||
leftTimeTableNode.style.transform = "translate(-20%, 0)";
|
||||
});
|
||||
let rightDateSelectNode = elppbCNode[0].children[2].children[1];
|
||||
let rightTimeSelectNode = rightDateSelectNode.children[0];
|
||||
rightTimeSelectNode.addEventListener("click", function () {
|
||||
let rightTimeTableNode = rightDateSelectNode.children[1];
|
||||
rightTimeTableNode.style.width = "10rem";
|
||||
rightTimeTableNode.style.fontSize = "1rem";
|
||||
rightTimeTableNode.style.transform = "translate(-20%, 0)";
|
||||
});
|
||||
elppbCNode[1].style.width = "15rem";
|
||||
let elppbCFCNode = elppbCNode[1].children;
|
||||
elppbCFCNode[0].style.fontSize = "1rem";
|
||||
let elppbCFCCNode = elppbCFCNode[0].children;
|
||||
elppbCFCCNode[0].style.margin = "0";
|
||||
elppbCFCCNode[1].style.margin = "0";
|
||||
elppbCFCCNode[2].style.fontSize = "1rem";
|
||||
elppbCFCCNode[2].style.margin = "0";
|
||||
elppbCFCNode[1].style.fontSize = "1rem";
|
||||
elppbCNode[2].style.width = "15rem";
|
||||
let elppbCSCNode = elppbCNode[2].children;
|
||||
elppbCSCNode[0].style.fontSize = "1rem";
|
||||
let elppbCSCCNode = elppbCSCNode[0].children;
|
||||
elppbCSCCNode[0].style.margin = "0";
|
||||
elppbCSCCNode[1].style.margin = "0";
|
||||
elppbCSCCNode[2].style.fontSize = "1rem";
|
||||
elppbCSCCNode[2].style.margin = "0";
|
||||
elppbCSCNode[1].style.fontSize = "1rem";
|
||||
}, 0);
|
||||
};
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
|
||||
onMounted(() => {
|
||||
handleVisibleChange();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
57
src/components/search/pagination.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="flex justify-center my-2">
|
||||
<el-pagination
|
||||
class="flex justify-center flex-wrap"
|
||||
v-model:current-page="offset"
|
||||
v-model:page-size="limit"
|
||||
:page-sizes="pageSizes"
|
||||
size="small"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="dataCount"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const offset = ref(1);
|
||||
const limit = ref(10);
|
||||
const dataCount = ref(0);
|
||||
const pageSizes = [10, 20, 50, 100];
|
||||
|
||||
const props = defineProps({
|
||||
active: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search", "sizeChangeBack"]);
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
offset.value = 1;
|
||||
props.active.offset = offset.value;
|
||||
emit("sizeChangeBack", val);
|
||||
limit.value = val;
|
||||
props.active.limit = limit.value;
|
||||
emit("search");
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
offset.value = val;
|
||||
props.active.offset = offset.value;
|
||||
emit("search");
|
||||
};
|
||||
|
||||
defineExpose({ offset, limit, dataCount, handleCurrentChange });
|
||||
|
||||
onMounted(() => {
|
||||
offset.value = props.active.offset;
|
||||
limit.value = props.active.limit;
|
||||
dataCount.value = props.active.dataCount;
|
||||
emit("search");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
40
src/components/search/searchKey.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="输入<span class='text-red-500 font-black'> *空 </span>可搜索空数据"
|
||||
raw-content
|
||||
placement="top"
|
||||
>
|
||||
<el-input
|
||||
size="small"
|
||||
:placeholder="holderText"
|
||||
clearable
|
||||
v-model="searchMsg"
|
||||
@blur="search"
|
||||
style="width: 100%; font-size: 1rem"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
holderText: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
};
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
42
src/components/search/selectSearch.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-select
|
||||
clearable
|
||||
size="small"
|
||||
:placeholder="holderText"
|
||||
v-model="searchMsg"
|
||||
@change="search"
|
||||
style="width: 100%; font-size: 1rem;"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
class="text-center"
|
||||
:style="item.style"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
holderText: String,
|
||||
options: Object
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
}
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
32
src/components/search/sortSearch.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<el-switch
|
||||
size="small"
|
||||
v-model="searchMsg"
|
||||
inline-prompt
|
||||
:active-text="activeText"
|
||||
:inactive-text="inactiveText"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949;"
|
||||
@change="search"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
activeText: String,
|
||||
inactiveText: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
}
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
173
src/components/tools/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="danger">
|
||||
密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*),且长度在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>
|
||||
34
src/components/tools/pay.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="扫码缴费" width="600" center align-center>
|
||||
<div class="w-h-full flex justify-center items-center">
|
||||
<img v-if="!know" src="../../assets/know.png" alt="" />
|
||||
<img v-if="know" src="../../assets/pay.jpg" alt="" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button
|
||||
v-if="!know"
|
||||
style="font-weight: bold"
|
||||
type="warning"
|
||||
size="small"
|
||||
@click="payFee"
|
||||
>
|
||||
我已知晓,继续缴费
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const know = ref(false);
|
||||
|
||||
const payFee = () => {
|
||||
know.value = true;
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible, know });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
79
src/components/tools/slideVerify.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="flex-1">
|
||||
<slide-verify
|
||||
class="mx-auto font-bold"
|
||||
ref="block"
|
||||
:slider-text="text"
|
||||
:accuracy="accuracy"
|
||||
:imgs="imgs"
|
||||
@again="onAgain"
|
||||
@success="onSuccess"
|
||||
@fail="onFail"
|
||||
@refresh="onRefresh"
|
||||
></slide-verify>
|
||||
<div class="flex justify-center items-center">
|
||||
<el-text class="mx-1 font-bold" :style="fontColor"> {{ msg }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import SlideVerify from "vue3-slide-verify";
|
||||
import "vue3-slide-verify/dist/style.css";
|
||||
|
||||
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 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 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;
|
||||
};
|
||||
|
||||
defineExpose({ block, msg, fontColor, verify });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
152
src/components/tools/uploadPhoto.vue
Normal file
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="上传照片" width="600" center align-center>
|
||||
<div class="flex justify-evenly items-center gap-x-12">
|
||||
<div class="w-h-full flex justify-center">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
ref="uploadRef"
|
||||
:action="photoUrl"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
>
|
||||
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div>
|
||||
<el-text>照片要求:</el-text>
|
||||
<br />
|
||||
<p style="text-indent: 2em">
|
||||
本人近期
|
||||
<el-text style="font-weight: bold; color: red"> 正面免冠彩色白底 </el-text>
|
||||
头像,头部占照片尺寸的比例在
|
||||
<el-text style="font-weight: bold; color: red"> 1/2 </el-text>
|
||||
至
|
||||
<el-text style="font-weight: bold; color: red"> 2/3 </el-text>
|
||||
之间,不着白色上衣,常戴眼镜的应配戴眼镜,
|
||||
<el-text style="font-weight: bold; color: red"> 白色背景无边框 </el-text>
|
||||
,人像清晰,层次丰富,神态自然,无明显畸变。
|
||||
</p>
|
||||
<br />
|
||||
<el-text> 照片规格: </el-text>
|
||||
<br />
|
||||
<p style="text-indent: 2em">
|
||||
照片采用
|
||||
<el-text style="font-weight: bold; color: red"> jpg </el-text>
|
||||
格式,并以本人身份证号命名(
|
||||
<el-text style="font-weight: bold; color: red"> X一律使用大写 </el-text>
|
||||
),标准
|
||||
<el-text style="font-weight: bold; color: red"> 两寸白底 </el-text>
|
||||
证件照片, 尺寸为
|
||||
<el-text style="font-weight: bold; color: red">
|
||||
5.3cm(高)×3.5cm(宽)
|
||||
</el-text>
|
||||
,413*626像素,分辨率300dpi,照片大小最小
|
||||
<el-text style="font-weight: bold; color: red"> 不小于150KB </el-text>
|
||||
,最大
|
||||
<el-text style="font-weight: bold; color: red"> 不超过500KB </el-text>
|
||||
,颜色模式24位RGB真彩色。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="resetForm"> 取消 </el-button>
|
||||
<el-button style="font-weight: bold" type="primary" @click="submitUpload">
|
||||
上传照片
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const uploadRef = ref();
|
||||
const imageUrl = ref("");
|
||||
const photoUrl = $url.uploadPhoto;
|
||||
const photo = ref();
|
||||
|
||||
const emit = defineEmits(["updateKey"]);
|
||||
|
||||
const resetForm = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const submitUpload = () => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", photo.value);
|
||||
uploadRef.value.submit();
|
||||
$http.post($url.uploadPhoto, formData).then((res) => {
|
||||
dialogVisible.value = false;
|
||||
if (res.data.code === 200) {
|
||||
emit("updateKey");
|
||||
ElMessage({
|
||||
message: "上传成功!",
|
||||
type: "success",
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("上传失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleAvatarSuccess = (response, uploadFile) => {
|
||||
imageUrl.value = URL.createObjectURL(uploadFile.raw);
|
||||
};
|
||||
|
||||
const beforeAvatarUpload = (rawFile) => {
|
||||
if (rawFile.type !== "image/jpeg") {
|
||||
ElMessage.error("照片应为jpg格式!");
|
||||
return false;
|
||||
} else if (rawFile.size / 1024 < 150 || rawFile.size / 1024 > 500) {
|
||||
ElMessage.error("照片大小应最小不小于150KB,最大不超过500KB!");
|
||||
return false;
|
||||
}
|
||||
photo.value = rawFile;
|
||||
return true;
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.avatar-uploader .avatar {
|
||||
width: 115px;
|
||||
height: 165px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
border-width: 2px;
|
||||
border-color: #a6b6c6;
|
||||
border-style: dashed;
|
||||
background: #f9f9f9;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
330
src/components/tools/userManager.vue
Normal file
@@ -0,0 +1,330 @@
|
||||
<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="user_name">
|
||||
<el-input
|
||||
v-if="ruleForm.real_check != 1"
|
||||
minlength="2"
|
||||
maxlength="10"
|
||||
clearable
|
||||
placeholder="请输入姓名"
|
||||
v-model="ruleForm.user_name"
|
||||
@input="filter(ruleForm.user_name, 'user_name')"
|
||||
/>
|
||||
<div v-if="ruleForm.real_check == 1">
|
||||
{{ ruleForm.user_name }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="身份证号码" prop="identity_card">
|
||||
<el-input
|
||||
v-if="ruleForm.real_check != 1"
|
||||
minlength="18"
|
||||
maxlength="18"
|
||||
clearable
|
||||
placeholder="请输入身份证号码"
|
||||
v-model="ruleForm.identity_card"
|
||||
@input="filter(ruleForm.identity_card, 'identity_card')"
|
||||
/>
|
||||
<div v-if="ruleForm.real_check == 1">
|
||||
{{ ruleForm.identity_card }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="工作单位" prop="workplace">
|
||||
<el-input
|
||||
maxlength="60"
|
||||
clearable
|
||||
placeholder="请输入工作单位(若没有则填“无”)"
|
||||
v-model="ruleForm.workplace"
|
||||
@input="filter(ruleForm.workplace, 'workplace')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="通信地址" prop="address">
|
||||
<el-input
|
||||
maxlength="120"
|
||||
clearable
|
||||
placeholder="请输入通信地址(若没有则填“无”)"
|
||||
v-model="ruleForm.address"
|
||||
@input="filter(ruleForm.address, 'address')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="电话号码" prop="phone">
|
||||
<el-input
|
||||
v-if="ruleForm.real_check != 1"
|
||||
minlength="11"
|
||||
maxlength="11"
|
||||
clearable
|
||||
placeholder="请输入电话号码"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
/>
|
||||
<div v-if="ruleForm.real_check == 1">
|
||||
{{ ruleForm.phone }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="电子邮箱号码" prop="email">
|
||||
<el-input
|
||||
maxlength="60"
|
||||
clearable
|
||||
placeholder="请输入电子邮箱号码"
|
||||
v-model="ruleForm.email"
|
||||
@input="filter(ruleForm.email, 'email')"
|
||||
/>
|
||||
</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, getCurrentInstance } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import { useCommunicationStore } from "../../stores/communicationStore";
|
||||
import userManagerActiveStore from "../../stores/userManager";
|
||||
|
||||
import { encrypt, decrypt } from "../../utils/aes";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const identityCardRegex = /^[1-9](?:\d{5})(?:19|20)?\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}(?:\d|X|x)$/;
|
||||
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 communicationStore = useCommunicationStore();
|
||||
const userManagerActive = userManagerActiveStore();
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
msg: Object,
|
||||
});
|
||||
|
||||
const acabled = ref(true);
|
||||
const ruleFormRef = ref();
|
||||
const ruleForm = reactive({
|
||||
user_name: "",
|
||||
identity_card: "",
|
||||
real_check: "",
|
||||
workplace: "",
|
||||
address: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
let filterValue;
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
filterValue = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
} else {
|
||||
filterValue = value;
|
||||
}
|
||||
if (label == "phone") {
|
||||
ruleForm[label] = filterValue.replace(/\D/g, "");
|
||||
} else {
|
||||
ruleForm[label] = filterValue;
|
||||
}
|
||||
};
|
||||
|
||||
const validateIdentityCard = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入身份证号"));
|
||||
} else if (!identityCardRegex.test(value)) {
|
||||
callback(new Error("身份证格式不正确,请重新输入!"));
|
||||
} else {
|
||||
const data = {
|
||||
identity_card: proxy.$AES_Encrypt(value),
|
||||
type: proxy.$AES_Encrypt("edit"),
|
||||
data: localStorage.data,
|
||||
};
|
||||
$http.post($url.diffIdentityCard, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("该身份证号已被使用"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const validateUserName = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入真实姓名"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validatePhone = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入手机号码"));
|
||||
} else if (!phoneRegex.test(value)) {
|
||||
callback(new Error("手机号码格式错误, 请重新输入!"));
|
||||
} else {
|
||||
const data = {
|
||||
phone: proxy.$AES_Encrypt(value),
|
||||
type: proxy.$AES_Encrypt("edit"),
|
||||
data: localStorage.data,
|
||||
};
|
||||
$http.post($url.diffPhone, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("该手机号码已被使用"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入电子邮箱号码"));
|
||||
} else if (!emailRegex.test(value)) {
|
||||
callback(new Error("邮箱格式不正确,请重新输入!"));
|
||||
} else {
|
||||
const data = {
|
||||
email: proxy.$AES_Encrypt(value),
|
||||
type: proxy.$AES_Encrypt("edit"),
|
||||
data: localStorage.data,
|
||||
};
|
||||
$http.post($url.diffEmail, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("该电子邮箱号码已被使用"));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
user_name: [{ required: true, validator: validateUserName, trigger: "blur" }],
|
||||
identity_card: [{ required: true, validator: validateIdentityCard, trigger: "blur" }],
|
||||
workplace: {
|
||||
required: true,
|
||||
message: "请输入工作单位(若没有则填“无”)",
|
||||
trigger: "blur",
|
||||
},
|
||||
address: { required: true, message: "请输入通讯地址(若没有则填“无”)", trigger: "blur" },
|
||||
phone: [
|
||||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||||
{ min: 11, max: 11, message: "输入长度应当为 11 位", trigger: "change" },
|
||||
{ validator: validatePhone },
|
||||
],
|
||||
email: [
|
||||
{ required: true, validator: validateEmail, trigger: "blur" },
|
||||
{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] },
|
||||
],
|
||||
});
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const emit = defineEmits(["resetData"]);
|
||||
|
||||
const reset = () => {
|
||||
ruleForm.user_name = props.msg.user_name;
|
||||
ruleForm.identity_card = props.msg.identity_card;
|
||||
ruleForm.real_check = props.msg.real_check;
|
||||
ruleForm.workplace = props.msg.workplace;
|
||||
ruleForm.address = props.msg.address;
|
||||
ruleForm.phone = props.msg.phone;
|
||||
ruleForm.email = props.msg.email;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => communicationStore.trigger,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
emit("resetData");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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");
|
||||
ElMessage({
|
||||
message: "提交成功!",
|
||||
type: "success",
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("编辑信息失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible, reset });
|
||||
|
||||
watch(ruleForm, (value) => {
|
||||
if (dialogVisible.value) {
|
||||
userManagerActive.dialogVisible = dialogVisible.value;
|
||||
userManagerActive.ruleForm.user_name = encrypt(value.user_name);
|
||||
userManagerActive.ruleForm.identity_card = encrypt(value.identity_card);
|
||||
userManagerActive.ruleForm.real_check = value.real_check;
|
||||
userManagerActive.ruleForm.workplace = encrypt(value.workplace);
|
||||
userManagerActive.ruleForm.address = encrypt(value.address);
|
||||
userManagerActive.ruleForm.phone = encrypt(value.phone);
|
||||
userManagerActive.ruleForm.email = encrypt(value.email);
|
||||
}
|
||||
});
|
||||
|
||||
watch(dialogVisible, (value) => {
|
||||
if (!value) {
|
||||
userManagerActive.dialogVisible = value;
|
||||
userManagerActive.ruleForm = {};
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
dialogVisible.value = userManagerActive.dialogVisible;
|
||||
acabled.value = false;
|
||||
ruleForm.email = "";
|
||||
ruleForm.workplace = "";
|
||||
ruleForm.address = "";
|
||||
if (userManagerActive.dialogVisible) {
|
||||
ruleForm.user_name = decrypt(userManagerActive.ruleForm.user_name);
|
||||
ruleForm.identity_card = decrypt(userManagerActive.ruleForm.identity_card);
|
||||
ruleForm.real_check = userManagerActive.ruleForm.real_check;
|
||||
ruleForm.workplace = decrypt(userManagerActive.ruleForm.workplace);
|
||||
ruleForm.address = decrypt(userManagerActive.ruleForm.address);
|
||||
ruleForm.phone = decrypt(userManagerActive.ruleForm.phone);
|
||||
ruleForm.email = decrypt(userManagerActive.ruleForm.email);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
23
src/main.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import './assets/main.css';
|
||||
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import piniaPersist from 'pinia-plugin-persist';
|
||||
import ElementPlus from 'element-plus';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
import 'element-plus/dist/index.css';
|
||||
import { AES_Encrypt } from '@/utils/aes.js';
|
||||
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPersist);
|
||||
|
||||
const app = createApp(App);
|
||||
app.config.globalProperties.$AES_Encrypt = AES_Encrypt //全局加密
|
||||
app.use(pinia);
|
||||
app.use(ElementPlus, { locale: zhCn });
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app');
|
||||
46
src/router/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/404page',
|
||||
name: '404page',
|
||||
meta: { label: "404页面" },
|
||||
component: () => import('../views/404page.vue')
|
||||
},
|
||||
{
|
||||
path: '/initialService',
|
||||
name: 'initialService',
|
||||
meta: { label: "登录页面" },
|
||||
component: () => import('../views/InitialService.vue')
|
||||
},
|
||||
{
|
||||
path: '/main',
|
||||
name: 'main',
|
||||
meta: { label: "主页面" },
|
||||
component: () => import('../views/Main.vue')
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.matched.length === 0) {
|
||||
if (to.path == "/") {
|
||||
router.push({ path: '/main' });
|
||||
} else if (to.path == "/initialService") {
|
||||
router.push({ path: '/initialService' });
|
||||
} else {
|
||||
router.push({ path: '/main' });
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
if(localStorage.length == 0) {
|
||||
setTimeout(() => {
|
||||
router.push({ path: '/initialService' });
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default router
|
||||
47
src/stores/applicationMsg.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const applicationMsgActiveStore = defineStore('applicationMsg', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
dataCount: 0,
|
||||
pageCount: 0,
|
||||
searchOptions: {
|
||||
exam_uid: "",
|
||||
exam_name: "",
|
||||
status: "",
|
||||
pay_status: "",
|
||||
created_at: "",
|
||||
application_end: "",
|
||||
exam_start: "",
|
||||
exam_end: "",
|
||||
},
|
||||
sortOptions: {
|
||||
created_at: false,
|
||||
application_end: false,
|
||||
exam_start: false,
|
||||
exam_end: false,
|
||||
},
|
||||
dialogSearchVisible: false,
|
||||
dialogSortVisible: false,
|
||||
dialogOperationVisible: false
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'applicationMsgActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["activeIndex", "offset", "limit", "dataCount", "pageCount", "searchOptions", "sortOptions", "dialogSearchVisible", "dialogSortVisible", "dialogOperationVisible"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default applicationMsgActiveStore;
|
||||
16
src/stores/communicationStore.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useCommunicationStore = defineStore('communication', {
|
||||
state: () => ({
|
||||
trigger: true,
|
||||
editSwitch: true
|
||||
}),
|
||||
actions: {
|
||||
setTrigger(value) {
|
||||
this.trigger = value;
|
||||
},
|
||||
setEditSwitch(value) {
|
||||
this.editSwitch = value;
|
||||
}
|
||||
},
|
||||
});
|
||||
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 }
|
||||
})
|
||||
45
src/stores/examMsg.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const examMsgActiveStore = defineStore('examMsg', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
activeIndex: "1",
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
dataCount: 0,
|
||||
pageCount: 0,
|
||||
searchOptions: {
|
||||
uid: "",
|
||||
exam_name: "",
|
||||
status: "",
|
||||
application_end: "",
|
||||
exam_start: "",
|
||||
exam_end: "",
|
||||
},
|
||||
sortOptions: {
|
||||
application_end: false,
|
||||
exam_start: false,
|
||||
exam_end: false,
|
||||
},
|
||||
dialogSearchVisible: false,
|
||||
dialogSortVisible: false,
|
||||
dialogOperationVisible: false
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'examMsgActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["activeIndex", "offset", "limit", "dataCount", "pageCount", "searchOptions", "sortOptions", "dialogSearchVisible", "dialogSortVisible", "dialogOperationVisible"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default examMsgActiveStore;
|
||||
41
src/stores/gradesMsg.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const gradesMsgActiveStore = defineStore('gradesMsg', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
dataCount: 0,
|
||||
pageCount: 0,
|
||||
searchOptions: {
|
||||
exam_uid: "",
|
||||
exam_name: "",
|
||||
status: "",
|
||||
grades: "",
|
||||
pass_type: "",
|
||||
},
|
||||
sortOptions: {
|
||||
grades: false,
|
||||
},
|
||||
dialogSearchVisible: false,
|
||||
dialogSortVisible: false,
|
||||
dialogOperationVisible: false
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'gradesMsgActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["activeIndex", "offset", "limit", "dataCount", "pageCount", "searchOptions", "sortOptions", "dialogSearchVisible", "dialogSortVisible", "dialogOperationVisible"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default gradesMsgActiveStore;
|
||||
34
src/stores/userManager.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const userManagerActiveStore = defineStore('userManager', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
dialogVisible: false,
|
||||
ruleForm: {
|
||||
user_name: "",
|
||||
identity_card: "",
|
||||
real_check: "",
|
||||
address: "",
|
||||
workplace: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
},
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'userManagerActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["dialogVisible", "ruleForm"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
})
|
||||
|
||||
export default userManagerActiveStore
|
||||
29
src/utils/aes.js
Normal file
@@ -0,0 +1,29 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
9
src/utils/hasNullOrEmptyPropertyWithTypeof.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export const hasNullOrEmptyPropertyWithTypeof = (obj) => {
|
||||
let hasNullOrEmpty = false;
|
||||
for (const prop in obj) {
|
||||
if (!(obj.hasOwnProperty(prop) && ((obj[prop] === null) || obj[prop] === undefined || (typeof obj[prop] === 'string' && obj[prop] === '')))) {
|
||||
hasNullOrEmpty = true;
|
||||
}
|
||||
}
|
||||
return hasNullOrEmpty;
|
||||
}
|
||||
5
src/utils/judgment.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export const judgment = (keyWord, key, value, val) => {
|
||||
if (value !== null) {
|
||||
keyWord[key] = value[val];
|
||||
}
|
||||
}
|
||||
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 type="primary" @click="backHome">返回首页</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const backHome = () => {
|
||||
router.push({ path: '/main' });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
62
src/views/InitialService.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<el-container class="w-h-full justify-center items-center bg-sky-200">
|
||||
<el-main>
|
||||
<Login v-show="pageMark == 'login'" @changePage="changePage" />
|
||||
<Registered v-show="pageMark == 'registered'" @changePage="changePage" />
|
||||
<ResetPassword v-show="pageMark == 'resetPassword'" @changePage="changePage" />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
|
||||
import $http from "../api/index";
|
||||
import $url from "../api/url";
|
||||
|
||||
import Login from "../components/initialService/login.vue";
|
||||
import Registered from "../components/initialService/registered.vue";
|
||||
import ResetPassword from "../components/initialService/resetPassword.vue";
|
||||
|
||||
const pageMark = ref("login");
|
||||
|
||||
const changePage = (value) => {
|
||||
pageMark.value = value;
|
||||
};
|
||||
|
||||
watch(pageMark, (value) => {});
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
localStorage.data &&
|
||||
localStorage.data !== "" &&
|
||||
localStorage.data !== null &&
|
||||
localStorage.data !== undefined &&
|
||||
localStorage.data !== NaN
|
||||
) {
|
||||
let client_id = "exam";
|
||||
const data = {
|
||||
data: localStorage.data,
|
||||
client_id,
|
||||
};
|
||||
$http.post($url.loginStatus, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
if (
|
||||
localStorage.data &&
|
||||
localStorage.data !== "" &&
|
||||
localStorage.data !== null &&
|
||||
localStorage.data !== undefined &&
|
||||
localStorage.data !== NaN
|
||||
) {
|
||||
window.location.href = window.location.href.replace("/initialService", "/main");
|
||||
}
|
||||
} else {
|
||||
localStorage.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
19
src/views/Main.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="common-layout w-h-full">
|
||||
<el-container class="w-h-full">
|
||||
<el-header style="padding: 0" height="120px">
|
||||
<Header />
|
||||
</el-header>
|
||||
<el-main style="padding: 0">
|
||||
<Main />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Header from "../components/pageStructure/header.vue";
|
||||
import Main from "../components/pageStructure/main.vue";
|
||||
</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'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
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: 7031,
|
||||
proxy: {
|
||||
'/authentication': {
|
||||
target: 'http://127.0.0.1:8011',//鉴权服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/authentication/, '')
|
||||
},
|
||||
'/bdTrainingExam': {
|
||||
target: 'http://127.0.0.1:8019',//目标服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/bdTrainingExam/, '')
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||