437 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="detail-container">
<el-form ref="formRef" :model="form" label-position="right" label-width="auto"
style="max-height: 60vh; overflow-y: hidden; padding-right: 50px" :rules="rules">
<el-form-item label="" prop="schedules">
<div class="tree-transfer-container">
<!-- 左侧树形结构 -->
<div class="tree-panel">
<el-tree :data="treeOrgs" :props="{
children: 'children',
label: 'orgName'
}" node-key="orgId" :expand-on-click-node="false" :default-expand-all="false" highlight-current
style="height: 100%; overflow-y: auto;" @node-click="handleNodeClick">
<template #default="{ node: _node }">
<span class="custom-tree-node">
<span>{{ _node.label }}</span>
</span>
</template>
</el-tree>
</div>
<!-- 中间用户列表 -->
<div class="middle-panel">
<div class="panel-title">用户列表</div>
<div class="user-list">
<div v-for="user in currentUsers" :key="user.userId" class="user-item"
:class="{ 'selected': isUserSelected(user) }" @click="toggleUserSelection(user)">
<span class="user-name">{{ user.realName || user.nickName || user.account }}</span>
<span class="user-position">{{ user.positionName }}</span>
</div>
</div>
</div>
<!-- 右侧已选择用户 -->
<div class="right-panel">
<div class="panel-title">已选择用户 ({{ selectedUsers.length }})</div>
<div class="selected-user-list">
<div v-for="user in selectedUsers" :key="user.userId" class="selected-user-item">
<div class="user-info">
<span class="user-name">{{ user.realName || user.nickName || user.account }}</span>
<span class="user-position">{{ user.positionName }}</span>
</div>
<div class="time-picker-container">
<el-date-picker v-model="user.timeRange" type="datetimerange" range-separator="至"
start-placeholder="开始时间" end-placeholder="结束时间" format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" size="small" :shortcuts="dateShortcuts"
@change="handleTimeChange(user)" />
</div>
<el-button type="danger" size="small" icon="Delete" circle @click.stop="removeSelectedUser(user)" />
</div>
</div>
</div>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from "vue";
import { request } from "@/utils/request";
import { ElMessage } from 'element-plus';
const formRef = ref(null);
// 获取保存用的用户数据(格式化后的数据)
const getFormattedSelectedUsers = () => {
return selectedUsers.value.map(user => ({
userId: user.userId,
userName: user.realName || user.nickName || user.account,
orgId: user.orgId,
startTime: user.timeRange ? user.timeRange[0] : '',
endTime: user.timeRange ? user.timeRange[1] : ''
}));
}
// 当前显示的用户列表
const currentUsers = ref([]);
// 已选择的用户列表
const selectedUsers = ref([]);
defineExpose({
formRef,
getFormattedSelectedUsers
});
const props = defineProps({
form: {
type: Object,
default: () => ({}),
},
});
// 监听已选择用户变化实时保存到form中
watch(selectedUsers, (newSelectedUsers) => {
// 格式化数据并保存到form
props.form.schedules = getFormattedSelectedUsers();
}, { deep: true, immediate: true });
const rules = computed(() => {
return {
};
});
// 用户组织列表
const userOrgs = ref([]);
// 树形结构数据
const treeOrgs = ref([]);
// 查询用户组织
const getUserOrgs = async () => {
try {
const res = await request({
url: '/snow-ops-platform/user/userOrgs',
method: 'GET',
})
if (res.code === '00000') {
userOrgs.value = res.data.data
// 构造树形结构
treeOrgs.value = buildOrgTree(userOrgs.value)
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('获取组织失败');
console.error('获取组织失败:', error);
}
}
/**
* 构建组织树形结构
* @param {Array} orgList 组织列表
* @returns {Array} 树形结构数据
*/
const buildOrgTree = (orgList) => {
if (!orgList || !orgList.length) return [];
// 创建映射表,便于查找
const orgMap = {};
orgList.forEach(org => {
orgMap[org.orgId] = {
...org,
children: []
};
});
const tree = [];
orgList.forEach(org => {
const currentOrg = orgMap[org.orgId];
// 根节点判断orgParentId为-1或orgPids为"[-1],"
if (org.orgParentId === -1 || org.orgPids === "[-1],") {
tree.push(currentOrg);
} else {
// 找到父节点
// 从orgPids中提取父级orgId这里简化处理实际可能需要更复杂的解析
let parentId = null;
// 方法1尝试从orgPids中解析父级ID
if (org.orgPids) {
const pidMatches = org.orgPids.match(/\d+/g);
if (pidMatches && pidMatches.length > 0) {
// 取最后一个ID作为直接父级排除-1
const parentIds = pidMatches.filter(id => id !== '-1');
if (parentIds.length > 0) {
parentId = parentIds[parentIds.length - 1]; // 保持字符串类型,不转成数字
}
}
}
// 方法2如果无法从orgPids解析使用orgParentId转换为字符串
if (parentId === null && org.orgParentId && org.orgParentId !== -1) {
parentId = String(org.orgParentId);
}
// 添加到父节点的children中
if (parentId && orgMap[parentId]) {
orgMap[parentId].children.push(currentOrg);
} else {
// 如果没有找到父节点,也作为根节点
tree.push(currentOrg);
}
}
});
// 按orgSort排序
const sortTree = (tree) => {
tree.sort((a, b) => (a.orgSort || 0) - (b.orgSort || 0));
tree.forEach(node => {
if (node.children && node.children.length) {
sortTree(node.children);
}
});
return tree;
};
return sortTree(tree);
}
// 检查用户是否已被选择
const isUserSelected = (user) => {
return selectedUsers.value.some(selected => selected.userId === user.userId);
};
// 切换用户选择状态
const toggleUserSelection = (user) => {
if (isUserSelected(user)) {
removeSelectedUser(user);
} else {
// 添加用户时初始化时间范围和orgId
selectedUsers.value.push({
...user,
orgId: user.orgId, // 确保包含orgId
timeRange: null
});
}
};
// 移除已选择的用户
const removeSelectedUser = (user) => {
const index = selectedUsers.value.findIndex(selected => selected.userId === user.userId);
if (index > -1) {
selectedUsers.value.splice(index, 1);
}
};
// 树节点点击事件
const handleNodeClick = async (_data) => {
// 获取该组织的用户列表
await getUsersByOrgId(_data.orgId);
}
// 日期时间范围快捷选项
const dateShortcuts = [
{
text: '今天',
value: [new Date(), new Date()],
},
{
text: '明天',
value: [
new Date(new Date().getTime() + 86400000),
new Date(new Date().getTime() + 86400000)
],
},
{
text: '最近一周',
value: [new Date(), new Date(new Date().getTime() + 604800000)],
},
];
// 处理时间范围变化
const handleTimeChange = (user) => {
// 时间变化时的处理逻辑可以在这里添加
console.log('时间范围变化:', user.userId, user.timeRange);
};
// 根据组织ID查询用户列表
const getUsersByOrgId = async (orgId) => {
try {
const res = await request({
url: '/snow-ops-platform/user/orgUsers',
method: 'GET',
params: {
orgId
}
})
if (res.code === '00000') {
currentUsers.value = res.data || [];
// console.log('@@@@@', res.data);
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('获取用户失败');
console.error('获取用户失败:', error);
}
}
onMounted(() => {
getUserOrgs();
})
</script>
<style scoped>
.form-part {
padding: 20px;
}
.text-center {
text-align: center;
}
.tree-transfer-container {
display: flex;
gap: 20px;
width: 100%;
height: 60vh;
}
.tree-panel {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
background-color: #fff;
height: 100%;
}
.middle-panel {
flex: 2;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
background-color: #fff;
display: flex;
flex-direction: column;
height: 100%;
}
.right-panel {
flex: 2;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
background-color: #fff;
display: flex;
flex-direction: column;
height: 100%;
}
.panel-title {
font-weight: bold;
font-size: 14px;
color: #303133;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid #ebeef5;
}
.user-list {
flex: 1;
overflow-y: auto;
}
.user-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
margin-bottom: 4px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
border: 1px solid transparent;
}
.user-item:hover {
background-color: #f5f7fa;
}
.user-item.selected {
background-color: #409eff;
color: white;
border-color: #409eff;
}
.user-name {
font-size: 14px;
font-weight: 500;
}
.user-position {
font-size: 12px;
opacity: 0.7;
}
.selected-user-list {
flex: 1;
overflow-y: auto;
}
.selected-user-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
margin-bottom: 4px;
border-radius: 4px;
background-color: #f0f9ff;
border: 1px solid #e1f5fe;
gap: 12px;
}
.time-picker-container {
flex: 1.2;
min-width: 280px;
}
.time-picker-container :deep(.el-date-editor) {
width: 100%;
}
.user-info {
display: flex;
flex-direction: column;
min-width: 120px;
flex: 0.8;
}
.selected-user-item .user-name {
font-size: 14px;
font-weight: 500;
color: #303133;
}
.selected-user-item .user-position {
font-size: 12px;
color: #606266;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
font-size: 14px;
padding-right: 8px;
}
</style>