Compare commits

...

2 Commits

4 changed files with 288 additions and 30 deletions

View File

@ -11,24 +11,45 @@
children: 'children', children: 'children',
label: 'orgName' label: 'orgName'
}" node-key="orgId" :expand-on-click-node="false" :default-expand-all="false" highlight-current }" node-key="orgId" :expand-on-click-node="false" :default-expand-all="false" highlight-current
style="height: 400px; overflow-y: auto;" style="height: 400px; overflow-y: auto;" @node-click="handleNodeClick">
@node-click="handleNodeClick"> <template #default="{ node: _node }">
<template #default="{ node, data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<span>{{ node.label }}</span> <span>{{ _node.label }}</span>
</span> </span>
</template> </template>
</el-tree> </el-tree>
</div> </div>
<!-- 中间空白区域 --> <!-- 中间用户列表 -->
<div class="middle-panel"> <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>
<!-- 右侧空白区域 --> <!-- 右侧已选择用户 -->
<div class="right-panel"> <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>
</div> </div>
</el-form-item> </el-form-item>
@ -38,13 +59,31 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted, watch } from "vue";
import { request } from "@/utils/request"; import { request } from "@/utils/request";
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
const formRef = ref(null); const formRef = ref(null);
defineExpose({ formRef }); //
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({ const props = defineProps({
form: { form: {
type: Object, type: Object,
@ -52,6 +91,13 @@ const props = defineProps({
}, },
}); });
// form
watch(selectedUsers, (newSelectedUsers) => {
// form
props.form.schedules = getFormattedSelectedUsers();
}, { deep: true, immediate: true });
const rules = computed(() => { const rules = computed(() => {
@ -122,14 +168,14 @@ const buildOrgTree = (orgList) => {
// ID-1 // ID-1
const parentIds = pidMatches.filter(id => id !== '-1'); const parentIds = pidMatches.filter(id => id !== '-1');
if (parentIds.length > 0) { if (parentIds.length > 0) {
parentId = parseInt(parentIds[parentIds.length - 1]); parentId = parentIds[parentIds.length - 1]; //
} }
} }
} }
// 2orgPids使orgParentId // 2orgPids使orgParentId
if (parentId === null && org.orgParentId && org.orgParentId !== -1) { if (parentId === null && org.orgParentId && org.orgParentId !== -1) {
parentId = org.orgParentId; parentId = String(org.orgParentId);
} }
// children // children
@ -156,15 +202,66 @@ const buildOrgTree = (orgList) => {
return sortTree(tree); return sortTree(tree);
} }
// //
const handleNodeClick = (data, node) => { const isUserSelected = (user) => {
// return selectedUsers.value.some(selected => selected.userId === user.userId);
if (!node.childNodes || node.childNodes.length === 0) { };
//
getUsersByOrgId(data.orgId); //
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 // ID
const getUsersByOrgId = async (orgId) => { const getUsersByOrgId = async (orgId) => {
try { try {
@ -176,7 +273,8 @@ const getUsersByOrgId = async (orgId) => {
} }
}) })
if (res.code === '00000') { if (res.code === '00000') {
console.log('@@@@@@用户列表',res.data); currentUsers.value = res.data || [];
// console.log('@@@@@', res.data);
} else { } else {
throw new Error(res.message) throw new Error(res.message)
} }
@ -215,16 +313,14 @@ onMounted(() => {
background-color: #fff; background-color: #fff;
} }
.empty-panel {
}
.middle-panel { .middle-panel {
flex: 2; flex: 2;
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
padding: 10px; padding: 10px;
background-color: #fff; background-color: #fff;
display: flex;
flex-direction: column;
} }
.right-panel { .right-panel {
@ -233,6 +329,98 @@ onMounted(() => {
border-radius: 4px; border-radius: 4px;
padding: 10px; padding: 10px;
background-color: #fff; background-color: #fff;
display: flex;
flex-direction: column;
}
.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 { .custom-tree-node {

View File

@ -68,7 +68,7 @@ const pagination = reactive({
}, },
}); });
// 获取预警列表 // 获取值班列表
const getTableData = async (filterData = {}) => { const getTableData = async (filterData = {}) => {
try { try {
// 过滤空字符串属性 // 过滤空字符串属性
@ -100,7 +100,28 @@ const getTableData = async (filterData = {}) => {
} }
} }
// 打开发布预警弹窗 // 排班提交
const addSchedule = async (form) => {
try {
const res = await request({
url: '/snow-ops-platform/law-duty/schedule',
method: "POST",
data: form
})
if (res.code === '00000') {
ElMessage.success('排班成功');
modelVisible.value = false;
getTableData(filterData)
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('排班失败');
console.error('排班失败:', error);
}
}
// 打开排班弹窗
const openAddDialog = () => { const openAddDialog = () => {
model.title = '立即排班'; model.title = '立即排班';
Object.assign(form, INIT_FORM); Object.assign(form, INIT_FORM);
@ -113,7 +134,7 @@ const openAddDialog = () => {
}; };
model.onConfirm = async () => { model.onConfirm = async () => {
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => { await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => {
console.log('@@@@@立即排班', form); await addSchedule(form)
// await publishWarning(form) // await publishWarning(form)
}) })
.catch((err) => { .catch((err) => {

View File

@ -2,6 +2,7 @@ import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
import { request } from "@/utils/request"; import { request } from "@/utils/request";
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import AddDialog from "./addDialog.vue"; import AddDialog from "./addDialog.vue";
import ScheduleDiaog from "../law/dutyManagement/addDialog.vue"
import DetailDrawer from "./detailDrawer.vue"; import DetailDrawer from "./detailDrawer.vue";
const tableData = ref([]); // 表格数据 const tableData = ref([]); // 表格数据
@ -305,6 +306,53 @@ const uploadFile = async (file) => {
} }
} }
const form2 = reactive({});
const INIT_FORM2 = {};
// 排班提交
const addSchedule = async (form) => {
try {
const res = await request({
url: '/snow-ops-platform/law-duty/schedule',
method: "POST",
data: form
})
if (res.code === '00000') {
ElMessage.success('排班成功');
modelVisible.value = false;
getTableData(filterData)
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('排班失败');
console.error('排班失败:', error);
}
}
const openScheduleDiaog = () => {
model.title = '立即排班';
Object.assign(form2, INIT_FORM2);
model.props = {
form: form2,
};
model.content = ScheduleDiaog;
model.onCancel = () => {
modelVisible.value = false;
};
model.onConfirm = async () => {
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => {
await addSchedule(form)
// await publishWarning(form)
})
.catch((err) => {
ElMessage.error('请处理表单中的错误项');
});
};
model.width = "70%"
modelVisible.value = true;
}
@ -356,6 +404,7 @@ export default () => {
dialogRef, dialogRef,
drawerRef, drawerRef,
openAddDialog, openAddDialog,
openScheduleDiaog,
uploadFile, uploadFile,
} }

View File

@ -15,7 +15,7 @@
<el-button type="primary" @click="script.openAddDialog">发布预警</el-button> <el-button type="primary" @click="script.openAddDialog">发布预警</el-button>
<el-button type="primary" @click="triggerFileSelect">上传线下帮扶</el-button> <el-button type="primary" @click="triggerFileSelect">上传线下帮扶</el-button>
<el-button type="primary" @click="script.gotoLedgerPage">线下帮扶台账</el-button> <el-button type="primary" @click="script.gotoLedgerPage">线下帮扶台账</el-button>
<el-button type="primary" @click="">立即排班</el-button> <el-button type="primary" @click="script.openScheduleDiaog">立即排班</el-button>
<el-button type="primary" @click="script.gotoDutyPage">值班管理</el-button> <el-button type="primary" @click="script.gotoDutyPage">值班管理</el-button>
<el-button type="primary" color="#952DE6" @click="">导出</el-button> <el-button type="primary" color="#952DE6" @click="">导出</el-button>
<input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".*"></input> <input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".*"></input>