北斗应用技术培训考试管理系统

This commit is contained in:
2026-03-27 17:40:17 +08:00
commit 7eb6b8b530
145 changed files with 31320 additions and 0 deletions

30
.gitignore vendored Normal file
View 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
View 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
```

13
index.html Normal file
View File

@@ -0,0 +1,13 @@
<!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>
</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
View File

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

8028
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
package.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "bd-training-exam-manager",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port=7022",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.8",
"bd-training-exam-manager": "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",
"xlsx": "^0.18.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
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

9
src/App.vue Normal file
View File

@@ -0,0 +1,9 @@
<template>
<RouterView />
</template>
<script setup>
import { RouterView } from "vue-router";
</script>
<style scoped></style>

130
src/api/index.js Normal file
View File

@@ -0,0 +1,130 @@
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: '/alogin' });
};
// 添加响应拦截器
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) => {
console.log(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);
},
}

192
src/api/url.js Normal file
View File

@@ -0,0 +1,192 @@
export default {
// 退出登录
unLogin: "/authentication/unLogin",
// 登录请求接口
alogin: "/authentication/alogin",
// 获取登录状态
loginStatus: "/authentication/loginStatus",
// 用于获取token
getToken: "/authentication/getToken",
// 用于验证token
verify: "/authentication/verify",
// 获取用户基本信息
getUser: "/authentication/getUser",
// 修改用户基本信息
editUser: "/authentication/editUser",
// 修改密码
changePassword: "/authentication/changePassword",
// 重置密码
resetPassword: "/authentication/resetPassword",
// 管理系统新增普通管理员账号信息
addAdmin: "/bdTrainingExamManager/addAdmin",
// 管理系统查看普通管理员账号信息
showAdmin: "/bdTrainingExamManager/showAdmin",
// 管理系统编辑普通管理员账号信息
editAdmin: "/bdTrainingExamManager/editAdmin",
// 管理系统删除普通管理员账号信息
deleteAdmin: "/bdTrainingExamManager/deleteAdmin",
// 管理系统导出管理员账号信息数据
downloadAdminData: "/bdTrainingExamManager/downloadAdminData",
// 管理系统新增考试信息
addExam: "/bdTrainingExamManager/addExam",
// 管理系统查看考试信息
showExam: "/bdTrainingExamManager/showExam",
// 管理系统编辑考试信息
editExam: "/bdTrainingExamManager/editExam",
// 管理系统删除考试信息
deleteExam: "/bdTrainingExamManager/deleteExam",
// 管理系统导出考试信息数据
downloadExamData: "/bdTrainingExamManager/downloadExamData",
// 管理系统新增用户账号信息
addUser: "/bdTrainingExamManager/addUser",
// 管理系统查看用户账号信息
showUser: "/bdTrainingExamManager/showUser",
// 管理系统编辑用户账号信息
editUser: "/bdTrainingExamManager/editUser",
// 管理系统删除用户账号信息
deleteUser: "/bdTrainingExamManager/deleteUser",
// 管理系统导出用户账号信息数据
downloadUserData: "/bdTrainingExamManager/downloadUserData",
// 比较身份证号
diffIdentityCard: "/bdTrainingExamManager/diffIdentityCard",
// 比较邮箱
diffEmail: "/bdTrainingExamManager/diffEmail",
// 比较电话号码
diffPhone: "/bdTrainingExamManager/diffPhone",
// 管理系统查看用户关键信息变更
auditList: "/bdTrainingExamManager/auditList",
// 管理系统审核用户关键信息变更
userMsgAudit: "/bdTrainingExamManager/userMsgAudit",
// 管理系统检索考生身份证号码
searchIdentityCard: "/bdTrainingExamManager/searchIdentityCard",
// 管理系统检索考生姓名
searchUserName: "/bdTrainingExamManager/searchUserName",
// 管理系统检索考试 uid
searchExamUid: "/bdTrainingExamManager/searchExamUid",
// 管理系统检索工作单位
searchWorkplace: "/bdTrainingExamManager/searchWorkplace",
// 管理系统检索报名结果说明
searchResult: "/bdTrainingExamManager/searchResult",
// 管理系统查找指定考生姓名
findUserName: "/bdTrainingExamManager/findUserName",
// 管理系统查找指定考生身份证号
findIdentityCard: "/bdTrainingExamManager/findIdentityCard",
// 管理系统查找指定考试 uid
findExamUid: "/bdTrainingExamManager/findExamUid",
// 管理系统查找指定工作单位
findWorkplace: "/bdTrainingExamManager/findWorkplace",
// 管理系统新增考生报名信息
addApplication: "/bdTrainingExamManager/addApplication",
// 管理系统查看考生报名信息
showApplication: "/bdTrainingExamManager/showApplication",
// 管理系统编辑考生报名信息
editApplication: "/bdTrainingExamManager/editApplication",
// 管理系统删除考生报名信息
deleteApplication: "/bdTrainingExamManager/deleteApplication",
// 管理系统导出考生报名信息数据
downloadApplicationData: "/bdTrainingExamManager/downloadApplicationData",
// 管理系统新增考生考试结果
addGrades: "/bdTrainingExamManager/addGrades",
// 管理系统查看考生考试结果
showGrades: "/bdTrainingExamManager/showGrades",
// 管理系统编辑考生考试结果
editGrades: "/bdTrainingExamManager/editGrades",
// 管理系统删除考生考试结果
deleteGrades: "/bdTrainingExamManager/deleteGrades",
// 管理系统导出考生考试结果数据
downloadGradesData: "/bdTrainingExamManager/downloadGradesData",
// 查看导入错误记录
showImporterror: "/bdTrainingExamManager/showImporterror",
// 删除导入错误记录
deleteImporterror: "/bdTrainingExamManager/deleteImporterror",
// 管理系统新增题库白名单
addTopicWhitelist: "/bdTrainingExamManager/addTopicWhitelist",
// 管理系统查看题库白名单
showTopicWhitelist: "/bdTrainingExamManager/showTopicWhitelist",
// 管理系统编辑题库白名单
editTopicWhitelist: "/bdTrainingExamManager/editTopicWhitelist",
// 管理系统删除题库白名单
deleteTopicWhitelist: "/bdTrainingExamManager/deleteTopicWhitelist",
// 管理系统导出题库白名单
downloadTopicWhitelistData: "/bdTrainingExamManager/downloadTopicWhitelistData",
// 管理系统新增问答题
addInterlocution: "/bdTrainingExamManager/addInterlocution",
// 管理系统查看问答题
showInterlocution: "/bdTrainingExamManager/showInterlocution",
// 管理系统编辑问答题
editInterlocution: "/bdTrainingExamManager/editInterlocution",
// 管理系统删除问答题
deleteInterlocution: "/bdTrainingExamManager/deleteInterlocution",
// 管理系统导出问答题
downloadInterlocutionData: "/bdTrainingExamManager/downloadInterlocutionData",
// 管理系统新增选择题
addJudgment: "/bdTrainingExamManager/addJudgment",
// 管理系统查看选择题
showJudgment: "/bdTrainingExamManager/showJudgment",
// 管理系统编辑选择题
editJudgment: "/bdTrainingExamManager/editJudgment",
// 管理系统删除选择题
deleteJudgment: "/bdTrainingExamManager/deleteJudgment",
// 管理系统导出选择题
downloadJudgmentData: "/bdTrainingExamManager/downloadJudgmentData",
// 管理系统新增多选题
addMultiple: "/bdTrainingExamManager/addMultiple",
// 管理系统查看多选题
showMultiple: "/bdTrainingExamManager/showMultiple",
// 管理系统编辑多选题
editMultiple: "/bdTrainingExamManager/editMultiple",
// 管理系统删除多选题
deleteMultiple: "/bdTrainingExamManager/deleteMultiple",
// 管理系统导出多选题
downloadMultipleData: "/bdTrainingExamManager/downloadMultipleData",
// 管理系统新增单选题
addRadio: "/bdTrainingExamManager/addRadio",
// 管理系统查看单选题
showRadio: "/bdTrainingExamManager/showRadio",
// 管理系统编辑单选题
editRadio: "/bdTrainingExamManager/editRadio",
// 管理系统删除单选题
deleteRadio: "/bdTrainingExamManager/deleteRadio",
// 管理系统导出单选题
downloadRadioData: "/bdTrainingExamManager/downloadRadioData",
// 查看监控记录
showMonitoring: "/bdTrainingExamManager/showMonitoring",
// 删除监控记录
deleteMonitoring: "/bdTrainingExamManager/deleteMonitoring",
// 导出小鹅通导入用户
importXiaoeTech: "/bdTrainingExamManager/importXiaoeTech",
// 导出小鹅通开课学员
commencementXiaoeTech: "/bdTrainingExamManager/commencementXiaoeTech",
// 导出批量报名导入 excel 表
importApplicationData: "/bdTrainingExamManager/importApplicationData",
// 导出考试星学员导入 excel 表
importExamStars: "/bdTrainingExamManager/importExamStars",
// 导出培训证书发放用户导入 excel 表
importIssuance: "/bdTrainingExamManager/importIssuance",
// 导出培训证书发放用户照片文件夹
importUserPhoto: "/bdTrainingExamManager/importUserPhoto",
// 上传用户照片
uploadUserPhoto: "/bdTrainingExamManager/uploadUserPhoto",
// 导出考试成绩导入 excel 表
importGradesData: "/bdTrainingExamManager/importGradesData",
// 核验用户身份证二要素
CNTEV: "/bdTrainingExamManager/CNTEV",
}

BIN
src/assets/bdjy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
src/assets/bdjyLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
src/assets/bdqhLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/assets/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 KiB

BIN
src/assets/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

1
src/assets/logo.svg Normal file
View 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
View File

@@ -0,0 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.w-h-full {
@apply w-full h-full m-0 p-0 overflow-hidden;
}
}

BIN
src/assets/qrcode.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -0,0 +1,163 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="adminMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addAdmin="addAdmin"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addAdmin="addAdmin"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addAdmin="addAdmin" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import adminActiveStore from "../../../stores/adminMsg";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes";
const adminMsgActive = adminActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
adminMsgActive.dialogSingleVisible = dialogSingleVisible.value;
adminMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
adminMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
adminMsgActive.dialogSingleVisible = dialogSingleVisible.value;
adminMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
adminMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
adminMsgActive.dialogSingleVisible = dialogSingleVisible.value;
adminMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
adminMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addAdmin = (requestMsg) => {
requestMsg.forEach((element) => {
element.account_name = encrypt(String(element.account_name));
element.role = encrypt(String(element.role));
});
const data = { requestMsg };
$http.post($url.addAdmin, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增普通管理员成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增普通管理员失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,92 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
account_name: {
value: "",
label: "账号名",
},
role: {
value: "",
label: "权限等级",
},
};
const relatedOptions = [
{
value: "0",
label: "账号名",
disabled: false,
},
{
value: "1",
label: "权限等级",
disabled: false,
},
];
const emit = defineEmits(["addAdmin"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
account_name: item[relatedRules.account_name.value],
role: item[relatedRules.role.value],
});
}
});
});
});
emit("addAdmin", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "管理员1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addAdmin"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.role = singleAddRef.value[0].ruleForm.role;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `管理员${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addAdmin", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,110 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="账号名" prop="account_name">
<el-input
clearable
placeholder="请输入账号名"
minlength="2"
maxlength="5"
v-model="ruleForm.account_name"
@input="filter(ruleForm.account_name, 'account_name')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="权限等级" prop="role">
<el-select
clearable
size="small"
placeholder="请选择权限等级"
autocomplete="off"
v-model="ruleForm.role"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
account_name: "",
role: "",
});
const formClone = ref({});
const rules = reactive({
account_name: [{ required: true, message: "请输入账号名", trigger: "blur" }],
role: [{ required: true, message: "请选择权限等级", trigger: "change" }],
});
const emit = defineEmits(["addAdmin"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addAdmin", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,179 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="applicationMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addApplication="addApplication"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addApplication="addApplication"
/>
<ExcleAdd
ref="excleAddRef"
v-if="dialogExcleVisible"
@addApplication="addApplication"
/>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import applicationActiveStore from "../../../stores/applicationMsg";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes";
const applicationMsgActive = applicationActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
applicationMsgActive.dialogSingleVisible = dialogSingleVisible.value;
applicationMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
applicationMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
applicationMsgActive.dialogSingleVisible = dialogSingleVisible.value;
applicationMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
applicationMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
applicationMsgActive.dialogSingleVisible = dialogSingleVisible.value;
applicationMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
applicationMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addApplication = (requestMsg) => {
requestMsg.forEach((element) => {
element.identity_card = encrypt(String(element.identity_card));
element.user_name = encrypt(String(element.user_name));
element.exam_uid = encrypt(String(element.exam_uid));
element.source = encrypt(String(element.source));
element.status = encrypt(String(element.status));
element.pay_status = encrypt(String(element.pay_status));
element.result = encrypt(String(element.result));
});
const data = { requestMsg };
$http.post($url.addApplication, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增考生报名信息成功!",
type: "success",
});
emit("reloadData");
} else if (res.data.code === 300) {
resetCheckForm();
ElMessage({
message: "新增部分考生报名信息成功,请查看新增失败的数据以查找原因!",
type: "warning",
});
emit("reloadData");
} else {
ElMessage.error("新增考生报名信息失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,142 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
identity_card: {
value: "",
label: "考生身份证号",
},
user_name: {
value: "",
label: "考生姓名",
},
exam_uid: {
value: "",
label: "考试uid",
},
source: {
value: "",
label: "报名来源",
},
status: {
value: "",
label: "报名状态",
},
pay_status: {
value: "",
label: "缴费状态",
},
result: {
value: "",
label: "报名结果说明",
},
};
const relatedOptions = [
{
value: "0",
label: "考生身份证号",
disabled: false,
},
{
value: "1",
label: "考生姓名",
disabled: false,
},
{
value: "2",
label: "考试uid",
disabled: false,
},
{
value: "3",
label: "报名来源",
disabled: false,
},
{
value: "4",
label: "报名状态",
disabled: false,
},
{
value: "5",
label: "缴费状态",
disabled: false,
},
{
value: "6",
label: "报名结果说明",
disabled: false,
},
];
const emit = defineEmits(["addApplication"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
identity_card: item[relatedRules.identity_card.value],
user_name: item[relatedRules.user_name.value],
exam_uid: item[relatedRules.exam_uid.value],
source: item[relatedRules.source.value],
status: item[relatedRules.status.value],
pay_status: item[relatedRules.pay_status.value],
result: item[relatedRules.result.value],
});
}
});
});
});
emit("addApplication", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,119 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "报名数据1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addApplication"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.source = singleAddRef.value[0].ruleForm.source;
item.ruleForm.status = singleAddRef.value[0].ruleForm.status;
item.ruleForm.pay_status = singleAddRef.value[0].ruleForm.pay_status;
item.ruleForm.result = singleAddRef.value[0].ruleForm.result;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `报名数据${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addApplication", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,282 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="考生姓名" prop="user_name">
<el-autocomplete
:fetch-suggestions="searchUserName"
clearable
placeholder="请输入考生姓名"
minlength="1"
maxlength="6"
v-model="ruleForm.user_name"
@input="filter(ruleForm.user_name, 'user_name')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="身份证号" prop="identity_card">
<el-autocomplete
:fetch-suggestions="searchIdentityCard"
clearable
placeholder="请输入身份证号"
minlength="18"
maxlength="18"
v-model="ruleForm.identity_card"
@input="filter(ruleForm.identity_card, 'identity_card')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="exam_uid">
<el-autocomplete
:fetch-suggestions="searchExamUid"
clearable
placeholder="请输入考试uid"
minlength="10"
maxlength="10"
v-model="ruleForm.exam_uid"
@input="filter(ruleForm.exam_uid, 'exam_uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名来源" prop="source">
<el-input
clearable
placeholder="请输入报名来源"
maxlength="24"
v-model="ruleForm.source"
@input="filter(ruleForm.source, 'source')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择报名状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="缴费状态" prop="pay_status">
<el-select
clearable
size="small"
placeholder="请选择缴费状态"
autocomplete="off"
v-model="ruleForm.pay_status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.payStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名结果说明" prop="result">
<el-input
clearable
placeholder="请输入报名结果说明"
maxlength="24"
v-model="ruleForm.result"
@input="filter(ruleForm.result, 'result')"
/>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
const ruleFormRef = ref(null);
const ruleForm = reactive({
user_name: "",
identity_card: "",
exam_uid: "",
source: "",
status: "",
pay_status: "",
result: "",
});
const formClone = ref({});
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 searchUserName = (value, cb) => {
const data = {
user_name: value,
};
$http.post($url.searchUserName, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateUserName = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考生姓名"));
} else {
const data = {
user_name: value,
};
$http.post($url.findUserName, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考生不存在!"));
}
});
}
};
const searchIdentityCard = (value, cb) => {
const data = {
identity_card: value,
};
$http.post($url.searchIdentityCard, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateIdentityCard = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入身份证号"));
} else if (!identityCardRegex.test(value)) {
callback(new Error("身份证格式不正确,请重新输入!"));
} else if (ruleForm.user_name == "") {
callback(new Error("请输入考生姓名"));
} else {
const data = {
identity_card: value,
user_name: ruleForm.user_name,
};
$http.post($url.findIdentityCard, data).then((res) => {
if (res.data.code === 200) {
callback();
} else if (res.data.code === 303) {
callback(new Error("考生身份证号码与考生姓名不匹配!"));
} else {
callback(new Error("该考生身份证号未注册!"));
}
});
}
};
const searchExamUid = (value, cb) => {
const data = {
exam_uid: value,
};
$http.post($url.searchExamUid, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateExamUid = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考试uid"));
} else {
const data = {
exam_uid: value,
};
$http.post($url.findExamUid, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考试不存在!"));
}
});
}
};
const rules = reactive({
identity_card: [{ required: true, validator: validateIdentityCard, trigger: "blur" }],
user_name: [{ required: true, validator: validateUserName, trigger: "blur" }],
exam_uid: [{ required: true, validator: validateExamUid, trigger: "blur" }],
source: [{ required: true, message: "请输入报名来源", trigger: "blur" }],
status: [{ required: true, message: "请选择报名状态", trigger: "change" }],
pay_status: [{ required: true, message: "请选择缴费状态", trigger: "change" }],
result: [{ required: true, message: "请输入报名结果说明", trigger: "blur" }],
});
const emit = defineEmits(["addApplication"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addApplication", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,168 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="examMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addExam="addExam"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addExam="addExam"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addExam="addExam" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import examActiveStore from "../../../stores/examMsg";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes";
const examMsgActive = examActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
examMsgActive.dialogSingleVisible = dialogSingleVisible.value;
examMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
examMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
examMsgActive.dialogSingleVisible = dialogSingleVisible.value;
examMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
examMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
examMsgActive.dialogSingleVisible = dialogSingleVisible.value;
examMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
examMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addExam = (requestMsg) => {
requestMsg.forEach((element) => {
element.uid = encrypt(String(element.uid));
element.exam_name = encrypt(String(element.exam_name));
element.status = encrypt(String(element.status));
element.application_start = new Date(element.application_start);
element.application_end = new Date(element.application_end);
element.exam_start = new Date(element.exam_start);
element.exam_end = new Date(element.exam_end);
});
const data = { requestMsg };
$http.post($url.addExam, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增考试信息成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增考试信息失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,142 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
uid: {
value: "",
label: "考试uid",
},
exam_name: {
value: "",
label: "考试名称",
},
status: {
value: "",
label: "考试状态",
},
application_start: {
value: "",
label: "报名开始时间",
},
application_end: {
value: "",
label: "报名结束时间",
},
exam_start: {
value: "",
label: "考试开始时间",
},
exam_end: {
value: "",
label: "考试结束时间",
},
};
const relatedOptions = [
{
value: "0",
label: "考试uid",
disabled: false,
},
{
value: "1",
label: "考试名称",
disabled: false,
},
{
value: "2",
label: "考试状态",
disabled: false,
},
{
value: "3",
label: "报名开始时间",
disabled: false,
},
{
value: "4",
label: "报名结束时间",
disabled: false,
},
{
value: "5",
label: "考试开始时间",
disabled: false,
},
{
value: "6",
label: "考试结束时间",
disabled: false,
},
];
const emit = defineEmits(["addExam"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
uid: item[relatedRules.uid.value],
exam_name: item[relatedRules.exam_name.value],
status: item[relatedRules.status.value],
application_start: item[relatedRules.application_start.value],
application_end: item[relatedRules.application_end.value],
exam_start: item[relatedRules.exam_start.value],
exam_end: item[relatedRules.exam_end.value],
});
}
});
});
});
emit("addExam", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "考试1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addExam"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.status = singleAddRef.value[0].ruleForm.status;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `考试${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addExam", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,187 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="uid">
<el-input
clearable
placeholder="请输入考试uid"
minlength="10"
maxlength="10"
v-model="ruleForm.uid"
@input="filter(ruleForm.uid, 'uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试名称" prop="exam_name">
<el-input
clearable
placeholder="请输入考试名称"
maxlength="60"
v-model="ruleForm.exam_name"
@input="filter(ruleForm.exam_name, 'exam_name')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择考试状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item label="报名起止时间">
<el-col :span="11">
<el-form-item prop="application_start">
<el-date-picker
v-model="ruleForm.application_start"
type="datetime"
placeholder="请选择报名开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-form-item prop="application_end">
<el-date-picker
v-model="ruleForm.application_end"
type="datetime"
placeholder="请选择报名结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="考试起止时间">
<el-col :span="11">
<el-form-item prop="exam_start">
<el-date-picker
v-model="ruleForm.exam_start"
type="datetime"
placeholder="请选择考试开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-form-item prop="exam_end">
<el-date-picker
v-model="ruleForm.exam_end"
type="datetime"
placeholder="请选择考试结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
uid: "",
exam_name: "",
status: "",
application_start: "",
application_end: "",
exam_start: "",
exam_end: "",
});
const formClone = ref({});
const rules = reactive({
uid: [{ required: true, message: "请输入考试uid", trigger: "blur" }],
exam_name: [{ required: true, message: "请输入考试名称", trigger: "blur" }],
status: [{ required: true, message: "请选择考试状态", trigger: "change" }],
application_start: [
{ type: "date", required: true, message: "请选择报名开始时间", trigger: "change" },
],
application_end: [
{ type: "date", required: true, message: "请选择报名结束时间", trigger: "change" },
],
exam_start: [
{ type: "date", required: true, message: "请选择考试开始时间", trigger: "change" },
],
exam_end: [
{ type: "date", required: true, message: "请选择考试结束时间", trigger: "change" },
],
});
const emit = defineEmits(["addExam"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addExam", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,132 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
user_name: {
value: "",
label: "考生姓名",
},
identity_card: {
value: "",
label: "身份证号",
},
exam_uid: {
value: "",
label: "考试uid",
},
status: {
value: "",
label: "参与状态",
},
grades: {
value: "",
label: "考试成绩",
},
pass_type: {
value: "",
label: "考试结果",
},
};
const relatedOptions = [
{
value: "0",
label: "考生姓名",
disabled: false,
},
{
value: "1",
label: "身份证号",
disabled: false,
},
{
value: "2",
label: "考试uid",
disabled: false,
},
{
value: "3",
label: "参与状态",
disabled: false,
},
{
value: "4",
label: "考试成绩",
disabled: false,
},
{
value: "5",
label: "考试结果",
disabled: false,
},
];
const emit = defineEmits(["addGrades"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
identity_card: item[relatedRules.identity_card.value],
user_name: item[relatedRules.user_name.value],
exam_uid: item[relatedRules.exam_uid.value],
status: item[relatedRules.status.value],
grades: item[relatedRules.grades.value],
pass_type: item[relatedRules.pass_type.value],
});
}
});
});
});
emit("addGrades", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,174 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="gradesMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addGrades="addGrades"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addGrades="addGrades"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addGrades="addGrades" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import gradesActiveStore from "../../../stores/gradesMsg";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes";
const gradesMsgActive = gradesActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
gradesMsgActive.dialogSingleVisible = dialogSingleVisible.value;
gradesMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
gradesMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
gradesMsgActive.dialogSingleVisible = dialogSingleVisible.value;
gradesMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
gradesMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
gradesMsgActive.dialogSingleVisible = dialogSingleVisible.value;
gradesMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
gradesMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addGrades = (requestMsg) => {
requestMsg.forEach((element) => {
element.identity_card = encrypt(String(element.identity_card));
element.user_name = encrypt(String(element.user_name));
element.exam_uid = encrypt(String(element.exam_uid));
element.status = encrypt(String(element.status));
element.grades = encrypt(String(element.grades));
element.pass_type = encrypt(String(element.pass_type));
});
const data = { requestMsg };
$http.post($url.addGrades, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增考生成绩成功!",
type: "success",
});
emit("reloadData");
} else if (res.data.code === 300) {
resetCheckForm();
ElMessage({
message: "新增部分考生成绩成功,请查看新增失败的数据以查找原因!",
type: "warning",
});
emit("reloadData");
} else {
ElMessage.error("新增考生成绩失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,117 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "成绩1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addGrades"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.status = singleAddRef.value[0].ruleForm.status;
item.ruleForm.pass_type = singleAddRef.value[0].ruleForm.pass_type;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `成绩${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addGrades", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,272 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="考生姓名" prop="user_name">
<el-autocomplete
:fetch-suggestions="searchUserName"
clearable
placeholder="请输入考生姓名"
minlength="1"
maxlength="6"
v-model="ruleForm.user_name"
@input="filter(ruleForm.user_name, 'user_name')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="身份证号" prop="identity_card">
<el-autocomplete
:fetch-suggestions="searchIdentityCard"
clearable
placeholder="请输入身份证号"
minlength="18"
maxlength="18"
v-model="ruleForm.identity_card"
@input="filter(ruleForm.identity_card, 'identity_card')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="exam_uid">
<el-autocomplete
:fetch-suggestions="searchExamUid"
clearable
placeholder="请输入考试uid"
minlength="10"
maxlength="10"
v-model="ruleForm.exam_uid"
@input="filter(ruleForm.exam_uid, 'exam_uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="参与状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择参与状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考生成绩" prop="grades">
<el-input
clearable
placeholder="请输入考生成绩"
minlength="1"
maxlength="5"
v-model="ruleForm.grades"
@input="filter(ruleForm.grades, 'grades')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试结果" prop="pass_type">
<el-select
clearable
size="small"
placeholder="请选择考试结果"
autocomplete="off"
v-model="ruleForm.pass_type"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.passTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
const ruleFormRef = ref(null);
const ruleForm = reactive({
identity_card: "",
user_name: "",
exam_uid: "",
status: "",
grades: "",
pass_type: "",
});
const formClone = ref({});
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 searchUserName = (value, cb) => {
const data = {
user_name: value,
};
$http.post($url.searchUserName, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateUserName = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考生姓名"));
} else {
const data = {
user_name: value,
};
$http.post($url.findUserName, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考生不存在!"));
}
});
}
};
const searchIdentityCard = (value, cb) => {
const data = {
identity_card: value,
};
$http.post($url.searchIdentityCard, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateIdentityCard = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入身份证号"));
} else if (!identityCardRegex.test(value)) {
callback(new Error("身份证格式不正确,请重新输入!"));
} else if (ruleForm.user_name == "") {
callback(new Error("请输入考生姓名"));
} else {
const data = {
identity_card: value,
user_name: ruleForm.user_name,
};
$http.post($url.findIdentityCard, data).then((res) => {
if (res.data.code === 200) {
callback();
} else if (res.data.code === 303) {
callback(new Error("考生身份证号码与考生姓名不匹配!"));
} else {
callback(new Error("该考生身份证号未注册!"));
}
});
}
};
const searchExamUid = (value, cb) => {
const data = {
exam_uid: value,
};
$http.post($url.searchExamUid, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateExamUid = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考试uid"));
} else {
const data = {
exam_uid: value,
};
$http.post($url.findExamUid, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考试不存在!"));
}
});
}
};
const rules = reactive({
identity_card: [{ required: true, validator: validateIdentityCard, trigger: "blur" }],
user_name: [{ required: true, validator: validateUserName, trigger: "blur" }],
exam_uid: [{ required: true, validator: validateExamUid, trigger: "blur" }],
status: [{ required: true, message: "请选择参与状态", trigger: "change" }],
grades: [{ required: true, message: "请输入考生成绩", trigger: "blur" }],
pass_type: [{ required: true, message: "请选择考试结果", trigger: "change" }],
});
const emit = defineEmits(["addGrades"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addGrades", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,101 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
phone: {
value: "",
label: "电话号码",
},
type: {
value: "",
label: "白名单类型",
},
expired_at: {
value: "",
label: "白名单过期时间",
},
};
const relatedOptions = [
{
value: "0",
label: "电话号码",
disabled: false,
},
{
value: "1",
label: "白名单类型",
disabled: false,
},
{
value: "2",
label: "白名单过期时间",
disabled: false,
},
];
const emit = defineEmits(["addTopicWhitelist"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
phone: item[relatedRules.phone.value],
type: item[relatedRules.type.value],
});
}
});
});
});
emit("addTopicWhitelist", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "白名单1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addTopicWhitelist"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.type = singleAddRef.value[0].ruleForm.typw;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `白名单${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addTopicWhitelist", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,123 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="电话号码" prop="phone">
<el-input
clearable
placeholder="请输入电话号码"
minlength="11"
maxlength="11"
v-model="ruleForm.phone"
@input="filter(ruleForm.phone, 'phone')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="白名单类型" prop="type">
<el-select
clearable
size="small"
placeholder="请选择白名单类型"
autocomplete="off"
v-model="ruleForm.type"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item label="白名单过期时间">
<el-col :span="11">
<el-form-item prop="expired_at">
<el-date-picker
v-model="ruleForm.expired_at"
type="datetime"
placeholder="请选择白名单过期时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
phone: "",
type: "",
expired_at: "",
});
const formClone = ref({});
const rules = reactive({
phone: [{ required: true, message: "请输入手机号码", trigger: "blur" }],
type: [{ required: true, message: "请选择白名单类型", trigger: "change" }],
});
const emit = defineEmits(["addTopicWhitelist"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addTopicWhitelist", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,168 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="topicWhitelistActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addTopicWhitelist="addTopicWhitelist"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addTopicWhitelist="addTopicWhitelist"
/>
<ExcleAdd
ref="excleAddRef"
v-if="dialogExcleVisible"
@addTopicWhitelist="addTopicWhitelist"
/>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import topicWhitelistActiveStore from "../../../stores/topicWhitelist";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes";
const topicWhitelistActive = topicWhitelistActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
topicWhitelistActive.dialogSingleVisible = dialogSingleVisible.value;
topicWhitelistActive.dialogMultipleVisible = dialogMultipleVisible.value;
topicWhitelistActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
topicWhitelistActive.dialogSingleVisible = dialogSingleVisible.value;
topicWhitelistActive.dialogMultipleVisible = dialogMultipleVisible.value;
topicWhitelistActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
topicWhitelistActive.dialogSingleVisible = dialogSingleVisible.value;
topicWhitelistActive.dialogMultipleVisible = dialogMultipleVisible.value;
topicWhitelistActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addTopicWhitelist = (requestMsg) => {
requestMsg.forEach((element) => {
element.phone = encrypt(String(element.phone));
element.type = encrypt(String(element.type));
element.expired_at = encrypt(String(element.expired_at));
});
const data = { requestMsg };
$http.post($url.addTopicWhitelist, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增题库白名单成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增题库白名单失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,102 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
stem: {
value: "",
label: "题干",
},
answer: {
value: "",
label: "答案",
},
difficulty: {
value: "",
label: "难度",
},
};
const relatedOptions = [
{
value: "0",
label: "题干",
disabled: false,
},
{
value: "1",
label: "答案",
disabled: false,
},
{
value: "2",
label: "难度",
disabled: false,
},
];
const emit = defineEmits(["addInterlocution"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
stem: item[relatedRules.stem.value],
answer: item[relatedRules.answer.value],
difficulty: item[relatedRules.difficulty.value],
});
}
});
});
});
emit("addInterlocution", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,161 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="interlocutionMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addInterlocution="addInterlocution"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addInterlocution="addInterlocution"
/>
<ExcleAdd
ref="excleAddRef"
v-if="dialogExcleVisible"
@addInterlocution="addInterlocution"
/>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import interlocutionActiveStore from "../../../../stores/trainindTopic/interlocution.js";
import $http from "../../../../api/index";
import $url from "../../../../api/url";
const interlocutionMsgActive = interlocutionActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
interlocutionMsgActive.dialogSingleVisible = dialogSingleVisible.value;
interlocutionMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
interlocutionMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
interlocutionMsgActive.dialogSingleVisible = dialogSingleVisible.value;
interlocutionMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
interlocutionMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
interlocutionMsgActive.dialogSingleVisible = dialogSingleVisible.value;
interlocutionMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
interlocutionMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addInterlocution = (requestMsg) => {
const data = { requestMsg };
$http.post($url.addInterlocution, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增问答题成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增问答题失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "问答题1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addInterlocution"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.role = singleAddRef.value[0].ruleForm.role;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `问答题${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addInterlocution", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,118 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="题干" prop="stem">
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="答案" prop="answer">
<el-input
clearable
placeholder="请输入答案"
v-model="ruleForm.answer"
@input="filter(ruleForm.answer, 'answer')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
stem: "",
answer: "",
difficulty: "",
});
const formClone = ref({});
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
answer: [{ required: true, message: "请输入答案", trigger: "blur" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const emit = defineEmits(["addInterlocution"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addInterlocution", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,102 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
stem: {
value: "",
label: "题干",
},
answer: {
value: "",
label: "答案",
},
difficulty: {
value: "",
label: "难度",
},
};
const relatedOptions = [
{
value: "0",
label: "题干",
disabled: false,
},
{
value: "1",
label: "答案",
disabled: false,
},
{
value: "2",
label: "难度",
disabled: false,
},
];
const emit = defineEmits(["addJudgment"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
stem: item[relatedRules.stem.value],
answer: item[relatedRules.answer.value],
difficulty: item[relatedRules.difficulty.value],
});
}
});
});
});
emit("addJudgment", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,157 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="judgmentMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addJudgment="addJudgment"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addJudgment="addJudgment"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addJudgment="addJudgment" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import judgmentActiveStore from "../../../../stores/trainindTopic/judgment.js";
import $http from "../../../../api/index";
import $url from "../../../../api/url";
const judgmentMsgActive = judgmentActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
judgmentMsgActive.dialogSingleVisible = dialogSingleVisible.value;
judgmentMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
judgmentMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
judgmentMsgActive.dialogSingleVisible = dialogSingleVisible.value;
judgmentMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
judgmentMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
judgmentMsgActive.dialogSingleVisible = dialogSingleVisible.value;
judgmentMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
judgmentMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addJudgment = (requestMsg) => {
const data = { requestMsg };
$http.post($url.addJudgment, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增判断题成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增判断题失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "判断题1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addJudgment"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.role = singleAddRef.value[0].ruleForm.role;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `判断题${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addJudgment", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,129 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="题干" prop="stem">
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="答案" prop="answer">
<el-select
clearable
size="small"
placeholder="请选择答案"
autocomplete="off"
v-model="ruleForm.answer"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.answerOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
stem: "",
answer: "",
difficulty: "",
});
const formClone = ref({});
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
answer: [{ required: true, message: "请选择答案", trigger: "change" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const emit = defineEmits(["addJudgment"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addJudgment", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,152 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
stem: {
value: "",
label: "题干",
},
options_a: {
value: "",
label: "选项A",
},
options_b: {
value: "",
label: "选项B",
},
options_c: {
value: "",
label: "选项C",
},
options_d: {
value: "",
label: "选项D",
},
options_e: {
value: "",
label: "选项E",
},
answer: {
value: "",
label: "答案",
},
difficulty: {
value: "",
label: "难度",
},
};
const relatedOptions = [
{
value: "0",
label: "题干",
disabled: false,
},
{
value: "1",
label: "选项A",
disabled: false,
},
{
value: "2",
label: "选项B",
disabled: false,
},
{
value: "3",
label: "选项C",
disabled: false,
},
{
value: "4",
label: "选项D",
disabled: false,
},
{
value: "5",
label: "选项E",
disabled: false,
},
{
value: "6",
label: "答案",
disabled: false,
},
{
value: "7",
label: "难度",
disabled: false,
},
];
const emit = defineEmits(["addMultiple"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
stem: item[relatedRules.stem.value],
options_a: item[relatedRules.options_a.value],
options_b: item[relatedRules.options_b.value],
options_c: item[relatedRules.options_c.value],
options_d: item[relatedRules.options_d.value],
options_e: item[relatedRules.options_e.value],
answer: item[relatedRules.answer.value],
difficulty: item[relatedRules.difficulty.value],
});
}
});
});
});
emit("addMultiple", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "多选题1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addMultiple"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.role = singleAddRef.value[0].ruleForm.role;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `多选题${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addMultiple", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,157 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="multipleMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addMultiple="addMultiple"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addMultiple="addMultiple"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addMultiple="addMultiple" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import multipleActiveStore from "../../../../stores/trainindTopic/multiple.js";
import $http from "../../../../api/index";
import $url from "../../../../api/url";
const multipleMsgActive = multipleActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
multipleMsgActive.dialogSingleVisible = dialogSingleVisible.value;
multipleMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
multipleMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
multipleMsgActive.dialogSingleVisible = dialogSingleVisible.value;
multipleMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
multipleMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
multipleMsgActive.dialogSingleVisible = dialogSingleVisible.value;
multipleMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
multipleMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addMultiple = (requestMsg) => {
const data = { requestMsg };
$http.post($url.addMultiple, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增多选题成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增多选题失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,168 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="题干" prop="stem">
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项A" prop="options_a">
<el-input
clearable
placeholder="请输入选项A"
v-model="ruleForm.options_a"
@input="filter(ruleForm.options_a, 'options_a')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项B" prop="options_b">
<el-input
clearable
placeholder="请输入选项B"
v-model="ruleForm.options_b"
@input="filter(ruleForm.options_b, 'options_b')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项C" prop="options_c">
<el-input
clearable
placeholder="请输入选项C"
v-model="ruleForm.options_c"
@input="filter(ruleForm.options_c, 'options_c')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项D" prop="options_d">
<el-input
clearable
placeholder="请输入选项D"
v-model="ruleForm.options_d"
@input="filter(ruleForm.options_d, 'options_d')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项E" prop="options_e">
<el-input
clearable
placeholder="请输入选项E"
v-model="ruleForm.options_e"
@input="filter(ruleForm.options_e, 'options_e')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="答案" prop="answer">
<el-input
clearable
placeholder="请输入答案"
v-model="ruleForm.answer"
@input="filter(ruleForm.answer, 'answer')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
stem: "",
options_a: "",
options_b: "",
options_c: "",
options_d: "",
options_e: "",
answer: "",
difficulty: "",
});
const formClone = ref({});
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
options_a: [{ required: true, message: "请输入选项A", trigger: "blur" }],
options_b: [{ required: true, message: "请输入选项B", trigger: "blur" }],
options_c: [{ required: true, message: "请输入选项C", trigger: "blur" }],
options_d: [{ required: true, message: "请输入选项D", trigger: "blur" }],
options_e: [{ required: true, message: "请输入选项E", trigger: "blur" }],
answer: [{ required: true, message: "请输入答案", trigger: "blur" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const emit = defineEmits(["addMultiple"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addMultiple", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,142 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
stem: {
value: "",
label: "题干",
},
options_a: {
value: "",
label: "选项A",
},
options_b: {
value: "",
label: "选项B",
},
options_c: {
value: "",
label: "选项C",
},
options_d: {
value: "",
label: "选项D",
},
answer: {
value: "",
label: "答案",
},
difficulty: {
value: "",
label: "难度",
},
};
const relatedOptions = [
{
value: "0",
label: "题干",
disabled: false,
},
{
value: "1",
label: "选项A",
disabled: false,
},
{
value: "2",
label: "选项B",
disabled: false,
},
{
value: "3",
label: "选项C",
disabled: false,
},
{
value: "4",
label: "选项D",
disabled: false,
},
{
value: "5",
label: "答案",
disabled: false,
},
{
value: "6",
label: "难度",
disabled: false,
},
];
const emit = defineEmits(["addRadio"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
stem: item[relatedRules.stem.value],
options_a: item[relatedRules.options_a.value],
options_b: item[relatedRules.options_b.value],
options_c: item[relatedRules.options_c.value],
options_d: item[relatedRules.options_d.value],
answer: item[relatedRules.answer.value],
difficulty: item[relatedRules.difficulty.value],
});
}
});
});
});
emit("addRadio", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,116 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "单选题1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addRadio"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {
item.ruleForm.role = singleAddRef.value[0].ruleForm.role;
});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `单选题${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addRadio", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,157 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="radioMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addRadio="addRadio"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="true"
:addData="addData"
@addRadio="addRadio"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addRadio="addRadio" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import radioActiveStore from "../../../../stores/trainindTopic/radio.js";
import $http from "../../../../api/index";
import $url from "../../../../api/url";
const radioMsgActive = radioActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
radioMsgActive.dialogSingleVisible = dialogSingleVisible.value;
radioMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
radioMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
radioMsgActive.dialogSingleVisible = dialogSingleVisible.value;
radioMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
radioMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
radioMsgActive.dialogSingleVisible = dialogSingleVisible.value;
radioMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
radioMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addRadio = (requestMsg) => {
const data = { requestMsg };
$http.post($url.addRadio, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增单选题成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增单选题失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,158 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="题干" prop="stem">
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项A" prop="options_a">
<el-input
clearable
placeholder="请输入选项A"
v-model="ruleForm.options_a"
@input="filter(ruleForm.options_a, 'options_a')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项B" prop="options_b">
<el-input
clearable
placeholder="请输入选项B"
v-model="ruleForm.options_b"
@input="filter(ruleForm.options_b, 'options_b')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项C" prop="options_c">
<el-input
clearable
placeholder="请输入选项C"
v-model="ruleForm.options_c"
@input="filter(ruleForm.options_c, 'options_c')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="选项D" prop="options_d">
<el-input
clearable
placeholder="请输入选项D"
v-model="ruleForm.options_d"
@input="filter(ruleForm.options_d, 'options_d')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="答案" prop="answer">
<el-input
clearable
placeholder="请输入答案"
v-model="ruleForm.answer"
@input="filter(ruleForm.answer, 'answer')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in addData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
const ruleFormRef = ref(null);
const ruleForm = reactive({
stem: "",
options_a: "",
options_b: "",
options_c: "",
options_d: "",
answer: "",
difficulty: "",
});
const formClone = ref({});
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
options_a: [{ required: true, message: "请输入选项A", trigger: "blur" }],
options_b: [{ required: true, message: "请输入选项B", trigger: "blur" }],
options_c: [{ required: true, message: "请输入选项C", trigger: "blur" }],
options_d: [{ required: true, message: "请输入选项D", trigger: "blur" }],
answer: [{ required: true, message: "请输入答案", trigger: "blur" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const emit = defineEmits(["addRadio"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addRadio", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,132 @@
<template>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";
import UploadFile from "../../operate/uploadFile.vue";
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
phone: {
value: "",
label: "手机号码",
},
user_name: {
value: "",
label: "真实姓名",
},
identity_card: {
value: "",
label: "身份证号",
},
workplace: {
value: "",
label: "工作单位",
},
email: {
value: "",
label: "电子邮箱",
},
address: {
value: "",
label: "通讯地址",
},
};
const relatedOptions = [
{
value: "0",
label: "手机号码",
disabled: false,
},
{
value: "1",
label: "真实姓名",
disabled: false,
},
{
value: "2",
label: "身份证号",
disabled: false,
},
{
value: "3",
label: "工作单位",
disabled: false,
},
{
value: "4",
label: "电子邮箱",
disabled: false,
},
{
value: "5",
label: "通讯地址",
disabled: false,
},
];
const emit = defineEmits(["addUser"]);
const resetCheckForm = () => {
if (!uploadFileRef) return;
uploadFileRef.value.handleExceed();
};
const confirm = async () => {
if (!uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
user_name: item[relatedRules.user_name.value],
identity_card: item[relatedRules.identity_card.value],
workplace: item[relatedRules.workplace.value],
address: item[relatedRules.address.value],
phone: item[relatedRules.phone.value],
email: item[relatedRules.email.value],
});
}
});
});
});
emit("addUser", formClone.value);
}, 3000);
};
defineExpose({ resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,114 @@
<template>
<div>
<div
style="font-size: 2rem"
class="flex flex-warp justify-center items-center font-bold"
>
<div style="font-size: 1rem">请输入要导入的数据数量:</div>
<el-input-number v-model="singleFormNum" :min="1" :max="10" @change="createForm" />
<div v-if="checkNeed">&nbsp;&nbsp;</div>
<el-checkbox
v-if="checkNeed"
v-model="checked"
label="为所有表单进行相同操作"
size="large"
/>
</div>
<el-scrollbar max-height="40rem">
<div class="mt-2 border-2 border-gray-300" v-for="item in input">
<div style="font-size: 1.2rem" class="w-full text-center font-bold">
{{ item.label }}
</div>
<SingleAdd ref="singleAddRef" :addData="addData" />
</div>
</el-scrollbar>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import SingleAdd from "./singleAdd.vue";
const singleFormNum = ref(1);
const checked = ref(true);
const input = ref([{ label: "用户1" }]);
const singleAddRef = ref(null);
const formClone = ref([]);
const props = defineProps({
checkNeed: Boolean,
addData: Object,
});
const emit = defineEmits(["addUser"]);
const initData = () => {
setTimeout(() => {
singleAddRef.value.forEach((item) => {});
}, 100);
};
const createForm = () => {
let difference = Math.abs(singleFormNum.value - input.value.length);
for (let i = 0; i < difference; i++) {
if (singleFormNum.value > input.value.length) {
input.value.push({ label: `用户${input.value.length + 1}` });
} else if (singleFormNum.value < input.value.length) {
input.value.pop();
}
}
if (checked.value) {
initData();
}
};
const resetCheckForm = () => {
if (!singleAddRef) return;
singleAddRef.value.forEach((element) => {
element.resetCheckForm();
});
singleFormNum.value = 1;
createForm();
};
const confirm = () => {
if (!singleAddRef) return;
let valid = true;
formClone.value = [];
singleAddRef.value.forEach((element) => {
element.validate().then((result) => {
if (result) {
formClone.value.push({ ...element.ruleForm });
} else {
valid = result;
}
});
});
setTimeout(() => {
if (valid) {
emit("addUser", formClone.value);
singleAddRef.value.forEach((item) => {
item.resetCheckForm();
});
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
}, 100);
};
defineExpose({ resetCheckForm, confirm, formClone });
watch(checked, (value) => {
if (value) {
initData();
}
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,224 @@
<template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="姓名" prop="user_name">
<el-input
clearable
placeholder="请输入姓名"
minlength="2"
maxlength="10"
v-model="ruleForm.user_name"
@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
placeholder="请输入身份证号"
minlength="18"
maxlength="18"
v-model="ruleForm.identity_card"
@input="filter(ruleForm.identity_card, 'identity_card')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="工作单位" prop="workplace">
<el-input
clearable
placeholder="请输入工作单位"
maxlength="60"
v-model="ruleForm.workplace"
@input="filter(ruleForm.workplace, 'workplace')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="通讯地址" prop="address">
<el-input
clearable
placeholder="请输入通讯地址"
maxlength="120"
v-model="ruleForm.address"
@input="filter(ruleForm.address, 'address')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="手机号码" prop="phone">
<el-input
clearable
placeholder="请输入手机号码"
minlength="11"
maxlength="11"
v-model="ruleForm.phone"
@input="filter(ruleForm.phone, 'phone')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="电子邮箱号码" prop="email">
<el-input
clearable
placeholder="请输入电子邮箱号码"
minlength="8"
maxlength="60"
v-model="ruleForm.email"
@input="filter(ruleForm.email, 'email')"
/>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from "vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
const ruleFormRef = ref(null);
const ruleForm = reactive({
user_name: "",
identity_card: "",
workplace: "",
address: "",
phone: "",
email: "",
});
const formClone = ref({});
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 validateIdentityCard = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入身份证号"));
} else if (!identityCardRegex.test(value)) {
callback(new Error("身份证格式不正确,请重新输入!"));
} else {
const data = {
identity_card: value,
type: "register",
data: "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 === "") {
callback(new Error("请输入手机号码"));
} else if (!phoneRegex.test(value)) {
callback(new Error("手机号码格式错误, 请重新输入!"));
} else {
const data = {
phone: value,
type: "register",
data: "null",
};
$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: value,
type: "register",
data: "null",
};
$http.post($url.diffEmail, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该电子邮箱号码已被注册"));
}
});
}
};
const rules = reactive({
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 emit = defineEmits(["addUser"]);
const filter = (value, label) => {
if (label == "phone") {
ruleForm.phone = value.replace(/\D/g, "");
} else {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
};
const props = defineProps({
addData: Object,
});
const resetCheckForm = () => {
if (!ruleFormRef) return;
ruleFormRef.value.resetFields();
};
const confirm = async () => {
if (!ruleFormRef) return;
await ruleFormRef.value.validate((valid) => {
if (valid) {
formClone.value = { ...ruleForm };
emit("addUser", [formClone.value]);
ruleFormRef.value.resetFields();
ElMessage({
message: `提交成功!`,
type: "success",
});
} else {
ElMessage.error("提交失败!");
}
});
};
const validate = () => {
if (!ruleFormRef) return Promise.resolve(false);
return new Promise((resolve) => {
ruleFormRef.value.validate((valid) => {
resolve(valid);
});
});
};
defineExpose({ resetCheckForm, confirm, validate, ruleForm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,170 @@
<template>
<el-dialog
v-model="dialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="resetCheckForm"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ title }}
</div>
<div
@click="resetCheckForm"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<FormTools @showTools="showTools" :dialogObj="userMsgActive" />
<SingleAdd
ref="singleAddRef"
v-if="dialogSingleVisible"
:addData="addData"
@addUser="addUser"
/>
<MultipleAdd
ref="multipleAddRef"
v-if="dialogMultipleVisible"
:checkNeed="false"
:addData="addData"
@addUser="addUser"
/>
<ExcleAdd ref="excleAddRef" v-if="dialogExcleVisible" @addUser="addUser" />
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm"> 取消 </el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import FormTools from "../../operate/formTools.vue";
import SingleAdd from "./singleAdd.vue";
import MultipleAdd from "./multipleAdd.vue";
import ExcleAdd from "./excleAdd.vue";
import userActiveStore from "../../../stores/userMsg";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes";
const userMsgActive = userActiveStore();
const dialogVisible = ref(false);
const singleAddRef = ref(null);
const multipleAddRef = ref(null);
const excleAddRef = ref(null);
const dialogSingleVisible = ref(true);
const dialogMultipleVisible = ref(false);
const dialogExcleVisible = ref(false);
const props = defineProps({
title: String,
addData: Object,
});
const emit = defineEmits(["reloadData"]);
const showTools = (operateType) => {
switch (operateType) {
case "single":
dialogSingleVisible.value = true;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = false;
userMsgActive.dialogSingleVisible = dialogSingleVisible.value;
userMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
userMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "multiple":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = true;
dialogExcleVisible.value = false;
userMsgActive.dialogSingleVisible = dialogSingleVisible.value;
userMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
userMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
case "excle":
dialogSingleVisible.value = false;
dialogMultipleVisible.value = false;
dialogExcleVisible.value = true;
userMsgActive.dialogSingleVisible = dialogSingleVisible.value;
userMsgActive.dialogMultipleVisible = dialogMultipleVisible.value;
userMsgActive.dialogExcleVisible = dialogExcleVisible.value;
break;
default:
break;
}
};
const resetCheckForm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.resetCheckForm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.resetCheckForm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.resetCheckForm();
}
dialogVisible.value = false;
};
const confirm = () => {
if (dialogSingleVisible.value) {
singleAddRef.value.confirm();
} else if (dialogMultipleVisible.value) {
multipleAddRef.value.confirm();
} else if (dialogExcleVisible.value) {
excleAddRef.value.confirm();
}
};
const addUser = (requestMsg) => {
requestMsg.forEach((element) => {
element.account_name = encrypt(String(element.account_name));
element.identity_card = encrypt(String(element.identity_card));
element.user_name = encrypt(String(element.user_name));
element.gender = encrypt(String(element.gender));
element.age = encrypt(String(element.age));
element.email = encrypt(String(element.email));
element.phone = encrypt(String(element.phone));
element.address = encrypt(String(element.address));
element.workplace = encrypt(String(element.workplace));
});
const data = { requestMsg };
$http.post($url.addUser, data).then((res) => {
if (res.data.code === 200) {
resetCheckForm();
ElMessage({
message: "新增用户信息成功!",
type: "success",
});
emit("reloadData");
} else {
ElMessage.error("新增用户信息失败!");
}
});
};
defineExpose({ dialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,204 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="账号名"
prop="account_name"
>
<el-input
clearable
placeholder="请输入账号名"
minlength="2"
maxlength="5"
v-model="ruleForm.account_name"
@input="filter(ruleForm.account_name, 'account_name')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="权限等级" prop="role">
<el-select
clearable
size="small"
placeholder="请选择权限等级"
autocomplete="off"
v-model="ruleForm.role"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="账号状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择账号状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
account_name: "",
role: "",
status: "",
});
const password = ref("");
const rules = reactive({
account_name: [{ required: true, message: "请输入账号名", trigger: "blur" }],
role: [{ required: true, message: "请选择权限等级", trigger: "change" }],
status: [{ required: true, message: "请选择账号状态", trigger: "change" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,238 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="exam_uid">
<el-autocomplete
:fetch-suggestions="searchExamUid"
clearable
placeholder="请输入考试uid"
minlength="10"
maxlength="10"
v-model="ruleForm.exam_uid"
@input="filter(ruleForm.exam_uid, 'exam_uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名来源" prop="source">
<el-input
clearable
placeholder="请输入报名来源"
maxlength="24"
v-model="ruleForm.source"
@input="filter(ruleForm.source, 'source')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择报名状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="缴费状态" prop="pay_status">
<el-select
clearable
size="small"
placeholder="请选择缴费状态"
autocomplete="off"
v-model="ruleForm.pay_status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.payStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名结果说明" prop="result">
<el-input
clearable
placeholder="请输入报名结果说明"
maxlength="24"
v-model="ruleForm.result"
@input="filter(ruleForm.result, 'result')"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../operate/passwordOauth.vue";
import $http from "../../api/index";
import $url from "../../api/url";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const password = ref("");
const ruleForm = reactive({
exam_uid: "",
source: "",
status: "",
pay_status: "",
result: "",
});
const searchExamUid = (value, cb) => {
const data = {
exam_uid: value,
};
$http.post($url.searchExamUid, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const rules = reactive({
exam_uid: [{ required: true, message: "请输入考试uid", trigger: "blur" }],
source: [{ required: true, message: "请输入报名来源", trigger: "blur" }],
status: [{ required: true, message: "请选择报名状态", trigger: "change" }],
pay_status: [{ required: true, message: "请选择缴费状态", trigger: "change" }],
result: [{ required: true, message: "请输入报名结果说明", trigger: "blur" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
createdAt: String,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,248 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="考试名称"
prop="exam_name"
>
<el-input
clearable
placeholder="请输入考试名称"
maxlength="60"
v-model="ruleForm.exam_name"
@input="filter(ruleForm.exam_name, 'exam_name')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择考试状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item label="报名起止时间">
<el-col :span="11">
<el-form-item prop="application_start">
<el-date-picker
v-model="ruleForm.application_start"
type="datetime"
placeholder="请选择报名开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-form-item prop="application_end">
<el-date-picker
v-model="ruleForm.application_end"
type="datetime"
placeholder="请选择报名结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="考试起止时间">
<el-col :span="11">
<el-form-item prop="exam_start">
<el-date-picker
v-model="ruleForm.exam_start"
type="datetime"
placeholder="请选择考试开始时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">
<span class="text-gray-500">-</span>
</el-col>
<el-col :span="11">
<el-form-item prop="exam_end">
<el-date-picker
v-model="ruleForm.exam_end"
type="datetime"
placeholder="请选择考试结束时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
exam_name: "",
status: "",
application_start: "",
application_end: "",
exam_start: "",
exam_end: "",
});
const password = ref("");
const rules = reactive({
exam_name: [{ required: true, message: "请输入考试名称", trigger: "blur" }],
status: [{ required: true, message: "请选择考试状态", trigger: "change" }],
application_start: [
{ type: "date", required: true, message: "请选择报名开始时间", trigger: "change" },
],
application_end: [
{ type: "date", required: true, message: "请选择报名结束时间", trigger: "change" },
],
exam_start: [
{ type: "date", required: true, message: "请选择考试开始时间", trigger: "change" },
],
exam_end: [
{ type: "date", required: true, message: "请选择考试结束时间", trigger: "change" },
],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,199 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="参与状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择参与状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考生成绩" prop="grades">
<el-input
clearable
placeholder="请输入考生成绩(没有则填“无”)"
minlength="1"
maxlength="5"
v-model="ruleForm.grades"
@input="filter(ruleForm.grades, 'grades')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试结果" prop="pass_type">
<el-select
clearable
size="small"
placeholder="请选择考试结果"
autocomplete="off"
v-model="ruleForm.pass_type"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.passTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
status: "",
grades: "",
pass_type: "",
});
const password = ref("");
const rules = reactive({
status: [{ required: true, message: "请选择参与状态", trigger: "change" }],
grades: [{ required: true, message: "请输入考生成绩", trigger: "blur" }],
pass_type: [{ required: true, message: "请选择考试结果", trigger: "change" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,171 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="权限等级" prop="type">
<el-select
clearable
size="small"
placeholder="请选择权限等级"
autocomplete="off"
v-model="ruleForm.type"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.typeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item label="白名单过期时间">
<el-col :span="11">
<el-form-item prop="expired_at">
<el-date-picker
v-model="ruleForm.expired_at"
type="datetime"
placeholder="请选择白名单过期时间"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
type: "",
expired_at: "",
});
const password = ref("");
const rules = reactive({
type: [{ required: true, message: "请选择白名单类型", trigger: "change" }],
});
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,196 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="题干"
prop="stem"
>
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="答案"
prop="answer"
>
<el-input
clearable
placeholder="请输入答案"
v-model="ruleForm.answer"
@input="filter(ruleForm.answer, 'answer')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
stem: "",
answer: "",
difficulty: "",
});
const password = ref("");
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
answer: [{ required: true, message: "请输入答案", trigger: "blur" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,202 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="题干"
prop="stem"
>
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="答案" prop="answer">
<el-select
clearable
size="small"
placeholder="请选择答案"
autocomplete="off"
v-model="ruleForm.answer"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.answerOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
stem: "",
answer: "",
difficulty: "",
});
const password = ref("");
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
answer: [{ required: true, message: "请选择答案", trigger: "change" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,271 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="题干"
prop="stem"
>
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项A"
prop="options_a"
>
<el-input
clearable
placeholder="请输入选项A"
v-model="ruleForm.options_a"
@input="filter(ruleForm.options_a, 'options_a')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项B"
prop="options_c"
>
<el-input
clearable
placeholder="请输入选项B"
v-model="ruleForm.options_c"
@input="filter(ruleForm.options_c, 'options_c')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项C"
prop="options_c"
>
<el-input
clearable
placeholder="请输入选项C"
v-model="ruleForm.options_c"
@input="filter(ruleForm.options_c, 'options_c')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项D"
prop="options_d"
>
<el-input
clearable
placeholder="请输入选项D"
v-model="ruleForm.options_d"
@input="filter(ruleForm.options_d, 'options_d')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项E"
prop="options_e"
>
<el-input
clearable
placeholder="请输入选项E"
v-model="ruleForm.options_e"
@input="filter(ruleForm.options_e, 'options_e')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="答案"
prop="answer"
>
<el-input
clearable
placeholder="请输入答案"
v-model="ruleForm.answer"
@input="filter(ruleForm.answer, 'answer')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
stem: "",
options_a: "",
options_b: "",
options_c: "",
options_d: "",
options_e: "",
answer: "",
difficulty: "",
});
const password = ref("");
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
options_a: [{ required: true, message: "请输入选项A", trigger: "blur" }],
options_b: [{ required: true, message: "请输入选项B", trigger: "blur" }],
options_c: [{ required: true, message: "请输入选项C", trigger: "blur" }],
options_d: [{ required: true, message: "请输入选项D", trigger: "blur" }],
options_e: [{ required: true, message: "请输入选项E", trigger: "blur" }],
answer: [{ required: true, message: "请输入答案", trigger: "blur" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,256 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="题干"
prop="stem"
>
<el-input
clearable
placeholder="请输入题干"
v-model="ruleForm.stem"
@input="filter(ruleForm.stem, 'stem')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项A"
prop="options_a"
>
<el-input
clearable
placeholder="请输入选项A"
v-model="ruleForm.options_a"
@input="filter(ruleForm.options_a, 'options_a')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项B"
prop="options_b"
>
<el-input
clearable
placeholder="请输入选项B"
v-model="ruleForm.options_b"
@input="filter(ruleForm.options_b, 'options_b')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项C"
prop="options_c"
>
<el-input
clearable
placeholder="请输入选项C"
v-model="ruleForm.options_c"
@input="filter(ruleForm.options_c, 'options_c')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="选项D"
prop="options_d"
>
<el-input
clearable
placeholder="请输入选项D"
v-model="ruleForm.options_d"
@input="filter(ruleForm.options_d, 'options_d')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="答案"
prop="answer"
>
<el-input
clearable
placeholder="请输入答案"
v-model="ruleForm.answer"
@input="filter(ruleForm.answer, 'answer')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="难度" prop="difficulty">
<el-select
clearable
size="small"
placeholder="请选择难度"
autocomplete="off"
v-model="ruleForm.difficulty"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.difficultyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import PasswordOauth from "../../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
stem: "",
options_a: "",
options_b: "",
options_c: "",
options_d: "",
answer: "",
difficulty: "",
});
const password = ref("");
const rules = reactive({
stem: [{ required: true, message: "请输入题干", trigger: "blur" }],
options_a: [{ required: true, message: "请输入选项A", trigger: "blur" }],
options_b: [{ required: true, message: "请输入选项B", trigger: "blur" }],
options_c: [{ required: true, message: "请输入选项C", trigger: "blur" }],
options_d: [{ required: true, message: "请输入选项D", trigger: "blur" }],
answer: [{ required: true, message: "请输入答案", trigger: "blur" }],
difficulty: [{ required: true, message: "请选择难度", trigger: "change" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,373 @@
<template>
<div>
<el-dialog
v-model="operateDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="姓名"
prop="user_name"
>
<el-input
clearable
placeholder="请输入姓名"
minlength="2"
maxlength="10"
v-model="ruleForm.user_name"
@input="filter(ruleForm.user_name, 'user_name')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="身份证号"
prop="identity_card"
>
<el-input
clearable
placeholder="请输入身份证号"
minlength="18"
maxlength="18"
v-model="ruleForm.identity_card"
@input="filter(ruleForm.identity_card, 'identity_card')"
/>
</el-form-item>
<el-form-item
class="mx-1 font-bold"
label="身份证二要素核验状态"
prop="real_check"
>
<el-select
clearable
size="small"
placeholder="请选择身份证二要素核验状态"
autocomplete="off"
v-model="ruleForm.real_check"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.realCheckOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="工作单位"
prop="workplace"
>
<el-input
clearable
placeholder="请输入工作单位"
maxlength="60"
v-model="ruleForm.workplace"
@input="filter(ruleForm.workplace, 'workplace')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="通讯地址"
prop="address"
>
<el-input
clearable
placeholder="请输入通讯地址"
maxlength="120"
v-model="ruleForm.address"
@input="filter(ruleForm.address, 'address')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="手机号码"
prop="phone"
>
<el-input
clearable
placeholder="请输入手机号码"
minlength="11"
maxlength="11"
v-model="ruleForm.phone"
@input="filter(ruleForm.phone, 'phone')"
/>
</el-form-item>
<el-form-item
v-if="model == 'single' ? true : false"
class="mx-1 font-bold"
label="电子邮箱号码"
prop="email"
>
<el-input
clearable
placeholder="请输入电子邮箱号码"
minlength="8"
maxlength="60"
v-model="ruleForm.email"
@input="filter(ruleForm.email, 'email')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="账号状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择账号状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in editData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button
style="font-weight: bold"
type="primary"
@click="confirm(ruleFormRef)"
>
确定
</el-button>
</div>
</template>
</el-dialog>
<PasswordOauth
ref="editPasswordOauthRef"
:operateLabel="operateLabel"
@requestOperate="requestOperate"
/>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import $http from "../../api/index";
import $url from "../../api/url";
import PasswordOauth from "../operate/passwordOauth.vue";
const operateDialogVisible = ref(false);
const model = ref("single");
const ruleFormRef = ref(null);
const editPasswordOauthRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
uid: "",
user_name: "",
identity_card: "",
real_check: "",
workplace: "",
address: "",
phone: "",
email: "",
status: "",
});
const password = ref("");
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 validateIdentityCard = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入身份证号"));
} else if (!identityCardRegex.test(value)) {
callback(new Error("身份证格式不正确,请重新输入!"));
} else {
const data = {
identity_card: value,
type: "edit",
data: ruleForm.uid,
};
$http.post($url.diffIdentityCard, 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: value,
type: "edit",
data: ruleForm.uid,
};
$http.post($url.diffEmail, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该电子邮箱号码已被注册"));
}
});
}
};
const validatePhone = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入手机号码"));
} else if (!phoneRegex.test(value)) {
callback(new Error("手机号码格式错误, 请重新输入!"));
} else {
const data = {
phone: value,
type: "edit",
data: ruleForm.uid,
};
$http.post($url.diffPhone, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该手机号码已被注册"));
}
});
}
};
const rules = reactive({
user_name: [{ required: true, message: "请输入姓名", trigger: "blur" }],
identity_card: [{ required: true, validator: validateIdentityCard, trigger: "blur" }],
real_check: [
{ required: true, message: "请选择身份证二要素核验状态", trigger: "change" },
],
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"] },
],
status: [{ required: true, message: "请选择账号状态", trigger: "change" }],
});
const filter = (value, label) => {
if (label == "phone") {
ruleForm.phone = value.replace(/\D/g, "");
} else if (label == "age") {
ruleForm.age = value.replace(/\D/g, "");
} else {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
};
const props = defineProps({
operateLabel: String,
editData: Object,
});
const emit = defineEmits(["requestOperate"]);
const resetForm = () => {
for (let key in ruleForm) {
if (ruleForm.hasOwnProperty(key)) {
ruleForm[key] = "";
}
}
};
const handleClose = () => {
operateDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
resetForm();
};
const resetCheckForm = (formEl) => {
operateDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
resetForm();
};
const requestOperate = () => {
password.value = editPasswordOauthRef.value.ruleForm.password;
emit("requestOperate");
operateDialogVisible.value = false;
formElClone.value.resetFields();
resetForm();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
editPasswordOauthRef.value.operateDialogVisible = true;
formElClone.value = formEl;
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ operateDialogVisible, model, ruleForm, password });
</script>
<style scoped></style>

View File

@@ -0,0 +1,777 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="adminMsgActive" />
<AddNewData newDataLabel="新增普通管理员" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="40rem"
max-height="40rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="uid">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUidKeyRef"
holderText="输入普通管理员uid以搜索相关数据"
@search="adminMsg"
/>
<div>
<el-text class="mx-1"> 普通管理员uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.uid }}
</div>
</template>
</el-table-column>
<el-table-column min-width="160" column-key="account_name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchAccountNameKeyRef"
holderText="输入账号名称以搜索相关数据"
@search="adminMsg"
/>
<div>
<el-text class="mx-1"> 账号名称 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.account_name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="role">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectRoleSearchRef"
holderText="选择权限等级以搜索相关数据"
:options="roleOptions"
@search="adminMsg"
/>
<div>
<el-text class="mx-1"> 权限等级 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(roleOptions, scope.row.role)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(roleOptions, scope.row.role) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="status">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectStatusSearchRef"
holderText="选择账号状态以搜索相关数据"
:options="statusOptions"
@search="adminMsg"
/>
<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"
>
{{ labelShunt(statusOptions, scope.row.status) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="created_at">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="createdDateTimePickerRef"
DateTimePicker="createdDateTimePicker"
@search="adminMsg"
/>
<div>
<el-text class="mx-1"> 注册时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="createdSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="adminMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.created_at }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="updated_at">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="updatedDateTimePickerRef"
DateTimePicker="updatedMsgDateTimePicker"
@search="adminMsg"
/>
<div>
<el-text class="mx-1"> 绑定状态变更时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="updatedSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="adminMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.updated_at }}
</div>
</template>
</el-table-column>
<el-table-column min-width="80" column-key="login_status">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectLoginStatusSearchRef"
holderText="选择登录状态以搜索相关数据"
:options="loginStatusOptions"
@search="adminMsg"
/>
<div>
<el-text class="mx-1"> 登录状态 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(loginStatusOptions, scope.row.login_status)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(loginStatusOptions, scope.row.login_status) }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="adminMsgActive"
@sizeChangeBack="changePage"
@search="adminMsg"
/>
</div>
</el-card>
<AddAdminForm
ref="addAdminFormRef"
title="新增普通管理员"
:addData="addData"
:createdAt="null"
@reloadData="adminMsg"
/>
<EditAdminForm
ref="editAdminFormRef"
operateLabel="编辑普通管理员"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除普通管理员"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出管理员数据"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../operate/tabelTools.vue";
import AddNewData from "../operate/addNewData.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 AddAdminForm from "../addNewData/adminForm/adminForm.vue";
import EditAdminForm from "../editData/adminForm.vue";
import PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import adminMsgActiveStore from "../../stores/adminMsg";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes.js";
import { downloadData } from "../../utils/downloadData.js";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const adminMsgActive = adminMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑普通管理员",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除普通管理员",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchUidKeyRef = ref(null);
const SearchAccountNameKeyRef = ref(null);
const SelectRoleSearchRef = ref(null);
const SelectStatusSearchRef = ref(null);
const SelectLoginStatusSearchRef = ref(null);
const createdDateTimePickerRef = ref(null);
const updatedDateTimePickerRef = ref(null);
const createdSortDateTimeRef = ref(null);
const updatedSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const addAdminFormRef = ref(null);
const editAdminFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
uid: "",
account_name: "",
role: "",
status: "",
created_at: "",
updated_at: "",
login_status: "",
});
const sortOptions = reactive({
created_at: false,
updated_at: false,
});
const tableData = ref([]);
const roleOptions = [
{
value: "9",
label: "系统管理员",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
{
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(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
];
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 loginStatusOptions = [
{
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;",
},
];
let singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
roleOptions,
statusOptions,
loginStatusOptions,
});
const editData = reactive({
roleOptions,
statusOptions,
loginStatusOptions,
});
const showAddForm = () => {
addAdminFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
adminMsgActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
adminMsgActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
adminMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const adminMsg = () => {
judgment(searchOptions, "uid", SearchUidKeyRef.value, "searchMsg");
judgment(searchOptions, "account_name", SearchAccountNameKeyRef.value, "searchMsg");
judgment(searchOptions, "role", SelectRoleSearchRef.value, "searchMsg");
judgment(searchOptions, "status", SelectStatusSearchRef.value, "searchMsg");
judgment(searchOptions, "created_at", createdDateTimePickerRef.value, "searchMsg");
judgment(searchOptions, "updated_at", updatedDateTimePickerRef.value, "searchMsg");
judgment(searchOptions, "login_status", SelectLoginStatusSearchRef.value, "searchMsg");
judgment(sortOptions, "created_at", createdSortDateTimeRef.value, "searchMsg");
judgment(sortOptions, "updated_at", updatedSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showAdmin, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.adminMsg;
tableData.value.forEach((item) => {
item.origin_createdAt = item.created_at;
item.created_at = moment
.utc(item.created_at)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
item.origin_updatedAt = item.updated_at;
item.updated_at = moment
.utc(item.updated_at)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
});
PaginationRef.value.dataCount = res.data.count;
adminMsgActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
adminMsgActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.created_at == element.created_at) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.created_at == element.created_at) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_createdAt) {
item.created_at = item.origin_createdAt;
}
if (item.origin_updatedAt) {
item.created_at = item.origin_updatedAt;
}
delete item.origin_createdAt;
delete item.origin_updatedAt;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.created_at = singleData.origin_createdAt;
singleData.updated_at = singleData.origin_updatedAt;
delete singleData.origin_createdAt;
delete singleData.origin_updatedAt;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editAdminFormRef.value.model = EditSelectRef.value.model;
editAdminFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
editAdminFormRef.value.ruleForm.account_name = singleData.account_name;
roleOptions.forEach((item) => {
if (item.value == singleData.role) {
editAdminFormRef.value.ruleForm.role = item.label;
}
});
statusOptions.forEach((item) => {
if (item.value == singleData.status) {
editAdminFormRef.value.ruleForm.status = item.label;
}
});
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editAdmin;
model = EditSelectRef.value.model;
password = editAdminFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteAdmin;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
data.updateMsg = { ...editAdminFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
adminMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
roleOptions.forEach((element) => {
if (element.value == item["role"]) {
item["role"] = element.label;
}
});
statusOptions.forEach((element) => {
if (element.value == item["status"]) {
item["status"] = element.label;
}
});
delete item.login_status;
delete item.origin_createdAt;
delete item.origin_updatedAt;
});
const dataHeaders = [
"账号uid",
"账号名称",
"权限等级",
"账号状态",
"注册时间",
"更新时间",
];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadAdminData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ adminMsg });
watch(searchOptions, (value) => {
adminMsgActive.searchOptions = value;
});
watch(sortOptions, (value) => {
adminMsgActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = adminMsgActive.pageCount;
dialogSearchVisible.value = adminMsgActive.dialogSearchVisible;
dialogSortVisible.value = adminMsgActive.dialogSortVisible;
dialogOperationVisible.value = adminMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
adminMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.uid = adminMsgActive.searchOptions.uid;
searchOptions.account_name = adminMsgActive.searchOptions.account_name;
searchOptions.role = adminMsgActive.searchOptions.role;
searchOptions.status = adminMsgActive.searchOptions.status;
searchOptions.created_at = adminMsgActive.searchOptions.created_at;
searchOptions.updated_at = adminMsgActive.searchOptions.updated_at;
searchOptions.login_status = adminMsgActive.searchOptions.login_status;
}
if (dialogSortVisible.value) {
sortOptions.created_at = adminMsgActive.sortOptions.created_at;
sortOptions.updated_at = adminMsgActive.sortOptions.updated_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
adminMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchUidKeyRef.value.searchMsg = adminMsgActive.searchOptions.uid;
SearchAccountNameKeyRef.value.searchMsg = adminMsgActive.searchOptions.account_name;
SelectRoleSearchRef.value.searchMsg = adminMsgActive.searchOptions.role;
SelectStatusSearchRef.value.searchMsg = adminMsgActive.searchOptions.status;
createdDateTimePickerRef.value.created_at = adminMsgActive.searchOptions.created_at;
updatedDateTimePickerRef.value.updated_at = adminMsgActive.searchOptions.updated_at;
SelectLoginStatusSearchRef.value.searchMsg =
adminMsgActive.searchOptions.login_status;
}
if (dialogSortVisible.value) {
createdSortDateTimeRef.value.searchMsg = adminMsgActive.sortOptions.created_at;
updatedSortDateTimeRef.value.searchMsg = adminMsgActive.sortOptions.updated_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,797 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="examMsgActive" />
<AddNewData newDataLabel="新增考试信息" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="40rem"
max-height="40rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="120" column-key="uid">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUidKeyRef"
holderText="输入考试uid以搜索相关数据"
@search="examMsg"
/>
<div>
<el-text class="mx-1"> 考试uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.uid }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" 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 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="SelectStatusSearchRef"
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"
>
{{ labelShunt(statusOptions, scope.row.status) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="application_start">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="applicationStartDateTimePickerRef"
DateTimePicker="applicationStartDateTimePicker"
@search="examMsg"
/>
<div>
<el-text class="mx-1"> 报名开始时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="applicationStartSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="examMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.application_start }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="application_end">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="applicationEndDateTimePickerRef"
DateTimePicker="applicationEndDateTimePicker"
@search="examMsg"
/>
<div>
<el-text class="mx-1"> 报名结束时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="applicationEndSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="examMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.application_end }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="exam_start">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="examStartDateTimePickerRef"
DateTimePicker="examStartDateTimePicker"
@search="examMsg"
/>
<div>
<el-text class="mx-1"> 考试开始时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="examStartSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="examMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.exam_start }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="exam_end">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="examEndDateTimePickerRef"
DateTimePicker="examEndMsgDateTimePicker"
@search="examMsg"
/>
<div>
<el-text class="mx-1"> 考试结束时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="examEndSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="examMsg"
/>
</div>
</template>
<template #default="scope">
<div 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-card>
<AddExamForm
ref="addExamFormRef"
title="新增考试信息"
:addData="addData"
:createdAt="null"
@reloadData="examMsg"
/>
<EditExamForm
ref="editExamFormRef"
operateLabel="编辑考试信息"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除考试信息"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出考试数据"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../operate/tabelTools.vue";
import AddNewData from "../operate/addNewData.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 AddExamForm from "../addNewData/examForm/examForm.vue";
import EditExamForm from "../editData/examForm.vue";
import PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import examMsgActiveStore from "../../stores/examMsg";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes.js";
import { downloadData } from "../../utils/downloadData.js";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const examMsgActive = examMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑考试信息",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除考试信息",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchUidKeyRef = ref(null);
const SearchExamNameKeyRef = ref(null);
const SelectStatusSearchRef = ref(null);
const applicationStartDateTimePickerRef = ref(null);
const applicationEndDateTimePickerRef = ref(null);
const examStartDateTimePickerRef = ref(null);
const examEndDateTimePickerRef = ref(null);
const applicationStartSortDateTimeRef = ref(null);
const applicationEndSortDateTimeRef = ref(null);
const examStartSortDateTimeRef = ref(null);
const examEndSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const addExamFormRef = ref(null);
const editExamFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
uid: "",
exam_name: "",
status: "",
application_start: "",
application_end: "",
exam_start: "",
exam_end: "",
});
const sortOptions = reactive({
application_start: false,
application_end: false,
exam_start: false,
exam_end: false,
});
const tableData = ref([]);
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 singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
statusOptions,
});
const editData = reactive({
statusOptions,
});
const showAddForm = () => {
addExamFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
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;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
examMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const examMsg = () => {
judgment(searchOptions, "uid", SearchUidKeyRef.value, "searchMsg");
judgment(searchOptions, "exam_name", SearchExamNameKeyRef.value, "searchMsg");
judgment(searchOptions, "status", SelectStatusSearchRef.value, "searchMsg");
judgment(
searchOptions,
"application_start",
applicationStartDateTimePickerRef.value,
"searchMsg"
);
judgment(
searchOptions,
"application_end",
applicationEndDateTimePickerRef.value,
"searchMsg"
);
judgment(searchOptions, "exam_start", examStartDateTimePickerRef.value, "searchMsg");
judgment(searchOptions, "exam_end", examEndDateTimePickerRef.value, "searchMsg");
judgment(
sortOptions,
"application_start",
applicationStartSortDateTimeRef.value,
"searchMsg"
);
judgment(
sortOptions,
"application_end",
applicationEndSortDateTimeRef.value,
"searchMsg"
);
judgment(sortOptions, "exam_start", examStartSortDateTimeRef.value, "searchMsg");
judgment(sortOptions, "exam_end", examEndSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showExam, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.examMsg;
tableData.value.forEach((item) => {
item.origin_applicationStart = item.application_start;
item.application_start = moment
.utc(item.application_start)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
item.origin_applicationEnd = item.application_end;
item.application_end = moment
.utc(item.application_end)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
item.origin_examStart = item.exam_start;
item.exam_start = moment
.utc(item.exam_start)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
item.origin_examEnd = item.exam_end;
item.exam_end = moment
.utc(item.exam_end)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
});
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 (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.uid == element.uid) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.uid == element.uid) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_applicationStart) {
item.application_start = item.origin_applicationStart;
}
if (item.origin_applicationEnd) {
item.application_end = item.origin_applicationEnd;
}
if (item.origin_examStart) {
item.exam_start = item.origin_examStart;
}
if (item.origin_examEnd) {
item.exam_end = item.origin_examEnd;
}
delete item.origin_applicationStart;
delete item.origin_applicationEnd;
delete item.origin_examStart;
delete item.origin_examEnd;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.application_start = singleData.origin_applicationStart;
singleData.application_end = singleData.origin_applicationEnd;
singleData.exam_start = singleData.origin_examStart;
singleData.exam_end = singleData.origin_examEnd;
delete singleData.origin_applicationStart;
delete singleData.origin_applicationEnd;
delete singleData.origin_examStart;
delete singleData.origin_examEnd;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editExamFormRef.value.model = EditSelectRef.value.model;
editExamFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
editExamFormRef.value.ruleForm.exam_name = singleData.exam_name;
statusOptions.forEach((item) => {
if (item.value == singleData.status) {
editExamFormRef.value.ruleForm.status = item.label;
}
});
editExamFormRef.value.ruleForm.application_start = singleData.application_start;
editExamFormRef.value.ruleForm.application_end = singleData.application_end;
editExamFormRef.value.ruleForm.exam_start = singleData.exam_start;
editExamFormRef.value.ruleForm.exam_end = singleData.exam_end;
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editExam;
model = EditSelectRef.value.model;
password = editExamFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteExam;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
statusOptions.forEach((item) => {
if (editExamFormRef.value.ruleForm.status == item.label) {
editExamFormRef.value.ruleForm.status = item.value;
}
});
data.updateMsg = { ...editExamFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
examMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
statusOptions.forEach((element) => {
if (element.value == item["status"]) {
item["status"] = element.label;
}
});
delete item.origin_applicationStart;
delete item.origin_applicationEnd;
delete item.origin_examStart;
delete item.origin_examEnd;
});
const dataHeaders = [
"考试uid",
"考试名称",
"考试状态",
"报名开始时间",
"报名结束时间",
"考试开始时间",
"考试结束时间",
];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadExamData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
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;
dialogOperationVisible.value = examMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
examMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.uid = examMsgActive.searchOptions.uid;
searchOptions.exam_name = examMsgActive.searchOptions.exam_name;
searchOptions.status = examMsgActive.searchOptions.status;
searchOptions.application_start = examMsgActive.searchOptions.application_start;
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_start = examMsgActive.sortOptions.application_start;
sortOptions.application_end = examMsgActive.sortOptions.application_end;
sortOptions.exam_start = examMsgActive.sortOptions.exam_start;
sortOptions.exam_end = examMsgActive.sortOptions.exam_end;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
examMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchUidKeyRef.value.searchMsg = examMsgActive.searchOptions.uid;
SearchExamNameKeyRef.value.searchMsg = examMsgActive.searchOptions.exam_name;
SelectStatusSearchRef.value.searchMsg = examMsgActive.searchOptions.status;
applicationStartDateTimePickerRef.value.application_start =
examMsgActive.searchOptions.application_start;
applicationEndDateTimePickerRef.value.application_end =
examMsgActive.searchOptions.application_end;
examStartDateTimePickerRef.value.exam_start =
examMsgActive.searchOptions.exam_start;
examEndDateTimePickerRef.value.exam_end = examMsgActive.searchOptions.exam_end;
}
if (dialogSortVisible.value) {
applicationStartSortDateTimeRef.value.searchMsg =
examMsgActive.sortOptions.application_start;
applicationEndSortDateTimeRef.value.searchMsg =
examMsgActive.sortOptions.application_end;
examStartSortDateTimeRef.value.searchMsg = examMsgActive.sortOptions.exam_start;
examEndSortDateTimeRef.value.searchMsg = examMsgActive.sortOptions.exam_end;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,957 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap relative top-0 right-0">
<div class="w-full mb-2 flex items-center absolute top-0 left-28 z-10">
<el-button
style="font-weight: bold"
type="primary"
size="small"
plain
@click="showExport('grades')"
>
生成考试成绩导入表
</el-button>
</div>
<TabelTools @showTools="showTools" :dialogObj="gradesMsgActive" />
<AddNewData newDataLabel="新增考生成绩" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="40rem"
max-height="40rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="userPhotoUrl">
<template #header>
<div>
<div>
<el-text class="mx-1"> 用户照片 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
<img
:key="scope.row.user_uid"
:src="
scope.row.userPhotoUrl == '#' ? defaultPhoto : scope.row.userPhotoUrl
"
style="width: 115px; height: 165px"
/>
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="user_uid">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserUidKeyRef"
holderText="输入考生uid以搜索相关数据"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 考生uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.user_uid }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="user_name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserNameKeyRef"
holderText="输入考生姓名以搜索相关数据"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 考生姓名 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examinee.user_name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="identity_card">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchIdentityCardKeyRef"
holderText="输入身份证号以搜索相关数据"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 身份证号 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examinee.identity_card }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="workplace">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchWorkplaceKeyRef"
holderText="输入工作单位以搜索相关数据"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 工作单位 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examinee.workplace }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="phone">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchPhoneKeyRef"
holderText="输入手机号码以搜索相关数据"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 手机号码 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examinee.phone }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="exam_uid">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchExamUidKeyRef"
holderText="输入考试uid以搜索相关数据"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 考试uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.exam_uid }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" 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 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="SelectStatusSearchRef"
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"
>
{{ labelShunt(statusOptions, scope.row.status) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="80" 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(passTypeOptions, scope.row.pass_type)"
class="flex justify-center items-center text-base"
>
{{ scope.row.grades == null ? "无成绩" : scope.row.grades }}
</div>
</template>
</el-table-column>
<el-table-column min-width="110" column-key="pass_type">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectPassTypeSearchRef"
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"
>
{{ labelShunt(passTypeOptions, scope.row.pass_type) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="exam_start">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="examStartDateTimePickerRef"
DateTimePicker="examStartDateTimePicker"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 考试开始时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="examStartSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="gradesMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.exam_start }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="exam_end">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="examEndDateTimePickerRef"
DateTimePicker="examEndMsgDateTimePicker"
@search="gradesMsg"
/>
<div>
<el-text class="mx-1"> 考试结束时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="examEndSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="gradesMsg"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.exam_end }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="gradesMsgActive"
@sizeChangeBack="changePage"
@search="gradesMsg"
/>
</div>
</el-card>
<AddGradesForm
ref="addGradesFormRef"
title="新增考生成绩"
:addData="addData"
:createdAt="null"
@reloadData="gradesMsg"
/>
<EditGradesForm
ref="editGradesFormRef"
operateLabel="编辑考生成绩"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除考生成绩"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出考生成绩数据"
@downloadData="executeDownloadData"
/>
<ExportGrades ref="gradesRef" />
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../operate/tabelTools.vue";
import AddNewData from "../operate/addNewData.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 AddGradesForm from "../addNewData/gradesForm/gradesForm.vue";
import EditGradesForm from "../editData/gradesForm.vue";
import PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import ExportGrades from "../operate/export/exportGrades.vue";
import gradesMsgActiveStore from "../../stores/gradesMsg";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes.js";
import { downloadData } from "../../utils/downloadData.js";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
import defaultPhoto from "../../assets/default.png";
const gradesMsgActive = gradesMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑考生成绩",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除考生成绩",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchUserUidKeyRef = ref(null);
const SearchIdentityCardKeyRef = ref(null);
const SearchWorkplaceKeyRef = ref(null);
const SearchPhoneKeyRef = ref(null);
const SearchUserNameKeyRef = ref(null);
const SearchExamUidKeyRef = ref(null);
const SearchExamNameKeyRef = ref(null);
const SelectStatusSearchRef = ref(null);
const SearchGradesKeyRef = ref(null);
const SelectPassTypeSearchRef = ref(null);
const examStartDateTimePickerRef = ref(null);
const examEndDateTimePickerRef = ref(null);
const SortGradesRef = ref(null);
const examStartSortDateTimeRef = ref(null);
const examEndSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const addGradesFormRef = ref(null);
const editGradesFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const gradesRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
user_uid: "",
user_name: "",
identity_card: "",
workplace: "",
phone: "",
exam_uid: "",
exam_name: "",
status: "",
grades: "",
pass_type: "",
exam_start: "",
exam_end: "",
});
const sortOptions = reactive({
grades: false,
exam_start: false,
exam_end: false,
});
const tableData = ref([]);
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 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 singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
statusOptions,
passTypeOptions,
});
const editData = reactive({
statusOptions,
passTypeOptions,
});
const exportData = reactive({
statusOptions,
passTypeOptions,
});
const showAddForm = () => {
addGradesFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showExport = (type) => {
if (type == "grades") {
gradesRef.value.gradesDialogVisible = true;
}
};
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;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
gradesMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const gradesMsg = () => {
judgment(searchOptions, "user_uid", SearchUserUidKeyRef.value, "searchMsg");
judgment(searchOptions, "user_name", SearchUserNameKeyRef.value, "searchMsg");
judgment(searchOptions, "identity_card", SearchIdentityCardKeyRef.value, "searchMsg");
judgment(searchOptions, "workplace", SearchWorkplaceKeyRef.value, "searchMsg");
judgment(searchOptions, "phone", SearchPhoneKeyRef.value, "searchMsg");
judgment(searchOptions, "exam_uid", SearchExamUidKeyRef.value, "searchMsg");
judgment(searchOptions, "exam_name", SearchExamNameKeyRef.value, "searchMsg");
judgment(searchOptions, "status", SelectStatusSearchRef.value, "searchMsg");
judgment(searchOptions, "grades", SearchGradesKeyRef.value, "searchMsg");
judgment(searchOptions, "pass_type", SelectPassTypeSearchRef.value, "searchMsg");
judgment(searchOptions, "exam_start", examStartDateTimePickerRef.value, "searchMsg");
judgment(searchOptions, "exam_end", examEndDateTimePickerRef.value, "searchMsg");
judgment(sortOptions, "grades", SortGradesRef.value, "searchMsg");
judgment(sortOptions, "exam_start", examStartSortDateTimeRef.value, "searchMsg");
judgment(sortOptions, "exam_end", examEndSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showGrades, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.gradesMsg;
tableData.value.forEach((item) => {
item.origin_examStart = item.exam_start;
item.exam_start = moment
.utc(item.exam_start)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
item.origin_examEnd = item.exam_end;
item.exam_end = moment
.utc(item.exam_end)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
});
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 (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.user_uid == element.user_uid && item.exam_uid == element.exam_uid) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.user_uid == element.user_uid && item.exam_uid == element.exam_uid) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_examStart) {
item.exam_start = item.origin_examStart;
}
if (item.origin_examEnd) {
item.exam_end = item.origin_examEnd;
}
delete item.origin_examStart;
delete item.origin_examEnd;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.exam_start = singleData.origin_examStart;
singleData.exam_end = singleData.origin_examEnd;
delete singleData.origin_examStart;
delete singleData.origin_examEnd;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editGradesFormRef.value.model = EditSelectRef.value.model;
editGradesFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
statusOptions.forEach((item) => {
if (item.value == singleData.status) {
editGradesFormRef.value.ruleForm.status = item.label;
}
});
editGradesFormRef.value.ruleForm.grades = singleData.grades;
passTypeOptions.forEach((item) => {
if (item.value == singleData.pass_type) {
editGradesFormRef.value.ruleForm.pass_type = item.label;
}
});
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editGrades;
model = EditSelectRef.value.model;
password = editGradesFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteGrades;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
statusOptions.forEach((item) => {
if (editGradesFormRef.value.ruleForm.status == item.label) {
editGradesFormRef.value.ruleForm.status = item.value;
}
});
passTypeOptions.forEach((item) => {
if (editGradesFormRef.value.ruleForm.pass_type == item.label) {
editGradesFormRef.value.ruleForm.pass_type = item.value;
}
});
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
data.updateMsg = { ...editGradesFormRef.value.ruleForm };
if (data.updateMsg.grades == "无") {
data.updateMsg.grades = null;
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
gradesMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
statusOptions.forEach((element) => {
if (element.value == item["status"]) {
item["status"] = element.label;
}
});
passTypeOptions.forEach((element) => {
if (element.value == item["pass_type"]) {
item["pass_type"] = element.label;
}
});
delete item.origin_examStart;
delete item.origin_examEnd;
});
const dataHeaders = [
"考生uid",
"考生姓名",
"身份证号",
"工作单位",
"手机号码",
"考试uid",
"考试名称",
"参与状态",
"考生成绩",
"考试开始时间",
"考试结束时间",
];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadGradesData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
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;
dialogOperationVisible.value = gradesMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
gradesMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.user_uid = gradesMsgActive.searchOptions.user_uid;
searchOptions.user_name = gradesMsgActive.searchOptions.user_name;
searchOptions.identity_card = gradesMsgActive.searchOptions.identity_card;
searchOptions.workplace = gradesMsgActive.searchOptions.workplace;
searchOptions.phone = gradesMsgActive.searchOptions.phone;
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;
searchOptions.exam_start = gradesMsgActive.searchOptions.exam_start;
searchOptions.exam_end = gradesMsgActive.searchOptions.exam_end;
}
if (dialogSortVisible.value) {
sortOptions.grades = gradesMsgActive.sortOptions.grades;
sortOptions.exam_start = gradesMsgActive.sortOptions.exam_start;
sortOptions.exam_end = gradesMsgActive.sortOptions.exam_end;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
gradesMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchUserUidKeyRef.value.searchMsg = gradesMsgActive.searchOptions.user_uid;
SearchUserNameKeyRef.value.searchMsg = gradesMsgActive.searchOptions.user_name;
SearchIdentityCardKeyRef.value.searchMsg =
gradesMsgActive.searchOptions.identity_card;
SearchWorkplaceKeyRef.value.searchMsg = gradesMsgActive.searchOptions.workplace;
SearchPhoneKeyRef.value.searchMsg = gradesMsgActive.searchOptions.phone;
SearchExamUidKeyRef.value.searchMsg = gradesMsgActive.searchOptions.exam_uid;
SearchExamNameKeyRef.value.searchMsg = gradesMsgActive.searchOptions.exam_name;
SelectStatusSearchRef.value.searchMsg = gradesMsgActive.searchOptions.status;
SearchGradesKeyRef.value.searchMsg = gradesMsgActive.searchOptions.grades;
SelectPassTypeSearchRef.value.searchMsg = gradesMsgActive.searchOptions.pass_type;
examStartDateTimePickerRef.value.exam_start =
gradesMsgActive.searchOptions.exam_start;
examEndDateTimePickerRef.value.exam_end = gradesMsgActive.searchOptions.exam_end;
}
if (dialogSortVisible.value) {
SortGradesRef.value.searchMsg = gradesMsgActive.sortOptions.grades;
examStartSortDateTimeRef.value.searchMsg = gradesMsgActive.sortOptions.exam_start;
examEndSortDateTimeRef.value.searchMsg = gradesMsgActive.sortOptions.exam_end;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,633 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="importerrorActive" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="40rem"
max-height="40rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="user_name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserNameKeyRef"
holderText="输入用户姓名以搜索相关数据"
@search="importerror"
/>
<div>
<el-text class="mx-1"> 用户姓名 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.user_name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="identity_card">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchIdentityCardKeyRef"
holderText="输入身份证号以搜索相关数据"
@search="importerror"
/>
<div>
<el-text class="mx-1"> 身份证号 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.identity_card }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="exam_uid">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchExamUidKeyRef"
holderText="输入考试uid以搜索相关数据"
@search="importerror"
/>
<div>
<el-text class="mx-1"> 考试uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.exam_uid }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="exam_name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchExamNameKeyRef"
holderText="输入考试名称以搜索相关数据"
@search="importerror"
/>
<div>
<el-text class="mx-1"> 考试名称 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examination.exam_name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="type">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectTypeSearchRef"
holderText="选择错误类型以搜索相关数据"
:options="typeOptions"
@search="importerror"
/>
<div>
<el-text class="mx-1"> 错误类型 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(typeOptions, scope.row.type)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(typeOptions, scope.row.type) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="status">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectStatusSearchRef"
holderText="选择处理状态以搜索相关数据"
:options="statusOptions"
@search="importerror"
/>
<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"
>
{{ labelShunt(statusOptions, scope.row.status) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="created_at">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="createdDateTimePickerRef"
DateTimePicker="createdDateTimePicker"
@search="importerror"
/>
<div>
<el-text class="mx-1"> 报错时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="createdSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="importerror"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.created_at }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="importerrorActive"
@sizeChangeBack="changePage"
@search="importerror"
/>
</div>
</el-card>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除错误日志"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出错误日志"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "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 PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import importerrorActiveStore from "../../stores/importerror";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes.js";
import { downloadData } from "../../utils/downloadData.js";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const importerrorActive = importerrorActiveStore();
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除错误日志",
};
const DelSelectRef = ref(null);
const SearchUserNameKeyRef = ref(null);
const SearchIdentityCardKeyRef = ref(null);
const SearchExamUidKeyRef = ref(null);
const SearchExamNameKeyRef = ref(null);
const SelectTypeSearchRef = ref(null);
const SelectStatusSearchRef = ref(null);
const createdDateTimePickerRef = ref(null);
const createdSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
user_name: "",
identity_card: "",
exam_uid: "",
exam_name: "",
type: "",
status: "",
created_at: "",
});
const sortOptions = reactive({
created_at: false,
});
const tableData = ref([]);
const typeOptions = [
{
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(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
];
const statusOptions = [
{
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;",
},
];
let singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
importerrorActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
importerrorActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
importerrorActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const importerror = () => {
judgment(searchOptions, "user_name", SearchUserNameKeyRef.value, "searchMsg");
judgment(searchOptions, "identity_card", SearchIdentityCardKeyRef.value, "searchMsg");
judgment(searchOptions, "exam_uid", SearchExamUidKeyRef.value, "searchMsg");
judgment(searchOptions, "exam_name", SearchExamNameKeyRef.value, "searchMsg");
judgment(searchOptions, "type", SelectTypeSearchRef.value, "searchMsg");
judgment(searchOptions, "status", SelectStatusSearchRef.value, "searchMsg");
judgment(searchOptions, "created_at", createdDateTimePickerRef.value, "searchMsg");
judgment(sortOptions, "created_at", createdSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showImporterror, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.importerrorMsg;
tableData.value.forEach((item) => {
item.origin_createdAt = item.created_at;
item.created_at = moment
.utc(item.created_at)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
});
PaginationRef.value.dataCount = res.data.count;
importerrorActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
importerrorActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.created_at == element.created_at) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.created_at == element.created_at) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_createdAt) {
item.created_at = item.origin_createdAt;
}
delete item.origin_createdAt;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.created_at = singleData.origin_createdAt;
delete singleData.origin_createdAt;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "del") {
operateType = "删除";
url = $url.deleteImporterror;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
importerror();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
typeOptions.forEach((element) => {
if (element.value == item["type"]) {
item["type"] = element.label;
}
});
statusOptions.forEach((element) => {
if (element.value == item["status"]) {
item["status"] = element.label;
}
});
delete item.origin_createdAt;
});
const dataHeaders = [
"用户姓名",
"身份证号",
"考试uid",
"考试名称",
"错误类型",
"处理状态",
"报错时间",
];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadImporterrorData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ importerror });
watch(searchOptions, (value) => {
importerrorActive.searchOptions = value;
});
watch(sortOptions, (value) => {
importerrorActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = importerrorActive.pageCount;
dialogSearchVisible.value = importerrorActive.dialogSearchVisible;
dialogSortVisible.value = importerrorActive.dialogSortVisible;
dialogOperationVisible.value = importerrorActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
importerrorActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.user_name = importerrorActive.searchOptions.user_name;
searchOptions.identity_card = importerrorActive.searchOptions.identity_card;
searchOptions.exam_uid = importerrorActive.searchOptions.exam_uid;
searchOptions.exam_name = importerrorActive.searchOptions.exam_name;
searchOptions.type = importerrorActive.searchOptions.type;
searchOptions.status = importerrorActive.searchOptions.status;
searchOptions.created_at = importerrorActive.searchOptions.created_at;
}
if (dialogSortVisible.value) {
sortOptions.created_at = importerrorActive.sortOptions.created_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
importerror();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchUserNameKeyRef.value.searchMsg = importerrorActive.searchOptions.user_name;
SearchIdentityCardKeyRef.value.searchMsg =
importerrorActive.searchOptions.identity_card;
SearchExamUidKeyRef.value.searchMsg = importerrorActive.searchOptions.exam_uid;
SearchExamNameKeyRef.value.searchMsg = importerrorActive.searchOptions.exam_name;
SelectTypeSearchRef.value.searchMsg = importerrorActive.searchOptions.type;
SelectStatusSearchRef.value.searchMsg = importerrorActive.searchOptions.status;
createdDateTimePickerRef.value.created_at =
importerrorActive.searchOptions.created_at;
}
if (dialogSortVisible.value) {
createdSortDateTimeRef.value.searchMsg = importerrorActive.sortOptions.created_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,759 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="monitoringActive" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="40rem"
max-height="40rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="120" column-key="operate">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectOperateSearchRef"
holderText="选择操作名称以搜索相关数据"
:options="operateOptions"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 操作名称 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(operateOptions, scope.row.operate)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(operateOptions, scope.row.operate) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="operate_msg">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOperateMsgKeyRef"
holderText="输入操作描述以搜索相关数据"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 操作描述 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.operate_msg }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="type">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectTypeSearchRef"
holderText="选择危险等级以搜索相关数据"
:options="typeOptions"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 危险等级 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(typeOptions, scope.row.type)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(typeOptions, scope.row.type) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="status">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectStatusSearchRef"
holderText="选择执行状态以搜索相关数据"
:options="statusOptions"
@search="monitoring"
/>
<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"
>
{{ labelShunt(statusOptions, scope.row.status) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="affect_entries">
<template #header>
<div>
<div>
<el-text class="mx-1"> 影响到的数据数目 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.affect_entries }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="user">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserKeyRef"
holderText="输入操作者uid以搜索相关数据"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 操作者uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.user }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="account_name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchAccountNameKeyRef"
holderText="输入操作者姓名以搜索相关数据"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 操作者姓名 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.account_name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="role">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectRoleSearchRef"
holderText="选择操作者权限以搜索相关数据"
:options="roleOptions"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 操作者权限 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(roleOptions, scope.row.role)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(roleOptions, scope.row.role) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="created_at">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="createdDateTimePickerRef"
DateTimePicker="createdDateTimePicker"
@search="monitoring"
/>
<div>
<el-text class="mx-1"> 操作发生时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="createdSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="monitoring"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.created_at }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="monitoringActive"
@sizeChangeBack="changePage"
@search="monitoring"
/>
</div>
</el-card>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除系统记录"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出系统记录"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "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 PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import monitoringActiveStore from "../../stores/monitoring";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes.js";
import { downloadData } from "../../utils/downloadData.js";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const monitoringActive = monitoringActiveStore();
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除系统记录",
};
const DelSelectRef = ref(null);
const SelectOperateSearchRef = ref(null);
const SearchOperateMsgKeyRef = ref(null);
const SelectTypeSearchRef = ref(null);
const SelectStatusSearchRef = ref(null);
const SearchUserKeyRef = ref(null);
const SearchAccountNameKeyRef = ref(null);
const SelectRoleSearchRef = ref(null);
const createdDateTimePickerRef = ref(null);
const createdSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
operate: "",
operate_msg: "",
type: "",
status: "",
user: "",
account_name: "",
role: "",
created_at: "",
});
const sortOptions = reactive({
created_at: false,
});
const tableData = ref([]);
const operateOptions = [
{
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(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
{
value: "2",
label: "删除数据",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
];
const typeOptions = [
{
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(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
{
value: "2",
label: "危险",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
];
const statusOptions = [
{
value: "0",
label: "操作执行失败,原因:无用户身份",
style:
"color: rgb(120, 140, 180);background-color: rgba(120, 140, 180, 0.1);padding: 0;",
},
{
value: "1",
label: "操作执行失败,原因:密码错误",
style:
"color: rgb(255, 100, 180);background-color: rgba(255, 100, 180, 0.1);padding: 0;",
},
{
value: "2",
label: "操作执行失败,原因:权限不足",
style:
"color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);padding: 0;",
},
{
value: "3",
label: "操作执行失败,原因:查询条件错误",
style: "color: rgb(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
{
value: "4",
label: "操作执行成功",
style:
"color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);padding: 0;",
},
{
value: "5",
label: "操作执行失败,原因:程序错误",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
];
const roleOptions = [
{
value: "9",
label: "系统管理员",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);padding: 0;",
},
{
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(255, 200, 0);background-color: rgba(255, 200, 0, 0.1);padding: 0;",
},
];
let singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
monitoringActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
monitoringActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
monitoringActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const monitoring = () => {
judgment(searchOptions, "operate", SelectOperateSearchRef.value, "searchMsg");
judgment(searchOptions, "operate_msg", SearchOperateMsgKeyRef.value, "searchMsg");
judgment(searchOptions, "type", SelectTypeSearchRef.value, "searchMsg");
judgment(searchOptions, "status", SelectStatusSearchRef.value, "searchMsg");
judgment(searchOptions, "user", SearchUserKeyRef.value, "searchMsg");
judgment(searchOptions, "account_name", SearchAccountNameKeyRef.value, "searchMsg");
judgment(searchOptions, "role", SelectRoleSearchRef.value, "searchMsg");
judgment(searchOptions, "created_at", createdDateTimePickerRef.value, "searchMsg");
judgment(sortOptions, "created_at", createdSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showMonitoring, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.monitoringMsg;
tableData.value.forEach((item) => {
item.origin_createdAt = item.created_at;
item.created_at = moment
.utc(item.created_at)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
});
PaginationRef.value.dataCount = res.data.count;
monitoringActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
monitoringActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.created_at == element.created_at) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.created_at == element.created_at) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_createdAt) {
item.created_at = item.origin_createdAt;
}
delete item.origin_createdAt;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.created_at = singleData.origin_createdAt;
delete singleData.origin_createdAt;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "del") {
operateType = "删除";
url = $url.deleteMonitoring;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
monitoring();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
operateOptions.forEach((element) => {
if (element.value == item["operate"]) {
item["operate"] = element.label;
}
});
typeOptions.forEach((element) => {
if (element.value == item["type"]) {
item["type"] = element.label;
}
});
statusOptions.forEach((element) => {
if (element.value == item["status"]) {
item["status"] = element.label;
}
});
roleOptions.forEach((element) => {
if (element.value == item["role"]) {
item["role"] = element.label;
}
});
delete item.origin_createdAt;
});
const dataHeaders = [
"操作名称",
"操作描述",
"危险等级",
"执行状态",
"操作者uid",
"操作者姓名",
"操作者权限",
"操作发生时间",
];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadMonitoringData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ monitoring });
watch(searchOptions, (value) => {
monitoringActive.searchOptions = value;
});
watch(sortOptions, (value) => {
monitoringActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = monitoringActive.pageCount;
dialogSearchVisible.value = monitoringActive.dialogSearchVisible;
dialogSortVisible.value = monitoringActive.dialogSortVisible;
dialogOperationVisible.value = monitoringActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
monitoringActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.operate = monitoringActive.searchOptions.operate;
searchOptions.operate_msg = monitoringActive.searchOptions.operate_msg;
searchOptions.type = monitoringActive.searchOptions.type;
searchOptions.status = monitoringActive.searchOptions.status;
searchOptions.user = monitoringActive.searchOptions.user;
searchOptions.account_name = monitoringActive.searchOptions.account_name;
searchOptions.role = monitoringActive.searchOptions.role;
searchOptions.created_at = monitoringActive.searchOptions.created_at;
}
if (dialogSortVisible.value) {
sortOptions.created_at = monitoringActive.sortOptions.created_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
monitoring();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SelectOperateSearchRef.value.searchMsg = monitoringActive.searchOptions.operate;
SearchOperateMsgKeyRef.value.searchMsg = monitoringActive.searchOptions.operate_msg;
SelectTypeSearchRef.value.searchMsg = monitoringActive.searchOptions.type;
SelectStatusSearchRef.value.searchMsg = monitoringActive.searchOptions.status;
SearchUserKeyRef.value.searchMsg = monitoringActive.searchOptions.user;
SearchAccountNameKeyRef.value.searchMsg =
monitoringActive.searchOptions.account_name;
SelectRoleSearchRef.value.searchMsg = monitoringActive.searchOptions.role;
createdDateTimePickerRef.value.created_at =
monitoringActive.searchOptions.created_at;
}
if (dialogSortVisible.value) {
createdSortDateTimeRef.value.searchMsg = monitoringActive.sortOptions.created_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,668 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="topicWhitelistActive" />
<AddNewData newDataLabel="新增题库白名单" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="40rem"
max-height="40rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="userPhotoUrl">
<template #header>
<div>
<div>
<el-text class="mx-1"> 用户照片 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
<img
:key="scope.row.user_uid"
:src="
scope.row.userPhotoUrl == '#' ? defaultPhoto : scope.row.userPhotoUrl
"
style="width: 115px; height: 165px"
/>
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="user_uid">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserUidKeyRef"
holderText="输入用户uid以搜索相关数据"
@search="topicWhitelist"
/>
<div>
<el-text class="mx-1"> 用户uid </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.user_uid }}
</div>
</template>
</el-table-column>
<el-table-column min-width="160" column-key="user_name">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchUserNameKeyRef"
holderText="输入用户姓名以搜索相关数据"
@search="topicWhitelist"
/>
<div>
<el-text class="mx-1"> 用户姓名 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examinee.user_name }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="identity_card">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchIdentityCardKeyRef"
holderText="输入身份证号以搜索相关数据"
@search="topicWhitelist"
/>
<div>
<el-text class="mx-1"> 身份证号 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.examinee.identity_card }}
</div>
</template>
</el-table-column>
<el-table-column min-width="120" column-key="type">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectTypeSearchRef"
holderText="选择白名单类型以搜索相关数据"
:options="typeOptions"
@search="topicWhitelist"
/>
<div>
<el-text class="mx-1"> 白名单类型 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(typeOptions, scope.row.type)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(typeOptions, scope.row.type) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="expired_at">
<template #header>
<DateTimePicker
v-if="dialogSearchVisible"
ref="expiredDateTimePickerRef"
DateTimePicker="expiredMsgDateTimePicker"
@search="topicWhitelist"
/>
<div>
<el-text class="mx-1"> 白名单过期时间 </el-text>
<SortSearch
v-if="dialogSortVisible"
ref="expiredSortDateTimeRef"
activeText="顺序"
inactiveText="倒序"
@search="topicWhitelist"
/>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.expired_at }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="topicWhitelistActive"
@sizeChangeBack="changePage"
@search="topicWhitelist"
/>
</div>
</el-card>
<AddTopicWhitelistForm
ref="addTopicWhitelistFormRef"
title="新增题库白名单"
:addData="addData"
:createdAt="null"
@reloadData="topicWhitelist"
/>
<EditTopicWhitelistForm
ref="editTopicWhitelistFormRef"
operateLabel="编辑题库白名单"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除题库白名单"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出题库白名单"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../operate/tabelTools.vue";
import AddNewData from "../operate/addNewData.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 AddTopicWhitelistForm from "../addNewData/topicWhitelistForm/topicWhitelistForm.vue";
import EditTopicWhitelistForm from "../editData/topicWhitelistForm.vue";
import PasswordOauth from "../operate/passwordOauth.vue";
import DownloadData from "../operate/downloadData.vue";
import topicWhitelistActiveStore from "../../stores/topicWhitelist";
import moment from "moment";
import $http from "../../api/index";
import $url from "../../api/url";
import { encrypt } from "../../utils/aes.js";
import { downloadData } from "../../utils/downloadData.js";
import { judgment } from "../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
import defaultPhoto from "../../assets/default.png";
const topicWhitelistActive = topicWhitelistActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑题库白名单",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除题库白名单",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchUserUidKeyRef = ref(null);
const SearchUserNameKeyRef = ref(null);
const SearchIdentityCardKeyRef = ref(null);
const SelectTypeSearchRef = ref(null);
const expiredDateTimePickerRef = ref(null);
const expiredSortDateTimeRef = ref(null);
const PaginationRef = ref(null);
const addTopicWhitelistFormRef = ref(null);
const editTopicWhitelistFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
user_uid: "",
user_name: "",
identity_card: "",
type: "",
expired_at: "",
});
const sortOptions = reactive({
expired_at: false,
});
const tableData = ref([]);
const typeOptions = [
{
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;",
},
];
let singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
typeOptions,
});
const editData = reactive({
typeOptions,
});
const showAddForm = () => {
addTopicWhitelistFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
topicWhitelistActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
topicWhitelistActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
topicWhitelistActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const topicWhitelist = () => {
judgment(searchOptions, "user_uid", SearchUserUidKeyRef.value, "searchMsg");
judgment(searchOptions, "user_name", SearchUserNameKeyRef.value, "searchMsg");
judgment(searchOptions, "identity_card", SearchIdentityCardKeyRef.value, "searchMsg");
judgment(searchOptions, "type", SelectTypeSearchRef.value, "searchMsg");
judgment(searchOptions, "expired_at", expiredDateTimePickerRef.value, "searchMsg");
judgment(sortOptions, "expired_at", expiredSortDateTimeRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showTopicWhitelist, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.topicWhitelistMsg;
tableData.value.forEach((item) => {
item.origin_expiredAt = item.expired_at;
if (item.expired_at != null) {
item.expired_at = moment
.utc(item.expired_at)
.add(8, "hours")
.format("YYYY年MM月DD日HH时mm分ss秒");
} else {
item.expired_at = "永久有效";
}
});
PaginationRef.value.dataCount = res.data.count;
topicWhitelistActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
topicWhitelistActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.expired_at == element.expired_at) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.expired_at == element.expired_at) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
for (const key in operateDataClone) {
operateDataClone[key].forEach((item) => {
if (item.origin_expiredAt) {
item.expired_at = item.origin_expiredAt;
}
delete item.origin_expiredAt;
delete item.location;
});
}
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
singleData.expired_at = singleData.origin_expiredAt;
delete singleData.origin_expiredAt;
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editTopicWhitelistFormRef.value.model = EditSelectRef.value.model;
editTopicWhitelistFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
typeOptions.forEach((item) => {
if (item.value == singleData.type) {
editTopicWhitelistFormRef.value.ruleForm.type = item.label;
}
});
editTopicWhitelistFormRef.value.ruleForm.expired_at = singleData.expired_at;
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editTopicWhitelist;
model = EditSelectRef.value.model;
password = editTopicWhitelistFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteTopicWhitelist;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
typeOptions.forEach((item) => {
if (editTopicWhitelistFormRef.value.ruleForm.type == item.label) {
editTopicWhitelistFormRef.value.ruleForm.type = item.value;
}
});
data.updateMsg = { ...editTopicWhitelistFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
console.log(data);
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
topicWhitelist();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
typeOptions.forEach((element) => {
if (element.value == item["type"]) {
item["type"] = element.label;
}
});
});
const dataHeaders = ["用户uid", "用户姓名", "身份证号", "白名单类型", "白名单过期时间"];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadTopicWhitelistData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ topicWhitelist });
watch(searchOptions, (value) => {
topicWhitelistActive.searchOptions = value;
});
watch(sortOptions, (value) => {
topicWhitelistActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = topicWhitelistActive.pageCount;
dialogSearchVisible.value = topicWhitelistActive.dialogSearchVisible;
dialogSortVisible.value = topicWhitelistActive.dialogSortVisible;
dialogOperationVisible.value = topicWhitelistActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
topicWhitelistActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.user_uid = topicWhitelistActive.searchOptions.user_uid;
searchOptions.user_name = topicWhitelistActive.searchOptions.user_name;
searchOptions.identity_card = topicWhitelistActive.searchOptions.identity_card;
searchOptions.type = topicWhitelistActive.searchOptions.type;
searchOptions.expired_at = topicWhitelistActive.searchOptions.expired_at;
}
if (dialogSortVisible.value) {
sortOptions.expired_at = topicWhitelistActive.sortOptions.expired_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
topicWhitelist();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchUserUidKeyRef.value.searchMsg = topicWhitelistActive.searchOptions.user_uid;
SearchUserNameKeyRef.value.searchMsg = topicWhitelistActive.searchOptions.user_name;
SearchIdentityCardKeyRef.value.searchMsg =
topicWhitelistActive.searchOptions.identity_card;
SelectTypeSearchRef.value.searchMsg = topicWhitelistActive.searchOptions.type;
expiredDateTimePickerRef.value.expired_at =
topicWhitelistActive.searchOptions.expired_at;
}
if (dialogSortVisible.value) {
expiredSortDateTimeRef.value.searchMsg =
topicWhitelistActive.sortOptions.expired_at;
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,72 @@
<template>
<div>
<el-menu
class="flex flex-wrap 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>
<el-card>
<div v-show="activeIndex === '1'">
<Radio />
</div>
<div v-show="activeIndex === '2'">
<Multiple />
</div>
<div v-show="activeIndex === '3'">
<Judgment />
</div>
<div v-show="activeIndex === '4'">
<Interlocution />
</div>
</el-card>
</div>
</template>
<script setup>
import { ref } from "vue";
import Interlocution from "../features/trainindTopic/interlocution.vue";
import Judgment from "../features/trainindTopic/judgment.vue";
import Multiple from "../features/trainindTopic/multiple.vue";
import Radio from "../features/trainindTopic/radio.vue";
const activeIndex = ref("1");
const indexStyle = "background-color: rgb(241 245 249);";
const activeStyle = "background-color: rgb(207 250 254);";
const handleSelect = (key, keyPath) => {
activeIndex.value = key;
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,556 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="interlocutionMsgActive" />
<AddNewData newDataLabel="新增问答题" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="30rem"
max-height="30rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="stem">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchStemKeyRef"
holderText="输入题干以搜索相关数据"
@search="interlocutionMsg"
/>
<div>
<el-text class="mx-1"> 题干 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.stem }}
</div>
</template>
</el-table-column>
<el-table-column min-width="160" column-key="answer">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchAnswerKeyRef"
holderText="输入答案以搜索相关数据"
@search="interlocutionMsg"
/>
<div>
<el-text class="mx-1"> 答案 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.answer }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="difficulty">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectDifficultySearchRef"
holderText="选择难度以搜索相关数据"
:options="difficultyOptions"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 难度 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(difficultyOptions, scope.row.difficulty)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(difficultyOptions, scope.row.difficulty) }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="interlocutionMsgActive"
@sizeChangeBack="changePage"
@search="interlocutionMsg"
/>
</div>
</el-card>
<AddInterlocutionForm
ref="addInterlocutionFormRef"
title="新增问答题"
:addData="addData"
:createdAt="null"
@reloadData="interlocutionMsg"
/>
<EditInterlocutionForm
ref="editInterlocutionFormRef"
operateLabel="编辑问答题"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除问答题"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出问答题"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../../operate/tabelTools.vue";
import AddNewData from "../../operate/addNewData.vue";
import TabelSelect from "../../operate/tabelSelect.vue";
import SearchKey from "../../search/searchKey.vue";
import Pagination from "../../search/pagination.vue";
import AddInterlocutionForm from "../../addNewData/trainindTopic/interlocution/interlocutionForm.vue";
import EditInterlocutionForm from "../../editData/trainindTopic/interlocution.vue";
import PasswordOauth from "../../operate/passwordOauth.vue";
import DownloadData from "../../operate/downloadData.vue";
import interlocutionMsgActiveStore from "../../../stores/trainindTopic/interlocution.js";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes.js";
import { downloadData } from "../../../utils/downloadData.js";
import { judgment } from "../../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const interlocutionMsgActive = interlocutionMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑问答题",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除问答题",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchStemKeyRef = ref(null);
const SearchAnswerKeyRef = ref(null);
const SelectDifficultySearchRef = ref(null);
const PaginationRef = ref(null);
const addInterlocutionFormRef = ref(null);
const editInterlocutionFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
stem: "",
answer: "",
difficulty: "",
});
const sortOptions = reactive({});
const tableData = ref([]);
const difficultyOptions = [
{
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 singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
difficultyOptions,
});
const editData = reactive({
difficultyOptions,
});
const showAddForm = () => {
addInterlocutionFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
interlocutionMsgActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
interlocutionMsgActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
interlocutionMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const interlocutionMsg = () => {
judgment(searchOptions, "stem", SearchStemKeyRef.value, "searchMsg");
judgment(searchOptions, "answer", SearchAnswerKeyRef.value, "searchMsg");
judgment(searchOptions, "difficulty", SelectDifficultySearchRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showInterlocution, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.interlocutionMsg;
PaginationRef.value.dataCount = res.data.count;
interlocutionMsgActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
interlocutionMsgActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.stem == element.stem) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.stem == element.stem) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editInterlocutionFormRef.value.model = EditSelectRef.value.model;
editInterlocutionFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
editInterlocutionFormRef.value.ruleForm.stem = singleData.stem;
editInterlocutionFormRef.value.ruleForm.answer = singleData.answer;
difficultyOptions.forEach((item) => {
if (item.value == singleData.difficulty) {
editInterlocutionFormRef.value.ruleForm.difficulty = item.label;
}
});
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editInterlocution;
model = EditSelectRef.value.model;
password = editInterlocutionFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteInterlocution;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
data.updateMsg = { ...editInterlocutionFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
interlocutionMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
difficultyOptions.forEach((element) => {
if (element.value == item["difficulty"]) {
item["difficulty"] = element.label;
}
});
});
const dataHeaders = ["题干", "答案", "难度"];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadInterlocutionData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ interlocutionMsg });
watch(searchOptions, (value) => {
interlocutionMsgActive.searchOptions = value;
});
watch(sortOptions, (value) => {
interlocutionMsgActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = interlocutionMsgActive.pageCount;
dialogSearchVisible.value = interlocutionMsgActive.dialogSearchVisible;
dialogSortVisible.value = interlocutionMsgActive.dialogSortVisible;
dialogOperationVisible.value = interlocutionMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
interlocutionMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.stem = interlocutionMsgActive.searchOptions.stem;
searchOptions.answer = interlocutionMsgActive.searchOptions.answer;
searchOptions.difficulty = interlocutionMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
interlocutionMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchStemKeyRef.value.searchMsg = interlocutionMsgActive.searchOptions.stem;
SearchAnswerKeyRef.value.searchMsg = interlocutionMsgActive.searchOptions.answer;
SelectDifficultySearchRef.value.searchMsg =
interlocutionMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,584 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="judgmentMsgActive" />
<AddNewData newDataLabel="新增判断题" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="30rem"
max-height="30rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="stem">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchStemKeyRef"
holderText="输入题干以搜索相关数据"
@search="judgmentMsg"
/>
<div>
<el-text class="mx-1"> 题干 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.stem }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="answer">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectAnswerSearchRef"
holderText="选择答案以搜索相关数据"
:options="answerOptions"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 答案 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(answerOptions, scope.row.answer)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(answerOptions, scope.row.answer) }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="difficulty">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectDifficultySearchRef"
holderText="选择难度以搜索相关数据"
:options="difficultyOptions"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 难度 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(difficultyOptions, scope.row.difficulty)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(difficultyOptions, scope.row.difficulty) }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="judgmentMsgActive"
@sizeChangeBack="changePage"
@search="judgmentMsg"
/>
</div>
</el-card>
<AddJudgmentForm
ref="addJudgmentFormRef"
title="新增判断题"
:addData="addData"
:createdAt="null"
@reloadData="judgmentMsg"
/>
<EditJudgmentForm
ref="editJudgmentFormRef"
operateLabel="编辑判断题"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除判断题"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出判断题"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../../operate/tabelTools.vue";
import AddNewData from "../../operate/addNewData.vue";
import TabelSelect from "../../operate/tabelSelect.vue";
import SearchKey from "../../search/searchKey.vue";
import SelectSearch from "../../search/selectSearch.vue";
import Pagination from "../../search/pagination.vue";
import AddJudgmentForm from "../../addNewData/trainindTopic/judgment/judgmentForm.vue";
import EditJudgmentForm from "../../editData/trainindTopic/judgment.vue";
import PasswordOauth from "../../operate/passwordOauth.vue";
import DownloadData from "../../operate/downloadData.vue";
import judgmentMsgActiveStore from "../../../stores/trainindTopic/judgment.js";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes.js";
import { downloadData } from "../../../utils/downloadData.js";
import { judgment } from "../../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const judgmentMsgActive = judgmentMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑判断题",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除判断题",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchStemKeyRef = ref(null);
const SelectAnswerSearchRef = ref(null);
const SelectDifficultySearchRef = ref(null);
const PaginationRef = ref(null);
const addJudgmentFormRef = ref(null);
const editJudgmentFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
stem: "",
answer: "",
difficulty: "",
});
const sortOptions = reactive({});
const tableData = ref([]);
const answerOptions = [
{
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;",
},
];
const difficultyOptions = [
{
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 singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
answerOptions,
difficultyOptions,
});
const editData = reactive({
answerOptions,
difficultyOptions,
});
const showAddForm = () => {
addJudgmentFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
judgmentMsgActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
judgmentMsgActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
judgmentMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const judgmentMsg = () => {
judgment(searchOptions, "stem", SearchStemKeyRef.value, "searchMsg");
judgment(searchOptions, "answer", SelectAnswerSearchRef.value, "searchMsg");
judgment(searchOptions, "difficulty", SelectDifficultySearchRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showJudgment, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.judgmentMsg;
PaginationRef.value.dataCount = res.data.count;
judgmentMsgActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
judgmentMsgActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.stem == element.stem) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.stem == element.stem) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editJudgmentFormRef.value.model = EditSelectRef.value.model;
editJudgmentFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
editJudgmentFormRef.value.ruleForm.stem = singleData.stem;
answerOptions.forEach((item) => {
if (item.value == singleData.answer) {
editJudgmentFormRef.value.ruleForm.answer = item.label;
}
});
difficultyOptions.forEach((item) => {
if (item.value == singleData.difficulty) {
editJudgmentFormRef.value.ruleForm.difficulty = item.label;
}
});
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editJudgment;
model = EditSelectRef.value.model;
password = editJudgmentFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteJudgment;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
data.updateMsg = { ...editJudgmentFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
judgmentMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
answerOptions.forEach((element) => {
if (element.value == item["answer"]) {
item["answer"] = element.label;
}
});
difficultyOptions.forEach((element) => {
if (element.value == item["difficulty"]) {
item["difficulty"] = element.label;
}
});
});
const dataHeaders = ["题干", "答案", "难度"];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadJudgmentData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ judgmentMsg });
watch(searchOptions, (value) => {
judgmentMsgActive.searchOptions = value;
});
watch(sortOptions, (value) => {
judgmentMsgActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = judgmentMsgActive.pageCount;
dialogSearchVisible.value = judgmentMsgActive.dialogSearchVisible;
dialogSortVisible.value = judgmentMsgActive.dialogSortVisible;
dialogOperationVisible.value = judgmentMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
judgmentMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.stem = judgmentMsgActive.searchOptions.stem;
searchOptions.answer = judgmentMsgActive.searchOptions.answer;
searchOptions.difficulty = judgmentMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
judgmentMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchStemKeyRef.value.searchMsg = judgmentMsgActive.searchOptions.stem;
SelectAnswerSearchRef.value.searchMsg = judgmentMsgActive.searchOptions.answer;
SelectDifficultySearchRef.value.searchMsg =
judgmentMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,695 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="multipleMsgActive" />
<AddNewData newDataLabel="新增多选题" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="30rem"
max-height="30rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="stem">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchStemKeyRef"
holderText="输入题干以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 题干 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.stem }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_a">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsAKeyRef"
holderText="输入选项A以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 选项A </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_a }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_b">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsBKeyRef"
holderText="输入选项B以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 选项B </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_b }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_c">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsCKeyRef"
holderText="输入选项C以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 选项C </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_c }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_d">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsDKeyRef"
holderText="输入选项D以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 选项D </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_d }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_e">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsEKeyRef"
holderText="输入选项E以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 选项E </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_e }}
</div>
</template>
</el-table-column>
<el-table-column min-width="160" column-key="answer">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchAnswerKeyRef"
holderText="输入答案以搜索相关数据"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 答案 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.answer }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="difficulty">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectDifficultySearchRef"
holderText="选择难度以搜索相关数据"
:options="difficultyOptions"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 难度 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(difficultyOptions, scope.row.difficulty)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(difficultyOptions, scope.row.difficulty) }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="multipleMsgActive"
@sizeChangeBack="changePage"
@search="multipleMsg"
/>
</div>
</el-card>
<AddMultipleForm
ref="addMultipleFormRef"
title="新增多选题"
:addData="addData"
:createdAt="null"
@reloadData="multipleMsg"
/>
<EditMultipleForm
ref="editMultipleFormRef"
operateLabel="编辑多选题"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除多选题"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出多选题"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../../operate/tabelTools.vue";
import AddNewData from "../../operate/addNewData.vue";
import TabelSelect from "../../operate/tabelSelect.vue";
import SearchKey from "../../search/searchKey.vue";
import Pagination from "../../search/pagination.vue";
import AddMultipleForm from "../../addNewData/trainindTopic/multiple/multipleForm.vue";
import EditMultipleForm from "../../editData/trainindTopic/multiple.vue";
import PasswordOauth from "../../operate/passwordOauth.vue";
import DownloadData from "../../operate/downloadData.vue";
import multipleMsgActiveStore from "../../../stores/trainindTopic/multiple.js";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes.js";
import { downloadData } from "../../../utils/downloadData.js";
import { judgment } from "../../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const multipleMsgActive = multipleMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑多选题",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除多选题",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchStemKeyRef = ref(null);
const SearchOptionsAKeyRef = ref(null);
const SearchOptionsBKeyRef = ref(null);
const SearchOptionsCKeyRef = ref(null);
const SearchOptionsDKeyRef = ref(null);
const SearchOptionsEKeyRef = ref(null);
const SearchAnswerKeyRef = ref(null);
const SelectDifficultySearchRef = ref(null);
const PaginationRef = ref(null);
const addMultipleFormRef = ref(null);
const editMultipleFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
stem: "",
options_a: "",
options_b: "",
options_c: "",
options_d: "",
options_e: "",
answer: "",
difficulty: "",
});
const sortOptions = reactive({});
const tableData = ref([]);
const difficultyOptions = [
{
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 singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
difficultyOptions,
});
const editData = reactive({
difficultyOptions,
});
const showAddForm = () => {
addMultipleFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
multipleMsgActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
multipleMsgActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
multipleMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const multipleMsg = () => {
judgment(searchOptions, "stem", SearchStemKeyRef.value, "searchMsg");
judgment(searchOptions, "options_a", SearchOptionsAKeyRef.value, "searchMsg");
judgment(searchOptions, "options_b", SearchOptionsBKeyRef.value, "searchMsg");
judgment(searchOptions, "options_c", SearchOptionsCKeyRef.value, "searchMsg");
judgment(searchOptions, "options_d", SearchOptionsDKeyRef.value, "searchMsg");
judgment(searchOptions, "options_e", SearchOptionsEKeyRef.value, "searchMsg");
judgment(searchOptions, "answer", SearchAnswerKeyRef.value, "searchMsg");
judgment(searchOptions, "difficulty", SelectDifficultySearchRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showMultiple, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.multipleMsg;
PaginationRef.value.dataCount = res.data.count;
multipleMsgActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
multipleMsgActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.stem == element.stem) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.stem == element.stem) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editMultipleFormRef.value.model = EditSelectRef.value.model;
editMultipleFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
editMultipleFormRef.value.ruleForm.stem = singleData.stem;
editMultipleFormRef.value.ruleForm.options_a = singleData.options_a;
editMultipleFormRef.value.ruleForm.options_b = singleData.options_b;
editMultipleFormRef.value.ruleForm.options_c = singleData.options_c;
editMultipleFormRef.value.ruleForm.options_d = singleData.options_d;
editMultipleFormRef.value.ruleForm.options_e = singleData.options_e;
editMultipleFormRef.value.ruleForm.answer = singleData.answer;
difficultyOptions.forEach((item) => {
if (item.value == singleData.difficulty) {
editMultipleFormRef.value.ruleForm.difficulty = item.label;
}
});
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editMultiple;
model = EditSelectRef.value.model;
password = editMultipleFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteMultiple;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
data.updateMsg = { ...editMultipleFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
multipleMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
difficultyOptions.forEach((element) => {
if (element.value == item["difficulty"]) {
item["difficulty"] = element.label;
}
});
});
const dataHeaders = [
"题干",
"选项A",
"选项B",
"选项C",
"选项D",
"选项E",
"答案",
"难度",
];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadMultipleData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ multipleMsg });
watch(searchOptions, (value) => {
multipleMsgActive.searchOptions = value;
});
watch(sortOptions, (value) => {
multipleMsgActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = multipleMsgActive.pageCount;
dialogSearchVisible.value = multipleMsgActive.dialogSearchVisible;
dialogSortVisible.value = multipleMsgActive.dialogSortVisible;
dialogOperationVisible.value = multipleMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
multipleMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.stem = multipleMsgActive.searchOptions.stem;
searchOptions.options_a = multipleMsgActive.searchOptions.options_a;
searchOptions.options_b = multipleMsgActive.searchOptions.options_b;
searchOptions.options_c = multipleMsgActive.searchOptions.options_c;
searchOptions.options_d = multipleMsgActive.searchOptions.options_d;
searchOptions.options_e = multipleMsgActive.searchOptions.options_e;
searchOptions.answer = multipleMsgActive.searchOptions.answer;
searchOptions.difficulty = multipleMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
multipleMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchStemKeyRef.value.searchMsg = multipleMsgActive.searchOptions.stem;
SearchOptionsAKeyRef.value.searchMsg = multipleMsgActive.searchOptions.options_a;
SearchOptionsBKeyRef.value.searchMsg = multipleMsgActive.searchOptions.options_b;
SearchOptionsCKeyRef.value.searchMsg = multipleMsgActive.searchOptions.options_c;
SearchOptionsDKeyRef.value.searchMsg = multipleMsgActive.searchOptions.options_d;
SearchOptionsEKeyRef.value.searchMsg = multipleMsgActive.searchOptions.options_e;
SearchAnswerKeyRef.value.searchMsg = multipleMsgActive.searchOptions.answer;
SelectDifficultySearchRef.value.searchMsg =
multipleMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,659 @@
<template>
<div>
<!-- 表格 -->
<el-card>
<div class="flex justify-center flex-wrap">
<TabelTools @showTools="showTools" :dialogObj="radioMsgActive" />
<AddNewData newDataLabel="新增单选题" @showAddForm="showAddForm" />
<el-table
ref="tableRef"
:data="tableData"
style="width: 100%"
height="30rem"
max-height="30rem"
table-layout="auto"
scrollbar-always-on
@selection-change="handleSelectionChange"
>
<el-table-column
v-if="dialogOperationVisible"
fixed="left"
type="selection"
min-width="40"
column-key="selection"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="EditSelectRef"
:holderText="editSelectHold"
singleColor="primary"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="edit"
@showComfirmDialog="showComfirmDialog"
/>
<TabelSelect
v-if="dialogOperationVisible"
ref="DelSelectRef"
:holderText="delSelectHold"
singleColor="danger"
multipleColor="warning"
allColor="danger"
singleSize="small"
multipleSize="small"
allSize="small"
:singlePlain="true"
:multiplePlain="false"
:allPlain="false"
operateType="del"
@showComfirmDialog="showComfirmDialog"
/>
<el-table-column min-width="200" column-key="stem">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchStemKeyRef"
holderText="输入题干以搜索相关数据"
@search="radioMsg"
/>
<div>
<el-text class="mx-1"> 题干 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.stem }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_a">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsAKeyRef"
holderText="输入选项A以搜索相关数据"
@search="radioMsg"
/>
<div>
<el-text class="mx-1"> 选项A </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_a }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_b">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsBKeyRef"
holderText="输入选项B以搜索相关数据"
@search="radioMsg"
/>
<div>
<el-text class="mx-1"> 选项B </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_b }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_c">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsCKeyRef"
holderText="输入选项C以搜索相关数据"
@search="radioMsg"
/>
<div>
<el-text class="mx-1"> 选项C </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_c }}
</div>
</template>
</el-table-column>
<el-table-column min-width="200" column-key="options_d">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchOptionsDKeyRef"
holderText="输入选项D以搜索相关数据"
@search="radioMsg"
/>
<div>
<el-text class="mx-1"> 选项D </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.options_d }}
</div>
</template>
</el-table-column>
<el-table-column min-width="160" column-key="answer">
<template #header>
<div>
<SearchKey
v-if="dialogSearchVisible"
ref="SearchAnswerKeyRef"
holderText="输入答案以搜索相关数据"
@search="radioMsg"
/>
<div>
<el-text class="mx-1"> 答案 </el-text>
</div>
</div>
</template>
<template #default="scope">
<div class="flex justify-start items-center text-base">
{{ scope.row.answer }}
</div>
</template>
</el-table-column>
<el-table-column min-width="100" column-key="difficulty">
<template #header>
<SelectSearch
v-if="dialogSearchVisible"
ref="SelectDifficultySearchRef"
holderText="选择难度以搜索相关数据"
:options="difficultyOptions"
@search="multipleMsg"
/>
<div>
<el-text class="mx-1"> 难度 </el-text>
</div>
</template>
<template #default="scope">
<div
:style="styleShunt(difficultyOptions, scope.row.difficulty)"
class="flex justify-center items-center text-base"
>
{{ labelShunt(difficultyOptions, scope.row.difficulty) }}
</div>
</template>
</el-table-column>
</el-table>
<!-- 表格 -->
<Pagination
ref="PaginationRef"
:active="radioMsgActive"
@sizeChangeBack="changePage"
@search="radioMsg"
/>
</div>
</el-card>
<AddRadioForm
ref="addRadioFormRef"
title="新增单选题"
:addData="addData"
:createdAt="null"
@reloadData="radioMsg"
/>
<EditRadioForm
ref="editRadioFormRef"
operateLabel="编辑单选题"
:editData="editData"
:createdAt="null"
@requestOperate="requestOperate"
/>
<PasswordOauth
ref="delPasswordOauthRef"
operateLabel="删除单选题"
@requestOperate="requestOperate"
/>
<DownloadData
ref="downloadDataRef"
operateLabel="导出单选题"
@downloadData="executeDownloadData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted, nextTick } from "vue";
import { ElMessage } from "element-plus";
import TabelTools from "../../operate/tabelTools.vue";
import AddNewData from "../../operate/addNewData.vue";
import TabelSelect from "../../operate/tabelSelect.vue";
import SearchKey from "../../search/searchKey.vue";
import Pagination from "../../search/pagination.vue";
import AddRadioForm from "../../addNewData/trainindTopic/radio/radioForm.vue";
import EditRadioForm from "../../editData/trainindTopic/radio.vue";
import PasswordOauth from "../../operate/passwordOauth.vue";
import DownloadData from "../../operate/downloadData.vue";
import radioMsgActiveStore from "../../../stores/trainindTopic/radio.js";
import $http from "../../../api/index";
import $url from "../../../api/url";
import { encrypt } from "../../../utils/aes.js";
import { downloadData } from "../../../utils/downloadData.js";
import { judgment } from "../../../utils/judgment.js";
import { hasNullOrEmptyPropertyWithTypeof } from "../../../utils/hasNullOrEmptyPropertyWithTypeof.js";
const radioMsgActive = radioMsgActiveStore();
const editSelectHold = {
single: "编辑",
multiple: "编辑选中",
all: "编辑所有",
label: "编辑单选题",
};
const delSelectHold = {
single: "删除",
multiple: "删除选中",
all: "删除所有",
label: "删除单选题",
};
const EditSelectRef = ref(null);
const DelSelectRef = ref(null);
const SearchStemKeyRef = ref(null);
const SearchOptionsAKeyRef = ref(null);
const SearchOptionsBKeyRef = ref(null);
const SearchOptionsCKeyRef = ref(null);
const SearchOptionsDKeyRef = ref(null);
const SearchAnswerKeyRef = ref(null);
const SelectDifficultySearchRef = ref(null);
const PaginationRef = ref(null);
const addRadioFormRef = ref(null);
const editRadioFormRef = ref(null);
const delPasswordOauthRef = ref(null);
const downloadDataRef = ref(null);
const dialogSearchVisible = ref(false);
const dialogSortVisible = ref(false);
const dialogOperationVisible = ref(false);
const tableRef = ref();
const offsetOff = ref(true);
const pageCount = ref(0);
const operate = ref("edit");
const searchOptions = reactive({
stem: "",
options_a: "",
options_b: "",
options_c: "",
options_d: "",
answer: "",
difficulty: "",
});
const sortOptions = reactive({});
const tableData = ref([]);
const difficultyOptions = [
{
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 singleData = reactive({});
let operateData = reactive({});
let operateDataClone = reactive({});
const addData = reactive({
difficultyOptions,
});
const editData = reactive({
difficultyOptions,
});
const showAddForm = () => {
addRadioFormRef.value.dialogVisible = true;
};
const styleShunt = (options, data) => {
let nowStyle;
options.forEach((item) => {
if (data == item.value) {
nowStyle = item.style;
}
});
return nowStyle;
};
const labelShunt = (options, data) => {
let nowLabel;
options.forEach((item) => {
if (data == item.value) {
nowLabel = item.label;
}
});
return nowLabel;
};
const showTools = (operateType) => {
switch (operateType) {
case "search":
dialogSearchVisible.value = !dialogSearchVisible.value;
radioMsgActive.dialogSearchVisible = dialogSearchVisible.value;
break;
case "sort":
dialogSortVisible.value = !dialogSortVisible.value;
radioMsgActive.dialogSortVisible = dialogSortVisible.value;
break;
case "operation":
dialogOperationVisible.value = !dialogOperationVisible.value;
radioMsgActive.dialogOperationVisible = dialogOperationVisible.value;
break;
case "downloadData":
downloadDataRef.value.downloadDataDialogVisible = true;
break;
default:
break;
}
};
const resetOffset = () => {
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
PaginationRef.value.handleCurrentChange(1);
offsetOff.value = false;
}
} else {
offsetOff.value = true;
}
};
const radioMsg = () => {
judgment(searchOptions, "stem", SearchStemKeyRef.value, "searchMsg");
judgment(searchOptions, "options_a", SearchOptionsAKeyRef.value, "searchMsg");
judgment(searchOptions, "options_b", SearchOptionsBKeyRef.value, "searchMsg");
judgment(searchOptions, "options_c", SearchOptionsCKeyRef.value, "searchMsg");
judgment(searchOptions, "options_d", SearchOptionsDKeyRef.value, "searchMsg");
judgment(searchOptions, "answer", SearchAnswerKeyRef.value, "searchMsg");
judgment(searchOptions, "difficulty", SelectDifficultySearchRef.value, "searchMsg");
resetOffset();
const data = {
offset: PaginationRef.value == null ? 1 : PaginationRef.value.offset,
limit: PaginationRef.value == null ? 10 : PaginationRef.value.limit,
searchOptions: searchOptions,
sortOptions: sortOptions,
};
$http.post($url.showRadio, data).then((res) => {
if (res.data.code === 200) {
tableData.value = res.data.radioMsg;
PaginationRef.value.dataCount = res.data.count;
radioMsgActive.dataCount = PaginationRef.value.dataCount;
pageCount.value = Math.ceil(
PaginationRef.value.dataCount / PaginationRef.value.limit
);
radioMsgActive.pageCount = pageCount.value;
if (!operateData[PaginationRef.value.offset]) return;
operateData[PaginationRef.value.offset].forEach((element) => {
tableData.value.forEach((item) => {
if (item.stem == element.stem) {
nextTick(() => {
tableRef.value.toggleRowSelection(item, true);
});
}
});
});
} else {
tableData.value = [];
// ElMessage.error("未查找到相关数据!");
}
});
};
const changePage = (val) => {
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
operateData = {};
for (let i = 0; i < pageCount.value; i++) {
operateData[i + 1] = [];
for (const key in operateDataClone) {
operateDataClone[key].forEach((element) => {
// 判断数据在第几页
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
operateData[i + 1].push(element);
}
});
}
}
};
const handleSelectionChange = (val) => {
operateData[PaginationRef.value.offset] = val;
tableData.value.forEach((item, index) => {
operateData[PaginationRef.value.offset].forEach((element) => {
if (item.stem == element.stem) {
// 数据在表格中的位置
element.location =
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
}
});
});
operateDataClone = JSON.parse(JSON.stringify(operateData));
};
const showComfirmDialog = (singleMsg) => {
singleData = { ...singleMsg.data };
operate.value = singleMsg.operateType;
if (singleMsg.operateType == "edit") {
editRadioFormRef.value.model = EditSelectRef.value.model;
editRadioFormRef.value.operateDialogVisible = true;
if (EditSelectRef.value.model == "single") {
editRadioFormRef.value.ruleForm.stem = singleData.stem;
editRadioFormRef.value.ruleForm.options_a = singleData.options_a;
editRadioFormRef.value.ruleForm.options_b = singleData.options_b;
editRadioFormRef.value.ruleForm.options_c = singleData.options_c;
editRadioFormRef.value.ruleForm.options_d = singleData.options_d;
editRadioFormRef.value.ruleForm.answer = singleData.answer;
difficultyOptions.forEach((item) => {
if (item.value == singleData.difficulty) {
editRadioFormRef.value.ruleForm.difficulty = item.label;
}
});
}
}
if (singleMsg.operateType == "del") {
delPasswordOauthRef.value.operateDialogVisible = true;
}
};
const requestOperate = () => {
let operateType, url, model, password;
if (operate.value == "edit") {
operateType = "编辑";
url = $url.editRadio;
model = EditSelectRef.value.model;
password = editRadioFormRef.value.password;
} else if (operate.value == "del") {
operateType = "删除";
url = $url.deleteRadio;
model = DelSelectRef.value.model;
password = delPasswordOauthRef.value.ruleForm.password;
}
const data = {
model: encrypt(model),
password: encrypt(password),
requestMsg:
model == "single" ? singleData : model == "multiple" ? operateDataClone : "",
};
if (operate.value == "edit") {
data.updateMsg = { ...editRadioFormRef.value.ruleForm };
for (const element in data.updateMsg) {
if (data.updateMsg[element] == "") {
delete data.updateMsg[element];
}
}
}
$http.post(url, data).then((res) => {
if (res.data.code === 200) {
tableRef.value.clearSelection();
operateData = {};
operateDataClone = {};
ElMessage({
message: `${operateType}成功!`,
type: "success",
});
radioMsg();
} else {
ElMessage.error(`${operateType}失败!`);
}
});
};
const executeDownloadData = (form) => {
// 根据数据量大小来决定在前端还是后端导出数据
if (
tableData.value == [] ||
tableData.value == undefined ||
PaginationRef.value.dataCount == 0 ||
PaginationRef.value.dataCount == null
) {
ElMessage.error("暂无数据!");
return;
}
let dataClone = JSON.parse(JSON.stringify(tableData.value));
dataClone.forEach((item) => {
difficultyOptions.forEach((element) => {
if (element.value == item["difficulty"]) {
item["difficulty"] = element.label;
}
});
});
const dataHeaders = ["题干", "选项A", "选项B", "选项C", "选项D", "答案", "难度"];
if (PaginationRef.value.dataCount <= PaginationRef.value.limit) {
downloadData(form, dataClone, dataHeaders);
} else {
const data = {
form,
searchMsg: {
searchOptions: searchOptions,
sortOptions: sortOptions,
},
dataHeaders,
};
$http.post($url.downloadRadioData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
}
};
defineExpose({ radioMsg });
watch(searchOptions, (value) => {
radioMsgActive.searchOptions = value;
});
watch(sortOptions, (value) => {
radioMsgActive.sortOptions = value;
});
onMounted(() => {
pageCount.value = radioMsgActive.pageCount;
dialogSearchVisible.value = radioMsgActive.dialogSearchVisible;
dialogSortVisible.value = radioMsgActive.dialogSortVisible;
dialogOperationVisible.value = radioMsgActive.dialogOperationVisible;
downloadDataRef.value.downloadDataDialogVisible =
radioMsgActive.downloadDataDialogVisible;
if (dialogSearchVisible.value) {
searchOptions.stem = radioMsgActive.searchOptions.stem;
searchOptions.options_a = radioMsgActive.searchOptions.options_a;
searchOptions.options_b = radioMsgActive.searchOptions.options_b;
searchOptions.options_c = radioMsgActive.searchOptions.options_c;
searchOptions.options_d = radioMsgActive.searchOptions.options_d;
searchOptions.answer = radioMsgActive.searchOptions.answer;
searchOptions.difficulty = radioMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
setTimeout(() => {
radioMsg();
}, 200);
setTimeout(() => {
if (dialogSearchVisible.value) {
SearchStemKeyRef.value.searchMsg = radioMsgActive.searchOptions.stem;
SearchOptionsAKeyRef.value.searchMsg = radioMsgActive.searchOptions.options_a;
SearchOptionsBKeyRef.value.searchMsg = radioMsgActive.searchOptions.options_b;
SearchOptionsCKeyRef.value.searchMsg = radioMsgActive.searchOptions.options_c;
SearchOptionsDKeyRef.value.searchMsg = radioMsgActive.searchOptions.options_d;
SearchAnswerKeyRef.value.searchMsg = radioMsgActive.searchOptions.answer;
SelectDifficultySearchRef.value.searchMsg = radioMsgActive.searchOptions.difficulty;
}
if (dialogSortVisible.value) {
}
if (dialogOperationVisible.value) {
}
if (downloadDataRef.value.downloadDataDialogVisible) {
}
}, 0);
});
</script>
<style scoped></style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
<template>
<div class="relative top-0 right-0 z-10">
<div class="absolute top-0 right-0" style="font-size: 1rem">
<el-button
style="font-weight: bold"
type="primary"
size="small"
plain
@click="showAddForm"
>
{{ newDataLabel }}
</el-button>
</div>
</div>
</template>
<script setup>
const props = defineProps({
newDataLabel: String,
});
const emit = defineEmits(["showAddForm"]);
const showAddForm = () => {
emit("showAddForm");
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,167 @@
<template>
<el-dialog
v-model="downloadDataDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
{{ operateLabel }}
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto text-center"
>
<el-text class="mx-1" size="small" type="danger">
如果需要导出符合一定条件的数据,请先使用表格工具来对表格进行操作,然后再进行数据导出。
</el-text>
<el-form-item class="mx-1 font-bold" label="工作表名称" prop="sheetName">
<el-input
clearable
placeholder="请输入工作表名称"
autocomplete="off"
v-model="ruleForm.sheetName"
@input="filter(ruleForm.sheetName, 'sheetName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件名称" prop="fileName">
<el-input
clearable
placeholder="请输入文件名称"
autocomplete="off"
v-model="ruleForm.fileName"
@input="filter(ruleForm.fileName, 'fileName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件类型" prop="extensionType">
<el-select
clearable
size="small"
placeholder="请选择文件类型"
autocomplete="off"
v-model="ruleForm.extensionType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in extensionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
const downloadDataDialogVisible = ref(false);
const ruleFormRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
sheetName: "",
fileName: "",
extensionType: "",
});
const extensionTypeOptions = [
{
value: "0",
label: "xlsx文件(推荐)",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "xls文件",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
{
value: "2",
label: "csv文件(数据库导入专用)",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);",
},
];
const rules = reactive({
sheetName: [{ required: true, message: "请输入工作表名称", trigger: "blur" }],
fileName: [{ required: true, message: "请输入文件名称", trigger: "blur" }],
extensionType: [{ required: true, message: "请选择文件类型", trigger: "blur" }],
});
const props = defineProps({
operateLabel: String,
});
const emit = defineEmits(["downloadData"]);
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const resetCheckForm = (formEl) => {
downloadDataDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
formElClone.value = JSON.parse(JSON.stringify(ruleForm));
emit("downloadData", formElClone.value);
downloadDataDialogVisible.value = false;
formEl.resetFields();
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ downloadDataDialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,407 @@
<template>
<el-dialog
v-model="applicationDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
<!-- {{ applicationLabel }} -->
导出报名用户
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="工作单位" prop="workplace">
<el-autocomplete
:fetch-suggestions="searchWorkplace"
clearable
placeholder="请输入工作单位"
v-model="ruleForm.workplace"
@input="filter(ruleForm.workplace, 'workplace')"
/>
</el-form-item>
<el-form-item label="注册时间段" prop="created_at">
<el-date-picker
v-model="ruleForm.created_at"
type="datetimerange"
size="small"
range-separator="到"
start-placeholder="开始时间"
end-placeholder="结束时间"
style="width: 100%; font-size: 1rem"
popper-class="DateTimePicker"
@visible-change="handleVisibleChange"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="exam_uid">
<el-autocomplete
:fetch-suggestions="searchExamUid"
clearable
placeholder="请输入考试uid"
v-model="ruleForm.exam_uid"
@input="filter(ruleForm.exam_uid, 'exam_uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名来源" prop="source">
<el-input
clearable
placeholder="请输入报名来源"
autocomplete="off"
v-model="ruleForm.source"
@input="filter(ruleForm.source, 'source')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名结果说明" prop="result">
<el-input
clearable
placeholder="请输入报名结果说明"
autocomplete="off"
v-model="ruleForm.result"
@input="filter(ruleForm.result, 'result')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="工作表名称" prop="sheetName">
<el-input
clearable
placeholder="请输入工作表名称"
autocomplete="off"
v-model="ruleForm.sheetName"
@input="filter(ruleForm.sheetName, 'sheetName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件名称" prop="fileName">
<el-input
clearable
placeholder="请输入文件名称"
autocomplete="off"
v-model="ruleForm.fileName"
@input="filter(ruleForm.fileName, 'fileName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件类型" prop="extensionType">
<el-select
clearable
size="small"
placeholder="请选择文件类型"
autocomplete="off"
v-model="ruleForm.extensionType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in extensionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
// const props = defineProps({
// applicationLabel: String,
// });
// const emit = defineEmits(["requestOperate"]);
const applicationDialogVisible = ref(false);
const ruleFormRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
workplace: "",
created_at: "",
exam_uid: "",
source: "",
result: "",
sheetName: "",
fileName: "",
extensionType: "",
});
const extensionTypeOptions = [
{
value: "0",
label: "xlsx文件(推荐)",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "xls文件",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
{
value: "2",
label: "csv文件(数据库导入专用)",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);",
},
];
const searchWorkplace = (value, cb) => {
const data = {
workplace: value,
};
$http.post($url.searchWorkplace, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const searchExamUid = (value, cb) => {
const data = {
exam_uid: value,
};
$http.post($url.searchExamUid, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateWorkplace = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入工作单位"));
} else {
const data = {
workplace: value,
};
$http.post($url.findWorkplace, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该工作单位不存在!"));
}
});
}
};
const validateExamUid = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考试uid"));
} else {
const data = {
exam_uid: value,
};
$http.post($url.findExamUid, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考试uid不存在!"));
}
});
}
};
const rules = reactive({
workplace: [{ required: true, validator: validateWorkplace, trigger: "blur" }],
created_at: [{ required: true, message: "请选择注册时间段", trigger: "change" }],
exam_uid: [{ required: true, validator: validateExamUid, trigger: "blur" }],
source: [{ required: true, message: "请输入报名来源", trigger: "blur" }],
result: [{ required: true, message: "请输入报名结果说明", trigger: "blur" }],
sheetName: [{ required: true, message: "请输入工作表名称", trigger: "blur" }],
fileName: [{ required: true, message: "请输入文件名称", trigger: "blur" }],
extensionType: [{ required: true, message: "请选择文件类型", trigger: "blur" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const handleClose = () => {
applicationDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
};
const resetCheckForm = (formEl) => {
applicationDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
};
const applicationOperate = (form, searchMsg, importForm) => {
const dataHeaders = [
"考生身份证号",
"考生姓名",
"考试uid",
"报名来源",
"报名状态",
"缴费状态",
"报名结果说明",
];
const data = {
form,
searchMsg,
importForm,
dataHeaders,
};
$http.post($url.importApplicationData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
let form = {};
form.sheetName = ruleForm.sheetName;
form.fileName = ruleForm.fileName;
form.extensionType = ruleForm.extensionType;
let searchMsg = {
searchOptions: {},
sortOptions: {},
};
searchMsg.searchOptions.workplace = ruleForm.workplace;
searchMsg.searchOptions.created_at = ruleForm.created_at;
let importForm = {};
importForm.exam_uid = ruleForm.exam_uid;
importForm.source = ruleForm.source;
importForm.result = ruleForm.result;
// emit("requestOperate", form, searchMsg, importForm);
applicationOperate(form, searchMsg, importForm);
applicationDialogVisible.value = false;
formEl.resetFields();
} else {
ElMessage.error("提交失败!");
}
});
};
const handleVisibleChange = () => {
setTimeout(() => {
let changeNode = window.document.getElementsByClassName("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({ applicationDialogVisible });
onMounted(() => {
handleVisibleChange();
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,284 @@
<template>
<el-dialog
v-model="examStarsDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
<!-- {{ examStarsLabel }} -->
导出考试星学员
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="exam_uid">
<el-autocomplete
:fetch-suggestions="searchExamUid"
clearable
placeholder="请输入考试uid"
minlength="10"
maxlength="10"
v-model="ruleForm.exam_uid"
@input="filter(ruleForm.exam_uid, 'exam_uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择报名状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in exportData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="工作表名称" prop="sheetName">
<el-input
clearable
placeholder="请输入工作表名称"
autocomplete="off"
v-model="ruleForm.sheetName"
@input="filter(ruleForm.sheetName, 'sheetName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件名称" prop="fileName">
<el-input
clearable
placeholder="请输入文件名称"
autocomplete="off"
v-model="ruleForm.fileName"
@input="filter(ruleForm.fileName, 'fileName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件类型" prop="extensionType">
<el-select
clearable
size="small"
placeholder="请选择文件类型"
autocomplete="off"
v-model="ruleForm.extensionType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in extensionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
const props = defineProps({
exportData: Object,
// examStarsLabel: String,
});
// const emit = defineEmits(["requestOperate"]);
const examStarsDialogVisible = ref(false);
const ruleFormRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
exam_uid: "",
status: "",
sheetName: "",
fileName: "",
extensionType: "",
});
const extensionTypeOptions = [
{
value: "0",
label: "xlsx文件(推荐)",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "xls文件",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
{
value: "2",
label: "csv文件(数据库导入专用)",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);",
},
];
const searchExamUid = (value, cb) => {
const data = {
exam_uid: value,
};
$http.post($url.searchExamUid, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateExamUid = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考试uid"));
} else {
const data = {
exam_uid: value,
};
$http.post($url.findExamUid, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考试不存在!"));
}
});
}
};
const rules = reactive({
exam_uid: [{ required: true, validator: validateExamUid, trigger: "blur" }],
status: [{ required: true, message: "请选择报名状态", trigger: "change" }],
sheetName: [{ required: true, message: "请输入工作表名称", trigger: "blur" }],
fileName: [{ required: true, message: "请输入文件名称", trigger: "blur" }],
extensionType: [{ required: true, message: "请选择文件类型", trigger: "blur" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const handleClose = () => {
examStarsDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
};
const resetCheckForm = (formEl) => {
examStarsDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
};
const examStarsOperate = (form, searchMsg, importForm) => {
const dataHeaders = ["*账号", "*密码", "*姓名", "*部门"];
const data = {
form,
searchMsg,
importForm,
dataHeaders,
};
$http.post($url.importExamStars, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
let form = {};
form.sheetName = ruleForm.sheetName;
form.fileName = ruleForm.fileName;
form.extensionType = ruleForm.extensionType;
let searchMsg = {
searchOptions: {},
sortOptions: {},
};
searchMsg.searchOptions.exam_uid = ruleForm.exam_uid;
searchMsg.searchOptions.status = ruleForm.status;
let importForm = {};
// emit("requestOperate", form, searchMsg, importForm);
examStarsOperate(form, searchMsg, importForm);
examStarsDialogVisible.value = false;
formEl.resetFields();
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ examStarsDialogVisible });
</script>
<style scoped></style>

View File

@@ -0,0 +1,390 @@
<template>
<el-dialog
v-model="gradesDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
<!-- {{ gradesLabel }} -->
生成考试成绩导入表
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="工作表名称" prop="sheetName">
<el-input
clearable
placeholder="请输入工作表名称"
autocomplete="off"
v-model="ruleForm.sheetName"
@input="filter(ruleForm.sheetName, 'sheetName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件名称" prop="fileName">
<el-input
clearable
placeholder="请输入文件名称"
autocomplete="off"
v-model="ruleForm.fileName"
@input="filter(ruleForm.fileName, 'fileName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件类型" prop="extensionType">
<el-select
clearable
size="small"
placeholder="请选择文件类型"
autocomplete="off"
v-model="ruleForm.extensionType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in extensionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="数据类型" prop="dataType">
<el-select
clearable
size="small"
placeholder="请选择文件类型"
autocomplete="off"
v-model="ruleForm.dataType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in dataTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="ruleForm.dataType == '1'"
class="mx-1 font-bold"
label="及格分数"
prop="passingScore"
>
<el-input
clearable
placeholder="请输入及格分数"
autocomplete="off"
v-model="ruleForm.passingScore"
@input="filter(ruleForm.passingScore, 'passingScore')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="成绩录入类型" prop="gradesType">
<el-select
clearable
size="small"
placeholder="请选择成绩录入类型"
autocomplete="off"
v-model="ruleForm.gradesType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in gradesTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<UploadFile
ref="uploadFileRef"
:options="options"
:relatedRules="relatedRules"
:relatedOptions="relatedOptions"
/>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
import UploadFile from "../uploadFile.vue";
const gradesDialogVisible = ref(false);
const ruleFormRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
sheetName: "",
fileName: "",
extensionType: "",
dataType: "",
passingScore: "",
gradesType: "",
});
const extensionTypeOptions = [
{
value: "0",
label: "xlsx文件(推荐)",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "xls文件",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
{
value: "2",
label: "csv文件(数据库导入专用)",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);",
},
];
const dataTypeOptions = [
{
value: "0",
label: "仅及格数据",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "全部数据",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
];
const gradesTypeOptions = [
{
value: "0",
label: "普通成绩录入",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "补考成绩录入",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
];
const uploadFileRef = ref(null);
const formClone = ref([]);
const options = {
limit: 1,
accept: ".xlsx",
action: "",
multiple: false,
listType: "text",
autoUpload: false,
};
const relatedRules = {
user_name: {
value: "",
label: "考生姓名",
},
identity_card: {
value: "",
label: "身份证号",
},
grades: {
value: "",
label: "考试成绩",
},
};
const relatedOptions = [
{
value: "0",
label: "考生姓名",
disabled: false,
},
{
value: "1",
label: "身份证号",
disabled: false,
},
{
value: "2",
label: "考试成绩",
disabled: false,
},
];
// const emit = defineEmits(["addGrades"]);
const rules = reactive({
sheetName: [{ required: true, message: "请输入工作表名称", trigger: "blur" }],
fileName: [{ required: true, message: "请输入文件名称", trigger: "blur" }],
extensionType: [{ required: true, message: "请选择文件类型", trigger: "blur" }],
dataType: [{ required: true, message: "请选择数据类型", trigger: "blur" }],
passingScore: [{ required: true, message: "请输入及格分数", trigger: "blur" }],
gradesType: [{ required: true, message: "请选择成绩录入类型", trigger: "blur" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const handleClose = () => {
gradesDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
};
const resetCheckForm = (formEl) => {
gradesDialogVisible.value = false;
if (formEl) {
formEl.resetFields();
}
if (uploadFileRef) {
uploadFileRef.value.handleExceed();
}
};
const gradesOperate = (form, formClone, dataType, passingScore, gradesType) => {
const dataHeaders = [
"考生姓名",
"身份证号",
"考试uid",
"参与状态",
"考试成绩",
"考试结果",
];
const data = {
form,
formClone,
dataType,
passingScore,
gradesType,
dataHeaders,
};
$http.post($url.importGradesData, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} else {
ElMessage.error("数据导出失败!");
}
});
};
const confirm = async (formEl) => {
if (!formEl || !uploadFileRef) return;
formClone.value = [];
const length = uploadFileRef.value.loadData.length;
if (length == 0) return ElMessage.error("请先上传文件!");
const flag = await uploadFileRef.value.confirm();
if (!flag) return ElMessage.error("请先选择表格关联项!");
await formEl.validate((valid) => {
if (valid) {
let form = {};
form.sheetName = ruleForm.sheetName;
form.fileName = ruleForm.fileName;
form.extensionType = ruleForm.extensionType;
ElMessage({
message: `正在上传数据中,请稍等...`,
type: "success",
});
setTimeout(() => {
uploadFileRef.value.loadData.forEach((ExcleForm) => {
ExcleForm.forEach((worksheet) => {
let context = [...worksheet];
context.shift();
context.forEach((item) => {
if (item.length != 0) {
formClone.value.push({
identity_card: item[relatedRules.identity_card.value],
user_name: item[relatedRules.user_name.value],
grades: item[relatedRules.grades.value],
});
}
});
});
});
gradesOperate(
form,
formClone.value,
ruleForm.dataType,
ruleForm.passingScore,
ruleForm.gradesType
);
gradesDialogVisible.value = false;
formEl.resetFields();
uploadFileRef.value.handleExceed();
}, 3000);
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ gradesDialogVisible, resetCheckForm, confirm, formClone });
</script>
<style scoped></style>

View File

@@ -0,0 +1,331 @@
<template>
<el-dialog
v-model="issuanceDialogVisible"
:show-close="false"
style="width: 80%"
center
align-center
:before-close="handleClose"
>
<template #header="{ titleId, titleClass }">
<div
style="font-size: 2rem"
class="w-full font-bold"
:id="titleId"
:class="titleClass"
>
<!-- {{ issuanceLabel }} -->
导出培训证书发放用户
</div>
<div
@click="resetCheckForm(ruleFormRef)"
class="bg-gray-400 hover:bg-red-600 text-3xl absolute top-0 right-0 cursor-pointer flex justify-center items-center"
>
<el-icon>
<CloseBold />
</el-icon>
</div>
</template>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
:rules="rules"
label-width="auto"
size="default"
status-icon
class="mx-auto"
>
<el-form-item class="mx-1 font-bold" label="考试uid" prop="exam_uid">
<el-autocomplete
:fetch-suggestions="searchExamUid"
clearable
placeholder="请输入考试uid"
minlength="10"
maxlength="10"
v-model="ruleForm.exam_uid"
@input="filter(ruleForm.exam_uid, 'exam_uid')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="报名状态" prop="status">
<el-select
clearable
size="small"
placeholder="请选择报名状态"
autocomplete="off"
v-model="ruleForm.status"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in exportData.statusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="工作表名称" prop="sheetName">
<el-input
clearable
placeholder="请输入工作表名称"
autocomplete="off"
v-model="ruleForm.sheetName"
@input="filter(ruleForm.sheetName, 'sheetName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件名称" prop="fileName">
<el-input
clearable
placeholder="请输入文件名称"
autocomplete="off"
v-model="ruleForm.fileName"
@input="filter(ruleForm.fileName, 'fileName')"
/>
</el-form-item>
<el-form-item class="mx-1 font-bold" label="文件类型" prop="extensionType">
<el-select
clearable
size="small"
placeholder="请选择文件类型"
autocomplete="off"
v-model="ruleForm.extensionType"
style="width: 100%; font-size: 1rem"
>
<el-option
v-for="item in extensionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
class="text-center"
:style="item.style"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button style="font-weight: bold" @click="resetCheckForm(ruleFormRef)">
取消
</el-button>
<el-button style="font-weight: bold" type="primary" @click="confirm(ruleFormRef)">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive } from "vue";
import { CloseBold } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import $http from "../../../api/index";
import $url from "../../../api/url";
const props = defineProps({
exportData: Object,
// issuanceLabel: String,
});
// const emit = defineEmits(["requestOperate"]);
const issuanceDialogVisible = ref(false);
const ruleFormRef = ref(null);
const formElClone = ref(null);
const ruleForm = reactive({
exam_uid: "",
status: "",
sheetName: "",
fileName: "",
extensionType: "",
});
const extensionTypeOptions = [
{
value: "0",
label: "xlsx文件(推荐)",
style: "color: rgb(56, 188, 248);background-color: rgba(56, 188, 248, 0.1);",
},
{
value: "1",
label: "xls文件",
style: "color: rgb(16, 184, 130);background-color: rgba(16, 184, 130, 0.1);",
},
{
value: "2",
label: "csv文件(数据库导入专用)",
style: "color: rgb(240, 70, 70);background-color: rgba(240, 70, 70, 0.1);",
},
];
const searchExamUid = (value, cb) => {
const data = {
exam_uid: value,
};
$http.post($url.searchExamUid, data).then((res) => {
if (res.data.code === 200) {
cb(res.data.data);
} else {
cb([]);
}
});
};
const validateExamUid = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入考试uid"));
} else {
const data = {
exam_uid: value,
};
$http.post($url.findExamUid, data).then((res) => {
if (res.data.code === 200) {
callback();
} else {
callback(new Error("该考试不存在!"));
}
});
}
};
const rules = reactive({
exam_uid: [{ required: true, validator: validateExamUid, trigger: "blur" }],
status: [{ required: true, message: "请选择报名状态", trigger: "change" }],
sheetName: [{ required: true, message: "请输入工作表名称", trigger: "blur" }],
fileName: [{ required: true, message: "请输入文件名称", trigger: "blur" }],
extensionType: [{ required: true, message: "请选择文件类型", trigger: "blur" }],
});
const filter = (value, label) => {
if (/[=\s"'+-]/.test(value)) {
if (ruleForm[label]) {
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
}
}
};
const handleClose = () => {
issuanceDialogVisible.value = false;
if (!ruleFormRef) return;
formElClone.value = ruleFormRef.value;
formElClone.value.resetFields();
};
const resetCheckForm = (formEl) => {
issuanceDialogVisible.value = false;
if (!formEl) return;
formEl.resetFields();
};
const issuanceOperate = (form, searchMsg, importForm) => {
const dataHeaders = [
"序号",
"姓名",
"身份证号",
"工作单位",
"通讯地址",
"联系电话",
"培训开始时间",
"培训结束时间",
"培训地点",
"考试方式",
"考试成绩",
"证书名称",
"级别",
];
const data = {
form,
searchMsg,
importForm,
dataHeaders,
};
// 导出培训证书发放用户 excel 表
$http.post($url.importIssuance, data).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "数据导出成功!",
type: "success",
});
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
setTimeout(() => {
// 导出培训证书发放用户照片文件夹
const dataPhoto = { searchMsg };
$http.post($url.importUserPhoto, dataPhoto).then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "正在下载压缩包!",
type: "success",
});
setTimeout(() => {
// 创建一个隐藏的<a>标签
const link = document.createElement("a");
link.style.display = "none";
link.href = res.data.fileUrl;
// 设置下载的文件名
link.download = res.data.fileName;
// 触发点击事件来下载文件
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
ElMessage({
message: "用户照片文件夹导出成功!",
type: "success",
});
}, 1000);
} else {
ElMessage.error("用户照片文件夹导出失败!");
}
});
}, 1000);
} else {
ElMessage.error("数据导出失败!");
}
});
};
const confirm = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (valid) {
let form = {};
form.sheetName = ruleForm.sheetName;
form.fileName = ruleForm.fileName;
form.extensionType = ruleForm.extensionType;
let searchMsg = {
searchOptions: {},
sortOptions: {},
};
searchMsg.searchOptions.exam_uid = ruleForm.exam_uid;
searchMsg.searchOptions.status = ruleForm.status;
let importForm = {};
// emit("requestOperate", form, searchMsg, importForm);
issuanceOperate(form, searchMsg, importForm);
issuanceDialogVisible.value = false;
formEl.resetFields();
} else {
ElMessage.error("提交失败!");
}
});
};
defineExpose({ issuanceDialogVisible });
</script>
<style scoped></style>

Some files were not shown because too many files have changed in this diff Show More