@@ -51,6 +51,13 @@ export interface ReqId { | |||||
/** id */ | /** id */ | ||||
id: number | string; | id: number | string; | ||||
} | } | ||||
/** ReqClothId请求参数 */ | |||||
export interface ReqClothId { | |||||
/** ReqClothId */ | |||||
clothSetId: number | string; | |||||
} | |||||
/** id请求参数 */ | /** id请求参数 */ | ||||
export interface ReqPersonId { | export interface ReqPersonId { | ||||
/** id */ | /** id */ | ||||
@@ -21,3 +21,4 @@ export * from "./organization"; | |||||
export * from "./auth"; | export * from "./auth"; | ||||
export * from "./warn"; | export * from "./warn"; | ||||
export * from "./usermanage"; | export * from "./usermanage"; | ||||
export * from "./monitor"; |
@@ -0,0 +1,60 @@ | |||||
/** | |||||
* @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/interface"; | |||||
/** | |||||
* @Description: 单页管理接口 | |||||
* @Author: wangwenpei | |||||
* @Date: 2023-12-15 15:34:54 | |||||
*/ | |||||
export namespace sysCamera { | |||||
/**单页分页查询 */ | |||||
export interface Page extends ReqPage { | |||||
alarmType: "visual_fence"; | |||||
} | |||||
/** 单页信息 */ | |||||
export interface MonitorInfo { | |||||
/** id */ | |||||
id: number | string; | |||||
deviceStatus: boolean; | |||||
directUrlIp: string; | |||||
fieldId: number | string; | |||||
fieldName: string; | |||||
groupId: number; | |||||
lastTime: string; | |||||
pushUserId: number; | |||||
resHeight: number; | |||||
resWidth: number; | |||||
sensorId: string; | |||||
sensorName: string; | |||||
/** 摄像头快照 */ | |||||
snapshotUrl: string; | |||||
} | |||||
/**摄像头分页查询 */ | |||||
export interface List extends ReqPage { | |||||
sensorName?: string; | |||||
sensorId?: number | string; | |||||
groupId?: number | string; | |||||
} | |||||
// 摄像头分组 | |||||
export interface Tree extends MonitorInfo { | |||||
children: Tree[]; | |||||
} | |||||
// 摄像头分组添加编辑删除信息 | |||||
export interface MonitorGroupInfo { | |||||
/** id */ | |||||
id: number | string; | |||||
name: string; | |||||
} | |||||
} |
@@ -0,0 +1 @@ | |||||
export * from "./camera"; |
@@ -0,0 +1,39 @@ | |||||
/** | |||||
* @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.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||||
*/ | |||||
/** | |||||
* @Description: 服装底库管理接口 | |||||
* @Author: syy | |||||
* @Date: 2023-12-15 15:34:54 | |||||
*/ | |||||
export namespace SysUserCloth { | |||||
// 服装底库树 | |||||
export interface Page { | |||||
clothSetId?: number | string; | |||||
clothSetName?: number | string; | |||||
} | |||||
export interface list { | |||||
clothUrl: string; | |||||
clothId: number | string; | |||||
clothSetId: string; | |||||
} | |||||
/** 用户信息 */ | |||||
export interface SysUserClothInfo { | |||||
clothSetId: number | string; | |||||
clothSetName: number | string; | |||||
/** 列表 */ | |||||
clothes?: Array<list>; | |||||
} | |||||
} |
@@ -13,3 +13,4 @@ | |||||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | * @see https://gitee.com/dotnetmoyu/SimpleAdmin | ||||
*/ | */ | ||||
export * from "./personnel"; | export * from "./personnel"; | ||||
export * from "./clothing"; |
@@ -26,9 +26,11 @@ export namespace SysUserPersonnel { | |||||
export interface ClassPage { | export interface ClassPage { | ||||
personSetId?: string | number | undefined; | personSetId?: string | number | undefined; | ||||
personSetName?: string | undefined; | personSetName?: string | undefined; | ||||
id?: string | undefined; | |||||
name?: string | undefined; | |||||
personId?: string | undefined | number; | |||||
id?: string | number | undefined; | |||||
name?: string | number | undefined; | |||||
userId?: string | number | undefined; | |||||
personId?: string | number | undefined; | |||||
userName?: string | number | undefined; | |||||
} | } | ||||
/** 人脸信息 */ | /** 人脸信息 */ | ||||
export interface SysUserAvatar { | export interface SysUserAvatar { | ||||
@@ -59,9 +61,9 @@ export namespace SysUserPersonnel { | |||||
/** 扩展字段 */ | /** 扩展字段 */ | ||||
extData?: string; | extData?: string; | ||||
/** 人脸 */ | /** 人脸 */ | ||||
faces: Array<SysUserAvatar>; | |||||
faces: any; | |||||
/** 分组 */ | /** 分组 */ | ||||
personSets: Array<ClassPage>; | |||||
personSets: any; | |||||
personSetId?: number | string; | personSetId?: number | string; | ||||
} | } | ||||
} | } |
@@ -13,28 +13,53 @@ | |||||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | * @see https://gitee.com/dotnetmoyu/SimpleAdmin | ||||
*/ | */ | ||||
import { moduleRequest } from "@/api/request"; | import { moduleRequest } from "@/api/request"; | ||||
import { ReqstartId, ResPage, ZJRQ, ReqstopId } from "@/api/interface"; | |||||
import { ReqstartId, ResPage, sysCamera, ReqstopId } from "@/api/interface"; | |||||
const http = moduleRequest("/business/deviceApi/"); | const http = moduleRequest("/business/deviceApi/"); | ||||
const http2 = moduleRequest("/business/cameraInfo/"); | |||||
const http3 = moduleRequest("/business/cameraGroup/"); | |||||
/** | /** | ||||
* @Description: 单页管理 | * @Description: 单页管理 | ||||
* @Author: huguodong | |||||
* @Author: wangwenpei | |||||
* @Date: 2023-12-15 15:34:54 | * @Date: 2023-12-15 15:34:54 | ||||
*/ | */ | ||||
const monitorLIVEApi = { | const monitorLIVEApi = { | ||||
/** 获取单页分页 */ | /** 获取单页分页 */ | ||||
page(params: ZJRQ.Page) { | |||||
return http.post<ResPage<ZJRQ.WarnInfo>>("brief", params); | |||||
page(params: sysCamera.Page) { | |||||
return http.post<ResPage<sysCamera.MonitorInfo>>("brief", params); | |||||
}, | }, | ||||
/** 获取单页详情 */ | /** 获取单页详情 */ | ||||
detail(params: ReqstartId) { | detail(params: ReqstartId) { | ||||
return http.get<ZJRQ.WarnInfo>("getStartVideoLive", params); | |||||
return http.get<sysCamera.MonitorInfo>("getStartVideoLive", params); | |||||
}, | |||||
/** 停止视频流获取 */ | |||||
stopUrl(params: ReqstopId) { | |||||
return http.get<sysCamera.MonitorInfo>("getStopVideoLive", params); | |||||
}, | |||||
/** 获取监控列表 */ | |||||
list(params: sysCamera.List) { | |||||
return http2.get<ResPage<sysCamera.MonitorInfo>>("getPageList", params); | |||||
}, | |||||
// 批量设置分组 | |||||
setGroup(params: sysCamera.setGroup) { | |||||
return http2.post<ResPage<sysCamera.MonitorInfo>>("batchSetGroup", params); | |||||
}, | |||||
// 获取摄像头分组树 | |||||
groupList(params: sysCamera.Tree) { | |||||
return http3.get<ResPage<sysCamera.MonitorInfo>>("getNoPageList"); | |||||
}, | |||||
// 添加摄像头分组树 | |||||
addGroup(params: sysCamera.MonitorGroupInfo) { | |||||
return http3.post<ResPage<sysCamera.MonitorInfo>>("add", params); | |||||
}, | }, | ||||
/** 停止视频流获取 */ | |||||
stopUrl(params: ReqstopId) { | |||||
return http.get<ZJRQ.WarnInfo>("getStopVideoLive", params); | |||||
// 编辑摄像头分组树 | |||||
updateGroup(params: sysCamera.MonitorGroupInfo) { | |||||
return http3.put<ResPage<sysCamera.MonitorInfo>>("update", params); | |||||
}, | }, | ||||
// 删除摄像头分组树 | |||||
deleteGroup(params: sysCamera.MonitorGroupInfo) { | |||||
return http3.delete<ResPage<sysCamera.MonitorInfo>>("delete", params); | |||||
} | |||||
}; | }; | ||||
/** | /** | ||||
* @Description: 监控管理按钮权限码 | * @Description: 监控管理按钮权限码 | ||||
@@ -23,8 +23,8 @@ const http = moduleRequest("/business/dfieldApi/"); | |||||
*/ | */ | ||||
const userManageClassManageApi = { | const userManageClassManageApi = { | ||||
/** 查询底库列表 */ | /** 查询底库列表 */ | ||||
page(params: SysUserPersonnel.ClassPage) { | |||||
return http.get("queryAll", params); | |||||
page() { | |||||
return http.get("queryAll"); | |||||
}, | }, | ||||
/** 删除底库 */ | /** 删除底库 */ | ||||
delete(params: ReqId) { | delete(params: ReqId) { | ||||
@@ -0,0 +1,68 @@ | |||||
/** | |||||
* @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, SysUserCloth, ReqClothId } from "@/api/interface"; | |||||
const http = moduleRequest("/business/clothApi/"); | |||||
/** | |||||
* @Description: 单页管理 | |||||
* @Author: SYY | |||||
* @Date: 2023-12-15 15:34:54 | |||||
*/ | |||||
const userManageClothApi = { | |||||
/** 查询服装底库列表 */ | |||||
getList(params: SysUserCloth.Page) { | |||||
return http.get("getList", params); | |||||
}, | |||||
/** 查询服装底库详情 */ | |||||
page(params: ReqClothId) { | |||||
return http.get("getInfo", params); | |||||
}, | |||||
/** 删除服装底库 */ | |||||
deleteClothDataBaseD(params: ReqClothId) { | |||||
return http.delete("deleteClothDataBaseD", params); | |||||
}, | |||||
/** 新增服装底库 */ | |||||
addClothDataBaseA(params: SysUserCloth.Page) { | |||||
return http.post("addClothDataBaseA", params); | |||||
}, | |||||
/** 更新服装底库 */ | |||||
update(params: SysUserCloth.Page) { | |||||
return http.put("updateClothU", params); | |||||
}, | |||||
/** 服装图片上传 */ | |||||
uploadFile(params: any) { | |||||
return http.post("uploadFile", params); | |||||
}, | |||||
/** 新增服装 */ | |||||
add(params: any) { | |||||
return http.post("addClothA", params); | |||||
}, | |||||
/** 删除服装 */ | |||||
delete(params: SysUserCloth.list) { | |||||
return http.post("deleteClothD", params); | |||||
} | |||||
}; | |||||
const userClothButtonCode = { | |||||
/** 新增人员 */ | |||||
add: "userManageClothAdd", | |||||
/** 删除人员 */ | |||||
edit: "userManageClothEdit", | |||||
/** 删除人员 */ | |||||
delete: "userManageClothDelete" | |||||
}; | |||||
export { userManageClothApi, userClothButtonCode }; |
@@ -14,3 +14,6 @@ | |||||
*/ | */ | ||||
export * from "./personnel"; | export * from "./personnel"; | ||||
export * from "./classManage"; | export * from "./classManage"; | ||||
export * from "./clothing"; | |||||
export * from "./teacher"; | |||||
export * from "./keyPersonnel"; |
@@ -0,0 +1,63 @@ | |||||
/** | |||||
* @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/keyPersonnel/"); | |||||
/** | |||||
* @Description: 单页管理 | |||||
* @Author: SYY | |||||
* @Date: 2024-7-19 15:34:54 | |||||
*/ | |||||
const userManageKeyPersonApi = { | |||||
/** 获取单页分页 */ | |||||
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: any) { | |||||
return http.post("createPersonA", params); | |||||
}, | |||||
/** 修改人员 */ | |||||
update(params: any) { | |||||
return http.put("updatePersionU", params); | |||||
}, | |||||
/** 添加人脸 */ | |||||
addFace(params: SysUserPersonnel.SysUserAvatar) { | |||||
return http.post("addFaceA", params); | |||||
}, | |||||
/** 删除人脸 */ | |||||
deleteFace(params: SysUserPersonnel.SysUserFace) { | |||||
return http.post("deleteFaceD", params); | |||||
} | |||||
}; | |||||
const userKeyPerButtonCode = { | |||||
/** 新增人员 */ | |||||
add: "userManageKeyPersonAdd", | |||||
/** 删除人员 */ | |||||
edit: "userManageKeyPersonEdit", | |||||
/** 删除人员 */ | |||||
delete: "userManageKeyPersonDelete" | |||||
}; | |||||
export { userManageKeyPersonApi, userKeyPerButtonCode }; |
@@ -0,0 +1,54 @@ | |||||
/** | |||||
* @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"; | |||||
const http = moduleRequest("/business/classTeacher/"); | |||||
/** | |||||
* @Description: 单页管理 | |||||
* @Author: SYY | |||||
* @Date: 2023-12-15 15:34:54 | |||||
*/ | |||||
const userManageTeacherApi = { | |||||
/** 查询班主任列表 */ | |||||
page() { | |||||
return http.get("getNoPageList"); | |||||
}, | |||||
/** 获取单页详情 */ | |||||
detail(params: any) { | |||||
return http.get("getInfo", params); | |||||
}, | |||||
/** 删除班主任 */ | |||||
delete(params: any) { | |||||
return http.delete("delete", params); | |||||
}, | |||||
/** 创建班主任 */ | |||||
add(params: any) { | |||||
return http.post("add", params); | |||||
}, | |||||
/** 更新班主任 */ | |||||
update(params: any) { | |||||
return http.put("update", params); | |||||
} | |||||
}; | |||||
const userTeacherButtonCode = { | |||||
/** 新增人员 */ | |||||
add: "userManageClassManageAdd", | |||||
/** 删除人员 */ | |||||
edit: "userManageClassManageEdit", | |||||
/** 删除人员 */ | |||||
delete: "userManageClassManageDelete" | |||||
}; | |||||
export { userManageTeacherApi, userTeacherButtonCode }; |
@@ -26,7 +26,8 @@ | |||||
.el-tree-node.is-current > .el-tree-node__content { | .el-tree-node.is-current > .el-tree-node__content { | ||||
background-color: var(--el-color-primary); | background-color: var(--el-color-primary); | ||||
.el-tree-node__label, | .el-tree-node__label, | ||||
.el-tree-node__expand-icon { | |||||
.el-tree-node__expand-icon, | |||||
.el-dropdown .el-link { | |||||
color: white; | color: white; | ||||
} | } | ||||
.is-leaf { | .is-leaf { | ||||
@@ -56,6 +56,7 @@ interface TreeFilterProps { | |||||
topName?: string; // 顶级分类名称 ==> 非必传,默认为 “全部” | topName?: string; // 顶级分类名称 ==> 非必传,默认为 “全部” | ||||
showAll?: boolean; // 是否显示全部选项 ==> 非必传,默认为 true | showAll?: boolean; // 是否显示全部选项 ==> 非必传,默认为 true | ||||
width: string; | width: string; | ||||
isData?: boolean; | |||||
} | } | ||||
const props = withDefaults(defineProps<TreeFilterProps>(), { | const props = withDefaults(defineProps<TreeFilterProps>(), { | ||||
id: "id", | id: "id", | ||||
@@ -65,7 +66,8 @@ const props = withDefaults(defineProps<TreeFilterProps>(), { | |||||
defaultExpandLevel: 1, | defaultExpandLevel: 1, | ||||
checkStrictly: false, | checkStrictly: false, | ||||
topName: "全部", | topName: "全部", | ||||
showAll: true | |||||
showAll: true, | |||||
isData: false | |||||
}); | }); | ||||
const defaultProps = { | const defaultProps = { | ||||
@@ -98,7 +100,7 @@ watch( | |||||
const setTreeAllData = (data: any) => { | const setTreeAllData = (data: any) => { | ||||
//如果需要显示全部选项就加上全部,否则就拿到什么输出什么 | //如果需要显示全部选项就加上全部,否则就拿到什么输出什么 | ||||
if (props.showAll) { | if (props.showAll) { | ||||
treeAllData.value = [{ id: "", [props.label]: props.topName }, ...data]; | |||||
treeAllData.value = [{ [props.id]: "", [props.label]: props.topName }, ...data]; | |||||
} else { | } else { | ||||
treeAllData.value = data; | treeAllData.value = data; | ||||
} | } | ||||
@@ -156,13 +158,17 @@ const refresh = async () => { | |||||
treeRef.value?.setCheckedKeys([]); | treeRef.value?.setCheckedKeys([]); | ||||
treeRef.value?.setCurrentKey(""); | treeRef.value?.setCurrentKey(""); | ||||
setSelected(); | setSelected(); | ||||
await getRequestData(); | |||||
if (!props.isData) { | |||||
await getRequestData(); | |||||
} | |||||
}; | }; | ||||
const getRequestData = async () => { | const getRequestData = async () => { | ||||
const { data } = await props.requestApi!(); | |||||
treeData.value = data; | |||||
setTreeAllData(data); | |||||
if (!props.isData) { | |||||
const { data } = await props.requestApi!(); | |||||
treeData.value = data; | |||||
setTreeAllData(data); | |||||
} | |||||
}; | }; | ||||
/** 获取默认展开层级 */ | /** 获取默认展开层级 */ | ||||
@@ -27,6 +27,8 @@ declare namespace FormProps { | |||||
disabled?: boolean; | disabled?: boolean; | ||||
/** 行内表单模式 */ | /** 行内表单模式 */ | ||||
inline?: boolean; | inline?: boolean; | ||||
// 树数据 | |||||
treeAllData?: Array<any>; | |||||
/** 表单布局 */ | /** 表单布局 */ | ||||
successful?: () => void; | successful?: () => void; | ||||
} | } | ||||
@@ -15,8 +15,8 @@ | |||||
label-width="auto" | label-width="auto" | ||||
label-suffix=" :" | 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 label="摄像头分组" prop="id"> | |||||
<org-selector v-model:org-value="orgProps.record.id" :org-tree-api="monitorLIVEApi.groupList" :show-all="false" /> | |||||
</s-form-item> | </s-form-item> | ||||
</el-form> | </el-form> | ||||
<template #footer> | <template #footer> | ||||
@@ -28,7 +28,7 @@ | |||||
</template> | </template> | ||||
<script setup lang="ts"> | <script setup lang="ts"> | ||||
import { SysOrg, SysUser, bizOrgApi, bizPositionApi, sysRoleApi, bizUserApi } from "@/api"; | |||||
import { SysOrg, monitorLIVEApi, SysUser, bizOrgApi, bizPositionApi, sysRoleApi, bizUserApi } from "@/api"; | |||||
import { FormOptEnum, SysDictEnum } from "@/enums"; | import { FormOptEnum, SysDictEnum } from "@/enums"; | ||||
import { required } from "@/utils/formRules"; | import { required } from "@/utils/formRules"; | ||||
import { FormInstance } from "element-plus"; | import { FormInstance } from "element-plus"; | ||||
@@ -44,12 +44,13 @@ const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS); | |||||
const orgProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({ | const orgProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({ | ||||
opt: FormOptEnum.ADD, | opt: FormOptEnum.ADD, | ||||
record: {}, | record: {}, | ||||
disabled: false | |||||
disabled: false, | |||||
records: [] | |||||
}); | }); | ||||
// 表单验证规则 | // 表单验证规则 | ||||
const rules = reactive({ | const rules = reactive({ | ||||
parentId: [required("请选择所属学校")] | |||||
id: [required("请选择摄像头分组")] | |||||
}); | }); | ||||
/** | /** | ||||
@@ -57,18 +58,12 @@ const rules = reactive({ | |||||
* @param props 表单参数 | * @param props 表单参数 | ||||
*/ | */ | ||||
function omMove(props: FormProps.Base<SysOrg.SysOrgInfo>) { | function omMove(props: FormProps.Base<SysOrg.SysOrgInfo>) { | ||||
// 合并参数 | |||||
Object.assign(orgProps, props); //合并参数 | Object.assign(orgProps, props); //合并参数 | ||||
if (props.opt == FormOptEnum.ADD) { | |||||
//如果是新增,设置默认值 | |||||
orgProps.record.sortCode = 99; | |||||
// orgProps.record.status = statusOptions[0].value; | |||||
if (orgProps.opt == FormOptEnum.ADD) { | |||||
} | } | ||||
visible.value = true; //显示表单 | visible.value = true; //显示表单 | ||||
if (props.record.id) { | |||||
//如果传了id,就去请求api获取record | |||||
bizOrgApi.detail({ id: props.record.id }).then(res => { | |||||
orgProps.record = res.data; | |||||
}); | |||||
if (orgProps.record.id) { | |||||
} | } | ||||
} | } | ||||
@@ -79,10 +74,13 @@ async function handleSubmit() { | |||||
liveFormRef.value?.validate(async valid => { | liveFormRef.value?.validate(async valid => { | ||||
if (!valid) return; //表单验证失败 | if (!valid) return; //表单验证失败 | ||||
console.log(orgProps); | console.log(orgProps); | ||||
return; | |||||
let params: any = { | |||||
id: orgProps.record.id, | |||||
ids: orgProps.records | |||||
}; | |||||
//提交表单 | //提交表单 | ||||
await bizOrgApi | |||||
.submitForm(orgProps.record, orgProps.record.id != undefined) | |||||
await monitorLIVEApi | |||||
.setGroup(params) | |||||
.then(() => { | .then(() => { | ||||
orgProps.successful!(); //调用父组件的successful方法 | orgProps.successful!(); //调用父组件的successful方法 | ||||
}) | }) | ||||
@@ -1,48 +1,53 @@ | |||||
ul,li { | |||||
list-style: none; | |||||
padding: 0; | |||||
margin: 0; | |||||
} | |||||
.treeBox { | .treeBox { | ||||
box-sizing: border-box; | box-sizing: border-box; | ||||
width: 280px; | |||||
flex-shrink: 1; | |||||
width: 320px; | |||||
height: 100%; | height: 100%; | ||||
padding: 14px; | padding: 14px; | ||||
margin-right: 10px; | margin-right: 10px; | ||||
flex-shrink: 1; | |||||
.title { | .title { | ||||
margin: 0 0 15px; | margin: 0 0 15px; | ||||
font-size: 18px; | font-size: 18px; | ||||
font-weight: bold; | font-weight: bold; | ||||
color: var(--el-color-info-dark-2); | color: var(--el-color-info-dark-2); | ||||
letter-spacing: 0.5px; | letter-spacing: 0.5px; | ||||
} | |||||
.btn { | |||||
} | } | ||||
.treeContent { | .treeContent { | ||||
padding: 10px 0; | padding: 10px 0; | ||||
// height: calc(100% - 100px); | // height: calc(100% - 100px); | ||||
// overflow: auto; | // overflow: auto; | ||||
.el-tree-node__content { | .el-tree-node__content { | ||||
height: 33px; | height: 33px; | ||||
} | } | ||||
.custom-tree-node { | .custom-tree-node { | ||||
flex: 1; | |||||
display: flex; | display: flex; | ||||
flex: 1; | |||||
align-items: center; | align-items: center; | ||||
justify-content: space-between; | justify-content: space-between; | ||||
font-size: 14px; | |||||
padding-right: 8px; | padding-right: 8px; | ||||
font-size: 14px; | |||||
.node-label { | .node-label { | ||||
width: 100px; | width: 100px; | ||||
overflow: hidden; | overflow: hidden; | ||||
font-size: 16px; | |||||
text-overflow: ellipsis; | text-overflow: ellipsis; | ||||
white-space: nowrap; | white-space: nowrap; | ||||
font-size: 16px; | |||||
} | } | ||||
} | |||||
:deep(.el-tree--highlight-current) { | |||||
.el-tree-node.is-current > .el-tree-node__content { | |||||
color: white; | |||||
background-color: var(--el-color-primary); | |||||
// .el-tree-node__label, | |||||
// .el-tree-node__expand-icon { | |||||
// color: white; | |||||
// } | |||||
// .is-leaf { | |||||
// color: transparent; | |||||
// } | |||||
} | |||||
} | } | ||||
:deep(.el-tree-node__content) { | :deep(.el-tree-node__content) { | ||||
height: 50px; | height: 50px; | ||||
@@ -74,5 +79,5 @@ ul,li { | |||||
} | } | ||||
} | } | ||||
.table-box { | .table-box { | ||||
width: calc(100% - 280px); | |||||
width: calc(100% - 320px); | |||||
} | } |
@@ -1,44 +1,39 @@ | |||||
<!-- | <!-- | ||||
* @Description: 告警管理 | |||||
* @Author: huguodong | |||||
* @Date: 2023-12-15 15:44:05 | |||||
* @Description: 监控管理 | |||||
* @Author: wangwenpei | |||||
* @Date: 2024-07-18 15:44:05 | |||||
!--> | !--> | ||||
<template> | <template> | ||||
<div class="main-box"> | <div class="main-box"> | ||||
<div class="card treeBox"> | <div class="card treeBox"> | ||||
<p class="title">摄像头分组管理</p> | <p class="title">摄像头分组管理</p> | ||||
<div class="btn"> | <div class="btn"> | ||||
<el-button @click="append('add', {})" type="primary">添加分组</el-button> | |||||
<el-button icon="CirclePlus" @click="append('add', {})" type="primary">添加分组</el-button> | |||||
</div> | </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"> | <div class="treeContent"> | ||||
<el-input v-model="filterText" style="width: 240px" placeholder="请输入关键字" /> | |||||
<el-tree | <el-tree | ||||
style="max-width: 600px" | style="max-width: 600px" | ||||
ref="treeRef" | |||||
:data="treeData" | :data="treeData" | ||||
node-key="id" | node-key="id" | ||||
default-expand-all | default-expand-all | ||||
:props="defaultProps" | |||||
:expand-on-click-node="false" | :expand-on-click-node="false" | ||||
:check-on-click-node="true" | :check-on-click-node="true" | ||||
:highlight-current="true" | :highlight-current="true" | ||||
:filter-node-method="filterNode" | |||||
:current-node-key="defaultHighlightKey" | |||||
@node-click="handleNodeClick" | @node-click="handleNodeClick" | ||||
> | > | ||||
<template #default="{ node, data }"> | <template #default="{ node, data }"> | ||||
<span class="custom-tree-node"> | <span class="custom-tree-node"> | ||||
<span class="node-label" :title="node.label">{{ node.label }}</span> | <span class="node-label" :title="node.label">{{ node.label }}</span> | ||||
<span v-if="data.id && data.id != '-1'"> | <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> | |||||
<el-icon size="16" title="添加" @click.stop="append('add', data)"><CirclePlus /></el-icon> | |||||
<el-icon size="16" title="编辑" @click.stop="append('edit', data)" style="margin-left: 8px"><Edit /></el-icon> | |||||
<el-icon size="16" title="设置推送人" @click.stop="pushPerson('push', data)" style="margin-left: 8px"><UserFilled /></el-icon> | |||||
<el-icon size="16" title="删除" @click.stop="remove(data.id, '删除该分组')" style="margin-left: 8px"><Delete /></el-icon> | |||||
</span> | </span> | ||||
</span> | </span> | ||||
</template> | </template> | ||||
@@ -46,7 +41,7 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="table-box"> | <div class="table-box"> | ||||
<ProTable ref="proTable" title="视频列表" :columns="columns" :request-api="monitorLIVEApi.page"> | |||||
<ProTable ref="proTable" title="视频列表" :columns="columns" :request-api="monitorLIVEApi.list"> | |||||
<!-- 表格 header 按钮 --> | <!-- 表格 header 按钮 --> | ||||
<template #tableHeader="scope"> | <template #tableHeader="scope"> | ||||
<s-button v-auth="monitorLiveButtonCode.add" suffix="摄像头" @click="onOpen(FormOptEnum.ADD)" /> | <s-button v-auth="monitorLiveButtonCode.add" suffix="摄像头" @click="onOpen(FormOptEnum.ADD)" /> | ||||
@@ -58,21 +53,9 @@ | |||||
:disabled="!scope.isSelected" | :disabled="!scope.isSelected" | ||||
@click="onDelete(scope.selectedListIds, '删除所选数据')" | @click="onDelete(scope.selectedListIds, '删除所选数据')" | ||||
/> | /> | ||||
<el-button plain @click="omMove(FormOptEnum.ADD)" type="success">移动至分组</el-button> | |||||
<el-button :disabled="!scope.isSelected" plain @click="omMove(FormOptEnum.ADD, scope.selectedListIds)" type="success">移动至分组</el-button> | |||||
</template> | </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"> | <template #operation="scope"> | ||||
<s-button link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)">编辑</s-button> | <s-button link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)">编辑</s-button> | ||||
@@ -103,7 +86,7 @@ | |||||
<!-- 移动至分组 --> | <!-- 移动至分组 --> | ||||
<moveForm ref="moveFormRef" /> | <moveForm ref="moveFormRef" /> | ||||
<!-- 视频详情 --> | <!-- 视频详情 --> | ||||
<el-dialog v-model="visible" :title="detailData.title" width="830px" :before-close="closeGroup"> | |||||
<el-dialog v-model="visible" :title="detailData.title" width="830px" :before-close="handleClose"> | |||||
<div> | <div> | ||||
<div class="dialogHeader"> | <div class="dialogHeader"> | ||||
<div></div> | <div></div> | ||||
@@ -128,187 +111,195 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</template> | </template> | ||||
<script setup lang="tsx" name="sysSpa"> | |||||
import VideoPlay from "@/components/VideoPlay/videoplay.vue"; | |||||
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 './ali.js' | |||||
const groupVisible = ref(false); //是否显示表单 | |||||
const groupTitle = ref('新增分组'); | |||||
const groupForm = reactive({ | |||||
name: '' | |||||
}); | |||||
const groupRules = ref({ | |||||
name: [{ required: true, message: '请输入分组名称', trigger: 'blur' }] | |||||
<script setup lang="tsx" name="sysSpa"> | |||||
import VideoPlay from "@/components/VideoPlay/videoplay.vue"; | |||||
import { ElMessage, ElMessageBox, ElTree } 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 "./ali.js"; | |||||
// 分组字段配置 | |||||
const defaultProps = { | |||||
children: "children", | |||||
label: "name" | |||||
}; | |||||
// 默认高亮 | |||||
const defaultHighlightKey = ref(""); | |||||
let treeDada1 = ref([ | |||||
{ | |||||
id: "", | |||||
name: "全部" | |||||
} | |||||
]); | |||||
const treeData = ref<any>([]); | |||||
var treeRef = ref<InstanceType<typeof ElTree>>(); | |||||
// 获取摄像头分组 | |||||
function getGroupList() { | |||||
monitorLIVEApi.groupList({}).then((res:any) => { | |||||
if (res.code == 200) { | |||||
treeData.value = [...treeDada1.value, ...res.data]; | |||||
} | |||||
}); | }); | ||||
const groupFormRef = ref(null); | |||||
const closeGroup = () => { | |||||
groupVisible.value = false; | |||||
groupFormRef.value.resetFields(); | |||||
groupForm.name = '' | |||||
} | |||||
const groupVisible = ref(false); //是否显示表单 | |||||
const groupTitle = ref("新增分组"); | |||||
const groupForm = reactive({ | |||||
name: "", | |||||
id: "" | |||||
}); | |||||
const groupRules = ref({ | |||||
name: [{ required: true, message: "请输入分组名称", trigger: "blur" }] | |||||
}); | |||||
const groupFormRef = ref<any>(null); | |||||
const closeGroup = () => { | |||||
groupVisible.value = false; | |||||
groupFormRef.value.resetFields(); | |||||
groupForm.name = ""; | |||||
}; | |||||
// 新增编辑分组 | |||||
const append = (type: string, data: Tree) => { | |||||
groupVisible.value = true; | |||||
if (type == "edit") { | |||||
groupForm.name = data.name; | |||||
groupForm.id = data.id; | |||||
} | } | ||||
const onSubmit = () => { | |||||
groupFormRef.value.validate((valid:any) => { | |||||
if (valid) { | |||||
closeGroup() | |||||
ElMessage.success('提交成功'); | |||||
}; | |||||
// 删除分组 | |||||
async function remove(id: string[], msg: string) { | |||||
await useHandleData(monitorLIVEApi.deleteGroup, { id }, msg); | |||||
getGroupList(); | |||||
} | |||||
// 提交分组 | |||||
const onSubmit = () => { | |||||
groupFormRef.value.validate((valid: any) => { | |||||
if (valid) { | |||||
let params: any = reactive({ | |||||
id: "", | |||||
name: groupForm.name | |||||
}); | |||||
if (!groupForm.id) { | |||||
monitorLIVEApi.addGroup(params).then(res => { | |||||
if (res.code == 200) { | |||||
getGroupList(); | |||||
closeGroup(); | |||||
} | |||||
}); | |||||
} else { | } else { | ||||
return false; | |||||
params.id = groupForm.id; | |||||
monitorLIVEApi.updateGroup(params).then(res => { | |||||
if (res.code == 200) { | |||||
getGroupList(); | |||||
closeGroup(); | |||||
} | |||||
}); | |||||
} | } | ||||
}); | |||||
}; | |||||
// 摄像头分组 | |||||
closeGroup(); | |||||
} else { | |||||
return false; | |||||
} | |||||
}); | |||||
}; | |||||
// 摄像头分组过滤 | |||||
interface Tree { | interface Tree { | ||||
id: any | |||||
label: string | |||||
children?: Tree[] | |||||
[key: string]: any; | |||||
id: any; | |||||
label: string; | |||||
children?: Tree[]; | |||||
} | } | ||||
let id = 1000 | |||||
const filterText = ref(""); | |||||
watch(filterText, val => { | |||||
treeRef.value!.filter(val); | |||||
}); | |||||
const filterNode = (value: string, data: Tree) => { | |||||
if (!value) return true; | |||||
return data.name.includes(value); | |||||
}; | |||||
const handleNodeClick = (data: Tree) => { | const handleNodeClick = (data: Tree) => { | ||||
console.log(data) | |||||
} | |||||
console.log(data); | |||||
// personSetId.value = val | |||||
proTable.value!.pageable.pageNum = 1; | |||||
proTable.value!.searchParam.groupId = data.id; | |||||
proTable.value!.search(); | |||||
}; | |||||
// 设置分组推送人 | |||||
// async function pushPerson(type:string,data: Tree) { | |||||
// } | |||||
const visible = ref(false); //是否显示表单 | |||||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||||
const proTable = ref<ProTableInstance>(); | |||||
const dictStore = useDictStore(); | |||||
const treeData = ref<Tree[]>([ | |||||
// 表格配置项 | |||||
const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||||
{ type: "selection", fixed: "left", width: 80 }, | |||||
{ | { | ||||
id: '', | |||||
label: '全部', | |||||
prop: "sensorId", | |||||
label: "摄像头ID" | |||||
}, | }, | ||||
{ | { | ||||
id: '-1', | |||||
label: '无分组', | |||||
prop: "sensorName", | |||||
label: "摄像头名称" | |||||
}, | }, | ||||
{ | { | ||||
id: 1, | |||||
label: '走廊', | |||||
prop: "fieldName", | |||||
label: "所属学校" | |||||
}, | }, | ||||
{ | { | ||||
id: 2, | |||||
label: '大厅', | |||||
prop: "directUrlIp", | |||||
label: "设备ip" | |||||
}, | }, | ||||
{ | { | ||||
id: 3, | |||||
label: '厨房', | |||||
prop: "resWidth", | |||||
label: "分辨率", | |||||
render: row => { | |||||
return row.row.resWidth + "*" + row.row.resHeight; | |||||
} | |||||
}, | }, | ||||
]) | |||||
// 新增分组 | |||||
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; | |||||
} | |||||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | |||||
]; | |||||
// 移动分组禁用 | |||||
const moveFormRef = ref<InstanceType<typeof Form> | null>(null); | |||||
function omMove(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
console.log(record,"record") | |||||
// const newChild = { id: id++, label: 'testtest', children: [] } | |||||
// if (!data.children) { | |||||
// data.children = [] | |||||
// } | |||||
// data.children.push(newChild) | |||||
// treeData.value = [...treeData.value] | |||||
moveFormRef.value?.omMove({ opt: opt, records: record, successful: RefreshTable }); | |||||
} | } | ||||
// 删除分组 | |||||
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] | |||||
/** | |||||
* 删除 | |||||
* @param ids id数组 | |||||
*/ | |||||
async function onDelete(ids: string[], msg: string) { | |||||
return; | |||||
// 二次确认 => 请求api => 刷新表格 | |||||
await useHandleData(monitorLIVEApi.delete, { ids }, msg); | |||||
RefreshTable(); | |||||
} | } | ||||
// 设置分组推送人 | |||||
// async function pushPerson(type:string,data: Tree) { | |||||
// } | |||||
const visible = ref(false); //是否显示表单 | |||||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||||
const proTable = ref<ProTableInstance>(); | |||||
const dictStore = useDictStore(); | |||||
// 表格配置项 | |||||
const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||||
{ type: "selection", fixed: "left", width: 80 }, | |||||
// { prop: "searchKey", label: "关键字", search: { el: "input" }, isShow: false }, | |||||
// { | |||||
// prop: "poiId", | |||||
// label: "所属学校", | |||||
// render: () => { | |||||
// return "演示学校"; | |||||
// } | |||||
// }, | |||||
{ | |||||
prop: "sensorId", | |||||
label: "摄像头ID", | |||||
// render: () => { | |||||
// return "楼道"; | |||||
// } | |||||
}, | |||||
{ | |||||
prop: "sensorName", | |||||
label: "摄像头名称", | |||||
// render: () => { | |||||
// return "楼道"; | |||||
// } | |||||
}, | |||||
{ | |||||
prop: "fieldName", | |||||
label: "所属学校", | |||||
}, | |||||
{ | |||||
prop: "directUrlIp", | |||||
label: "设备ip", | |||||
}, | |||||
{ | |||||
prop: "resWidth", | |||||
label: "分辨率", | |||||
render: (row) => { | |||||
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(); | |||||
} | } | ||||
/** | |||||
* 刷新表格 | |||||
*/ | |||||
function RefreshTable() { | |||||
proTable.value?.refresh(); | |||||
} | |||||
// 表单引用 | |||||
// 表单引用 | |||||
const formRef = ref<InstanceType<typeof Form> | null>(null); | const formRef = ref<InstanceType<typeof Form> | null>(null); | ||||
/** | |||||
/** | |||||
* 打开表单 | * 打开表单 | ||||
* @param opt 操作类型 | * @param opt 操作类型 | ||||
* @param record 记录 | * @param record 记录 | ||||
@@ -318,9 +309,9 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
} | } | ||||
// 人员选择 | // 人员选择 | ||||
// 表单引用 | |||||
const userFormRef = ref<InstanceType<typeof Form> | null>(null); | |||||
/** | |||||
// 表单引用 | |||||
const userFormRef = ref<InstanceType<typeof Form> | null>(null); | |||||
/** | |||||
* 打开表单 | * 打开表单 | ||||
* @param opt 操作类型 | * @param opt 操作类型 | ||||
* @param record 记录 | * @param record 记录 | ||||
@@ -328,183 +319,179 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
function pushPerson(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | function pushPerson(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | ||||
userFormRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | userFormRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | ||||
} | } | ||||
// 详情数据 | |||||
// let detailData: globalThis.Ref<{}> | |||||
let detailData = reactive({ | |||||
title: '', | |||||
videoUrl: '', | |||||
sensorId: '', | |||||
streamId: '', | |||||
videoToken:'', | |||||
videoType: '' | |||||
}); | |||||
function onDetail(row: any) { | |||||
visible.value = true; | |||||
detailData.sensorId = row.sensorId; | |||||
detailData.title = row.sensorName + '('+ row.sensorId + ')'; | |||||
getUrl() | |||||
} | |||||
// 刷新 | |||||
// var showVideo = false | |||||
const showVideo = ref(false) | |||||
function refreshUrl() { | |||||
stopUrl(); | |||||
showVideo.value = false; | |||||
setTimeout(()=> { | |||||
showVideo.value = true; | |||||
getUrl() | |||||
},1000) | |||||
} | |||||
let num = 1; | |||||
function getUrl() { | |||||
detailData.videoType = 'm3u8' | |||||
setTimeout(async ()=> { | |||||
await monitorLIVEApi.detail({ sensorId: detailData.sensorId}).then(res => { | |||||
let { code, data } = res; | |||||
if (code == 200) { | |||||
// window.location.origin+ | |||||
detailData.videoUrl = ''; | |||||
detailData.videoUrl= data.rtsPullStreamUrls[0].url; | |||||
// detailData.videoUrl = data.pullStreamUrls[2].url; | |||||
detailData.streamId = data.streamId; | |||||
detailData.videoToken = data.videoToken; | |||||
// getvideo1() | |||||
getvideo2() | |||||
} | |||||
}); | |||||
}) | |||||
} | |||||
function getvideo1() { | |||||
let aliRts = new AliRTS() | |||||
// let url = detailData.videoUrl.replace('http://rts-pull-live.deepeleph.com', '/Files') | |||||
// console.log(url,888) | |||||
let pullStreamUrl = detailData.videoUrl; | |||||
const mediaEle = document.querySelector('video'); | |||||
aliRts.on("onError", (err) => { | |||||
console.log(`errorCode: ${err.errorCode}`); | |||||
console.log(`message: ${err.message}`); | |||||
}) | |||||
// 详情数据 | |||||
// let detailData: globalThis.Ref<{}> | |||||
let detailData = reactive({ | |||||
title: "", | |||||
videoUrl: "", | |||||
sensorId: "", | |||||
streamId: "", | |||||
videoToken: "", | |||||
videoType: "" | |||||
}); | |||||
function onDetail(row: any) { | |||||
visible.value = true; | |||||
detailData.sensorId = row.sensorId; | |||||
detailData.title = row.sensorName + "(" + row.sensorId + ")"; | |||||
getUrl(); | |||||
} | |||||
// 刷新 | |||||
const showVideo = ref(false); | |||||
function refreshUrl() { | |||||
stopUrl(); | |||||
showVideo.value = false; | |||||
setTimeout(() => { | |||||
showVideo.value = true; | |||||
getUrl(); | |||||
}, 1000); | |||||
} | |||||
let num = 1; | |||||
function getUrl() { | |||||
detailData.videoType = "m3u8"; | |||||
setTimeout(async () => { | |||||
await monitorLIVEApi.detail({ sensorId: detailData.sensorId }).then((res:any) => { | |||||
let { code, data } = res; | |||||
const PLAY_EVENT = { | |||||
CANPLAY: "canplay", | |||||
WAITING: "waiting", | |||||
PLAYING: "playing" | |||||
} | |||||
if (code == '200') { | |||||
// window.location.origin+ | |||||
detailData.videoUrl = ""; | |||||
detailData.videoUrl = data.rtsPullStreamUrls[0].url; | |||||
// detailData.videoUrl = data.pullStreamUrls[2].url; | |||||
detailData.streamId = data.streamId; | |||||
detailData.videoToken = data.videoToken; | |||||
// getvideo1() | |||||
getvideo2(); | |||||
} | |||||
}); | |||||
}); | |||||
} | |||||
function getvideo1() { | |||||
let aliRts = new AliRTS(); | |||||
let pullStreamUrl = detailData.videoUrl; | |||||
const mediaEle = document.querySelector("video"); | |||||
aliRts.on("onError", err => { | |||||
console.log(`errorCode: ${err.errorCode}`); | |||||
console.log(`message: ${err.message}`); | |||||
}); | |||||
aliRts.on('onPlayEvent', (play) => { | |||||
console.log(">>play.event:" + play.event); | |||||
if (play.event === PLAY_EVENT.CANPLAY) { | |||||
// 拉流可以播放 | |||||
console.log('可以播放') | |||||
} else if (play.event === PLAY_EVENT.WAITING) { | |||||
// 拉流卡顿等待缓冲中 (仅Chrome) | |||||
console.log('拉流卡顿等待缓冲中') | |||||
const PLAY_EVENT = { | |||||
CANPLAY: "canplay", | |||||
WAITING: "waiting", | |||||
PLAYING: "playing" | |||||
}; | |||||
} else if (play.event === PLAY_EVENT.PLAYING) { | |||||
// 拉流卡顿结束恢复播放 (仅Chrome) | |||||
console.log('拉流卡顿结束恢复播放') | |||||
} | |||||
}); | |||||
aliRts.startLiveStream(pullStreamUrl, mediaEle); | |||||
// aliRts.startLiveStream(pullStreamUrl, mediaEle); | |||||
} | |||||
let player = ref(null); | |||||
function getvideo2() { | |||||
var options = { | |||||
"id": "player-con", | |||||
"source": detailData.videoUrl + '&subaudio=no&jitterbuffer=6000', | |||||
// "rtsFallbackSource": "降级地址,如HLS", | |||||
"width": "100%", | |||||
"height": "600px", | |||||
"autoplay": true, | |||||
"isLive": true, | |||||
"playsinline": true, | |||||
"skipRtsSupportCheck": false, // 对于不在 https://help.aliyun.com/document_detail/397569.html 中的浏览器,可以传 true 跳过检查,强制使用 RTS(有风险,需要自测保证) | |||||
aliRts.on("onPlayEvent", play => { | |||||
console.log(">>play.event:" + play.event); | |||||
if (play.event === PLAY_EVENT.CANPLAY) { | |||||
// 拉流可以播放 | |||||
console.log("可以播放"); | |||||
} else if (play.event === PLAY_EVENT.WAITING) { | |||||
// 拉流卡顿等待缓冲中 (仅Chrome) | |||||
console.log("拉流卡顿等待缓冲中"); | |||||
} else if (play.event === PLAY_EVENT.PLAYING) { | |||||
// 拉流卡顿结束恢复播放 (仅Chrome) | |||||
console.log("拉流卡顿结束恢复播放"); | |||||
} | |||||
}); | |||||
aliRts.startLiveStream(pullStreamUrl, mediaEle); | |||||
// aliRts.startLiveStream(pullStreamUrl, mediaEle); | |||||
} | |||||
let player = ref<any>(null); | |||||
function getvideo2() { | |||||
var options = { | |||||
id: "player-con", | |||||
source: detailData.videoUrl + "&subaudio=no&jitterbuffer=6000", | |||||
// "rtsFallbackSource": "降级地址,如HLS", | |||||
width: "100%", | |||||
height: "600px", | |||||
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: 1500, | |||||
/** | |||||
* RTS 拉流超时会默认重试 | |||||
* 以下两个参数用来控制降级之前的重试策略,比如 3000 毫秒超时,重试一次,如果再拉不到流就降级,那么总共等待 6000 毫秒降级 | |||||
**/ | |||||
// RTS 多久拉不到流会重试,默认 3000 ms | |||||
rtsLoadDataTimeout: 1500, | |||||
// RTS 拉不到流重试的次数,默认 5,此参数建议设为 1,即重试 1 次后降级,可以减少降级等待时间 | |||||
liveRetry: 1, | |||||
}; | |||||
// RTS 拉不到流重试的次数,默认 5,此参数建议设为 1,即重试 1 次后降级,可以减少降级等待时间 | |||||
liveRetry: 1 | |||||
}; | |||||
player.value = new Aliplayer(options, function () {/* player ready */ }) | |||||
player.value = new Aliplayer(options, function () { | |||||
/* player ready */ | |||||
}); | |||||
// 降级时会触发此事件 | |||||
player.value.on('rtsFallback', function (event) { | |||||
console.log('[EVENT]rtsFallback', event.paramData); | |||||
// event.paramData.reason 降级的原因 | |||||
// event.paramData.fallbackUrl 降级到的地址 | |||||
}) | |||||
// 降级时会触发此事件 | |||||
player.value.on("rtsFallback", function (event:any) { | |||||
console.log("[EVENT]rtsFallback", event.paramData); | |||||
// event.paramData.reason 降级的原因 | |||||
// event.paramData.fallbackUrl 降级到的地址 | |||||
}); | |||||
player.value.on('error', function (event) { | |||||
console.log('[EVENT]error', event.paramData); | |||||
}) | |||||
player.value.on("error", function (event:any) { | |||||
console.log("[EVENT]error", event.paramData); | |||||
}); | |||||
// 当RTS拉流成功时触发,通过订阅该事件,可以获取到RTS TraceId | |||||
player.value.on('rtsTraceId', function (data) { | |||||
console.log('[EVENT]rtsTraceId', data.paramData); | |||||
// event.paramData.traceId 拉流的TraceId | |||||
// event.paramData.source 当前RTS流的播放地址 | |||||
}) | |||||
// 当RTS拉流成功时触发,通过订阅该事件,可以获取到RTS TraceId | |||||
player.value.on("rtsTraceId", function (data:any) { | |||||
console.log("[EVENT]rtsTraceId", data.paramData); | |||||
// event.paramData.traceId 拉流的TraceId | |||||
// event.paramData.source 当前RTS流的播放地址 | |||||
}); | |||||
} | |||||
const handleClose = () => { | |||||
visible.value = false; | |||||
detailData.videoUrl = ""; | |||||
detailData.videoType = ""; | |||||
stopUrl(); | |||||
if (player.value) { | |||||
player.value.dispose(); | |||||
} | } | ||||
const handleClose = () => { | |||||
visible.value = false; | |||||
detailData.videoUrl = '' | |||||
detailData.videoType = '' | |||||
stopUrl(); | |||||
if(player.value) { | |||||
player.value.dispose(); | |||||
} | |||||
}; | |||||
function stopUrl() { | |||||
detailData.videoUrl = '' | |||||
if(player.value) { | |||||
player.value.dispose(); | |||||
} | |||||
let params = { | |||||
sensorId: detailData.sensorId, | |||||
streamId: detailData.streamId, | |||||
videoToken: detailData.videoToken | |||||
} | |||||
setTimeout(async ()=> { | |||||
await monitorLIVEApi.stopUrl(params).then(res => { | |||||
let { code, data, msg } = res; | |||||
if (code == 200) { | |||||
// ElMessage.success(msg); | |||||
} | |||||
}); | |||||
}) | |||||
}; | |||||
function stopUrl() { | |||||
detailData.videoUrl = ""; | |||||
if (player.value) { | |||||
player.value.dispose(); | |||||
} | } | ||||
</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"); | |||||
let params = { | |||||
sensorId: detailData.sensorId, | |||||
streamId: detailData.streamId, | |||||
videoToken: detailData.videoToken | |||||
}; | |||||
setTimeout(async () => { | |||||
await monitorLIVEApi.stopUrl(params).then((res:any) => { | |||||
let { code, data, msg } = res; | |||||
if (code == 200) { | |||||
// ElMessage.success(msg); | |||||
} | |||||
}); | |||||
}); | |||||
} | |||||
onMounted(() => { | |||||
getGroupList(); | |||||
}); | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
@import "https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css"; | |||||
@import "./index.scss"; | @import "./index.scss"; | ||||
.dialogHeader { | .dialogHeader { | ||||
display: flex; | display: flex; | ||||
justify-content: space-between; | |||||
align-items: center; | align-items: center; | ||||
justify-content: space-between; | |||||
margin: 20px; | margin: 20px; | ||||
color: #409efc; | |||||
font-size: 18px; | font-size: 18px; | ||||
color: #409efc; | |||||
cursor: pointer; | cursor: pointer; | ||||
.dialogBtn { | .dialogBtn { | ||||
display: flex; | display: flex; | ||||
justify-content: space-between; | |||||
align-items: center; | align-items: center; | ||||
justify-content: space-between; | |||||
} | } | ||||
} | } | ||||
</style> | </style> | ||||
@@ -0,0 +1,175 @@ | |||||
<!-- | |||||
* @Description: 告警管理 | |||||
* @Author: huguodong | |||||
* @Date: 2023-12-15 15:44:05 | |||||
!--> | |||||
<template> | |||||
<div class="table-box"> | |||||
<ProTable ref="proTable" title="告警列表" :columns="columns" :data="tableData"> | |||||
<!-- 表格 header 按钮 --> | |||||
<!-- 表格 菜单类型 按钮 --> | |||||
<!-- 状态 --> | |||||
<template #status="scope"> | |||||
<el-tag v-if="scope.row.status === 1" type="success">已部署</el-tag> | |||||
<el-tag v-else type="danger">未部署</el-tag> | |||||
</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> | |||||
</ProTable> | |||||
</div> | |||||
</template> | |||||
<script setup lang="tsx" name="sysSpa"> | |||||
import { warnZJRQApi } 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 { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | |||||
import { dividerDark } from "naive-ui"; | |||||
const visible = ref(false); //是否显示表单 | |||||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||||
const proTable = ref<ProTableInstance>(); | |||||
const dictStore = useDictStore(); | |||||
// 表格配置项 | |||||
const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||||
{ | |||||
prop: "name", | |||||
label: "模型名称", | |||||
// render: () => { | |||||
// return "演示学校"; | |||||
// } | |||||
}, | |||||
{ | |||||
prop: "type", | |||||
label: "模型类型", | |||||
// render: () => { | |||||
// return "楼道"; | |||||
// } | |||||
}, | |||||
{ | |||||
prop: "core", | |||||
label: "分数", | |||||
}, | |||||
{ | |||||
prop: "time", | |||||
label: "训练时间", | |||||
}, | |||||
{ | |||||
prop: "status", | |||||
label: "部署状态", | |||||
}, | |||||
{ | |||||
prop: "isshow", | |||||
label: "训练模型开关", | |||||
render: scope => { | |||||
return ( | |||||
<el-switch | |||||
v-model={scope.row.isshow} | |||||
onChange={() => { | |||||
switchChange(scope.row) | |||||
}} | |||||
/> | |||||
); | |||||
} | |||||
}, | |||||
{ | |||||
prop: "ishandle", | |||||
label: "实训业务开关", | |||||
render: scope => { | |||||
return ( | |||||
<el-switch | |||||
v-model={scope.row.ishandle} | |||||
onChange={() => { | |||||
switchChange(scope.row) | |||||
}} | |||||
/> | |||||
); | |||||
} | |||||
}, | |||||
]; | |||||
function switchChange(row: object) { | |||||
console.log(row) | |||||
RefreshTable(); | |||||
} | |||||
const tableData = ref([ | |||||
{ | |||||
name: '无人机巡检算法', | |||||
type: "物体分类", | |||||
core: "95", | |||||
time: "2024-02-10 15:00:25", | |||||
status: 1, | |||||
isshow: true, | |||||
ishandle: true | |||||
}, | |||||
{ | |||||
name: '越界检测', | |||||
type: "物体分类", | |||||
core: "89", | |||||
time: "2024-03-10 16:00:25", | |||||
status: 1, | |||||
isshow: true, | |||||
ishandle: false | |||||
}, | |||||
{ | |||||
name: '口罩识别', | |||||
type: "物体分类", | |||||
core: "80", | |||||
time: "2024-05-26 15:10:00", | |||||
status: 1, | |||||
isshow: true, | |||||
ishandle: true | |||||
}, | |||||
{ | |||||
name: '监控室脱岗检测', | |||||
type: "边界检测", | |||||
core: "80", | |||||
time: "2024-08-10 16:20:25", | |||||
status: 0, | |||||
isshow: true, | |||||
ishandle: true | |||||
}, | |||||
{ | |||||
name: '反光衣识别', | |||||
type: "物体分类", | |||||
core: "70", | |||||
time: "2024-06-02 18:00:25", | |||||
status: 1, | |||||
isshow: true, | |||||
ishandle: true | |||||
}, | |||||
]); | |||||
/** | |||||
* 刷新表格 | |||||
*/ | |||||
function RefreshTable() { | |||||
proTable.value?.refresh(); | |||||
} | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
.detailpic { | |||||
width: 800px; | |||||
object-fit: cover; | |||||
} | |||||
.linebox { | |||||
margin-top: 20px; | |||||
} | |||||
</style> | |||||
@@ -4,52 +4,89 @@ | |||||
* @Date: 2023-12-15 15:44:05 | * @Date: 2023-12-15 15:44:05 | ||||
!--> | !--> | ||||
<template> | <template> | ||||
<div class="card content-main"> | |||||
<el-collapse accordion> | |||||
<el-collapse-item :name="ins" v-for="(item, ins) in warnGroupList" :key="item.cameraId[0]"> | |||||
<template #title> | |||||
<div class="collapse-title" @click.stop=""> | |||||
<div class="titlemodel">{{ item.name }} {{ item.cameraId[0] }}</div> | |||||
<div class="btns"> | |||||
<el-switch v-model="item.state" @change="stateChange" /> | |||||
<div class="abilityBox"> | |||||
<TreeFilter ref="treeFilter" label="name" id="id" width="300px" :request-api="monitorLIVEApi.groupList" @change="changeGroup"> </TreeFilter> | |||||
<TreeFilter ref="treeFilters" label="sensorName" id="sensorId" :isData="true" width="300px" :data="videoList" @change="changeVideo"> </TreeFilter> | |||||
<div class="card content-main"> | |||||
<el-collapse accordion> | |||||
<el-collapse-item :name="ins" v-for="(item, ins) in warnGroupList" :key="item.cameraId[0]"> | |||||
<template #title> | |||||
<div class="collapse-title" @click.stop=""> | |||||
<div class="titlemodel">{{ item.name }} {{ item.cameraId[0] }}</div> | |||||
<div class="btns"> | |||||
<el-switch v-model="item.state" @change="stateChange" /> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | |||||
</template> | |||||
<div class="collapse-content"> | |||||
<!-- <div class="contentinfo" v-for="(v, index) in item.subset" :key="v.code"> | |||||
</template> | |||||
<div class="collapse-content"> | |||||
<!-- <div class="contentinfo" v-for="(v, index) in item.subset" :key="v.code"> | |||||
<div class="modellabel">{{ v.name }}</div> | <div class="modellabel">{{ v.name }}</div> | ||||
<div class="btns"> | <div class="btns"> | ||||
<el-switch v-model="v.state" active-text="开启" inactive-text="关闭" /> | <el-switch v-model="v.state" active-text="开启" inactive-text="关闭" /> | ||||
</div> | </div> | ||||
</div> --> | </div> --> | ||||
<el-row :gutter="20"> | |||||
<el-col :span="6" v-for="(v, index) in item.subset" :key="v.code"> | |||||
<div class="contentinfo"> | |||||
<div class="modellabel">{{ v.name }}</div> | |||||
<el-row :gutter="20"> | |||||
<el-col :span="6" v-for="(v, index) in item.subset" :key="v.code"> | |||||
<div class="contentinfo"> | |||||
<div class="modellabel">{{ v.name }}</div> | |||||
<div class="btns"> | |||||
<el-switch v-model="v.state" @change="stateChange" /> | |||||
<div class="btns"> | |||||
<el-switch v-model="v.state" @change="stateChange" /> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | |||||
</el-col> | |||||
</el-row> | |||||
</div> | |||||
</el-collapse-item> | |||||
</el-collapse> | |||||
</el-col> | |||||
</el-row> | |||||
</div> | |||||
</el-collapse-item> | |||||
</el-collapse> | |||||
</div> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script setup lang="tsx" name="sysSpa"> | <script setup lang="tsx" name="sysSpa"> | ||||
import { ref, watch, provide, onMounted, unref, computed, reactive } from "vue"; | import { ref, watch, provide, onMounted, unref, computed, reactive } from "vue"; | ||||
import TreeFilter from "@/components/TreeFilter/index.vue"; | |||||
import { ElMessage } from "element-plus"; | import { ElMessage } from "element-plus"; | ||||
import { abilityApi } from "@/api"; | |||||
import { abilityApi, userManageClassManageApi, monitorLIVEApi } from "@/api"; | |||||
const value = ref(true); | const value = ref(true); | ||||
onMounted(() => { | onMounted(() => { | ||||
// 在这里执行其他需要在组件挂载后运行的代码 | // 在这里执行其他需要在组件挂载后运行的代码 | ||||
getwarnGroup(); | getwarnGroup(); | ||||
getVideoList() | |||||
}); | }); | ||||
const treeFilter = ref<InstanceType<typeof TreeFilter> | null>(null); | |||||
const treeFilters = ref<InstanceType<typeof TreeFilter> | null>(null); | |||||
function changeGroup(val: number | string) { | |||||
console.log(val) | |||||
// personSetId.value = val | |||||
// proTable.value!.pageable.pageNum = 1; | |||||
// proTable.value!.searchParam.personSetId = val; | |||||
// proTable.value!.search(); | |||||
// getVideoList(val.id) | |||||
} | |||||
const videoList = ref<any>(); | |||||
function getVideoList() { | |||||
setTimeout(async () => { | |||||
await monitorLIVEApi.list({ pageNum: 1, pageSize: 1000 }).then(res => { | |||||
videoList.value = res.data.list; | |||||
}); | |||||
}); | |||||
} | |||||
function changeVideo(val: number | string) { | |||||
console.log(val) | |||||
// personSetId.value = val | |||||
// proTable.value!.pageable.pageNum = 1; | |||||
// proTable.value!.searchParam.personSetId = val; | |||||
// proTable.value!.search(); | |||||
// getVideoList(val.id) | |||||
} | |||||
// 获取配置树 | |||||
// 获取配置树 | |||||
let warnGroupList = ref([]); | let warnGroupList = ref([]); | ||||
function getwarnGroup() { | function getwarnGroup() { | ||||
setTimeout(async () => { | setTimeout(async () => { | ||||
@@ -62,7 +99,7 @@ function getwarnGroup() { | |||||
}); | }); | ||||
}); | }); | ||||
} | } | ||||
// 开关 | |||||
function stateChange() { | function stateChange() { | ||||
let params: string = JSON.stringify(warnGroupList.value); | let params: string = JSON.stringify(warnGroupList.value); | ||||
setTimeout(async () => { | setTimeout(async () => { | ||||
@@ -77,60 +114,54 @@ function stateChange() { | |||||
}); | }); | ||||
} | } | ||||
</script> | </script> | ||||
<style lang="scss" scoped> | |||||
.content-main { | |||||
::v-deep(.el-collapse-item__header) { | |||||
flex: 1 0 auto; | |||||
order: -1; | |||||
height: 50px; | |||||
line-height: 50px; | |||||
} | |||||
.collapse-title { | |||||
flex: 1 0 90%; | |||||
order: 1; | |||||
display: flex; | |||||
align-items: center; | |||||
justify-content: space-between; | |||||
// padding: 30px 0; | |||||
.titlemodel { | |||||
// display: inline-block; | |||||
font-size: 18px; | |||||
margin-right: 40px; | |||||
} | |||||
<style lang="scss" scoped> | |||||
.abilityBox { | |||||
display: flex; | |||||
width: 100%; | |||||
height: 100%; | |||||
.btns { | |||||
margin-left: 40px; | |||||
// 配置列表 | |||||
.content-main { | |||||
width: calc(100% - 630px); | |||||
::v-deep(.el-collapse-item__header) { | |||||
flex: 1 0 auto; | |||||
order: -1; | |||||
height: 50px; | |||||
line-height: 50px; | |||||
} | } | ||||
} | |||||
.collapse-content { | |||||
padding: 20px; | |||||
.contentinfo { | |||||
margin-top: 20px; | |||||
// display: flex; | |||||
// justify-content: space-between; | |||||
// align-items: center; | |||||
padding: 20px 20px; | |||||
box-sizing: border-box; | |||||
// border-bottom: 1px solid #dcdfe6; | |||||
// background: red; | |||||
border-radius: 10px; | |||||
// box-shadow: 3px 3px 3px #00000014, 3px -3px 3px #00000014, -3px 3px 3px #00000014, -3px -3px 3px #00000014; | |||||
// box-shadow: 0 1px 1px hsl(0deg 0% 0% / 0.075), 0 2px 2px hsl(0deg 0% 0% / 0.075), 0 4px 4px hsl(0deg 0% 0% / 0.075), | |||||
// 0 8px 8px hsl(0deg 0% 0% / 0.075), 0 16px 16px hsl(0deg 0% 0% / 0.075); | |||||
box-shadow: 0px 2px 1px rgba(0, 0, 0, 0.1), 0 0px 8px rgba(0, 0, 0, 0.1), 0 -1px 1px #fff, 0px 0 0px #fff, 0 0 16px #fff; | |||||
.modellabel { | |||||
font-size: 16px; | |||||
.collapse-title { | |||||
display: flex; | |||||
flex: 1 0 90%; | |||||
align-items: center; | |||||
justify-content: space-between; | |||||
order: 1; | |||||
.titlemodel { | |||||
margin-right: 40px; | |||||
font-size: 18px; | |||||
} | } | ||||
.btns { | .btns { | ||||
margin-left: 40px; | |||||
} | |||||
} | |||||
.collapse-content { | |||||
padding: 20px; | |||||
.contentinfo { | |||||
box-sizing: border-box; | |||||
padding: 20px; | |||||
margin-top: 20px; | margin-top: 20px; | ||||
margin-right: 0; | |||||
text-align: right; | |||||
border-radius: 10px; | |||||
box-shadow: 0 2px 1px rgb(0 0 0 / 10%), 0 0 8px rgb(0 0 0 / 10%), 0 -1px 1px #ffffff, 0 0 0 #ffffff, 0 0 16px #ffffff; | |||||
.modellabel { | |||||
font-size: 16px; | |||||
} | |||||
.btns { | |||||
margin-top: 20px; | |||||
margin-right: 0; | |||||
text-align: right; | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
</style> | </style> | ||||
@@ -0,0 +1,128 @@ | |||||
<!-- | |||||
* @Description: 推送配置 | |||||
* @Author: 王文沛 | |||||
* @Date: 2023-12-15 15:44:05 | |||||
!--> | |||||
<template> | |||||
<div class="card content-main"> | |||||
<el-form | |||||
ref="ruleFormRef" | |||||
style="max-width: 600px" | |||||
:model="ruleForm" | |||||
:rules="rules" | |||||
label-width="auto" | |||||
class="demo-ruleForm" | |||||
:size="formSize" | |||||
status-icon | |||||
> | |||||
<s-form-item label="推送设置:" prop="resource"> | |||||
<s-radio-group v-model="ruleForm.resource" :options="pushOptions" /> | |||||
</s-form-item> | |||||
<div class="ruleTitle">推送规则</div> | |||||
<div class="ruleContent"> | |||||
<span>当日预警每达到</span> | |||||
<el-input-number v-model="ruleForm.num" class="mx-4" :min="1" controls-position="right" @change="handleChange" /> | |||||
<span>条时进行推送</span> | |||||
<div class="tips">当日预警数据每达到设置的条数后,会进行消息推送。</div> | |||||
</div> | |||||
<el-form-item> | |||||
<el-button type="primary" @click="submitForm(ruleFormRef)"> 保 存 </el-button> | |||||
<!-- <el-button @click="resetForm(ruleFormRef)">取消</el-button> --> | |||||
</el-form-item> | |||||
</el-form> | |||||
</div> | |||||
</template> | |||||
<script setup lang="tsx" name="sysconfigPush"> | |||||
import { reactive, ref } from 'vue' | |||||
import type { ComponentSize, FormInstance, FormRules } from 'element-plus' | |||||
import { ElMessage, ElMessageBox } from 'element-plus' | |||||
interface RuleForm { | |||||
resource: string, | |||||
num: number | |||||
} | |||||
const formSize = ref<ComponentSize>('default') | |||||
const ruleFormRef = ref<FormInstance>() | |||||
const ruleForm = reactive<RuleForm>({ | |||||
resource: '0', | |||||
num: 1 | |||||
}) | |||||
const pushOptions = [{ | |||||
label: '开启', | |||||
value: '1', | |||||
},{ | |||||
label: '关闭', | |||||
value: '0', | |||||
}] | |||||
const rules = reactive<FormRules<RuleForm>>({ | |||||
resource: [ | |||||
{ | |||||
required: true, | |||||
message: '请选择推送设置', | |||||
trigger: 'change', | |||||
}, | |||||
], | |||||
}) | |||||
const handleChange = (value: number) => { | |||||
console.log(value) | |||||
} | |||||
function isPositiveInteger(value:any) { | |||||
return /^[1-9]\d*$/.test(value); | |||||
} | |||||
const submitForm = async (formEl: FormInstance | undefined) => { | |||||
if (!formEl) return | |||||
await formEl.validate((valid, fields) => { | |||||
if (valid) { | |||||
if(!isPositiveInteger(ruleForm.num)) { | |||||
ElMessage({ | |||||
message: '推送规则条数必须是大于零的整数', | |||||
type: 'warning', | |||||
}) | |||||
return | |||||
} | |||||
ElMessage({ | |||||
message: '操作成功', | |||||
type: 'success', | |||||
}) | |||||
console.log('submit!') | |||||
} else { | |||||
console.log('error submit!', fields) | |||||
} | |||||
}) | |||||
} | |||||
const resetForm = (formEl: FormInstance | undefined) => { | |||||
if (!formEl) return | |||||
formEl.resetFields() | |||||
} | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
.content-main { | |||||
height: 100%; | |||||
.ruleTitle { | |||||
font-size: 14px; | |||||
font-weight: 600; | |||||
margin: 30px 0 20px 0; | |||||
} | |||||
.ruleContent { | |||||
font-size: 14px; | |||||
color: #666666; | |||||
margin: 20px 0; | |||||
.tips { | |||||
font-size: 14px; | |||||
color: red; | |||||
margin-top: 10px; | |||||
} | |||||
} | |||||
} | |||||
</style> | |||||
@@ -0,0 +1,156 @@ | |||||
<!-- | |||||
* @Description: 表单 | |||||
* @Author: syy | |||||
* @Date: 2023-12-15 15:45:59 | |||||
--> | |||||
<template> | |||||
<div> | |||||
<form-container v-model="visible" :title="`${sysUserProps.opt}服装`" form-size="500px" @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="24"> | |||||
<s-form-item label="所属服装库" prop="clothSetId"> | |||||
<s-select | |||||
v-model="sysUserProps.record.clothSetId" | |||||
:options="sysUserProps.treeAllData" | |||||
label="clothSetName" | |||||
value="clothSetId" | |||||
></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 | |||||
class="avatar-uploader" | |||||
action="/api/business/clothApi/uploadFile" | |||||
:show-file-list="false" | |||||
:on-success="handleAvatarSuccess" | |||||
accept=".jpg, .jpeg, .png" | |||||
:headers="{ | |||||
Authorization: `${TokenEnum.TOKEN_PREFIX} ${accessToken}` | |||||
}" | |||||
> | |||||
<img v-if="sysUserProps.record.clothUrl" :src="sysUserProps.record.clothUrl" class="avatar" /> | |||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> | |||||
</el-upload> | |||||
</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="SysUserClothForm"> | |||||
import { SysUserCloth, userManageClothApi } from "@/api"; | |||||
import { FormOptEnum } from "@/enums"; | |||||
import { required } from "@/utils/formRules"; | |||||
import { FormInstance } from "element-plus"; | |||||
import { useUserStore } from "@/stores/modules"; | |||||
import type { UploadProps } from "element-plus"; | |||||
import { TokenEnum } from "@/enums"; | |||||
const visible = ref(false); //是否显示表单 | |||||
const activeName = ref("basic"); | |||||
const userStore = useUserStore(); | |||||
const { accessToken } = userStore; | |||||
// 表单参数 | |||||
const sysUserProps = reactive<FormProps.Base<SysUserCloth.list>>({ | |||||
opt: FormOptEnum.ADD, | |||||
record: {}, | |||||
treeAllData: [], | |||||
disabled: false | |||||
}); | |||||
// 表单验证规则 | |||||
const rules = reactive({ | |||||
clothSetId: [required("请选择所属服装库")], | |||||
clothUrl: [required("请上传服装")] | |||||
}); | |||||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response: any) => { | |||||
if (response.code === 200) { | |||||
sysUserProps.record.clothUrl = response.data; | |||||
} | |||||
}; | |||||
/** | |||||
* 打开表单 | |||||
* @param props 表单参数 | |||||
*/ | |||||
function onOpen(props: FormProps.Base<SysUserCloth.list>) { | |||||
Object.assign(sysUserProps, props); //合并参数 | |||||
visible.value = true; //显示表单 | |||||
sysUserProps.record = props.record; | |||||
} | |||||
// 提交数据(新增/编辑) | |||||
const sysUserFormRef = ref<FormInstance>(); | |||||
/** 提交表单 */ | |||||
async function handleSubmit() { | |||||
sysUserFormRef.value?.validate(async valid => { | |||||
if (!valid) return; //表单验证失败 | |||||
await userManageClothApi | |||||
.add(sysUserProps.record) | |||||
.then(() => { | |||||
sysUserProps.successful!(); //调用父组件的successful方法 | |||||
}) | |||||
.finally(() => { | |||||
onClose(); | |||||
}); | |||||
}); | |||||
} | |||||
/** 关闭表单*/ | |||||
function onClose() { | |||||
visible.value = false; | |||||
activeName.value = "basic"; | |||||
} | |||||
// 暴露给父组件的方法 | |||||
defineExpose({ | |||||
onOpen | |||||
}); | |||||
</script> | |||||
<style lang="scss" scoped> | |||||
.avatar-uploader .avatar { | |||||
display: block; | |||||
width: 178px; | |||||
height: 178px; | |||||
} | |||||
</style> | |||||
<style> | |||||
.avatar-uploader .el-upload { | |||||
position: relative; | |||||
overflow: hidden; | |||||
cursor: pointer; | |||||
border: 1px dashed var(--el-border-color); | |||||
border-radius: 6px; | |||||
transition: var(--el-transition-duration-fast); | |||||
} | |||||
.avatar-uploader .el-upload:hover { | |||||
border-color: var(--el-color-primary); | |||||
} | |||||
.el-icon.avatar-uploader-icon { | |||||
width: 178px; | |||||
height: 178px; | |||||
font-size: 28px; | |||||
color: #8c939d; | |||||
text-align: center; | |||||
} | |||||
</style> |
@@ -0,0 +1,105 @@ | |||||
<!-- | |||||
* @Description: 表单 | |||||
* @Author: syy | |||||
* @Date: 2023-12-18 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="clothSetName"> | |||||
<s-input v-model="sysUserProps.record.clothSetName"></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="SysUserClothDk"> | |||||
import { ref } from "vue"; | |||||
import { userManageClothApi, SysUserCloth } 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<SysUserCloth.Page>>({ | |||||
opt: FormOptEnum.ADD, | |||||
record: {}, | |||||
disabled: false | |||||
}); | |||||
// 表单验证规则 | |||||
const rules = reactive({ | |||||
clothSetName: [required("请输入服装库名称")] | |||||
}); | |||||
/** | |||||
* 打开表单 | |||||
* @param props 表单参数 | |||||
*/ | |||||
function onOpen(props: FormProps.Base<SysUserCloth.Page>) { | |||||
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; //表单验证失败 | |||||
//提交表单 | |||||
if (sysUserProps.record.clothSetId) { | |||||
await userManageClothApi | |||||
.update(sysUserProps.record) | |||||
.then(() => { | |||||
sysUserProps.successful!(); //调用父组件的successful方法 | |||||
}) | |||||
.finally(() => { | |||||
onClose(); | |||||
}); | |||||
} else { | |||||
await userManageClothApi | |||||
.addClothDataBaseA(sysUserProps.record) | |||||
.then(() => { | |||||
sysUserProps.successful!(); //调用父组件的successful方法 | |||||
}) | |||||
.finally(() => { | |||||
onClose(); | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
/** 关闭表单*/ | |||||
function onClose() { | |||||
visibleClass.value = false; | |||||
} | |||||
// 暴露给父组件的方法 | |||||
defineExpose({ | |||||
onOpen | |||||
}); | |||||
</script> | |||||
<style lang="scss" scoped></style> |
@@ -0,0 +1,195 @@ | |||||
<!-- | |||||
* @Description: 服装库管理 | |||||
* @Author: syy | |||||
* @Date: 2024-7-18 | |||||
--> | |||||
<template> | |||||
<div class="main-box"> | |||||
<TreeFilter | |||||
ref="treeFilter" | |||||
label="clothSetName" | |||||
id="clothSetId" | |||||
width="300px" | |||||
:show-all="true" | |||||
:request-api="userManageClothApi.getList" | |||||
@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 v-if="row.node.label != '全部'"> | |||||
<a @click.stop="addClass(FormOptEnum.EDIT, row.node.data)"> | |||||
<el-icon><Edit /></el-icon> | |||||
</a> | |||||
<a style="margin-left: 8px" @click.stop="addDelete(row.node.data.clothSetId, '删除服装库')"> | |||||
<el-icon><Delete /></el-icon> | |||||
</a> | |||||
</span> | |||||
</span> | |||||
</template> | |||||
</TreeFilter> | |||||
<div class="table-box"> | |||||
<ProTable ref="proTable" title="服装库管理" :columns="columns" rowKey="clothId" :data="tableData.clothes"> | |||||
<!-- 表格 header 按钮 --> | |||||
<template #tableHeader="scope"> | |||||
<s-button suffix="服装" @click="onOpen(FormOptEnum.ADD, { clothSetId: clothSetId }, treeFilter.treeAllData)" /> | |||||
<s-button | |||||
type="danger" | |||||
:opt="FormOptEnum.DELETE" | |||||
plain | |||||
suffix="服装" | |||||
:disabled="!scope.isSelected" | |||||
@click="onDelete(scope.selectedListIds, '删除所选服装')" | |||||
/> | |||||
</template> | |||||
<!-- 表格操作栏 --> | |||||
<template #operation="scope"> | |||||
<el-space> | |||||
<s-button link :opt="FormOptEnum.DELETE" @click="onDelete([scope.row.clothId], `删除服装`)" /> | |||||
</el-space> | |||||
</template> | |||||
</ProTable> | |||||
</div> | |||||
<!-- 人员新增/编辑表单 --> | |||||
<Form ref="formRef"></Form> | |||||
<!-- 班级新增/编辑表单 --> | |||||
<Form1 ref="formRefC" /> | |||||
<!-- 预览头像 --> | |||||
<el-dialog v-model="visible" title="查看头像" width="830px" :before-close="handleClose"> | |||||
<div style="display: flex; align-items: center; justify-content: center"> | |||||
<img style="max-width: 100%" class="detailpic" :src="faceUrl" alt="" /> | |||||
</div> | |||||
<template #footer> | |||||
<div class="dialog-footer"> | |||||
<el-button @click="visible = false">关闭</el-button> | |||||
</div> | |||||
</template> | |||||
</el-dialog> | |||||
</div> | |||||
</template> | |||||
<script setup lang="tsx" name="SysUserClothing"> | |||||
import { userManageClothApi,userClothButtonCode,SysUserCloth } from "@/api"; | |||||
import { useHandleData } from "@/hooks/useHandleData"; | |||||
import { FormOptEnum } from "@/enums"; | |||||
import Form from "./components/form/index.vue"; | |||||
import Form1 from "./components/form1/index.vue"; | |||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||||
import TreeFilter from "@/components/TreeFilter/index.vue"; | |||||
import { useUserStore } from "@/stores/modules"; | |||||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||||
const faceUrl = ref(''); | |||||
const visible = ref(false); //是否显示人员表单 | |||||
const proTable = ref<ProTableInstance>(); | |||||
const treeFilter = ref<any>({}); | |||||
const userStore = useUserStore(); | |||||
const { accessToken } = userStore; | |||||
const tableData = ref<any>([]) | |||||
// 表格配置项 | |||||
const columns: ColumnProps<SysUserCloth.list>[] = [ | |||||
{ type: "selection", fixed: "left", width: 50 }, | |||||
{ | |||||
prop: "clothUrl", | |||||
label: "服装图", | |||||
render: scope => { | |||||
return ( | |||||
<img src={scope.row.clothUrl} onClick={() => viewHeadImage(scope)} style='width:50px;height:50px' alt=''/> | |||||
); | |||||
} | |||||
}, | |||||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | |||||
]; | |||||
const viewHeadImage = (scope: any) => { | |||||
faceUrl.value = scope.row.clothUrl; | |||||
visible.value = true | |||||
}; | |||||
const handleClose = () => { | |||||
visible.value = false; | |||||
}; | |||||
// 人员表单引用 | |||||
const formRef = ref<InstanceType<typeof Form> | null>(null); | |||||
// 班级表单引用 | |||||
const formRefC = ref<InstanceType<typeof Form1> | null>(null); | |||||
/** | |||||
* 打开服装表单 | |||||
* @param opt 操作类型 | |||||
* @param record 记录 | |||||
*/ | |||||
function onOpen(opt: FormOptEnum, record: {},treeAllData:any) { | |||||
formRef.value?.onOpen({ opt: opt, record: record,treeAllData:treeAllData, successful: RefreshTable }); | |||||
} | |||||
/** | |||||
* 打开服装库表单 | |||||
* @param opt 操作类型 | |||||
* @param record 记录 | |||||
*/ | |||||
function addClass(opt: FormOptEnum, record: {} | SysUserCloth.list = {}) { | |||||
formRefC.value?.onOpen({ opt: opt, record: JSON.parse(JSON.stringify(record)), successful: RefreshTree }); | |||||
} | |||||
/** | |||||
* 服装库删除 | |||||
* @param ids id数组 | |||||
*/ | |||||
async function addDelete(clothSetId: string[],msg: string) { | |||||
// 二次确认 => 请求api => 刷新表格 | |||||
await useHandleData(userManageClothApi.deleteClothDataBaseD, { clothSetId }, msg); | |||||
RefreshTree(); //刷新表格 | |||||
} | |||||
/** | |||||
* 服装删除 | |||||
* @param ids id数组 | |||||
*/ | |||||
async function onDelete(ids: string[], msg: string) { | |||||
// 二次确认 => 请求api => 刷新表格 | |||||
await useHandleData(userManageClothApi.delete, {clothId: ids.join(","), clothSetId:tableData.value.clothSetId}, msg); | |||||
RefreshTable(); //刷新表格 | |||||
} | |||||
// 刷新表格 | |||||
const RefreshTable = () => { | |||||
getList(clothSetId.value) | |||||
} | |||||
// 刷新表格+树 | |||||
const RefreshTree = () => { | |||||
getList(clothSetId.value) | |||||
treeFilter.value?.refresh(); //刷新树形筛选器 | |||||
} | |||||
/** 服装库切换切换 */ | |||||
const clothSetId = ref<number | string>() | |||||
function changeTreeFilter(val: number | string) { | |||||
clothSetId.value = val | |||||
proTable.value!.pageable.pageNum = 1; | |||||
getList(val) | |||||
} | |||||
// 获取列表 | |||||
const getList = (clothSetId:any)=>{ | |||||
if(!clothSetId) return false | |||||
userManageClothApi.page({clothSetId:clothSetId}).then((resp:any)=>{ | |||||
if(resp.code == 200){ | |||||
tableData.value = resp.data | |||||
} | |||||
}) | |||||
} | |||||
</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> |
@@ -0,0 +1,181 @@ | |||||
<!-- | |||||
* @Description: 人员表单 | |||||
* @Author: syy | |||||
* @Date: 2023-12-15 15:45:50 | |||||
--> | |||||
<template> | |||||
<div class="userManageForm"> | |||||
<div> | |||||
<el-row :gutter="16"> | |||||
<el-col :span="24"> | |||||
<s-form-item label="人员姓名" prop="name"> | |||||
<s-input v-model="userInfo.name"></s-input> | |||||
</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, userManageKeyPersonApi } from "@/api"; | |||||
import { Plus } from "@element-plus/icons-vue"; | |||||
import { useUserStore } from "@/stores/modules"; | |||||
import type { UploadProps } 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<any>([]); | |||||
const faces = ref<SysUserPersonnel.SysUserAvatar[]>([]); | |||||
const dialogImageUrl = ref(""); | |||||
const dialogVisible = ref(false); | |||||
const handleRemove: UploadProps["onRemove"] = (uploadFile: any) => { | |||||
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) { | |||||
userManageKeyPersonApi.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" | |||||
} | |||||
]); | |||||
onMounted(() => { | |||||
// 初始化 | |||||
userInfo.value.gender = userInfo.value.gender ? userInfo.value.gender : genderOptions.value[0].value; | |||||
if (userInfo.value.personId) { | |||||
if (userInfo.value.faces?.length > 0) { | |||||
fileList.value = [ | |||||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map((item: any) => { | |||||
return { | |||||
url: item.faceUrl, | |||||
uid: item.faceId, | |||||
personId: userInfo.value.personId | |||||
}; | |||||
}) | |||||
]; | |||||
faces.value = [ | |||||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map((item: any) => { | |||||
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,117 @@ | |||||
<!-- | |||||
* @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="SysUserKeyPersonnelForm"> | |||||
import { SysUserPersonnel, userManageKeyPersonApi } from "@/api"; | |||||
import { FormOptEnum } from "@/enums"; | |||||
import { required } from "@/utils/formRules"; | |||||
import { FormInstance } from "element-plus"; | |||||
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({ | |||||
name: [required("请输入姓名")], | |||||
gender: [required("请选择性别")], | |||||
faces: [required("请上传人脸图片")], | |||||
phone: [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 | |||||
userManageKeyPersonApi.detail({ id: props.record.personId }).then((res: any) => { | |||||
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 userManageKeyPersonApi | |||||
.update(sysUserProps.record) | |||||
.then(() => { | |||||
sysUserProps.successful!(); //调用父组件的successful方法 | |||||
}) | |||||
.finally(() => { | |||||
onClose(); | |||||
}); | |||||
} else { | |||||
await userManageKeyPersonApi | |||||
.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,220 @@ | |||||
<!-- | |||||
* @Description: 人员管理 | |||||
* @Author: syy | |||||
* @Date: 2024-7-15 | |||||
--> | |||||
<template> | |||||
<div class="main-box"> | |||||
<div class="table-box"> | |||||
<ProTable ref="proTable" title="人员管理" :columns="columns" rowKey="personId" :request-api="userManageKeyPersonApi.page"> | |||||
<!-- 表格 header 按钮 --> | |||||
<template #tableHeader="scope"> | |||||
<s-button suffix="人员" @click="onOpen(FormOptEnum.ADD)" /> | |||||
<s-button | |||||
type="danger" | |||||
:opt="FormOptEnum.DELETE" | |||||
plain | |||||
suffix="人员" | |||||
:disabled="!scope.isSelected" | |||||
@click="onDelete(scope.selectedListIds, '删除所选人员')" | |||||
/> | |||||
</template> | |||||
<!-- 表格操作栏 --> | |||||
<template #operation="scope"> | |||||
<el-space> | |||||
<s-button link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)" /> | |||||
<s-button 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-menu> | |||||
</template> | |||||
</el-dropdown> | |||||
</el-space> | |||||
</template> | |||||
</ProTable> | |||||
</div> | |||||
<!-- 人员新增/编辑表单 --> | |||||
<Form ref="formRef"></Form> | |||||
<!-- 预览头像 --> | |||||
<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="" style="max-width: 100%" /> | |||||
</div> | |||||
</el-dialog> | |||||
</div> | |||||
</template> | |||||
<script setup lang="tsx" name="SysUserKeyPersonnel"> | |||||
import { userManageKeyPersonApi,SysUserPersonnel } from "@/api"; | |||||
import { useHandleData } from "@/hooks/useHandleData"; | |||||
import { FormOptEnum } from "@/enums"; | |||||
import Form from "./components/form/index.vue"; | |||||
import { ArrowDown } from "@element-plus/icons-vue"; | |||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||||
import { ElMessage } from "element-plus"; | |||||
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 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); | |||||
/** | |||||
* 打开表单 | |||||
* @param opt 操作类型 | |||||
* @param record 记录 | |||||
*/ | |||||
function onOpen(opt: FormOptEnum, record: {} | SysUserPersonnel.SysUserPerInfo = {}) { | |||||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||||
} | |||||
/** | |||||
* 人员删除 | |||||
* @param ids id数组 | |||||
*/ | |||||
async function onDelete(ids: string[], msg: string) { | |||||
if(ids.length === 0){ | |||||
ElMessage({ | |||||
message: '请选择要删除的人员', | |||||
type: 'warning' | |||||
}); | |||||
return | |||||
} | |||||
// 二次确认 => 请求api => 刷新表格 | |||||
await useHandleData(userManageKeyPersonApi.delete, {id: ids.join(",") }, msg); | |||||
RefreshTable(); //刷新表格 | |||||
} | |||||
// 刷新表格 | |||||
const RefreshTable = () => { | |||||
proTable.value?.refresh(); | |||||
} | |||||
/** 更多下拉菜单命令枚举 */ | |||||
enum cmdEnum { | |||||
AddFace = "添加人脸", | |||||
} | |||||
/** 下拉菜单参数接口 */ | |||||
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 | |||||
} | |||||
} | |||||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response) => { | |||||
if (response.code === 200) { | |||||
userManageKeyPersonApi.addFace({ | |||||
personId: personId.value, | |||||
faceUrl: response.data | |||||
}).then(res=>{ | |||||
RefreshTable() | |||||
}) | |||||
} else { | |||||
ElMessage.error(response.msg); | |||||
} | |||||
}; | |||||
</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> |
@@ -75,7 +75,7 @@ | |||||
import { SysUserPersonnel, userManagePersonnelApi, userManageClassManageApi } from "@/api"; | import { SysUserPersonnel, userManagePersonnelApi, userManageClassManageApi } from "@/api"; | ||||
import { Plus } from "@element-plus/icons-vue"; | import { Plus } from "@element-plus/icons-vue"; | ||||
import { useUserStore } from "@/stores/modules"; | import { useUserStore } from "@/stores/modules"; | ||||
import type { UploadProps, UploadUserFile } from "element-plus"; | |||||
import type { UploadProps } from "element-plus"; | |||||
import { ElMessage } from "element-plus"; | import { ElMessage } from "element-plus"; | ||||
import { TokenEnum } from "@/enums"; | import { TokenEnum } from "@/enums"; | ||||
// props | // props | ||||
@@ -92,12 +92,12 @@ const userInfo = computed({ | |||||
/* */ | /* */ | ||||
const userStore = useUserStore(); | const userStore = useUserStore(); | ||||
const { accessToken } = userStore; | const { accessToken } = userStore; | ||||
const fileList = ref([]); | |||||
const fileList = ref<any>([]); | |||||
const faces = ref<SysUserPersonnel.SysUserAvatar[]>([]); | const faces = ref<SysUserPersonnel.SysUserAvatar[]>([]); | ||||
const dialogImageUrl = ref(""); | const dialogImageUrl = ref(""); | ||||
const dialogVisible = ref(false); | const dialogVisible = ref(false); | ||||
const treeData = ref<{ [key: string]: any }[]>([]); | |||||
const handleRemove: UploadProps["onRemove"] = uploadFile => { | |||||
const treeData = ref<any>([]); | |||||
const handleRemove: UploadProps["onRemove"] = (uploadFile: any) => { | |||||
const index = faces.value.findIndex(item => item.uid === uploadFile.uid); | const index = faces.value.findIndex(item => item.uid === uploadFile.uid); | ||||
if (index > -1) { | if (index > -1) { | ||||
faces.value.splice(index, 1); | faces.value.splice(index, 1); | ||||
@@ -143,7 +143,6 @@ const genderOptions = ref([ | |||||
const getRequestData = async () => { | const getRequestData = async () => { | ||||
const { data } = await userManageClassManageApi.page(); | const { data } = await userManageClassManageApi.page(); | ||||
treeData.value = data; | treeData.value = data; | ||||
console.log(treeData.value, "treeData"); | |||||
}; | }; | ||||
onMounted(() => { | onMounted(() => { | ||||
// 初始化 | // 初始化 | ||||
@@ -152,7 +151,7 @@ onMounted(() => { | |||||
if (userInfo.value.personId) { | if (userInfo.value.personId) { | ||||
if (userInfo.value.faces?.length > 0) { | if (userInfo.value.faces?.length > 0) { | ||||
fileList.value = [ | fileList.value = [ | ||||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map(item => { | |||||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map((item: any) => { | |||||
return { | return { | ||||
url: item.faceUrl, | url: item.faceUrl, | ||||
uid: item.faceId, | uid: item.faceId, | ||||
@@ -161,7 +160,7 @@ onMounted(() => { | |||||
}) | }) | ||||
]; | ]; | ||||
faces.value = [ | faces.value = [ | ||||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map(item => { | |||||
...JSON.parse(JSON.stringify(userInfo.value.faces)).map((item: any) => { | |||||
return { | return { | ||||
faceUrl: item.faceUrl, | faceUrl: item.faceUrl, | ||||
uid: item.faceId, | uid: item.faceId, | ||||
@@ -0,0 +1,116 @@ | |||||
<!-- | |||||
* @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="userId"> | |||||
<s-select v-model="sysUserProps.record.userId" :options="teacherData" label="name" value="userId"></s-select> | |||||
</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, userManageTeacherApi } from "@/api"; | |||||
import { required } from "@/utils/formRules"; | |||||
import { FormInstance } from "element-plus"; | |||||
const teacherData = ref<any>([]); | |||||
const visibleClass = ref(false); //是否显示表单 | |||||
// 表单参数 | |||||
enum FormOptEnum { | |||||
/** 新增 */ | |||||
AddTeacher = "绑定班主任", | |||||
/** 编辑 */ | |||||
UpdateTeacher = "修改班主任" | |||||
} | |||||
const sysUserProps = reactive<FormProps.Base<SysUserPersonnel.ClassPage>>({ | |||||
opt: FormOptEnum.AddTeacher, | |||||
record: {}, | |||||
disabled: false | |||||
}); | |||||
// 表单验证规则 | |||||
const rules = reactive({ | |||||
userId: [required("请选择班主任名称")] | |||||
}); | |||||
/** | |||||
* 打开表单 | |||||
* @param props 表单参数 | |||||
*/ | |||||
function onOpen(props: FormProps.Base<SysUserPersonnel.ClassPage>) { | |||||
getRequestData(); | |||||
Object.assign(sysUserProps, props); //合并参数 | |||||
visibleClass.value = true; //显示表单 | |||||
sysUserProps.record = props.record; | |||||
} | |||||
const getRequestData = async () => { | |||||
const { data } = await userManageTeacherApi.page(); | |||||
teacherData.value = data; | |||||
}; | |||||
// 提交数据(新增/编辑) | |||||
const sysUserFormRef = ref<FormInstance>(); | |||||
/** 提交表单 */ | |||||
async function handleSubmit() { | |||||
sysUserFormRef.value?.validate(async valid => { | |||||
if (!valid) return; //表单验证失败 | |||||
console.log(sysUserProps); | |||||
//提交表单 | |||||
if (sysUserProps.opt === FormOptEnum.UpdateTeacher) { | |||||
await userManageTeacherApi | |||||
.update(sysUserProps.record) | |||||
.then(() => { | |||||
sysUserProps.successful!(); //调用父组件的successful方法 | |||||
}) | |||||
.finally(() => { | |||||
onClose(); | |||||
}); | |||||
} else { | |||||
await userManageTeacherApi | |||||
.add(sysUserProps.record) | |||||
.then(() => { | |||||
sysUserProps.successful!(); //调用父组件的successful方法 | |||||
}) | |||||
.finally(() => { | |||||
onClose(); | |||||
}); | |||||
} | |||||
}); | |||||
} | |||||
/** 关闭表单*/ | |||||
function onClose() { | |||||
visibleClass.value = false; | |||||
} | |||||
// 暴露给父组件的方法 | |||||
defineExpose({ | |||||
onOpen | |||||
}); | |||||
</script> | |||||
<style lang="scss" scoped></style> |
@@ -10,7 +10,7 @@ | |||||
label="personSetName" | label="personSetName" | ||||
id="personSetId" | id="personSetId" | ||||
width="300px" | width="300px" | ||||
:show-all="false" | |||||
:show-all="true" | |||||
:request-api="userManageClassManageApi.page" | :request-api="userManageClassManageApi.page" | ||||
@change="changeTreeFilter" | @change="changeTreeFilter" | ||||
> | > | ||||
@@ -20,13 +20,31 @@ | |||||
<template v-slot:label="{ row }"> | <template v-slot:label="{ row }"> | ||||
<span class="custom-tree-node"> | <span class="custom-tree-node"> | ||||
<span>{{ row.node.label }}</span> | <span>{{ row.node.label }}</span> | ||||
<span> | |||||
<a @click="addClass(FormOptEnum.EDIT, row.node.data)"> | |||||
<span v-if="row.node.label != '全部'"> | |||||
<a @click.stop="addClass(FormOptEnum.EDIT, row.node.data)"> | |||||
<el-icon><Edit /></el-icon> | <el-icon><Edit /></el-icon> | ||||
</a> | </a> | ||||
<a style="margin-left: 8px" @click="addDelete(row.node.data.personSetId, '删除班级')"> | |||||
<a style="margin-left: 8px" @click.stop="addDelete(row.node.data.personSetId, '删除班级')"> | |||||
<el-icon><Delete /></el-icon> | <el-icon><Delete /></el-icon> | ||||
</a> | </a> | ||||
<a style="margin-left: 8px" @click.stop> | |||||
<el-dropdown @command="handleCommandTree"> | |||||
<el-link :underline="false" :icon="More"> </el-link> | |||||
<template #dropdown> | |||||
<el-dropdown-menu> | |||||
<el-dropdown-item v-if="!row.node.data.userId" :command="commander(row.node.data, cmdEnumTree.AddTeacher)"> | |||||
{{ cmdEnumTree.AddTeacher }} | |||||
</el-dropdown-item> | |||||
<el-dropdown-item v-if="row.node.data.userId" :command="commander(row.node.data, cmdEnumTree.UpdateTeacher)"> | |||||
{{ cmdEnumTree.UpdateTeacher }} | |||||
</el-dropdown-item> | |||||
<el-dropdown-item v-if="row.node.data.userId" :command="commander(row.node.data, cmdEnumTree.DeleteTeacher)"> | |||||
{{ cmdEnumTree.DeleteTeacher }} | |||||
</el-dropdown-item> | |||||
</el-dropdown-menu> | |||||
</template> | |||||
</el-dropdown> | |||||
</a> | |||||
</span> | </span> | ||||
</span> | </span> | ||||
</template> | </template> | ||||
@@ -86,6 +104,8 @@ | |||||
<Form ref="formRef"></Form> | <Form ref="formRef"></Form> | ||||
<!-- 班级新增/编辑表单 --> | <!-- 班级新增/编辑表单 --> | ||||
<FormClass ref="formRefC" /> | <FormClass ref="formRefC" /> | ||||
<!-- 班主任绑定/修改 --> | |||||
<FormTeacher ref="formRefT" /> | |||||
<!-- 预览头像 --> | <!-- 预览头像 --> | ||||
<el-dialog v-model="visible" title="查看头像" width="830px" :before-close="handleClose"> | <el-dialog v-model="visible" title="查看头像" width="830px" :before-close="handleClose"> | ||||
<div style="display: flex; align-items: center; justify-content: center"> | <div style="display: flex; align-items: center; justify-content: center"> | ||||
@@ -95,12 +115,13 @@ | |||||
</div> | </div> | ||||
</template> | </template> | ||||
<script setup lang="tsx" name="SysUserPersonnel"> | <script setup lang="tsx" name="SysUserPersonnel"> | ||||
import { userManagePersonnelApi,userPerButtonCode,SysUserPersonnel,userManageClassManageApi } from "@/api"; | |||||
import { userManagePersonnelApi,userPerButtonCode,SysUserPersonnel,userManageClassManageApi,userManageTeacherApi } from "@/api"; | |||||
import { useHandleData } from "@/hooks/useHandleData"; | import { useHandleData } from "@/hooks/useHandleData"; | ||||
import { FormOptEnum } from "@/enums"; | import { FormOptEnum } from "@/enums"; | ||||
import Form from "./components/form/index.vue"; | import Form from "./components/form/index.vue"; | ||||
import FormClass from "./components/formClass/index.vue"; | import FormClass from "./components/formClass/index.vue"; | ||||
import { ArrowDown } from "@element-plus/icons-vue"; | |||||
import FormTeacher from "./components/formTeacher/index.vue"; | |||||
import { ArrowDown,More } from "@element-plus/icons-vue"; | |||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | ||||
import TreeFilter from "@/components/TreeFilter/index.vue"; | import TreeFilter from "@/components/TreeFilter/index.vue"; | ||||
import { useUserStore } from "@/stores/modules"; | import { useUserStore } from "@/stores/modules"; | ||||
@@ -134,8 +155,8 @@ const columns: ColumnProps<SysUserPersonnel.SysUserPerInfo>[] = [ | |||||
label: "人员ID" | label: "人员ID" | ||||
}, | }, | ||||
{ | { | ||||
prop: "age", | |||||
label: "年龄" | |||||
prop: "phone", | |||||
label: "手机号" | |||||
}, | }, | ||||
{ | { | ||||
prop: "personSets", | prop: "personSets", | ||||
@@ -158,8 +179,11 @@ const handleClose = () => { | |||||
// 人员表单引用 | // 人员表单引用 | ||||
const formRef = ref<InstanceType<typeof Form> | null>(null); | const formRef = ref<InstanceType<typeof Form> | null>(null); | ||||
// 班级表单引用 | |||||
// 班级表单引用 | |||||
const formRefC = ref<InstanceType<typeof FormClass> | null>(null); | const formRefC = ref<InstanceType<typeof FormClass> | null>(null); | ||||
// 班级表单引用 | |||||
const formRefT = ref<InstanceType<typeof FormTeacher> | null>(null); | |||||
/** | /** | ||||
* 打开表单 | * 打开表单 | ||||
* @param opt 操作类型 | * @param opt 操作类型 | ||||
@@ -176,7 +200,7 @@ function onOpen(opt: FormOptEnum, record: {} | SysUserPersonnel.SysUserPerInfo = | |||||
*/ | */ | ||||
function addClass(opt: FormOptEnum, record: {} | SysUserPersonnel.ClassPage = {}) { | function addClass(opt: FormOptEnum, record: {} | SysUserPersonnel.ClassPage = {}) { | ||||
formRefC.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||||
formRefC.value?.onOpen({ opt: opt, record: JSON.parse(JSON.stringify(record)), successful: RefreshTable }); | |||||
} | } | ||||
/** | /** | ||||
* 班级删除 | * 班级删除 | ||||
@@ -231,11 +255,11 @@ function command(row: SysUserPersonnel.SysUserPerInfo, command: cmdEnum): Comman | |||||
}; | }; | ||||
} | } | ||||
/** | /** | ||||
* 更多下拉菜单点击事件 | |||||
* 列表更多下拉菜单点击事件 | |||||
* @param command | * @param command | ||||
*/ | */ | ||||
const personId = ref<number | string>(); //人员id | |||||
function handleCommand(command: Command) { | |||||
const personId = ref<number | string>(); //人员id | |||||
function handleCommand(command: Command) { | |||||
switch (command.command) { | switch (command.command) { | ||||
case cmdEnum.AddFace: | case cmdEnum.AddFace: | ||||
personId.value = command.row.personId; //获取人员id | personId.value = command.row.personId; //获取人员id | ||||
@@ -245,15 +269,57 @@ function command(row: SysUserPersonnel.SysUserPerInfo, command: cmdEnum): Comman | |||||
personId:command.row.personId, | personId:command.row.personId, | ||||
personSetId: command.row.personSets[0].personSetId | personSetId: command.row.personSets[0].personSetId | ||||
}).then(res=>{ | }).then(res=>{ | ||||
if(res.code == 200){ | |||||
ElMessage.success('底库解绑成功'); | ElMessage.success('底库解绑成功'); | ||||
RefreshTable() | RefreshTable() | ||||
} | |||||
}) | }) | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response, uploadFile) => { | |||||
/** 更多下拉菜单命令枚举 */ | |||||
enum cmdEnumTree { | |||||
AddTeacher = "绑定班主任", | |||||
UpdateTeacher = "修改班主任", | |||||
DeleteTeacher = "解绑班主任", | |||||
} | |||||
/** 树下拉菜单参数接口 */ | |||||
interface CommandTree { | |||||
row: SysUserPersonnel.ClassPage; | |||||
commander: cmdEnumTree; | |||||
} | |||||
/**配置command的参数 */ | |||||
function commander(row: SysUserPersonnel.ClassPage, commander: cmdEnumTree): CommandTree { | |||||
return { | |||||
row: row, | |||||
commander: commander, | |||||
}; | |||||
} | |||||
/** | |||||
* 树更多下拉菜单点击事件 | |||||
* @param commandtree | |||||
*/ | |||||
async function handleCommandTree(commander: CommandTree) { | |||||
switch (commander.commander) { | |||||
case cmdEnumTree.AddTeacher: | |||||
formRefT.value?.onOpen({ opt: commander.commander, record: commander.row, successful: RefreshTable }); | |||||
break | |||||
case cmdEnumTree.UpdateTeacher: | |||||
formRefT.value?.onOpen({ opt: commander.commander, record: commander.row, successful: RefreshTable }); | |||||
break; | |||||
case cmdEnumTree.DeleteTeacher: | |||||
// 二次确认 => 请求api => 刷新表格 | |||||
await useHandleData(userManageTeacherApi.delete, {id: commander.row.id}, '解绑教师'); | |||||
RefreshTable(); //刷新表格 | |||||
break; | |||||
} | |||||
} | |||||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response) => { | |||||
if (response.code === 200) { | if (response.code === 200) { | ||||
userManagePersonnelApi.addFace({ | userManagePersonnelApi.addFace({ | ||||
personId: personId.value, | personId: personId.value, | ||||
@@ -98,6 +98,12 @@ | |||||
</div> | </div> | ||||
</template> | </template> | ||||
</el-dialog> | </el-dialog> | ||||
<!-- 预览头像 --> | |||||
<el-dialog v-model="preViewvisible" title="查看图片" width="830px" :before-close="previewhandleClose"> | |||||
<div style="display: flex; align-items: center; justify-content: center"> | |||||
<img class="detailpic" :src="faceUrl" alt="" /> | |||||
</div> | |||||
</el-dialog> | |||||
</div> | </div> | ||||
</template> | </template> | ||||
@@ -109,6 +115,16 @@ import { useHandleData } from "@/hooks/useHandleData"; | |||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | ||||
import { useDictStore } from "@/stores/modules"; | import { useDictStore } from "@/stores/modules"; | ||||
import { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | import { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | ||||
const faceUrl = ref(''); | |||||
const preViewvisible = ref(false); //是否显示人员表单 | |||||
const viewHeadImage = (scope: any) => { | |||||
faceUrl.value = scope.row.snapshotUrl; | |||||
preViewvisible.value = true | |||||
console.log(faceUrl); | |||||
}; | |||||
const previewhandleClose = () => { | |||||
preViewvisible.value = false; | |||||
}; | |||||
const visible = ref(false); //是否显示表单 | const visible = ref(false); //是否显示表单 | ||||
onMounted(() => { | onMounted(() => { | ||||
getWarnTypeList(); | getWarnTypeList(); | ||||
@@ -161,13 +177,7 @@ const columns: ColumnProps<ZJRQ.WarnInfo>[] = [ | |||||
label: "告警快照", | label: "告警快照", | ||||
render: scope => { | render: scope => { | ||||
return ( | return ( | ||||
<img | |||||
src={scope.row.snapshotUrl} | |||||
class="h-6 w-6" | |||||
onClick={() => { | |||||
window.open(scope.row.snapshotUrl, "_blank"); | |||||
}} | |||||
/> | |||||
<img src={scope.row.snapshotUrl ? scope.row.snapshotUrl : ''} onClick={() => viewHeadImage(scope)} style='width:50px;height:50px;' alt=''/> | |||||
); | ); | ||||
} | } | ||||
}, | }, | ||||