<template>
|
<div>
|
<el-form :model="searchForm"
|
inline>
|
<el-form-item label="会议主题">
|
<el-input v-model="searchForm.title"
|
placeholder="请输入会议主题"
|
clearable />
|
</el-form-item>
|
<el-form-item label="申请人">
|
<el-input v-model="searchForm.applicant"
|
placeholder="请输入申请人"
|
clearable />
|
</el-form-item>
|
<el-form-item label="审批状态">
|
<el-select style="width: 100px"
|
v-model="searchForm.status"
|
placeholder="请选择审批状态"
|
clearable>
|
<el-option label="待审批"
|
value="0" />
|
<el-option label="已通过"
|
value="1" />
|
<el-option label="未审批"
|
value="2" />
|
<el-option label="已取消"
|
value="3" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary"
|
@click="handleSearch">搜索</el-button>
|
<el-button @click="resetSearch">重置</el-button>
|
</el-form-item>
|
</el-form>
|
<!-- 会议审批列表 -->
|
<el-card>
|
<el-table v-loading="loading"
|
:data="approvalList"
|
border
|
:height="tableHeight">
|
<el-table-column prop="title"
|
label="会议主题"
|
align="center"
|
min-width="200"
|
show-overflow-tooltip />
|
<el-table-column prop="applicant"
|
label="申请人"
|
align="center"
|
width="120" />
|
<el-table-column prop="host"
|
label="主理人"
|
align="center"
|
width="120" />
|
<el-table-column prop="meetingTime"
|
label="会议时间"
|
align="center"
|
width="180">
|
<template #default="scope">
|
{{ formatDateTime(scope.row.meetingTime) }}
|
</template>
|
</el-table-column>
|
<el-table-column prop="location"
|
label="会议地点"
|
align="center"
|
width="150" />
|
<el-table-column prop="participants"
|
label="参会人数"
|
align="center"
|
width="100">
|
<template #default="scope">
|
{{ scope.row.participants.length }}人
|
</template>
|
</el-table-column>
|
<el-table-column prop="status"
|
label="审批状态"
|
align="center"
|
width="120">
|
<template #default="scope">
|
<el-tag :type="getStatusType(scope.row.status)">
|
{{ getStatusText(scope.row.status) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作"
|
align="center"
|
width="200"
|
fixed="right">
|
<template #default="scope">
|
<el-button type="primary"
|
link
|
@click="viewDetail(scope.row)">查看</el-button>
|
<el-button v-if="scope.row.status == '0'"
|
type="primary"
|
link
|
@click="handleApproval(scope.row)">
|
审批
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<!-- 分页 -->
|
<pagination v-show="total > 0"
|
:total="total"
|
v-model:page="queryParams.current"
|
v-model:limit="queryParams.size"
|
@pagination="getList" />
|
</el-card>
|
<!-- 会议详情对话框 -->
|
<el-dialog title="会议详情"
|
v-model="detailDialogVisible"
|
width="800px">
|
<div v-if="currentMeeting">
|
<el-descriptions label-width="100px"
|
class="meeting-desc"
|
:column="2"
|
border>
|
<el-descriptions-item label="会议主题"
|
label-class-name="nowrap-label">{{
|
currentMeeting.title
|
}}</el-descriptions-item>
|
<el-descriptions-item label="申请人"
|
label-class-name="nowrap-label">{{
|
currentMeeting.applicant
|
}}</el-descriptions-item>
|
<el-descriptions-item label="主理人"
|
label-class-name="nowrap-label">{{
|
currentMeeting.host
|
}}</el-descriptions-item>
|
<el-descriptions-item label="会议时间"
|
:span="2"
|
label-class-name="nowrap-label">
|
{{ formatDateTime(currentMeeting.meetingTime) }}
|
</el-descriptions-item>
|
<el-descriptions-item label="会议地点"
|
label-class-name="nowrap-label">{{
|
currentMeeting.location
|
}}</el-descriptions-item>
|
<el-descriptions-item label="参会人数"
|
label-class-name="nowrap-label">{{
|
currentMeeting.participants.length
|
}}人</el-descriptions-item>
|
<el-descriptions-item label="审批状态"
|
label-class-name="nowrap-label">
|
<el-tag :type="getStatusType(currentMeeting.status)">
|
{{ getStatusText(currentMeeting.status) }}
|
</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="申请时间"
|
label-class-name="nowrap-label">{{
|
currentMeeting.createTime
|
}}</el-descriptions-item>
|
<el-descriptions-item style="max-height: 400px"
|
label="会议说明"
|
:span="2"
|
label-class-name="nowrap-label">{{ currentMeeting.description }}</el-descriptions-item>
|
</el-descriptions>
|
<div class="content-section mt-20">
|
<h4>参会人员</h4>
|
<div class="participants-list">
|
<el-tag v-for="participant in currentMeeting.participants"
|
:key="participant.id"
|
style="margin-right: 10px; margin-bottom: 10px;">
|
{{ participant.name }}
|
</el-tag>
|
</div>
|
</div>
|
</div>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button @click="detailDialogVisible = false">关 闭</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
<!-- 会议审批对话框 -->
|
<el-dialog title="会议审批"
|
v-model="approvalDialogVisible">
|
<div v-if="currentMeeting">
|
<el-descriptions :column="2"
|
border>
|
<el-descriptions-item label="会议主题">{{ currentMeeting.title }}</el-descriptions-item>
|
<el-descriptions-item label="申请人">{{ currentMeeting.applicant }}</el-descriptions-item>
|
<el-descriptions-item label="主理人">{{ currentMeeting.host }}</el-descriptions-item>
|
<el-descriptions-item label="会议时间"
|
:span="2">
|
{{ formatDateTime(currentMeeting.meetingTime) }}
|
</el-descriptions-item>
|
<el-descriptions-item label="会议地点">{{ currentMeeting.location }}</el-descriptions-item>
|
<el-descriptions-item label="参会人数">{{ currentMeeting.participants.length }}人</el-descriptions-item>
|
</el-descriptions>
|
<div class="content-section mt-20">
|
<h4>参会人员</h4>
|
<div class="participants-list">
|
<el-tag v-for="participant in currentMeeting.participants"
|
:key="participant.id"
|
style="margin-right: 10px; margin-bottom: 10px;">
|
{{ participant.name }}
|
</el-tag>
|
</div>
|
</div>
|
<div v-show="false"
|
class="approval-opinion mt-20">
|
<h4>审批意见</h4>
|
<el-input v-model="approvalOpinion"
|
type="textarea"
|
placeholder="请输入审批意见"
|
:rows="4" />
|
</div>
|
</div>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button @click="approvalDialogVisible = false">取 消</el-button>
|
<el-button type="danger"
|
@click="submitApproval('2')">不通过</el-button>
|
<el-button type="primary"
|
@click="submitApproval('1')">通 过</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted } from "vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import Pagination from "@/components/Pagination/index.vue";
|
import {
|
getRoomEnum,
|
getExamineList,
|
saveMeetingApplication,
|
} from "@/api/collaborativeApproval/meeting.js";
|
import dayjs from "dayjs";
|
import { userListNoPageByTenantId } from "@/api/system/user.js";
|
|
// 数据列表加载状态
|
const loading = ref(false);
|
|
// 总条数
|
const total = ref(0);
|
|
// 表格高度(根据窗口高度自适应)
|
const tableHeight = ref(window.innerHeight - 380);
|
const roomEnum = ref([]);
|
const userList = ref([]);
|
// 审批列表数据
|
const approvalList = ref([]);
|
|
// 查询参数
|
const queryParams = reactive({
|
current: 1,
|
size: 10,
|
});
|
|
// 搜索表单
|
const searchForm = reactive({
|
title: "",
|
applicant: "",
|
status: "",
|
});
|
|
// 是否显示对话框
|
const detailDialogVisible = ref(false);
|
const approvalDialogVisible = ref(false);
|
|
// 当前查看的会议
|
const currentMeeting = ref(null);
|
|
// 审批意见
|
const approvalOpinion = ref("");
|
|
// 查询数据
|
const getList = async () => {
|
loading.value = true;
|
let resp = await getExamineList({ ...searchForm, ...queryParams });
|
const userMap = new Map(userList.value.map(u => [String(u.userId), u]));
|
approvalList.value = resp.data.records.map(it => {
|
let room = roomEnum.value.find(room => it.roomId === room.id);
|
it.location = `${room.name}(${room.location})`;
|
let participantIds = [];
|
try {
|
participantIds = Array.isArray(it.participants)
|
? it.participants
|
: JSON.parse(it.participants);
|
} catch (_e) {
|
participantIds = [];
|
}
|
if (!Array.isArray(participantIds)) participantIds = [];
|
it.staffCount = participantIds.length;
|
it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format(
|
"HH:mm:ss"
|
)} ~ ${dayjs(it.endTime).format("HH:mm:ss")}`;
|
it.participants = participantIds.map(id => {
|
const user = userMap.get(String(id));
|
const nickName = user?.nickName || user?.userName || String(id ?? "");
|
const deptNames = user?.deptNames || "";
|
return {
|
id,
|
name: deptNames ? `${nickName} (${deptNames})` : nickName,
|
};
|
});
|
|
return it;
|
});
|
total.value = resp.data.total;
|
loading.value = false;
|
};
|
|
// 搜索按钮操作
|
const handleSearch = () => {
|
queryParams.pageNum = 1;
|
getList();
|
};
|
|
// 重置搜索表单
|
const resetSearch = () => {
|
Object.assign(searchForm, {
|
title: "",
|
applicant: "",
|
status: "",
|
});
|
handleSearch();
|
};
|
|
// 查看详情
|
const viewDetail = row => {
|
currentMeeting.value = row;
|
detailDialogVisible.value = true;
|
};
|
|
// 处理审批
|
const handleApproval = row => {
|
currentMeeting.value = row;
|
approvalOpinion.value = "";
|
approvalDialogVisible.value = true;
|
};
|
|
// 获取状态类型
|
const getStatusType = status => {
|
const statusMap = {
|
0: "info", // 待审批
|
1: "success", // 已通过
|
2: "warning", // 未通过
|
3: "danger", // 取消
|
};
|
return statusMap[status] || "info";
|
};
|
|
// 获取状态文本
|
const getStatusText = status => {
|
const statusMap = {
|
0: "待审批",
|
1: "已通过",
|
2: "未通过",
|
3: "已取消",
|
};
|
return statusMap[status] || "未知";
|
};
|
|
// 格式化日期时间
|
const formatDateTime = dateTime => {
|
if (!dateTime) return "";
|
return dateTime.replace(" ", "\n");
|
};
|
|
// 提交审批
|
const submitApproval = status => {
|
// if (status === 'approved' && !approvalOpinion.value.trim()) {
|
// ElMessage.warning('请填写审批意见')
|
// return
|
// }
|
|
ElMessageBox.confirm(
|
`确认${status === "1" ? "通过" : "不通过"}该会议申请?`,
|
"审批确认",
|
{
|
confirmButtonText: "确定",
|
cancelButtonText: "取消",
|
type: "warning",
|
}
|
)
|
.then(() => {
|
saveMeetingApplication({
|
id: currentMeeting.value.id,
|
status: status,
|
}).then(resp => {
|
// 更新会议状态
|
currentMeeting.value.status = status;
|
|
ElMessage.success("审批提交成功");
|
approvalDialogVisible.value = false;
|
getList();
|
});
|
})
|
.catch(() => {});
|
};
|
|
// 页面加载时获取数据
|
onMounted(async () => {
|
const [resp1, resp2] = await Promise.all([
|
getRoomEnum(),
|
userListNoPageByTenantId(),
|
]);
|
roomEnum.value = resp1.data;
|
const list = Array.isArray(resp2?.data) ? resp2.data : [];
|
userList.value = list
|
.map(item => ({
|
userId: item?.userId,
|
nickName: item?.nickName,
|
userName: item?.userName,
|
deptNames:
|
item?.deptNames || (item?.dept?.deptName ? item.dept.deptName : ""),
|
}))
|
.filter(item => item.userId !== null && item.userId !== undefined);
|
|
await getList();
|
});
|
</script>
|
|
<style scoped>
|
.app-container {
|
padding: 20px;
|
}
|
|
.page-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.page-header h2 {
|
margin: 0;
|
color: #303133;
|
}
|
|
.search-card {
|
margin-bottom: 20px;
|
}
|
|
.dialog-footer {
|
display: flex;
|
justify-content: flex-end;
|
gap: 10px;
|
}
|
|
.content-section h4 {
|
margin: 0 0 15px 0;
|
color: #303133;
|
}
|
|
.mt-20 {
|
margin-top: 20px;
|
}
|
|
.participants-list {
|
min-height: 40px;
|
padding: 15px;
|
border-radius: 4px;
|
line-height: 1.6;
|
}
|
|
.approval-opinion h4 {
|
margin: 0 0 15px 0;
|
color: #303133;
|
}
|
|
.nowrap-label {
|
white-space: nowrap !important;
|
}
|
|
.description-content {
|
white-space: pre-wrap;
|
word-wrap: break-word;
|
line-height: 1.6;
|
padding: 10px;
|
background-color: #f5f7fa;
|
border-radius: 4px;
|
min-height: 60px;
|
}
|
</style>
|