Compare commits
2 Commits
299b1c7bbb
...
b01a5f663e
| Author | SHA1 | Date | |
|---|---|---|---|
| b01a5f663e | |||
| 1a4041cb49 |
@ -190,6 +190,17 @@ const routes = [
|
||||
parentRoute: 'warningManagement3'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/messageManagement',
|
||||
name: 'messageManagement',
|
||||
component: () => import('../views/WarningManagement/law/messageManagement/index.vue'),
|
||||
meta: {
|
||||
title: '消息推送设置',
|
||||
breadcrumb: true,
|
||||
parentRoute: 'warningManagement3'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 项目管理 - 区县
|
||||
{
|
||||
|
||||
@ -372,6 +372,12 @@ export default () => {
|
||||
path: '/dutyManagement'
|
||||
});
|
||||
};
|
||||
// 跳转到消息推送设置
|
||||
const gotoMessagePage = () => {
|
||||
router.push({
|
||||
path: '/messageManagement'
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
@ -396,6 +402,7 @@ export default () => {
|
||||
columns,
|
||||
gotoLedgerPage,
|
||||
gotoDutyPage,
|
||||
gotoMessagePage,
|
||||
|
||||
modelVisible,
|
||||
model,
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
<el-button type="primary" @click="script.gotoLedgerPage">线下帮扶台账</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.gotoMessagePage">消息推送设置</el-button>
|
||||
<el-button type="primary" color="#952DE6" @click="">导出</el-button>
|
||||
<input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".*"></input>
|
||||
<!-- <el-button type="primary" @click="script.gotoLedgerPage">驻地台账</el-button> -->
|
||||
|
||||
@ -0,0 +1,267 @@
|
||||
<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="user-select-container">
|
||||
<!-- 左侧用户列表 -->
|
||||
<div class="user-list-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="selected-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>
|
||||
<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 currentUsers = ref([]);
|
||||
// 已选择的用户列表
|
||||
const selectedUsers = ref([]);
|
||||
|
||||
defineExpose({
|
||||
formRef,
|
||||
});
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
orgId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
orgName: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const rules = computed(() => {
|
||||
return {
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
// 检查用户是否已被选择
|
||||
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
|
||||
...user,
|
||||
orgId: user.orgId,
|
||||
orgName: props.orgName,
|
||||
userId: user.userId,
|
||||
userAccount: user.account,
|
||||
userPhone: user.phone
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 移除已选择的用户
|
||||
const removeSelectedUser = (user) => {
|
||||
const index = selectedUsers.value.findIndex(selected => selected.userId === user.userId);
|
||||
if (index > -1) {
|
||||
selectedUsers.value.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 根据组织ID查询用户列表
|
||||
const getUsersByOrgId = async (orgId) => {
|
||||
// console.log('@@@@@', 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(() => {
|
||||
getUsersByOrgId(props.orgId);
|
||||
})
|
||||
|
||||
watch(selectedUsers.value, (val) => {
|
||||
props.form.data = val;
|
||||
}, { deep: true })
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-part {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-select-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.user-list-panel {
|
||||
flex: 1;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.selected-panel {
|
||||
flex: 1;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.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>
|
||||
@ -0,0 +1,224 @@
|
||||
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
|
||||
import { request } from "@/utils/request";
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import AddDialog from "./addDialog.vue";
|
||||
|
||||
const modelVisible = ref(false); // 弹窗状态
|
||||
const drawerVisible = ref(false); // 抽屉状态
|
||||
// 弹窗内容
|
||||
const model = reactive({
|
||||
|
||||
});
|
||||
const form = reactive({
|
||||
|
||||
});
|
||||
const INIT_FORM = {
|
||||
|
||||
};
|
||||
// 抽屉内容
|
||||
const drawer = reactive({
|
||||
title: '',
|
||||
content: null,
|
||||
props: {},
|
||||
onCancel: null,
|
||||
onConfirm: null,
|
||||
direction: 'rtl',
|
||||
size: '50%'
|
||||
});
|
||||
const dialogRef = ref(null); // 弹窗实例
|
||||
const drawerRef = ref(null); // 抽屉实例
|
||||
|
||||
// 消息推送组织列表(固定六个),增加personList存储该组织的人员数组
|
||||
const messageOrgList = ref([
|
||||
{ title: '中心领导', orgName: '中心领导', personList: [] },
|
||||
{ title: '法规处', orgName: '法规处', personList: [] },
|
||||
{ title: '养护处', orgName: '养护处', personList: [] },
|
||||
{ title: '农村公路处', orgName: '农村公路处', personList: [] },
|
||||
{ title: '建设处', orgName: '建设处', personList: [] },
|
||||
{ title: '市公路应急中心/管理段', orgName: '公路管理段', personList: [] },
|
||||
])
|
||||
|
||||
const messagePushPerson = ref([])
|
||||
const userOrgsList = ref([])
|
||||
|
||||
// 查询所有消息推送人员
|
||||
const getAllMessagePushPerson = async () => {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/messagePushPerson/listAll',
|
||||
method: 'GET',
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
messagePushPerson.value = res.data
|
||||
|
||||
// 按orgId分组填充人员数据到对应组织
|
||||
messageOrgList.value.forEach(org => {
|
||||
org.personList = messagePushPerson.value.filter(person => person.orgId === org.orgId)
|
||||
});
|
||||
|
||||
// console.log('@@@@@@', messageOrgList.value);
|
||||
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取消息推送人员失败');
|
||||
console.error('获取消息推送人员失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 查询所有组织
|
||||
const getUserOrgs = async () => {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/user/userOrgs',
|
||||
method: 'GET',
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
userOrgsList.value = res.data.data
|
||||
|
||||
// 遍历后端返回的组织数据,与固定组织列表匹配并赋值orgId
|
||||
messageOrgList.value.forEach(fixedOrg => {
|
||||
const matchedOrg = userOrgsList.value.find(org =>
|
||||
org.orgName === fixedOrg.orgName
|
||||
);
|
||||
if (matchedOrg) {
|
||||
fixedOrg.orgId = matchedOrg.orgId;
|
||||
}
|
||||
});
|
||||
|
||||
// 在获取到orgId后,立即根据orgId填充对应的人员数据
|
||||
messageOrgList.value.forEach(org => {
|
||||
org.personList = messagePushPerson.value.filter(person => person.orgId === org.orgId)
|
||||
});
|
||||
|
||||
// console.log('@@@@',messageOrgList.value);
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取组织失败');
|
||||
console.error('获取组织失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加消息推送人
|
||||
const handelAdd = async (data) => {
|
||||
try {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '操作中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/messagePushPerson/add',
|
||||
method: 'POST',
|
||||
data: data
|
||||
})
|
||||
loading.close();
|
||||
if (res.code === '00000') {
|
||||
ElMessage.success('添加成功');
|
||||
modelVisible.value = false;
|
||||
await getAllMessagePushPerson();
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('添加失败');
|
||||
console.error('添加失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 打开添加人员弹窗
|
||||
const openAddDialog = (orgId, orgName) => {
|
||||
model.title = '添加人员';
|
||||
Object.assign(form, INIT_FORM);
|
||||
model.props = {
|
||||
orgId: orgId,
|
||||
orgName: orgName,
|
||||
form: form,
|
||||
};
|
||||
model.content = AddDialog;
|
||||
model.onCancel = () => {
|
||||
modelVisible.value = false;
|
||||
};
|
||||
model.onConfirm = async () => {
|
||||
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => {
|
||||
// console.log('@@@@@@form',form);
|
||||
await handelAdd(form.data)
|
||||
// await addSchedule(form)
|
||||
// await publishWarning(form)
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error('请处理表单中的错误项');
|
||||
});
|
||||
};
|
||||
model.width = "70%"
|
||||
modelVisible.value = true;
|
||||
}
|
||||
|
||||
// 删除消息推送人
|
||||
const deletePushPerson = async (person) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除【${person.realName}】吗?`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '操作中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/messagePushPerson/deleteByUserId',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: person.userId
|
||||
}
|
||||
})
|
||||
loading.close();
|
||||
if (res.code === '00000') {
|
||||
ElMessage.success('删除成功');
|
||||
await getAllMessagePushPerson();
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error('删除失败');
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(async () => {
|
||||
await getUserOrgs();
|
||||
await getAllMessagePushPerson();
|
||||
|
||||
})
|
||||
|
||||
return {
|
||||
modelVisible,
|
||||
model,
|
||||
drawerVisible,
|
||||
drawer,
|
||||
dialogRef,
|
||||
drawerRef,
|
||||
openAddDialog,
|
||||
deletePushPerson,
|
||||
|
||||
messageOrgList,
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
<div class="content-box">
|
||||
<el-card shadow="never" v-for="org in script.messageOrgList.value" class="org-box">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ org.title }}</span>
|
||||
<el-button type="primary" size="small" text
|
||||
@click="script.openAddDialog(org.orgId, org.orgName)">添加</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 人员列表 -->
|
||||
<div class="person-list">
|
||||
<div v-if="org.personList.length === 0" class="empty-text">暂无人员</div>
|
||||
<div v-else class="person-item" v-for="person in org.personList" :key="person.userId">
|
||||
<div class="person-info">
|
||||
<span class="name">{{ person.realName }}</span>
|
||||
<!-- <span class="account">({{ person.userAccount }})</span> -->
|
||||
<span class="phone" v-if="person.userPhone">{{ person.userPhone }}</span>
|
||||
</div>
|
||||
<el-button type="danger" size="small" text @click="script.deletePushPerson(person)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="model-box">
|
||||
<MyDialog v-model="script.modelVisible.value" :title="script.model?.title"
|
||||
:dynamicComponent="script.model?.content" :component-props="script.model?.props"
|
||||
:onConfirm="script.model?.onConfirm" :onCancel="script.model?.onCancel" ref="dialogRef"
|
||||
:width="script.model?.width">
|
||||
</MyDialog>
|
||||
<MyDrawer v-model="script.drawerVisible.value" :title="script.drawer?.title"
|
||||
:dynamicComponent="script.drawer?.content" :component-props="script.drawer?.props"
|
||||
:onConfirm="script.drawer?.onConfirm" :onCancel="script.drawer?.onCancel" ref="drawerRef"
|
||||
:direction="script.drawer?.direction" :size="script.drawer?.size">
|
||||
</MyDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DynamicTable from "@/component/DynamicTable/index.js";
|
||||
import { Search, ArrowDown } from "@element-plus/icons-vue";
|
||||
import MyDialog from "@/component/MyDialog/index.js";
|
||||
import MyDrawer from "@/component/MyDrawer/index.js";
|
||||
import scriptFn from "./index.js";
|
||||
const script = scriptFn();
|
||||
const { dialogRef, drawerRef } = script;
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.root {
|
||||
height: 100%;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.content-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.org-box {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.person-list {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.person-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.person-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.person-info {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.account {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.phone {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user