2025年04月11日18:46:50

This commit is contained in:
luojiayi 2025-04-11 18:46:52 +08:00
parent 3346134b9a
commit f82d3934ba
27 changed files with 632 additions and 179 deletions

View File

@ -1 +1,2 @@
VITE_APP_URL = 'http://192.168.3.116:8001' VITE_APP_URL = 'http://192.168.3.116:8001'
VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket'

View File

@ -1,3 +1,4 @@
VITE_APP_URL = 'http://192.168.3.116:8001/' VITE_APP_URL = 'http://192.168.3.116:8001/'
VITE_APP_URL_WEBSOCKET = 'http://192.168.3.116:8000/api/websocket'

2
auto-imports.d.ts vendored
View File

@ -1,5 +1,5 @@
// Generated by 'unplugin-auto-import' // Generated by 'unplugin-auto-import'
export {} export {}
declare global { declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
} }

10
src/api/index.d.ts vendored
View File

@ -20,7 +20,7 @@ export interface TRoleList {
id?: number id?: number
name: string name: string
createTime?: string createTime?: string
roleMenu: TRoleMenu[] roleMenu?: TRoleMenu[]
} }
@ -81,9 +81,10 @@ export interface TDeviceConfigModify {
export namespace TRoleModify { export namespace TRoleModify {
export interface Ireq { export interface Ireq {
id: number id?: number
name: string name: string
roleMenu: TRoleMenu[] roleMenu: TRoleMenu[]
menus?: TRoleMenu[]
} }
} }
@ -257,9 +258,6 @@ export namespace TDevice {
} }
export namespace TOrg { export namespace TOrg {
export interface IAdd { export interface IAdd {
name: string; name: string;
@ -267,7 +265,7 @@ export namespace TOrg {
parentId: string; parentId: string;
} }
export interface IListReq extends Ipaging { export interface IListReq extends Ipaging {
name?: number; name?: string;
} }
export interface Idel { export interface Idel {
id: number; id: number;

View File

@ -3,7 +3,7 @@ import { TLogin, TAccount, IpagingRes, TDevice, TOrg, TRoleList, TStatisticsDevi
export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => { export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
return request({ return request({
url: '/v1/web/login', url: '/v1/api/login',
method: 'post', method: 'post',
data: p data: p
}); });
@ -12,7 +12,7 @@ export const fetchLogin = (p: TLogin.Ireq): Promise<TLogin.IRes> => {
// 批量导入账号样例 // 批量导入账号样例
export const exportDemoAccount = (): Promise<any> => { export const exportDemoAccount = (): Promise<any> => {
return request({ return request({
url: '/v1/web/upload/exportDemo/account', url: '/v1/api/upload/exportDemo/account',
method: 'get', method: 'get',
responseType: 'blob' responseType: 'blob'
}); });
@ -47,7 +47,7 @@ export const fetchRoleData = () => {
// 添加用户 // 添加用户
export const accountAdd = (p: TAccount.IAdd): Promise<null> => { export const accountAdd = (p: TAccount.IAdd): Promise<null> => {
return request({ return request({
url: '/v1/web/account/add', url: '/v1/api/account/add',
method: 'post', method: 'post',
data: p data: p
}); });
@ -55,7 +55,7 @@ export const accountAdd = (p: TAccount.IAdd): Promise<null> => {
// 删除用户 // 删除用户
export const accountDeletet = (p: TAccount.Idel): Promise<null> => { export const accountDeletet = (p: TAccount.Idel): Promise<null> => {
return request({ return request({
url: '/v1/web/account/delete', url: '/v1/api/account/delete',
method: 'post', method: 'post',
data: p data: p
}); });
@ -63,7 +63,7 @@ export const accountDeletet = (p: TAccount.Idel): Promise<null> => {
// 修改用户 // 修改用户
export const accountModify = (p: TAccount.IAdd): Promise<null> => { export const accountModify = (p: TAccount.IAdd): Promise<null> => {
return request({ return request({
url: '/v1/web/account/modify', url: '/v1/api/account/modify',
method: 'post', method: 'post',
data: p data: p
}); });
@ -71,7 +71,7 @@ export const accountModify = (p: TAccount.IAdd): Promise<null> => {
// 获取用户列表 // 获取用户列表
export const accountList = (p: TAccount.IListReq): Promise<IpagingRes<TAccount.IListRes>> => { export const accountList = (p: TAccount.IListReq): Promise<IpagingRes<TAccount.IListRes>> => {
return request({ return request({
url: '/v1/web/account/list', url: '/v1/api/account/list',
method: 'get', method: 'get',
params: p params: p
}); });
@ -79,7 +79,7 @@ export const accountList = (p: TAccount.IListReq): Promise<IpagingRes<TAccount.I
// 重置密码 // 重置密码
export const passwordReset = (p: TAccount.IResetPwd): Promise<null> => { export const passwordReset = (p: TAccount.IResetPwd): Promise<null> => {
return request({ return request({
url: '/v1/web/account/password/reset', url: '/v1/api/account/password/reset',
method: 'post', method: 'post',
data: p data: p
}); });
@ -89,7 +89,7 @@ export const passwordReset = (p: TAccount.IResetPwd): Promise<null> => {
// 获取设备列表 // 获取设备列表
export const deviceList = (p: TDevice.IListReq): Promise<IpagingRes<TDevice.IListRes>> => { export const deviceList = (p: TDevice.IListReq): Promise<IpagingRes<TDevice.IListRes>> => {
return request({ return request({
url: '/v1/web/device/list', url: '/v1/api/device/list',
method: 'get', method: 'get',
params: p params: p
}); });
@ -98,7 +98,7 @@ export const deviceList = (p: TDevice.IListReq): Promise<IpagingRes<TDevice.ILis
// 设置监测模式 // 设置监测模式
export const setMonitor = (p: TDevice.ISetMonitor): Promise<null> => { export const setMonitor = (p: TDevice.ISetMonitor): Promise<null> => {
return request({ return request({
url: '/v1/web/device/set/monitor', url: '/v1/api/device/set/monitor',
method: 'post', method: 'post',
data: p data: p
}); });
@ -107,7 +107,7 @@ export const setMonitor = (p: TDevice.ISetMonitor): Promise<null> => {
// 设置模式 // 设置模式
export const setMode = (p: TDevice.ISetMonitor): Promise<null> => { export const setMode = (p: TDevice.ISetMonitor): Promise<null> => {
return request({ return request({
url: '/v1/web/device/set/mode', url: '/v1/api/device/set/mode',
method: 'post', method: 'post',
data: p data: p
}); });
@ -116,7 +116,7 @@ export const setMode = (p: TDevice.ISetMonitor): Promise<null> => {
// 获取定位 // 获取定位
export const deviceGetLocation = (p: TDevice.ISetMonitor): Promise<null> => { export const deviceGetLocation = (p: TDevice.ISetMonitor): Promise<null> => {
return request({ return request({
url: '/v1/web/device/getLocate', url: '/v1/api/device/getLocate',
method: 'post', method: 'post',
data: p data: p
}); });
@ -125,7 +125,7 @@ export const deviceGetLocation = (p: TDevice.ISetMonitor): Promise<null> => {
// 设备控制 // 设备控制
export const deviceControl = (p: TDevice.ISetMonitor): Promise<null> => { export const deviceControl = (p: TDevice.ISetMonitor): Promise<null> => {
return request({ return request({
url: '/v1/web/device/control', url: '/v1/api/device/control',
method: 'post', method: 'post',
data: p data: p
}); });
@ -134,7 +134,7 @@ export const deviceControl = (p: TDevice.ISetMonitor): Promise<null> => {
// 设备使用记录 // 设备使用记录
export const deviceUseRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevice.IUseRecordRes>> => { export const deviceUseRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevice.IUseRecordRes>> => {
return request({ return request({
url: '/v1/web/device/use/record', url: '/v1/api/device/use/record',
method: 'get', method: 'get',
params: p params: p
}); });
@ -143,7 +143,7 @@ export const deviceUseRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevi
// 预警记录 // 预警记录
export const warningRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevice.IWarningRecordRes>> => { export const warningRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevice.IWarningRecordRes>> => {
return request({ return request({
url: '/v1/web/warning/record', url: '/v1/api/warning/record',
method: 'get', method: 'get',
params: p params: p
}); });
@ -152,7 +152,7 @@ export const warningRecord = (p: TDevice.IRecordReq): Promise<IpagingRes<TDevice
// 新增机构 // 新增机构
export const orgAdd = (p: TOrg.IAdd): Promise<null> => { export const orgAdd = (p: TOrg.IAdd): Promise<null> => {
return request({ return request({
url: '/v1/web/org/add', url: '/v1/api/org/add',
method: 'post', method: 'post',
data: p data: p
}); });
@ -160,7 +160,7 @@ export const orgAdd = (p: TOrg.IAdd): Promise<null> => {
// 修改机构 // 修改机构
export const orgModify = (p: TOrg.IAdd): Promise<null> => { export const orgModify = (p: TOrg.IAdd): Promise<null> => {
return request({ return request({
url: '/v1/web/org/modify', url: '/v1/api/org/modify',
method: 'post', method: 'post',
data: p data: p
}); });
@ -168,7 +168,7 @@ export const orgModify = (p: TOrg.IAdd): Promise<null> => {
// 机构列表 // 机构列表
export const orgList = (p?: TOrg.IListReq): Promise<IpagingRes<TOrg.IOrgRecordRes>> => { export const orgList = (p?: TOrg.IListReq): Promise<IpagingRes<TOrg.IOrgRecordRes>> => {
return request({ return request({
url: '/v1/web/org/list', url: '/v1/api/org/page',
method: 'get', method: 'get',
params: p params: p
}); });
@ -177,7 +177,7 @@ export const orgList = (p?: TOrg.IListReq): Promise<IpagingRes<TOrg.IOrgRecordRe
// 删除机构 // 删除机构
export const orgDelete = (p?: TOrg.Idel): Promise<null> => { export const orgDelete = (p?: TOrg.Idel): Promise<null> => {
return request({ return request({
url: '/v1/web/org/delete', url: '/v1/api/org/delete',
method: 'post', method: 'post',
data: p data: p
}); });
@ -186,7 +186,7 @@ export const orgDelete = (p?: TOrg.Idel): Promise<null> => {
// 获取角色列表 // 获取角色列表
export const roleList = (): Promise<TRoleList[]> => { export const roleList = (): Promise<TRoleList[]> => {
return request({ return request({
url: '/v1/web/role/list', url: '/v1/api/role/list',
method: 'get', method: 'get',
}); });
}; };
@ -194,7 +194,16 @@ export const roleList = (): Promise<TRoleList[]> => {
// 修改角色信息 // 修改角色信息
export const roleModify = (p: TRoleModify.Ireq): Promise<null> => { export const roleModify = (p: TRoleModify.Ireq): Promise<null> => {
return request({ return request({
url: '/v1/web/role/modify', url: '/v1/api/role/modify',
method: 'post',
data: p
});
};
// 添加角色信息
export const roleAdd = (p: TRoleModify.Ireq): Promise<null> => {
return request({
url: '/v1/api/role/add',
method: 'post', method: 'post',
data: p data: p
}); });
@ -203,7 +212,7 @@ export const roleModify = (p: TRoleModify.Ireq): Promise<null> => {
// 设备在线统计 // 设备在线统计
export const statisticsDevice = (): Promise<TStatisticsDevice> => { export const statisticsDevice = (): Promise<TStatisticsDevice> => {
return request({ return request({
url: '/v1/web/statistics/device', url: '/v1/api/statistics/device',
method: 'get', method: 'get',
}); });
}; };
@ -211,7 +220,7 @@ export const statisticsDevice = (): Promise<TStatisticsDevice> => {
// 内容数据 // 内容数据
export const statisticsContent = (p: statisticsContentReq): Promise<statisticsContentRes> => { export const statisticsContent = (p: statisticsContentReq): Promise<statisticsContentRes> => {
return request({ return request({
url: '/v1/web/statistics/content', url: '/v1/api/statistics/content',
method: 'post', method: 'post',
data: p data: p
}); });
@ -220,7 +229,7 @@ export const statisticsContent = (p: statisticsContentReq): Promise<statisticsCo
// 获取统计数据 // 获取统计数据
export const statisticsCount = (): Promise<TStatisticsCount> => { export const statisticsCount = (): Promise<TStatisticsCount> => {
return request({ return request({
url: '/v1/web/statistics/count', url: '/v1/api/statistics/count',
method: 'get', method: 'get',
}); });
}; };
@ -228,7 +237,7 @@ export const statisticsCount = (): Promise<TStatisticsCount> => {
// 告警统计 // 告警统计
export const statisticsWarningapi = (): Promise<statisticsContentRes> => { export const statisticsWarningapi = (): Promise<statisticsContentRes> => {
return request({ return request({
url: '/v1/web/statistics/warning', url: '/v1/api/statistics/warning',
method: 'get', method: 'get',
}); });
}; };
@ -236,7 +245,7 @@ export const statisticsWarningapi = (): Promise<statisticsContentRes> => {
// 预警记录 // 预警记录
export const warnRecord = (p: TWarnRecord.IListReq): Promise<IpagingRes<TWarnRecord.IListRes>> => { export const warnRecord = (p: TWarnRecord.IListReq): Promise<IpagingRes<TWarnRecord.IListRes>> => {
return request({ return request({
url: '/v1/web/warning/record', url: '/v1/api/warning/record',
method: 'get', method: 'get',
params: p params: p
}); });
@ -245,7 +254,7 @@ export const warnRecord = (p: TWarnRecord.IListReq): Promise<IpagingRes<TWarnRec
// 预警记录 // 预警记录
export const warningDetail = (p: TWarningDetail.TParams): Promise<TWarningDetail.TRes> => { export const warningDetail = (p: TWarningDetail.TParams): Promise<TWarningDetail.TRes> => {
return request({ return request({
url: '/v1/web/warning/detail', url: '/v1/api/warning/detail',
method: 'get', method: 'get',
params: p params: p
}); });
@ -254,7 +263,7 @@ export const warningDetail = (p: TWarningDetail.TParams): Promise<TWarningDetail
// 预警记录 // 预警记录
export const warningConfirm = (p: TWarningConfirm): Promise<null> => { export const warningConfirm = (p: TWarningConfirm): Promise<null> => {
return request({ return request({
url: '/v1/web/warning/confirm', url: '/v1/api/warning/confirm',
method: 'post', method: 'post',
data: p data: p
}); });
@ -263,7 +272,7 @@ export const warningConfirm = (p: TWarningConfirm): Promise<null> => {
// 获取专项配置 // 获取专项配置
export const deviceConfig = (p: TDeviceConfig.Treq): Promise<TDeviceConfig.Tres> => { export const deviceConfig = (p: TDeviceConfig.Treq): Promise<TDeviceConfig.Tres> => {
return request({ return request({
url: '/v1/web/device/deviceConfig', url: '/v1/api/device/deviceConfig',
method: 'get', method: 'get',
params: p params: p
}); });
@ -272,7 +281,7 @@ export const deviceConfig = (p: TDeviceConfig.Treq): Promise<TDeviceConfig.Tres>
// 修改专项配置 // 修改专项配置
export const deviceConfigModify = (p: TDeviceConfigModify): Promise<null> => { export const deviceConfigModify = (p: TDeviceConfigModify): Promise<null> => {
return request({ return request({
url: '/v1/web/device/deviceConfig/modify', url: '/v1/api/device/deviceConfig/modify',
method: 'post', method: 'post',
data: p data: p
}); });
@ -281,7 +290,7 @@ export const deviceConfigModify = (p: TDeviceConfigModify): Promise<null> => {
// 获取最新健康数据 // 获取最新健康数据
export const healthLatestData = (p: THealthLatestData.TReq): Promise<THealthLatestData.TRes> => { export const healthLatestData = (p: THealthLatestData.TReq): Promise<THealthLatestData.TRes> => {
return request({ return request({
url: '/v1/web/health/latestData', url: '/v1/api/health/latestData',
method: 'get', method: 'get',
params: p params: p
}); });
@ -290,7 +299,7 @@ export const healthLatestData = (p: THealthLatestData.TReq): Promise<THealthLate
// 设备定位记录 // 设备定位记录
export const locateRecord = (p: TLocateRecord.TReq): Promise<TLocateRecord.TRes[]> => { export const locateRecord = (p: TLocateRecord.TReq): Promise<TLocateRecord.TRes[]> => {
return request({ return request({
url: '/v1/web/device/locate/record', url: '/v1/api/device/locate/record',
method: 'get', method: 'get',
params: p params: p
}); });
@ -299,7 +308,7 @@ export const locateRecord = (p: TLocateRecord.TReq): Promise<TLocateRecord.TRes[
// 设置使用状态 // 设置使用状态
export const setUseStatus = (p: TSetUseStatus.TReq): Promise<null> => { export const setUseStatus = (p: TSetUseStatus.TReq): Promise<null> => {
return request({ return request({
url: '/v1/web/device/set/useStatus', url: '/v1/api/device/set/useStatus',
method: 'post', method: 'post',
data: p data: p
}); });
@ -308,7 +317,7 @@ export const setUseStatus = (p: TSetUseStatus.TReq): Promise<null> => {
// 设置禁用状态 // 设置禁用状态
export const setStatus = (p: TSetUseStatus.TReq): Promise<null> => { export const setStatus = (p: TSetUseStatus.TReq): Promise<null> => {
return request({ return request({
url: '/v1/web/device/set/status', url: '/v1/api/device/set/status',
method: 'post', method: 'post',
data: p data: p
}); });
@ -317,7 +326,7 @@ export const setStatus = (p: TSetUseStatus.TReq): Promise<null> => {
// 获取菜单列表 // 获取菜单列表
export const roleMenuList = (): Promise<TRoleMenuList.TRes[]> => { export const roleMenuList = (): Promise<TRoleMenuList.TRes[]> => {
return request({ return request({
url: '/v1/web/role/menu/list', url: '/v1/api/role/menu/list',
method: 'get', method: 'get',
}); });
}; };

Binary file not shown.

View File

@ -18,9 +18,9 @@
<div class="button-glow"></div> <div class="button-glow"></div>
</button> </button>
<!-- <audio ref="audioPlayer" autoplay loop> <audio ref="audioPlayer" hidden>
<source src="../assets/audio/alarm.mp3" type="video/mp3" /> <source src="../assets/audio/alarm.mp3" type="audio/mpeg" />
</audio> --> </audio>
</div> </div>
</div> </div>
</transition> </transition>
@ -43,14 +43,16 @@ defineProps({
}, },
}); });
defineEmits(["close", "confirm"]); const emit = defineEmits(["close", "confirm"]);
const handleAudioEnd = () => { const handleAudioEnd = () => {
if (audioPlayer.value) { emit("close");
audioPlayer.value.play().catch((error) => {
console.error("自动播放被阻止:", error); // if (audioPlayer.value) {
}); // audioPlayer.value.play().catch((error) => {
} // console.error(":", error);
// });
// }
}; };
onMounted(() => { onMounted(() => {

View File

@ -34,7 +34,7 @@ interface Tdata {
} }
const comm = useCommonStore(); const comm = useCommonStore();
const ACTION = import.meta.env.VITE_APP_URL + "/v1/web/upload/account"; const ACTION = import.meta.env.VITE_APP_URL + "/v1/api/upload/account";
const HEADERS = { const HEADERS = {
"Access-Token": comm.user.token, "Access-Token": comm.user.token,
}; };

View File

@ -24,6 +24,7 @@
style="width: 220px" style="width: 220px"
v-else-if="item.type === 'daterange'" v-else-if="item.type === 'daterange'"
v-model="query[item.prop]" v-model="query[item.prop]"
value-format="YYYY-MM-DD"
type="daterange" type="daterange"
range-separator="-" range-separator="-"
start-placeholder="开始日期" start-placeholder="开始日期"

View File

@ -1,17 +1,20 @@
<template> <template>
<el-upload <el-upload
:style="`--width: ${size}px; --height: ${size}px`" :style="`--width: ${props.size}px; --height: ${props.size}px`"
class="upload" class="upload"
:class="{ hide: disabled }"
accept="image/*" accept="image/*"
list-type="picture-card" list-type="picture-card"
v-model:file-list="fileList" v-model:file-list="fileList"
:action="ACTION" :action="ACTION"
:headers="HEADERS" :headers="HEADERS"
name="files"
:on-preview="handlePictureCardPreview" :on-preview="handlePictureCardPreview"
:on-remove="handleRemove" :on-remove="handleRemove"
:on-success="handleSuccess" :on-success="handleSuccess"
:limit="9" :limit="9"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:disabled="disabled"
> >
<el-icon><Plus /></el-icon> <el-icon><Plus /></el-icon>
</el-upload> </el-upload>
@ -22,9 +25,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref, watch } from "vue";
import { Plus } from "@element-plus/icons-vue"; import { Plus } from "@element-plus/icons-vue";
import { ElMessage, UploadProps } from "element-plus"; import { ElMessage, UploadFiles, UploadProps } from "element-plus";
import { useCommonStore } from "@/store/common"; import { useCommonStore } from "@/store/common";
interface Tdata { interface Tdata {
@ -35,12 +38,12 @@ interface Tdata {
msg: string; msg: string;
} }
const comm = useCommonStore(); const comm = useCommonStore();
const ACTION = import.meta.env.VITE_APP_URL + "/v1/web/upload/warning/img"; const ACTION = import.meta.env.VITE_APP_URL + "/v1/api/upload/warning/img";
const HEADERS = { const HEADERS = {
"Access-Token": comm.user.token, "Access-Token": comm.user.token,
}; };
const { size, modelValue } = defineProps({ const props = defineProps({
size: { size: {
type: Number, type: Number,
default: 100, default: 100,
@ -49,6 +52,14 @@ const { size, modelValue } = defineProps({
type: Array, type: Array,
default: () => [], default: () => [],
}, },
disabled: {
type: Boolean,
default: () => false,
},
limit: {
type: Number,
default: () => 9,
},
}); });
const emit = defineEmits(["update:modelValue"]); const emit = defineEmits(["update:modelValue"]);
@ -58,6 +69,22 @@ const fileList = ref<any[]>([]);
const dialogImageUrl = ref(""); const dialogImageUrl = ref("");
const dialogVisible = ref(false); const dialogVisible = ref(false);
watch(
() => props.modelValue,
(newVal) => {
if (newVal && newVal.length) {
fileList.value = newVal.map((item) => {
return {
name: item,
response: { code: 200, msg: "成功", data: [item] },
status: "success",
url: item,
};
});
}
}
);
const beforeUpload = (rawFile: any) => { const beforeUpload = (rawFile: any) => {
const isLtSize = rawFile.size / 1024 / 1024 < 5; const isLtSize = rawFile.size / 1024 / 1024 < 5;
if (!isLtSize) { if (!isLtSize) {
@ -67,13 +94,13 @@ const beforeUpload = (rawFile: any) => {
return true; return true;
}; };
const handleSuccess = ({ code, data, msg }: Tdata) => { const handleSuccess: UploadProps["onSuccess"] = (_: any, _1: any, uploadFiles: any) => {
if (code != 200) return ElMessage.error(msg); let list = uploadFiles.map((item) => item?.response?.data[0]);
let list = fileList.value.map((item) => item?.response?.data?.imeUrl);
emit("update:modelValue", list); emit("update:modelValue", list);
}; };
const handleRemove: UploadProps["onRemove"] = () => {
let list = fileList.value.map((item) => item?.response?.data?.imeUrl); const handleRemove = (_: any, uploadFiles: any) => {
let list = uploadFiles.map((item) => item.response.data[0]);
emit("update:modelValue", list); emit("update:modelValue", list);
}; };
@ -84,17 +111,18 @@ const handlePictureCardPreview: UploadProps["onPreview"] = (uploadFile) => {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.upload { .upload :deep(.el-upload) {
/deep/ .el-upload { width: var(--width);
width: var(--width); height: var(--height);
height: var(--height); }
} .upload :deep(.el-upload-list__item) {
/deep/ .el-upload-list__item { width: var(--width);
width: var(--width); height: var(--height);
height: var(--height); }
} .upload :deep(.el-icon--close-tip) {
/deep/ .el-icon--close-tip { display: none;
display: none; }
} .hide :deep(.el-upload--picture-card) {
display: none;
} }
</style> </style>

View File

@ -0,0 +1,19 @@
<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>

120
src/layout/index copy.vue Normal file
View File

@ -0,0 +1,120 @@
<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>

View File

@ -13,15 +13,30 @@
</router-view> </router-view>
</div> </div>
</div> </div>
<!-- <Alarm ref="alarmRef" v-if="visible" @close="close" /> -->
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useSidebarStore } from "@/store/sidebar"; import { useSidebarStore } from "@/store/sidebar";
import vHeader from "@/components/header.vue"; import vHeader from "@/components/header.vue";
import vSidebar from "@/components/sidebar.vue"; import vSidebar from "@/components/sidebar.vue";
import { routes } from "@/router/index"; 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 sidebar = useSidebarStore();
const alarmRef = ref(null);
const audioPlayer = ref(null);
const visible = ref(true);
const getInclude = (routeList: any) => { const getInclude = (routeList: any) => {
if (!routeList) return []; if (!routeList) return [];
@ -36,6 +51,27 @@ const getInclude = (routeList: any) => {
}); });
return list; return list;
}; };
const close = async () => {
try {
audioPlayer.value.play();
console.log(1111);
} catch (error) {
console.log(error, 2222);
}
};
setTimeout(() => {
audioPlayer.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); // const include = getInclude(routes[0].children);
</script> </script>

View File

@ -1,11 +1,13 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { TLogin } from "@/api/index.d"; import { TLogin, TRoleList } from "@/api/index.d";
import { roleList } from '@/api';
export const useCommonStore = defineStore('common', { export const useCommonStore = defineStore('common', {
state: () => { state: () => {
return { return {
time: new Date(), time: new Date(),
user: <TLogin.IRes>{} user: <TLogin.IRes>{},
roleList: <TRoleList[]>[]
}; };
}, },
getters: {}, getters: {},
@ -13,6 +15,11 @@ export const useCommonStore = defineStore('common', {
setTime() { setTime() {
this.time = new Date(); this.time = new Date();
}, },
getRoleList() {
roleList().then(res => {
this.roleList = res
})
},
setUser(data: TLogin.IRes) { setUser(data: TLogin.IRes) {
this.user = data this.user = data
}, },

158
src/utils/webSocket.js Normal file
View File

@ -0,0 +1,158 @@
import { ref, onMounted, onUnmounted } from 'vue';
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.heartbeatTimer = null;
this.reconnectTimer = null;
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket已连接');
this.webScoketLogin();
this.reconnectAttempts = 0; // 成功连接后重置重试次数
this.startHeartbeat(); // 开始心跳检测
};
this.socket.onmessage = (event) => {
// 处理接收到的消息
console.log('Message received:', event.data);
};
this.socket.onclose = () => {
console.log('WebSocket连接已关闭');
this.stopHeartbeat();
this.attemptReconnect();
};
this.socket.onerror = (error) => {
console.log('WebSocket连接错误', error);
};
}
webScoketLogin() {
this.sendMessage({
username: "admin",
password: "111111",
type: 0,
cmd: "webLogin",
})
}
startHeartbeat() {
if (!this.heartbeatTimer) {
this.heartbeatTimer = setInterval(() => {
if (this.socket.readyState === WebSocket.OPEN) {
this.isAlive = false;
this.sendPing();
}
}, this.HEARTBEAT_INTERVAL);
}
}
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
sendPing() {
console.log('发送心跳', JSON.stringify({ cmd: 'heartbeat' }));
this.socket.send(JSON.stringify({ cmd: 'heartbeat' }));
setTimeout(() => {
if (!this.isAlive) {
console.warn('WebSocket heartbeat failed, attempting to reconnect...');
this.socket.close();
}
}, this.HEARTBEAT_INTERVAL / 2); // 如果没有pong响应则关闭并重连
}
receivePong() {
this.isAlive = true;
}
attemptReconnect() {
if (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
this.reconnectAttempts++;
console.log(`Attempting to reconnect (${this.reconnectAttempts})...`);
this.reconnectTimer = setTimeout(() => {
this.connect();
}, 1000 * this.reconnectAttempts); // 指数退避算法
} else {
console.error('Max reconnect attempts reached');
}
}
sendMessage(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
}
close() {
this.stopHeartbeat();
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.socket) {
this.socket.close();
this.socket = null;
}
}
}
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 };
}

View File

@ -44,23 +44,33 @@ enum warnTypeEnum {
} }
const router = useRouter(); const router = useRouter();
// //
const query = reactive({ const query = reactive<any>({});
name: "",
});
const searchOpt = ref<FormOptionList[]>([ const searchOpt = ref<FormOptionList[]>([
{ type: "select", label: "来源手铐:", prop: "name1", opts: [{ label: "866503071660886", value: "866503071660886" }] }, { type: "input", label: "手铐IMEI号", prop: "deviceId" },
{ type: "select", label: "事件类型:", prop: "name2", opts: [{ label: "866503071660886", value: "866503071660886" }] }, {
{ type: "select", label: "事件级别:", prop: "name3", opts: [{ label: "866503071660886", value: "866503071660886" }] }, type: "select",
label: "告警类型:",
prop: "warnType",
opts: [
{ label: "SOS告警", value: 0 },
{ label: "围栏告警", value: 1 },
{ label: "破坏告警", value: 2 },
{ label: "低电告警", value: 3 },
{ label: "心率告警", value: 4 },
{ label: "血氧告警", value: 5 },
{ label: "体温告警", value: 6 },
],
},
{ {
type: "select", type: "select",
label: "处理状态:", label: "处理状态:",
prop: "status", prop: "status",
opts: [ opts: [
{ label: "未处理", value: "0" }, { label: "待处理", value: 0 },
{ label: "已处理", value: "1" }, { label: "已处理", value: 1 },
], ],
}, },
{ type: "daterange", label: "创建时间:", prop: "name5" }, { type: "daterange", label: "创建时间:", prop: "time" },
]); ]);
const handleSearch = () => { const handleSearch = () => {
changePage(1); changePage(1);
@ -86,7 +96,10 @@ const tableData = ref<TWarnRecord.IListRes[]>([]);
const getData = async () => { const getData = async () => {
try { try {
const res = await warnRecord(paging); console.log(query, "queryqueryquery");
let p = { ...paging, ...query, startDate: query.time?.[0], endDate: query.time?.[1] };
delete p.time;
const res = await warnRecord(p);
tableData.value = res.records; tableData.value = res.records;
paging.total = res.total; paging.total = res.total;
} catch (error) {} } catch (error) {}
@ -97,10 +110,10 @@ const changePage = (val: number) => {
paging.page = val; paging.page = val;
getData(); getData();
}; };
const toIncidentDispose = (deviceId: string) => { const toIncidentDispose = (id: string) => {
router.push({ router.push({
path: "/incidentDispose", path: "/incidentDispose",
query: { deviceId }, query: { id },
}); });
}; };
</script> </script>

View File

@ -47,26 +47,26 @@
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
<el-form-item label="处理人:" prop="name"> <el-form-item label="处理人:" prop="name">
<el-input v-model="ruleForm.name" clearable :disabled="curData.status == 1" /> <el-input v-model="ruleForm.name" clearable :disabled="disabled" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="警号:" prop="username"> <el-form-item label="警号:" prop="username">
<el-input v-model="ruleForm.username" clearable :disabled="curData.status == 1" /> <el-input v-model="ruleForm.username" clearable :disabled="disabled" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-form-item label="处理记录:" prop="content"> <el-form-item label="处理记录:" prop="content">
<el-input v-model="ruleForm.content" clearable :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" :disabled="curData.status == 1" /> <el-input v-model="ruleForm.content" clearable :autosize="{ minRows: 2, maxRows: 4 }" type="textarea" :disabled="disabled" />
</el-form-item> </el-form-item>
<el-form-item label="处理图片:" prop="images"> <el-form-item label="处理图片:" prop="images">
<Upload v-model="ruleForm.images" :disabled="curData.status == 1" /> <Upload v-model="ruleForm.images" :disabled="disabled" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</div> </div>
<div class="right-foot"> <div class="right-foot" v-if="!disabled">
<el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button> <el-button type="primary" @click="submitForm(ruleFormRef)" :disabled="disabled">保存</el-button>
</div> </div>
</div> </div>
</el-col> </el-col>
@ -77,7 +77,7 @@
<script setup lang="ts" name="incidentDispose"> <script setup lang="ts" name="incidentDispose">
import location from "@/assets/img/location.png"; import location from "@/assets/img/location.png";
import { MapCustom } from "@/utils/mapCustom"; import { MapCustom } from "@/utils/mapCustom";
import { onMounted, ref, reactive } from "vue"; import { onMounted, ref, reactive, watch } from "vue";
import Upload from "@/components/upload-img.vue"; import Upload from "@/components/upload-img.vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { warningDetail, warningConfirm } from "@/api/index"; import { warningDetail, warningConfirm } from "@/api/index";
@ -106,6 +106,7 @@ enum warnTypeEnum {
} }
const { query } = useRoute(); const { query } = useRoute();
const chartRef = ref(null); const chartRef = ref(null);
const disabled = ref(false);
const ruleFormRef = ref<FormInstance>(); const ruleFormRef = ref<FormInstance>();
let map = null; let map = null;
@ -135,7 +136,7 @@ const ringOptions = {
], ],
}; };
const ruleForm = reactive<TWarningConfirm>({ const ruleForm = reactive<TWarningConfirm>({
id: query.deviceId as string, id: query.id as string,
name: "", name: "",
username: "", username: "",
content: "", content: "",
@ -174,17 +175,23 @@ const rules = reactive<FormRules<TWarningConfirm>>({
content: [{ required: true, message: "请输入处理记录", trigger: "blur" }], content: [{ required: true, message: "请输入处理记录", trigger: "blur" }],
images: [{ required: true, message: "请上传图片", trigger: "blur" }], images: [{ required: true, message: "请上传图片", trigger: "blur" }],
}); });
watch(
() => curData.value,
(newVal) => {
if (newVal && newVal.status == 1) {
disabled.value = true;
}
}
);
const getData = async () => { const getData = async () => {
try { try {
const res = await warningDetail({ id: query.deviceId as string }); const res = await warningDetail({ id: query.id as string });
curData.value = res; curData.value = res;
ruleForm.name = res.rname; ruleForm.name = res.rname;
ruleForm.username = res.rname; ruleForm.username = res.rname;
ruleForm.content = res.rcontent; ruleForm.content = res.rcontent;
ruleForm.images = JSON.parse(res.rimg); ruleForm.images = JSON.parse(res.rimg);
console.log(ruleForm, "ruleFormruleFormruleFormruleForm");
let icon = map.newIcon(location); let icon = map.newIcon(location);
let marker = map.marker({ icon, position: [116.406315, 39.908775] }); let marker = map.marker({ icon, position: [116.406315, 39.908775] });
marker.setMap(map.map); marker.setMap(map.map);
@ -245,6 +252,7 @@ onMounted(() => {
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%;
.info { .info {
background: #ffffff; background: #ffffff;
height: 100%; height: 100%;

View File

@ -56,6 +56,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
if (valid) { if (valid) {
fetchLogin(ruleForm).then((res) => { fetchLogin(ruleForm).then((res) => {
comm.setUser(res); comm.setUser(res);
comm.getRoleList();
ElMessage({ ElMessage({
message: "登录成功", message: "登录成功",
type: "success", type: "success",

View File

@ -8,7 +8,7 @@
</div> </div>
<TableCustom :columns="columns" :tableData="tableData" :paging="page" :changePage="changePage"> <TableCustom :columns="columns" :tableData="tableData" :paging="page" :changePage="changePage">
<template #location="{ rows }"> <template #location="{ rows }">
<el-button type="success" link :icon="View" v-if="rows.warnType == 0 || rows.warnType == 1" @click="toIncidentDispose(rows.deviceId)"> 查看 </el-button> <el-button type="success" link :icon="View" v-if="rows.warnType == 0 || rows.warnType == 1" @click="toIncidentDispose(rows.id)"> 查看 </el-button>
</template> </template>
<template #status="{ rows }"> <template #status="{ rows }">
<el-tag :type="rows.status == 1 ? 'success' : 'danger'"> <el-tag :type="rows.status == 1 ? 'success' : 'danger'">
@ -75,10 +75,10 @@ const handleInput = debounce((e) => {
getData(); getData();
}, 500); }, 500);
const toIncidentDispose = (deviceId: string) => { const toIncidentDispose = (id: string) => {
router.push({ router.push({
path: "/incidentDispose", path: "/incidentDispose",
query: { deviceId }, query: { id },
}); });
}; };
</script> </script>

View File

@ -27,7 +27,6 @@ const { list } = defineProps({
default: () => [], default: () => [],
}, },
}); });
console.log(list, "list1111");
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.monitoring-top { .monitoring-top {

View File

@ -24,7 +24,7 @@
</template> </template>
<script setup lang="ts" name="basetable"> <script setup lang="ts" name="basetable">
import { ref, reactive } from "vue"; import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import { CirclePlusFilled } from "@element-plus/icons-vue"; import { CirclePlusFilled } from "@element-plus/icons-vue";
import { fetchData, orgAdd, orgModify, orgList, orgDelete } from "@/api/index"; import { fetchData, orgAdd, orgModify, orgList, orgDelete } from "@/api/index";
@ -45,17 +45,8 @@ const query = reactive({
name: "", name: "",
}); });
const searchOpt = ref<FormOptionList[]>([ const searchOpt = ref<FormOptionList[]>([
{ type: "input", label: "辖区名称:", prop: "name1" }, { type: "input", label: "辖区名称:", prop: "name" },
{ type: "daterange", label: "创建时间:", prop: "name2" }, // { type: "daterange", label: "", prop: "name2" },
{
type: "select",
label: "状态:",
prop: "name3",
opts: [
{ label: "启用", value: "1" },
{ label: "禁用", value: "0" },
],
},
]); ]);
// //
@ -77,11 +68,11 @@ const paging = reactive({
const tableData = ref<TOrg.IOrgRecordRes[]>([]); const tableData = ref<TOrg.IOrgRecordRes[]>([]);
const getData = async () => { const getData = async () => {
const res = await orgList(paging); const res = await orgList({ ...paging, ...query });
tableData.value = res.records; tableData.value = res.records;
paging.total = res.total; paging.total = res.total;
}; };
getData();
const handleSearch = () => { const handleSearch = () => {
changePage(1); changePage(1);
}; };
@ -128,6 +119,9 @@ const updateData = (res) => {
getData(); getData();
}); });
}; };
onMounted(() => {
getData();
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -143,10 +143,10 @@ const changeWarningPage = (val: number) => {
paging1.page = val; paging1.page = val;
getWarningData(); getWarningData();
}; };
const toIncidentDispose = (deviceId: string) => { const toIncidentDispose = (id: string) => {
router.push({ router.push({
path: "/incidentDispose", path: "/incidentDispose",
query: { deviceId }, query: { id },
}); });
}; };
</script> </script>

View File

@ -88,12 +88,39 @@ const editOp = [
]; ];
// //
const query = reactive({ const query = reactive({});
name: "",
});
const searchOpt = ref<FormOptionList[]>([ const searchOpt = ref<FormOptionList[]>([
{ type: "input", label: "手铐SN", prop: "name" }, { type: "input", label: "手铐SN", prop: "deviceId" },
{ type: "input", label: "警察账号:", prop: "name1" }, { type: "input", label: "警察名称:", prop: "name" },
{
type: "select",
label: "当前模式:",
prop: "mode",
opts: [
{ label: "常规", value: "0" },
{ label: "审讯", value: "1" },
{ label: "户外押送", value: "2" },
],
},
{
type: "select",
label: "设备状态:",
prop: "status",
opts: [
{ label: "离线", value: "0" },
{ label: "在线", value: "1" },
{ label: "充电中", value: "2" },
],
},
{
type: "select",
label: "使用状态:",
prop: "useStatus",
opts: [
{ label: "未使用", value: "0" },
{ label: "使用中", value: "1" },
],
},
]); ]);
const handleSearch = () => { const handleSearch = () => {
changePage(1); changePage(1);
@ -103,19 +130,18 @@ const handleSearch = () => {
let columns = ref([ let columns = ref([
{ type: "selection" }, { type: "selection" },
{ type: "index", label: "序号", width: 55, align: "center" }, { type: "index", label: "序号", width: 55, align: "center" },
{ prop: "deviceId", label: "手铐IMEI" }, { prop: "deviceId", label: "手铐IMEI", width: 120 },
{ prop: "name", label: "绑定警察名称" }, { prop: "adminName", label: "绑定警察名称", width: 180 },
{ prop: "username", label: "绑定警察账户" }, { prop: "adminUsername", label: "绑定警察账户", width: 120 },
{ prop: "battery", label: "电量" }, { prop: "battery", label: "电量", width: 100 },
{ prop: "deviceVersion", label: "版本号" }, { prop: "deviceVersion", label: "版本号", width: 100 },
{ prop: "status", label: "设备状态" }, { prop: "status", label: "设备状态", width: 100 },
{ prop: "mode", label: "当前模式" }, { prop: "mode", label: "当前模式", width: 100 },
{ prop: "useStatus", label: "使用状态" }, { prop: "useStatus", label: "使用状态", width: 100 },
{ prop: "deviceSwitch", label: "启用开关" }, { prop: "deviceSwitch", label: "启用开关", width: 100 },
{ prop: "orgName", label: "关联辖区编号" }, { prop: "orgName", label: "关联辖区编号", width: 120 },
{ prop: "createTime", label: "最新通信时间" }, { prop: "createTime", label: "最新通信时间", width: 180 },
{ prop: "createTime", label: "创建时间" }, { prop: "createTime", label: "创建时间", width: 180 },
{ prop: "operator", label: "操作", width: 400 }, { prop: "operator", label: "操作", width: 400 },
]); ]);
const paging = reactive({ const paging = reactive({
@ -130,7 +156,7 @@ const controlForm = reactive({
const tableData = ref<TDevice.IListRes[]>([]); const tableData = ref<TDevice.IListRes[]>([]);
const getData = async () => { const getData = async () => {
const res = await deviceList(paging); const res = await deviceList({ ...paging, ...query });
tableData.value = res.records; tableData.value = res.records;
paging.total = res.total; paging.total = res.total;
}; };

View File

@ -11,14 +11,14 @@
<!-- <el-button type="danger">删除</el-button> --> <!-- <el-button type="danger">删除</el-button> -->
</template> </template>
<template #state="{ rows }"> <template #status="{ rows }">
<el-switch v-model="rows.state" :active-value="1" :inactive-value="0" /> <el-switch v-model="rows.status" :active-value="1" :inactive-value="0" />
</template> </template>
<template #roleId="{ rows }"> <template #roleId="{ rows }">
{{ roleEnum[rows.roleId] }} {{ roleEnum[rows.roleId] }}
</template> </template>
<!--
<template #password="{ rows }"> <template #password="{ rows }">
<div v-if="rows.flag" @click="rows.flag = false"> <div v-if="rows.flag" @click="rows.flag = false">
{{ rows.password }} {{ rows.password }}
@ -28,7 +28,7 @@
****** ******
<el-icon style="cursor: pointer"><Hide /></el-icon> <el-icon style="cursor: pointer"><Hide /></el-icon>
</div> </div>
</template> </template> -->
<template #operator="{ rows }"> <template #operator="{ rows }">
<el-button link type="primary" size="small" @click="handelRow('pwd', rows)"> 重置密码 </el-button> <el-button link type="primary" size="small" @click="handelRow('pwd', rows)"> 重置密码 </el-button>
@ -50,7 +50,7 @@
<script setup lang="ts" name="basetable"> <script setup lang="ts" name="basetable">
import { ref, reactive, onMounted } from "vue"; import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import { accountAdd, accountModify, accountList, accountDeletet, passwordReset, roleList, roleMenuList, roleModify } from "@/api/index"; import { accountAdd, accountModify, accountList, accountDeletet, passwordReset, roleList, roleMenuList, roleModify, roleAdd } from "@/api/index";
import TableCustom from "@/components/table-custom.vue"; import TableCustom from "@/components/table-custom.vue";
import TableSearch from "@/components/table-search.vue"; import TableSearch from "@/components/table-search.vue";
import TableEdit from "@/components/table-edit.vue"; import TableEdit from "@/components/table-edit.vue";
@ -59,17 +59,18 @@ import { useCommonStore } from "@/store/common";
import UserType from "./userType.vue"; import UserType from "./userType.vue";
import { TableItem } from "@/types/table"; import { TableItem } from "@/types/table";
import { FormOption, FormOptionList } from "@/types/form-option"; import { FormOption, FormOptionList } from "@/types/form-option";
import { TAccount, TRoleList, TRoleMenuList } from "@/api/index.d"; import { TAccount, TRoleList, TRoleMenuList, TRoleModify } from "@/api/index.d";
// import { Hide, View } from "@element-plus/icons-vue"; // import { Hide, View } from "@element-plus/icons-vue";
enum roleEnum { enum roleEnum {
管理员 = -1, "管理员" = -1,
警察 = 1, "警察" = 1,
辅警 = 2, "辅警" = 2,
协警 = 3, "协警" = 3,
} }
const comm = useCommonStore(); const comm = useCommonStore();
const visible = ref(false); const visible = ref(false);
const typeVisible = ref(false); const typeVisible = ref(false);
const isEdit = ref(false); const isEdit = ref(false);
@ -92,25 +93,25 @@ const roleListData = ref<TRoleList[]>([]);
const searchOpt = ref<FormOptionList[]>([ const searchOpt = ref<FormOptionList[]>([
{ type: "input", label: "用户名:", prop: "name" }, { type: "input", label: "用户名:", prop: "name" },
{ type: "input", label: "警员号:", prop: "username" },
{ {
type: "select", type: "select",
label: "用户类型:", label: "用户类型:",
prop: "name1", prop: "roleId",
opts: [ opts: comm.roleList.map((item) => {
{ label: "管理员", value: "1" }, return {
{ label: "警察", value: "2" }, label: item.name,
{ label: "辅警", value: "3" }, value: item.id,
{ label: "协警", value: "4" }, };
], }),
}, },
{ type: "daterange", label: "创建时间:", prop: "name2" },
{ {
type: "select", type: "select",
label: "状态:", label: "状态:",
prop: "name3", prop: "status",
opts: [ opts: [
{ label: "启用", value: "1" },
{ label: "禁用", value: "0" }, { label: "禁用", value: "0" },
{ label: "启用", value: "1" },
], ],
}, },
]); ]);
@ -122,10 +123,10 @@ let columns = ref([
{ prop: "name", label: "用户名称" }, { prop: "name", label: "用户名称" },
{ prop: "username", label: "警员号" }, { prop: "username", label: "警员号" },
{ prop: "phone", label: "手机号" }, { prop: "phone", label: "手机号" },
{ prop: "password", label: "密码", width: 120 }, // { prop: "password", label: "", width: 120 },
{ prop: "roleId", label: "类型" }, { prop: "roleId", label: "类型" },
{ prop: "createTime", label: "创建时间" }, { prop: "createTime", label: "创建时间" },
{ prop: "state", label: "状态" }, { prop: "status", label: "状态" },
{ prop: "operator", label: "操作", width: 250 }, { prop: "operator", label: "操作", width: 250 },
]); ]);
@ -191,7 +192,7 @@ const roleListFn = () => {
}); });
}; };
const getData = async () => { const getData = async () => {
const res = await accountList(paging); const res = await accountList({ ...paging, ...query });
tableData.value = res.records?.map((item) => { tableData.value = res.records?.map((item) => {
item.flag = false; item.flag = false;
return item; return item;
@ -229,11 +230,13 @@ const updateData = (res) => {
getData(); getData();
}); });
}; };
const saveType = (row: TRoleList) => { const saveType = (row: TRoleModify.Ireq) => {
roleModify(row).then(() => { const api = row.id ? roleModify : roleAdd;
api(row).then(() => {
ElMessage.success("保存成功"); ElMessage.success("保存成功");
roleListFn(); comm.getRoleList();
typeVisible.value = false; // roleListFn();
// typeVisible.value = false;
}); });
}; };
const addType = () => { const addType = () => {

View File

@ -26,7 +26,7 @@
<el-table-column prop="date" label="操作" width="200" align="center"> <el-table-column prop="date" label="操作" width="200" align="center">
<template #default="{ row }"> <template #default="{ row }">
<el-button link type="primary" size="small" @click="emit('complete', row)">保存</el-button> <el-button link type="primary" size="small" @click="handleComplete(row)">保存</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -49,6 +49,11 @@ const { roleListData, typeHeadList } = defineProps({
}); });
const emit = defineEmits(["complete", "addType"]); const emit = defineEmits(["complete", "addType"]);
const handleComplete = (row: TRoleList) => {
let p = { ...row, menus: row.roleMenu };
delete p.roleMenu;
emit("complete", p);
};
const isCheck = (ite, row) => { const isCheck = (ite, row) => {
let flag = false; let flag = false;

31
sta.html Normal file
View File

@ -0,0 +1,31 @@
<!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>

View File

@ -1067,6 +1067,11 @@ hasown@^2.0.0:
dependencies: dependencies:
function-bind "^1.1.2" function-bind "^1.1.2"
howler@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/howler/-/howler-2.2.4.tgz#bd3df4a4f68a0118a51e4bd84a2bfc2e93e6e5a1"
integrity sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w==
html-void-elements@^2.0.0: html-void-elements@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f" resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f"
@ -1511,13 +1516,6 @@ pify@^4.0.1:
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
pinia-plugin-persist@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz#fc696f225527f30bd5955109fafadd43c725e888"
integrity sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==
dependencies:
vue-demi "^0.12.1"
pinia-plugin-persistedstate@^4.2.0: pinia-plugin-persistedstate@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.2.0.tgz#31973291b768bd84ce24e55083e7171b2f451ad1" resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.2.0.tgz#31973291b768bd84ce24e55083e7171b2f451ad1"
@ -1966,11 +1964,6 @@ vue-demi@*, vue-demi@>=0.14.5:
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2"
integrity sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA== integrity sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==
vue-demi@^0.12.1:
version "0.12.5"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.12.5.tgz#8eeed566a7d86eb090209a11723f887d28aeb2d1"
integrity sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==
vue-demi@^0.13.11: vue-demi@^0.13.11:
version "0.13.11" version "0.13.11"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"