iot-ui-vue/src/views/notice/Config/SyncUser/index.vue

515 lines
16 KiB
Vue

<!-- 同步用户 -->
<template>
<div>
<j-modal
v-model:visible="_vis"
title="同步用户"
:footer="null"
@cancel="_vis = false"
width="80%"
>
<j-row :gutter="10" class="model-body">
<j-col :span="4">
<j-input
v-model:value="deptName"
@keyup.enter="getDepartment"
allowClear
placeholder="请输入部门名称"
style="margin-bottom: 8px"
>
<template #addonAfter>
<AIcon
type="SearchOutlined"
style="cursor: pointer"
@click="getDepartment"
/>
</template>
</j-input>
<j-tree
:tree-data="deptTreeData"
:fieldNames="{ title: 'name', key: 'id' }"
:selectedKeys="[deptId]"
@select="onTreeSelect"
:showLine="{ showLeafIcon: false }"
:show-icon="true"
>
</j-tree>
<j-empty v-if="!deptTreeData.length" />
</j-col>
<j-col :span="20">
<j-button type="primary" @click="handleAutoBind">
自动绑定
</j-button>
<JTable
ref="tableRef"
:columns="columns"
:dataSource="dataSource"
:loading="tableLoading"
:pagination="{
total: dataSource.length,
current: current,
pageSize: pageSize,
pageSizeOptions: ['12', '24', '48', '96'],
showSizeChanger: true,
hideOnSinglePage: false,
showTotal: (total: number, range: number) => `第 ${range[0]} - ${range[1]} 条/总共 ${total} 条`,
}"
@change="handleTableChange"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'status'">
<j-space>
<j-badge
:status="record.status.value"
:text="record.status.text"
></j-badge>
</j-space>
</template>
<template v-if="column.dataIndex === 'action'">
<j-space :size="16">
<j-tooltip
v-for="i in getActions(record, 'table')"
:key="i.key"
v-bind="i.tooltip"
>
<j-popconfirm
v-if="i.popConfirm"
v-bind="i.popConfirm"
:disabled="i.disabled"
>
<j-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></j-button>
</j-popconfirm>
<j-button
style="padding: 0"
type="link"
v-else
@click="
i.onClick && i.onClick(record)
"
>
<j-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></j-button>
</j-button>
</j-tooltip>
</j-space>
</template>
</template>
</JTable>
</j-col>
</j-row>
</j-modal>
<!-- 绑定用户 -->
<j-modal
v-model:visible="bindVis"
title="绑定用户"
:maskClosable="false"
:confirm-loading="confirmLoading"
@cancel="handleCancel"
@ok="handleBindSubmit"
>
<j-form layout="vertical">
<j-form-item label="用户" v-bind="validateInfos.userId">
<j-select
v-model:value="formData.userId"
:options="allUserList"
allowClear
show-search
option-filter-prop="children"
:filter-option="filterOption"
placeholder="请选择用户"
/>
</j-form-item>
</j-form>
</j-modal>
</div>
</template>
<script setup lang="ts" name="SyncUser">
import configApi from '@/api/notice/config';
import { PropType } from 'vue';
import type { ActionsType } from '@/views/device/Instance/typings';
import { Form } from 'ant-design-vue';
import { onlyMessage } from '@/utils/comm';
const useForm = Form.useForm;
type Emits = {
(e: 'update:visible', data: boolean): void;
};
const emit = defineEmits<Emits>();
const props = defineProps({
visible: { type: Boolean, default: false },
data: {
type: Object as PropType<Partial<Record<string, any>>>,
default: () => ({}),
},
});
const _vis = computed({
get: () => props.visible,
set: (val) => emit('update:visible', val),
});
watch(
() => _vis.value,
(val) => {
if (val) {
getDepartment();
} else {
dataSource.value = [];
}
},
);
// 左侧部门
const deptTreeData = ref([]);
const deptName = ref('');
const deptId = ref('');
/**
* 获取部门
*/
const getDepartment = async () => {
let res = null;
if (props.data.type === 'dingTalk') {
res = await configApi.dingTalkDept(props.data.id);
} else if (props.data.type === 'weixin') {
res = await configApi.weChatDept(props.data.id);
}
let _result = res?.result;
if (deptName.value) {
_result = res?.result?.filter(
(f: any) => f.name.indexOf(deptName.value) > -1,
);
}
deptTreeData.value = _result;
deptId.value = _result[0]?.id;
};
watch(
() => deptName.value,
(val: any) => {
if (!val) getDepartment();
},
);
/**
* 部门点击
*/
const onTreeSelect = (keys: any) => {
if (keys.length) {
deptId.value = keys[0];
pageSize.value = 12;
current.value = 1;
}
};
// 右侧表格
const columns = [
{
title: '钉钉用户名',
dataIndex: 'thirdPartyUserName',
key: 'thirdPartyUserName',
},
{
title: '用户',
dataIndex: 'userName',
key: 'userName',
scopedSlots: true,
},
{
title: '绑定状态',
dataIndex: 'status',
key: 'status',
scopedSlots: true,
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
scopedSlots: true,
},
];
const getActions = (
data: Partial<Record<string, any>>,
type: 'card' | 'table',
): ActionsType[] => {
if (!data) return [];
const actions = [
{
key: 'bind',
text: '绑定',
tooltip: {
title: '绑定',
},
icon: 'EditOutlined',
onClick: () => {
handleBind(data);
},
},
{
key: 'unbind',
text: '解绑',
icon: 'DisconnectOutlined',
popConfirm: {
title: '确认解绑?',
onConfirm: async () => {
configApi
.unBindUser({ bindingId: data.bindId }, data.bindId)
.then(() => {
onlyMessage('操作成功');
getTableData();
});
},
},
},
];
if (data.status.value === 'success') return actions;
return actions.filter((i: ActionsType) => i.key !== 'unbind');
};
/**
* 自动绑定
*/
const handleAutoBind = async () => {
await getTableData([
{
column: `id$user-third$${props.data.type}_${props.data.provider}$not`,
},
]);
const params = dataSource.value
.filter((f: any) => f.userId && f.status.value === 'error')
.map((m: any) => ({
userId: m.userId,
providerName: m.thirdPartyUserName,
thirdPartyUserId: m.thirdPartyUserId,
}));
if (props.data.type === 'dingTalk') {
configApi.dingTalkBindUser(params, props.data.id).then(() => {
onlyMessage('操作成功');
getTableData();
});
} else if (props.data.type === 'weixin') {
configApi.weChatBindUser(params, props.data.id).then(() => {
onlyMessage('操作成功');
getTableData();
});
}
};
/**
* 获取钉钉/微信部门用户
*/
const getDeptUsers = async () => {
let res = null;
if (props.data.type === 'dingTalk') {
res = await configApi.getDingTalkUsers(props.data.id, deptId.value);
} else if (props.data.type === 'weixin') {
res = await configApi.getWeChatUsers(props.data.id, deptId.value);
}
return res?.result;
};
/**
* 获取已经绑定的用户
*/
const getBindUsers = async () => {
let res = null;
if (props.data.type === 'dingTalk') {
res = await configApi.getDingTalkBindUsers(props.data.id);
} else if (props.data.type === 'weixin') {
res = await configApi.getWeChatBindUsers(props.data.id);
}
return res?.result;
};
/**
* 获取所有用户未绑定的用户
*/
const allUserList = ref([]);
const getAllUsers = async (terms?: any) => {
const params = {
paging: false,
terms,
};
const { result } = await configApi.getPlatformUsers(params);
allUserList.value = result.map((m: any) => ({
label: m.name,
value: m.id,
...m,
}));
return result;
};
/**
* 处理列表数据
*/
const dataSource = ref<any>([]);
const tableLoading = ref(false);
const getTableData = (terms?: any) => {
tableLoading.value = true;
Promise.all<any>([getDeptUsers(), getBindUsers(), getAllUsers(terms)])
.then((res) => {
dataSource.value = [];
const [deptUsers, bindUsers, unBindUsers] = res;
(deptUsers || []).forEach((deptUser: any) => {
// 未绑定的用户
let unBindUser = unBindUsers.find(
(f: any) => f.name === deptUser?.name,
);
// 绑定的用户
const bindUser = bindUsers.find(
(f: any) => f.thirdPartyUserId === deptUser.id,
);
if (bindUser) {
unBindUser = unBindUsers.find(
(f: any) => f.id === bindUser.userId,
);
}
dataSource.value.push({
thirdPartyUserId: deptUser.id,
thirdPartyUserName: deptUser.name,
bindId: bindUser?.id,
userId: unBindUser?.id,
userName: unBindUser
? `${unBindUser.name}(${unBindUser.username})`
: bindUser?.providerName,
status: {
text: bindUser?.providerName ? '已绑定' : '未绑定',
value: bindUser?.providerName ? 'success' : 'error',
},
});
});
// console.log('dataSource.value: ', dataSource.value);
})
.finally(() => {
tableLoading.value = false;
});
};
/**
* 前端分页
*/
const current = ref(1);
const pageSize = ref(12);
const handleTableChange = (pagination: any) => {
current.value = pagination.current;
pageSize.value = pagination.pageSize;
};
watch(
() => deptId.value,
() => {
getTableData();
},
{ immediate: true },
);
/**
* 绑定用户
*/
const bindVis = ref(false);
const confirmLoading = ref(false);
const formData = ref({
userId: '',
thirdPartyUserId: '',
thirdPartyUserName: '',
bindId: '',
});
const formRules = ref({
userId: [{ required: true, message: '请选择用户', trigger: 'change' }],
});
const { resetFields, validate, validateInfos, clearValidate } = useForm(
formData.value,
formRules.value,
);
const handleBind = (row: any) => {
bindVis.value = true;
// formData.value = row;
Object.assign(formData.value, row);
getAllUsers([
{
column: `id$user-third$${props.data.type}_${props.data.provider}$not`,
},
]);
};
/**
* 绑定用户, 用户下拉筛选
*/
const filterOption = (input: string, option: any) => {
const text = option?.componentOptions?.children?.[0]?.text || option.label
return (
text
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
);
};
/**
* 绑定提交
*/
const handleBindSubmit = () => {
validate().then(async () => {
const params = {
providerName: formData.value.thirdPartyUserName,
thirdPartyUserId: formData.value.thirdPartyUserId,
userId: formData.value.userId,
id: formData.value.bindId,
};
confirmLoading.value = true;
if (props.data.type === 'dingTalk') {
configApi
.dingTalkBindUser([params], props.data.id)
.then(() => {
onlyMessage('操作成功');
bindVis.value = false;
getTableData();
})
.finally(() => {
confirmLoading.value = false;
});
} else if (props.data.type === 'weixin') {
configApi
.weChatBindUser([params], props.data.id)
.then(() => {
onlyMessage('操作成功');
bindVis.value = false;
getTableData();
})
.finally(() => {
confirmLoading.value = false;
});
}
});
};
const handleCancel = () => {
bindVis.value = false;
resetFields();
};
</script>
<style lang="less" scoped>
.model-body {
height: 600px;
overflow-y: auto;
&:deep(.ant-pagination-item) {
display: none;
}
}
</style>