北斗短报文通信服务平台
30
.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# bd-short-message-platform
|
||||
|
||||
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
|
||||
```
|
||||
52
index.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="w-h-full">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>北斗启航科技教育平台</title>
|
||||
<style>
|
||||
/* 当屏幕宽度大于或等于3000px时 */
|
||||
@media (min-width: 3000px) {
|
||||
html {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度在1920px到2999px之间时 */
|
||||
@media (min-width: 1920px) and (max-width: 2999px) {
|
||||
html {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度在750px到1919px之间时 */
|
||||
@media (min-width: 750px) and (max-width: 1919px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度在600px到749px之间时 */
|
||||
@media (min-width: 600px) and (max-width: 749px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 当屏幕宽度小于600px时 */
|
||||
@media (max-width: 599px) {
|
||||
html {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="w-h-full">
|
||||
<div id="app" class="w-h-full text-white"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
8
jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
142784
package-lock.json
generated
Normal file
40
package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "bd-short-message-platform",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=7013",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^1.6.8",
|
||||
"bd-short-message-platform": "file:",
|
||||
"core-js": "^3.37.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"element-plus": "^2.7.8",
|
||||
"fingerprintjs2": "^2.1.4",
|
||||
"gbk.js": "^0.3.0",
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"socket.io-client": "^4.8.0",
|
||||
"terser": "^5.31.5",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.0",
|
||||
"zipson": "^0.2.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-legacy": "^5.4.1",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.40",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-vue-components": "^0.27.0",
|
||||
"vite": "^5.3.5"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 72 KiB |
11
src/App.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
import { RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
118
src/api/index.js
Normal file
@@ -0,0 +1,118 @@
|
||||
import axios from "axios";
|
||||
import $url from "./url";
|
||||
import router from "../router";
|
||||
import { encrypt } from "../utils/aes";
|
||||
|
||||
// 添加请求拦截器
|
||||
axios.interceptors.request.use(function (config) {
|
||||
// 在发送请求之前做些什么
|
||||
if (config.headers) {
|
||||
config.headers.uid = localStorage.data;
|
||||
if (localStorage.access_token && localStorage.access_token !== "" && localStorage.access_token !== null && localStorage.access_token !== undefined && localStorage.access_token !== NaN) {
|
||||
config.headers.authorization = localStorage.access_token;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}, function (error) {
|
||||
// 对请求错误做些什么
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 转到登录页面
|
||||
const toLogin = () => {
|
||||
localStorage.clear();
|
||||
const data = { origin_url: window.location.href };
|
||||
axios.post($url.toLogin, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
window.location.href = res.data.redirect;
|
||||
} else {
|
||||
router.push({ path: '/404page' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 添加响应拦截器
|
||||
let result = true;
|
||||
axios.interceptors.response.use(function (response) {
|
||||
// 2xx 范围内的状态码都会触发该函数。
|
||||
// 对响应数据做点什么
|
||||
if (response.data.code === 200) {
|
||||
if ("access_token" in response.data) {
|
||||
localStorage.setItem("access_token", response.data.access_token);
|
||||
}
|
||||
if ("refresh_token" in response.data) {
|
||||
localStorage.setItem("refresh_token", response.data.refresh_token);
|
||||
}
|
||||
if ("data" in response.data) {
|
||||
localStorage.setItem("data", response.data.data);
|
||||
}
|
||||
}
|
||||
// if (response.data.code === 302) {
|
||||
// localStorage.clear();
|
||||
// //转到主页
|
||||
// router.push({ path: '/main' });
|
||||
// }
|
||||
if(response.data.code === 401 && result) {
|
||||
result = false;
|
||||
setTimeout(() => {
|
||||
result = true;
|
||||
}, 1000);
|
||||
let getCode = window.location.href.indexOf('?');
|
||||
if (getCode != '-1') {
|
||||
let code = window.location.href.substring(getCode + 1);
|
||||
const data = {
|
||||
code: code,
|
||||
type: encrypt('code')
|
||||
};
|
||||
axios.post($url.getToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("access_token", res.data.access_token);
|
||||
localStorage.setItem("refresh_token", res.data.refresh_token);
|
||||
window.location.href = res.data.redirect;
|
||||
} else {
|
||||
toLogin();
|
||||
}
|
||||
});
|
||||
} else if (localStorage.refresh_token && localStorage.refresh_token !== "" && localStorage.refresh_token !== null && localStorage.refresh_token !== undefined && localStorage.refresh_token !== NaN) {
|
||||
const data = {
|
||||
code: localStorage.refresh_token,
|
||||
type: localStorage.data
|
||||
};
|
||||
axios.post($url.getToken, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
localStorage.setItem("data", res.data.data);
|
||||
localStorage.setItem("access_token", res.data.access_token);
|
||||
localStorage.setItem("refresh_token", res.data.refresh_token);
|
||||
window.location.reload();
|
||||
} else {
|
||||
toLogin();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
toLogin();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}, function (error) {
|
||||
// 超出 2xx 范围的状态码都会触发该函数。
|
||||
// 对响应错误做点什么
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
export default {
|
||||
get(url, query = {}) {
|
||||
return axios.get(url, {
|
||||
params: query
|
||||
});
|
||||
},
|
||||
post(url, body) {
|
||||
return axios.post(url, body);
|
||||
},
|
||||
put(url, body) {
|
||||
return axios.put(url, body);
|
||||
},
|
||||
del(url) {
|
||||
return axios.delete(url);
|
||||
},
|
||||
}
|
||||
40
src/api/url.js
Normal file
@@ -0,0 +1,40 @@
|
||||
export default {
|
||||
// 用于确认哪个网页在进行登录
|
||||
toLogin: "/authentication/toLogin",
|
||||
// 退出登录
|
||||
unLogin: "/authentication/unLogin",
|
||||
// 用于获取token
|
||||
getToken: "/authentication/getToken",
|
||||
// 用于验证token
|
||||
verify: "/authentication/verify",
|
||||
// 获取用户基本信息
|
||||
getUser: "/authentication/getUser",
|
||||
// 修改用户基本信息
|
||||
editUser: "/authentication/editUser",
|
||||
// B端单位管理员编辑用户基本信息
|
||||
editUserManager: "/authentication/editUserManager",
|
||||
// 修改密码
|
||||
changePassword: "/authentication/changePassword",
|
||||
// 重置密码
|
||||
resetPassword: "/authentication/resetPassword",
|
||||
// 获取卡号
|
||||
getCard: "/bdShortMessage/getCard",
|
||||
// 搜索卡号
|
||||
searchCard: "/bdShortMessage/card",
|
||||
// 绑卡
|
||||
bindCard: "/bdShortMessage/bindCard",
|
||||
// 解绑
|
||||
unBindingCard: "/bdShortMessage/unBindingCard",
|
||||
// B端单位管理员解绑
|
||||
unBindManager: "/bdShortMessage/unBindManager",
|
||||
// 获取绑定记录
|
||||
getRecord: "/bdShortMessage/getRecord",
|
||||
// 获取历史消息
|
||||
getHistoryMsg: "/bdShortMessage/getHistoryMsg",
|
||||
// 删除历史消息
|
||||
delHistoryMsg: "/bdShortMessage/delHistoryMsg",
|
||||
// 获取单位用户
|
||||
bsideManager: "/bdShortMessage/bsideManager",
|
||||
// webSocket服务端地址
|
||||
bdrdWebSocket: "/bdrdWebSocket/",
|
||||
}
|
||||
BIN
src/assets/favicon.ico
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
src/assets/img/Q&A/showType.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/img/Q&A/tabelTools.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/img/Q&A/toolsClick.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/assets/img/Q&A/toolsSelect.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/assets/img/code/changeMode.png
Normal file
|
After Width: | Height: | Size: 1006 B |
BIN
src/assets/img/code/codeGuide.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
src/assets/img/code/codeScience.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/img/code/codeType.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/img/code/fontGuide.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/img/code/showType.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
src/assets/img/code/transCode.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
src/assets/img/code/transFont.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
src/assets/img/header/headerGuide.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/img/header/headerSelect.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/assets/img/manager/operateSelect.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/img/manager/operateType.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
src/assets/img/msg/msg.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/assets/img/msg/operateSelect.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/img/msg/operateType.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
src/assets/img/msg/showMsg.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/img/xor/errorMsg.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/assets/img/xor/input.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/assets/img/xor/xor.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/assets/img/xor/xorGuide.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
1
src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
9
src/assets/main.css
Normal file
@@ -0,0 +1,9 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.w-h-full {
|
||||
@apply w-full h-full m-0 p-0 overflow-hidden;
|
||||
}
|
||||
}
|
||||
BIN
src/assets/qrcode.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
115
src/components/calculationXOR/calculationXOR.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="m-2 font-bold">
|
||||
<el-text class="mx-1 font-bold"> 校验数据 : </el-text>
|
||||
<el-input
|
||||
v-model="check"
|
||||
:style="style"
|
||||
placeholder="请输入将要校验的数据"
|
||||
clearable
|
||||
@input="handleInput"
|
||||
/>
|
||||
</div>
|
||||
<div class="m-2 font-bold">
|
||||
<el-text> 校验结果(十六进制): {{ resultHex }} </el-text>
|
||||
</div>
|
||||
<div class="m-2 font-bold">
|
||||
<el-text> 校验结果(十进制): {{ resultDec }} </el-text>
|
||||
</div>
|
||||
<div class="m-2 font-bold">
|
||||
<el-text> 校验结果(二进制): {{ resultBin }} </el-text>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<el-button style="font-weight: bold; width: 25%;" type="primary" size="small" @click="calculation">
|
||||
校验
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold; width: 25%;" type="primary" size="small" @click="clear">
|
||||
清除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const style = ref("width: 80%;");
|
||||
const check = ref("");
|
||||
const resultHex = ref("");
|
||||
const resultDec = ref("");
|
||||
const resultBin = ref("");
|
||||
|
||||
const clear = () => {
|
||||
style.value = "width: 80%";
|
||||
check.value = "";
|
||||
resultHex.value = "";
|
||||
resultBin.value = "";
|
||||
resultDec.value = "";
|
||||
};
|
||||
|
||||
// 判断字符是否为简体汉字
|
||||
const isChinese = (s) => {
|
||||
return /[\u4e00-\u9fa5]/.test(s);
|
||||
};
|
||||
|
||||
const inputFilter = (inputData) => {
|
||||
let infilter = [];
|
||||
inputData.forEach((item) => {
|
||||
if (!isChinese(item)) {
|
||||
infilter.push(item);
|
||||
}
|
||||
});
|
||||
return infilter.join("");
|
||||
};
|
||||
|
||||
const handleInput = () => {
|
||||
check.value = inputFilter(check.value.split(""));
|
||||
};
|
||||
|
||||
const checkit = (acheck) => {
|
||||
let checkArr = acheck.split("");
|
||||
let resultBox;
|
||||
checkArr.forEach((element) => {
|
||||
if (resultBox == undefined) {
|
||||
resultBox = element.charCodeAt();
|
||||
} else {
|
||||
resultBox = resultBox ^ element.charCodeAt();
|
||||
}
|
||||
});
|
||||
let toOverHex, toOverDec, toOverBin;
|
||||
toOverHex = resultBox.toString(16).toUpperCase();
|
||||
if (resultBox.toString(16).toUpperCase().length < 2) {
|
||||
toOverHex = "0" + toOverHex;
|
||||
}
|
||||
toOverDec = resultBox.toString().toUpperCase();
|
||||
if (resultBox.toString().toUpperCase().length < 2) {
|
||||
toOverDec = "0" + toOverDec;
|
||||
}
|
||||
toOverBin = resultBox.toString(2);
|
||||
if (resultBox.toString(2).length < 8) {
|
||||
for (let i = 0; i < 8 - resultBox.toString(2).length; i++) {
|
||||
toOverBin = "0" + toOverBin;
|
||||
}
|
||||
}
|
||||
return { hex: toOverHex, dec: toOverDec, bin: toOverBin };
|
||||
};
|
||||
|
||||
const calculation = () => {
|
||||
resultHex.value = "";
|
||||
resultBin.value = "";
|
||||
resultDec.value = "";
|
||||
if (check.value == "") {
|
||||
style.value = "width: 80%; border-width: 2px; border-color: rgb(239 68 68);";
|
||||
ElMessage.error("校验数据不能为空");
|
||||
} else {
|
||||
const { hex, dec, bin } = checkit(check.value);
|
||||
style.value = "width: 80%;";
|
||||
resultHex.value = hex;
|
||||
resultDec.value = dec;
|
||||
resultBin.value = bin;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
453
src/components/features/bsideManager.vue
Normal file
@@ -0,0 +1,453 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<el-card>
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<TabelTools @showTools="showTools" :dialogObj="bsideManagerActive" />
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
:data="tableData"
|
||||
row-key="account_name"
|
||||
style="width: 100%"
|
||||
max-height="32rem"
|
||||
scrollbar-always-on
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
v-if="dialogOperationVisible"
|
||||
fixed="left"
|
||||
type="selection"
|
||||
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="DefaultSelectRef"
|
||||
:holderText="defaultSelectHold"
|
||||
singleColor="danger"
|
||||
multipleColor="warning"
|
||||
allColor="danger"
|
||||
singleSize="small"
|
||||
multipleSize="small"
|
||||
allSize="small"
|
||||
:singlePlain="true"
|
||||
:multiplePlain="false"
|
||||
:allPlain="false"
|
||||
operateType="default"
|
||||
@showComfirmDialog="showComfirmDialog"
|
||||
/>
|
||||
<TabelSelect
|
||||
v-if="dialogOperationVisible"
|
||||
ref="ResetSelectRef"
|
||||
:holderText="resetSelectHold"
|
||||
singleColor="danger"
|
||||
multipleColor="warning"
|
||||
allColor="danger"
|
||||
singleSize="small"
|
||||
multipleSize="small"
|
||||
allSize="small"
|
||||
:singlePlain="true"
|
||||
:multiplePlain="false"
|
||||
:allPlain="false"
|
||||
operateType="reset"
|
||||
@showComfirmDialog="showComfirmDialog"
|
||||
/>
|
||||
<TabelSelect
|
||||
v-if="dialogOperationVisible"
|
||||
ref="unBindSelectRef"
|
||||
:holderText="bindSelectHold"
|
||||
singleColor="danger"
|
||||
multipleColor="warning"
|
||||
allColor="danger"
|
||||
singleSize="small"
|
||||
multipleSize="small"
|
||||
allSize="small"
|
||||
:singlePlain="true"
|
||||
:multiplePlain="false"
|
||||
:allPlain="false"
|
||||
operateType="unbind"
|
||||
@showComfirmDialog="showComfirmDialog"
|
||||
/>
|
||||
<el-table-column min-width="100" column-key="account_name">
|
||||
<template #header>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchAccountKeyRef"
|
||||
holderText="输入单位用户名以搜索相关数据"
|
||||
@search="bsideManager"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 单位用户名 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>{{ scope.row.account_name }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="80" column-key="user_name">
|
||||
<template #header>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchUserKeyRef"
|
||||
holderText="输入用户姓名以搜索相关数据"
|
||||
@search="bsideManager"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 用户姓名 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>{{ scope.row.user_name }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="120" column-key="phone">
|
||||
<template #header>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchPhoneKeyRef"
|
||||
holderText="输入用户手机号码以搜索相关数据"
|
||||
@search="bsideManager"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 用户手机号码 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div>{{ scope.row.phone }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 表格 -->
|
||||
<Pagination
|
||||
ref="PaginationRef"
|
||||
:active="bsideManagerActive"
|
||||
@sizeChangeBack="changePage"
|
||||
@search="bsideManager"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<EditBsideUser
|
||||
ref="editBsideUserRef"
|
||||
operateLabel="编辑信息"
|
||||
@requestOperate="requestOperate"
|
||||
/>
|
||||
<PasswordOauth
|
||||
ref="defaultPasswordOauthRef"
|
||||
operateLabel="重置账号"
|
||||
@requestOperate="requestOperate"
|
||||
/>
|
||||
<PasswordOauth
|
||||
ref="resetPasswordOauthRef"
|
||||
operateLabel="重置密码"
|
||||
@requestOperate="requestOperate"
|
||||
/>
|
||||
<PasswordOauth
|
||||
ref="unBindPasswordOauthRef"
|
||||
operateLabel="解绑用户"
|
||||
@requestOperate="requestOperate"
|
||||
/>
|
||||
</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 Pagination from "../search/pagination.vue";
|
||||
import EditBsideUser from "../operate/editBsideUser.vue";
|
||||
import PasswordOauth from "../operate/passwordOauth.vue";
|
||||
|
||||
import bsideManagerActiveStore from "../../stores/bsideManager";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { encrypt } from "../../utils/aes";
|
||||
import { judgment } from "../../utils/judgment.js";
|
||||
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
|
||||
|
||||
const bsideManagerActive = bsideManagerActiveStore();
|
||||
|
||||
const editSelectHold = {
|
||||
single: "编辑",
|
||||
label: "编辑信息"
|
||||
};
|
||||
const EditSelectRef = ref(null);
|
||||
const defaultSelectHold = {
|
||||
single: "重置",
|
||||
multiple: "重置选中",
|
||||
all: "重置所有",
|
||||
label: "重置账号"
|
||||
};
|
||||
const DefaultSelectRef = ref(null);
|
||||
const resetSelectHold = {
|
||||
single: "重置",
|
||||
multiple: "重置选中",
|
||||
all: "重置所有",
|
||||
label: "重置密码"
|
||||
};
|
||||
const ResetSelectRef = ref(null);
|
||||
const bindSelectHold = {
|
||||
single: "解绑",
|
||||
multiple: "解绑选中",
|
||||
all: "解绑所有",
|
||||
label: "解绑用户"
|
||||
};
|
||||
const unBindSelectRef = ref(null);
|
||||
const SearchAccountKeyRef = ref(null);
|
||||
const SearchUserKeyRef = ref(null);
|
||||
const SearchPhoneKeyRef = ref(null);
|
||||
const PaginationRef = ref(null);
|
||||
const editBsideUserRef = ref(null);
|
||||
const defaultPasswordOauthRef = ref(null);
|
||||
const resetPasswordOauthRef = ref(null);
|
||||
const unBindPasswordOauthRef = ref(null);
|
||||
const dialogSearchVisible = ref(false);
|
||||
const dialogSortVisible = ref(false);
|
||||
const dialogOperationVisible = ref(false);
|
||||
const userTableRef = ref();
|
||||
const offsetOff = ref(true);
|
||||
const pageCount = ref(0);
|
||||
const searchOptions = reactive({
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: ""
|
||||
});
|
||||
const tableData = ref([]);
|
||||
let operateData = reactive({});
|
||||
let operateDataClone = reactive({});
|
||||
const operate = ref("unbind");
|
||||
let singleData = reactive({});
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
dialogSearchVisible.value = !dialogSearchVisible.value;
|
||||
bsideManagerActive.dialogSearchVisible = dialogSearchVisible.value;
|
||||
break;
|
||||
case "sort":
|
||||
dialogSortVisible.value = !dialogSortVisible.value;
|
||||
bsideManagerActive.dialogSortVisible = dialogSortVisible.value;
|
||||
break;
|
||||
case "operation":
|
||||
dialogOperationVisible.value = !dialogOperationVisible.value;
|
||||
bsideManagerActive.dialogOperationVisible = dialogOperationVisible.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const bsideManager = () => {
|
||||
judgment(searchOptions, "account_name", SearchAccountKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "user_name", SearchUserKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "phone", SearchPhoneKeyRef.value, "searchMsg");
|
||||
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
|
||||
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
|
||||
PaginationRef.value.handleCurrentChange(1);
|
||||
offsetOff.value = false;
|
||||
}
|
||||
} else {
|
||||
offsetOff.value = true;
|
||||
}
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
offset: PaginationRef.value.offset,
|
||||
limit: PaginationRef.value.limit,
|
||||
searchOptions: searchOptions,
|
||||
};
|
||||
$http.post($url.bsideManager, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
tableData.value = res.data.bsideUsers;
|
||||
PaginationRef.value.dataCount = res.data.count;
|
||||
pageCount.value = Math.ceil(
|
||||
PaginationRef.value.dataCount / PaginationRef.value.limit
|
||||
);
|
||||
bsideManagerActive.dataCount = PaginationRef.value.dataCount;
|
||||
bsideManagerActive.pageCount = pageCount.value;
|
||||
if (!operateData[PaginationRef.value.offset]) return;
|
||||
operateData[PaginationRef.value.offset].forEach((element) => {
|
||||
tableData.value.forEach((item) => {
|
||||
if (item.account_name == element.account_name) {
|
||||
nextTick(() => {
|
||||
userTableRef.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.account_name == element.account_name) {
|
||||
// 数据在表格中的位置
|
||||
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") {
|
||||
const { account_name, user_name, phone } = singleMsg.data;
|
||||
editBsideUserRef.value.ruleForm.account_name = account_name;
|
||||
editBsideUserRef.value.ruleForm.user_name = user_name;
|
||||
editBsideUserRef.value.ruleForm.phone = phone;
|
||||
editBsideUserRef.value.originMsg = { ...singleMsg.data };
|
||||
editBsideUserRef.value.operateDialogVisible = true;
|
||||
}
|
||||
if (singleMsg.operateType == "default") {
|
||||
defaultPasswordOauthRef.value.operateDialogVisible = true;
|
||||
}
|
||||
if (singleMsg.operateType == "reset") {
|
||||
resetPasswordOauthRef.value.operateDialogVisible = true;
|
||||
}
|
||||
if (singleMsg.operateType == "unbind") {
|
||||
unBindPasswordOauthRef.value.operateDialogVisible = true;
|
||||
}
|
||||
};
|
||||
|
||||
const requestOperate = () => {
|
||||
let operateType, url, model, defaultPassword, password;
|
||||
if (operate.value == "edit") {
|
||||
operateType = "编辑信息";
|
||||
url = $url.editUserManager;
|
||||
model = EditSelectRef.value.model;
|
||||
} else if (operate.value == "default") {
|
||||
operateType = "重置账号";
|
||||
url = $url.resetPassword;
|
||||
model = DefaultSelectRef.value.model;
|
||||
defaultPassword = defaultPasswordOauthRef.value.ruleForm.defaultPassword;
|
||||
password = defaultPasswordOauthRef.value.ruleForm.password;
|
||||
} else if (operate.value == "reset") {
|
||||
operateType = "重置密码";
|
||||
url = $url.resetPassword;
|
||||
model = ResetSelectRef.value.model;
|
||||
defaultPassword = resetPasswordOauthRef.value.ruleForm.defaultPassword;
|
||||
password = resetPasswordOauthRef.value.ruleForm.password;
|
||||
} else if (operate.value == "unbind") {
|
||||
operateType = "解绑用户";
|
||||
url = $url.unBindManager;
|
||||
model = unBindSelectRef.value.model;
|
||||
password = unBindPasswordOauthRef.value.ruleForm.password;
|
||||
}
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
model: encrypt(model),
|
||||
};
|
||||
if(operate.value == "edit") {
|
||||
data.editMsg = { ...editBsideUserRef.value.ruleForm };
|
||||
data.userMsg = editBsideUserRef.value.originMsg;
|
||||
} else {
|
||||
data.password = encrypt(password);
|
||||
data.userMsg = model == "single" ? singleData : model == "multiple" ? operateDataClone : "";
|
||||
}
|
||||
if(operate.value == "default" || operate.value == "reset") {
|
||||
data.defaultPassword = encrypt(defaultPassword);
|
||||
data.resetType = operate.value;
|
||||
}
|
||||
$http.post(url, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userTableRef.value.clearSelection();
|
||||
operateData = {};
|
||||
operateDataClone = {};
|
||||
ElMessage({
|
||||
message: `${operateType}成功!`,
|
||||
type: "success",
|
||||
});
|
||||
bsideManager();
|
||||
} else {
|
||||
ElMessage.error(`${operateType}失败!`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ bsideManager });
|
||||
|
||||
watch(searchOptions, (value) => {
|
||||
bsideManagerActive.searchOptions = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
pageCount.value = bsideManagerActive.pageCount;
|
||||
dialogSearchVisible.value = bsideManagerActive.dialogSearchVisible;
|
||||
dialogSortVisible.value = bsideManagerActive.dialogSortVisible;
|
||||
dialogOperationVisible.value = bsideManagerActive.dialogOperationVisible;
|
||||
|
||||
if (dialogSearchVisible.value) {
|
||||
searchOptions.account_name = bsideManagerActive.searchOptions.account_name;
|
||||
searchOptions.user_name = bsideManagerActive.searchOptions.user_name;
|
||||
searchOptions.phone = bsideManagerActive.searchOptions.phone;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
}
|
||||
|
||||
if (dialogOperationVisible.value) {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
bsideManager();
|
||||
}, 200);
|
||||
|
||||
setTimeout(() => {
|
||||
if (dialogSearchVisible.value) {
|
||||
SearchAccountKeyRef.value.searchMsg = bsideManagerActive.searchOptions.account_name;
|
||||
SearchUserKeyRef.value.searchMsg = bsideManagerActive.searchOptions.user_name;
|
||||
SearchPhoneKeyRef.value.searchMsg = bsideManagerActive.searchOptions.phone;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
}
|
||||
|
||||
if (dialogOperationVisible.value) {
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
455
src/components/features/historyMsg.vue
Normal file
@@ -0,0 +1,455 @@
|
||||
<template>
|
||||
<!-- 表格 -->
|
||||
<el-card>
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<TabelTools @showTools="showTools" :dialogObj="historyMsgActive" />
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
max-height="32rem"
|
||||
table-layout="auto"
|
||||
scrollbar-always-on
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
v-if="dialogOperationVisible"
|
||||
fixed="left"
|
||||
type="selection"
|
||||
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="100" column-key="card_id">
|
||||
<template #header>
|
||||
<div>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchCardIdKeyRef"
|
||||
holderText="输入卡号以搜索相关数据"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 卡号 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortCardIdRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.encode_type)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.card_id }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="80" column-key="encode_type">
|
||||
<template #header>
|
||||
<SelectSearch
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SelectSearchRef"
|
||||
holderText="选择编码类型以搜索相关数据"
|
||||
:options="options"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 编码类型 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.encode_type)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.encode_type == true ? "代码" : "汉字" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="200" width="720" column-key="msg_data">
|
||||
<template #header>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchDataKeyRef"
|
||||
holderText="输入关键字以搜索相关数据"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 接收内容 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div class="flex justify-between items-center text-base">
|
||||
<el-text
|
||||
truncated
|
||||
:style="
|
||||
scope.row.msg_data
|
||||
? styleShunt(options, scope.row.encode_type)
|
||||
: 'color: rgb(240 70 70)'
|
||||
"
|
||||
style="font-size: 1rem"
|
||||
>
|
||||
{{ scope.row.msg_data ? scope.row.msg_data : "解析错误" }}
|
||||
</el-text>
|
||||
<el-button
|
||||
style="font-size: 1rem; font-weight: bold"
|
||||
size="small"
|
||||
:type="scope.row.encode_type == true ? 'primary' : 'success'"
|
||||
plain
|
||||
@click="showAll(scope.row.msg_data)"
|
||||
>
|
||||
展示全部内容
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="170" column-key="created_at">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="DateTimePickerRef"
|
||||
DateTimePicker="historyMsgDateTimePicker"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 接收时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortDateTimeRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.encode_type)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.created_at }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 表格 -->
|
||||
<Pagination
|
||||
ref="PaginationRef"
|
||||
:active="historyMsgActive"
|
||||
@sizeChangeBack="changePage"
|
||||
@search="historyMsg"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<DataShow ref="DataShowRef" />
|
||||
<PasswordOauth
|
||||
ref="PasswordOauthRef"
|
||||
operateLabel="删除历史消息"
|
||||
@requestOperate="requestOperate"
|
||||
/>
|
||||
</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 DataShow from "../operate/dataShow.vue";
|
||||
import PasswordOauth from "../operate/passwordOauth.vue";
|
||||
|
||||
import historyMsgActiveStore from "../../stores/historyMsg";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { encrypt } from "../../utils/aes";
|
||||
import { judgment } from "../../utils/judgment.js";
|
||||
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
|
||||
|
||||
const historyMsgActive = historyMsgActiveStore();
|
||||
|
||||
const delSelectHold = {
|
||||
single: "删除",
|
||||
multiple: "删除选中",
|
||||
all: "删除所有",
|
||||
label: "删除历史消息",
|
||||
};
|
||||
const DelSelectRef = ref(null);
|
||||
const SearchCardIdKeyRef = ref(null);
|
||||
const SortCardIdRef = ref(null);
|
||||
const SortDateTimeRef = ref(null);
|
||||
const SelectSearchRef = ref(null);
|
||||
const SearchDataKeyRef = ref(null);
|
||||
const DateTimePickerRef = ref(null);
|
||||
const PaginationRef = ref(null);
|
||||
const DataShowRef = ref(null);
|
||||
const PasswordOauthRef = ref(null);
|
||||
const dialogSearchVisible = ref(false);
|
||||
const dialogSortVisible = ref(false);
|
||||
const dialogOperationVisible = ref(false);
|
||||
const userTableRef = ref();
|
||||
const offsetOff = ref(true);
|
||||
const pageCount = ref(0);
|
||||
const searchOptions = reactive({
|
||||
card_id: "",
|
||||
encode_type: "",
|
||||
msg_data: "",
|
||||
created_at: "",
|
||||
});
|
||||
const sortOptions = reactive({
|
||||
card_id: false,
|
||||
created_at: false,
|
||||
});
|
||||
const tableData = ref([]);
|
||||
const options = [
|
||||
{
|
||||
value: "0",
|
||||
label: "汉字",
|
||||
style: "color: rgb(16, 184, 130)",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "代码",
|
||||
style: "color: rgb(56, 188, 248)",
|
||||
},
|
||||
];
|
||||
let singleData = reactive({});
|
||||
let delData = reactive({});
|
||||
let delDataClone = reactive({});
|
||||
|
||||
const styleShunt = (options, data) => {
|
||||
let nowStyle;
|
||||
options.forEach((item) => {
|
||||
if (data == item.value) {
|
||||
nowStyle = item.style;
|
||||
}
|
||||
});
|
||||
return nowStyle;
|
||||
};
|
||||
|
||||
const showAll = (msg) => {
|
||||
DataShowRef.value.dialogMsg = msg;
|
||||
DataShowRef.value.dialogVisible = true;
|
||||
};
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
dialogSearchVisible.value = !dialogSearchVisible.value;
|
||||
historyMsgActive.dialogSearchVisible = dialogSearchVisible.value;
|
||||
break;
|
||||
case "sort":
|
||||
dialogSortVisible.value = !dialogSortVisible.value;
|
||||
historyMsgActive.dialogSortVisible = dialogSortVisible.value;
|
||||
break;
|
||||
case "operation":
|
||||
dialogOperationVisible.value = !dialogOperationVisible.value;
|
||||
historyMsgActive.dialogOperationVisible = dialogOperationVisible.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const historyMsg = () => {
|
||||
judgment(searchOptions, "card_id", SearchCardIdKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "encode_type", SelectSearchRef.value, "searchMsg");
|
||||
judgment(searchOptions, "msg_data", SearchDataKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "created_at", DateTimePickerRef.value, "searchMsg");
|
||||
judgment(sortOptions, "card_id", SortCardIdRef.value, "searchMsg");
|
||||
judgment(sortOptions, "created_at", SortDateTimeRef.value, "searchMsg");
|
||||
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
|
||||
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
|
||||
PaginationRef.value.handleCurrentChange(1);
|
||||
offsetOff.value = false;
|
||||
}
|
||||
} else {
|
||||
offsetOff.value = true;
|
||||
}
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
offset: PaginationRef.value.offset,
|
||||
limit: PaginationRef.value.limit,
|
||||
searchOptions: searchOptions,
|
||||
sortOptions: sortOptions,
|
||||
};
|
||||
$http.post($url.getHistoryMsg, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
tableData.value = res.data.historyMsg;
|
||||
tableData.value.forEach((item) => {
|
||||
item.origin_time = item.created_at;
|
||||
item.created_at = moment
|
||||
.utc(item.created_at)
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
});
|
||||
PaginationRef.value.dataCount = res.data.count;
|
||||
historyMsgActive.dataCount = PaginationRef.value.dataCount;
|
||||
pageCount.value = Math.ceil(
|
||||
PaginationRef.value.dataCount / PaginationRef.value.limit
|
||||
);
|
||||
historyMsgActive.pageCount = pageCount.value;
|
||||
if (!delData[PaginationRef.value.offset]) return;
|
||||
delData[PaginationRef.value.offset].forEach((element) => {
|
||||
tableData.value.forEach((item) => {
|
||||
if (item.created_at == element.created_at) {
|
||||
nextTick(() => {
|
||||
userTableRef.value.toggleRowSelection(item, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
tableData.value = [];
|
||||
// ElMessage.error("未查找到相关数据!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const changePage = (val) => {
|
||||
pageCount.value = Math.ceil(PaginationRef.value.dataCount / val);
|
||||
delData = {};
|
||||
for (let i = 0; i < pageCount.value; i++) {
|
||||
delData[i + 1] = [];
|
||||
for (const key in delDataClone) {
|
||||
delDataClone[key].forEach((element) => {
|
||||
// 判断数据在第几页
|
||||
if (Math.ceil(element.location / PaginationRef.value.limit) == i + 1) {
|
||||
delData[i + 1].push(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectionChange = (val) => {
|
||||
delData[PaginationRef.value.offset] = val;
|
||||
tableData.value.forEach((item, index) => {
|
||||
delData[PaginationRef.value.offset].forEach((element) => {
|
||||
if (item.created_at == element.created_at) {
|
||||
// 数据在表格中的位置
|
||||
element.location =
|
||||
(PaginationRef.value.offset - 1) * PaginationRef.value.limit + (index + 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
delDataClone = JSON.parse(JSON.stringify(delData));
|
||||
};
|
||||
|
||||
const showComfirmDialog = (singleMsg) => {
|
||||
singleData = singleMsg.data;
|
||||
PasswordOauthRef.value.operateDialogVisible = true;
|
||||
};
|
||||
|
||||
const requestOperate = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
model: encrypt(DelSelectRef.value.model),
|
||||
password: encrypt(PasswordOauthRef.value.ruleForm.password),
|
||||
userMsg:
|
||||
DelSelectRef.value.model == "single"
|
||||
? singleData
|
||||
: DelSelectRef.value.model == "multiple"
|
||||
? delDataClone
|
||||
: "",
|
||||
};
|
||||
$http.post($url.delHistoryMsg, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
userTableRef.value.clearSelection();
|
||||
delData = {};
|
||||
delDataClone = {};
|
||||
ElMessage({
|
||||
message: "操作成功!",
|
||||
type: "success",
|
||||
});
|
||||
historyMsg();
|
||||
} else {
|
||||
ElMessage.error("操作失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ historyMsg });
|
||||
|
||||
watch(searchOptions, (value) => {
|
||||
historyMsgActive.searchOptions = value;
|
||||
});
|
||||
|
||||
watch(sortOptions, (value) => {
|
||||
historyMsgActive.sortOptions = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
pageCount.value = historyMsgActive.pageCount;
|
||||
dialogSearchVisible.value = historyMsgActive.dialogSearchVisible;
|
||||
dialogSortVisible.value = historyMsgActive.dialogSortVisible;
|
||||
dialogOperationVisible.value = historyMsgActive.dialogOperationVisible;
|
||||
|
||||
if (dialogSearchVisible.value) {
|
||||
searchOptions.card_id = historyMsgActive.searchOptions.card_id;
|
||||
searchOptions.encode_type = historyMsgActive.searchOptions.encode_type;
|
||||
searchOptions.msg_data = historyMsgActive.searchOptions.msg_data;
|
||||
searchOptions.created_at = historyMsgActive.searchOptions.created_at;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
sortOptions.card_id = historyMsgActive.sortOptions.card_id;
|
||||
sortOptions.created_at = historyMsgActive.sortOptions.created_at;
|
||||
}
|
||||
|
||||
if (dialogOperationVisible.value) {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
historyMsg();
|
||||
}, 200);
|
||||
|
||||
setTimeout(() => {
|
||||
if (dialogSearchVisible.value) {
|
||||
SearchCardIdKeyRef.value.searchMsg = historyMsgActive.searchOptions.card_id;
|
||||
SelectSearchRef.value.searchMsg = historyMsgActive.searchOptions.encode_type;
|
||||
SearchDataKeyRef.value.searchMsg = historyMsgActive.searchOptions.msg_data;
|
||||
DateTimePickerRef.value.created_at = historyMsgActive.searchOptions.created_at;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
SortCardIdRef.value.searchMsg = historyMsgActive.sortOptions.card_id;
|
||||
SortDateTimeRef.value.searchMsg = historyMsgActive.sortOptions.created_at;
|
||||
}
|
||||
|
||||
if (dialogOperationVisible.value) {
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
314
src/components/features/record.vue
Normal file
@@ -0,0 +1,314 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
绑定记录
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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-card>
|
||||
<div class="flex justify-center flex-wrap">
|
||||
<TabelTools @showTools="showTools" :dialogObj="recordActive" />
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
max-height="400"
|
||||
table-layout="auto"
|
||||
scrollbar-always-on
|
||||
>
|
||||
<el-table-column min-width="100" column-key="card_id">
|
||||
<template #header>
|
||||
<SearchKey
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SearchCardIdKeyRef"
|
||||
holderText="输入卡号以搜索相关数据"
|
||||
@search="getRecord"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 卡号 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortCardIdRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="getRecord"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.type)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.card_id }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" column-key="type">
|
||||
<template #header>
|
||||
<SelectSearch
|
||||
v-if="dialogSearchVisible"
|
||||
ref="SelectSearchRef"
|
||||
holderText="选择操作类型以搜索相关数据"
|
||||
:options="options"
|
||||
@search="getRecord"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 操作类型 </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.type)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.type == true ? "绑定" : "解绑" }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="200" column-key="created_at">
|
||||
<template #header>
|
||||
<DateTimePicker
|
||||
v-if="dialogSearchVisible"
|
||||
ref="DateTimePickerRef"
|
||||
DateTimePicker="recordDateTimePicker"
|
||||
@search="getRecord"
|
||||
/>
|
||||
<div>
|
||||
<el-text class="mx-1"> 操作时间 </el-text>
|
||||
<SortSearch
|
||||
v-if="dialogSortVisible"
|
||||
ref="SortDateTimeRef"
|
||||
activeText="顺序"
|
||||
inactiveText="倒序"
|
||||
@search="getRecord"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<div
|
||||
:style="styleShunt(options, scope.row.type)"
|
||||
class="flex justify-start items-center text-base"
|
||||
>
|
||||
{{ scope.row.created_at }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 表格 -->
|
||||
<Pagination ref="PaginationRef" :active="recordActive" @search="getRecord" />
|
||||
</div>
|
||||
</el-card>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" type="primary" @click="resetForm">
|
||||
返回
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted, nextTick } from "vue";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import TabelTools from "../operate/tabelTools.vue";
|
||||
import SearchKey from "../search/searchKey.vue";
|
||||
import SortSearch from "../search/sortSearch.vue";
|
||||
import SelectSearch from "../search/selectSearch.vue";
|
||||
import DateTimePicker from "../search/dateTimePicker.vue";
|
||||
import Pagination from "../search/pagination.vue";
|
||||
|
||||
import recordActiveStore from "../../stores/record";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import { judgment } from "../../utils/judgment.js";
|
||||
import { hasNullOrEmptyPropertyWithTypeof } from "../../utils/hasNullOrEmptyPropertyWithTypeof.js";
|
||||
|
||||
const recordActive = recordActiveStore();
|
||||
|
||||
const SearchCardIdKeyRef = ref(null);
|
||||
const SortCardIdRef = ref(null);
|
||||
const SortDateTimeRef = ref(null);
|
||||
const SelectSearchRef = ref(null);
|
||||
const DateTimePickerRef = ref(null);
|
||||
const PaginationRef = ref(null);
|
||||
const dialogSearchVisible = ref(false);
|
||||
const dialogSortVisible = ref(false);
|
||||
const dialogOperationVisible = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const offsetOff = ref(true);
|
||||
const searchOptions = reactive({
|
||||
card_id: "",
|
||||
type: "",
|
||||
created_at: "",
|
||||
});
|
||||
const sortOptions = reactive({
|
||||
card_id: false,
|
||||
created_at: false,
|
||||
});
|
||||
const tableData = ref([]);
|
||||
const options = [
|
||||
{
|
||||
value: "0",
|
||||
label: "解绑",
|
||||
style: "color: rgb(16, 184, 130)",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "绑定",
|
||||
style: "color: rgb(56, 188, 248)",
|
||||
},
|
||||
];
|
||||
|
||||
const styleShunt = (options, data) => {
|
||||
let nowStyle;
|
||||
options.forEach((item) => {
|
||||
if (data == item.value) {
|
||||
nowStyle = item.style;
|
||||
}
|
||||
});
|
||||
return nowStyle;
|
||||
};
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
dialogSearchVisible.value = !dialogSearchVisible.value;
|
||||
recordActive.dialogSearchVisible = dialogSearchVisible.value;
|
||||
break;
|
||||
case "sort":
|
||||
dialogSortVisible.value = !dialogSortVisible.value;
|
||||
recordActive.dialogSortVisible = dialogSortVisible.value;
|
||||
break;
|
||||
case "operation":
|
||||
dialogOperationVisible.value = !dialogOperationVisible.value;
|
||||
recordActive.dialogOperationVisible = dialogOperationVisible.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const getRecord = () => {
|
||||
judgment(searchOptions, "card_id", SearchCardIdKeyRef.value, "searchMsg");
|
||||
judgment(searchOptions, "type", SelectSearchRef.value, "searchMsg");
|
||||
judgment(searchOptions, "created_at", DateTimePickerRef.value, "searchMsg");
|
||||
judgment(sortOptions, "card_id", SortCardIdRef.value, "searchMsg");
|
||||
judgment(sortOptions, "created_at", SortDateTimeRef.value, "searchMsg");
|
||||
if (hasNullOrEmptyPropertyWithTypeof(searchOptions)) {
|
||||
if (PaginationRef.value.offset !== 1 && offsetOff.value) {
|
||||
PaginationRef.value.handleCurrentChange(1);
|
||||
offsetOff.value = false;
|
||||
}
|
||||
} else {
|
||||
offsetOff.value = true;
|
||||
}
|
||||
nextTick(() => {
|
||||
if (PaginationRef.value !== null) {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
offset: PaginationRef.value.offset,
|
||||
limit: PaginationRef.value.limit,
|
||||
searchOptions: searchOptions,
|
||||
sortOptions: sortOptions,
|
||||
};
|
||||
$http.post($url.getRecord, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
tableData.value = res.data.recordMsg;
|
||||
tableData.value.forEach((item) => {
|
||||
item.created_at = moment
|
||||
.utc(item.created_at)
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
});
|
||||
PaginationRef.value.dataCount = res.data.count;
|
||||
recordActive.dataCount = PaginationRef.value.dataCount;
|
||||
} else {
|
||||
tableData.value = [];
|
||||
// ElMessage.error("未查找到相关数据!");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ getRecord, dialogVisible });
|
||||
|
||||
watch(searchOptions, (value) => {
|
||||
recordActive.searchOptions = value;
|
||||
});
|
||||
|
||||
watch(sortOptions, (value) => {
|
||||
recordActive.sortOptions = value;
|
||||
});
|
||||
|
||||
watch(dialogVisible, (value) => {
|
||||
if (value) {
|
||||
setTimeout(() => {
|
||||
SearchCardIdKeyRef.value.searchMsg = recordActive.searchOptions.card_id;
|
||||
SelectSearchRef.value.searchMsg = recordActive.searchOptions.type;
|
||||
searchOptions.card_id = recordActive.searchOptions.card_id;
|
||||
DateTimePickerRef.value.created_at = recordActive.searchOptions.created_at;
|
||||
searchOptions.created_at = recordActive.searchOptions.created_at;
|
||||
SortCardIdRef.value.searchMsg = recordActive.sortOptions.card_id;
|
||||
SortDateTimeRef.value.searchMsg = recordActive.sortOptions.created_at;
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
dialogSearchVisible.value = recordActive.dialogSearchVisible;
|
||||
dialogSortVisible.value = recordActive.dialogSortVisible;
|
||||
dialogOperationVisible.value = recordActive.dialogOperationVisible;
|
||||
|
||||
if (dialogSearchVisible.value) {
|
||||
searchOptions.card_id = recordActive.searchOptions.card_id;
|
||||
searchOptions.type = recordActive.searchOptions.type;
|
||||
searchOptions.created_at = recordActive.searchOptions.created_at;
|
||||
}
|
||||
|
||||
if (dialogSortVisible.value) {
|
||||
sortOptions.card_id = recordActive.sortOptions.card_id;
|
||||
sortOptions.created_at = recordActive.sortOptions.created_at;
|
||||
}
|
||||
|
||||
if (dialogOperationVisible.value) {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
getRecord();
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
87
src/components/guide/Q&A.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div>
|
||||
常见问题指南:
|
||||
<br />
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">Q:</span
|
||||
>为什么平台在打开状态下且一段时间无任何操作后会显示<span
|
||||
class="text-blue-600 font-black"
|
||||
>未绑定且无连接状态</span
|
||||
>?
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">A:</span
|
||||
>这是由于身份信息过期导致的,进行数据搜索的操作后身份信息会自动刷新,数据也会正常显示。
|
||||
<br />
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">Q:</span
|
||||
>为什么平台在使用中有时候会突然跳转到登录页面然后跳转回来?
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">A:</span
|
||||
>这是由于身份信息刷新导致的,不会导致数据丢失,可放心使用。
|
||||
<br />
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">Q:</span
|
||||
>为什么平台在登录和刷新后有时会弹出“<span class="text-red-500 font-black"
|
||||
>未查找到相关数据!</span
|
||||
>”的提示?
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">A:</span
|
||||
>这是由于身份信息正在刷新导致的,刷新完毕后数据会正常显示。
|
||||
<br />
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">Q:</span>为什么当前消息一直显示“<span
|
||||
class="text-sky-400 font-black"
|
||||
>连接中...</span
|
||||
>”?
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">A:</span
|
||||
>这种情况需要检查您的网络状况或更改连接的网络后重试。
|
||||
<br />
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">Q:</span>表格左上角的<span
|
||||
class="text-emerald-500 font-black"
|
||||
>表格工具</span
|
||||
>是什么?
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">A:</span
|
||||
><span class="text-amber-500 font-black">表格工具</span
|
||||
>是用来显示或隐藏用于对表格进行搜索、排序和编辑删除等操作的控件,
|
||||
<br />
|
||||
使用时只需要将鼠标移动到<span class="text-amber-500 font-black">表格工具</span
|
||||
>上,然后选择需要使用的功能并点击即可,附操作图如下:
|
||||
<div class="border-2 flex flex-wrap justify-center items-center">
|
||||
<div class="w-1/4">
|
||||
<span class="text-red-500 font-black">1. </span
|
||||
>鼠标移入表格工具,弹出下拉框
|
||||
<img src="../../assets/img/Q&A/tabelTools.png" alt="" />
|
||||
</div>
|
||||
<div class="w-1/4 border-l-2">
|
||||
<span class="text-red-500 font-black">2. </span
|
||||
>选择需要使用的工具,工具显
|
||||
<br />
|
||||
示,显示状态下右边存在眼睛图标
|
||||
<img src="../../assets/img/Q&A/toolsClick.png" alt="" />
|
||||
</div>
|
||||
<div class="w-1/4 border-l-2">
|
||||
<span class="text-red-500 font-black">3. </span
|
||||
>可选择多个工具同时进行使用
|
||||
<img src="../../assets/img/Q&A/toolsSelect.png" alt="" />
|
||||
</div>
|
||||
<div class="w-1/4 border-l-2">
|
||||
<span class="text-red-500 font-black">4. </span
|
||||
>如果工具使用完毕,可以再次
|
||||
<br />
|
||||
点击下拉框中的对应工具进行隐藏
|
||||
<img src="../../assets/img/Q&A/showType.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import tabelTools from "../../assets/img/Q&A/tabelTools.png";
|
||||
import toolsClick from "../../assets/img/Q&A/toolsClick.png";
|
||||
import toolsSelect from "../../assets/img/Q&A/toolsSelect.png";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
92
src/components/guide/code.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div>
|
||||
汉字编码使用指南:
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-sky-500 font-black">编码科普:</span>
|
||||
如果您不懂汉字编码,建议您先点击链接“<span class="text-sky-500 font-black"
|
||||
>编码科普</span
|
||||
>”以了解汉字编码的基本知识。
|
||||
<img src="../../assets/img/code/codeScience.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">汉字转换为编码:</span>
|
||||
<br />
|
||||
<span class="text-slate-500 font-black">灰色框</span>为输入区域, 在此区域输入需要转换为编码的汉字
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">黄色框</span>为“<span class="text-amber-500 font-black">显示类型</span>”选择下拉框,共提供两种显示类型
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">绿色框</span>为“<span class="text-emerald-500 font-black">转换汉字</span>”按钮,点击此按钮可将输入框的汉字转换为编码并显示在输出区域
|
||||
<br />
|
||||
<span class="text-sky-500 font-black">蓝色框</span>为“<span class="text-sky-500 font-black">清除</span>”按钮,点击此按钮可清除输入框和输出区域的内容
|
||||
<img src="../../assets/img/code/fontGuide.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-sky-500 font-black">选择显示类型:</span>显示类型分为<span class="text-amber-500 font-black">单字</span>显示和<span class="text-emerald-500 font-black">连续</span>显示两种模式
|
||||
<img src="../../assets/img/code/showType.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">汉字转换为编码的转换结果:</span>
|
||||
<img src="../../assets/img/code/transFont.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
点击左上角的<span class="text-red-500 font-black">转换模式</span>按钮可切换编码模式
|
||||
<img src="../../assets/img/code/changeMode.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">编码转换为汉字:</span>
|
||||
<br />
|
||||
<span class="text-slate-500 font-black">灰色框</span>为输入区域, 在此区域输入需要转换为汉字的编码
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">黄色框</span>为“<span class="text-amber-500 font-black">解码类型</span>”选择下拉框,共提供三种解码类型
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">绿色框</span>为“<span class="text-emerald-500 font-black">显示类型</span>”选择下拉框,共提供两种显示类型
|
||||
<br />
|
||||
<span class="text-red-500 font-black">红色框</span>为“<span class="text-red-500 font-black">转换编码</span>”按钮,点击此按钮可将输入框的编码转换为汉字并显示在输出区域
|
||||
<br />
|
||||
<span class="text-sky-500 font-black">蓝色框</span>为“<span class="text-sky-500 font-black">清除</span>”按钮,点击此按钮可清除输入框和输出区域的内容
|
||||
<img src="../../assets/img/code/codeGuide.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-amber-500 font-black">选择解码类型:</span>解码类型分为<span class="text-amber-500 font-black">十进制</span>解码,<span class="text-emerald-500 font-black">十六进制</span>解码和<span class="text-red-500 font-black">二进制</span>解码三种模式
|
||||
<img src="../../assets/img/code/codeType.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-sky-500 font-black">选择显示类型:</span>显示类型分为<span class="text-amber-500 font-black">单字</span>显示和<span class="text-emerald-500 font-black">连续</span>显示两种模式
|
||||
<img src="../../assets/img/code/showType.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">编码转换为汉字的转换结果:</span>
|
||||
<img src="../../assets/img/code/transCode.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import codeGuide from "../../assets/img/code/codeGuide.png";
|
||||
import codeScience from "../../assets/img/code/codeScience.png";
|
||||
import codeType from "../../assets/img/code/codeType.png";
|
||||
import fontGuide from "../../assets/img/code/fontGuide.png";
|
||||
import showType from "../../assets/img/code/showType.png";
|
||||
import transCode from "../../assets/img/code/transCode.png";
|
||||
import transFont from "../../assets/img/code/transFont.png";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
128
src/components/guide/guide.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
北斗短报文通信服务平台使用帮助
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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-menu
|
||||
class="flex flex-wrap font-bold"
|
||||
:default-active="activeIndex"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item
|
||||
index="0"
|
||||
:style="activeIndex == '0' ? activeStyle : indexStyle"
|
||||
class="border-x-4 border-slate-300"
|
||||
>
|
||||
Q&A
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="1"
|
||||
:style="activeIndex == '1' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
顶部导航栏
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="2"
|
||||
:style="activeIndex == '2' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
查看消息
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="3"
|
||||
:style="activeIndex == '3' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
汉字编码
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
index="4"
|
||||
:style="activeIndex == '4' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
异或校验
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
v-if="manager"
|
||||
index="5"
|
||||
:style="activeIndex == '5' ? activeStyle : indexStyle"
|
||||
class="border-r-4 border-slate-300"
|
||||
>
|
||||
单位管理
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-card>
|
||||
<div v-show="activeIndex === '0'">
|
||||
<QA />
|
||||
</div>
|
||||
<div v-show="activeIndex === '1'">
|
||||
<Header />
|
||||
</div>
|
||||
<div v-show="activeIndex === '2'">
|
||||
<Msg />
|
||||
</div>
|
||||
<div v-show="activeIndex === '3'">
|
||||
<Code />
|
||||
</div>
|
||||
<div v-show="activeIndex === '4'">
|
||||
<Xor />
|
||||
</div>
|
||||
<div v-show="activeIndex === '5'">
|
||||
<Manager />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
|
||||
import QA from "./Q&A.vue";
|
||||
import Header from "./header.vue";
|
||||
import Msg from "./msg.vue";
|
||||
import Code from "./code.vue";
|
||||
import Xor from "./xor.vue";
|
||||
import Manager from "./manager.vue";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const activeIndex = ref("1");
|
||||
const indexStyle = "background-color: rgb(241 245 249);";
|
||||
const activeStyle = "background-color: rgb(207 250 254);";
|
||||
const manager = ref(false);
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
const handleSelect = (key, keyPath) => {
|
||||
activeIndex.value = key;
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible, manager });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
29
src/components/guide/header.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
顶部导航栏指南:
|
||||
<br />
|
||||
<br />
|
||||
导航栏组成:
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-amber-500 font-black">左侧:</span>
|
||||
展示了平台号码,卡号,绑定/解绑按钮,以及帮助按钮
|
||||
<img src="../../assets/img/header/headerGuide.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">右侧:</span>
|
||||
内容为账户名的下拉框,鼠标移动到下拉框时,会显示一些功能
|
||||
<img src="../../assets/img/header/headerSelect.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import headerGuide from "../../assets/img/header/headerGuide.png";
|
||||
import headerSelect from "../../assets/img/header/headerSelect.png";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
38
src/components/guide/manager.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
单位管理指南:
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-amber-500 font-black">单位管理操作工具:</span>
|
||||
<br />
|
||||
单位管理功能中最重要的部分是表格的操作工具
|
||||
<br />
|
||||
可以通过表格左上角的下拉框找到操作工具并使其显示
|
||||
<img src="../../assets/img/manager/operateSelect.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">操作工具介绍:</span>
|
||||
<br />
|
||||
单位管理功能的表格的操作工具具有四种操作类型
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">黄色框</span>内为账号信息的编辑功能,只允许单个账号信息的编辑操作
|
||||
<br />
|
||||
<span class="text-red-500 font-black">红色框</span>内为账号状态的重置功能,可以重置账号的密码、解绑账号所绑定的北斗卡、删除账号现有的姓名和手机号
|
||||
<br />
|
||||
<span class="text-sky-500 font-black">蓝色框</span>内为账号密码的重置功能,可以重置账号的密码
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">绿色框</span>内为账号与北斗卡的的解绑功能,可以解绑账号所绑定的北斗卡
|
||||
<img src="../../assets/img/manager/operateType.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import operateSelect from "../../assets/img/manager/operateSelect.png";
|
||||
import operateType from "../../assets/img/manager/operateType.png";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
72
src/components/guide/msg.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
查看消息指南:
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">实时短报文消息:</span>
|
||||
左侧为接收到的实时短报文消息的内容,右侧为接收到实时短报文消息时的时间
|
||||
<img src="../../assets/img/msg/msg.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-sky-500 font-black">查看历史消息的全部内容:</span>
|
||||
消息内容过长时,可以点击右侧的“<span class="text-sky-500 font-black"
|
||||
>展示全部内容</span
|
||||
>”按钮来查看消息的全部内容
|
||||
<img src="../../assets/img/msg/showMsg.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-amber-500 font-black">删除历史消息:</span>
|
||||
想要删除历史消息时,首先需要显示出<span class="text-amber-500 font-black"
|
||||
>表格工具</span
|
||||
>中的“<span class="text-emerald-500 font-black">操作工具</span>”
|
||||
<img src="../../assets/img/msg/operateSelect.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-red-500 font-black">删除历史消息:</span>
|
||||
<br />
|
||||
此处存在三种删除操作:
|
||||
<br />
|
||||
<span class="text-red-500 font-black">1. </span>下图<span
|
||||
class="text-red-500 font-black"
|
||||
>红色框</span
|
||||
>内的按钮“<span class="text-red-500 font-black">删除</span>”可以删除当前行的数据
|
||||
<br />
|
||||
<span class="text-sky-500 font-black">2. </span>下图<span
|
||||
class="text-sky-500 font-black"
|
||||
>蓝色框</span
|
||||
>内的按钮“<span class="text-red-500 font-black">删除选中</span>”可以删除选中行的数据
|
||||
<br />
|
||||
<span class="text-slate-500 font-black">2.1 </span>下图<span
|
||||
class="text-slate-500 font-black"
|
||||
>灰色框</span
|
||||
>内的单选框可以全选中或全不选中所有行的数据
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">2.2 </span>下图<span
|
||||
class="text-emerald-500 font-black"
|
||||
>绿色框</span
|
||||
>内的单选框可以选中或不选中当前行的数据
|
||||
<br />
|
||||
<span class="text-amber-500 font-black">3. </span>下图<span
|
||||
class="text-amber-500 font-black"
|
||||
>黄色框</span
|
||||
>内的按钮“<span class="text-red-500 font-black">删除所有</span>”可以删除所有行的数据
|
||||
<img src="../../assets/img/msg/operateType.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import msg from "../../assets/img/msg/msg.png";
|
||||
import operateSelect from "../../assets/img/msg/operateSelect.png";
|
||||
import operateType from "../../assets/img/msg/operateType.png";
|
||||
import showMsg from "../../assets/img/msg/showMsg.png";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
42
src/components/guide/xor.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
异或校验指南:
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-emerald-500 font-black">功能组成:</span>
|
||||
<br />
|
||||
<span class="text-sky-500 font-black">蓝色框</span>内为校验数据的输入框
|
||||
<br />
|
||||
<span class="text-emerald-500 font-black">绿色区域</span>为校验结果的显示区域
|
||||
<br />
|
||||
下方的“<span class="text-amber-500 font-black">校验</span>”按钮用于执行校验
|
||||
<br />
|
||||
下方的“<span class="text-red-500 font-black">清除</span
|
||||
>”按钮用于清除输入框的校验数据和显示区域的校验结果。
|
||||
<img src="../../assets/img/xor/xorGuide.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-red-500 font-black">未输入校验数据时的报错示例:</span>
|
||||
<img src="../../assets/img/xor/input.png" alt="" />
|
||||
<img src="../../assets/img/xor/errorMsg.png" alt="" />
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<div>
|
||||
<span class="text-amber-500 font-black">校验结果示例:</span>
|
||||
<img src="../../assets/img/xor/xor.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import errorMsg from "../../assets/img/xor/errorMsg.png";
|
||||
import input from "../../assets/img/xor/input.png";
|
||||
import xor from "../../assets/img/xor/xor.png";
|
||||
import xorGuide from "../../assets/img/xor/xorGuide.png";
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
187
src/components/manager/bind.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
绑定
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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="bindCardID">
|
||||
<el-input
|
||||
minlength="8"
|
||||
maxlength="8"
|
||||
clearable
|
||||
placeholder="请输入卡号"
|
||||
v-model="ruleForm.bindCardID"
|
||||
@input="filter(ruleForm.bindCardID, 'bindCardID')"
|
||||
@blur="serviceCheck(ruleForm.bindCardID)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="IMSI 号" prop="IMSI">
|
||||
<el-input
|
||||
minlength="18"
|
||||
maxlength="18"
|
||||
clearable
|
||||
placeholder="请输入 IMSI 号"
|
||||
v-model="ruleForm.IMSI"
|
||||
@input="filter(ruleForm.IMSI, 'IMSI')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" @click="resetForm(ruleFormRef)">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold" type="primary" @click="bind(ruleFormRef)">
|
||||
绑定
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } 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 dialogVisible = ref(false);
|
||||
const ruleFormRef = ref(null);
|
||||
const formElClone = ref(null);
|
||||
|
||||
const ruleForm = reactive({
|
||||
bindCardID: "",
|
||||
IMSI: "",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
bindCardID: [
|
||||
{ required: true, message: "请输入卡号", trigger: "blur" },
|
||||
{ min: 8, max: 8, message: "输入长度应当为 8 位", trigger: "change" },
|
||||
],
|
||||
IMSI: [
|
||||
{ required: true, message: "请输入 IMSI 号", trigger: "blur" },
|
||||
{ min: 18, max: 18, message: "输入长度应当为 18 位", trigger: "change" },
|
||||
],
|
||||
});
|
||||
|
||||
const emit = defineEmits(["bindCard", "connect"]);
|
||||
|
||||
const props = defineProps({
|
||||
cardid: String,
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
let filterValue = value.replace(/\D/g, "");
|
||||
if (label == "bindCardID") {
|
||||
ruleForm.bindCardID = filterValue;
|
||||
} else if (label == "IMSI") {
|
||||
ruleForm.IMSI = filterValue;
|
||||
}
|
||||
};
|
||||
|
||||
const serviceCheck = (cardID) => {
|
||||
if (cardID !== "" && cardID !== undefined) {
|
||||
const data = {
|
||||
cardID: cardID,
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.card, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "可绑定!",
|
||||
type: "success",
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("此卡号已被绑定!");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
if (!ruleFormRef) return;
|
||||
formElClone.value = ruleFormRef.value;
|
||||
formElClone.value.resetFields();
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const bind = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
const data = {
|
||||
cardID: ruleForm.bindCardID,
|
||||
IMSI: ruleForm.IMSI,
|
||||
};
|
||||
$http.post($url.bindCard, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "绑定成功!",
|
||||
type: "success",
|
||||
});
|
||||
emit("bindCard", ruleForm.bindCardID);
|
||||
ruleForm.bindCardID = "";
|
||||
ruleForm.IMSI = "";
|
||||
emit("connect");
|
||||
window.location.reload();
|
||||
} else {
|
||||
ElMessage.error("绑定失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible });
|
||||
|
||||
watch(dialogVisible, (value) => {
|
||||
if (!value) {
|
||||
resetForm(ruleFormRef.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
217
src/components/manager/changePassword.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
密码管理
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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="oldPassword">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入原密码"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.oldPassword"
|
||||
@input="filter(ruleForm.oldPassword, 'oldPassword')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="新密码" prop="newPassword">
|
||||
<el-text
|
||||
style="color: rgb(240 70 70)"
|
||||
class="mx-1 font-bold"
|
||||
size="small"
|
||||
type="info"
|
||||
>
|
||||
密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*.),且长度在8-16位之间
|
||||
</el-text>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入新密码"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.newPassword"
|
||||
@input="filter(ruleForm.newPassword, 'newPassword')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="确认新密码" prop="checkPass">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请再次输入新密码"
|
||||
type="checkPass"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.checkPass"
|
||||
@input="filter(ruleForm.checkPass, 'checkPass')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" @click="resetForm(ruleFormRef)">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
type="primary"
|
||||
@click="changePassword(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 { encrypt } from "../../utils/aes";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const ruleFormRef = ref(null);
|
||||
const formElClone = ref(null);
|
||||
const ruleForm = reactive({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
checkPass: "",
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const passwordRegex = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*.]).{8,16}$/;
|
||||
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请输入密码"));
|
||||
} else if (!passwordRegex.test(value)) {
|
||||
callback( new Error("密码必须包含数字、大小写字母以及特殊字符(!@#$%^&*.),且长度在8-16位之间"));
|
||||
} else {
|
||||
if (ruleForm.checkPass !== "") {
|
||||
if (!ruleFormRef.value) return;
|
||||
ruleFormRef.value.validateField("checkPass");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateCheck = (rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请再次输入密码"));
|
||||
} else if (value !== ruleForm.newPassword) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [{ required: true, validator: validatePass, trigger: "blur" }],
|
||||
checkPass: [{ required: true, validator: validateCheck, trigger: "blur" }],
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
if (!ruleFormRef) return;
|
||||
formElClone.value = ruleFormRef.value;
|
||||
formElClone.value.resetFields();
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const changePassword = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
let encryptForm = {
|
||||
oldPassword: encrypt(ruleForm.oldPassword),
|
||||
newPassword: encrypt(ruleForm.newPassword),
|
||||
};
|
||||
formEl.resetFields();
|
||||
const data = {
|
||||
passwordForm: encryptForm,
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.changePassword, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "密码修改成功!",
|
||||
type: "success",
|
||||
});
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
} else {
|
||||
ElMessage.error("密码修改失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
149
src/components/manager/unbind.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
解绑
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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="IMSI 号" prop="unbindIMSI">
|
||||
<el-input
|
||||
minlength="18"
|
||||
maxlength="18"
|
||||
clearable
|
||||
placeholder="请输入 IMSI 号"
|
||||
v-model="ruleForm.unbindIMSI"
|
||||
@input="filter(ruleForm.unbindIMSI, 'unbindIMSI')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" @click="resetForm(ruleFormRef)">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold" type="primary" @click="unbind(ruleFormRef)">
|
||||
解绑
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch } 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 dialogVisible = ref(false);
|
||||
const ruleFormRef = ref(null);
|
||||
const formElClone = ref(null);
|
||||
|
||||
const ruleForm = reactive({
|
||||
unbindIMSI: "",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
unbindIMSI: [
|
||||
{ required: true, message: "请输入 IMSI 号", trigger: "blur" },
|
||||
{ min: 18, max: 18, message: "输入长度应当为 18 位", trigger: "change" },
|
||||
],
|
||||
});
|
||||
|
||||
const emit = defineEmits(["unBindCard", "disconnect"]);
|
||||
|
||||
const props = defineProps({
|
||||
cardid: String,
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
let filterValue = value.replace(/\D/g, "");
|
||||
if (label == "unbindIMSI") {
|
||||
ruleForm.unbindIMSI = filterValue;
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
if (!ruleFormRef) return;
|
||||
formElClone.value = ruleFormRef.value;
|
||||
formElClone.value.resetFields();
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const unbind = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
const data = {
|
||||
cardID: props.cardid,
|
||||
IMSI: ruleForm.unbindIMSI,
|
||||
};
|
||||
$http.post($url.unBindingCard, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "解绑成功!",
|
||||
type: "success",
|
||||
});
|
||||
emit("unBindCard");
|
||||
ruleForm.unbindIMSI = "";
|
||||
emit("disconnect");
|
||||
window.location.reload();
|
||||
} else {
|
||||
ElMessage.error("解绑失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible });
|
||||
|
||||
watch(dialogVisible, (value) => {
|
||||
if (!value) {
|
||||
resetForm(ruleFormRef.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
344
src/components/manager/userManager.vue
Normal file
@@ -0,0 +1,344 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
账户管理
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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="account_name">
|
||||
<el-input
|
||||
:disabled="acabled"
|
||||
v-model="ruleForm.account_name"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="姓名" prop="user_name">
|
||||
<el-input
|
||||
maxlength="20"
|
||||
clearable
|
||||
placeholder="请输入姓名"
|
||||
v-model="ruleForm.user_name"
|
||||
@input="filter(ruleForm.user_name, 'user_name')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="手机号码" prop="phone">
|
||||
<el-input
|
||||
minlength="11"
|
||||
maxlength="11"
|
||||
clearable
|
||||
placeholder="请输入手机号码"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
class="mx-1 font-bold"
|
||||
v-if="sideMark == 'C'"
|
||||
label="性别"
|
||||
prop="gender"
|
||||
>
|
||||
<el-radio-group v-model="ruleForm.gender">
|
||||
<el-radio value="1"> 男 </el-radio>
|
||||
<el-radio value="0"> 女 </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" v-if="sideMark == 'C'" label="年龄" prop="age">
|
||||
<el-input
|
||||
maxlength="2"
|
||||
clearable
|
||||
placeholder="请输入年龄"
|
||||
v-model="ruleForm.age"
|
||||
@input="filter(ruleForm.age, 'age')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
class="mx-1 font-bold"
|
||||
v-if="sideMark == 'C'"
|
||||
label="邮箱号"
|
||||
prop="email"
|
||||
>
|
||||
<el-input
|
||||
maxlength="254"
|
||||
clearable
|
||||
placeholder="请输入邮箱号"
|
||||
v-model="ruleForm.email"
|
||||
@input="filter(ruleForm.email, 'email')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
class="mx-1 font-bold"
|
||||
v-if="sideMark == 'C'"
|
||||
label="通信地址"
|
||||
prop="address"
|
||||
>
|
||||
<el-input
|
||||
maxlength="300"
|
||||
clearable
|
||||
placeholder="请输入通信地址"
|
||||
v-model="ruleForm.address"
|
||||
@input="filter(ruleForm.address, 'address')"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
class="mx-1 font-bold"
|
||||
v-if="sideMark == 'C'"
|
||||
label="邮政编码"
|
||||
prop="post_code"
|
||||
>
|
||||
<el-input
|
||||
maxlength="6"
|
||||
clearable
|
||||
placeholder="请输入邮政编码"
|
||||
v-model="ruleForm.post_code"
|
||||
@input="filter(ruleForm.post_code, 'post_code')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" @click="resetForm(ruleFormRef)">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
type="primary"
|
||||
@click="editUser(ruleFormRef)"
|
||||
>
|
||||
提交修改
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from "vue";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
import userManagerActiveStore from "../../stores/userManager";
|
||||
|
||||
import { encrypt, decrypt } from "../../utils/aes";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
const phoneRegex = /^(?:13\d|14[579]|15\d|16[67]|17[0135678]|18\d|19[89])\d{8}$/;
|
||||
const emailRegex = /^([a-zA-Z0-9]+[|-|.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[|-|.]?)*[a-zA-Z0-9]+(.[a-zA-Z]{2,3})+$/;
|
||||
const postCodeRegex = /^(0[1234567]|1[012356]|2[01234567]|3[0123456]|4[01234567]|5[1234567]|6[1234567]|7[012345]|8[013456])\d{4}$/;
|
||||
|
||||
const userManagerActive = userManagerActiveStore();
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
msg: Object,
|
||||
});
|
||||
|
||||
let sideMark;
|
||||
const acabled = ref(true);
|
||||
const ruleFormRef = ref(null);
|
||||
const formElClone = ref(null);
|
||||
const ruleForm = reactive({
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: "",
|
||||
});
|
||||
|
||||
const filter = (value, label) => {
|
||||
let filterValue;
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
filterValue = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
} else {
|
||||
filterValue = value;
|
||||
}
|
||||
if (label == "phone" || label == "age" || label == "post_code") {
|
||||
ruleForm[label] = filterValue.replace(/\D/g, "");
|
||||
} else {
|
||||
ruleForm[label] = filterValue;
|
||||
}
|
||||
};
|
||||
|
||||
const validatePhone = (rule, value, callback) => {
|
||||
if (value && !phoneRegex.test(value)) {
|
||||
callback(new Error("请输入正确的电话号码"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (value && !emailRegex.test(value)) {
|
||||
callback(new Error("请输入正确的邮箱地址"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateCode = (rule, value, callback) => {
|
||||
if (value && !postCodeRegex.test(value)) {
|
||||
callback(new Error("请输入正确的邮政编码"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
account_name: [{ required: false, message: "请输入用户名", trigger: "blur" }],
|
||||
user_name: [],
|
||||
phone: [
|
||||
{ required: false, message: "请输入手机号", trigger: "blur" },
|
||||
{ min: 11, max: 11, message: "输入长度应当为 11 位", trigger: "change" },
|
||||
{ validator: validatePhone },
|
||||
],
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
if (!ruleFormRef) return;
|
||||
formElClone.value = ruleFormRef.value;
|
||||
formElClone.value.resetFields();
|
||||
};
|
||||
|
||||
const resetForm = (formEl) => {
|
||||
dialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const emit = defineEmits(["resetData"]);
|
||||
|
||||
const reset = () => {
|
||||
ruleForm.account_name = props.msg.account_name;
|
||||
ruleForm.user_name = props.msg.user_name;
|
||||
ruleForm.phone = props.msg.phone;
|
||||
if (sideMark == "C") {
|
||||
ruleForm.gender = props.msg.gender ? "1" : "0";
|
||||
ruleForm.age = props.msg.age;
|
||||
ruleForm.email = props.msg.email;
|
||||
ruleForm.address = props.msg.address;
|
||||
ruleForm.post_code = props.msg.post_code;
|
||||
}
|
||||
};
|
||||
|
||||
const editUser = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
dialogVisible.value = false;
|
||||
const data = {
|
||||
userMsg: ruleForm,
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.editUser, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "用户信息修改成功!",
|
||||
type: "success",
|
||||
});
|
||||
emit("resetData");
|
||||
} else {
|
||||
ElMessage.error("用户信息修改失败!");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ dialogVisible, reset });
|
||||
|
||||
watch(ruleForm, (value) => {
|
||||
if (dialogVisible.value) {
|
||||
userManagerActive.dialogVisible = dialogVisible.value;
|
||||
userManagerActive.ruleForm.account_name = encrypt(value.account_name);
|
||||
userManagerActive.ruleForm.user_name = encrypt(value.user_name);
|
||||
userManagerActive.ruleForm.phone = encrypt(value.phone);
|
||||
if (sideMark == "C") {
|
||||
userManagerActive.ruleForm.gender = encrypt(value.gender);
|
||||
userManagerActive.ruleForm.age = encrypt(value.age.toString());
|
||||
userManagerActive.ruleForm.email = encrypt(value.email);
|
||||
userManagerActive.ruleForm.address = encrypt(value.address);
|
||||
userManagerActive.ruleForm.post_code = encrypt(value.post_code);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
watch(dialogVisible, (value) => {
|
||||
if (!value) {
|
||||
userManagerActive.dialogVisible = value;
|
||||
userManagerActive.ruleForm = {};
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
dialogVisible.value = userManagerActive.dialogVisible;
|
||||
if (localStorage.data) {
|
||||
let decryptData = decrypt(localStorage.data);
|
||||
sideMark = decryptData[decryptData.length - 1];
|
||||
}
|
||||
if (sideMark == "C") {
|
||||
acabled.value = false;
|
||||
ruleForm.gender = "";
|
||||
ruleForm.age = "";
|
||||
ruleForm.email = "";
|
||||
ruleForm.address = "";
|
||||
ruleForm.post_code = "";
|
||||
rules.account_name[0].required = true;
|
||||
rules.phone[0].required = true;
|
||||
rules.age = [];
|
||||
rules.email = [{ validator: validateEmail }];
|
||||
rules.address = [];
|
||||
rules.post_code = [
|
||||
{ min: 6, max: 6, message: "输入长度应当为 6 位", trigger: "change" },
|
||||
{ validator: validateCode },
|
||||
];
|
||||
}
|
||||
if (userManagerActive.dialogVisible) {
|
||||
ruleForm.account_name = decrypt(userManagerActive.ruleForm.account_name);
|
||||
ruleForm.user_name = decrypt(userManagerActive.ruleForm.user_name);
|
||||
ruleForm.phone = decrypt(userManagerActive.ruleForm.phone);
|
||||
if (sideMark == "C") {
|
||||
ruleForm.gender = decrypt(userManagerActive.ruleForm.gender);
|
||||
ruleForm.age = decrypt(userManagerActive.ruleForm.age);
|
||||
ruleForm.email = decrypt(userManagerActive.ruleForm.email);
|
||||
ruleForm.address = decrypt(userManagerActive.ruleForm.address);
|
||||
ruleForm.post_code = decrypt(userManagerActive.ruleForm.post_code);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
54
src/components/operate/dataShow.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
数据展示
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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-text class="block text-center">
|
||||
{{ dialogMsg }}
|
||||
</el-text>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" type="primary" @click="resetForm">
|
||||
返回
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
|
||||
const dialogMsg = ref();
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const resetForm = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
defineExpose({ dialogMsg, dialogVisible });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
140
src/components/operate/editBsideUser.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="operateDialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
{{ operateLabel }}
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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="account_name">
|
||||
<el-input
|
||||
disabled="false"
|
||||
v-model="ruleForm.account_name"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="用户姓名" prop="user_name">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入用户姓名"
|
||||
maxlength="6"
|
||||
autocomplete="off"
|
||||
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="phone">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入用户手机号码"
|
||||
maxlength="11"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.phone"
|
||||
@input="filter(ruleForm.phone, 'phone')"
|
||||
/>
|
||||
</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 operateDialogVisible = ref(false);
|
||||
const ruleFormRef = ref(null);
|
||||
const formElClone = ref(null);
|
||||
|
||||
const ruleForm = reactive({
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: ""
|
||||
});
|
||||
|
||||
const originMsg = reactive({});
|
||||
|
||||
const rules = reactive({});
|
||||
|
||||
const props = defineProps({
|
||||
operateLabel: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["requestOperate"]);
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
operateDialogVisible.value = false;
|
||||
if (!ruleFormRef) return;
|
||||
formElClone.value = ruleFormRef.value;
|
||||
formElClone.value.resetFields();
|
||||
};
|
||||
|
||||
const resetCheckForm = (formEl) => {
|
||||
operateDialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const confirm = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
emit("requestOperate");
|
||||
operateDialogVisible.value = false;
|
||||
formEl.resetFields();
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ operateDialogVisible, ruleForm, originMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
146
src/components/operate/passwordOauth.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="operateDialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
{{ operateLabel }}
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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="operateLabel == '重置密码' || operateLabel == '重置账号'"
|
||||
class="mx-1 font-bold"
|
||||
label="重置后的默认密码"
|
||||
prop="defaultPassword"
|
||||
>
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请设置重置后的默认密码"
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.defaultPassword"
|
||||
@input="filter(ruleForm.defaultPassword, 'defaultPassword')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="mx-1 font-bold" label="密码" prop="password">
|
||||
<el-input
|
||||
clearable
|
||||
placeholder="请输入密码"
|
||||
type="password"
|
||||
show-password
|
||||
maxlength="16"
|
||||
autocomplete="off"
|
||||
v-model="ruleForm.password"
|
||||
@input="filter(ruleForm.password, 'password')"
|
||||
oncut="return false"
|
||||
onpaste="return false"
|
||||
oncopy="return false"
|
||||
/>
|
||||
</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 operateDialogVisible = ref(false);
|
||||
const ruleFormRef = ref(null);
|
||||
const formElClone = ref(null);
|
||||
|
||||
const ruleForm = reactive({
|
||||
defaultPassword: "123456",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const rules = reactive({
|
||||
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
operateLabel: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["requestOperate"]);
|
||||
|
||||
const filter = (value, label) => {
|
||||
if (/[=\s"'+-]/.test(value)) {
|
||||
if (ruleForm[label]) {
|
||||
ruleForm[label] = value.replace(/[=\s"'+-]/g, "");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
operateDialogVisible.value = false;
|
||||
if (!ruleFormRef) return;
|
||||
formElClone.value = ruleFormRef.value;
|
||||
formElClone.value.resetFields();
|
||||
};
|
||||
|
||||
const resetCheckForm = (formEl) => {
|
||||
operateDialogVisible.value = false;
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
|
||||
const confirm = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
emit("requestOperate");
|
||||
operateDialogVisible.value = false;
|
||||
formEl.resetFields();
|
||||
} else {
|
||||
ElMessage.error("提交失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ operateDialogVisible, ruleForm });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
18
src/components/operate/shortMsg.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<div class="flex justify-between items-center flex-wrap">
|
||||
<el-text :style="textColor" class="mx-1 font-bold"> 当前消息: {{ props.socketMsg }} </el-text>
|
||||
<el-text :style="textColor" class="mx-1 font-bold"> 接收时间: {{ props.socketCreatedAt }} </el-text>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
textColor: String,
|
||||
socketMsg: String,
|
||||
socketCreatedAt: String,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
78
src/components/operate/tabelSelect.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<el-table-column min-width="180" width="180" :column-key="holderText.label">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<el-button
|
||||
v-if="operateType !== 'edit'"
|
||||
style="margin-left: 0px;font-weight: bold;"
|
||||
:type="multipleColor"
|
||||
:size="multipleSize"
|
||||
:plain="multiplePlain"
|
||||
@click="operateModel('multiple')"
|
||||
>
|
||||
{{ holderText.multiple }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="operateType !== 'edit'"
|
||||
style="margin-left: 0px;font-weight: bold;"
|
||||
:type="allColor"
|
||||
:size="allSize"
|
||||
:plain="allPlain"
|
||||
@click="operateModel('all')"
|
||||
>
|
||||
{{ holderText.all }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-text class="mx-1"> {{ holderText.label }} </el-text>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
style="font-weight: bold;"
|
||||
:type="singleColor"
|
||||
:size="singleSize"
|
||||
:plain="singlePlain"
|
||||
@click="operateMsg(scope.$index, scope.row)"
|
||||
>
|
||||
{{ holderText.single }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const model = ref("single");
|
||||
|
||||
const props = defineProps({
|
||||
holderText: Object,
|
||||
singleColor: String,
|
||||
multipleColor: String,
|
||||
allColor: String,
|
||||
singleSize: String,
|
||||
multipleSize: String,
|
||||
allSize: String,
|
||||
singlePlain: Boolean,
|
||||
multiplePlain: Boolean,
|
||||
allPlain: Boolean,
|
||||
operateType: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(["showComfirmDialog"]);
|
||||
|
||||
const operateModel = (modelType) => {
|
||||
model.value = modelType;
|
||||
emit("showComfirmDialog", { operateType: props.operateType });
|
||||
};
|
||||
|
||||
const operateMsg = (index, row) => {
|
||||
model.value = "single";
|
||||
emit("showComfirmDialog", { data: row, operateType: props.operateType });
|
||||
};
|
||||
|
||||
defineExpose({ model });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
113
src/components/operate/tabelTools.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-full h-8 flex flex-col justify-center items-center relative top-0 right-0"
|
||||
>
|
||||
<div class="absolute top-0 left-0" style="font-size: 1rem">
|
||||
<el-dropdown>
|
||||
<el-button style="font-weight: bold" type="warning" size="small" plain>
|
||||
表格工具
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :style="searchBgColor" @click="showTools('search')">
|
||||
<el-link style="font-weight: bold" :underline="false" type="success">
|
||||
搜索工具
|
||||
<el-icon v-show="showSearch" class="el-icon--right">
|
||||
<View />
|
||||
</el-icon>
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :style="sortBgColor" @click="showTools('sort')">
|
||||
<el-link style="font-weight: bold" :underline="false" type="success">
|
||||
排序工具
|
||||
<el-icon v-show="showSort" class="el-icon--right">
|
||||
<View />
|
||||
</el-icon>
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :style="operationBgColor" @click="showTools('operation')">
|
||||
<el-link style="font-weight: bold" :underline="false" type="success">
|
||||
操作工具
|
||||
<el-icon v-show="showOperation" class="el-icon--right">
|
||||
<View />
|
||||
</el-icon>
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { ArrowDown, View } from "@element-plus/icons-vue";
|
||||
|
||||
const showSearch = ref(false);
|
||||
const searchBgColor = ref("background-color: rgba(16, 184, 130, 0.1);");
|
||||
const showSort = ref(false);
|
||||
const sortBgColor = ref("background-color: rgba(16, 184, 130, 0.1);");
|
||||
const showOperation = ref(false);
|
||||
const operationBgColor = ref("background-color: rgba(16, 184, 130, 0.1);");
|
||||
|
||||
const props = defineProps({
|
||||
dialogObj: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["showTools"]);
|
||||
|
||||
const showTools = (operateType) => {
|
||||
switch (operateType) {
|
||||
case "search":
|
||||
emit("showTools", "search");
|
||||
showSearch.value = !showSearch.value;
|
||||
break;
|
||||
case "sort":
|
||||
emit("showTools", "sort");
|
||||
showSort.value = !showSort.value;
|
||||
break;
|
||||
case "operation":
|
||||
emit("showTools", "operation");
|
||||
showOperation.value = !showOperation.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
watch(showSearch, (value) => {
|
||||
if (value) {
|
||||
searchBgColor.value = "background-color: rgba(16, 184, 130, 0.2);";
|
||||
} else {
|
||||
searchBgColor.value = "background-color: rgba(16, 184, 130, 0.1);";
|
||||
}
|
||||
});
|
||||
|
||||
watch(showSort, (value) => {
|
||||
if (value) {
|
||||
sortBgColor.value = "background-color: rgba(16, 184, 130, 0.2);";
|
||||
} else {
|
||||
sortBgColor.value = "background-color: rgba(16, 184, 130, 0.1);";
|
||||
}
|
||||
});
|
||||
|
||||
watch(showOperation, (value) => {
|
||||
if (value) {
|
||||
operationBgColor.value = "background-color: rgba(16, 184, 130, 0.2);";
|
||||
} else {
|
||||
operationBgColor.value = "background-color: rgba(16, 184, 130, 0.1);";
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
showSearch.value = props.dialogObj.dialogSearchVisible;
|
||||
showSort.value = props.dialogObj.dialogSortVisible;
|
||||
showOperation.value = props.dialogObj.dialogOperationVisible;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
16
src/components/pageStructure/footer.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="w-full flex justify-center items-center flex-wrap">
|
||||
<el-text class="mx-1 font-bold">
|
||||
北斗短报文通信服务平台
|
||||
</el-text>
|
||||
|
||||
|
||||
<el-text class="mx-1 font-bold">
|
||||
技术支持电话:400-166-5286
|
||||
</el-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style scoped></style>
|
||||
217
src/components/pageStructure/header.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div style="width: 320px" class="flex flex-wrap justify-around items-center">
|
||||
<el-text class="mx-1 font-bold"> 平台号码: </el-text>
|
||||
<el-text style="color: rgb(50, 100, 200)" class="mx-1 font-bold"> 15950077 </el-text>
|
||||
<el-text class="mx-1 font-bold"> 卡号(北斗ID): </el-text>
|
||||
<el-text style="color: rgb(50, 100, 200)" class="mx-1 font-bold">
|
||||
{{ cardID ? cardID : "未绑定" }}
|
||||
</el-text>
|
||||
</div>
|
||||
|
||||
<div class="flex grow justify-around items-center">
|
||||
<el-button style="font-weight: bold" type="primary" @click="seleBind">
|
||||
{{ cardID ? "解绑" : "绑定" }}
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold" type="warning" @click="guide">
|
||||
帮助
|
||||
<el-icon class="el-icon--right">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-dropdown class="flex flex-auto justify-end items-center">
|
||||
<el-button style="font-weight: bold" type="success" size="small">
|
||||
账户名: {{ userMsg.account_name }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(56, 188, 248, 0.1)"
|
||||
@click="bindRecord"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="primary">
|
||||
绑卡记录
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(56, 188, 248, 0.1)"
|
||||
@click="editUser"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="primary">
|
||||
账户管理
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(56, 188, 248, 0.1)"
|
||||
@click="changePassword"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="primary">
|
||||
密码管理
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
style="background-color: rgba(240, 70, 70, 0.1)"
|
||||
@click="unLogin"
|
||||
>
|
||||
<el-link style="font-weight: bold" :underline="false" type="danger">
|
||||
退出登录
|
||||
</el-link>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<Bind @bindCard="bindCard" @connect="connect" ref="bindRef" :cardid="cardID" />
|
||||
<Unbind
|
||||
@unBindCard="unBindCard"
|
||||
@disconnect="disconnect"
|
||||
ref="unBindRef"
|
||||
:cardid="cardID"
|
||||
/>
|
||||
<Guide ref="guideRef" />
|
||||
<UserManager @resetData="resetData" ref="userManagerRef" :msg="userMsg" />
|
||||
<PasswordManager ref="passwordManagerRef" />
|
||||
<Record ref="recordRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { QuestionFilled, ArrowDown } from "@element-plus/icons-vue";
|
||||
|
||||
import $http from "../../api/index";
|
||||
import $url from "../../api/url";
|
||||
|
||||
import Bind from "../manager/bind.vue";
|
||||
import Unbind from "../manager/unbind.vue";
|
||||
import Guide from "../guide/guide.vue";
|
||||
import UserManager from "../manager/userManager.vue";
|
||||
import PasswordManager from "../manager/changePassword.vue";
|
||||
import Record from "../features/record.vue";
|
||||
|
||||
const cardID = ref("");
|
||||
const manager = ref(false);
|
||||
const bindRef = ref(null);
|
||||
const unBindRef = ref(null);
|
||||
const guideRef = ref(null);
|
||||
const userManagerRef = ref(null);
|
||||
const userMsg = ref({});
|
||||
const passwordManagerRef = ref(null);
|
||||
const recordRef = ref(null);
|
||||
|
||||
const emit = defineEmits(["connect", "disconnect"]);
|
||||
|
||||
const seleBind = () => {
|
||||
if (cardID.value == "") {
|
||||
bindRef.value.dialogVisible = true;
|
||||
} else {
|
||||
unBindRef.value.dialogVisible = true;
|
||||
}
|
||||
};
|
||||
|
||||
const guide = () => {
|
||||
guideRef.value.dialogVisible = true;
|
||||
};
|
||||
|
||||
const bindRecord = () => {
|
||||
recordRef.value.dialogVisible = true;
|
||||
};
|
||||
|
||||
const editUser = () => {
|
||||
if (userManagerRef.value) {
|
||||
userManagerRef.value.dialogVisible = true;
|
||||
userManagerRef.value.reset();
|
||||
}
|
||||
};
|
||||
|
||||
const changePassword = () => {
|
||||
if (passwordManagerRef.value) {
|
||||
passwordManagerRef.value.dialogVisible = true;
|
||||
}
|
||||
};
|
||||
|
||||
const unLogin = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
access_token: localStorage.access_token,
|
||||
refresh_token: localStorage.refresh_token,
|
||||
};
|
||||
$http.post($url.unLogin, data).then((res) => {
|
||||
emit("disconnect");
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
const bindCard = (bindCardID) => {
|
||||
cardID.value = bindCardID;
|
||||
recordRef.value.getRecord();
|
||||
};
|
||||
|
||||
const unBindCard = () => {
|
||||
cardID.value = "";
|
||||
recordRef.value.getRecord();
|
||||
};
|
||||
|
||||
const connect = () => {
|
||||
emit("connect");
|
||||
};
|
||||
|
||||
const disconnect = () => {
|
||||
emit("disconnect");
|
||||
};
|
||||
|
||||
const resetData = () => {
|
||||
const data = {
|
||||
uid: localStorage.data,
|
||||
};
|
||||
$http.post($url.getUser, data).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
ElMessage({
|
||||
message: "获取用户信息成功!",
|
||||
type: "success",
|
||||
});
|
||||
userMsg.value = res.data.userData;
|
||||
if (res.data.userData.role == 1) {
|
||||
manager.value = true;
|
||||
guideRef.value.manager = true;
|
||||
}
|
||||
if (res.data.userData.status == 0) {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: "您的账号正在使用默认密码,请尽快进行更改!",
|
||||
type: "warning",
|
||||
});
|
||||
}
|
||||
if (res.data.userData.status == 2) {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: "您的账号已被封禁,请联系管理员进行申诉!",
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("404");
|
||||
ElMessage.error("获取用户信息失败!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ cardID, manager });
|
||||
|
||||
watch(manager, (value) => {
|
||||
if (value) {
|
||||
emit("allowManager");
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
resetData();
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
125
src/components/pageStructure/main.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div style="height: 100%">
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<el-text style="font-size: 2.4rem" class="mx-1 font-bold" size="large">
|
||||
北斗短报文通信服务平台
|
||||
</el-text>
|
||||
</div>
|
||||
<el-menu
|
||||
class="flex flex-wrap font-bold"
|
||||
: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
|
||||
v-if="manager"
|
||||
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'">
|
||||
<ShortMsg :textColor="color" :socketMsg="message" :socketCreatedAt="created_at" />
|
||||
<br />
|
||||
<HistoryMsg ref="historyMsgRef" />
|
||||
</div>
|
||||
<div v-show="activeIndex === '2'">
|
||||
<el-button
|
||||
style="font-weight: bold"
|
||||
type="primary"
|
||||
@click="showModel"
|
||||
class="mr-4"
|
||||
>
|
||||
转换模式
|
||||
</el-button>
|
||||
<CodeHelp />
|
||||
<Encode v-if="showCode" />
|
||||
<Decode v-if="!showCode" />
|
||||
</div>
|
||||
<div v-show="activeIndex === '3'">
|
||||
<CalculationXOR />
|
||||
</div>
|
||||
<div v-show="activeIndex === '4'">
|
||||
<BsideManager ref="bsideManagerRef" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
|
||||
import ShortMsg from "../operate/shortMsg.vue";
|
||||
import HistoryMsg from "../features/historyMsg.vue";
|
||||
import CodeHelp from "../transcoding/codeHelp.vue";
|
||||
import Encode from "../transcoding/encode.vue";
|
||||
import Decode from "../transcoding/decode.vue";
|
||||
import CalculationXOR from "../calculationXOR/calculationXOR.vue";
|
||||
import BsideManager from "../features/bsideManager.vue";
|
||||
|
||||
import historyMsgActiveStore from "../../stores/historyMsg";
|
||||
import shortMsgActiveStore from "../../stores/shortMsg";
|
||||
|
||||
const historyMsgActive = historyMsgActiveStore();
|
||||
const shortMsgActive = shortMsgActiveStore();
|
||||
|
||||
const activeIndex = ref("1");
|
||||
const indexStyle = "background-color: rgb(241 245 249);";
|
||||
const activeStyle = "background-color: rgb(207 250 254);";
|
||||
const manager = ref(false);
|
||||
const color = ref();
|
||||
const message = ref();
|
||||
const created_at = ref();
|
||||
const showCode = ref(true);
|
||||
const historyMsgRef = ref(null);
|
||||
const bsideManagerRef = ref(null);
|
||||
|
||||
const handleSelect = (key, keyPath) => {
|
||||
activeIndex.value = key;
|
||||
};
|
||||
|
||||
const showModel = () => {
|
||||
showCode.value = !showCode.value;
|
||||
};
|
||||
|
||||
const historyMsg = () => {
|
||||
historyMsgRef.value.historyMsg();
|
||||
};
|
||||
|
||||
defineExpose({ manager, color, message, created_at, historyMsg });
|
||||
|
||||
watch(activeIndex, (value) => {
|
||||
historyMsgActive.activeIndex = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
activeIndex.value = historyMsgActive.activeIndex;
|
||||
message.value = shortMsgActive.message;
|
||||
created_at.value = shortMsgActive.created_at;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
94
src/components/search/dateTimePicker.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-date-picker
|
||||
v-model="searchMsg"
|
||||
type="datetimerange"
|
||||
size="small"
|
||||
range-separator="到"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
@change="search"
|
||||
style="width: 100%; font-size: 1rem"
|
||||
:popper-class="DateTimePicker"
|
||||
@visible-change="handleVisibleChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
DateTimePicker: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
}
|
||||
|
||||
const handleVisibleChange = () => {
|
||||
setTimeout(() => {
|
||||
let changeNode = window.document.getElementsByClassName(props.DateTimePicker)[0];
|
||||
let Triangles = changeNode.children[1];
|
||||
Triangles.style.display = "none";
|
||||
let elppNode = changeNode.children[0];
|
||||
elppNode.style.width = "30rem";
|
||||
elppNode.style.backgroundColor = "#aaffff";
|
||||
let elppbwNode = elppNode.children[0];
|
||||
elppbwNode.style.width = "30rem";
|
||||
let elppfNode = elppNode.children[1];
|
||||
elppfNode.style.width = "30rem";
|
||||
elppfNode.style.backgroundColor = "#aaffff";
|
||||
let elppbNode = elppbwNode.children[0];
|
||||
elppbNode.style.width = "30rem";
|
||||
let elppbCNode = elppbNode.children;
|
||||
elppbCNode[0].style.width = "30rem";
|
||||
let leftDateSelectNode = elppbCNode[0].children[0].children[1];
|
||||
let leftTimeSelectNode = leftDateSelectNode.children[0];
|
||||
leftTimeSelectNode.addEventListener("click", function () {
|
||||
let leftTimeTableNode = leftDateSelectNode.children[1];
|
||||
leftTimeTableNode.style.width = "10rem";
|
||||
leftTimeTableNode.style.fontSize = "1rem";
|
||||
leftTimeTableNode.style.transform = "translate(-20%, 0)";
|
||||
});
|
||||
let rightDateSelectNode = elppbCNode[0].children[2].children[1];
|
||||
let rightTimeSelectNode = rightDateSelectNode.children[0];
|
||||
rightTimeSelectNode.addEventListener("click", function () {
|
||||
let rightTimeTableNode = rightDateSelectNode.children[1];
|
||||
rightTimeTableNode.style.width = "10rem";
|
||||
rightTimeTableNode.style.fontSize = "1rem";
|
||||
rightTimeTableNode.style.transform = "translate(-20%, 0)";
|
||||
});
|
||||
elppbCNode[1].style.width = "15rem";
|
||||
let elppbCFCNode = elppbCNode[1].children;
|
||||
elppbCFCNode[0].style.fontSize = "1rem";
|
||||
let elppbCFCCNode = elppbCFCNode[0].children;
|
||||
elppbCFCCNode[0].style.margin = "0";
|
||||
elppbCFCCNode[1].style.margin = "0";
|
||||
elppbCFCCNode[2].style.fontSize = "1rem";
|
||||
elppbCFCCNode[2].style.margin = "0";
|
||||
elppbCFCNode[1].style.fontSize = "1rem";
|
||||
elppbCNode[2].style.width = "15rem";
|
||||
let elppbCSCNode = elppbCNode[2].children;
|
||||
elppbCSCNode[0].style.fontSize = "1rem";
|
||||
let elppbCSCCNode = elppbCSCNode[0].children;
|
||||
elppbCSCCNode[0].style.margin = "0";
|
||||
elppbCSCCNode[1].style.margin = "0";
|
||||
elppbCSCCNode[2].style.fontSize = "1rem";
|
||||
elppbCSCCNode[2].style.margin = "0";
|
||||
elppbCSCNode[1].style.fontSize = "1rem";
|
||||
}, 0);
|
||||
};
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
|
||||
onMounted(() => {
|
||||
handleVisibleChange();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
57
src/components/search/pagination.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="flex justify-center my-2">
|
||||
<el-pagination
|
||||
class="flex justify-center flex-wrap"
|
||||
v-model:current-page="offset"
|
||||
v-model:page-size="limit"
|
||||
:page-sizes="pageSizes"
|
||||
size="small"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="dataCount"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const offset = ref(1);
|
||||
const limit = ref(10);
|
||||
const dataCount = ref(0);
|
||||
const pageSizes = [10, 20, 50, 100];
|
||||
|
||||
const props = defineProps({
|
||||
active: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search", "sizeChangeBack"]);
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
offset.value = 1;
|
||||
props.active.offset = offset.value;
|
||||
emit("sizeChangeBack", val);
|
||||
limit.value = val;
|
||||
props.active.limit = limit.value;
|
||||
emit("search");
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
offset.value = val;
|
||||
props.active.offset = offset.value;
|
||||
emit("search");
|
||||
};
|
||||
|
||||
defineExpose({ offset, limit, dataCount, handleCurrentChange });
|
||||
|
||||
onMounted(() => {
|
||||
offset.value = props.active.offset;
|
||||
limit.value = props.active.limit;
|
||||
dataCount.value = props.active.dataCount;
|
||||
emit("search");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
40
src/components/search/searchKey.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="输入<span class='text-red-500 font-black'> *空 </span>可搜索空数据"
|
||||
raw-content
|
||||
placement="top"
|
||||
>
|
||||
<el-input
|
||||
size="small"
|
||||
:placeholder="holderText"
|
||||
clearable
|
||||
v-model="searchMsg"
|
||||
@blur="search"
|
||||
style="width: 100%; font-size: 1rem"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
holderText: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
};
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
42
src/components/search/selectSearch.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-select
|
||||
clearable
|
||||
size="small"
|
||||
:placeholder="holderText"
|
||||
v-model="searchMsg"
|
||||
@change="search"
|
||||
style="width: 100%; font-size: 1rem;"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
class="text-center"
|
||||
:style="item.style"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
holderText: String,
|
||||
options: Object
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
}
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
32
src/components/search/sortSearch.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<el-switch
|
||||
size="small"
|
||||
v-model="searchMsg"
|
||||
inline-prompt
|
||||
:active-text="activeText"
|
||||
:inactive-text="inactiveText"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949;"
|
||||
@change="search"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const searchMsg = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
activeText: String,
|
||||
inactiveText: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(["search"]);
|
||||
|
||||
const search = () => {
|
||||
emit("search");
|
||||
}
|
||||
|
||||
defineExpose({ searchMsg });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
96
src/components/transcoding/codeHelp.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<el-text class="mx-1 font-bold">
|
||||
注: 键盘输入的空格、回车、制表符以及普通英文、数字和标点,将自动转为对应的全角字符进行编码,使用时请注意
|
||||
</el-text>
|
||||
<el-link
|
||||
style="font-weight: bold"
|
||||
:underline="false"
|
||||
type="primary"
|
||||
@click="dialogVisible = true"
|
||||
>
|
||||
编码科普
|
||||
</el-link>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:show-close="false"
|
||||
style="width: 80%"
|
||||
center
|
||||
align-center
|
||||
>
|
||||
<template #header="{ close, titleId, titleClass }">
|
||||
<div
|
||||
style="font-size: 2rem"
|
||||
class="w-full font-bold"
|
||||
:id="titleId"
|
||||
:class="titleClass"
|
||||
>
|
||||
编码帮助
|
||||
</div>
|
||||
<div
|
||||
@click="close"
|
||||
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-text class="mx-1">
|
||||
<h1 class="font-bold text-xl">GB2312 编码表</h1>
|
||||
<br />
|
||||
GB 2312 标准由中国国家标准总局 1980 年发布,GB 即国标,共收录
|
||||
6763个汉字,其中一级汉字 3755 个,二级汉字 3008 个。
|
||||
<br />
|
||||
<br />
|
||||
<ul class="list-disc">
|
||||
<li class="ml-10">
|
||||
GB 2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆
|
||||
99.75% 的使用频率。
|
||||
</li>
|
||||
<li class="ml-10">
|
||||
对于人名、古汉语等方面出现的罕用字,GB 2312 不能处理,这导致了后来 GBK 及 GB
|
||||
18030 汉字字符集的出现。
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
GB 2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的
|
||||
682 个全角字符。
|
||||
<br />
|
||||
GB 2312 兼容 ASCII 码(0 -
|
||||
127),之后对任意一个图形字符都采用两个字节表示,高位字节和低位字节都大于127。
|
||||
<br />
|
||||
<br />
|
||||
GB 2312 字符集分成 94
|
||||
个区,每区有94个位,分别对应第一字节和第二字节,这种表示方式也称为区位码。
|
||||
<br />
|
||||
<br />
|
||||
<ul class="list-disc">
|
||||
<li class="ml-10">01-09 区为特殊符号</li>
|
||||
<li class="ml-10">10-15 区为用户自定义符号区(未编码)</li>
|
||||
<li class="ml-10">16-55 区为一级汉字,按拼音排序,共 3755 个</li>
|
||||
<li class="ml-10">56-87 区为二级汉字,按部首/笔画排序,共 3008 个</li>
|
||||
<li class="ml-10">88-94 区为用户自定义汉字区(未编码)</li>
|
||||
</ul>
|
||||
</el-text>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button style="font-weight: bold" type="primary" @click="back">
|
||||
返回
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { CloseBold } from "@element-plus/icons-vue";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const back = () => {
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
328
src/components/transcoding/decode.vue
Normal file
@@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<el-input
|
||||
placeholder="请输入编码"
|
||||
v-model="fontcode"
|
||||
type="textarea"
|
||||
resize="none"
|
||||
:rows="10"
|
||||
style="box-shadow: 0 0px 20px -10px rgb(0 0 0 / 0.5)"
|
||||
class="mt-4 mb-4"
|
||||
/>
|
||||
<div>
|
||||
<el-select
|
||||
v-model="decodeType"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="选择解码类型"
|
||||
style="width: 106px"
|
||||
class="mr-4"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="showType"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="选择显示类型"
|
||||
style="width: 106px"
|
||||
class="mr-4"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in showOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button style="font-weight: bold;" type="primary" @click="transformcode(fontcode)">
|
||||
转换编码
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold;" type="primary" @click="reset">
|
||||
清除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-card v-show="showType == 1" class="mt-4 mb-4">
|
||||
<el-text class="mx-1">
|
||||
编码(十进制): {{ fontcodedec ? fontcodedec.join(" , ") : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(十六进制): {{ fontcodehex ? fontcodehex.join(" , ") : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(二进制): {{ fontcodebin ? fontcodebin.join(" , ") : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text v-show="decodeType == 'transcode'" class="mx-1">
|
||||
汉字: {{ transcode }}
|
||||
</el-text>
|
||||
<el-text v-show="decodeType == 'transcodehex'" class="mx-1">
|
||||
汉字: {{ transcodehex }}
|
||||
</el-text>
|
||||
<el-text v-show="decodeType == 'transcodebin'" class="mx-1">
|
||||
汉字: {{ transcodebin }}
|
||||
</el-text>
|
||||
</el-card>
|
||||
<div class="flex flex-wrap justify-start items-center gap-4 mt-4 mb-4">
|
||||
<el-card
|
||||
v-for="(item, index) in chinafont"
|
||||
v-show="showType == 0"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-text class="mx-1">
|
||||
编码(十进制): {{ fontcodedec ? fontcodedec[index] : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(十六进制): {{ fontcodehex ? fontcodehex[index] : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(二进制): {{ fontcodebin ? fontcodebin[index] : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text v-show="decodeType == 'transcode'" class="mx-1">
|
||||
汉字: {{ transcode[index] }}
|
||||
</el-text>
|
||||
<el-text v-show="decodeType == 'transcodehex'" class="mx-1">
|
||||
汉字: {{ transcodehex[index] }}
|
||||
</el-text>
|
||||
<el-text v-show="decodeType == 'transcodebin'" class="mx-1">
|
||||
汉字: {{ transcodebin[index] }}
|
||||
</el-text>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
|
||||
import decodeActiveStore from "../../stores/decode";
|
||||
|
||||
import { toFullWidth } from "../../utils/toFullWidth";
|
||||
|
||||
import GBK from "gbk.js";
|
||||
|
||||
const decodeActive = decodeActiveStore();
|
||||
|
||||
const decodeType = ref("transcode");
|
||||
const showType = ref("1");
|
||||
const chinafont = ref();
|
||||
const fontcode = ref();
|
||||
const fontcodedec = ref();
|
||||
const fontcodehex = ref();
|
||||
const fontcodebin = ref();
|
||||
const transcode = ref();
|
||||
const transcodehex = ref();
|
||||
const transcodebin = ref();
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: "transcode",
|
||||
label: "十进制",
|
||||
},
|
||||
{
|
||||
value: "transcodehex",
|
||||
label: "十六进制",
|
||||
},
|
||||
{
|
||||
value: "transcodebin",
|
||||
label: "二进制",
|
||||
},
|
||||
];
|
||||
|
||||
const showOptions = [
|
||||
{
|
||||
value: "0",
|
||||
label: "单字",
|
||||
},
|
||||
{
|
||||
value: "1",
|
||||
label: "连续",
|
||||
},
|
||||
];
|
||||
|
||||
const decToHex = (dec) => {
|
||||
let hexArr = [];
|
||||
dec.forEach((element) => {
|
||||
let hex = parseInt(element, 10).toString(16).toUpperCase();
|
||||
if (hex.length == 1) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
hexArr.push(hex);
|
||||
});
|
||||
return hexArr.join(" , ");
|
||||
};
|
||||
|
||||
const decToBinary = (dec) => {
|
||||
let binaryArr = [];
|
||||
dec.forEach((element) => {
|
||||
let binaryString = parseInt(element, 10).toString(2);
|
||||
while (binaryString.length < 8) {
|
||||
binaryString = "0" + binaryString;
|
||||
}
|
||||
binaryArr.push(binaryString);
|
||||
});
|
||||
return binaryArr.join(" , ");
|
||||
};
|
||||
|
||||
const BinaryToDec = (binary) => {
|
||||
let decArr = [];
|
||||
binary.forEach((element) => {
|
||||
let dec = parseInt(element, 2);
|
||||
decArr.push(dec);
|
||||
});
|
||||
return decArr.join(" , ");
|
||||
};
|
||||
|
||||
const BinaryToHex = (binary) => {
|
||||
let hexArr = [];
|
||||
binary.forEach((element) => {
|
||||
let hex = parseInt(element, 2).toString(16).toUpperCase();
|
||||
if (hex.length == 1) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
hexArr.push(hex);
|
||||
});
|
||||
return hexArr.join(" , ");
|
||||
};
|
||||
|
||||
const transformcode = (fontcode) => {
|
||||
decodeActive.fontcode = fontcode;
|
||||
let dec = [], hex = [], bin = [];
|
||||
fontcode.replace(/\s+/g, '').replace(/,/g, ",").split(",").forEach((element) => {
|
||||
let newArray;
|
||||
if(element.length >= 4 && decodeType.value == "transcodehex") {
|
||||
newArray = element.split("").reduce((acc, curr, index) => {
|
||||
if (index % 2 === 0 && index + 1 < element.split("").length) {
|
||||
acc.push([curr, element.split("")[index + 1]].join(""));
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
if(newArray.length > 2) {
|
||||
newArray.shift();
|
||||
newArray = newArray.join(",");
|
||||
}
|
||||
} else {
|
||||
newArray = element;
|
||||
}
|
||||
hex.push(parseInt(newArray, 16));
|
||||
dec.push(newArray);
|
||||
bin.push(parseInt(newArray, 2));
|
||||
});
|
||||
transcode.value = "";
|
||||
decodeActive.transcode = "";
|
||||
transcodehex.value = "";
|
||||
decodeActive.transcodehex = "";
|
||||
transcodebin.value = "";
|
||||
decodeActive.transcodebin = "";
|
||||
switch (decodeType.value) {
|
||||
case "transcode":
|
||||
chinafont.value = GBK.decode(dec);
|
||||
transcode.value = GBK.decode(dec);
|
||||
decodeActive.transcode = transcode.value;
|
||||
fontcodedec.value = fontcode.split(",").join(" , ");
|
||||
decodeActive.fontcodedec = fontcode.split(",").join(" , ");
|
||||
fontcodehex.value = decToHex(fontcode.split(","));
|
||||
decodeActive.fontcodehex = decToHex(fontcode.split(","));
|
||||
fontcodebin.value = decToBinary(fontcode.split(","));
|
||||
decodeActive.fontcodebin = decToBinary(fontcode.split(","));
|
||||
break;
|
||||
case "transcodehex":
|
||||
chinafont.value = GBK.decode(hex);
|
||||
transcodehex.value = GBK.decode(hex);
|
||||
decodeActive.transcodehex = transcodehex.value;
|
||||
fontcodedec.value = hex.join(" , ");
|
||||
decodeActive.fontcodedec = hex.join(" , ");
|
||||
fontcodehex.value = fontcode.split(",").join(" , ");
|
||||
decodeActive.fontcodehex = fontcode.split(",").join(" , ");
|
||||
fontcodebin.value = decToBinary(hex);
|
||||
decodeActive.fontcodebin = decToBinary(hex);
|
||||
break;
|
||||
case "transcodebin":
|
||||
chinafont.value = GBK.decode(bin);
|
||||
transcodebin.value = GBK.decode(bin);
|
||||
decodeActive.transcodebin = transcodebin.value;
|
||||
fontcodedec.value = BinaryToDec(fontcode.split(","));
|
||||
decodeActive.fontcodedec = BinaryToDec(fontcode.split(","));
|
||||
fontcodehex.value = BinaryToHex(fontcode.split(","));
|
||||
decodeActive.fontcodehex = BinaryToHex(fontcode.split(","));
|
||||
fontcodebin.value = fontcode.split(",").join(" , ");
|
||||
decodeActive.fontcodebin = fontcode.split(",").join(" , ");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
let transfont = [];
|
||||
hex = [], bin = [];
|
||||
//将半角字符转为全角字符
|
||||
toFullWidth(chinafont.value).split("").forEach((item) => {
|
||||
transfont.push(GBK.encode(item).join(" , "));
|
||||
let hexSingle = [], binSingle = [];
|
||||
GBK.encode(item).forEach((element) => {
|
||||
hexSingle.push(element.toString(16).toUpperCase().length < 2 ? "0" + element.toString(16).toUpperCase() : element.toString(16).toUpperCase());
|
||||
binSingle.push(element.toString(2));
|
||||
});
|
||||
hex.push(hexSingle.join(""));
|
||||
bin.push(binSingle.join(" , "));
|
||||
});
|
||||
fontcodedec.value = transfont;
|
||||
decodeActive.fontcodedec = transfont;
|
||||
fontcodehex.value = hex;
|
||||
decodeActive.fontcodehex = hex;
|
||||
fontcodebin.value = bin;
|
||||
decodeActive.fontcodebin = bin;
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
fontcode.value = "";
|
||||
fontcodedec.value = "";
|
||||
fontcodehex.value = "";
|
||||
fontcodebin.value = "";
|
||||
transcode.value = "";
|
||||
transcodehex.value = "";
|
||||
transcodebin.value = "";
|
||||
chinafont.value = "";
|
||||
decodeActive.fontcode = "";
|
||||
decodeActive.fontcodedec = "";
|
||||
decodeActive.fontcodehex = "";
|
||||
decodeActive.fontcodebin = "";
|
||||
decodeActive.transcode = "";
|
||||
decodeActive.transcodehex = "";
|
||||
decodeActive.transcodebin = "";
|
||||
};
|
||||
|
||||
watch(fontcode, (value) => {
|
||||
decodeActive.fontcode = value;
|
||||
});
|
||||
|
||||
watch(decodeType, (value) => {
|
||||
decodeActive.decodeType = value;
|
||||
});
|
||||
|
||||
watch(showType, (value) => {
|
||||
decodeActive.showType = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
decodeType.value = decodeActive.decodeType;
|
||||
showType.value = decodeActive.showType;
|
||||
fontcode.value = decodeActive.fontcode;
|
||||
fontcodedec.value = decodeActive.fontcodedec;
|
||||
fontcodehex.value = decodeActive.fontcodehex;
|
||||
fontcodebin.value = decodeActive.fontcodebin;
|
||||
transcode.value = decodeActive.transcode;
|
||||
transcodehex.value = decodeActive.transcodehex;
|
||||
transcodebin.value = decodeActive.transcodebin;
|
||||
chinafont.value = decodeActive[decodeType.value];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
159
src/components/transcoding/encode.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<el-input
|
||||
placeholder="请输入汉字"
|
||||
v-model="inputfont"
|
||||
type="textarea"
|
||||
resize="none"
|
||||
:rows="10"
|
||||
style="box-shadow: 0 0px 20px -10px rgb(0 0 0 / 0.5);"
|
||||
class="mt-4 mb-4"
|
||||
/>
|
||||
<div>
|
||||
<el-select
|
||||
v-model="showType"
|
||||
clearable
|
||||
size="small"
|
||||
placeholder="选择显示类型"
|
||||
style="width: 106px"
|
||||
class="mr-4"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button style="font-weight: bold;" type="primary" @click="transformfont(inputfont)">
|
||||
转换汉字
|
||||
</el-button>
|
||||
<el-button style="font-weight: bold;" type="primary" @click="reset">
|
||||
清除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-card v-show="showType == 1" class="mt-4 mb-4">
|
||||
<el-text class="mx-1">
|
||||
汉字: {{ chinafont }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(十进制): {{ transfont ? transfont.join(" , ") : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(十六进制): {{ transfonthex ? transfonthex.join(" , ") : "" }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(二进制): {{ transfontbin ? transfontbin.join(" , ") : "" }}
|
||||
</el-text>
|
||||
</el-card>
|
||||
<div class="flex flex-wrap justify-start items-center gap-4 mt-4 mb-4">
|
||||
<el-card
|
||||
v-for="(item, index) in chinafont"
|
||||
v-show="showType == 0"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-text class="mx-1">
|
||||
汉字: {{ item }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(十进制): {{ transfont[index] }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(十六进制): {{ transfonthex[index] }}
|
||||
</el-text>
|
||||
<br />
|
||||
<el-text class="mx-1">
|
||||
编码(二进制): {{ transfontbin[index] }}
|
||||
</el-text>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
|
||||
import encodeActiveStore from "../../stores/encode";
|
||||
|
||||
import { toFullWidth } from "../../utils/toFullWidth";
|
||||
|
||||
import GBK from "gbk.js";
|
||||
|
||||
const encodeActive = encodeActiveStore();
|
||||
|
||||
const showType = ref(1);
|
||||
const inputfont = ref();
|
||||
const chinafont = ref();
|
||||
const transfont = ref();
|
||||
const transfonthex = ref();
|
||||
const transfontbin = ref();
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: 0,
|
||||
label: "单字",
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: "连续",
|
||||
},
|
||||
];
|
||||
|
||||
const transformfont = (inputfont) => {
|
||||
chinafont.value = inputfont;
|
||||
encodeActive.inputfont = inputfont;
|
||||
//将半角字符转为全角字符
|
||||
let inputfontFullWidth = toFullWidth(inputfont);
|
||||
transfont.value = [];
|
||||
let hex = [], bin = [];
|
||||
inputfontFullWidth.split("").forEach((item) => {
|
||||
transfont.value.push(GBK.encode(item).join(" , "));
|
||||
let hexSingle = [], binSingle = [];
|
||||
GBK.encode(item).forEach((element) => {
|
||||
hexSingle.push(element.toString(16).toUpperCase().length < 2 ? "0" + element.toString(16).toUpperCase() : element.toString(16).toUpperCase());
|
||||
binSingle.push(element.toString(2));
|
||||
});
|
||||
hex.push(hexSingle.join(""));
|
||||
bin.push(binSingle.join(" , "));
|
||||
});
|
||||
encodeActive.transfont = transfont.value;
|
||||
transfonthex.value = hex;
|
||||
encodeActive.transfonthex = transfonthex.value;
|
||||
transfontbin.value = bin;
|
||||
encodeActive.transfontbin = transfontbin.value;
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
inputfont.value = "";
|
||||
chinafont.value = "";
|
||||
transfont.value = "";
|
||||
transfonthex.value = "";
|
||||
transfontbin.value = "";
|
||||
encodeActive.inputfont = "";
|
||||
encodeActive.transfont = "";
|
||||
encodeActive.transfonthex = "";
|
||||
encodeActive.transfontbin = "";
|
||||
};
|
||||
|
||||
watch(inputfont, (value) => {
|
||||
encodeActive.inputfont = value;
|
||||
});
|
||||
|
||||
watch(showType, (value) => {
|
||||
encodeActive.showType = value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
showType.value = encodeActive.showType;
|
||||
inputfont.value = encodeActive.inputfont;
|
||||
chinafont.value = encodeActive.inputfont;
|
||||
transfont.value = encodeActive.transfont;
|
||||
transfonthex.value = encodeActive.transfonthex;
|
||||
transfontbin.value = encodeActive.transfontbin;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
23
src/main.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import piniaPersist from 'pinia-plugin-persist';
|
||||
import ElementPlus from 'element-plus';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
import 'element-plus/dist/index.css';
|
||||
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
|
||||
const app = createApp(App); // 创建 vue 实例
|
||||
|
||||
const store = createPinia(); // 创建 pinia 实例
|
||||
|
||||
store.use(piniaPersist);
|
||||
|
||||
app.use(store);
|
||||
app.use(ElementPlus, { locale: zhCn });
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app');
|
||||
31
src/router/index.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/main',
|
||||
name: 'main',
|
||||
component: () => import('../views/Main.vue')
|
||||
},
|
||||
{
|
||||
path: '/404page',
|
||||
name: '404',
|
||||
component: () => import('../views/404page.vue')
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.matched.length === 0) {
|
||||
if (to.path == "/") {
|
||||
router.push({ path: '/main' });
|
||||
} else {
|
||||
router.push({ path: '/404page' });
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
37
src/stores/bsideManager.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const bsideManagerActiveStore = defineStore('bsideManager', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
dataCount: 0,
|
||||
pageCount: 0,
|
||||
searchOptions: {
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: "",
|
||||
},
|
||||
dialogVisible: false,
|
||||
dialogSearchVisible: false,
|
||||
dialogSortVisible: false,
|
||||
dialogOperationVisible: false
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'bsideManagerActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["offset", "limit", "dataCount", "pageCount", "searchOptions", "dialogVisible", "dialogSearchVisible", "dialogSortVisible", "dialogOperationVisible"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default bsideManagerActiveStore;
|
||||
33
src/stores/decode.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const decodeActiveStore = defineStore('decode', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
decodeType: "",
|
||||
showType: "",
|
||||
fontcode: "",
|
||||
transcode: "",
|
||||
transcodehex: "",
|
||||
transcodebin: "",
|
||||
fontcodedec: "",
|
||||
fontcodehex: "",
|
||||
fontcodebin: "",
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'decodeActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["decodeType", "showType", "fontcode", "transcode", "transcodehex", "transcodebin", "fontcodedec", "fontcodehex", "fontcodebin"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export default decodeActiveStore;
|
||||
29
src/stores/encode.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const encodeActiveStore = defineStore('encode', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
showType: "",
|
||||
inputfont: "",
|
||||
transfont: "",
|
||||
transfonthex: "",
|
||||
transfontbin: "",
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'encodeActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["showType", "inputfont", "transfont", "transfonthex", "transfontbin"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default encodeActiveStore;
|
||||
42
src/stores/historyMsg.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const historyMsgActiveStore = defineStore('historyMsg', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
activeIndex: "1",
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
dataCount: 0,
|
||||
pageCount: 0,
|
||||
searchOptions: {
|
||||
card_id: "",
|
||||
encode_type: "",
|
||||
msg_data: "",
|
||||
created_at: "",
|
||||
},
|
||||
sortOptions: {
|
||||
card_id: false,
|
||||
created_at: false,
|
||||
},
|
||||
dialogSearchVisible: false,
|
||||
dialogSortVisible: false,
|
||||
dialogOperationVisible: false
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'historyMsgActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["activeIndex", "offset", "limit", "dataCount", "pageCount", "searchOptions", "sortOptions", "dialogSearchVisible", "dialogSortVisible", "dialogOperationVisible"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default historyMsgActiveStore;
|
||||
39
src/stores/record.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const recordActiveStore = defineStore('record', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
dataCount: 0,
|
||||
searchOptions: {
|
||||
card_id: "",
|
||||
type: "",
|
||||
created_at: "",
|
||||
},
|
||||
sortOptions: {
|
||||
card_id: false,
|
||||
created_at: false,
|
||||
},
|
||||
dialogSearchVisible: false,
|
||||
dialogSortVisible: false,
|
||||
dialogOperationVisible: false
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'recordActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["offset", "limit", "dataCount", "searchOptions", "sortOptions", "dialogSearchVisible", "dialogSortVisible", "dialogOperationVisible"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default recordActiveStore;
|
||||
26
src/stores/shortMsg.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const shortMsgActiveStore = defineStore('shortMsg', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
message: "",
|
||||
created_at: ""
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'shortMsgActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["message", "created_at"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default shortMsgActiveStore;
|
||||
35
src/stores/userManager.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { parse, stringify } from 'zipson';
|
||||
|
||||
const userManagerActiveStore = defineStore('userManager', {
|
||||
state: () => ({
|
||||
// 持久化数据
|
||||
dialogVisible: false,
|
||||
ruleForm: {
|
||||
account_name: "",
|
||||
user_name: "",
|
||||
phone: "",
|
||||
gender: "",
|
||||
age: "",
|
||||
email: "",
|
||||
address: "",
|
||||
post_code: "",
|
||||
},
|
||||
}),
|
||||
persist: {
|
||||
enabled: true, // Store 中数据持久化生效
|
||||
strategies: [
|
||||
{
|
||||
key: 'userManagerActive', // 存储的 key
|
||||
storage: localStorage, // 存储位置
|
||||
paths: ["dialogVisible", "ruleForm"], // 需要持久化的字段
|
||||
serializer: {
|
||||
deserialize: parse,
|
||||
serialize: stringify,
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
export default userManagerActiveStore;
|
||||
21
src/utils/aes.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
const KEY = CryptoJS.enc.Utf8.parse('BDQH');
|
||||
|
||||
export const encrypt = (plaintext) => {
|
||||
if(plaintext !== null && plaintext !== undefined && plaintext !== "") {
|
||||
return CryptoJS.AES.encrypt(plaintext, KEY, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString();
|
||||
}
|
||||
};
|
||||
|
||||
export const decrypt = (plaintext) => {
|
||||
if(plaintext !== null && plaintext !== undefined && !plaintext == "") {
|
||||
return CryptoJS.AES.decrypt(plaintext, KEY, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString(CryptoJS.enc.Utf8);
|
||||
}
|
||||
};
|
||||
9
src/utils/hasNullOrEmptyPropertyWithTypeof.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export const hasNullOrEmptyPropertyWithTypeof = (obj) => {
|
||||
let hasNullOrEmpty = false;
|
||||
for (const prop in obj) {
|
||||
if (!(obj.hasOwnProperty(prop) && ((obj[prop] === null) || obj[prop] === undefined || (typeof obj[prop] === 'string' && obj[prop] === '')))) {
|
||||
hasNullOrEmpty = true;
|
||||
}
|
||||
}
|
||||
return hasNullOrEmpty;
|
||||
}
|
||||
5
src/utils/judgment.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export const judgment = (keyWord, key, value, val) => {
|
||||
if (value !== null) {
|
||||
keyWord[key] = value[val];
|
||||
}
|
||||
};
|
||||
7
src/utils/toFullWidth.js
Normal file
@@ -0,0 +1,7 @@
|
||||
//将半角空格,制表符,回车和半角字符转为全角空格,全角制表符,全角回车和全角字符
|
||||
export const toFullWidth = (str) => {
|
||||
//全角制表符使用4个全角空格模拟
|
||||
return str.replace(/ /g, '\u3000').replace(/\t/g, '\u3000\u3000\u3000\u3000').replace(/\n/g, '\u21B5').replace(/[\x21-\x7E]/g, (font) => {
|
||||
return String.fromCharCode(font.charCodeAt(0) + 0xFEE0);
|
||||
});
|
||||
};
|
||||
21
src/views/404page.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="common-layout w-h-full">
|
||||
<el-text class="mx-1"> 404 not found </el-text>
|
||||
<br />
|
||||
<el-text class="mx-1"> 您搜索的页面不存在 </el-text>
|
||||
<br />
|
||||
<el-button style="font-weight: bold;" type="primary" @click="backHome">返回首页</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const backHome = () => {
|
||||
router.push("/main");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
186
src/views/Main.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<el-container class="w-h-full">
|
||||
<el-header class="flex flex-wrap" style="height: 80px; background-color: #f0f0f0">
|
||||
<Header
|
||||
ref="headerRef"
|
||||
@allowManager="allowManager"
|
||||
@connect="connect"
|
||||
@disconnect="disconnect"
|
||||
/>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<Main ref="mainRef" />
|
||||
</el-main>
|
||||
<el-footer class="flex" style="background-color: #f2f2f9">
|
||||
<Footer />
|
||||
</el-footer>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeMount, onBeforeUnmount } from "vue";
|
||||
|
||||
import io from "socket.io-client";
|
||||
|
||||
import GBK from "gbk.js";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
import $http from "../api/index";
|
||||
import $url from "../api/url";
|
||||
|
||||
import shortMsgActiveStore from "../stores/shortMsg";
|
||||
|
||||
import Header from "../components/pageStructure/header.vue";
|
||||
import Main from "../components/pageStructure/main.vue";
|
||||
import Footer from "../components/pageStructure/footer.vue";
|
||||
|
||||
const shortMsgActive = shortMsgActiveStore();
|
||||
|
||||
const headerRef = ref(null);
|
||||
const mainRef = ref(null);
|
||||
|
||||
//开发/测试环境
|
||||
// const socket = io("http://127.0.0.1:8010", {
|
||||
// 生产环境
|
||||
const socket = io("http://bdrdserver.hwasmart.com:8010", {
|
||||
// path: "/bdrdWebSocket/",
|
||||
transports: ["websocket", "polling"],
|
||||
reconnection: true,
|
||||
reconnectionDelay: 1000,
|
||||
reconnectionDelayMax: 6000,
|
||||
reconnectionAttempts: 10,
|
||||
});
|
||||
|
||||
const decode = (data) => {
|
||||
let arr = [];
|
||||
data.match(/..?/g).forEach((element) => {
|
||||
arr.push(parseInt(element, 16));
|
||||
});
|
||||
return GBK.decode(arr);
|
||||
};
|
||||
|
||||
const getMsg = () => {
|
||||
socket.emit("message", headerRef.value.cardID);
|
||||
socket.removeListener(headerRef.value.cardID);
|
||||
if (socket.listeners(headerRef.value.cardID).length == 0) {
|
||||
socket.on(headerRef.value.cardID, (msg) => {
|
||||
const { encode_type, msg_data, created_at } = msg;
|
||||
mainRef.value.message = Number(encode_type) == 0 ? decode(msg_data) : msg_data;
|
||||
shortMsgActive.message = mainRef.value.message;
|
||||
mainRef.value.created_at = moment
|
||||
.utc(created_at)
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
shortMsgActive.created_at = mainRef.value.created_at;
|
||||
mainRef.value.historyMsg();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const connect = () => {
|
||||
socket.connect();
|
||||
};
|
||||
|
||||
const disconnect = () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
|
||||
const allowManager = () => {
|
||||
if (headerRef.value.manager) {
|
||||
mainRef.value.manager = true;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
mainRef.value.color = "color: rgb(56 188 248);";
|
||||
mainRef.value.message = "连接中...";
|
||||
mainRef.value.created_at = moment
|
||||
.utc(new Date())
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
$http.post($url.getCard, { uid: localStorage.data }).then((res) => {
|
||||
if (res.data.code === 200) {
|
||||
headerRef.value.cardID = res.data.cardID;
|
||||
|
||||
let connectStatus = false,
|
||||
reconnectStatus = false;
|
||||
socket.on("connect", () => {
|
||||
connectStatus = true;
|
||||
mainRef.value.color = "color: rgb(16 184 130);";
|
||||
mainRef.value.message = "连接成功!";
|
||||
mainRef.value.created_at = moment
|
||||
.utc(new Date())
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
getMsg();
|
||||
});
|
||||
|
||||
socket.on("disconnect", (reason) => {
|
||||
mainRef.value.color = "color: rgb(240 70 70);";
|
||||
mainRef.value.message = "连接断开!请检查网络状态并刷新网页重试!";
|
||||
mainRef.value.created_at = moment
|
||||
.utc(new Date())
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
if (reason === "io server disconnect" || reason === "io client disconnect") {
|
||||
if (headerRef.value.cardID !== "") {
|
||||
mainRef.value.color = "color: rgb(56 188 248);";
|
||||
mainRef.value.message = "连接中...";
|
||||
mainRef.value.created_at = moment
|
||||
.utc(new Date())
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
socket.removeListener(headerRef.value.cardID);
|
||||
socket.connect();
|
||||
reconnectStatus = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("connect_error", (err) => {
|
||||
// 如果连接异常,修改transports传输方式
|
||||
socket.io.opts.transports = ["websocket", "polling"];
|
||||
if (reconnectStatus) {
|
||||
setTimeout(() => {
|
||||
reconnectStatus = false;
|
||||
mainRef.value.color = "color: rgb(240 70 70);";
|
||||
mainRef.value.message = "连接异常!请检查网络状态并刷新网页重试!";
|
||||
mainRef.value.created_at = moment
|
||||
.utc(new Date())
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
}, socket.io.opts.reconnectionAttempts * socket.io.opts.reconnectionDelayMax);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
if (!connectStatus) {
|
||||
mainRef.value.color = "color: rgb(240 70 70);";
|
||||
mainRef.value.message = "连接异常!请检查网络状态并刷新网页重试!";
|
||||
mainRef.value.created_at = moment
|
||||
.utc(new Date())
|
||||
.add(8, "hours")
|
||||
.format("YYYY-MM-DD-HH:mm:ss");
|
||||
}
|
||||
}, socket.io.opts.reconnectionAttempts * socket.io.opts.reconnectionDelayMax);
|
||||
} else {
|
||||
socket.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (socket) {
|
||||
socket.connect();
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (socket) {
|
||||
socket.disconnect(); // 组件卸载时断开连接
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
13
tailwind.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
content: [],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
54
vite.config.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
import legacy from '@vitejs/plugin-legacy'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
publicPath: './',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
legacy({
|
||||
target: ['ie>=11'],
|
||||
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server: {
|
||||
//用来配置跨域
|
||||
host: '127.0.0.1',
|
||||
port: 7003,
|
||||
proxy: {
|
||||
'/authentication': {
|
||||
target: 'http://127.0.0.1:8011',//鉴权服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/authentication/, '')
|
||||
},
|
||||
'/bdShortMessage': {
|
||||
target: 'http://127.0.0.1:8090',//资源服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/bdShortMessage/, '')
|
||||
},
|
||||
'/bdrdWebSocket': {
|
||||
target: 'http://127.0.0.1:8010',//webSocket服务器地址
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/bdrdWebSocket/, '')
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||