2025年04月12日18:35:52
This commit is contained in:
parent
f82d3934ba
commit
6b04c453d9
2
auto-imports.d.ts
vendored
2
auto-imports.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
export {}
|
||||
declare global {
|
||||
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||
|
||||
}
|
||||
|
22
src/api/index.d.ts
vendored
22
src/api/index.d.ts
vendored
@ -22,6 +22,10 @@ export interface TRoleList {
|
||||
createTime?: string
|
||||
roleMenu?: TRoleMenu[]
|
||||
}
|
||||
export interface TbindWeb {
|
||||
devices: number[]
|
||||
accountId: number
|
||||
}
|
||||
|
||||
|
||||
export interface TStatisticsDevice {
|
||||
@ -131,11 +135,10 @@ export namespace TLogin {
|
||||
export namespace TAccount {
|
||||
export interface IAdd {
|
||||
username: string;
|
||||
password: string;
|
||||
phone: string;
|
||||
name: string;
|
||||
orgId: number;
|
||||
roleId: number;
|
||||
orgId?: number | string;
|
||||
roleId: number | string;
|
||||
status: number;
|
||||
}
|
||||
export interface IListReq extends Ipaging {
|
||||
@ -282,6 +285,11 @@ export namespace TOrg {
|
||||
}
|
||||
}
|
||||
|
||||
export interface TstatisticsUseCount {
|
||||
defaultCount: number;
|
||||
monitorCount: number;
|
||||
outsideCount: number;
|
||||
}
|
||||
export namespace TWarnRecord {
|
||||
|
||||
export interface IListReq extends Ipaging {
|
||||
@ -314,6 +322,12 @@ export namespace TWarningDetail {
|
||||
interface TParams {
|
||||
id: number | string
|
||||
}
|
||||
interface THealthData {
|
||||
bo: number
|
||||
hr: number
|
||||
temp: number
|
||||
time: number
|
||||
}
|
||||
interface TRes {
|
||||
address: string
|
||||
adminName: string
|
||||
@ -321,7 +335,7 @@ export namespace TWarningDetail {
|
||||
creatUser: string
|
||||
createTime: string
|
||||
deviceId: string
|
||||
healthData: string
|
||||
healthData: THealthData[]
|
||||
id: number
|
||||
lat: number
|
||||
lng: number
|
||||
|
@ -1,5 +1,5 @@
|
||||
import request from '../utils/request';
|
||||
import { TLogin, TAccount, IpagingRes, TDevice, TOrg, TRoleList, TStatisticsDevice, statisticsContentReq, statisticsContentRes, TStatisticsCount, TWarnRecord, TWarningDetail, TWarningConfirm, TDeviceConfigModify, TDeviceConfig, THealthLatestData, TLocateRecord, TSetUseStatus, TRoleMenuList, TRoleModify } from "./index.d";
|
||||
import { TLogin, TAccount, IpagingRes, TDevice, TOrg, TRoleList, TStatisticsDevice, statisticsContentReq, statisticsContentRes, TStatisticsCount, TWarnRecord, TWarningDetail, TWarningConfirm, TDeviceConfigModify, TDeviceConfig, THealthLatestData, TLocateRecord, TSetUseStatus, TRoleMenuList, TRoleModify, TbindWeb, TstatisticsUseCount } from "./index.d";
|
||||
|
||||
export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
|
||||
return request({
|
||||
@ -76,6 +76,14 @@ export const accountList = (p: TAccount.IListReq): Promise<IpagingRes<TAccount.I
|
||||
params: p
|
||||
});
|
||||
};
|
||||
// 获取组织下账号列表
|
||||
export const accountListOrg = (orgId: number): Promise<TAccount.IListRes[]> => {
|
||||
return request({
|
||||
url: '/v1/api/account/list/org',
|
||||
method: 'get',
|
||||
params: { orgId }
|
||||
});
|
||||
};
|
||||
// 重置密码
|
||||
export const passwordReset = (p: TAccount.IResetPwd): Promise<null> => {
|
||||
return request({
|
||||
@ -173,6 +181,13 @@ export const orgList = (p?: TOrg.IListReq): Promise<IpagingRes<TOrg.IOrgRecordRe
|
||||
params: p
|
||||
});
|
||||
};
|
||||
// 机构列表
|
||||
export const orgAllList = (): Promise<TOrg.IOrgRecordRes[]> => {
|
||||
return request({
|
||||
url: '/v1/api/org/list',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 删除机构
|
||||
export const orgDelete = (p?: TOrg.Idel): Promise<null> => {
|
||||
@ -183,6 +198,15 @@ export const orgDelete = (p?: TOrg.Idel): Promise<null> => {
|
||||
});
|
||||
};
|
||||
|
||||
// web设备绑定管理员
|
||||
export const bindWeb = (p: TbindWeb): Promise<null> => {
|
||||
return request({
|
||||
url: '/v1/api/device/web/bind/web',
|
||||
method: 'post',
|
||||
data: p
|
||||
});
|
||||
};
|
||||
|
||||
// 获取角色列表
|
||||
export const roleList = (): Promise<TRoleList[]> => {
|
||||
return request({
|
||||
@ -251,6 +275,14 @@ export const warnRecord = (p: TWarnRecord.IListReq): Promise<IpagingRes<TWarnRec
|
||||
});
|
||||
};
|
||||
|
||||
// 获取设备使用数量
|
||||
export const statisticsUseCount = (): Promise<TstatisticsUseCount> => {
|
||||
return request({
|
||||
url: '/v1/api/statistics/useCount',
|
||||
method: 'get',
|
||||
});
|
||||
};
|
||||
|
||||
// 预警记录
|
||||
export const warningDetail = (p: TWarningDetail.TParams): Promise<TWarningDetail.TRes> => {
|
||||
return request({
|
||||
|
Binary file not shown.
BIN
src/assets/img/avatar.png
Normal file
BIN
src/assets/img/avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 166 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB |
@ -47,7 +47,6 @@ const emit = defineEmits(["close", "confirm"]);
|
||||
|
||||
const handleAudioEnd = () => {
|
||||
emit("close");
|
||||
|
||||
// if (audioPlayer.value) {
|
||||
// audioPlayer.value.play().catch((error) => {
|
||||
// console.error("自动播放被阻止:", error);
|
||||
@ -56,23 +55,11 @@ const handleAudioEnd = () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 确保在DOM完全加载后播放音频
|
||||
// if (audioPlayer.value) {
|
||||
// setTimeout(() => {
|
||||
// audioPlayer.value.play();
|
||||
// });
|
||||
// audioPlayer.value.play();
|
||||
// // 设置音频静音,浏览器允许自动播放
|
||||
// audioPlayer.value
|
||||
// .play()
|
||||
// .then(() => {
|
||||
// // 自动播放成功后取消静音
|
||||
// audioPlayer.value.muted = false;
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.log("自动播放失败", error);
|
||||
// });
|
||||
// }
|
||||
if (audioPlayer.value) {
|
||||
audioPlayer.value.play().catch((error) => {
|
||||
console.error("自动播放被阻止:", error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
{{ item }}{{ index != tab.list.length - 1 ? " / " : "" }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="web-time">{{ format(time) }} 更新</div>
|
||||
<div class="web-time">{{ format(comm.time) }} 更新</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-user-con">
|
||||
@ -33,7 +33,7 @@
|
||||
<!-- 用户名下拉菜单 -->
|
||||
<el-dropdown class="user-name" trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
{{ user.username }}
|
||||
{{ comm.user.username }}
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
@ -49,23 +49,24 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import { inject, onMounted } from "vue";
|
||||
import { useSidebarStore } from "../store/sidebar";
|
||||
import { useCommonStore } from "../store/common";
|
||||
import { useRouter } from "vue-router";
|
||||
import imgurl from "../assets/img/img.jpg";
|
||||
import imgurl from "../assets/img/avatar.png";
|
||||
import { useTabsStore } from "@/store/tabs";
|
||||
import { routes } from "@/router/index";
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
import { format } from "@/utils";
|
||||
|
||||
const ws: any = inject("ws");
|
||||
const tab = useTabsStore();
|
||||
|
||||
const username: string | null = localStorage.getItem("vuems_name");
|
||||
|
||||
const message: number = 2;
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
const { user, time, clearStore } = useCommonStore();
|
||||
const comm = useCommonStore();
|
||||
|
||||
// onMounted(() => {
|
||||
// if (document.body.clientWidth < 1500) {
|
||||
// sidebar.handleCollapse();
|
||||
@ -76,8 +77,9 @@ const { user, time, clearStore } = useCommonStore();
|
||||
const router = useRouter();
|
||||
const handleCommand = (command: string) => {
|
||||
if (command == "loginout") {
|
||||
clearStore();
|
||||
comm.clearStore();
|
||||
router.push("/login");
|
||||
ws.close();
|
||||
} else if (command == "user") {
|
||||
router.push("/ucenter");
|
||||
}
|
||||
|
@ -45,7 +45,16 @@
|
||||
table-layout="auto"
|
||||
>
|
||||
<template v-for="item in columns" :key="item.prop">
|
||||
<el-table-column v-if="item.visible" :prop="item.prop" :label="item.label" :width="item.width" :type="item.type" :align="item.align || 'center'">
|
||||
<el-table-column
|
||||
v-if="item.visible"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
:width="item.width"
|
||||
:type="item.type"
|
||||
:align="item.align || 'center'"
|
||||
:fixed="item.fixed"
|
||||
:reserve-selection="item.reserveSelection"
|
||||
>
|
||||
<template #default="{ row, column, $index }" v-if="item.type === 'index'">
|
||||
{{ getIndex($index) }}
|
||||
</template>
|
||||
@ -144,6 +153,10 @@ const props = defineProps({
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
selectionChange: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
editFunc: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
@ -178,8 +191,10 @@ const tableRowClassName = ({ row, rowIndex }: { row: TableItem; rowIndex: number
|
||||
|
||||
// 当选择项发生变化时会触发该事件
|
||||
const multipleSelection = ref([]);
|
||||
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
multipleSelection.value = selection;
|
||||
props.selectionChange(selection);
|
||||
};
|
||||
|
||||
// 当前页码变化的事件
|
||||
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div class="app-wrapper">
|
||||
<audio ref="audioRef" autoplay loop hidden>
|
||||
<source src="../assets/audio/alarm.mp3" type="video/mp3" />
|
||||
</audio>
|
||||
<el-button @click="playAudio">Default</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
const audioSrc = ref(new URL("@/assets/audio/alarm.mp3", import.meta.url).href);
|
||||
const audioRef = ref(null);
|
||||
|
||||
const playAudio = (context) => {
|
||||
audioRef.value.play().catch((err) => {
|
||||
// console.log(err, "暂停失败");
|
||||
});
|
||||
};
|
||||
</script>
|
@ -1,120 +0,0 @@
|
||||
<template>
|
||||
<div class="app-wrapper">
|
||||
<v-sidebar />
|
||||
<v-header />
|
||||
<div class="main-container">
|
||||
<div class="app-main" :class="{ 'content-collapse': sidebar.collapse }">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
<keep-alive :include="[]">
|
||||
<component :is="Component"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <audio ref="audioPlayer" src="../assets/audio/alarm.mp3" hidden /> -->
|
||||
<audio ref="audioPlayer" autoplay loop hidden>
|
||||
<source src="../assets/audio/alarm.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<!-- <Alarm ref="alarmRef" v-if="visible" @close="close" /> -->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useSidebarStore } from "@/store/sidebar";
|
||||
import vHeader from "@/components/header.vue";
|
||||
import vSidebar from "@/components/sidebar.vue";
|
||||
import Alarm from "@/components/alarm.vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
// import useWebSocket from "@/utils/webSocket";
|
||||
// const { onMessage } = useWebSocket();
|
||||
// onMessage((res) => {
|
||||
// console.log(res, "WebSocket 接收服务器消息");
|
||||
// if (res.cmd == "warning") {
|
||||
// console.log("预警弹窗");
|
||||
// }
|
||||
// });
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
const alarmRef = ref(null);
|
||||
const audioPlayer = ref(null);
|
||||
const visible = ref(true);
|
||||
|
||||
const getInclude = (routeList: any) => {
|
||||
if (!routeList) return [];
|
||||
let list = [];
|
||||
routeList.forEach((item) => {
|
||||
if (item.meta && item.meta.keepAlive) {
|
||||
list.push(item.name);
|
||||
}
|
||||
if (item.children && item.children.length) {
|
||||
list = [...list, ...getInclude(item.children)];
|
||||
}
|
||||
});
|
||||
return list;
|
||||
};
|
||||
const close = async () => {
|
||||
try {
|
||||
audioPlayer.value.play();
|
||||
console.log(1111);
|
||||
} catch (error) {
|
||||
console.log(error, 2222);
|
||||
}
|
||||
};
|
||||
|
||||
// setTimeout(() => {
|
||||
// audioRef.value.play().catch((error) => {
|
||||
// console.error("自动播放被阻止:", error);
|
||||
// });
|
||||
// }, 5000);
|
||||
const playAudio = () => {
|
||||
if (audioPlayer.value) {
|
||||
audioPlayer.value.play().catch((error) => {
|
||||
console.error("自动播放被阻止:", error);
|
||||
});
|
||||
}
|
||||
};
|
||||
// const include = getInclude(routes[0].children);
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
// @media screen and (max-width: 800px) {
|
||||
// .main-container {
|
||||
// margin-left: 0 !important;
|
||||
// }
|
||||
// .sidebar-container {
|
||||
// width: 0 !important;
|
||||
// }
|
||||
// .app-wrapper-header {
|
||||
// width: 100% !important;
|
||||
// }
|
||||
// }
|
||||
.app-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #f0f2f5;
|
||||
overflow: hidden;
|
||||
.main-container {
|
||||
height: 100vh;
|
||||
margin-left: 210px;
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
transition: 0.3s;
|
||||
background-color: #f2f4fa;
|
||||
}
|
||||
.app-main {
|
||||
// height: 100vh;
|
||||
height: calc(100vh - 90px);
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// padding-top: 70px;
|
||||
margin-top: 70px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -13,8 +13,7 @@
|
||||
</router-view>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <Alarm ref="alarmRef" v-if="visible" @close="close" /> -->
|
||||
<Alarm ref="alarmRef" v-if="visible" @close="visible = false" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
@ -22,7 +21,9 @@ import { useSidebarStore } from "@/store/sidebar";
|
||||
import vHeader from "@/components/header.vue";
|
||||
import vSidebar from "@/components/sidebar.vue";
|
||||
import Alarm from "@/components/alarm.vue";
|
||||
import { ref } from "vue";
|
||||
import { inject, onBeforeUnmount, onMounted, ref } from "vue";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
const ws: any = inject("ws");
|
||||
|
||||
// import useWebSocket from "@/utils/webSocket";
|
||||
// const { onMessage } = useWebSocket();
|
||||
@ -30,13 +31,14 @@ import { ref } from "vue";
|
||||
// console.log(res, "WebSocket 接收服务器消息");
|
||||
// if (res.cmd == "warning") {
|
||||
// console.log("预警弹窗");
|
||||
// visible.value = true;
|
||||
// }
|
||||
// });
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
const alarmRef = ref(null);
|
||||
const audioPlayer = ref(null);
|
||||
const visible = ref(true);
|
||||
const visible = ref(false);
|
||||
|
||||
const getInclude = (routeList: any) => {
|
||||
if (!routeList) return [];
|
||||
@ -51,27 +53,35 @@ const getInclude = (routeList: any) => {
|
||||
});
|
||||
return list;
|
||||
};
|
||||
const close = async () => {
|
||||
try {
|
||||
audioPlayer.value.play();
|
||||
console.log(1111);
|
||||
} catch (error) {
|
||||
console.log(error, 2222);
|
||||
}
|
||||
|
||||
const handleBeforeUnload = (event: any) => {
|
||||
localStorage.removeItem("isReload");
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
audioPlayer.value.play().catch((error) => {
|
||||
console.error("自动播放被阻止:", error);
|
||||
onMounted(() => {
|
||||
const isReload = localStorage.getItem("isReload");
|
||||
// if (isReload !== "true") {
|
||||
// ElMessageBox.alert("由于浏览器安全策略,用户必须点击屏幕才能播放告警声音", "提示", {
|
||||
// confirmButtonText: "OK",
|
||||
// callback: () => {
|
||||
// localStorage.setItem("isReload", "true");
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (ws.socket == null) {
|
||||
// ws.connect();
|
||||
// }
|
||||
});
|
||||
}, 5000);
|
||||
const playAudio = () => {
|
||||
if (audioPlayer.value) {
|
||||
audioPlayer.value.play().catch((error) => {
|
||||
console.error("自动播放被阻止:", error);
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||
});
|
||||
|
||||
// const include = getInclude(routes[0].children);
|
||||
</script>
|
||||
|
||||
|
@ -7,12 +7,14 @@ import { usePermissStore } from './store/permiss';
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
import 'element-plus/dist/index.css';
|
||||
import './assets/css/icon.css';
|
||||
|
||||
import WebSocketService from "@/utils/webSocket.js";
|
||||
const ws = new WebSocketService()
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate);
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
app.provide('ws', ws)
|
||||
|
||||
// 注册elementplus图标
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
|
@ -17,7 +17,7 @@ import m4_a from "@/assets/img/m4_a.png";
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
component: Layout,
|
||||
component: () => import('@/layout/index.vue'),
|
||||
redirect: '/statisticalCenter',
|
||||
children: [
|
||||
{
|
||||
@ -123,14 +123,12 @@ const router = createRouter({
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
|
||||
NProgress.start();
|
||||
const permiss = usePermissStore();
|
||||
const comm = useCommonStore();
|
||||
const tab = useTabsStore();
|
||||
tab.setTabsItem(to.meta.tabs);
|
||||
comm.setTime()
|
||||
|
||||
if (typeof to.meta.permiss == 'string' && !permiss.key.includes(to.meta.permiss)) {
|
||||
// 如果没有权限,则进入403
|
||||
next('/403');
|
||||
|
@ -18,5 +18,6 @@ export interface FormOptionList {
|
||||
activeText?: string;
|
||||
inactiveText?: string;
|
||||
required?: boolean;
|
||||
defalut?: any;
|
||||
validator?: Function;
|
||||
}
|
@ -10,6 +10,7 @@ export class MapCustom {
|
||||
}
|
||||
// 多边形回显
|
||||
polyEditor(list, fn) {
|
||||
if (!list) return;
|
||||
this.map.clearMap();
|
||||
let pathList = [];
|
||||
list.forEach((item, index) => {
|
||||
|
@ -22,6 +22,8 @@ service.interceptors.request.use(
|
||||
service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const { data, headers } = response;
|
||||
if (headers['content-type'] === "application/vnd.ms-excel;charset=utf-8") return data
|
||||
|
||||
if (data.code !== 200) {
|
||||
ElMessage.error(data.msg)
|
||||
|
||||
|
@ -1,33 +1,32 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
class WebSocketService {
|
||||
import { useCommonStore } from "@/store/common";
|
||||
export default class WebSocketService {
|
||||
constructor() {
|
||||
this.url = import.meta.env.VITE_APP_URL_WEBSOCKET;
|
||||
this.socket = null;
|
||||
this.isAlive = false; // 用于判断心跳是否正常
|
||||
this.reconnectAttempts = 0; // 重连尝试次数
|
||||
this.MAX_RECONNECT_ATTEMPTS = 5; // 最大重连次数
|
||||
this.HEARTBEAT_INTERVAL = 5000; // 心跳间隔时间 (30秒)
|
||||
// this.HEARTBEAT_INTERVAL = 30000; // 心跳间隔时间 (30秒)
|
||||
// this.HEARTBEAT_INTERVAL = 5000; // 心跳间隔时间 (30秒)
|
||||
this.HEARTBEAT_INTERVAL = 30000; // 心跳间隔时间 (30秒)
|
||||
this.heartbeatTimer = null;
|
||||
this.reconnectTimer = null;
|
||||
|
||||
this.connect();
|
||||
// this.connect();
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.socket = new WebSocket(this.url);
|
||||
|
||||
this.socket.onopen = () => {
|
||||
console.log('WebSocket已连接');
|
||||
this.webScoketLogin();
|
||||
this.reconnectAttempts = 0; // 成功连接后重置重试次数
|
||||
this.startHeartbeat(); // 开始心跳检测
|
||||
};
|
||||
|
||||
this.onMessage()
|
||||
this.socket.onmessage = (event) => {
|
||||
// 处理接收到的消息
|
||||
console.log('Message received:', event.data);
|
||||
console.log('WebSocket收到消息:', event.data);
|
||||
this.receivePong()
|
||||
};
|
||||
|
||||
this.socket.onclose = () => {
|
||||
@ -49,8 +48,9 @@ class WebSocketService {
|
||||
cmd: "webLogin",
|
||||
})
|
||||
}
|
||||
|
||||
// 开始发送心跳
|
||||
startHeartbeat() {
|
||||
console.log('开始发送心跳');
|
||||
if (!this.heartbeatTimer) {
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
if (this.socket.readyState === WebSocket.OPEN) {
|
||||
@ -60,7 +60,7 @@ class WebSocketService {
|
||||
}, this.HEARTBEAT_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭发送心态
|
||||
stopHeartbeat() {
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
@ -69,8 +69,6 @@ class WebSocketService {
|
||||
}
|
||||
|
||||
sendPing() {
|
||||
console.log('发送心跳', JSON.stringify({ cmd: 'heartbeat' }));
|
||||
|
||||
this.socket.send(JSON.stringify({ cmd: 'heartbeat' }));
|
||||
setTimeout(() => {
|
||||
if (!this.isAlive) {
|
||||
@ -83,25 +81,39 @@ class WebSocketService {
|
||||
receivePong() {
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
// 重新连接
|
||||
attemptReconnect() {
|
||||
const comm = useCommonStore();
|
||||
if (!comm.user.token) return
|
||||
if (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
|
||||
this.reconnectAttempts++;
|
||||
console.log(`Attempting to reconnect (${this.reconnectAttempts})...`);
|
||||
console.log(`正在尝试重新连接 (${this.reconnectAttempts})...`);
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.connect();
|
||||
}, 1000 * this.reconnectAttempts); // 指数退避算法
|
||||
} else {
|
||||
console.error('Max reconnect attempts reached');
|
||||
console.error('达到的最大重连尝试数');
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
sendMessage(message) {
|
||||
console.log('sendMessage');
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
onMessage = (callback) => {
|
||||
this.socket.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.cmd == "webLogin" && !data.code == 200) return this.webScoketLogin()
|
||||
callback && callback(data);
|
||||
if (event.data.includes('heartbeat')) {
|
||||
this.receivePong();
|
||||
}
|
||||
};
|
||||
};
|
||||
// 关闭连接
|
||||
close() {
|
||||
this.stopHeartbeat();
|
||||
if (this.reconnectTimer) {
|
||||
@ -115,44 +127,3 @@ class WebSocketService {
|
||||
}
|
||||
}
|
||||
|
||||
export default function useWebSocket() {
|
||||
const ws = new WebSocketService();
|
||||
|
||||
const message = ref(null);
|
||||
const isConnected = ref(false);
|
||||
|
||||
const onMessage = (callback) => {
|
||||
ws.socket.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.cmd == "webLogin" && !data.code == 200) return ws.webScoketLogin()
|
||||
|
||||
callback(data);
|
||||
if (event.data.includes('heartbeat')) {
|
||||
ws.receivePong();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const onClose = () => {
|
||||
ws.socket.onclose()
|
||||
};
|
||||
|
||||
const onOpen = (callback) => {
|
||||
ws.socket.onopen = () => {
|
||||
isConnected.value = true;
|
||||
callback();
|
||||
};
|
||||
};
|
||||
|
||||
// onMounted(() => {
|
||||
// onOpen(() => console.log('Connected'));
|
||||
// onClose(() => console.log('Disconnected'));
|
||||
// onMessage((data) => console.log('Received:', data));
|
||||
// });
|
||||
|
||||
// onUnmounted(() => {
|
||||
// ws.close();
|
||||
// });
|
||||
|
||||
return { isConnected, message, sendMessage: ws.sendMessage.bind(ws), onMessage, onClose };
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
<div class="map-content">
|
||||
<div class="map" id="mapcontainer"></div>
|
||||
<div class="chart">
|
||||
<div class="title">设备生理数据</div>
|
||||
<div ref="chartRef" style="width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -20,7 +21,12 @@
|
||||
</div>
|
||||
<div class="info-text">绑定关联人名称:{{ curData.username }}</div>
|
||||
<div class="info-text">绑定关联人警号:{{ curData.userNumber }}</div>
|
||||
<div class="info-text">状态:{{ statusEnum[curData.status] }}</div>
|
||||
<div class="info-text">
|
||||
状态:
|
||||
<el-tag :type="curData.status == 1 ? 'success' : 'danger'">
|
||||
{{ statusEnum[curData.status] }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="info-text">紧急电话:{{ curData.adminPhone }}</div>
|
||||
<div class="info-text">隶属辖区:{{ curData.orgName }}</div>
|
||||
|
||||
@ -77,13 +83,14 @@
|
||||
<script setup lang="ts" name="incidentDispose">
|
||||
import location from "@/assets/img/location.png";
|
||||
import { MapCustom } from "@/utils/mapCustom";
|
||||
import { onMounted, ref, reactive, watch } from "vue";
|
||||
import { onMounted, ref, reactive, watch, onDeactivated, onUnmounted } from "vue";
|
||||
import Upload from "@/components/upload-img.vue";
|
||||
import * as echarts from "echarts";
|
||||
import { warningDetail, warningConfirm } from "@/api/index";
|
||||
import { TWarningConfirm, TWarningDetail } from "@/api/index.d";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ElMessage, FormInstance, FormRules } from "element-plus";
|
||||
import { debounce, format } from "@/utils";
|
||||
|
||||
// 定义一个响应式变量
|
||||
enum unitEnum {
|
||||
@ -109,29 +116,70 @@ const chartRef = ref(null);
|
||||
const disabled = ref(false);
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
let map = null;
|
||||
let myChart = null;
|
||||
|
||||
const ringOptions = {
|
||||
title: {
|
||||
text: "Stacked Line",
|
||||
left: "3%",
|
||||
const options = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
formatter: function (params) {
|
||||
let unit = { 0: "次/分", 1: "%", 2: "℃" };
|
||||
var res = format(options.times[params[0].dataIndex]) + "<br/>";
|
||||
res += params
|
||||
.map(function (param, index) {
|
||||
return param.marker + param.seriesName + ":" + param.value + unit[index] + "<br/>";
|
||||
})
|
||||
.join("");
|
||||
return res;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ["心率", "体表温度", "血氧"],
|
||||
},
|
||||
grid: {
|
||||
left: "0%",
|
||||
right: "4%",
|
||||
bottom: "3%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
boundaryGap: false,
|
||||
data: [],
|
||||
},
|
||||
times: [],
|
||||
yAxis: {
|
||||
type: "value",
|
||||
},
|
||||
grid: {
|
||||
left: "10%",
|
||||
right: "4%",
|
||||
bottom: "10%",
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
itemStyle: {
|
||||
color: "#FF4241",
|
||||
},
|
||||
name: "心率",
|
||||
type: "line",
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: [],
|
||||
time: [],
|
||||
},
|
||||
{
|
||||
itemStyle: {
|
||||
color: "#12CCA2",
|
||||
},
|
||||
name: "血氧",
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
data: [],
|
||||
time: [],
|
||||
},
|
||||
{
|
||||
itemStyle: {
|
||||
color: "#FF7D00",
|
||||
},
|
||||
showSymbol: false,
|
||||
name: "体表温度",
|
||||
type: "line",
|
||||
data: [],
|
||||
time: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -149,7 +197,7 @@ let curData = ref<TWarningDetail.TRes>({
|
||||
creatUser: "",
|
||||
createTime: "",
|
||||
deviceId: "",
|
||||
healthData: "",
|
||||
healthData: [],
|
||||
id: 0,
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
@ -195,6 +243,16 @@ const getData = async () => {
|
||||
let icon = map.newIcon(location);
|
||||
let marker = map.marker({ icon, position: [116.406315, 39.908775] });
|
||||
marker.setMap(map.map);
|
||||
if (res.healthData && res.healthData.length) {
|
||||
res.healthData.forEach((item) => {
|
||||
options.times.push(item.time);
|
||||
options.xAxis.data.push(format(item.time, "HH:mm:ss"));
|
||||
options.series[0].data.push(item.hr);
|
||||
options.series[1].data.push(item.bo);
|
||||
options.series[2].data.push(item.temp);
|
||||
});
|
||||
myChart.setOption(options);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
@ -208,14 +266,22 @@ const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleResize = debounce(() => {
|
||||
myChart.resize();
|
||||
}, 200);
|
||||
onMounted(() => {
|
||||
getData();
|
||||
map = new MapCustom({ dom: "mapcontainer", center: [116.406315, 39.908775] });
|
||||
if (chartRef.value) {
|
||||
const myChart = echarts.init(chartRef.value);
|
||||
myChart.setOption(ringOptions);
|
||||
myChart = echarts.init(chartRef.value);
|
||||
myChart.setOption(options);
|
||||
window.addEventListener("resize", handleResize);
|
||||
}
|
||||
});
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@ -245,6 +311,21 @@ onMounted(() => {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
.title {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 20px;
|
||||
&::before {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
content: "";
|
||||
width: 2px;
|
||||
height: 14px;
|
||||
border-radius: 20px;
|
||||
background: #061451;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,9 +51,9 @@ const rules = reactive<FormRules<typeof ruleForm>>({
|
||||
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
localStorage.setItem("isReload", "true");
|
||||
fetchLogin(ruleForm).then((res) => {
|
||||
comm.setUser(res);
|
||||
comm.getRoleList();
|
||||
|
@ -2,17 +2,17 @@
|
||||
<div class="device">
|
||||
<div class="device-head">
|
||||
<div class="title">设备列表</div>
|
||||
<el-select class="select" v-model="paging.mode" @change="handelMode">
|
||||
<el-option label="全部" :value="undefined" />
|
||||
<el-select class="select" v-model="props.paging.mode" @change="handelMode">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="常规模式" value="0" />
|
||||
<el-option label="审讯模式" value="1" />
|
||||
<el-option label="户外押送" value="2" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-infinite-scroll="load" :infinite-scroll-immediate="false" class="device-list noScrollbar infinite-list" style="overflow: auto">
|
||||
<el-popover :width="350" class="box-item" placement="bottom" v-for="item in list" :key="item.id">
|
||||
<el-popover :width="350" class="box-item" placement="right" v-for="item in props.list" :key="item.id">
|
||||
<template #reference>
|
||||
<div class="item" :class="{ active: deviceId === item.deviceId }" @click="emit('click', item)">
|
||||
<div class="item" :class="{ active: props.deviceInfo?.deviceId === item.deviceId }" @click="emit('click', item)">
|
||||
<div class="item-img">
|
||||
<img src="@/assets/img/handcuffs.png" alt="" srcset="" />
|
||||
</div>
|
||||
@ -75,7 +75,9 @@ enum modeEnum {
|
||||
"户外押送",
|
||||
}
|
||||
const emit = defineEmits(["click"]);
|
||||
const { list, paging, api, deviceId } = defineProps({
|
||||
// const { list, paging, api, deviceInfo }
|
||||
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array<TDevice.IListRes>,
|
||||
default: () => [],
|
||||
@ -88,19 +90,19 @@ const { list, paging, api, deviceId } = defineProps({
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
deviceId: {
|
||||
type: Number || String,
|
||||
default: () => "",
|
||||
deviceInfo: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const handelMode = () => {
|
||||
paging.page += 1;
|
||||
api();
|
||||
props.paging.page += 1;
|
||||
props.api();
|
||||
};
|
||||
const load = () => {
|
||||
paging.page += 1;
|
||||
api();
|
||||
props.paging.page += 1;
|
||||
props.api();
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
|
@ -4,11 +4,11 @@
|
||||
<div class="title">当前设备告警记录</div>
|
||||
<div class="search">
|
||||
<el-select class="select" placeholder="请选择事件类型" v-model="paging.warnType" style="width: 150px; margin-right: 20px" @change="handelChange">
|
||||
<el-option label="全部" :value="undefined" />
|
||||
<el-option label="全部" value="" />
|
||||
<el-option v-for="(item, index) in warnTypeList" :key="index" :label="item" :value="index" />
|
||||
</el-select>
|
||||
<el-select class="select" placeholder="请选择处理状态" v-model="paging.status" style="width: 150px" @change="handelChange">
|
||||
<el-option label="全部" :value="undefined" />
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="待处理" value="0" />
|
||||
<el-option label="已处理" value="1" />
|
||||
</el-select>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="container">
|
||||
<el-row class="el-row" :gutter="20">
|
||||
<el-col :span="6" class="el-row-left"
|
||||
><DeviceInfo :deviceId="deviceInfo?.deviceId" :paging="devicePaging" :api="getdeviceList" :list="deviceData" @click="handelClickDevice"
|
||||
><DeviceInfo :deviceInfo="deviceInfo" :paging="devicePaging" :api="getdeviceList" :list="deviceData" @click="handelClickDevice"
|
||||
/></el-col>
|
||||
<el-col :span="18" class="el-row-right scrollbar">
|
||||
<MonitoringTop :funcList="funcList" />
|
||||
@ -48,7 +48,7 @@ import { MapCustom } from "@/utils/mapCustom";
|
||||
import * as echarts from "echarts";
|
||||
import { deviceList, healthLatestData, warningRecord, locateRecord } from "@/api/index";
|
||||
import { TDevice, THealthLatestData } from "@/api/index.d";
|
||||
import { onMounted, onDeactivated, ref, reactive } from "vue";
|
||||
import { onMounted, onDeactivated, ref, reactive, onUnmounted } from "vue";
|
||||
import { debounce, format } from "@/utils";
|
||||
import heart from "@/assets/img/heart.png";
|
||||
import temperature from "@/assets/img/temperature.png";
|
||||
@ -58,6 +58,7 @@ import location from "@/assets/img/location.png";
|
||||
const chartRef = ref(null);
|
||||
let myChart = null;
|
||||
let newMap = null;
|
||||
let Interval = null;
|
||||
let funcList = ref([
|
||||
{ title: "当前心率", en: "DANGQIANXINLV", icon: heart, unit: "次/分", num: 0, color: "#FF0303" },
|
||||
{ title: "当前血氧", en: "DANGQIANXUEYANG", icon: blood, unit: "%", num: 0, color: "#8B51FD" },
|
||||
@ -67,7 +68,18 @@ let funcList = ref([
|
||||
const options = {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
formatter: function (params) {
|
||||
let unit = { 心率: "次/分", 血氧: "%", 体表温度: "℃" };
|
||||
var res = format(options.times[params[0].dataIndex]) + "<br/>";
|
||||
res += params
|
||||
.map(function (param, index) {
|
||||
return param.marker + param.seriesName + ":" + param.value + unit[param.seriesName] + "<br/>";
|
||||
})
|
||||
.join("");
|
||||
return res;
|
||||
},
|
||||
},
|
||||
times: [],
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: [],
|
||||
@ -83,6 +95,7 @@ const options = {
|
||||
name: "",
|
||||
data: [],
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
itemStyle: {
|
||||
color: "#ff4567", // 设置线条颜色
|
||||
},
|
||||
@ -107,6 +120,7 @@ const getdeviceList = async () => {
|
||||
deviceInfo.value = res.records[1];
|
||||
getHealthLatestData();
|
||||
getLocateRecord();
|
||||
IntervalFn();
|
||||
}
|
||||
};
|
||||
|
||||
@ -134,6 +148,7 @@ const getOptionsData = (list: { time: string; value: number }[], name: string, c
|
||||
options.series.data = [];
|
||||
if (list && list.length) {
|
||||
list.forEach((item) => {
|
||||
options.times.push(item.time);
|
||||
options.xAxis.data.push(format(item.time, "HH:mm:ss"));
|
||||
options.series.data.push(item.value);
|
||||
});
|
||||
@ -166,7 +181,12 @@ const getLocateRecord = () => {
|
||||
const handleResize = debounce(() => {
|
||||
myChart.resize();
|
||||
}, 200);
|
||||
|
||||
const IntervalFn = () => {
|
||||
Interval = setInterval(() => {
|
||||
getHealthLatestData();
|
||||
getLocateRecord();
|
||||
}, 60000);
|
||||
};
|
||||
const handelClickDevice = (val: TDevice.IListRes) => {
|
||||
deviceInfo.value = val;
|
||||
getHealthLatestData();
|
||||
@ -180,7 +200,8 @@ onMounted(() => {
|
||||
window.addEventListener("resize", handleResize);
|
||||
}
|
||||
});
|
||||
onDeactivated(() => {
|
||||
onUnmounted(() => {
|
||||
clearInterval(Interval);
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
</script>
|
||||
|
@ -30,7 +30,7 @@ interface TPaging {
|
||||
page: number;
|
||||
size: number;
|
||||
total: number;
|
||||
deviceId?: number;
|
||||
deviceId?: number | string;
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -39,15 +39,17 @@ import StatisticsWarning from "./statisticsWarning.vue";
|
||||
import EmergencyList from "./emergencyList.vue";
|
||||
import * as echarts from "echarts";
|
||||
import { MapCustom } from "@/utils/mapCustom";
|
||||
import { statisticsDevice, statisticsContent, statisticsCount, statisticsWarningapi, warnRecord } from "@/api/index";
|
||||
import { onMounted, ref, reactive, onDeactivated } from "vue";
|
||||
import { debounce } from "@/utils";
|
||||
import { statisticsDevice, statisticsContent, statisticsCount, statisticsWarningapi, warnRecord, statisticsUseCount } from "@/api/index";
|
||||
import { onMounted, ref, reactive, onDeactivated, onUnmounted } from "vue";
|
||||
import { debounce, format } from "@/utils";
|
||||
import { TWarnRecord } from "@/api/index.d";
|
||||
import handcuffs from "@/assets/img/handcuffs.png";
|
||||
import onLine from "@/assets/img/onLine.png";
|
||||
import report from "@/assets/img/report.png";
|
||||
import newly from "@/assets/img/newly.png";
|
||||
|
||||
let d = new Date();
|
||||
|
||||
const chartRef = ref(null);
|
||||
const chartRef1 = ref(null);
|
||||
const chartRef2 = ref(null);
|
||||
@ -95,9 +97,9 @@ let option1 = ref({
|
||||
type: "pie",
|
||||
radius: ["20%", "50%"],
|
||||
data: [
|
||||
{ value: 1048, name: "常规模式" },
|
||||
{ value: 735, name: "户外押送" },
|
||||
{ value: 580, name: "审讯模式" },
|
||||
{ value: 0, name: "常规模式" },
|
||||
{ value: 0, name: "户外押送" },
|
||||
{ value: 0, name: "审讯模式" },
|
||||
],
|
||||
},
|
||||
});
|
||||
@ -122,12 +124,13 @@ let option2 = ref({
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const paging = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
deviceId: "",
|
||||
startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
|
||||
endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
|
||||
});
|
||||
|
||||
const tableData = ref<TWarnRecord.IListRes[]>([]);
|
||||
@ -156,6 +159,16 @@ const getStatisticsWarningApi = () => {
|
||||
myChart2.setOption(option2.value);
|
||||
});
|
||||
};
|
||||
const getStatisticsUseCount = () => {
|
||||
statisticsUseCount().then((res) => {
|
||||
option1.value.series.data = [
|
||||
{ value: res.defaultCount, name: "常规模式" },
|
||||
{ value: res.outsideCount, name: "户外押送" },
|
||||
{ value: res.monitorCount, name: "审讯模式" },
|
||||
];
|
||||
myChart1.setOption(option1.value);
|
||||
});
|
||||
};
|
||||
const getStatisticsContent = (res) => {
|
||||
statisticsContent(res).then((res) => {
|
||||
option.value.xAxis.data = res?.times;
|
||||
@ -212,6 +225,7 @@ onMounted(() => {
|
||||
getData();
|
||||
getStatisticsCount();
|
||||
getStatisticsWarningApi();
|
||||
getStatisticsUseCount();
|
||||
|
||||
new MapCustom({ dom: "mapcontainer" });
|
||||
|
||||
@ -228,7 +242,7 @@ onMounted(() => {
|
||||
window.addEventListener("resize", handleResize);
|
||||
}
|
||||
});
|
||||
onDeactivated(() => {
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
});
|
||||
</script>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<template #header>
|
||||
<div class="card-header">使用记录</div>
|
||||
</template>
|
||||
<TableCustom :hasToolbar="false" :columns="record" :tableData="tableData" :total="paging.total" :currentPage="paging.page" :changePage="changePage">
|
||||
<TableCustom :hasToolbar="false" :columns="record" :tableData="tableData" :paging="paging" :changePage="changePage">
|
||||
<template #status="{ rows }">
|
||||
{{ recordStatusEnum[rows.status] }}
|
||||
</template>
|
||||
|
103
src/views/synthesizeManage/deviceManage/bindArea.vue
Normal file
103
src/views/synthesizeManage/deviceManage/bindArea.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<el-form ref="ruleFormRef" label-position="top" :model="ruleForm" status-icon :rules="rules" label-width="auto" class="demo-ruleForm">
|
||||
<el-form-item label="已选择手铐">
|
||||
<div>{{ deviceNames }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="辖区" prop="org">
|
||||
<el-select v-model="ruleForm.org" placeholder="请选择辖区" @change="getAccountListOrg">
|
||||
<el-option v-for="item in orgAllData" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="人员" prop="accountId">
|
||||
<el-select v-model="ruleForm.accountId" placeholder="请选择人员">
|
||||
<el-option v-for="item in accountLis" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="footr">
|
||||
<el-button type="primary" @click="submitForm(ruleFormRef)"> 保存 </el-button>
|
||||
<el-button @click="emit('close')">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, PropType, reactive, ref, watch } from "vue";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import { TDevice, TOrg } from "@/api/index.d";
|
||||
import { accountListOrg } from "@/api/index";
|
||||
interface IForm {
|
||||
devices: number[];
|
||||
org: number | string;
|
||||
accountId: number | string;
|
||||
}
|
||||
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
const accountLis = ref([]);
|
||||
|
||||
const props = defineProps({
|
||||
orgAllData: {
|
||||
type: Array as PropType<TOrg.IOrgRecordRes[]>,
|
||||
dfault: () => {},
|
||||
},
|
||||
selectDeviceList: {
|
||||
type: Array as PropType<TDevice.IListRes[]>,
|
||||
dfault: () => {},
|
||||
},
|
||||
api: {
|
||||
type: Function,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["close"]);
|
||||
|
||||
const getAccountListOrg = (orgId: number, accountId?: number) => {
|
||||
accountLis.value = [];
|
||||
ruleForm.accountId = "";
|
||||
accountListOrg(orgId).then((res) => {
|
||||
accountLis.value = res;
|
||||
if (accountId) {
|
||||
ruleForm.accountId = accountId;
|
||||
}
|
||||
});
|
||||
};
|
||||
const deviceNames = computed(() => props.selectDeviceList.map((item) => item.deviceId).join("、"));
|
||||
|
||||
const ruleForm = reactive<IForm>({
|
||||
devices: props.selectDeviceList.map((item) => item.deviceId),
|
||||
org: "",
|
||||
accountId: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (props.selectDeviceList.length == 1) {
|
||||
let item = props.selectDeviceList[0];
|
||||
ruleForm.org = item.orgId;
|
||||
getAccountListOrg(item.orgId, item.accountId);
|
||||
}
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<typeof ruleForm>>({
|
||||
org: [{ required: true, message: "请选择辖区", trigger: "blur" }],
|
||||
accountId: [{ required: true, message: "请选择人员", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
props.api(ruleForm);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.footr {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footr :deep(.el-form-item__content) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
@ -2,10 +2,10 @@
|
||||
<div class="container">
|
||||
<TableSearch :query="query" :options="searchOpt" :search="handleSearch" />
|
||||
<div class="table-container">
|
||||
<TableCustom :columns="columns" :tableData="tableData" :paging="paging" :changePage="changePage">
|
||||
<TableCustom :selection-change="handleSelect" :columns="columns" :tableData="tableData" :paging="paging" :changePage="changePage">
|
||||
<template #toolbarBtn>
|
||||
<!-- <el-button type="primary" @click="handleAdd">新增</el-button> -->
|
||||
<el-button @click="handleEdit">手铐关联</el-button>
|
||||
<el-button @click="handleEdit()">设备关联</el-button>
|
||||
<!-- <el-button>地图位置</el-button> -->
|
||||
<!-- <el-button>导出</el-button> -->
|
||||
</template>
|
||||
@ -41,18 +41,22 @@
|
||||
<!-- <TableEdit :form-data="rowData" :options="controlOptions" :update="updateData" /> -->
|
||||
<DeviceControl v-model="controlForm" @change="handelControl" />
|
||||
</el-dialog>
|
||||
<el-dialog title="设备关联" v-model="visible2" width="700px" destroy-on-close :close-on-click-modal="false" @close="closeDialog">
|
||||
<BindArea :orgAllData="orgAllData" :selectDeviceList="selectDeviceList" @close="visible2 = false" :api="bindWebFn" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="basetable">
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { deviceList, setMode, deviceGetLocation, deviceControl, setUseStatus, setStatus } from "@/api/index";
|
||||
import { TDevice } from "@/api/index.d";
|
||||
import { deviceList, setMode, deviceGetLocation, deviceControl, setUseStatus, setStatus, orgAllList, bindWeb } from "@/api/index";
|
||||
import { TbindWeb, TDevice, TOrg } from "@/api/index.d";
|
||||
import TableCustom from "@/components/table-custom.vue";
|
||||
import TableSearch from "@/components/table-search.vue";
|
||||
import TableEdit from "@/components/table-edit.vue";
|
||||
import DeviceControl from "./deviceControl.vue";
|
||||
import BindArea from "./bindArea.vue";
|
||||
import { TableItem } from "@/types/table";
|
||||
import { FormOption, FormOptionList } from "@/types/form-option";
|
||||
import { useRouter } from "vue-router";
|
||||
@ -82,9 +86,8 @@ let TableEditOptions = ref<FormOption>({
|
||||
const addOp = [{ type: "input", label: "唯一编码", prop: "name", required: true }];
|
||||
const editOp = [
|
||||
{ type: "select", label: "选择手铐", prop: "name", required: true },
|
||||
{ type: "input", label: "唯一编码", prop: "name1" },
|
||||
{ type: "select", label: "辖区绑定", prop: "name2" },
|
||||
{ type: "input", label: "紧急电话", prop: "name3" },
|
||||
{ type: "select", label: "辖区列表", prop: "name2" },
|
||||
{ type: "select", label: "账号列表", prop: "name3" },
|
||||
];
|
||||
|
||||
// 查询相关
|
||||
@ -125,24 +128,24 @@ const searchOpt = ref<FormOptionList[]>([
|
||||
const handleSearch = () => {
|
||||
changePage(1);
|
||||
};
|
||||
|
||||
const selectDeviceList = ref<TDevice.IListRes[]>([]);
|
||||
// 表格相关
|
||||
let columns = ref([
|
||||
{ type: "selection" },
|
||||
{ type: "selection", reserveSelection: true },
|
||||
{ type: "index", label: "序号", width: 55, align: "center" },
|
||||
{ prop: "deviceId", label: "手铐IMEI", width: 120 },
|
||||
{ prop: "deviceId", label: "IMEI", width: 120 },
|
||||
{ prop: "adminName", label: "绑定警察名称", width: 180 },
|
||||
{ prop: "adminUsername", label: "绑定警察账户", width: 120 },
|
||||
{ prop: "orgName", label: "关联辖区", width: 120 },
|
||||
{ prop: "battery", label: "电量", width: 100 },
|
||||
{ prop: "deviceVersion", label: "版本号", width: 100 },
|
||||
{ prop: "status", label: "设备状态", width: 100 },
|
||||
{ prop: "mode", label: "当前模式", width: 100 },
|
||||
{ prop: "useStatus", label: "使用状态", width: 100 },
|
||||
{ prop: "deviceSwitch", label: "启用开关", width: 100 },
|
||||
{ prop: "orgName", label: "关联辖区编号", width: 120 },
|
||||
{ prop: "createTime", label: "最新通信时间", width: 180 },
|
||||
{ prop: "createTime", label: "创建时间", width: 180 },
|
||||
{ prop: "operator", label: "操作", width: 400 },
|
||||
{ prop: "operator", label: "操作", width: 400, fixed: "right" },
|
||||
]);
|
||||
const paging = reactive({
|
||||
page: 1,
|
||||
@ -155,18 +158,32 @@ const controlForm = reactive({
|
||||
});
|
||||
|
||||
const tableData = ref<TDevice.IListRes[]>([]);
|
||||
const orgAllData = ref<TOrg.IOrgRecordRes[]>([]);
|
||||
const getData = async () => {
|
||||
const res = await deviceList({ ...paging, ...query });
|
||||
tableData.value = res.records;
|
||||
paging.total = res.total;
|
||||
};
|
||||
|
||||
const bindWebFn = (val: TbindWeb) => {
|
||||
bindWeb(val).then(() => {
|
||||
ElMessage.success("操作成功");
|
||||
visible2.value = false;
|
||||
getData();
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelect = (val: TDevice.IListRes[]) => {
|
||||
selectDeviceList.value = val;
|
||||
};
|
||||
|
||||
const changePage = (val: number) => {
|
||||
paging.page = val;
|
||||
getData();
|
||||
};
|
||||
|
||||
const visible1 = ref(false);
|
||||
const visible2 = ref(false);
|
||||
const visible = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const rowData = ref<TDevice.IListRes>();
|
||||
@ -178,10 +195,11 @@ const handleAdd = () => {
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row?: TDevice.IListRes) => {
|
||||
rowData.value = { ...row };
|
||||
TableEditOptions.value.list = editOp;
|
||||
isEdit.value = true;
|
||||
visible.value = true;
|
||||
if (row) {
|
||||
selectDeviceList.value = [row];
|
||||
}
|
||||
if (selectDeviceList.value.length == 0) return ElMessage.warning("请选择设备");
|
||||
visible2.value = true;
|
||||
};
|
||||
|
||||
const handelRow = (row?: TDevice.IListRes) => {
|
||||
@ -199,6 +217,12 @@ const handelSwitch = (val: TDevice.IListRes, type: number) => {
|
||||
ElMessage.success("操作成功");
|
||||
});
|
||||
};
|
||||
|
||||
const getOrgAllList = () => {
|
||||
orgAllList().then((res) => {
|
||||
orgAllData.value = res;
|
||||
});
|
||||
};
|
||||
const handelControl = (type: number) => {
|
||||
switch (type) {
|
||||
case 4:
|
||||
@ -247,6 +271,7 @@ const closeDialog = () => {
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
getOrgAllList();
|
||||
});
|
||||
|
||||
//
|
||||
|
@ -82,10 +82,8 @@ let InfoWin = null;
|
||||
|
||||
const params = reactive<TLocateRecord.TReq>({
|
||||
deviceId: query.deviceId as string,
|
||||
// startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
|
||||
// endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
|
||||
startDate: "2021-04-14 00:00:00",
|
||||
endDate: "2025-05-14 23:59:59",
|
||||
startDate: `${format(d, "YYYY-MM-DD")} 00:00:00`,
|
||||
endDate: `${format(d, "YYYY-MM-DD")} 23:59:59`,
|
||||
});
|
||||
|
||||
const weekChange = (val: any) => {
|
||||
|
104
src/views/synthesizeManage/userManage/addUser.vue
Normal file
104
src/views/synthesizeManage/userManage/addUser.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
|
||||
<el-form-item label="用户名称" prop="name">
|
||||
<el-input v-model="ruleForm.name" placeholder="请输入用户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话号码" prop="phone">
|
||||
<el-input :maxlength="11" v-model="ruleForm.phone" placeholder="请输入电话号码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="警员号" prop="username">
|
||||
<el-input :maxlength="20" v-model="ruleForm.username" placeholder="请输入警员号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="roleId">
|
||||
<el-select v-model="ruleForm.roleId" placeholder="请选择人员">
|
||||
<el-option v-for="item in comm.roleList" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="辖区" prop="orgId">
|
||||
<el-select v-model="ruleForm.orgId" placeholder="请选择辖区">
|
||||
<el-option v-for="item in orgAllData" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-switch v-model="ruleForm.status" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<el-form-item class="footr">
|
||||
<el-button type="primary" @click="submitForm(ruleFormRef)"> 保存 </el-button>
|
||||
<el-button @click="emit('close')">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, PropType, reactive, ref } from "vue";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import { TDevice, TOrg } from "@/api/index.d";
|
||||
import { useCommonStore } from "@/store/common";
|
||||
import { TAccount } from "@/api/index.d";
|
||||
|
||||
const comm = useCommonStore();
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
|
||||
const { orgAllData, formData, api } = defineProps({
|
||||
orgAllData: {
|
||||
type: Array as PropType<TOrg.IOrgRecordRes[]>,
|
||||
dfault: () => {},
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
api: {
|
||||
type: Function,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["close"]);
|
||||
|
||||
const ruleForm = reactive(
|
||||
formData
|
||||
? { ...formData }
|
||||
: {
|
||||
name: "",
|
||||
phone: "",
|
||||
username: "",
|
||||
roleId: "",
|
||||
orgId: "",
|
||||
status: 1,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {});
|
||||
const rules = reactive<FormRules<typeof ruleForm>>({
|
||||
name: [{ required: true, message: "请输入用户名称", trigger: "blur" }],
|
||||
phone: [
|
||||
{ required: true, message: "请输入电话号码", trigger: "blur" },
|
||||
{ min: 11, message: "请输入11位手机号", trigger: "blur" },
|
||||
],
|
||||
username: [
|
||||
{ required: true, message: "请输入警员号", trigger: "blur" },
|
||||
{ min: 5, message: "警员号长度不能小于5位", trigger: "blur" },
|
||||
],
|
||||
roleId: [{ required: true, message: "请选择人员", trigger: "blur" }],
|
||||
orgId: [{ required: true, message: "请选择辖区", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
api(ruleForm);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.footr {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footr :deep(.el-form-item__content) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
@ -38,7 +38,10 @@
|
||||
</TableCustom>
|
||||
</div>
|
||||
<el-dialog :title="dialogTitle" v-model="visible" width="700px" destroy-on-close :close-on-click-modal="false" @close="closeDialog">
|
||||
<TableEdit :form-data="rowData" :options="options" :edit="isEdit" :update="updateData" @close="closeDialog" />
|
||||
<AddUser :orgAllData="orgAllData" :form-data="rowData" :api="updateData" @close="closeDialog" />
|
||||
</el-dialog>
|
||||
<el-dialog title="重置密码" v-model="visible1" width="700px" destroy-on-close :close-on-click-modal="false" @close="visible1 = false">
|
||||
<ResetPwd :api="handleResetPwd" @close="visible1 = false" />
|
||||
</el-dialog>
|
||||
<el-dialog title="编辑类型" v-model="typeVisible" width="800px" destroy-on-close :close-on-click-modal="false" @close="closeDialog">
|
||||
<UserType :typeHeadList="typeHeadList" :roleListData="roleListData" @complete="saveType" @addType="addType" />
|
||||
@ -50,16 +53,18 @@
|
||||
<script setup lang="ts" name="basetable">
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { accountAdd, accountModify, accountList, accountDeletet, passwordReset, roleList, roleMenuList, roleModify, roleAdd } from "@/api/index";
|
||||
import { accountAdd, accountModify, accountList, accountDeletet, passwordReset, roleList, roleMenuList, roleModify, roleAdd, orgAllList } from "@/api/index";
|
||||
import TableCustom from "@/components/table-custom.vue";
|
||||
import TableSearch from "@/components/table-search.vue";
|
||||
import TableEdit from "@/components/table-edit.vue";
|
||||
import BatchImp from "@/components/batch-imp.vue";
|
||||
import AddUser from "./addUser.vue";
|
||||
import { useCommonStore } from "@/store/common";
|
||||
import UserType from "./userType.vue";
|
||||
import ResetPwd from "./resetPwd.vue";
|
||||
import { TableItem } from "@/types/table";
|
||||
import { FormOption, FormOptionList } from "@/types/form-option";
|
||||
import { TAccount, TRoleList, TRoleMenuList, TRoleModify } from "@/api/index.d";
|
||||
import { TAccount, TOrg, TRoleList, TRoleMenuList, TRoleModify } from "@/api/index.d";
|
||||
// import { Hide, View } from "@element-plus/icons-vue";
|
||||
|
||||
enum roleEnum {
|
||||
@ -70,14 +75,15 @@ enum roleEnum {
|
||||
}
|
||||
|
||||
const comm = useCommonStore();
|
||||
|
||||
const visible = ref(false);
|
||||
const visible1 = ref(false);
|
||||
const typeVisible = ref(false);
|
||||
const isEdit = ref(false);
|
||||
const dialogTitle = ref("");
|
||||
const rowData = ref<TAccount.IListRes | undefined>();
|
||||
// 类型列表头列表
|
||||
let typeHeadList = ref<TRoleMenuList.TRes[]>([]);
|
||||
const orgAllData = ref<TOrg.IOrgRecordRes[]>([]);
|
||||
// 查询相关
|
||||
const query = reactive({
|
||||
name: "",
|
||||
@ -125,51 +131,12 @@ let columns = ref([
|
||||
{ prop: "phone", label: "手机号" },
|
||||
// { prop: "password", label: "密码", width: 120 },
|
||||
{ prop: "roleId", label: "类型" },
|
||||
{ prop: "orgName", label: "辖区" },
|
||||
{ prop: "createTime", label: "创建时间" },
|
||||
{ prop: "status", label: "状态" },
|
||||
{ prop: "operator", label: "操作", width: 250 },
|
||||
]);
|
||||
|
||||
let addOpt = {
|
||||
labelWidth: "100px",
|
||||
span: 24,
|
||||
list: [
|
||||
{ placeholder: "请输入用户名称", type: "input", label: "用户名称", prop: "name", required: true },
|
||||
{ placeholder: "请输入电话号码", type: "input", label: "电话号码", prop: "phone", required: true },
|
||||
{ placeholder: "请输入警员号", type: "input", label: "警员号", prop: "username", required: true },
|
||||
{
|
||||
placeholder: "请选择类型",
|
||||
type: "select",
|
||||
label: "类型",
|
||||
opts: [
|
||||
{ label: "管理员", value: -1 },
|
||||
{ label: "警察", value: 1 },
|
||||
{ label: "辅警", value: 2 },
|
||||
{ label: "协警", value: 3 },
|
||||
],
|
||||
prop: "roleId",
|
||||
required: true,
|
||||
},
|
||||
{ placeholder: "请输入密码", type: "password", label: "密码", prop: "password", required: true },
|
||||
{ type: "switch", label: "状态", prop: "status", activeValue: 1, inactiveValue: 0 },
|
||||
// { placeholder: "请输入确认密码", type: "password", label: "确认密码", prop: "confirmpassword" },
|
||||
// { placeholder: "请输入备注", type: "textarea", label: "备注", prop: "mon8ey" },
|
||||
],
|
||||
};
|
||||
|
||||
// 新增/编辑弹窗相关
|
||||
let options = ref<FormOption>();
|
||||
// 重置密码
|
||||
let pwdOpt = {
|
||||
labelWidth: "100px",
|
||||
span: 24,
|
||||
list: [
|
||||
// { type: "password", label: "原始密码", prop: "name1", required: true },
|
||||
{ type: "password", label: "新密码", prop: "password", required: true },
|
||||
// { type: "password", label: "确认密码", prop: "money3", required: true },
|
||||
],
|
||||
};
|
||||
|
||||
const handleImp = () => {
|
||||
batchImpRef.value.dialogVisible = true;
|
||||
};
|
||||
@ -182,15 +149,7 @@ const changePage = (val: number) => {
|
||||
paging.page = val;
|
||||
getData();
|
||||
};
|
||||
const roleListFn = () => {
|
||||
roleList().then((res) => {
|
||||
roleListData.value = res;
|
||||
let opts = res?.map((item) => {
|
||||
return { label: item.name, value: item.id };
|
||||
});
|
||||
addOpt.list[3].opts = opts;
|
||||
});
|
||||
};
|
||||
|
||||
const getData = async () => {
|
||||
const res = await accountList({ ...paging, ...query });
|
||||
tableData.value = res.records?.map((item) => {
|
||||
@ -200,6 +159,11 @@ const getData = async () => {
|
||||
paging.total = res.total;
|
||||
};
|
||||
|
||||
const getOrgAllList = () => {
|
||||
orgAllList().then((res) => {
|
||||
orgAllData.value = res;
|
||||
});
|
||||
};
|
||||
// 删除相关
|
||||
const handelDel = (row: TableItem) => {
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
@ -219,7 +183,7 @@ const updateData = (res) => {
|
||||
p = { ...res, id: rowData.value.id };
|
||||
msg = "重置成功";
|
||||
} else {
|
||||
p = { ...res, orgId: comm.user.orgId };
|
||||
p = { ...res };
|
||||
api = isEdit.value ? accountModify : accountAdd;
|
||||
msg = isEdit.value ? "修改成功" : "新增成功";
|
||||
}
|
||||
@ -230,41 +194,44 @@ const updateData = (res) => {
|
||||
getData();
|
||||
});
|
||||
};
|
||||
const handleResetPwd = (res) => {
|
||||
passwordReset({ ...res, id: rowData.value.id }).then((res) => {
|
||||
ElMessage.success("密码重置成功");
|
||||
visible1.value = true;
|
||||
});
|
||||
};
|
||||
const saveType = (row: TRoleModify.Ireq) => {
|
||||
const api = row.id ? roleModify : roleAdd;
|
||||
api(row).then(() => {
|
||||
ElMessage.success("保存成功");
|
||||
comm.getRoleList();
|
||||
// roleListFn();
|
||||
// typeVisible.value = false;
|
||||
});
|
||||
};
|
||||
const addType = () => {
|
||||
roleListData.value.push({ name: "", roleMenu: [] });
|
||||
};
|
||||
const roleListFn = () => {
|
||||
roleList().then((res) => {
|
||||
roleListData.value = res;
|
||||
});
|
||||
};
|
||||
const closeDialog = () => {
|
||||
visible.value = false;
|
||||
isEdit.value = false;
|
||||
};
|
||||
|
||||
const handelRow = (name: string, row?: TAccount.IListRes) => {
|
||||
if (name == "add") {
|
||||
dialogTitle.value = "新增用户";
|
||||
options.value = { ...addOpt };
|
||||
rowData.value = undefined;
|
||||
isEdit.value = false;
|
||||
visible.value = true;
|
||||
} else if (name == "pwd") {
|
||||
dialogTitle.value = "重置密码";
|
||||
options.value = { ...pwdOpt };
|
||||
rowData.value = { ...row };
|
||||
isEdit.value = true;
|
||||
visible1.value = true;
|
||||
} else if (name == "edit") {
|
||||
dialogTitle.value = "编辑";
|
||||
isEdit.value = true;
|
||||
options.value = { ...addOpt };
|
||||
rowData.value = { ...row };
|
||||
}
|
||||
visible.value = true;
|
||||
}
|
||||
};
|
||||
const getRoleMenuList = () => {
|
||||
roleMenuList().then((res) => {
|
||||
@ -272,9 +239,10 @@ const getRoleMenuList = () => {
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
roleListFn();
|
||||
getData();
|
||||
getRoleMenuList();
|
||||
getOrgAllList();
|
||||
roleListFn();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
58
src/views/synthesizeManage/userManage/resetPwd.vue
Normal file
58
src/views/synthesizeManage/userManage/resetPwd.vue
Normal file
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px">
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="ruleForm.password" type="password" placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="footr">
|
||||
<el-button type="primary" @click="submitForm(ruleFormRef)"> 保存 </el-button>
|
||||
<el-button @click="emit('close')">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import type { FormInstance } from "element-plus";
|
||||
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
|
||||
const { api } = defineProps({
|
||||
api: {
|
||||
type: Function,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["close"]);
|
||||
|
||||
const ruleForm = reactive({
|
||||
password: "",
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
const rules = reactive({
|
||||
password: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "密码长度不能小于6位", trigger: "blur" },
|
||||
],
|
||||
});
|
||||
|
||||
const submitForm = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
api(ruleForm);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.footr {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footr :deep(.el-form-item__content) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
31
sta.html
31
sta.html
@ -1,31 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>背景音乐示例</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>欢迎来到我的网站</h1>
|
||||
<!-- 插入背景音乐 -->
|
||||
<audio id="bgm" loop controls>
|
||||
<source src="./src/assets/audio/alarm.mp3" type="audio/mpeg">
|
||||
您的浏览器不支持 audio 标签。
|
||||
</audio>
|
||||
|
||||
<!-- 点击页面播放音乐js -->
|
||||
<script>
|
||||
document.addEventListener('click', function () {
|
||||
var audioElement = document.getElementById('bgm');
|
||||
if (audioElement.paused) { // 检查音频是否已播放
|
||||
audioElement.volume = 0.5; // 设置音量
|
||||
audioElement.play(); // 播放音频
|
||||
}
|
||||
}, { once: true }); // 确保事件处理器只执行一次
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user