update: 部门管理over

This commit is contained in:
easy 2023-02-14 17:52:59 +08:00
parent b02f7f5add
commit da5b9bd790
11 changed files with 596 additions and 74 deletions

View File

@ -10,6 +10,8 @@ export const updateDepartment_api = (data: object) => server.patch(`/organizatio
export const delDepartment_api = (id: string) => server.remove(`/organization/${id}`);
// 获取所属产品列表
export const getDeviceProduct_api = (data: object) => server.get(`/device/product/_query/no-paging`, data);
// 获取产品列表
export const getDeviceOrProductList_api = (data: object) => server.post(`/device-product/_query`, data);
// 获取设备列表
@ -25,3 +27,12 @@ export const bindDeviceOrProductList_api = (type: 'device' | 'product', data: ob
export const unBindDeviceOrProduct_api = (type: 'device' | 'product', data: object) => server.post(`/assets/unbind/${type}`, data);
// 批量更新权限
export const updatePermission_api = (type: 'device' | 'product', parentId: string, data: object) => server.put(`/assets/permission/${type}/org/${parentId}/_batch`, data);
// 用户相关
// 获取绑定用户列表
export const getBindUserList_api = (data: object) => server.post(`/user/_query`, data);
// 绑定用户
export const bindUser_api = (parentId:string,data: object) => server.post(`/organization/${parentId}/users/_bind`, data);
// 解绑用户
export const unBindUser_api = (parentId:string,data: object) => server.post(`/organization/${parentId}/users/_unbind`, data);

View File

@ -28,7 +28,7 @@
<a-checkbox-group v-model:value="bulkList" :options="options" />
</div>
<Search :columns="query.columns" @search="query.search" />
<Search :columns="props.queryColumns" @search="query.search" />
<JTable
ref="tableRef"
@ -118,6 +118,7 @@ import { message } from 'ant-design-vue';
const emits = defineEmits(['confirm']);
const props = defineProps<{
queryColumns: any[];
parentId: string;
allPermission: dictType;
assetType: 'product' | 'device';
@ -139,7 +140,6 @@ const dialog = {
permission: item.selectPermissions,
}));
// console.log(params);
dialog.loading.value = true;
bindDeviceOrProductList_api(props.assetType, params)
.then(() => {
@ -334,6 +334,14 @@ const table: any = {
data.forEach((item) => {
item.permissionList = permissionObj[item.id];
item.selectPermissions = ['read'];
//
if(props.assetType === 'product') {
item.state = {
value: item.state === 1 ? 'online': item.state === 0 ? 'offline': '',
text: item.state === 1 ? '正常': item.state === 0 ? '禁用': ''
}
}
});
resolve({

View File

@ -117,8 +117,8 @@ const form = reactive({
form.loading = true;
const api = form.data.id ? updateDepartment_api : addDepartment_api;
api(form.data)
.then(() => {
emits('refresh');
.then((resp:any) => {
emits('refresh',resp.result.id);
dialog.changeVisible(false);
})
.finally(() => (form.loading = false));

View File

@ -46,7 +46,6 @@ const dialog = {
},
//
changeVisible: (ids: string[], permissionList: string[]) => {
console.log(ids, permissionList);
form.permission = [...permissionList];
form.assetIdList = ids;
options.value = setOptions(permissionList);

View File

@ -10,7 +10,7 @@
<search-outlined />
</template>
</a-input>
<a-button type="primary" @click="openDialog" class="add-btn">
<a-button type="primary" @click="openDialog()" class="add-btn">
新增
</a-button>
<a-tree
@ -21,7 +21,7 @@
>
<template #title="{ name, data }">
<span>{{ name }}</span>
<span class="func-btns">
<span class="func-btns" @click="(e) => e.stopPropagation()">
<a-tooltip>
<template #title>编辑</template>
<a-button style="padding: 0" type="link">
@ -65,7 +65,7 @@
<EditDepartmentDialog
:tree-data="sourceTree"
ref="editDialogRef"
@refresh="getTree"
@refresh="refresh"
/>
</div>
</template>
@ -84,6 +84,7 @@ import {
} from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
const save = useRoute().query.save;
const emits = defineEmits(['change']);
const searchValue = ref(''); //
const loading = ref<boolean>(false); //
@ -92,11 +93,6 @@ const treeMap = new Map(); // 数据的map版本
const treeData = ref<any[]>([]); //
const selectedKeys = ref<string[]>([]); //
getTree();
watch(selectedKeys, (n) => {
emits('change', n[0]);
});
function getTree() {
loading.value = true;
const params = {
@ -119,7 +115,7 @@ function getTree() {
.finally(() => {
loading.value = false;
});
};
}
const search = debounce(() => {
const key = searchValue.value;
const treeArray = new Map();
@ -167,14 +163,30 @@ function delDepartment(id: string) {
getTree();
});
}
function refresh(id: string) {
// @ts-ignore
window?.onSaveSuccess && window.onSaveSuccess('department', id);
window.close();
getTree();
}
//
const editDialogRef = ref(); //
const openDialog = (row: any = {}) => {
editDialogRef.value.openDialog(true, row);
};
init();
function init() {
getTree();
watch(selectedKeys, (n) => {
emits('change', n[0]);
});
if (save) {
nextTick(() => {
openDialog();
});
}
}
</script>
<style lang="less" scoped>

View File

@ -0,0 +1,33 @@
<template>
<a-modal
v-model:visible="visible"
title="绑定"
width="520px"
@ok="handleOk"
class="edit-dialog-container"
cancelText="取消"
okText="确定"
>
是否继续分配产品下的具体设备
</a-modal>
</template>
<script setup lang="ts">
const emits = defineEmits(['confirm']);
const visible = ref<boolean>(false);
const handleOk = () => {
emits('confirm');
changeVisible();
};
//
const changeVisible = () => {
visible.value = !visible.value;
};
//
defineExpose({
openDialog: changeVisible,
});
</script>
<style scoped></style>

View File

@ -125,6 +125,7 @@
<div class="dialogs">
<AddDeviceOrProductDialog
ref="addDialogRef"
:query-columns="query.columns"
:parent-id="props.parentId"
:all-permission="table.permissionList.value"
asset-type="device"
@ -155,14 +156,17 @@ import {
getPermission_api,
getPermissionDict_api,
unBindDeviceOrProduct_api,
getDeviceProduct_api,
} from '@/api/system/department';
import { intersection } from 'lodash-es';
import { dictType } from '../typing.d.ts';
import { message } from 'ant-design-vue';
const emits = defineEmits(['update:bindBool']);
const props = defineProps<{
parentId: string;
bindBool: boolean;
}>();
const query = {
columns: [
@ -186,6 +190,41 @@ const query = {
type: 'string',
},
},
{
title: '所属产品',
dataIndex: 'productId$product-info',
key: 'productId$product-info',
ellipsis: true,
fixed: 'left',
search: {
type: 'select',
options: () =>
new Promise((resolve) => {
const params = {
paging: false,
'sorts[0].name': 'createTime',
'sorts[0].order': 'desc',
};
getDeviceProduct_api(params).then((resp: any) => {
const result = resp.result.map((item: any) => ({
label: item.name,
value: item.id,
}));
resolve(result);
});
}),
},
},
{
title: '注册时间',
dataIndex: 'registryTime',
key: 'registryTime',
ellipsis: true,
fixed: 'left',
search: {
type: 'date',
},
},
{
title: '状态',
dataIndex: 'state',
@ -279,13 +318,16 @@ const table = {
const { pageIndex, pageSize, total, data } =
resp.result as resultType;
const ids = data.map((item) => item.id);
getPermission_api('device',ids, parentId).then((perResp: any) => {
getPermission_api('device', ids, parentId).then(
(perResp: any) => {
const permissionObj = {};
perResp.result.forEach((item: any) => {
permissionObj[item.assetId] = item.grantedPermissions;
permissionObj[item.assetId] =
item.grantedPermissions;
});
data.forEach(
(item) => (item.permission = permissionObj[item.id]),
(item) =>
(item.permission = permissionObj[item.id]),
);
resolve({
@ -298,7 +340,8 @@ const table = {
},
status: 200,
});
});
},
);
});
}),
//
@ -393,6 +436,10 @@ const addDialogRef = ref();
const editDialogRef = ref();
table.init();
nextTick(() => {
props.bindBool && table.clickAdd();
emits('update:bindBool', false);
});
</script>
<style lang="less" scoped>

View File

@ -7,13 +7,19 @@
<div class="right">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="product" tab="产品">
<Product :parentId="departmentId" />
<Product
:parentId="departmentId"
@open-device-bind="openDeviceBind"
/>
</a-tab-pane>
<a-tab-pane key="device" tab="设备">
<Device :parentId="departmentId" />
<Device
:parentId="departmentId"
v-model:bindBool="bindBool"
/>
</a-tab-pane>
<a-tab-pane key="user" tab="用户">
<User />
<User :parentId="departmentId" />
</a-tab-pane>
</a-tabs>
</div>
@ -30,6 +36,12 @@ import User from './user/index.vue';
const activeKey = ref<'product' | 'device' | 'user'>('product');
const departmentId = ref<string>('');
const bindBool = ref<boolean>(false);
const openDeviceBind = () => {
bindBool.value = true;
activeKey.value = 'device';
};
</script>
<style lang="less" scoped>

View File

@ -125,10 +125,11 @@
<div class="dialogs">
<AddDeviceOrProductDialog
ref="addDialogRef"
:query-columns="query.columns"
:parent-id="props.parentId"
:all-permission="table.permissionList.value"
asset-type="product"
@confirm="table.refresh"
@confirm="table.addConfirm"
/>
<EditPermissionDialog
ref="editDialogRef"
@ -137,6 +138,7 @@
asset-type="product"
@confirm="table.refresh"
/>
<NextDialog ref="nextDialogRef" @confirm="emits('openDeviceBind')" />
</div>
</div>
</template>
@ -149,6 +151,7 @@ import {
} from '@ant-design/icons-vue';
import AddDeviceOrProductDialog from '../components/AddDeviceOrProductDialog.vue';
import EditPermissionDialog from '../components/EditPermissionDialog.vue';
import NextDialog from '../components/NextDialog.vue';
import { getImage } from '@/utils/comm';
import {
getDeviceOrProductList_api,
@ -161,6 +164,7 @@ import { intersection } from 'lodash-es';
import { dictType } from '../typing.d.ts';
import { message } from 'ant-design-vue';
const emits = defineEmits(['openDeviceBind'])
const props = defineProps<{
parentId: string;
}>();
@ -196,16 +200,12 @@ const query = {
type: 'select',
options: [
{
label: '在线',
value: 'online',
},
{
label: '离线',
value: 'offline',
label: '正常',
value: 1,
},
{
label: '禁用',
value: 'notActive',
value: 0,
},
],
},
@ -279,14 +279,30 @@ const table = {
const { pageIndex, pageSize, total, data } =
resp.result as resultType;
const ids = data.map((item) => item.id);
getPermission_api('product', ids, parentId).then((perResp: any) => {
getPermission_api('product', ids, parentId).then(
(perResp: any) => {
const permissionObj = {};
perResp.result.forEach((item: any) => {
permissionObj[item.assetId] = item.grantedPermissions;
permissionObj[item.assetId] =
item.grantedPermissions;
});
data.forEach((item) => {
item.permission = permissionObj[item.id];
item.state = {
value:
item.state === 1
? 'online'
: item.state === 0
? 'offline'
: '',
text:
item.state === 1
? '正常'
: item.state === 0
? '禁用'
: '',
};
});
data.forEach(
(item) => (item.permission = permissionObj[item.id]),
);
resolve({
code: 200,
@ -298,7 +314,8 @@ const table = {
},
status: 200,
});
});
},
);
});
}),
//
@ -350,6 +367,7 @@ const table = {
},
clickEdit: (row?: any) => {
const ids = row ? [row.id] : [...table._selectedRowKeys.value];
if (row || table.selectedRows.length === 1) {
const permissionList =
row?.permission || table.selectedRows[0].permission;
@ -387,11 +405,15 @@ const table = {
tableRef.value.reload();
});
},
addConfirm: ()=>{
table.refresh()
nextDialogRef.value && nextDialogRef.value.openDialog()
}
};
const addDialogRef = ref();
const editDialogRef = ref();
const nextDialogRef = ref();
table.init();
</script>

View File

@ -0,0 +1,165 @@
<template>
<a-modal
class="add-bind-user-dialog-container"
title="绑定"
width="1440px"
@ok="dialog.handleOk"
centered
:confirmLoading="dialog.loading.value"
cancelText="取消"
okText="确定"
v-model:visible="dialog.visible.value"
>
<Search :columns="query.columns" @search="query.search" />
<div class="table">
<JTable
ref="tableRef"
:columns="table.columns"
:request="table.requestFun"
:params="query.params"
:rowSelection="{
selectedRowKeys: table._selectedRowKeys,
onChange: table.onSelectChange,
}"
@cancelSelect="table.cancelSelect"
model="TABLE"
:defaultParams="{
sorts: [{ name: 'createTime', order: 'desc' }],
}"
/>
</div>
</a-modal>
</template>
<script setup lang="ts">
import { bindUser_api, getBindUserList_api } from '@/api/system/department';
import { message } from 'ant-design-vue';
const emits = defineEmits(['confirm']);
const props = defineProps({
parentId: String,
});
//
const dialog = {
loading: ref<boolean>(false),
visible: ref<boolean>(false),
handleOk: () => {
if (table._selectedRowKeys.length && props.parentId) {
bindUser_api(props.parentId, table._selectedRowKeys).then(() => {
emits('confirm');
message.success('操作成功');
dialog.changeVisible();
});
} else {
dialog.changeVisible();
}
},
//
changeVisible: () => {
if (!dialog.visible.value) query.search({});
dialog.visible.value = !dialog.visible.value;
},
};
//
defineExpose({
openDialog: dialog.changeVisible,
});
const query = {
columns: [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
ellipsis: true,
fixed: 'left',
search: {
type: 'string',
},
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
ellipsis: true,
fixed: 'left',
search: {
type: 'string',
},
},
],
params: ref({}),
search: (params: any) => {
query.params.value = params;
},
};
const table = reactive({
columns: [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
},
],
_selectedRowKeys: [] as string[],
requestFun: async (oParams: any) => {
table.cancelSelect();
if (props.parentId) {
const params = {
...oParams,
sorts: [{ name: 'createTime', order: 'desc' }],
terms: [
...oParams.terms,
{
terms: [
{
column: 'id$in-dimension$org$not',
value: props.parentId,
},
],
},
],
};
const resp: any = await getBindUserList_api(params);
return {
code: resp.status,
result: resp.result,
status: resp.status,
};
} else {
return {
code: 200,
result: {
data: [],
pageIndex: 0,
pageSize: 0,
total: 0,
},
status: 200,
};
}
},
onSelectChange: (keys: string[]) => {
table._selectedRowKeys = keys;
},
cancelSelect: () => {
table._selectedRowKeys = [];
},
});
</script>
<style lang="less" scoped>
:deep(.add-bind-user-dialog-container) {
.table {
height: 600px;
overflow-y: auto;
}
}
</style>

View File

@ -1,13 +1,226 @@
<template>
<div>
用户
<Search :columns="query.columns" @search="query.search" />
<JTable
ref="tableRef"
:columns="table.columns"
:request="table.requestFun"
:params="query.params"
:rowSelection="{
selectedRowKeys: table._selectedRowKeys,
onChange: table.onSelectChange,
}"
@cancelSelect="table.cancelSelect"
model="TABLE"
>
<template #headerTitle>
<a-button
type="primary"
@click="table.openDialog"
style="margin-right: 10px"
>
<AIcon type="PlusOutlined" />绑定用户
</a-button>
<a-popconfirm
title="是否解除绑定"
ok-text="确定"
cancel-text="取消"
@confirm="table.unBind()"
>
<a-button
><AIcon type="DisconnectOutlined" />批量解绑</a-button
>
</a-popconfirm>
</template>
<template #status="slotProps">
<BadgeStatus
:status="slotProps.status"
:text="slotProps.status ? '正常' : '禁用'"
:statusNames="{
1: 'success',
0: 'error',
}"
></BadgeStatus>
</template>
<template #action="slotProps">
<a-space :size="16">
<a-popconfirm
title="是否解除绑定"
ok-text="确定"
cancel-text="取消"
@confirm="table.unBind(slotProps)"
>
<a-button style="padding: 0" type="link">
<AIcon type="DisconnectOutlined" />
</a-button>
</a-popconfirm>
</a-space>
</template>
</JTable>
<div class="dialogs">
<AddBindUserDialog
ref="addDialogRef"
:parent-id="props.parentId"
@confirm="table.refresh"
/>
</div>
</div>
</template>
<script setup lang="ts">
<script setup lang="ts" name="user">
import AddBindUserDialog from './components/addBindUserDialog.vue';
import { getBindUserList_api, unBindUser_api } from '@/api/system/department';
import { message } from 'ant-design-vue';
const addDialogRef = ref();
const props = defineProps<{
parentId: string;
}>();
const query = {
columns: [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
ellipsis: true,
fixed: 'left',
search: {
type: 'string',
},
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
ellipsis: true,
fixed: 'left',
search: {
type: 'string',
},
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
ellipsis: true,
fixed: 'left',
search: {
type: 'select',
options: [
{
label: '正常',
value: 1,
},
{
label: '禁用',
value: 0,
},
],
},
},
],
params: ref({}),
search: (params: any) => {
query.params.value = params;
},
};
//
const tableRef = ref<Record<string, any>>({}); //
const table = reactive({
columns: [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
scopedSlots: true,
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
scopedSlots: true,
},
],
_selectedRowKeys: [] as string[],
requestFun: async (oParams: any) => {
table.cancelSelect();
if (props.parentId) {
const params = {
...oParams,
sorts: [{ name: 'createTime', order: 'desc' }],
terms: [
...oParams.terms,
{
terms: [
{
column: 'id$in-dimension$org',
value: props.parentId,
},
],
},
],
};
const resp: any = await getBindUserList_api(params);
return {
code: resp.status,
result: resp.result,
status: resp.status,
};
} else {
return {
code: 200,
result: {
data: [],
pageIndex: 0,
pageSize: 0,
total: 0,
},
status: 200,
};
}
},
unBind: (row?: any) => {
const ids = row ? [row.id] : table._selectedRowKeys;
if (ids.length < 1) return message.warning('请勾选需要解绑的数据');
unBindUser_api(props.parentId, ids).then(() => {
message.success('操作成功');
table.refresh();
});
},
//
openDialog: () => {
addDialogRef.value && addDialogRef.value.openDialog();
},
onSelectChange: (keys: string[]) => {
table._selectedRowKeys = keys;
},
cancelSelect: () => {
table._selectedRowKeys = [];
},
//
refresh: () => {
tableRef.value.reload();
},
});
</script>
<style scoped>
</style>
<style scoped></style>