@@ -24,7 +24,6 @@ declare module 'vue' { | |||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] | |||
ElButton: typeof import('element-plus/es')['ElButton'] | |||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] | |||
ElCol: typeof import('element-plus/es')['ElCol'] | |||
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] | |||
ElContainer: typeof import('element-plus/es')['ElContainer'] | |||
ElDialog: typeof import('element-plus/es')['ElDialog'] | |||
@@ -43,13 +42,10 @@ declare module 'vue' { | |||
ElMain: typeof import('element-plus/es')['ElMain'] | |||
ElMenu: typeof import('element-plus/es')['ElMenu'] | |||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] | |||
ElOption: typeof import('element-plus/es')['ElOption'] | |||
ElPagination: typeof import('element-plus/es')['ElPagination'] | |||
ElPopover: typeof import('element-plus/es')['ElPopover'] | |||
ElRadio: typeof import('element-plus/es')['ElRadio'] | |||
ElRow: typeof import('element-plus/es')['ElRow'] | |||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] | |||
ElSelect: typeof import('element-plus/es')['ElSelect'] | |||
ElSpace: typeof import('element-plus/es')['ElSpace'] | |||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] | |||
ElSwitch: typeof import('element-plus/es')['ElSwitch'] | |||
@@ -59,6 +55,8 @@ declare module 'vue' { | |||
ElTabs: typeof import('element-plus/es')['ElTabs'] | |||
ElTag: typeof import('element-plus/es')['ElTag'] | |||
ElTooltip: typeof import('element-plus/es')['ElTooltip'] | |||
ElTree: typeof import('element-plus/es')['ElTree'] | |||
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] | |||
ESign: typeof import('./src/components/ESign/index.vue')['default'] | |||
FormContainer: typeof import('./src/components/Form/FormContainer/index.vue')['default'] | |||
Grid: typeof import('./src/components/Grid/index.vue')['default'] | |||
@@ -2,7 +2,7 @@ | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<link rel="icon" href="/qjkj.ico" /> | |||
<link rel="icon" href="/api/sys/ico" /> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |||
<title><%- title %></title> | |||
<!-- <script src="https://unpkg.com/aliyun-rts-sdk@1.2.1/dist/aliyun-rts-sdk.js"></script> --> | |||
@@ -0,0 +1,86 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta http-equiv="x-ua-compatible" content="IE=edge"> | |||
<meta name="viewport" | |||
content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" /> | |||
<title>Aliplayer Rts Demo</title> | |||
<link rel="stylesheet" | |||
href="https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css" /> | |||
<script type="text/javascript" charset="utf-8" | |||
src="https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/aliplayer-min.js"></script> | |||
</head> | |||
<body style="margin: 0;"> | |||
<div class="prism-player" id="player-con"></div> | |||
<script> | |||
// 使用: /static/rtsPlayer.html?rtsUrl= | |||
/** | |||
* 播放器默认播放 source 提供的 rts 拉流地址,如果失败,则会自动降级至 rtsFallbackSource 提供的拉流地址(如 HLS 地址)。 | |||
* 可能的降级场景包括: | |||
* 1. 浏览器不支持 RTS,直接降级 | |||
* 2. RTS 信令请求失败(拉流地址无效、https配置无效、RTS配置无效等),直接降级 | |||
* 3. RTS 起播超时或中途断流,按自定义策略重试失败后降级 | |||
**/ | |||
// 更多播放器配置请参考 https://player.alicdn.com/aliplayer/index.html | |||
let rtsUrl = getUrlParams(location.href)['rtsUrl'] || '' | |||
var options = { | |||
"id": "player-con", | |||
"source": rtsUrl, | |||
"rtsFallbackSource": "降级地址,如HLS", | |||
"width": "100%", | |||
"height": "500px", | |||
"autoplay": true, | |||
"isLive": true, | |||
"playsinline": true, | |||
"skipRtsSupportCheck": false, // 对于不在 https://help.aliyun.com/document_detail/397569.html 中的浏览器,可以传 true 跳过检查,强制使用 RTS(有风险,需要自测保证) | |||
/** | |||
* RTS 拉流超时会默认重试 | |||
* 以下两个参数用来控制降级之前的重试策略,比如 3000 毫秒超时,重试一次,如果再拉不到流就降级,那么总共等待 6000 毫秒降级 | |||
**/ | |||
// RTS 多久拉不到流会重试,默认 3000 ms | |||
// rtsLoadDataTimeout: 2000, | |||
// RTS 拉不到流重试的次数,默认 5,此参数建议设为 1,即重试 1 次后降级,可以减少降级等待时间 | |||
liveRetry: 1, | |||
}; | |||
var player = new Aliplayer(options, function () {/* player ready */ }); | |||
// 降级时会触发此事件 | |||
player.on('rtsFallback', function (event) { | |||
console.log('[EVENT]rtsFallback', event.paramData); | |||
// event.paramData.reason 降级的原因 | |||
// event.paramData.fallbackUrl 降级到的地址 | |||
}) | |||
player.on('error', function (event) { | |||
console.log('[EVENT]error', event.paramData); | |||
}) | |||
// 当RTS拉流成功时触发,通过订阅该事件,可以获取到RTS TraceId | |||
player.on('rtsTraceId', function (data) { | |||
console.log('[EVENT]rtsTraceId', data.paramData); | |||
// event.paramData.traceId 拉流的TraceId | |||
// event.paramData.source 当前RTS流的播放地址 | |||
}) | |||
function getUrlParams(url) { | |||
const reg = /(\w+)=([^&]+)/g; | |||
const params = {}; | |||
let match; | |||
while ((match = reg.exec(url)) !== null) { | |||
params[match[1]] = match[2]; | |||
} | |||
return params; | |||
} | |||
</script> | |||
</body> |
@@ -51,6 +51,11 @@ export interface ReqId { | |||
/** id */ | |||
id: number | string; | |||
} | |||
/** id请求参数 */ | |||
export interface ReqPersonId { | |||
/** id */ | |||
personId: number | string; | |||
} | |||
/** id请求参数 */ | |||
export interface ReqstartId { | |||
@@ -68,7 +73,6 @@ export interface ReqstopId { | |||
} | |||
export interface setWarn { | |||
configJson: string; | |||
} | |||
@@ -20,3 +20,4 @@ export * from "./audit"; | |||
export * from "./organization"; | |||
export * from "./auth"; | |||
export * from "./warn"; | |||
export * from "./usermanage"; |
@@ -0,0 +1,15 @@ | |||
/** | |||
* @description | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | |||
*/ | |||
export * from "./personnel"; |
@@ -0,0 +1,67 @@ | |||
/** | |||
* @description 用户管理接口 | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
*/ | |||
import { ReqPage } from "@/api"; | |||
/** | |||
* @Description: 人员管理接口 | |||
* @Author: syy | |||
* @Date: 2023-12-15 15:34:54 | |||
*/ | |||
export namespace SysUserPersonnel { | |||
export interface Page extends ReqPage {} | |||
/** 底库信息 */ | |||
export interface ClassPage { | |||
personSetId?: string | number | undefined; | |||
personSetName?: string | undefined; | |||
id?: string | undefined; | |||
name?: string | undefined; | |||
personId?: string | undefined | number; | |||
} | |||
/** 人脸信息 */ | |||
export interface SysUserAvatar { | |||
/** 人脸 */ | |||
personId?: string | undefined | number; | |||
faceId?: string | number; | |||
faceUrl: string; | |||
uid?: string | number; | |||
} | |||
// 人脸删除 | |||
export interface SysUserFace { | |||
personId: string | undefined; | |||
faceIds: Array<string | number>; | |||
} | |||
/** 用户信息 */ | |||
export interface SysUserPerInfo { | |||
/** 人员id */ | |||
personId?: string; | |||
/** 姓名 */ | |||
name?: string; | |||
/** 年龄 */ | |||
age?: number | string; | |||
/** 性别 */ | |||
gender?: string; | |||
/** 手机 */ | |||
phone?: string; | |||
/** 扩展字段 */ | |||
extData?: string; | |||
/** 人脸 */ | |||
faces: Array<SysUserAvatar>; | |||
/** 分组 */ | |||
personSets: Array<ClassPage>; | |||
personSetId?: number | string; | |||
} | |||
} |
@@ -19,4 +19,4 @@ export * from "./warn"; | |||
export * from "./monitor"; | |||
export * from "./sysconfig"; | |||
export * from "./statistion"; | |||
export * from "./usermanage"; |
@@ -36,5 +36,22 @@ const monitorLIVEApi = { | |||
}, | |||
}; | |||
/** | |||
* @Description: 监控管理按钮权限码 | |||
* @Author: huguodong | |||
* @Date: 2024-02-20 09:51:15 | |||
*/ | |||
const monitorLiveButtonCode = { | |||
/** 新增监控 */ | |||
add: "monitorLiveAdd", | |||
/** 编辑监控 */ | |||
edit: "monitorLiveEdit", | |||
/** 删除监控 */ | |||
delete: "monitorLiveDelete", | |||
/** 批量删除监控 */ | |||
batchDelete: "monitorLiveBatchDelete", | |||
/** 复制监控 */ | |||
copy: "monitorLiveCopy" | |||
}; | |||
export { monitorLIVEApi }; | |||
export { monitorLIVEApi, monitorLiveButtonCode }; |
@@ -0,0 +1,52 @@ | |||
/** | |||
* @description 单页管理接口 | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | |||
*/ | |||
import { moduleRequest } from "@/api/request"; | |||
import { ReqId, SysUserPersonnel } from "@/api/interface"; | |||
const http = moduleRequest("/business/dfieldApi/"); | |||
/** | |||
* @Description: 单页管理 | |||
* @Author: SYY | |||
* @Date: 2023-12-15 15:34:54 | |||
*/ | |||
const userManageClassManageApi = { | |||
/** 查询底库列表 */ | |||
page(params: SysUserPersonnel.ClassPage) { | |||
return http.get("queryAll", params); | |||
}, | |||
/** 删除底库 */ | |||
delete(params: ReqId) { | |||
return http.delete("deleteDfieldD", params); | |||
}, | |||
/** 创建底库 */ | |||
add(params: SysUserPersonnel.ClassPage) { | |||
return http.post("createDfieldA", params); | |||
}, | |||
/** 更新底库 */ | |||
update(params: SysUserPersonnel.ClassPage) { | |||
return http.put("updateDfieldU", params); | |||
} | |||
}; | |||
const userClassButtonCode = { | |||
/** 新增人员 */ | |||
add: "userManageClassManageAdd", | |||
/** 删除人员 */ | |||
edit: "userManageClassManageEdit", | |||
/** 删除人员 */ | |||
delete: "userManageClassManageDelete" | |||
}; | |||
export { userManageClassManageApi, userClassButtonCode }; |
@@ -0,0 +1,16 @@ | |||
/** | |||
* @description | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | |||
*/ | |||
export * from "./personnel"; | |||
export * from "./classManage"; |
@@ -0,0 +1,72 @@ | |||
/** | |||
* @description 单页管理接口 | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | |||
*/ | |||
import { moduleRequest } from "@/api/request"; | |||
import { ReqId, ResPage, ReqPersonId, SysUserPersonnel } from "@/api/interface"; | |||
const http = moduleRequest("/business/personApi/"); | |||
/** | |||
* @Description: 单页管理 | |||
* @Author: SYY | |||
* @Date: 2023-12-15 15:34:54 | |||
*/ | |||
const userManagePersonnelApi = { | |||
/** 获取单页分页 */ | |||
page(params: SysUserPersonnel.Page) { | |||
return http.post("pageQuery", params); | |||
}, | |||
/** 获取单页详情 */ | |||
detail(params: ReqId) { | |||
return http.get("getPersionById", params); | |||
}, | |||
/** 删除人员 */ | |||
delete(params: ReqId) { | |||
return http.delete("deletePersonD", params); | |||
}, | |||
/** 新增人员 */ | |||
add(params: SysUserPersonnel.SysUserPerInfo) { | |||
return http.post("createPersonA", params); | |||
}, | |||
/** 修改人员 */ | |||
update(params: SysUserPersonnel.SysUserPerInfo) { | |||
return http.put("updatePersionU", params); | |||
}, | |||
/** 添加人脸 */ | |||
addFace(params: SysUserPersonnel.SysUserAvatar) { | |||
return http.post("addFaceA", params); | |||
}, | |||
/** 删除人脸 */ | |||
deleteFace(params: SysUserPersonnel.SysUserFace) { | |||
return http.post("deleteFaceD", params); | |||
}, | |||
/**底库绑定 */ | |||
personBindDfie(params: SysUserPersonnel.ClassPage) { | |||
return http.post("personBindDfie", params); | |||
}, | |||
/**底库解绑*/ | |||
personUnBindDfie(params: SysUserPersonnel.ClassPage) { | |||
return http.post("personUnBindDfie", params); | |||
} | |||
}; | |||
const userPerButtonCode = { | |||
/** 新增人员 */ | |||
add: "userManagePersonnelAdd", | |||
/** 删除人员 */ | |||
edit: "userManagePersonnelEdit", | |||
/** 删除人员 */ | |||
delete: "userManagePersonnelDelete" | |||
}; | |||
export { userManagePersonnelApi, userPerButtonCode }; |
@@ -38,6 +38,10 @@ const warnZJRQApi = { | |||
warnGroup(params: ReqId) { | |||
return http.get("getInfo", params); | |||
}, | |||
/** 获取告警分组 */ | |||
warnType(params: ReqId) { | |||
return http.get("getAlarmType", params); | |||
}, | |||
}; | |||
export { warnZJRQApi }; |
@@ -0,0 +1,246 @@ | |||
<!-- | |||
* @Description: 用户选择器 | |||
* @Author: huguodong | |||
* @Date: 2023-12-15 15:40:45 | |||
!--> | |||
<template> | |||
<form-container v-model="visible" :title="`${userName}选择`" form-size="90%" v-bind="$attrs"> | |||
<div class="-mt-15px min-h-350px"> | |||
<el-row :gutter="12" justify="space-between"> | |||
<el-col :span="4"> | |||
<el-tabs v-model="activeName" type="card" stretch class="min-h-350px"> | |||
<el-tab-pane :label="`${orgName}`" class="ml-5px mr-5px" name="org"> | |||
<el-scrollbar max-height="650px"> | |||
<TreeFilter | |||
label="name" | |||
class="filterWidth" | |||
:title="`${orgName}列表`" | |||
:show-all="!biz" | |||
:default-expand-all="false" | |||
:request-api="orgTreeApi" | |||
@change="changeOrgTreeFilter" | |||
/> | |||
</el-scrollbar> | |||
</el-tab-pane> | |||
<!-- <el-tab-pane v-if="positionTreeApi" :label="`${positionName}`" class="ml-5px mr-5px" name="pos"> | |||
<el-scrollbar max-height="650px"> | |||
<TreeFilter | |||
label="name" | |||
class="filterWidth" | |||
:title="`${positionName}列表`" | |||
:show-all="!biz" | |||
:default-expand-all="false" | |||
:request-api="positionTreeApi" | |||
@change="changePositionTreeFilter" | |||
/> | |||
</el-scrollbar> | |||
</el-tab-pane> | |||
<el-tab-pane v-if="roleTreeApi" label="角色" class="ml-5px mr-5px" name="role"> | |||
<el-scrollbar max-height="650px"> | |||
<TreeFilter | |||
label="name" | |||
class="filterWidth" | |||
title="角色列表" | |||
:show-all="!biz" | |||
:default-expand-all="false" | |||
:request-api="roleTreeApi" | |||
@change="changeRoleTreeFilter" | |||
/> | |||
</el-scrollbar> | |||
</el-tab-pane> --> | |||
</el-tabs> | |||
</el-col> | |||
<el-col :span="10"> | |||
<ProTable ref="userTable" :columns="columns" :tool-button="false" :init-param="initParam" :request-api="userSelectorApi"> | |||
<!-- 表格 header 按钮 --> | |||
<template #tableHeader="scope"> | |||
<el-button type="primary" @click="addRecords(userTable!.tableData)">添加当前</el-button> | |||
<el-button type="primary" plain :disabled="!scope.isSelected" @click="addRecords(scope.selectedList)">添加选中</el-button> | |||
</template> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<el-button type="primary" link :icon="Plus" plain @click="addRecords([scope.row])">添加</el-button> | |||
</template> | |||
</ProTable> | |||
</el-col> | |||
<el-col :span="10"> | |||
<ProTable ref="chooseTable" :columns="columns" :tool-button="true" :data="chooseDataTmp" @search="searchRecords" @reset="resetRecords"> | |||
<!-- 表格 header 按钮 --> | |||
<template #tableHeader="scope"> | |||
<el-button type="danger" @click="delRecords(chooseTable!.tableData)">删除当前</el-button> | |||
<el-button type="danger" plain :disabled="!scope.isSelected" @click="delRecords(scope.selectedList)">删除选中</el-button> | |||
</template> | |||
<template #toolButton> | |||
<span>已选择:{{ chooseData.length }}人</span> | |||
<span v-if="maxCount">,最多选择:{{ maxCount }}人</span> | |||
</template> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<el-button type="danger" link :icon="Delete" plain @click="delRecords([scope.row])">删除</el-button> | |||
</template> | |||
</ProTable> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
<template #footer> | |||
<div class="mt-20px"> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button type="primary" @click="handleOk"> 确定 </el-button> | |||
</div> | |||
</template> | |||
</form-container> | |||
</template> | |||
<script setup lang="ts" name="UserSelector"> | |||
import { SysUser, SysPosition, SysRole } from "@/api"; | |||
import { UserSelectProps, UserSelectTableInitParams } from "./interface"; | |||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||
import { ElMessage } from "element-plus"; | |||
import { Plus, Delete } from "@element-plus/icons-vue"; | |||
const activeName = ref("org"); | |||
const emit = defineEmits({ successful: null }); // 自定义事件 | |||
// const handleClick = (tab: TabsPaneContext, event: Event) => { | |||
// console.log(tab, event); | |||
// }; | |||
const visible = ref(false); //是否显示 | |||
// 定义组件props | |||
const props = withDefaults(defineProps<UserSelectProps>(), { | |||
multiple: false, | |||
biz: false | |||
}); | |||
// 根据是否业务显示不同名称 | |||
const userName = props.biz ? "人员" : "用户"; | |||
const positionName = props.biz ? "职位" : "岗位"; | |||
const orgName = props.biz ? "机构" : "班级"; | |||
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据) | |||
const initParam = reactive<UserSelectTableInitParams>({}); | |||
// 获取 userTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const userTable = ref<ProTableInstance>(); | |||
// 获取 chooseTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const chooseTable = ref<ProTableInstance>(); | |||
// 表格配置项 | |||
const columns: ColumnProps<SysUser.SysUserInfo>[] = [ | |||
{ type: "selection", fixed: "left", width: 50 }, | |||
{ prop: "operation", label: "操作", width: 80, fixed: "left" }, | |||
{ prop: "account", label: "账号", search: { el: "input", span: 2 } }, | |||
{ prop: "name", label: "姓名" } | |||
]; | |||
/** 显示选择器 */ | |||
function showSelector(data: SysUser.SysUserInfo[] = []) { | |||
visible.value = true; | |||
chooseDataTmp.value = data; | |||
chooseData.value = data; | |||
} | |||
/** 关闭选择器 */ | |||
function onClose() { | |||
visible.value = false; | |||
chooseDataTmp.value = []; | |||
chooseData.value = []; | |||
} | |||
/** 提交数据 */ | |||
function handleOk() { | |||
visible.value = false; | |||
emit("successful", chooseData.value); | |||
} | |||
/** 部门切换 */ | |||
function changeOrgTreeFilter(val: number | string) { | |||
userTable.value!.pageable.pageNum = 1; | |||
if (val != "") { | |||
// 如果传入的val不为空 | |||
initParam.orgId = val; | |||
} else { | |||
initParam.orgId = null; | |||
} | |||
} | |||
/** 职位切换 */ | |||
function changePositionTreeFilter(val: number | string, data: SysPosition.SysPositionTree) { | |||
userTable.value!.pageable.pageNum = 1; | |||
if (data.isPosition) { | |||
// 如果是职位 | |||
initParam.positionId = val; | |||
} else { | |||
initParam.positionId = null; | |||
} | |||
} | |||
/** 角色切换 */ | |||
function changeRoleTreeFilter(val: number | string, data: SysRole.SysRoleTree) { | |||
userTable.value!.pageable.pageNum = 1; | |||
if (data.isRole) { | |||
// 如果是角色 | |||
initParam.roleId = val; | |||
} else { | |||
// 置空 | |||
initParam.roleId = null; | |||
} | |||
} | |||
const chooseData = ref<SysUser.SysUserInfo[]>([]); //选择的数据 | |||
const chooseDataTmp = ref<SysUser.SysUserInfo[]>([]); //临时选择的数据 | |||
/** 添加记录 */ | |||
function addRecords(records: any[]) { | |||
//如果不是多选,判断是否已经添加了 | |||
if (!props.multiple) { | |||
if (chooseData.value.length > 0 || records.length > 1) { | |||
ElMessage.warning("只可选择一条"); | |||
return; | |||
} | |||
chooseData.value = records; | |||
chooseDataTmp.value = chooseData.value; | |||
} else { | |||
//如果是多选,先判断已添加列表是否有重复,有则过滤掉,没有则直接添加 | |||
records = records.filter(item => !chooseData.value.find(it => it.id == item.id)); | |||
if (props.maxCount && props.maxCount < records.length + chooseData.value.length) { | |||
ElMessage.warning("最多选择" + props.maxCount + "条"); | |||
return; | |||
} | |||
chooseData.value = chooseData.value.concat(records); //添加到已选中列表 | |||
chooseDataTmp.value = chooseData.value; | |||
} | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
/** 删除记录 */ | |||
function delRecords(records: any[]) { | |||
chooseData.value = chooseData.value.filter(item => !records.includes(item)); //过滤掉已选中的 | |||
chooseDataTmp.value = chooseData.value; | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
/** 搜索记录 */ | |||
function searchRecords() { | |||
if (chooseTable.value?.searchParam?.account) { | |||
//搜索account符合的记录 | |||
chooseDataTmp.value = chooseDataTmp.value.filter(item => item.account.includes(chooseTable.value?.searchParam.account)); //过滤掉已选中的 | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
} | |||
/** 重置记录 */ | |||
function resetRecords() { | |||
chooseDataTmp.value = chooseData.value; | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
// 暴露方法 | |||
defineExpose({ showSelector }); | |||
</script> | |||
<style lang="scss" scoped> | |||
.filterWidth { | |||
width: 100%; | |||
} | |||
:deep(.el-tabs--border-card > .el-tabs__content) { | |||
padding: 5px; | |||
} | |||
:deep(.table-main) { | |||
height: 90%; | |||
} | |||
</style> |
@@ -0,0 +1,48 @@ | |||
/** | |||
* @description 用户选择器接口 | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | |||
*/ | |||
import UserSelector from "./index.vue"; | |||
/** 用户选择器属性 */ | |||
export interface UserSelectProps { | |||
/** 组织树api */ | |||
orgTreeApi: (data?: any) => Promise<any>; | |||
/** 职位选择api */ | |||
positionTreeApi?: (data?: any) => Promise<any>; | |||
/** 角色选择api */ | |||
roleTreeApi?: (data?: any) => Promise<any>; | |||
/** 用户选择api */ | |||
userSelectorApi: (data?: any) => Promise<any>; | |||
/** 是否多选 */ | |||
multiple?: boolean; | |||
/** 最大用户数 */ | |||
maxCount?: number; | |||
/** 是否是业务 */ | |||
biz?: boolean; | |||
} | |||
/** 用户选择器表格初始化参数 */ | |||
export interface UserSelectTableInitParams { | |||
/** 组织ID */ | |||
orgId?: number | string | null; | |||
/** 职位ID */ | |||
positionId?: number | string | null; | |||
/** 角色ID */ | |||
roleId?: number | string | null; | |||
} | |||
/** | |||
* @description 用户选择器实例类型 | |||
*/ | |||
export type UserSelectorInstance = Omit<InstanceType<typeof UserSelector>, keyof ComponentPublicInstance | keyof UserSelectProps>; |
@@ -1,9 +1,11 @@ | |||
<template> | |||
<div class="card filter"> | |||
<div class="card filter" :style="{ width: width }"> | |||
<h4 v-if="title" class="title sle"> | |||
{{ title }} | |||
</h4> | |||
<slot name="header"></slot> | |||
<el-input v-model="filterText" placeholder="输入关键字进行过滤" clearable /> | |||
<el-scrollbar :style="{ height: title ? `calc(100% - 95px)` : `calc(100% - 56px)` }"> | |||
<el-tree | |||
ref="treeRef" | |||
@@ -24,8 +26,8 @@ | |||
@check="handleCheckChange" | |||
> | |||
<template #default="scope"> | |||
<span class="el-tree-node__label"> | |||
<slot :row="scope"> | |||
<span class="el-tree-node__label" style="width: 100%"> | |||
<slot :row="scope" name="label"> | |||
{{ scope.node.label }} | |||
</slot> | |||
</span> | |||
@@ -53,6 +55,7 @@ interface TreeFilterProps { | |||
checkStrictly?: boolean; // 是否开启子节点和父节点不关联 ==> 非必传,默认为 false | |||
topName?: string; // 顶级分类名称 ==> 非必传,默认为 “全部” | |||
showAll?: boolean; // 是否显示全部选项 ==> 非必传,默认为 true | |||
width: string; | |||
} | |||
const props = withDefaults(defineProps<TreeFilterProps>(), { | |||
id: "id", | |||
@@ -1,7 +1,7 @@ | |||
<template> | |||
<div class="footer flx-center"> | |||
<!-- <a :href="props.sysCopyrightUrl" target="_blank"> {{ props.sysCopyright }} </a> | |||
<a v-for="link in props.footerLinks" :key="link.name" :href="link.url" target="_blank" class="mx-1"> | {{ link.name }}</a> --> | |||
<a :href="props.sysCopyrightUrl" target="_blank"> {{ props.sysCopyright }} </a> | |||
<a v-for="link in props.footerLinks" :key="link.name" :href="link.url" target="_blank" class="mx-1"> | {{ link.name }}</a> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
@@ -249,13 +249,25 @@ | |||
/* el-dialog */ | |||
.el-dialog { | |||
padding: 0 !important; | |||
.el-dialog__header { | |||
padding: 15px 20px; | |||
padding: 16px; | |||
margin: 0; | |||
border-bottom: 1px solid var(--el-border-color-lighter); | |||
.el-dialog__title { | |||
font-size: 17px; | |||
} | |||
.el-dialog__headerbtn { | |||
top: 6px; | |||
} | |||
} | |||
.el-dialog__body { | |||
padding: 20px; | |||
} | |||
.el-dialog__footer { | |||
box-sizing: border-box; | |||
padding: 15px 16px; | |||
border-top: 1px solid var(--el-border-color-lighter); | |||
} | |||
} | |||
@@ -1,12 +1,60 @@ | |||
.home { | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
width: 100%; | |||
height: 100%; | |||
.home-bg { | |||
width: 70%; | |||
max-width: 1200px; | |||
margin-bottom: 20px; | |||
margin-bottom: 15px; | |||
height: 390px; | |||
// height: 424px; | |||
.home-bg-title { | |||
display: flex; | |||
height: 50px; | |||
align-items: center; | |||
div:first-child { | |||
height: 15px; | |||
width: 4px; | |||
background: #3a84ff; | |||
margin-right: 10px; | |||
} | |||
div:nth-child(2) { | |||
font-size: 20px; | |||
font-weight: 600; | |||
margin-left: 10px; | |||
} | |||
} | |||
.home-bg-content { | |||
height: calc(100% - 50px); | |||
.home-bg-content-item { | |||
margin-left: 15px; | |||
margin-top: 60px; | |||
display: flex; | |||
align-items: center; | |||
.home-bg-content-item-icon { | |||
margin-right: 15px; | |||
width: 70px; | |||
height: 70px; | |||
img { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
.home-bg-content-item-content { | |||
.home-bg-content-item-title { | |||
} | |||
.home-bg-content-item-value { | |||
margin-top: 10px; | |||
font-size: 20px; | |||
font-weight: 600; | |||
// color: #3a84ff; | |||
} | |||
} | |||
} | |||
} | |||
.handleBox ::v-deep(.card) { | |||
padding:0; | |||
} | |||
} | |||
.topCard { | |||
height: 355px; | |||
} | |||
} |
@@ -1,10 +1,387 @@ | |||
<template> | |||
<div class="home card"> | |||
<img class="home-bg" src="@/assets/images/welcome.png" alt="welcome" /> | |||
<div class="home"> | |||
<el-row :gutter="20"> | |||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8"> | |||
<div class="home-bg card"> | |||
<div class="home-bg-title"> | |||
<div></div> | |||
<div>基础数据</div> | |||
</div> | |||
<div class="home-bg-content"> | |||
<el-row :gutter="20"> | |||
<el-col :span="12" | |||
><div class="home-bg-content-item"> | |||
<div class="home-bg-content-item-icon"> | |||
<img src="@/assets/images/home/carame.png" alt="" /> | |||
</div> | |||
<div class="home-bg-content-item-content"> | |||
<div class="home-bg-content-item-title">摄像头数量</div> | |||
<div class="home-bg-content-item-value">3</div> | |||
</div> | |||
</div></el-col | |||
> | |||
<el-col :span="12" | |||
><div class="home-bg-content-item"> | |||
<div class="home-bg-content-item-icon"> | |||
<img src="@/assets/images/home/warn.png" alt="" /> | |||
</div> | |||
<div class="home-bg-content-item-content"> | |||
<div class="home-bg-content-item-title">告警信息总量</div> | |||
<div class="home-bg-content-item-value">197218</div> | |||
</div> | |||
</div></el-col | |||
> | |||
<el-col :span="12" | |||
><div class="home-bg-content-item"> | |||
<div class="home-bg-content-item-icon"> | |||
<img src="@/assets/images/home/handle.png" alt="" /> | |||
</div> | |||
<div class="home-bg-content-item-content"> | |||
<div class="home-bg-content-item-title">处理意见提交</div> | |||
<div class="home-bg-content-item-value">40</div> | |||
</div> | |||
</div></el-col | |||
> | |||
<el-col :span="12" | |||
><div class="home-bg-content-item"> | |||
<div class="home-bg-content-item-icon"> | |||
<img src="@/assets/images/home/participation.png" alt="" /> | |||
</div> | |||
<div class="home-bg-content-item-content"> | |||
<div class="home-bg-content-item-title">参与安防教师</div> | |||
<div class="home-bg-content-item-value">22</div> | |||
</div> | |||
</div></el-col | |||
> | |||
</el-row> | |||
</div> | |||
</div> | |||
</el-col> | |||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8"> | |||
<div class="home-bg card"> | |||
<div class="home-bg-title"> | |||
<div></div> | |||
<div>今日告警情况</div> | |||
</div> | |||
<div class="home-bg-content"> | |||
<div ref="chart1" style="width: 100%; height: 100%"></div> | |||
</div> | |||
</div> | |||
</el-col> | |||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8"> | |||
<div class="home-bg card"> | |||
<div class="home-bg-title"> | |||
<div></div> | |||
<div>今日处理情况</div> | |||
</div> | |||
<div class="home-bg-content"> | |||
<div ref="chart2" style="width: 100%; height: 100%"></div> | |||
</div> | |||
</div> | |||
</el-col> | |||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="8"> | |||
<div class="home-bg card"> | |||
<div class="home-bg-title"> | |||
<div></div> | |||
<div>高危预警信息统计</div> | |||
</div> | |||
<div class="home-bg-content handleBox"> | |||
<ProTable ref="proTable" title="视频列表" :toolButton="false" :pagination="false" :columns="columns" :data="tableData"> </ProTable> | |||
</div> | |||
</div> | |||
</el-col> | |||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="16"> | |||
<div class="home-bg card"> | |||
<div class="home-bg-title"> | |||
<div></div> | |||
<div>统计分析</div> | |||
</div> | |||
<div class="home-bg-content handleBox"> | |||
<div ref="chart3" style="width: 100%; height: 100%"></div> | |||
</div> | |||
</div> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
</template> | |||
<script setup lang="ts" name="home"></script> | |||
<script setup lang="ts" name="home"> | |||
import { ref, watch, provide, onMounted, unref, computed, reactive } from "vue"; | |||
import { ElMessage } from "element-plus"; | |||
import { monitorLIVEApi } from "@/api"; | |||
import { ZJRQ } from "@/api/interface"; | |||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||
import { useDictStore } from "@/stores/modules"; | |||
import { statistionApi } from "@/api"; | |||
import * as echarts from "echarts"; | |||
const value = ref(true); | |||
const chart1 = ref(null); | |||
const chart2 = ref(null); | |||
const chart3 = ref(null); | |||
onMounted(() => { | |||
getDataChart(); | |||
getWeekData(); | |||
}); | |||
function getDataChart() { | |||
setTimeout(async () => { | |||
await statistionApi.warnstatistion({}).then(res => { | |||
let { code, data } = res; | |||
if (code == 200) { | |||
let chartData1 = data.alarm.map(item => { | |||
return { | |||
value: item.count, | |||
name: item.name | |||
}; | |||
}); | |||
let chartData2 = data.hand.map(item => { | |||
return { | |||
value: item.count, | |||
name: item.name | |||
}; | |||
}); | |||
// let chartData = [ | |||
// { | |||
// name: "1", | |||
// value: 100 | |||
// }, | |||
// { | |||
// name: "2", | |||
// value: 200 | |||
// } | |||
// ]; | |||
getCharts1(chartData1); | |||
getCharts2(chartData2); | |||
} | |||
}); | |||
}); | |||
} | |||
function getCharts1(data) { | |||
const chart = echarts.init(chart1.value); | |||
const option = { | |||
tooltip: { | |||
trigger: "item" | |||
}, | |||
// legend: { | |||
// top: "5%", | |||
// left: "center" | |||
// }, | |||
legend: { | |||
orient: "vertical", | |||
left: "right", | |||
top: "middle" | |||
}, | |||
series: [ | |||
{ | |||
name: "今日告警情况", | |||
type: "pie", | |||
radius: ["60%", "90%"], | |||
avoidLabelOverlap: false, | |||
itemStyle: { | |||
borderRadius: 10, | |||
borderColor: "#fff", | |||
borderWidth: 2 | |||
}, | |||
label: { | |||
show: false, | |||
position: "center" | |||
}, | |||
emphasis: { | |||
label: { | |||
show: true, | |||
fontSize: 25, | |||
fontWeight: "bold" | |||
} | |||
}, | |||
labelLine: { | |||
show: false | |||
}, | |||
data | |||
} | |||
] | |||
}; | |||
chart.setOption(option); | |||
window.addEventListener("resize", function () { | |||
chart.resize(); | |||
}); | |||
} | |||
function getCharts2(data) { | |||
const chartstation = echarts.init(chart2.value); | |||
const option = { | |||
tooltip: { | |||
trigger: "item" | |||
}, | |||
// legend: { | |||
// top: "5%", | |||
// left: "center" | |||
// }, | |||
legend: { | |||
orient: "vertical", | |||
left: "right", | |||
top: "middle" | |||
}, | |||
series: [ | |||
{ | |||
name: "今日处理情况", | |||
type: "pie", | |||
radius: ["60%", "90%"], | |||
avoidLabelOverlap: false, | |||
itemStyle: { | |||
borderRadius: 10, | |||
borderColor: "#fff", | |||
borderWidth: 2 | |||
}, | |||
label: { | |||
show: false, | |||
position: "center" | |||
}, | |||
emphasis: { | |||
label: { | |||
show: true, | |||
fontSize: 25, | |||
fontWeight: "bold" | |||
} | |||
}, | |||
labelLine: { | |||
show: false | |||
}, | |||
data | |||
} | |||
] | |||
}; | |||
chartstation.setOption(option); | |||
window.addEventListener("resize", function () { | |||
chartstation.resize(); | |||
}); | |||
} | |||
function getWeekData() { | |||
setTimeout(async () => { | |||
await statistionApi.weekstatistion({}).then(res => { | |||
let { code, data } = res; | |||
if (code == 200) { | |||
// let chartData = data; | |||
let time = data.dataX; | |||
let chartData = data.dataY.map(item => { | |||
return { | |||
data: item.data, | |||
name: item.name, | |||
type: "bar", | |||
barWidth: "12px", // 设置柱子粗细 | |||
itemStyle: { | |||
normal: { | |||
barBorderRadius: [30, 30, 0, 0] | |||
} | |||
} | |||
}; | |||
}); | |||
getCharts3(time, chartData); | |||
} | |||
}); | |||
}); | |||
} | |||
function getCharts3(time, data) { | |||
const chartstation3 = echarts.init(chart3.value); | |||
const option = { | |||
tooltip: { | |||
trigger: "axis", | |||
axisPointer: { | |||
type: "shadow" | |||
} | |||
}, | |||
legend: {}, | |||
grid: { | |||
left: "3%", | |||
right: "4%", | |||
bottom: "3%", | |||
containLabel: true | |||
}, | |||
yAxis: { | |||
type: "value", | |||
boundaryGap: [0, 0.01] | |||
}, | |||
xAxis: { | |||
type: "category", | |||
data: time | |||
}, | |||
series: data | |||
}; | |||
chartstation3.setOption(option); | |||
window.addEventListener("resize", function () { | |||
chartstation3.resize(); | |||
}); | |||
} | |||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const proTable = ref<ProTableInstance>(); | |||
const dictStore = useDictStore(); | |||
// 表格配置项 | |||
const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||
{ | |||
prop: "type", | |||
label: "数据类型" | |||
// render: () => { | |||
// return "楼道"; | |||
// } | |||
}, | |||
{ | |||
prop: "warntotal", | |||
label: "告警总量" | |||
// render: () => { | |||
// return "楼道"; | |||
// } | |||
}, | |||
{ | |||
prop: "lowrisk", | |||
label: "低危信息 " | |||
}, | |||
{ | |||
prop: "highrisk", | |||
label: "高危信息" | |||
} | |||
]; | |||
const tableData = ref([ | |||
{ | |||
type: "今日", | |||
warntotal: "828", | |||
lowrisk: "675", | |||
highrisk: "153" | |||
}, | |||
{ | |||
type: "本周", | |||
warntotal: "828", | |||
lowrisk: "675", | |||
highrisk: "153" | |||
}, | |||
{ | |||
type: "本月", | |||
warntotal: "19813", | |||
lowrisk: "17671", | |||
highrisk: "2129" | |||
}, | |||
{ | |||
type: "上月", | |||
warntotal: "21789", | |||
lowrisk: "0", | |||
highrisk: "0" | |||
}, | |||
{ | |||
type: "环比", | |||
warntotal: "-9.07%", | |||
lowrisk: "0%", | |||
highrisk: "0%" | |||
} | |||
]); | |||
const tableLoading = ref(false); | |||
</script> | |||
<style scoped lang="scss"> | |||
@import "./index.scss"; | |||
@@ -0,0 +1,118 @@ | |||
<!-- | |||
* @Description: 表单 | |||
* @Author: huguodong | |||
* @Date: 2023-12-15 15:45:28 | |||
!--> | |||
<template> | |||
<div> | |||
<form-container v-model="visible" :title="`${orgProps.opt}监控`" form-size="600px"> | |||
<el-form | |||
ref="liveFormRef" | |||
:rules="rules" | |||
:disabled="orgProps.disabled" | |||
:model="orgProps.record" | |||
:hide-required-asterisk="orgProps.disabled" | |||
label-width="auto" | |||
label-suffix=" :" | |||
> | |||
<s-form-item label="摄像头名称" prop="name"> | |||
<s-input v-model="orgProps.record.name"></s-input> | |||
</s-form-item> | |||
<s-form-item label="所属学校" prop="parentId"> | |||
<org-selector v-model:org-value="orgProps.record.parentId" :org-tree-api="bizOrgApi.tree" :show-all="false" /> | |||
</s-form-item> | |||
<s-form-item label="设备IP" prop="codeip"> | |||
<s-input v-model="orgProps.record.codeip" clearable></s-input> | |||
</s-form-item> | |||
<s-form-item label="分辨率" prop="status"> | |||
<s-input v-model="orgProps.record.status" clearable></s-input> | |||
</s-form-item> | |||
</el-form> | |||
<template #footer> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button v-show="!orgProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button> | |||
</template> | |||
</form-container> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
import { SysOrg, SysUser, bizOrgApi, bizPositionApi, sysRoleApi, bizUserApi } from "@/api"; | |||
import { FormOptEnum, SysDictEnum } from "@/enums"; | |||
import { required } from "@/utils/formRules"; | |||
import { FormInstance } from "element-plus"; | |||
import { useDictStore } from "@/stores/modules"; | |||
const visible = ref(false); //是否显示表单 | |||
const dictStore = useDictStore(); //字典仓库 | |||
// 通用状态选项 | |||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS); | |||
// 表单参数 | |||
const orgProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({ | |||
opt: FormOptEnum.ADD, | |||
record: {}, | |||
disabled: false | |||
}); | |||
// 表单验证规则 | |||
const rules = reactive({ | |||
name: [required("请输入摄像头名称")], | |||
parentId: [required("请选择所属学校")], | |||
codeip: [required("请选择设备IP")], | |||
status: [required("请输入分辨率")] | |||
}); | |||
/** | |||
* 打开表单 | |||
* @param props 表单参数 | |||
*/ | |||
function onOpen(props: FormProps.Base<SysOrg.SysOrgInfo>) { | |||
Object.assign(orgProps, props); //合并参数 | |||
if (props.opt == FormOptEnum.ADD) { | |||
//如果是新增,设置默认值 | |||
orgProps.record.sortCode = 99; | |||
// orgProps.record.status = statusOptions[0].value; | |||
} | |||
visible.value = true; //显示表单 | |||
if (props.record.id) { | |||
//如果传了id,就去请求api获取record | |||
bizOrgApi.detail({ id: props.record.id }).then(res => { | |||
orgProps.record = res.data; | |||
}); | |||
} | |||
} | |||
// 提交数据(新增/编辑) | |||
const liveFormRef = ref<FormInstance>(); | |||
/** 提交表单 */ | |||
async function handleSubmit() { | |||
liveFormRef.value?.validate(async valid => { | |||
if (!valid) return; //表单验证失败 | |||
console.log(orgProps); | |||
return; | |||
//提交表单 | |||
await bizOrgApi | |||
.submitForm(orgProps.record, orgProps.record.id != undefined) | |||
.then(() => { | |||
orgProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
}); | |||
} | |||
/** 关闭表单*/ | |||
function onClose() { | |||
visible.value = false; | |||
} | |||
// 暴露给父组件的方法 | |||
defineExpose({ | |||
onOpen | |||
}); | |||
</script> | |||
<style lang="scss" scoped></style> |
@@ -0,0 +1,106 @@ | |||
<!-- | |||
* @Description: 表单 | |||
* @Author: huguodong | |||
* @Date: 2023-12-15 15:45:28 | |||
!--> | |||
<template> | |||
<div> | |||
<form-container v-model="visible" title="选择分组" form-size="600px"> | |||
<el-form | |||
ref="liveFormRef" | |||
:rules="rules" | |||
:disabled="orgProps.disabled" | |||
:model="orgProps.record" | |||
:hide-required-asterisk="orgProps.disabled" | |||
label-width="auto" | |||
label-suffix=" :" | |||
> | |||
<s-form-item label="所属学校" prop="parentId"> | |||
<org-selector v-model:org-value="orgProps.record.parentId" :org-tree-api="bizOrgApi.tree" :show-all="false" /> | |||
</s-form-item> | |||
</el-form> | |||
<template #footer> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button v-show="!orgProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button> | |||
</template> | |||
</form-container> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
import { SysOrg, SysUser, bizOrgApi, bizPositionApi, sysRoleApi, bizUserApi } from "@/api"; | |||
import { FormOptEnum, SysDictEnum } from "@/enums"; | |||
import { required } from "@/utils/formRules"; | |||
import { FormInstance } from "element-plus"; | |||
import { useDictStore } from "@/stores/modules"; | |||
const visible = ref(false); //是否显示表单 | |||
const dictStore = useDictStore(); //字典仓库 | |||
// 通用状态选项 | |||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS); | |||
// 表单参数 | |||
const orgProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({ | |||
opt: FormOptEnum.ADD, | |||
record: {}, | |||
disabled: false | |||
}); | |||
// 表单验证规则 | |||
const rules = reactive({ | |||
parentId: [required("请选择所属学校")] | |||
}); | |||
/** | |||
* 打开表单 | |||
* @param props 表单参数 | |||
*/ | |||
function omMove(props: FormProps.Base<SysOrg.SysOrgInfo>) { | |||
Object.assign(orgProps, props); //合并参数 | |||
if (props.opt == FormOptEnum.ADD) { | |||
//如果是新增,设置默认值 | |||
orgProps.record.sortCode = 99; | |||
// orgProps.record.status = statusOptions[0].value; | |||
} | |||
visible.value = true; //显示表单 | |||
if (props.record.id) { | |||
//如果传了id,就去请求api获取record | |||
bizOrgApi.detail({ id: props.record.id }).then(res => { | |||
orgProps.record = res.data; | |||
}); | |||
} | |||
} | |||
// 提交数据(新增/编辑) | |||
const liveFormRef = ref<FormInstance>(); | |||
/** 提交表单 */ | |||
async function handleSubmit() { | |||
liveFormRef.value?.validate(async valid => { | |||
if (!valid) return; //表单验证失败 | |||
console.log(orgProps); | |||
return; | |||
//提交表单 | |||
await bizOrgApi | |||
.submitForm(orgProps.record, orgProps.record.id != undefined) | |||
.then(() => { | |||
orgProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
}); | |||
} | |||
/** 关闭表单*/ | |||
function onClose() { | |||
visible.value = false; | |||
} | |||
// 暴露给父组件的方法 | |||
defineExpose({ | |||
omMove | |||
}); | |||
</script> | |||
<style lang="scss" scoped></style> |
@@ -0,0 +1,127 @@ | |||
<!-- | |||
* @Description: 表单 | |||
* @Author: huguodong | |||
* @Date: 2023-12-15 15:45:28 | |||
!--> | |||
<template> | |||
<div> | |||
<form-container v-model="visible" title="人员选择" form-size="600px"> | |||
<el-form | |||
ref="userFormRef" | |||
:rules="rules" | |||
:disabled="liveUserProps.disabled" | |||
:model="liveUserProps.record" | |||
:hide-required-asterisk="liveUserProps.disabled" | |||
label-width="auto" | |||
label-suffix=" :" | |||
> | |||
<s-form-item label="指定分组推送人" prop="directorId"> | |||
<el-button link type="primary" @click="showSelector">选择</el-button> | |||
<el-tag v-if="liveUserProps.record.directorId" class="ml-3px" type="warning" closable @close="removeDirector">{{ | |||
liveUserProps.record.directorInfo?.name | |||
}}</el-tag> | |||
</s-form-item> | |||
</el-form> | |||
<template #footer> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button v-show="!liveUserProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button> | |||
</template> | |||
</form-container> | |||
<user-selector ref="userSelectorRef" :org-tree-api="sysOrgApi.tree" :user-selector-api="sysUserApi.selector" @successful="handleChooseUser" /> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
import { SysOrg, SysUser, sysOrgApi, sysPositionApi, sysRoleApi, sysUserApi } from "@/api"; | |||
import { FormOptEnum, SysDictEnum } from "@/enums"; | |||
import { required } from "@/utils/formRules"; | |||
import { FormInstance } from "element-plus"; | |||
import { useDictStore } from "@/stores/modules"; | |||
import { UserSelectorInstance } from "@/components/Selectors/UserSelector/interface"; | |||
const visible = ref(false); //是否显示表单 | |||
const dictStore = useDictStore(); //字典仓库 | |||
// 表单参数 | |||
const liveUserProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({ | |||
opt: FormOptEnum.ADD, | |||
record: {}, | |||
disabled: false | |||
}); | |||
// 表单验证规则 | |||
const rules = reactive({ | |||
directorId: [required("请选择指定分组推送人")] | |||
}); | |||
/** | |||
* 打开表单 | |||
* @param props 表单参数 | |||
*/ | |||
function onOpen(props: FormProps.Base<SysOrg.SysOrgInfo>) { | |||
Object.assign(liveUserProps, props); //合并参数 | |||
if (props.opt == FormOptEnum.ADD) { | |||
//如果是新增,设置默认值 | |||
} | |||
visible.value = true; //显示表单 | |||
// if (props.record.id) { | |||
// //如果传了id,就去请求api获取record | |||
// sysOrgApi.detail({ id: props.record.id }).then(res => { | |||
// liveUserProps.record = res.data; | |||
// }); | |||
// } | |||
} | |||
// 提交数据(新增/编辑) | |||
const userFormRef = ref<FormInstance>(); | |||
/** 提交表单 */ | |||
async function handleSubmit() { | |||
userFormRef.value?.validate(async valid => { | |||
if (!valid) return; //表单验证失败 | |||
return; | |||
//提交表单 | |||
await sysOrgApi | |||
.submitForm(liveUserProps.record, liveUserProps.record.id != undefined) | |||
.then(() => { | |||
liveUserProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
}); | |||
} | |||
/** 关闭表单*/ | |||
function onClose() { | |||
visible.value = false; | |||
} | |||
const userSelectorRef = ref<UserSelectorInstance>(); //用户选择器引用 | |||
/** 显示用户选择器 */ | |||
function showSelector() { | |||
//将liveUserProps.record.directorInfo转为 SysUser.SysUserInfo[]类型 | |||
const directorInfo = liveUserProps.record.directorInfo ? [liveUserProps.record.directorInfo] : []; | |||
userSelectorRef.value?.showSelector(directorInfo); | |||
} | |||
/** 选择用户 */ | |||
function handleChooseUser(data: SysUser.SysUserInfo[]) { | |||
// 选择用户后,将用户id赋值给liveUserProps.record.directorId | |||
if (data.length > 0) { | |||
liveUserProps.record.directorId = data[0].id; | |||
liveUserProps.record.directorInfo = data[0]; | |||
} | |||
} | |||
/** 移除主管 */ | |||
function removeDirector() { | |||
liveUserProps.record.directorId = null; | |||
liveUserProps.record.directorInfo = null; | |||
} | |||
// 暴露给父组件的方法 | |||
defineExpose({ | |||
onOpen | |||
}); | |||
</script> | |||
<style lang="scss" scoped></style> |
@@ -0,0 +1,78 @@ | |||
ul,li { | |||
list-style: none; | |||
padding: 0; | |||
margin: 0; | |||
} | |||
.treeBox { | |||
box-sizing: border-box; | |||
width: 280px; | |||
height: 100%; | |||
padding: 14px; | |||
margin-right: 10px; | |||
flex-shrink: 1; | |||
.title { | |||
margin: 0 0 15px; | |||
font-size: 18px; | |||
font-weight: bold; | |||
color: var(--el-color-info-dark-2); | |||
letter-spacing: 0.5px; | |||
} | |||
.btn { | |||
} | |||
.treeContent { | |||
padding: 10px 0; | |||
// height: calc(100% - 100px); | |||
// overflow: auto; | |||
.el-tree-node__content { | |||
height: 33px; | |||
} | |||
.custom-tree-node { | |||
flex: 1; | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
font-size: 14px; | |||
padding-right: 8px; | |||
.node-label { | |||
width: 100px; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
font-size: 16px; | |||
} | |||
} | |||
:deep(.el-tree-node__content) { | |||
height: 50px; | |||
} | |||
} | |||
.el-input { | |||
margin: 0 0 15px; | |||
} | |||
.el-scrollbar { | |||
:deep(.el-tree) { | |||
height: 80%; | |||
overflow: auto; | |||
.el-tree-node__content { | |||
height: 33px; | |||
} | |||
} | |||
:deep(.el-tree--highlight-current) { | |||
.el-tree-node.is-current > .el-tree-node__content { | |||
background-color: var(--el-color-primary); | |||
.el-tree-node__label, | |||
.el-tree-node__expand-icon { | |||
color: white; | |||
} | |||
.is-leaf { | |||
color: transparent; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.table-box { | |||
width: calc(100% - 280px); | |||
} |
@@ -4,66 +4,236 @@ | |||
* @Date: 2023-12-15 15:44:05 | |||
!--> | |||
<template> | |||
<div class="table-box"> | |||
<ProTable ref="proTable" title="视频列表" :columns="columns" :request-api="monitorLIVEApi.page"> | |||
<!-- 表格 header 按钮 --> | |||
<div class="main-box"> | |||
<div class="card treeBox"> | |||
<p class="title">摄像头分组管理</p> | |||
<div class="btn"> | |||
<el-button @click="append('add', {})" type="primary">添加分组</el-button> | |||
</div> | |||
<!-- <div class="treeContent"> | |||
<ul class="treeList" v-for="(item, i) in treeData" :key="i"> | |||
<li class="treeItem"> | |||
<div class="treeLabel">{{ item.label }}</div> | |||
<div class="treeBtn"> | |||
<el-icon><Edit /></el-icon> | |||
<el-icon><Delete /></el-icon> | |||
</div> | |||
</li> | |||
</ul> | |||
</div> --> | |||
<div class="treeContent"> | |||
<el-tree | |||
style="max-width: 600px" | |||
:data="treeData" | |||
node-key="id" | |||
default-expand-all | |||
:expand-on-click-node="false" | |||
:check-on-click-node="true" | |||
:highlight-current="true" | |||
@node-click="handleNodeClick" | |||
> | |||
<template #default="{ node, data }"> | |||
<span class="custom-tree-node"> | |||
<span class="node-label" :title="node.label">{{ node.label }}</span> | |||
<span v-if="data.id && data.id != '-1'"> | |||
<el-icon size="16" @click.stop="append('edit', data)"><Edit /></el-icon> | |||
<el-icon size="16" @click.stop="pushPerson('push', data)" style="margin-left: 8px"><UserFilled /></el-icon> | |||
<el-icon size="16" @click.stop="remove(node, data)" style="margin-left: 8px"><Delete /></el-icon> | |||
</span> | |||
</span> | |||
</template> | |||
</el-tree> | |||
</div> | |||
</div> | |||
<div class="table-box"> | |||
<ProTable ref="proTable" title="视频列表" :columns="columns" :request-api="monitorLIVEApi.page"> | |||
<!-- 表格 header 按钮 --> | |||
<template #tableHeader="scope"> | |||
<s-button v-auth="monitorLiveButtonCode.add" suffix="摄像头" @click="onOpen(FormOptEnum.ADD)" /> | |||
<s-button | |||
type="danger" | |||
plain | |||
suffix="摄像头" | |||
:opt="FormOptEnum.DELETE" | |||
:disabled="!scope.isSelected" | |||
@click="onDelete(scope.selectedListIds, '删除所选数据')" | |||
/> | |||
<el-button plain @click="omMove(FormOptEnum.ADD)" type="success">移动至分组</el-button> | |||
</template> | |||
<!-- 表格 菜单类型 按钮 --> | |||
<!-- <template #menuType="scope"> | |||
<el-space wrap> | |||
<el-tag v-if="scope.row.menuType === MenuTypeDictEnum.MENU" type="success">{{ | |||
dictStore.dictTranslation(SysDictEnum.MENU_TYPE, MenuTypeDictEnum.MENU) | |||
}}</el-tag> | |||
<el-tag v-else-if="scope.row.menuType === MenuTypeDictEnum.LINK" type="warning">{{ | |||
dictStore.dictTranslation(SysDictEnum.MENU_TYPE, MenuTypeDictEnum.LINK) | |||
}}</el-tag> | |||
<el-tag v-else type="info">{{ dictStore.dictTranslation(SysDictEnum.MENU_TYPE, scope.row.menuType) }}</el-tag> | |||
<el-tag v-if="scope.row.isHome === true" type="danger">首页</el-tag> | |||
</el-space> | |||
</template> --> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<s-button link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)">编辑</s-button> | |||
<s-button link :opt="FormOptEnum.VIEW" @click="onDetail(scope.row)"> 查看 </s-button> | |||
<s-button link :opt="FormOptEnum.DELETE" @click="onDelete([scope.row.id], `确定删除该摄像头吗?`)" /> | |||
<s-button link :opt="FormOptEnum.VIEW" @click="pushPerson(scope.row)"> 推送人 </s-button> | |||
</template> | |||
</ProTable> | |||
<!-- 添加分组弹框 --> | |||
<el-dialog v-model="groupVisible" :title="groupTitle" width="600px" :before-close="closeGroup"> | |||
<el-form :model="groupForm" :rules="groupRules" ref="groupFormRef" label-width="80px"> | |||
<el-form-item label="分组名称" prop="name"> | |||
<el-input v-model="groupForm.name" placeholder="请输入分组名称"></el-input> | |||
</el-form-item> | |||
</el-form> | |||
<!-- 表格 菜单类型 按钮 --> | |||
<template #menuType="scope"> | |||
<el-space wrap> | |||
<el-tag v-if="scope.row.menuType === MenuTypeDictEnum.MENU" type="success">{{ | |||
dictStore.dictTranslation(SysDictEnum.MENU_TYPE, MenuTypeDictEnum.MENU) | |||
}}</el-tag> | |||
<el-tag v-else-if="scope.row.menuType === MenuTypeDictEnum.LINK" type="warning">{{ | |||
dictStore.dictTranslation(SysDictEnum.MENU_TYPE, MenuTypeDictEnum.LINK) | |||
}}</el-tag> | |||
<el-tag v-else type="info">{{ dictStore.dictTranslation(SysDictEnum.MENU_TYPE, scope.row.menuType) }}</el-tag> | |||
<el-tag v-if="scope.row.isHome === true" type="danger">首页</el-tag> | |||
</el-space> | |||
</template> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<s-button link :opt="FormOptEnum.VIEW" @click="onDetail(scope.row)"> 查看 </s-button> | |||
<!-- <s-button link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)">处理</s-button> | |||
<s-button link :opt="FormOptEnum.DELETE" @click="onDelete([scope.row.id], `确定删除该预警吗?`)" /> --> | |||
</template> | |||
</ProTable> | |||
<el-dialog v-model="visible" :title="detailData.title" width="830px" :before-close="handleClose"> | |||
<div> | |||
<div class="dialogHeader"> | |||
<div></div> | |||
<div class="dialogBtn" @click="refreshUrl"> | |||
<el-icon color="#409efc" :size="20"> | |||
<Refresh /> | |||
</el-icon> | |||
<div>刷新视频</div> | |||
<template #footer> | |||
<div class="dialog-footer"> | |||
<el-button type="primary" @click="onSubmit">提交</el-button> | |||
<el-button @click="closeGroup">取消</el-button> | |||
</div> | |||
</template> | |||
</el-dialog> | |||
<!-- 新增/编辑表单 --> | |||
<Form ref="formRef" /> | |||
<!-- 人员选择 --> | |||
<userForm ref="userFormRef" /> | |||
<!-- 移动至分组 --> | |||
<moveForm ref="moveFormRef" /> | |||
<!-- 视频详情 --> | |||
<el-dialog v-model="visible" :title="detailData.title" width="830px" :before-close="closeGroup"> | |||
<div> | |||
<div class="dialogHeader"> | |||
<div></div> | |||
<div class="dialogBtn" @click="refreshUrl"> | |||
<el-icon color="#409efc" :size="20"> | |||
<Refresh /> | |||
</el-icon> | |||
<div>刷新视频</div> | |||
</div> | |||
</div> | |||
<div v-if="visible || showVideo" class="prism-player" id="player-con"></div> | |||
<!-- <VideoPlay :videoUrl="detailData.videoUrl" :videoType="detailData.videoType" /> --> | |||
<!-- <video style="width: 100%; height: 500px" id="video" controls muted="false"></video> --> | |||
<!-- <iframe src="/static/artcvideo.html" width="100%" height="600px" ref="iframeDom"></iframe> --> | |||
</div> | |||
<div v-if="visible || showVideo" class="prism-player" id="player-con"></div> | |||
<!-- <VideoPlay :videoUrl="detailData.videoUrl" :videoType="detailData.videoType" /> --> | |||
<!-- <video style="width: 100%; height: 500px" id="video" controls muted="false"></video> --> | |||
<!-- <iframe src="/static/artcvideo.html" width="100%" height="600px" ref="iframeDom"></iframe> --> | |||
</div> | |||
<template #footer> | |||
<div class="dialog-footer"> | |||
<el-button @click="handleClose">关闭</el-button> | |||
</div> | |||
</template> | |||
</el-dialog> | |||
<template #footer> | |||
<div class="dialog-footer"> | |||
<el-button @click="handleClose">关闭</el-button> | |||
</div> | |||
</template> | |||
</el-dialog> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup lang="tsx" name="sysSpa"> | |||
import VideoPlay from "@/components/VideoPlay/videoplay.vue"; | |||
import { ElMessage } from "element-plus"; | |||
import { monitorLIVEApi } from "@/api"; | |||
import { ElMessage,ElMessageBox } from "element-plus"; | |||
import type Node from 'element-plus/es/components/tree/src/model/node' | |||
import { monitorLIVEApi, monitorLiveButtonCode } from "@/api"; | |||
import { ZJRQ } from "@/api/interface"; | |||
import { useHandleData } from "@/hooks/useHandleData"; | |||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||
import { useDictStore } from "@/stores/modules"; | |||
import Form from "./components/form.vue"; | |||
import userForm from "./components/userForm.vue"; | |||
import moveForm from "./components/moveForm.vue"; | |||
import { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | |||
import './aliyun-rts-sdk.js' | |||
// import './aliyun-rts-sdk.js' | |||
import './ali.js' | |||
const groupVisible = ref(false); //是否显示表单 | |||
const groupTitle = ref('新增分组'); | |||
const groupForm = reactive({ | |||
name: '' | |||
}); | |||
const groupRules = ref({ | |||
name: [{ required: true, message: '请输入分组名称', trigger: 'blur' }] | |||
}); | |||
const groupFormRef = ref(null); | |||
const closeGroup = () => { | |||
groupVisible.value = false; | |||
groupFormRef.value.resetFields(); | |||
groupForm.name = '' | |||
} | |||
const onSubmit = () => { | |||
groupFormRef.value.validate((valid:any) => { | |||
if (valid) { | |||
closeGroup() | |||
ElMessage.success('提交成功'); | |||
} else { | |||
return false; | |||
} | |||
}); | |||
}; | |||
// 摄像头分组 | |||
interface Tree { | |||
id: any | |||
label: string | |||
children?: Tree[] | |||
} | |||
let id = 1000 | |||
const handleNodeClick = (data: Tree) => { | |||
console.log(data) | |||
} | |||
const treeData = ref<Tree[]>([ | |||
{ | |||
id: '', | |||
label: '全部', | |||
}, | |||
{ | |||
id: '-1', | |||
label: '无分组', | |||
}, | |||
{ | |||
id: 1, | |||
label: '走廊', | |||
}, | |||
{ | |||
id: 2, | |||
label: '大厅', | |||
}, | |||
{ | |||
id: 3, | |||
label: '厨房', | |||
}, | |||
]) | |||
// 新增分组 | |||
const append = (type:string,data: Tree) => { | |||
console.log(type,data) | |||
groupVisible.value = true; | |||
if(type == 'edit') { | |||
groupForm.name = treeData.value.find(item => item.id ==data.id).label; | |||
} | |||
// const newChild = { id: id++, label: 'testtest', children: [] } | |||
// if (!data.children) { | |||
// data.children = [] | |||
// } | |||
// data.children.push(newChild) | |||
// treeData.value = [...treeData.value] | |||
} | |||
// 删除分组 | |||
const remove = (node: Node, data: Tree) => { | |||
const parent = node.parent | |||
const children: Tree[] = parent.data.children || parent.data | |||
const index = children.findIndex((d) => d.id === data.id) | |||
children.splice(index, 1) | |||
treeData.value = [...treeData.value] | |||
} | |||
// 设置分组推送人 | |||
// async function pushPerson(type:string,data: Tree) { | |||
// } | |||
const visible = ref(false); //是否显示表单 | |||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
@@ -72,7 +242,7 @@ | |||
// 表格配置项 | |||
const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||
// { type: "selection", fixed: "left", width: 80 }, | |||
{ type: "selection", fixed: "left", width: 80 }, | |||
// { prop: "searchKey", label: "关键字", search: { el: "input" }, isShow: false }, | |||
// { | |||
// prop: "poiId", | |||
@@ -107,29 +277,57 @@ | |||
prop: "resWidth", | |||
label: "分辨率", | |||
render: (row) => { | |||
console.log(row,88) | |||
return row.row.resWidth + '*' + row.row.resHeight; | |||
} | |||
}, | |||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | |||
]; | |||
/** | |||
* 删除 | |||
* @param ids id数组 | |||
*/ | |||
async function onDelete(ids: string[], msg: string) { | |||
return | |||
// 二次确认 => 请求api => 刷新表格 | |||
await useHandleData(monitorLIVEApi.delete, { ids }, msg); | |||
RefreshTable(); | |||
} | |||
const moveFormRef = ref<InstanceType<typeof Form> | null>(null); | |||
function omMove(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
moveFormRef.value?.omMove({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
/** | |||
* 刷新表格 | |||
*/ | |||
function RefreshTable() { | |||
proTable.value?.refresh(); | |||
} | |||
// 表单引用 | |||
const formRef = ref<InstanceType<typeof Form> | null>(null); | |||
/** | |||
* 打开表单 | |||
* @param opt 操作类型 | |||
* @param record 记录 | |||
*/ | |||
function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
// 人员选择 | |||
// 表单引用 | |||
const userFormRef = ref<InstanceType<typeof Form> | null>(null); | |||
/** | |||
* 打开表单 | |||
* @param opt 操作类型 | |||
* @param record 记录 | |||
*/ | |||
function pushPerson(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
userFormRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
// 详情数据 | |||
// let detailData: globalThis.Ref<{}> | |||
let detailData = reactive({ | |||
@@ -174,16 +372,6 @@ | |||
// detailData.videoUrl = data.pullStreamUrls[2].url; | |||
detailData.streamId = data.streamId; | |||
detailData.videoToken = data.videoToken; | |||
// let timer = setInterval(() => { | |||
// num+=1 | |||
// if(num > 2) { | |||
// } else { | |||
// getvideo1() | |||
// } | |||
// },200) | |||
// getvideo1() | |||
getvideo2() | |||
} | |||
@@ -195,7 +383,6 @@ | |||
// let url = detailData.videoUrl.replace('http://rts-pull-live.deepeleph.com', '/Files') | |||
// console.log(url,888) | |||
let pullStreamUrl = detailData.videoUrl; | |||
console.log(pullStreamUrl,888) | |||
const mediaEle = document.querySelector('video'); | |||
aliRts.on("onError", (err) => { | |||
console.log(`errorCode: ${err.errorCode}`); | |||
@@ -275,11 +462,16 @@ | |||
detailData.videoUrl = '' | |||
detailData.videoType = '' | |||
stopUrl(); | |||
player.value.dispose(); | |||
if(player.value) { | |||
player.value.dispose(); | |||
} | |||
}; | |||
function stopUrl() { | |||
detailData.videoUrl = '' | |||
player.value.dispose(); | |||
if(player.value) { | |||
player.value.dispose(); | |||
} | |||
let params = { | |||
sensorId: detailData.sensorId, | |||
streamId: detailData.streamId, | |||
@@ -291,31 +483,15 @@ | |||
if (code == 200) { | |||
// ElMessage.success(msg); | |||
} | |||
}); | |||
}) | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
@import url("https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css"); | |||
.detailpic { | |||
width: 800px; | |||
object-fit: cover; | |||
} | |||
.linebox { | |||
margin-top: 20px; | |||
} | |||
/* 自定义样式 */ | |||
.vjs-custom-skin .vjs-play-control { | |||
/* 播放按钮的样式 */ | |||
} | |||
@import "./index.scss"; | |||
.dialogHeader { | |||
display: flex; | |||
justify-content: space-between; | |||
@@ -0,0 +1,192 @@ | |||
<!-- | |||
* @Description: 人员表单 | |||
* @Author: syy | |||
* @Date: 2023-12-15 15:45:50 | |||
--> | |||
<template> | |||
<div class="userManageForm"> | |||
<div> | |||
<el-row :gutter="16"> | |||
<el-col :span="12"> | |||
<s-form-item label="人员姓名" prop="name"> | |||
<s-input v-model="userInfo.name"></s-input> | |||
</s-form-item> | |||
</el-col> | |||
<el-col :span="12"> | |||
<s-form-item label="所属班级" prop="personSetId"> | |||
<s-select v-model="userInfo.personSetId" :options="treeData" label="personSetName" value="personSetId"></s-select> | |||
</s-form-item> | |||
</el-col> | |||
</el-row> | |||
<el-row :gutter="16"> | |||
<el-col :span="24"> | |||
<s-form-item label="上传人脸" prop="faces"> | |||
<el-upload | |||
v-model:file-list="fileList" | |||
action="/api/business/personApi/uploadFile" | |||
list-type="picture-card" | |||
:on-success="handleAvatarSuccess" | |||
:on-error="handleAvatarError" | |||
:on-preview="handlePictureCardPreview" | |||
:on-remove="handleRemove" | |||
accept=".jpg, .jpeg, .png" | |||
:headers="{ | |||
Authorization: `${TokenEnum.TOKEN_PREFIX} ${accessToken}` | |||
}" | |||
> | |||
<el-icon><Plus /></el-icon> | |||
</el-upload> | |||
<el-dialog v-model="dialogVisible" title="查看图片"> | |||
<img w-full :src="dialogImageUrl" alt="Preview Image" style="width: 100%" /> | |||
</el-dialog> | |||
</s-form-item> | |||
</el-col> | |||
</el-row> | |||
<el-row :gutter="16"> | |||
<el-col :span="12"> | |||
<s-form-item label="性别" prop="gender"> | |||
<s-radio-group v-model="userInfo.gender" :options="genderOptions" /> | |||
</s-form-item> | |||
</el-col> | |||
<el-col :span="12"> | |||
<s-form-item label="年龄" prop="age"> | |||
<s-input v-model="userInfo.age"></s-input> | |||
</s-form-item> | |||
</el-col> | |||
</el-row> | |||
<el-row :gutter="16"> | |||
<el-col :span="12"> | |||
<s-form-item label="手机号" prop="phone"> | |||
<s-input v-model="userInfo.phone"></s-input> | |||
</s-form-item> | |||
</el-col> | |||
<el-col :span="12"> | |||
<s-form-item label="扩展字段" prop="extData"> | |||
<s-input v-model="userInfo.extData"></s-input> | |||
</s-form-item> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
import { SysUserPersonnel, userManagePersonnelApi, userManageClassManageApi } from "@/api"; | |||
import { Plus } from "@element-plus/icons-vue"; | |||
import { useUserStore } from "@/stores/modules"; | |||
import type { UploadProps, UploadUserFile } from "element-plus"; | |||
import { ElMessage } from "element-plus"; | |||
import { TokenEnum } from "@/enums"; | |||
// props | |||
interface FormProps { | |||
modelValue: Partial<SysUserPersonnel.SysUserPerInfo>; | |||
} | |||
const emit = defineEmits(["update:modelValue"]); //定义emit | |||
const props = defineProps<FormProps>(); //定义props | |||
// 人员信息 | |||
const userInfo = computed({ | |||
get: () => props.modelValue, | |||
set: val => emit("update:modelValue", val) | |||
}); | |||
/* */ | |||
const userStore = useUserStore(); | |||
const { accessToken } = userStore; | |||
const fileList = ref([]); | |||
const faces = ref<SysUserPersonnel.SysUserAvatar[]>([]); | |||
const dialogImageUrl = ref(""); | |||
const dialogVisible = ref(false); | |||
const treeData = ref<{ [key: string]: any }[]>([]); | |||
const handleRemove: UploadProps["onRemove"] = uploadFile => { | |||
const index = faces.value.findIndex(item => item.uid === uploadFile.uid); | |||
if (index > -1) { | |||
faces.value.splice(index, 1); | |||
} | |||
userInfo.value.faces = faces.value; | |||
if (uploadFile.personId) { | |||
userManagePersonnelApi.deleteFace({ personId: uploadFile.personId, faceIds: [uploadFile.uid] }).then(res => {}); | |||
} | |||
}; | |||
const handlePictureCardPreview: UploadProps["onPreview"] = uploadFile => { | |||
dialogImageUrl.value = uploadFile.url!; | |||
dialogVisible.value = true; | |||
}; | |||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response, uploadFile) => { | |||
if (response.code === 200) { | |||
faces.value.push({ faceUrl: response.data, uid: uploadFile.uid }); | |||
userInfo.value.faces = faces.value; | |||
ElMessage.success(response.msg); | |||
} else { | |||
ElMessage.error(response.msg); | |||
fileList.value = fileList.value.splice(0, fileList.value.length - 1); | |||
} | |||
}; | |||
const handleAvatarError: UploadProps["onError"] = (error, uploadFile, uploadFiles) => { | |||
console.log(error, uploadFile, uploadFiles, "err"); | |||
}; | |||
// 通用状态选项 | |||
const genderOptions = ref([ | |||
{ | |||
label: "未知", | |||
value: "GENDER_UNKNOWN" | |||
}, | |||
{ | |||
label: "男", | |||
value: "GENDER_MALE" | |||
}, | |||
{ | |||
label: "女", | |||
value: "GENDER_FEMALE" | |||
} | |||
]); | |||
const getRequestData = async () => { | |||
const { data } = await userManageClassManageApi.page(); | |||
treeData.value = data; | |||
console.log(treeData.value, "treeData"); | |||
}; | |||
onMounted(() => { | |||
// 初始化 | |||
userInfo.value.gender = userInfo.value.gender ? userInfo.value.gender : genderOptions.value[0].value; | |||
getRequestData(); | |||
if (userInfo.value.personId) { | |||
if (userInfo.value.faces?.length > 0) { | |||
fileList.value = [ | |||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map(item => { | |||
return { | |||
url: item.faceUrl, | |||
uid: item.faceId, | |||
personId: userInfo.value.personId | |||
}; | |||
}) | |||
]; | |||
faces.value = [ | |||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map(item => { | |||
return { | |||
faceUrl: item.faceUrl, | |||
uid: item.faceId, | |||
personId: userInfo.value.personId | |||
}; | |||
}) | |||
]; | |||
} | |||
} | |||
}); | |||
</script> | |||
<style lang="scss" scoped> | |||
:deep(.el-input__wrapper) { | |||
width: 100% !important; | |||
} | |||
:deep(.el-date-editor.el-input) { | |||
width: 92% !important; | |||
} | |||
:deep(.el-upload-list--picture-card .el-upload-list__item) { | |||
width: 100px !important; | |||
height: 100px !important; | |||
} | |||
:deep(.el-upload--picture-card) { | |||
width: 100px !important; | |||
height: 100px !important; | |||
} | |||
</style> |
@@ -0,0 +1,120 @@ | |||
<!-- | |||
* @Description: 表单 | |||
* @Author: syy | |||
* @Date: 2023-12-15 15:45:59 | |||
--> | |||
<template> | |||
<div> | |||
<form-container v-model="visible" :title="`${sysUserProps.opt}人员`" form-size="800px" @close="onClose"> | |||
<el-form | |||
ref="sysUserFormRef" | |||
:rules="rules" | |||
:disabled="sysUserProps.disabled" | |||
:model="sysUserProps.record" | |||
:hide-required-asterisk="sysUserProps.disabled" | |||
label-width="auto" | |||
label-suffix=" :" | |||
> | |||
<el-tabs v-model="activeName"> | |||
<Basic v-model="sysUserProps.record"></Basic> | |||
</el-tabs> | |||
</el-form> | |||
<template #footer> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button v-show="!sysUserProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button> | |||
</template> | |||
</form-container> | |||
</div> | |||
</template> | |||
<script setup lang="ts" name="SysUserPersonnelForm"> | |||
import { SysUserPersonnel, userManagePersonnelApi } from "@/api"; | |||
import { FormOptEnum, SysDictEnum } from "@/enums"; | |||
import { required } from "@/utils/formRules"; | |||
import { FormInstance } from "element-plus"; | |||
import { useDictStore } from "@/stores/modules"; | |||
import Basic from "./form_basic.vue"; | |||
const visible = ref(false); //是否显示表单 | |||
const activeName = ref("basic"); | |||
// 表单参数 | |||
const sysUserProps = reactive<FormProps.Base<SysUserPersonnel.SysUserPerInfo>>({ | |||
opt: FormOptEnum.ADD, | |||
record: {}, | |||
disabled: false | |||
}); | |||
// 表单验证规则 | |||
const rules = reactive({ | |||
// personId: [required("请输入人员ID")], | |||
name: [required("请输入姓名")], | |||
gender: [required("请选择性别")], | |||
faces: [required("请上传人脸图片")], | |||
phone: [required("请输入手机号")] | |||
// extData: [required("请输入扩展数据")] | |||
}); | |||
/** | |||
* 打开表单 | |||
* @param props 表单参数 | |||
*/ | |||
function onOpen(props: FormProps.Base<SysUserPersonnel.SysUserPerInfo>) { | |||
Object.assign(sysUserProps, props); //合并参数 | |||
if (props.opt == FormOptEnum.ADD) { | |||
//如果是新增,设置默认值 | |||
// sysUserProps.record.sortCode = 99; | |||
} | |||
visible.value = true; //显示表单 | |||
if (props.record.personId) { | |||
//如果传了id,就去请求api获取record | |||
userManagePersonnelApi.detail({ id: props.record.personId }).then(res => { | |||
sysUserProps.record = res.data; | |||
}); | |||
} | |||
} | |||
// 提交数据(新增/编辑) | |||
const sysUserFormRef = ref<FormInstance>(); | |||
/** 提交表单 */ | |||
async function handleSubmit() { | |||
sysUserFormRef.value?.validate(async valid => { | |||
if (!valid) return; //表单验证失败 | |||
//提交表单 | |||
if (sysUserProps.record.faces.length === 0) { | |||
return ElMessage.error("请上传人脸图片"); | |||
} | |||
if (sysUserProps.record.personId) { | |||
await userManagePersonnelApi | |||
.update(sysUserProps.record) | |||
.then(() => { | |||
sysUserProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
} else { | |||
await userManagePersonnelApi | |||
.add(sysUserProps.record) | |||
.then(() => { | |||
sysUserProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
} | |||
}); | |||
} | |||
/** 关闭表单*/ | |||
function onClose() { | |||
visible.value = false; | |||
activeName.value = "basic"; | |||
} | |||
// 暴露给父组件的方法 | |||
defineExpose({ | |||
onOpen | |||
}); | |||
</script> | |||
<style lang="scss" scoped></style> |
@@ -0,0 +1,107 @@ | |||
<!-- | |||
* @Description: 表单 | |||
* @Author: syy | |||
* @Date: 2023-12-15 15:45:59 | |||
--> | |||
<template> | |||
<div> | |||
<form-container v-model="visibleClass" :title="`${sysUserProps.opt}班级`" form-size="400px" @close="onClose"> | |||
<el-form | |||
ref="sysUserFormRef" | |||
:rules="rules" | |||
:disabled="sysUserProps.disabled" | |||
:model="sysUserProps.record" | |||
:hide-required-asterisk="sysUserProps.disabled" | |||
label-width="auto" | |||
label-suffix=" :" | |||
> | |||
<div> | |||
<el-row :gutter="16"> | |||
<el-col :span="22"> | |||
<s-form-item label="班级名称" prop="personSetName"> | |||
<s-input v-model="sysUserProps.record.personSetName"></s-input> | |||
</s-form-item> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
</el-form> | |||
<template #footer> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button v-show="!sysUserProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button> | |||
</template> | |||
</form-container> | |||
</div> | |||
</template> | |||
<script setup lang="ts" name="SysUserPerformClass"> | |||
import { ref } from "vue"; | |||
import { SysUserPersonnel, userManageClassManageApi } from "@/api"; | |||
import { FormOptEnum } from "@/enums"; | |||
import { required } from "@/utils/formRules"; | |||
import { FormInstance } from "element-plus"; | |||
const visibleClass = ref(false); //是否显示表单 | |||
// 表单参数 | |||
const sysUserProps = reactive<FormProps.Base<SysUserPersonnel.ClassPage>>({ | |||
opt: FormOptEnum.ADD, | |||
record: {}, | |||
disabled: false | |||
}); | |||
// 表单验证规则 | |||
const rules = reactive({ | |||
personSetName: [required("请输入班级名称")] | |||
}); | |||
/** | |||
* 打开表单 | |||
* @param props 表单参数 | |||
*/ | |||
function onOpen(props: FormProps.Base<SysUserPersonnel.ClassPage>) { | |||
Object.assign(sysUserProps, props); //合并参数 | |||
visibleClass.value = true; //显示表单 | |||
sysUserProps.record = props.record; | |||
} | |||
// 提交数据(新增/编辑) | |||
const sysUserFormRef = ref<FormInstance>(); | |||
/** 提交表单 */ | |||
async function handleSubmit() { | |||
sysUserFormRef.value?.validate(async valid => { | |||
if (!valid) return; //表单验证失败 | |||
sysUserProps.record.name = sysUserProps.record.personSetName; | |||
//提交表单 | |||
if (sysUserProps.record.personSetId) { | |||
sysUserProps.record.id = sysUserProps.record.personSetId; | |||
await userManageClassManageApi | |||
.update(sysUserProps.record) | |||
.then(() => { | |||
sysUserProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
} else { | |||
await userManageClassManageApi | |||
.add(sysUserProps.record) | |||
.then(() => { | |||
sysUserProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
} | |||
}); | |||
} | |||
/** 关闭表单*/ | |||
function onClose() { | |||
visibleClass.value = false; | |||
} | |||
// 暴露给父组件的方法 | |||
defineExpose({ | |||
onOpen | |||
}); | |||
</script> | |||
<style lang="scss" scoped></style> |
@@ -0,0 +1,291 @@ | |||
<!-- | |||
* @Description: 人员管理 | |||
* @Author: syy | |||
* @Date: 2024-7-15 | |||
--> | |||
<template> | |||
<div class="main-box"> | |||
<TreeFilter | |||
ref="treeFilter" | |||
label="personSetName" | |||
id="personSetId" | |||
width="300px" | |||
:show-all="false" | |||
:request-api="userManageClassManageApi.page" | |||
@change="changeTreeFilter" | |||
> | |||
<template v-slot:header> | |||
<s-button suffix="班级" @click="addClass(FormOptEnum.ADD)" style="margin-bottom: 15px" /> | |||
</template> | |||
<template v-slot:label="{ row }"> | |||
<span class="custom-tree-node"> | |||
<span>{{ row.node.label }}</span> | |||
<span> | |||
<a @click="addClass(FormOptEnum.EDIT, row.node.data)"> | |||
<el-icon><Edit /></el-icon> | |||
</a> | |||
<a style="margin-left: 8px" @click="addDelete(row.node.data.personSetId, '删除班级')"> | |||
<el-icon><Delete /></el-icon> | |||
</a> | |||
</span> | |||
</span> | |||
</template> | |||
</TreeFilter> | |||
<div class="table-box"> | |||
<ProTable ref="proTable" title="人员管理" :columns="columns" rowKey="personId" :request-api="userManagePersonnelApi.page"> | |||
<!-- 表格 header 按钮 --> | |||
<template #tableHeader="scope"> | |||
<s-button v-auth="userPerButtonCode.add" suffix="人员" @click="onOpen(FormOptEnum.ADD, { personSetId: personSetId })" /> | |||
<s-button | |||
v-auth="userPerButtonCode.delete" | |||
type="danger" | |||
:opt="FormOptEnum.DELETE" | |||
plain | |||
suffix="人员" | |||
:disabled="!scope.isSelected" | |||
@click="onDelete(scope.selectedListIds, '删除所选人员')" | |||
/> | |||
</template> | |||
<!-- 表格操作栏 --> | |||
<template #operation="scope"> | |||
<el-space> | |||
<s-button v-auth="userPerButtonCode.edit" link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)" /> | |||
<s-button v-auth="userPerButtonCode.delete" link :opt="FormOptEnum.DELETE" @click="onDelete([scope.row.personId], `删除人员`)" /> | |||
<el-dropdown @command="handleCommand"> | |||
<el-link type="primary" :underline="false" :icon="ArrowDown"> 更多 </el-link> | |||
<template #dropdown> | |||
<el-dropdown-menu> | |||
<el-dropdown-item :command="command(scope.row, cmdEnum.AddFace)" | |||
><el-upload | |||
ref="upload" | |||
class="upload-demo" | |||
action="/api/business/personApi/uploadFile" | |||
:show-file-list="false" | |||
:on-success="handleAvatarSuccess" | |||
accept=".jpg, .jpeg, .png" | |||
:headers="{ | |||
Authorization: `${TokenEnum.TOKEN_PREFIX} ${accessToken}` | |||
}" | |||
> | |||
<template #trigger> | |||
{{ cmdEnum.AddFace }} | |||
</template> | |||
</el-upload> | |||
</el-dropdown-item> | |||
<el-dropdown-item :command="command(scope.row, cmdEnum.UnderpantsUnBinding)"> | |||
{{ cmdEnum.UnderpantsUnBinding }} | |||
</el-dropdown-item> | |||
</el-dropdown-menu> | |||
</template> | |||
</el-dropdown> | |||
</el-space> | |||
</template> | |||
</ProTable> | |||
</div> | |||
<!-- 人员新增/编辑表单 --> | |||
<Form ref="formRef"></Form> | |||
<!-- 班级新增/编辑表单 --> | |||
<FormClass ref="formRefC" /> | |||
<!-- 预览头像 --> | |||
<el-dialog v-model="visible" title="查看头像" width="830px" :before-close="handleClose"> | |||
<div style="display: flex; align-items: center; justify-content: center"> | |||
<img class="detailpic" :src="faceUrl" alt="" /> | |||
</div> | |||
</el-dialog> | |||
</div> | |||
</template> | |||
<script setup lang="tsx" name="SysUserPersonnel"> | |||
import { userManagePersonnelApi,userPerButtonCode,SysUserPersonnel,userManageClassManageApi } from "@/api"; | |||
import { useHandleData } from "@/hooks/useHandleData"; | |||
import { FormOptEnum } from "@/enums"; | |||
import Form from "./components/form/index.vue"; | |||
import FormClass from "./components/formClass/index.vue"; | |||
import { ArrowDown } from "@element-plus/icons-vue"; | |||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||
import TreeFilter from "@/components/TreeFilter/index.vue"; | |||
import { useUserStore } from "@/stores/modules"; | |||
import { TokenEnum } from "@/enums"; | |||
import type { UploadProps } from "element-plus"; | |||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const faceUrl = ref(''); | |||
const visible = ref(false); //是否显示人员表单 | |||
const proTable = ref<ProTableInstance>(); | |||
const treeFilter = ref<InstanceType<typeof TreeFilter> | null>(null); | |||
const userStore = useUserStore(); | |||
const { accessToken } = userStore; | |||
// 表格配置项 | |||
const columns: ColumnProps<SysUserPersonnel.SysUserPerInfo>[] = [ | |||
{ type: "selection", fixed: "left", width: 50 }, | |||
{ | |||
prop: "faceUrl", | |||
label: "人脸", | |||
render: scope => { | |||
return ( | |||
<img src={scope.row.faces.length > 0 ? scope.row.faces[0].faceUrl : ''} onClick={() => viewHeadImage(scope)} style='width:50px;height:50px;' alt=''/> | |||
); | |||
} | |||
}, | |||
{ | |||
prop: "name", | |||
label: "姓名" | |||
}, | |||
{ | |||
prop: "personId", | |||
label: "人员ID" | |||
}, | |||
{ | |||
prop: "age", | |||
label: "年龄" | |||
}, | |||
{ | |||
prop: "personSets", | |||
label: "所属班级", | |||
render: scope => { | |||
return scope.row.personSets.length > 0 ? scope.row.personSets[0].personSetName : '' | |||
} | |||
}, | |||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | |||
]; | |||
const viewHeadImage = (scope: any) => { | |||
faceUrl.value = scope.row.faces[0].faceUrl; | |||
visible.value = true | |||
console.log(faceUrl); | |||
}; | |||
const handleClose = () => { | |||
visible.value = false; | |||
}; | |||
// 人员表单引用 | |||
const formRef = ref<InstanceType<typeof Form> | null>(null); | |||
// 班级表单引用 | |||
const formRefC = ref<InstanceType<typeof FormClass> | null>(null); | |||
/** | |||
* 打开表单 | |||
* @param opt 操作类型 | |||
* @param record 记录 | |||
*/ | |||
function onOpen(opt: FormOptEnum, record: {} | SysUserPersonnel.SysUserPerInfo = {}) { | |||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
/** | |||
* 打开班级表单 | |||
* @param opt 操作类型 | |||
* @param record 记录 | |||
*/ | |||
function addClass(opt: FormOptEnum, record: {} | SysUserPersonnel.ClassPage = {}) { | |||
formRefC.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
/** | |||
* 班级删除 | |||
* @param ids id数组 | |||
*/ | |||
async function addDelete(id: string[],msg: string) { | |||
// 二次确认 => 请求api => 刷新表格 | |||
await useHandleData(userManageClassManageApi.delete, { id }, msg); | |||
RefreshTable(); //刷新表格 | |||
} | |||
/** | |||
* 人员删除 | |||
* @param ids id数组 | |||
*/ | |||
async function onDelete(ids: string[], msg: string) { | |||
if(ids.length === 0){ | |||
ElMessage({ | |||
message: '请选择要删除的人员', | |||
type: 'warning' | |||
}); | |||
return | |||
} | |||
// 二次确认 => 请求api => 刷新表格 | |||
await useHandleData(userManagePersonnelApi.delete, {id: ids.join(",") }, msg); | |||
RefreshTable(); //刷新表格 | |||
} | |||
// 刷新表格 | |||
const RefreshTable = () => { | |||
proTable.value?.refresh(); | |||
treeFilter.value?.refresh(); //刷新树形筛选器 | |||
} | |||
/** 更多下拉菜单命令枚举 */ | |||
enum cmdEnum { | |||
AddFace = "添加人脸", | |||
UnderpantsUnBinding = "底库解绑" | |||
} | |||
/** 下拉菜单参数接口 */ | |||
interface Command { | |||
row: SysUserPersonnel.SysUserPerInfo; | |||
command: cmdEnum; | |||
} | |||
/**配置command的参数 */ | |||
function command(row: SysUserPersonnel.SysUserPerInfo, command: cmdEnum): Command { | |||
return { | |||
row: row, | |||
command: command | |||
}; | |||
} | |||
/** | |||
* 更多下拉菜单点击事件 | |||
* @param command | |||
*/ | |||
const personId = ref<number | string>(); //人员id | |||
function handleCommand(command: Command) { | |||
switch (command.command) { | |||
case cmdEnum.AddFace: | |||
personId.value = command.row.personId; //获取人员id | |||
break | |||
case cmdEnum.UnderpantsUnBinding: | |||
userManagePersonnelApi.personUnBindDfie({ | |||
personId:command.row.personId, | |||
personSetId: command.row.personSets[0].personSetId | |||
}).then(res=>{ | |||
if(res.code == 200){ | |||
ElMessage.success('底库解绑成功'); | |||
RefreshTable() | |||
} | |||
}) | |||
break; | |||
} | |||
} | |||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response, uploadFile) => { | |||
if (response.code === 200) { | |||
userManagePersonnelApi.addFace({ | |||
personId: personId.value, | |||
faceUrl: response.data | |||
}).then(res=>{ | |||
RefreshTable() | |||
}) | |||
} else { | |||
ElMessage.error(response.msg); | |||
} | |||
}; | |||
/** 部门切换 */ | |||
const personSetId = ref<number | string>() | |||
function changeTreeFilter(val: number | string) { | |||
personSetId.value = val | |||
proTable.value!.pageable.pageNum = 1; | |||
proTable.value!.searchParam.personSetId = val; | |||
proTable.value!.search(); | |||
} | |||
</script> | |||
<style scoped lang="scss"> | |||
.table-box { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
.custom-tree-node { | |||
display: flex; | |||
flex: 1; | |||
align-items: center; | |||
justify-content: space-between; | |||
padding-right: 8px; | |||
font-size: 14px; | |||
} | |||
</style> |
@@ -7,13 +7,13 @@ | |||
<div class="card content-main"> | |||
<el-row :gutter="20"> | |||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> | |||
<div ref="chart1" style="width: 600px; height: 400px"></div> | |||
<div ref="chart1" style="width: 600px; height: 378px"></div> | |||
</el-col> | |||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"> | |||
<div ref="chart2" style="width: 600px; height: 400px"></div> | |||
<div ref="chart2" style="width: 600px; height: 378px"></div> | |||
</el-col> | |||
<el-col :span="24"> | |||
<div ref="chart3" style="width: 100%; height: 400px"></div> | |||
<div ref="chart3" style="width: 100%; height: 378px"></div> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
@@ -110,7 +110,28 @@ import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||
import { useDictStore } from "@/stores/modules"; | |||
import { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | |||
const visible = ref(false); //是否显示表单 | |||
onMounted(() => { | |||
getWarnTypeList(); | |||
}); | |||
let warnOptions = ref([]); | |||
function getWarnTypeList() { | |||
setTimeout(async ()=> { | |||
await warnZJRQApi.warnType({}).then(res => { | |||
let { code, data } = res; | |||
if (code == 200) { | |||
warnOptions.value = data.map(item => { | |||
return { | |||
label: item.name, | |||
value: item.code | |||
}; | |||
}) | |||
} | |||
}); | |||
}) | |||
} | |||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const proTable = ref<ProTableInstance>(); | |||
const dictStore = useDictStore(); | |||
@@ -118,7 +139,7 @@ const dictStore = useDictStore(); | |||
// 表格配置项 | |||
const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||
{ type: "selection", fixed: "left", width: 80 }, | |||
{ prop: "searchKey", label: "关键字", search: { el: "input" }, isShow: false }, | |||
// { prop: "searchKey", label: "关键字", search: { el: "input" }, isShow: false }, | |||
// { prop: "cameraId", label: "所属摄像头", search: { el: "input" }, isShow: false }, | |||
// { prop: "alarmTypeDesc", label: "告警类型", search: { el: "input" }, isShow: false }, | |||
{ | |||
@@ -151,21 +172,75 @@ const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||
} | |||
}, | |||
{ | |||
prop: "alarmTypeDesc", | |||
label: "告警类型" | |||
prop: "alarmType", | |||
label: "告警类型", | |||
enum: warnOptions, | |||
search: { | |||
el: "tree-select", | |||
// span: 1 | |||
} | |||
}, | |||
{ | |||
prop: "warnHand", | |||
label: "处理状态", | |||
enum: [ | |||
{ | |||
label: "已处理", | |||
value: 1 | |||
}, | |||
{ | |||
label: "未处理", | |||
value: 0 | |||
} | |||
], | |||
render: scope => { | |||
if (scope.row.warnHand === 1) { | |||
return "已处理"; | |||
} else { | |||
return "未处理"; | |||
} | |||
} | |||
}, | |||
search: { | |||
el: "tree-select", | |||
// span: 1 | |||
} | |||
}, | |||
{ prop: "tick", label: "预警时间" }, | |||
{ | |||
prop: "tick", | |||
label: "预警时间", | |||
search: { | |||
// 自定义 search 组件 | |||
span: 1, | |||
render: ({ searchParam }) => { | |||
return ( | |||
<div class="flex-center"> | |||
<el-date-picker | |||
style="150px; flex-shink: 1;" | |||
v-model={searchParam.StartTick} | |||
type="datetime" | |||
placeholder="开始时间" | |||
format="YYYY-MM-DD HH:mm:ss" | |||
date-format="YYYY-MM-DD" | |||
time-format="HH:mm:ss" | |||
value-format="YYYY-MM-DD HH:mm:ss" | |||
/> | |||
<span class="mr10 ml10">-</span> | |||
<el-date-picker | |||
style="150px;" | |||
v-model={searchParam.EndTick} | |||
type="datetime" | |||
placeholder="结束时间" | |||
format="YYYY-MM-DD HH:mm:ss" | |||
date-format="YYYY-MM-DD" | |||
time-format="HH:mm:ss" | |||
value-format="YYYY-MM-DD HH:mm:ss" | |||
/> | |||
</div> | |||
); | |||
} | |||
} | |||
}, | |||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | |||
]; | |||