@@ -0,0 +1,246 @@ | |||
<!-- | |||
* @Description: 用户选择器 | |||
* @Author: huguodong | |||
* @Date: 2023-12-15 15:40:45 | |||
!--> | |||
<template> | |||
<form-container v-model="visible" :title="`${userName}选择`" form-size="90%" v-bind="$attrs"> | |||
<div class="-mt-15px min-h-350px"> | |||
<el-row :gutter="12" justify="space-between"> | |||
<el-col :span="4"> | |||
<el-tabs v-model="activeName" type="card" stretch class="min-h-350px"> | |||
<el-tab-pane :label="`${orgName}`" class="ml-5px mr-5px" name="org"> | |||
<el-scrollbar max-height="650px"> | |||
<TreeFilter | |||
label="name" | |||
class="filterWidth" | |||
:title="`${orgName}列表`" | |||
:show-all="!biz" | |||
:default-expand-all="false" | |||
:request-api="orgTreeApi" | |||
@change="changeOrgTreeFilter" | |||
/> | |||
</el-scrollbar> | |||
</el-tab-pane> | |||
<!-- <el-tab-pane v-if="positionTreeApi" :label="`${positionName}`" class="ml-5px mr-5px" name="pos"> | |||
<el-scrollbar max-height="650px"> | |||
<TreeFilter | |||
label="name" | |||
class="filterWidth" | |||
:title="`${positionName}列表`" | |||
:show-all="!biz" | |||
:default-expand-all="false" | |||
:request-api="positionTreeApi" | |||
@change="changePositionTreeFilter" | |||
/> | |||
</el-scrollbar> | |||
</el-tab-pane> | |||
<el-tab-pane v-if="roleTreeApi" label="角色" class="ml-5px mr-5px" name="role"> | |||
<el-scrollbar max-height="650px"> | |||
<TreeFilter | |||
label="name" | |||
class="filterWidth" | |||
title="角色列表" | |||
:show-all="!biz" | |||
:default-expand-all="false" | |||
:request-api="roleTreeApi" | |||
@change="changeRoleTreeFilter" | |||
/> | |||
</el-scrollbar> | |||
</el-tab-pane> --> | |||
</el-tabs> | |||
</el-col> | |||
<el-col :span="10"> | |||
<ProTable ref="userTable" :columns="columns" :tool-button="false" :init-param="initParam" :request-api="userSelectorApi"> | |||
<!-- 表格 header 按钮 --> | |||
<template #tableHeader="scope"> | |||
<el-button type="primary" @click="addRecords(userTable!.tableData)">添加当前</el-button> | |||
<el-button type="primary" plain :disabled="!scope.isSelected" @click="addRecords(scope.selectedList)">添加选中</el-button> | |||
</template> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<el-button type="primary" link :icon="Plus" plain @click="addRecords([scope.row])">添加</el-button> | |||
</template> | |||
</ProTable> | |||
</el-col> | |||
<el-col :span="10"> | |||
<ProTable ref="chooseTable" :columns="columns" :tool-button="true" :data="chooseDataTmp" @search="searchRecords" @reset="resetRecords"> | |||
<!-- 表格 header 按钮 --> | |||
<template #tableHeader="scope"> | |||
<el-button type="danger" @click="delRecords(chooseTable!.tableData)">删除当前</el-button> | |||
<el-button type="danger" plain :disabled="!scope.isSelected" @click="delRecords(scope.selectedList)">删除选中</el-button> | |||
</template> | |||
<template #toolButton> | |||
<span>已选择:{{ chooseData.length }}人</span> | |||
<span v-if="maxCount">,最多选择:{{ maxCount }}人</span> | |||
</template> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<el-button type="danger" link :icon="Delete" plain @click="delRecords([scope.row])">删除</el-button> | |||
</template> | |||
</ProTable> | |||
</el-col> | |||
</el-row> | |||
</div> | |||
<template #footer> | |||
<div class="mt-20px"> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button type="primary" @click="handleOk"> 确定 </el-button> | |||
</div> | |||
</template> | |||
</form-container> | |||
</template> | |||
<script setup lang="ts" name="UserSelector"> | |||
import { SysUser, SysPosition, SysRole } from "@/api"; | |||
import { UserSelectProps, UserSelectTableInitParams } from "./interface"; | |||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface"; | |||
import { ElMessage } from "element-plus"; | |||
import { Plus, Delete } from "@element-plus/icons-vue"; | |||
const activeName = ref("org"); | |||
const emit = defineEmits({ successful: null }); // 自定义事件 | |||
// const handleClick = (tab: TabsPaneContext, event: Event) => { | |||
// console.log(tab, event); | |||
// }; | |||
const visible = ref(false); //是否显示 | |||
// 定义组件props | |||
const props = withDefaults(defineProps<UserSelectProps>(), { | |||
multiple: false, | |||
biz: false | |||
}); | |||
// 根据是否业务显示不同名称 | |||
const userName = props.biz ? "人员" : "用户"; | |||
const positionName = props.biz ? "职位" : "岗位"; | |||
const orgName = props.biz ? "机构" : "班级"; | |||
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据) | |||
const initParam = reactive<UserSelectTableInitParams>({}); | |||
// 获取 userTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const userTable = ref<ProTableInstance>(); | |||
// 获取 chooseTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
const chooseTable = ref<ProTableInstance>(); | |||
// 表格配置项 | |||
const columns: ColumnProps<SysUser.SysUserInfo>[] = [ | |||
{ type: "selection", fixed: "left", width: 50 }, | |||
{ prop: "operation", label: "操作", width: 80, fixed: "left" }, | |||
{ prop: "account", label: "账号", search: { el: "input", span: 2 } }, | |||
{ prop: "name", label: "姓名" } | |||
]; | |||
/** 显示选择器 */ | |||
function showSelector(data: SysUser.SysUserInfo[] = []) { | |||
visible.value = true; | |||
chooseDataTmp.value = data; | |||
chooseData.value = data; | |||
} | |||
/** 关闭选择器 */ | |||
function onClose() { | |||
visible.value = false; | |||
chooseDataTmp.value = []; | |||
chooseData.value = []; | |||
} | |||
/** 提交数据 */ | |||
function handleOk() { | |||
visible.value = false; | |||
emit("successful", chooseData.value); | |||
} | |||
/** 部门切换 */ | |||
function changeOrgTreeFilter(val: number | string) { | |||
userTable.value!.pageable.pageNum = 1; | |||
if (val != "") { | |||
// 如果传入的val不为空 | |||
initParam.orgId = val; | |||
} else { | |||
initParam.orgId = null; | |||
} | |||
} | |||
/** 职位切换 */ | |||
function changePositionTreeFilter(val: number | string, data: SysPosition.SysPositionTree) { | |||
userTable.value!.pageable.pageNum = 1; | |||
if (data.isPosition) { | |||
// 如果是职位 | |||
initParam.positionId = val; | |||
} else { | |||
initParam.positionId = null; | |||
} | |||
} | |||
/** 角色切换 */ | |||
function changeRoleTreeFilter(val: number | string, data: SysRole.SysRoleTree) { | |||
userTable.value!.pageable.pageNum = 1; | |||
if (data.isRole) { | |||
// 如果是角色 | |||
initParam.roleId = val; | |||
} else { | |||
// 置空 | |||
initParam.roleId = null; | |||
} | |||
} | |||
const chooseData = ref<SysUser.SysUserInfo[]>([]); //选择的数据 | |||
const chooseDataTmp = ref<SysUser.SysUserInfo[]>([]); //临时选择的数据 | |||
/** 添加记录 */ | |||
function addRecords(records: any[]) { | |||
//如果不是多选,判断是否已经添加了 | |||
if (!props.multiple) { | |||
if (chooseData.value.length > 0 || records.length > 1) { | |||
ElMessage.warning("只可选择一条"); | |||
return; | |||
} | |||
chooseData.value = records; | |||
chooseDataTmp.value = chooseData.value; | |||
} else { | |||
//如果是多选,先判断已添加列表是否有重复,有则过滤掉,没有则直接添加 | |||
records = records.filter(item => !chooseData.value.find(it => it.id == item.id)); | |||
if (props.maxCount && props.maxCount < records.length + chooseData.value.length) { | |||
ElMessage.warning("最多选择" + props.maxCount + "条"); | |||
return; | |||
} | |||
chooseData.value = chooseData.value.concat(records); //添加到已选中列表 | |||
chooseDataTmp.value = chooseData.value; | |||
} | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
/** 删除记录 */ | |||
function delRecords(records: any[]) { | |||
chooseData.value = chooseData.value.filter(item => !records.includes(item)); //过滤掉已选中的 | |||
chooseDataTmp.value = chooseData.value; | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
/** 搜索记录 */ | |||
function searchRecords() { | |||
if (chooseTable.value?.searchParam?.account) { | |||
//搜索account符合的记录 | |||
chooseDataTmp.value = chooseDataTmp.value.filter(item => item.account.includes(chooseTable.value?.searchParam.account)); //过滤掉已选中的 | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
} | |||
/** 重置记录 */ | |||
function resetRecords() { | |||
chooseDataTmp.value = chooseData.value; | |||
chooseTable.value?.refresh(); //刷新表格 | |||
} | |||
// 暴露方法 | |||
defineExpose({ showSelector }); | |||
</script> | |||
<style lang="scss" scoped> | |||
.filterWidth { | |||
width: 100%; | |||
} | |||
:deep(.el-tabs--border-card > .el-tabs__content) { | |||
padding: 5px; | |||
} | |||
:deep(.table-main) { | |||
height: 90%; | |||
} | |||
</style> |
@@ -0,0 +1,48 @@ | |||
/** | |||
* @description 用户选择器接口 | |||
* @license Apache License Version 2.0 | |||
* @Copyright (c) 2022-Now 少林寺驻北固山办事处大神父王喇嘛 | |||
* @remarks | |||
* SimpleAdmin 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款: | |||
* 1.请不要删除和修改根目录下的LICENSE文件。 | |||
* 2.请不要删除和修改SimpleAdmin源码头部的版权声明。 | |||
* 3.分发源码时候,请注明软件出处 https://gitee.com/dotnetmoyu/SimpleAdmin | |||
* 4.基于本软件的作品,只能使用 SimpleAdmin 作为后台服务,除外情况不可商用且不允许二次分发或开源。 | |||
* 5.请不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为不要删除和修改作者声明。 | |||
* 6.任何基于本软件而产生的一切法律纠纷和责任,均于我司无关 | |||
* @see https://gitee.com/dotnetmoyu/SimpleAdmin | |||
*/ | |||
import UserSelector from "./index.vue"; | |||
/** 用户选择器属性 */ | |||
export interface UserSelectProps { | |||
/** 组织树api */ | |||
orgTreeApi: (data?: any) => Promise<any>; | |||
/** 职位选择api */ | |||
positionTreeApi?: (data?: any) => Promise<any>; | |||
/** 角色选择api */ | |||
roleTreeApi?: (data?: any) => Promise<any>; | |||
/** 用户选择api */ | |||
userSelectorApi: (data?: any) => Promise<any>; | |||
/** 是否多选 */ | |||
multiple?: boolean; | |||
/** 最大用户数 */ | |||
maxCount?: number; | |||
/** 是否是业务 */ | |||
biz?: boolean; | |||
} | |||
/** 用户选择器表格初始化参数 */ | |||
export interface UserSelectTableInitParams { | |||
/** 组织ID */ | |||
orgId?: number | string | null; | |||
/** 职位ID */ | |||
positionId?: number | string | null; | |||
/** 角色ID */ | |||
roleId?: number | string | null; | |||
} | |||
/** | |||
* @description 用户选择器实例类型 | |||
*/ | |||
export type UserSelectorInstance = Omit<InstanceType<typeof UserSelector>, keyof ComponentPublicInstance | keyof UserSelectProps>; |
@@ -249,13 +249,27 @@ | |||
/* el-dialog */ | |||
.el-dialog { | |||
padding: 0!important; | |||
.el-dialog__header { | |||
padding: 15px 20px; | |||
padding: 16px; | |||
margin: 0; | |||
border-bottom: 1px solid var(--el-border-color-lighter); | |||
.el-dialog__title { | |||
font-size: 17px; | |||
} | |||
.el-dialog__headerbtn { | |||
top: 6px; | |||
} | |||
} | |||
.el-dialog__body { | |||
padding: 30px; | |||
} | |||
.el-dialog__footer { | |||
padding: 15px 16px; | |||
box-sizing: border-box; | |||
border-top: 1px solid var(--el-border-color-lighter) | |||
} | |||
} | |||
@@ -0,0 +1,127 @@ | |||
<!-- | |||
* @Description: 表单 | |||
* @Author: huguodong | |||
* @Date: 2023-12-15 15:45:28 | |||
!--> | |||
<template> | |||
<div> | |||
<form-container v-model="visible" title="人员选择" form-size="600px"> | |||
<el-form | |||
ref="userFormRef" | |||
:rules="rules" | |||
:disabled="liveUserProps.disabled" | |||
:model="liveUserProps.record" | |||
:hide-required-asterisk="liveUserProps.disabled" | |||
label-width="auto" | |||
label-suffix=" :" | |||
> | |||
<s-form-item label="指定分组推送人" prop="directorId"> | |||
<el-button link type="primary" @click="showSelector">选择</el-button> | |||
<el-tag v-if="liveUserProps.record.directorId" class="ml-3px" type="warning" closable @close="removeDirector">{{ | |||
liveUserProps.record.directorInfo?.name | |||
}}</el-tag> | |||
</s-form-item> | |||
</el-form> | |||
<template #footer> | |||
<el-button @click="onClose"> 取消 </el-button> | |||
<el-button v-show="!liveUserProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button> | |||
</template> | |||
</form-container> | |||
<user-selector ref="userSelectorRef" :org-tree-api="sysOrgApi.tree" :user-selector-api="sysUserApi.selector" @successful="handleChooseUser" /> | |||
</div> | |||
</template> | |||
<script setup lang="ts"> | |||
import { SysOrg, SysUser, sysOrgApi, sysPositionApi, sysRoleApi, sysUserApi } from "@/api"; | |||
import { FormOptEnum, SysDictEnum } from "@/enums"; | |||
import { required } from "@/utils/formRules"; | |||
import { FormInstance } from "element-plus"; | |||
import { useDictStore } from "@/stores/modules"; | |||
import { UserSelectorInstance } from "@/components/Selectors/UserSelector/interface"; | |||
const visible = ref(false); //是否显示表单 | |||
const dictStore = useDictStore(); //字典仓库 | |||
// 表单参数 | |||
const liveUserProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({ | |||
opt: FormOptEnum.ADD, | |||
record: {}, | |||
disabled: false | |||
}); | |||
// 表单验证规则 | |||
const rules = reactive({ | |||
directorId: [required("请选择指定分组推送人")] | |||
}); | |||
/** | |||
* 打开表单 | |||
* @param props 表单参数 | |||
*/ | |||
function onOpen(props: FormProps.Base<SysOrg.SysOrgInfo>) { | |||
Object.assign(liveUserProps, props); //合并参数 | |||
if (props.opt == FormOptEnum.ADD) { | |||
//如果是新增,设置默认值 | |||
} | |||
visible.value = true; //显示表单 | |||
// if (props.record.id) { | |||
// //如果传了id,就去请求api获取record | |||
// sysOrgApi.detail({ id: props.record.id }).then(res => { | |||
// liveUserProps.record = res.data; | |||
// }); | |||
// } | |||
} | |||
// 提交数据(新增/编辑) | |||
const userFormRef = ref<FormInstance>(); | |||
/** 提交表单 */ | |||
async function handleSubmit() { | |||
userFormRef.value?.validate(async valid => { | |||
if (!valid) return; //表单验证失败 | |||
return; | |||
//提交表单 | |||
await sysOrgApi | |||
.submitForm(liveUserProps.record, liveUserProps.record.id != undefined) | |||
.then(() => { | |||
liveUserProps.successful!(); //调用父组件的successful方法 | |||
}) | |||
.finally(() => { | |||
onClose(); | |||
}); | |||
}); | |||
} | |||
/** 关闭表单*/ | |||
function onClose() { | |||
visible.value = false; | |||
} | |||
const userSelectorRef = ref<UserSelectorInstance>(); //用户选择器引用 | |||
/** 显示用户选择器 */ | |||
function showSelector() { | |||
//将liveUserProps.record.directorInfo转为 SysUser.SysUserInfo[]类型 | |||
const directorInfo = liveUserProps.record.directorInfo ? [liveUserProps.record.directorInfo] : []; | |||
userSelectorRef.value?.showSelector(directorInfo); | |||
} | |||
/** 选择用户 */ | |||
function handleChooseUser(data: SysUser.SysUserInfo[]) { | |||
// 选择用户后,将用户id赋值给liveUserProps.record.directorId | |||
if (data.length > 0) { | |||
liveUserProps.record.directorId = data[0].id; | |||
liveUserProps.record.directorInfo = data[0]; | |||
} | |||
} | |||
/** 移除主管 */ | |||
function removeDirector() { | |||
liveUserProps.record.directorId = null; | |||
liveUserProps.record.directorInfo = null; | |||
} | |||
// 暴露给父组件的方法 | |||
defineExpose({ | |||
onOpen | |||
}); | |||
</script> | |||
<style lang="scss" scoped></style> |
@@ -1,9 +1,16 @@ | |||
.filter { | |||
ul,li { | |||
list-style: none; | |||
padding: 0; | |||
margin: 0; | |||
} | |||
.treeBox { | |||
box-sizing: border-box; | |||
width: 280px; | |||
height: 100%; | |||
padding: 18px; | |||
padding: 14px; | |||
margin-right: 10px; | |||
flex-shrink: 1; | |||
.title { | |||
margin: 0 0 15px; | |||
font-size: 18px; | |||
@@ -11,6 +18,36 @@ | |||
color: var(--el-color-info-dark-2); | |||
letter-spacing: 0.5px; | |||
} | |||
.btn { | |||
} | |||
.treeContent { | |||
padding: 10px 0; | |||
// height: calc(100% - 100px); | |||
// overflow: auto; | |||
.el-tree-node__content { | |||
height: 33px; | |||
} | |||
.custom-tree-node { | |||
flex: 1; | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
font-size: 14px; | |||
padding-right: 8px; | |||
.node-label { | |||
width: 100px; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
font-size: 16px; | |||
} | |||
} | |||
:deep(.el-tree-node__content) { | |||
height: 50px; | |||
} | |||
} | |||
.el-input { | |||
margin: 0 0 15px; | |||
} | |||
@@ -36,3 +73,6 @@ | |||
} | |||
} | |||
} | |||
.table-box { | |||
width: calc(100% - 280px); | |||
} |
@@ -5,19 +5,45 @@ | |||
!--> | |||
<template> | |||
<div class="main-box"> | |||
<div class="custom-tree-container card filter"> | |||
<p>摄像头分组管理</p> | |||
<el-tree style="max-width: 600px" :data="dataSource" node-key="id" default-expand-all :expand-on-click-node="false"> | |||
<template #default="{ node, data }"> | |||
<span class="custom-tree-node"> | |||
<span>{{ node.label }}</span> | |||
<span> | |||
<a @click="append(data)"> Append </a> | |||
<a style="margin-left: 8px" @click="remove(node, data)"> Delete </a> | |||
<div class="card treeBox"> | |||
<p class="title">摄像头分组管理</p> | |||
<div class="btn"> | |||
<el-button @click="append('add', {})" type="primary">添加分组</el-button> | |||
</div> | |||
<!-- <div class="treeContent"> | |||
<ul class="treeList" v-for="(item, i) in treeData" :key="i"> | |||
<li class="treeItem"> | |||
<div class="treeLabel">{{ item.label }}</div> | |||
<div class="treeBtn"> | |||
<el-icon><Edit /></el-icon> | |||
<el-icon><Delete /></el-icon> | |||
</div> | |||
</li> | |||
</ul> | |||
</div> --> | |||
<div class="treeContent"> | |||
<el-tree | |||
style="max-width: 600px" | |||
:data="treeData" | |||
node-key="id" | |||
default-expand-all | |||
:expand-on-click-node="false" | |||
:check-on-click-node="true" | |||
:highlight-current="true" | |||
@node-click="handleNodeClick" | |||
> | |||
<template #default="{ node, data }"> | |||
<span class="custom-tree-node"> | |||
<span class="node-label" :title="node.label">{{ node.label }}</span> | |||
<span v-if="data.id && data.id != '-1'"> | |||
<el-icon size="16" @click.stop="append('edit', data)"><Edit /></el-icon> | |||
<el-icon size="16" @click.stop="pushPerson('push', data)" style="margin-left: 8px"><UserFilled /></el-icon> | |||
<el-icon size="16" @click.stop="remove(node, data)" style="margin-left: 8px"><Delete /></el-icon> | |||
</span> | |||
</span> | |||
</span> | |||
</template> | |||
</el-tree> | |||
</template> | |||
</el-tree> | |||
</div> | |||
</div> | |||
<div class="table-box"> | |||
<ProTable ref="proTable" title="视频列表" :columns="columns" :request-api="monitorLIVEApi.page"> | |||
@@ -32,9 +58,10 @@ | |||
:disabled="!scope.isSelected" | |||
@click="onDelete(scope.selectedListIds, '删除所选数据')" | |||
/> | |||
<el-button plain @click="move(scope.selectedListIds, '移动至分组')" type="success">移动至分组</el-button> | |||
</template> | |||
<!-- 表格 菜单类型 按钮 --> | |||
<template #menuType="scope"> | |||
<!-- <template #menuType="scope"> | |||
<el-space wrap> | |||
<el-tag v-if="scope.row.menuType === MenuTypeDictEnum.MENU" type="success">{{ | |||
dictStore.dictTranslation(SysDictEnum.MENU_TYPE, MenuTypeDictEnum.MENU) | |||
@@ -45,17 +72,37 @@ | |||
<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> --> | |||
<!-- 操作 --> | |||
<template #operation="scope"> | |||
<s-button link :opt="FormOptEnum.VIEW" @click="onDetail(scope.row)"> 查看 </s-button> | |||
<s-button link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)">编辑</s-button> | |||
<s-button link :opt="FormOptEnum.VIEW" @click="onDetail(scope.row)"> 查看 </s-button> | |||
<s-button link :opt="FormOptEnum.DELETE" @click="onDelete([scope.row.id], `确定删除该摄像头吗?`)" /> | |||
<s-button link :opt="FormOptEnum.VIEW" @click="pushPerson(scope.row)"> 推送人 </s-button> | |||
</template> | |||
</ProTable> | |||
<!-- 添加分组弹框 --> | |||
<el-dialog v-model="groupVisible" :title="groupTitle" width="600px" :before-close="closeGroup"> | |||
<el-form :model="groupForm" :rules="groupRules" ref="groupFormRef" label-width="80px"> | |||
<el-form-item label="分组名称" prop="name"> | |||
<el-input v-model="groupForm.name" placeholder="请输入分组名称"></el-input> | |||
</el-form-item> | |||
</el-form> | |||
<template #footer> | |||
<div class="dialog-footer"> | |||
<el-button type="primary" @click="onSubmit">提交</el-button> | |||
<el-button @click="closeGroup">取消</el-button> | |||
</div> | |||
</template> | |||
</el-dialog> | |||
<!-- 新增/编辑表单 --> | |||
<Form ref="formRef" /> | |||
<el-dialog v-model="visible" :title="detailData.title" width="830px" :before-close="handleClose"> | |||
<!-- 人员选择 --> | |||
<userForm ref="userFormRef" /> | |||
<!-- 视频详情 --> | |||
<el-dialog v-model="visible" :title="detailData.title" width="830px" :before-close="closeGroup"> | |||
<div> | |||
<div class="dialogHeader"> | |||
<div></div> | |||
@@ -83,7 +130,7 @@ | |||
<script setup lang="tsx" name="sysSpa"> | |||
import VideoPlay from "@/components/VideoPlay/videoplay.vue"; | |||
import { ElMessage } from "element-plus"; | |||
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"; | |||
@@ -91,48 +138,100 @@ | |||
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 { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | |||
import './aliyun-rts-sdk.js' | |||
// import './aliyun-rts-sdk.js' | |||
import './ali.js' | |||
interface Tree { | |||
id: number | |||
const groupVisible = ref(false); //是否显示表单 | |||
const groupTitle = ref('新增分组'); | |||
const groupForm = reactive({ | |||
name: '' | |||
}); | |||
const groupRules = ref({ | |||
name: [{ required: true, message: '请输入分组名称', trigger: 'blur' }] | |||
}); | |||
const groupFormRef = ref(null); | |||
const closeGroup = () => { | |||
groupVisible.value = false; | |||
groupFormRef.value.resetFields(); | |||
groupForm.name = '' | |||
} | |||
const onSubmit = () => { | |||
groupFormRef.value.validate((valid:any) => { | |||
if (valid) { | |||
closeGroup() | |||
ElMessage.success('提交成功'); | |||
} else { | |||
return false; | |||
} | |||
}); | |||
}; | |||
// 摄像头分组 | |||
interface Tree { | |||
id: any | |||
label: string | |||
children?: Tree[] | |||
} | |||
let id = 1000 | |||
const append = (data: Tree) => { | |||
const newChild = { id: id++, label: 'testtest', children: [] } | |||
if (!data.children) { | |||
data.children = [] | |||
} | |||
data.children.push(newChild) | |||
dataSource.value = [...dataSource.value] | |||
const handleNodeClick = (data: Tree) => { | |||
console.log(data) | |||
} | |||
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) | |||
dataSource.value = [...dataSource.value] | |||
} | |||
const dataSource = ref<Tree[]>([ | |||
const treeData = ref<Tree[]>([ | |||
{ | |||
id: '', | |||
label: '全部', | |||
}, | |||
{ | |||
id: '-1', | |||
label: '无分组', | |||
}, | |||
{ | |||
id: 1, | |||
label: 'Level one 1', | |||
label: '走廊', | |||
}, | |||
{ | |||
id: 2, | |||
label: 'Level one 2', | |||
label: '大厅', | |||
}, | |||
{ | |||
id: 3, | |||
label: 'Level one 3', | |||
label: '厨房', | |||
}, | |||
]) | |||
// 新增分组 | |||
const append = (type:string,data: Tree) => { | |||
console.log(type,data) | |||
groupVisible.value = true; | |||
if(type == 'edit') { | |||
groupForm.name = treeData.value.find(item => item.id ==data.id).label; | |||
} | |||
// const newChild = { id: id++, label: 'testtest', children: [] } | |||
// if (!data.children) { | |||
// data.children = [] | |||
// } | |||
// data.children.push(newChild) | |||
// treeData.value = [...treeData.value] | |||
} | |||
// 删除分组 | |||
const remove = (node: Node, data: Tree) => { | |||
const parent = node.parent | |||
const children: Tree[] = parent.data.children || parent.data | |||
const index = children.findIndex((d) => d.id === data.id) | |||
children.splice(index, 1) | |||
treeData.value = [...treeData.value] | |||
} | |||
// 设置分组推送人 | |||
// async function pushPerson(type:string,data: Tree) { | |||
// } | |||
const visible = ref(false); //是否显示表单 | |||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | |||
@@ -181,7 +280,7 @@ const dataSource = ref<Tree[]>([ | |||
}, | |||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | |||
]; | |||
/** | |||
* 删除 | |||
* @param ids id数组 | |||
@@ -192,7 +291,29 @@ const dataSource = ref<Tree[]>([ | |||
await useHandleData(monitorLIVEApi.delete, { ids }, msg); | |||
RefreshTable(); | |||
} | |||
async function move(ids: string[], msg: string) { | |||
return new Promise((resolve, reject) => { | |||
ElMessageBox.confirm(`是否${msg}?`, "温馨提示", { | |||
confirmButtonText: "确定", | |||
cancelButtonText: "取消", | |||
type: 'warning', | |||
draggable: true | |||
}) | |||
.then(async () => { | |||
// const res = await api(params); | |||
// if (!res) return reject(false); | |||
// ElMessage({ | |||
// type: "success", | |||
// message: `${message}成功!` | |||
// }); | |||
resolve(true); | |||
}) | |||
.catch(() => { | |||
//啥也不干 | |||
}); | |||
}) | |||
} | |||
/** | |||
* 刷新表格 | |||
*/ | |||
@@ -209,6 +330,18 @@ const formRef = ref<InstanceType<typeof Form> | null>(null); | |||
function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
// 人员选择 | |||
// 表单引用 | |||
const userFormRef = ref<InstanceType<typeof Form> | null>(null); | |||
/** | |||
* 打开表单 | |||
* @param opt 操作类型 | |||
* @param record 记录 | |||
*/ | |||
function pushPerson(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
userFormRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | |||
} | |||
// 详情数据 | |||
// let detailData: globalThis.Ref<{}> | |||
let detailData = reactive({ | |||
@@ -253,16 +386,6 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
// detailData.videoUrl = data.pullStreamUrls[2].url; | |||
detailData.streamId = data.streamId; | |||
detailData.videoToken = data.videoToken; | |||
// let timer = setInterval(() => { | |||
// num+=1 | |||
// if(num > 2) { | |||
// } else { | |||
// getvideo1() | |||
// } | |||
// },200) | |||
// getvideo1() | |||
getvideo2() | |||
} | |||
@@ -274,7 +397,6 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
// let url = detailData.videoUrl.replace('http://rts-pull-live.deepeleph.com', '/Files') | |||
// console.log(url,888) | |||
let pullStreamUrl = detailData.videoUrl; | |||
console.log(pullStreamUrl,888) | |||
const mediaEle = document.querySelector('video'); | |||
aliRts.on("onError", (err) => { | |||
console.log(`errorCode: ${err.errorCode}`); | |||
@@ -354,11 +476,16 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
detailData.videoUrl = '' | |||
detailData.videoType = '' | |||
stopUrl(); | |||
player.value.dispose(); | |||
if(player.value) { | |||
player.value.dispose(); | |||
} | |||
}; | |||
function stopUrl() { | |||
detailData.videoUrl = '' | |||
player.value.dispose(); | |||
if(player.value) { | |||
player.value.dispose(); | |||
} | |||
let params = { | |||
sensorId: detailData.sensorId, | |||
streamId: detailData.streamId, | |||
@@ -370,32 +497,15 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||
if (code == 200) { | |||
// ElMessage.success(msg); | |||
} | |||
}); | |||
}) | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
@import url("https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css"); | |||
@import "./index.scss"; | |||
.detailpic { | |||
width: 800px; | |||
object-fit: cover; | |||
} | |||
.linebox { | |||
margin-top: 20px; | |||
} | |||
/* 自定义样式 */ | |||
.vjs-custom-skin .vjs-play-control { | |||
/* 播放按钮的样式 */ | |||
} | |||
.dialogHeader { | |||
display: flex; | |||
justify-content: space-between; | |||