@@ -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 */ | ||||
.el-dialog { | .el-dialog { | ||||
padding: 0!important; | |||||
.el-dialog__header { | .el-dialog__header { | ||||
padding: 15px 20px; | |||||
padding: 16px; | |||||
margin: 0; | margin: 0; | ||||
border-bottom: 1px solid var(--el-border-color-lighter); | border-bottom: 1px solid var(--el-border-color-lighter); | ||||
.el-dialog__title { | .el-dialog__title { | ||||
font-size: 17px; | 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; | box-sizing: border-box; | ||||
width: 280px; | width: 280px; | ||||
height: 100%; | height: 100%; | ||||
padding: 18px; | |||||
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; | ||||
@@ -11,6 +18,36 @@ | |||||
color: var(--el-color-info-dark-2); | color: var(--el-color-info-dark-2); | ||||
letter-spacing: 0.5px; | 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 { | .el-input { | ||||
margin: 0 0 15px; | margin: 0 0 15px; | ||||
} | } | ||||
@@ -36,3 +73,6 @@ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
.table-box { | |||||
width: calc(100% - 280px); | |||||
} |
@@ -5,19 +5,45 @@ | |||||
!--> | !--> | ||||
<template> | <template> | ||||
<div class="main-box"> | <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> | ||||
</span> | |||||
</template> | |||||
</el-tree> | |||||
</template> | |||||
</el-tree> | |||||
</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.page"> | ||||
@@ -32,9 +58,10 @@ | |||||
:disabled="!scope.isSelected" | :disabled="!scope.isSelected" | ||||
@click="onDelete(scope.selectedListIds, '删除所选数据')" | @click="onDelete(scope.selectedListIds, '删除所选数据')" | ||||
/> | /> | ||||
<el-button plain @click="move(scope.selectedListIds, '移动至分组')" type="success">移动至分组</el-button> | |||||
</template> | </template> | ||||
<!-- 表格 菜单类型 按钮 --> | <!-- 表格 菜单类型 按钮 --> | ||||
<template #menuType="scope"> | |||||
<!-- <template #menuType="scope"> | |||||
<el-space wrap> | <el-space wrap> | ||||
<el-tag v-if="scope.row.menuType === MenuTypeDictEnum.MENU" type="success">{{ | <el-tag v-if="scope.row.menuType === MenuTypeDictEnum.MENU" type="success">{{ | ||||
dictStore.dictTranslation(SysDictEnum.MENU_TYPE, MenuTypeDictEnum.MENU) | 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-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-tag v-if="scope.row.isHome === true" type="danger">首页</el-tag> | ||||
</el-space> | </el-space> | ||||
</template> | |||||
</template> --> | |||||
<!-- 操作 --> | <!-- 操作 --> | ||||
<template #operation="scope"> | <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.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.DELETE" @click="onDelete([scope.row.id], `确定删除该摄像头吗?`)" /> | ||||
<s-button link :opt="FormOptEnum.VIEW" @click="pushPerson(scope.row)"> 推送人 </s-button> | |||||
</template> | </template> | ||||
</ProTable> | </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" /> | <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> | ||||
<div class="dialogHeader"> | <div class="dialogHeader"> | ||||
<div></div> | <div></div> | ||||
@@ -83,7 +130,7 @@ | |||||
<script setup lang="tsx" name="sysSpa"> | <script setup lang="tsx" name="sysSpa"> | ||||
import VideoPlay from "@/components/VideoPlay/videoplay.vue"; | 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 type Node from 'element-plus/es/components/tree/src/model/node' | ||||
import { monitorLIVEApi, monitorLiveButtonCode } from "@/api"; | import { monitorLIVEApi, monitorLiveButtonCode } from "@/api"; | ||||
import { ZJRQ } from "@/api/interface"; | import { ZJRQ } from "@/api/interface"; | ||||
@@ -91,48 +138,100 @@ | |||||
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 Form from "./components/form.vue"; | import Form from "./components/form.vue"; | ||||
import userForm from "./components/userForm.vue"; | |||||
import { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | import { FormOptEnum, SysDictEnum, MenuTypeDictEnum } from "@/enums"; | ||||
import './aliyun-rts-sdk.js' | |||||
// import './aliyun-rts-sdk.js' | |||||
import './ali.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 | label: string | ||||
children?: Tree[] | children?: Tree[] | ||||
} | } | ||||
let id = 1000 | 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, | id: 1, | ||||
label: 'Level one 1', | |||||
label: '走廊', | |||||
}, | }, | ||||
{ | { | ||||
id: 2, | id: 2, | ||||
label: 'Level one 2', | |||||
label: '大厅', | |||||
}, | }, | ||||
{ | { | ||||
id: 3, | 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); //是否显示表单 | const visible = ref(false); //是否显示表单 | ||||
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | // 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数) | ||||
@@ -181,7 +280,7 @@ const dataSource = ref<Tree[]>([ | |||||
}, | }, | ||||
{ prop: "operation", label: "操作", width: 250, fixed: "right" } | { prop: "operation", label: "操作", width: 250, fixed: "right" } | ||||
]; | ]; | ||||
/** | /** | ||||
* 删除 | * 删除 | ||||
* @param ids id数组 | * @param ids id数组 | ||||
@@ -192,7 +291,29 @@ const dataSource = ref<Tree[]>([ | |||||
await useHandleData(monitorLIVEApi.delete, { ids }, msg); | await useHandleData(monitorLIVEApi.delete, { ids }, msg); | ||||
RefreshTable(); | 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 = {}) { | function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | ||||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable }); | 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: globalThis.Ref<{}> | ||||
let detailData = reactive({ | let detailData = reactive({ | ||||
@@ -253,16 +386,6 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
// detailData.videoUrl = data.pullStreamUrls[2].url; | // detailData.videoUrl = data.pullStreamUrls[2].url; | ||||
detailData.streamId = data.streamId; | detailData.streamId = data.streamId; | ||||
detailData.videoToken = data.videoToken; | detailData.videoToken = data.videoToken; | ||||
// let timer = setInterval(() => { | |||||
// num+=1 | |||||
// if(num > 2) { | |||||
// } else { | |||||
// getvideo1() | |||||
// } | |||||
// },200) | |||||
// getvideo1() | // getvideo1() | ||||
getvideo2() | getvideo2() | ||||
} | } | ||||
@@ -274,7 +397,6 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
// let url = detailData.videoUrl.replace('http://rts-pull-live.deepeleph.com', '/Files') | // let url = detailData.videoUrl.replace('http://rts-pull-live.deepeleph.com', '/Files') | ||||
// console.log(url,888) | // console.log(url,888) | ||||
let pullStreamUrl = detailData.videoUrl; | let pullStreamUrl = detailData.videoUrl; | ||||
console.log(pullStreamUrl,888) | |||||
const mediaEle = document.querySelector('video'); | const mediaEle = document.querySelector('video'); | ||||
aliRts.on("onError", (err) => { | aliRts.on("onError", (err) => { | ||||
console.log(`errorCode: ${err.errorCode}`); | console.log(`errorCode: ${err.errorCode}`); | ||||
@@ -354,11 +476,16 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
detailData.videoUrl = '' | detailData.videoUrl = '' | ||||
detailData.videoType = '' | detailData.videoType = '' | ||||
stopUrl(); | stopUrl(); | ||||
player.value.dispose(); | |||||
if(player.value) { | |||||
player.value.dispose(); | |||||
} | |||||
}; | }; | ||||
function stopUrl() { | function stopUrl() { | ||||
detailData.videoUrl = '' | detailData.videoUrl = '' | ||||
player.value.dispose(); | |||||
if(player.value) { | |||||
player.value.dispose(); | |||||
} | |||||
let params = { | let params = { | ||||
sensorId: detailData.sensorId, | sensorId: detailData.sensorId, | ||||
streamId: detailData.streamId, | streamId: detailData.streamId, | ||||
@@ -370,32 +497,15 @@ function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) { | |||||
if (code == 200) { | if (code == 200) { | ||||
// ElMessage.success(msg); | // ElMessage.success(msg); | ||||
} | } | ||||
}); | }); | ||||
}) | }) | ||||
} | } | ||||
</script> | </script> | ||||
<style lang="scss" scoped> | <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 url("https://g.alicdn.com/apsara-media-box/imp-web-player/2.16.3/skins/default/aliplayer-min.css"); | ||||
@import "./index.scss"; | @import "./index.scss"; | ||||
.detailpic { | |||||
width: 800px; | |||||
object-fit: cover; | |||||
} | |||||
.linebox { | |||||
margin-top: 20px; | |||||
} | |||||
/* 自定义样式 */ | |||||
.vjs-custom-skin .vjs-play-control { | |||||
/* 播放按钮的样式 */ | |||||
} | |||||
.dialogHeader { | .dialogHeader { | ||||
display: flex; | display: flex; | ||||
justify-content: space-between; | justify-content: space-between; | ||||