1220 lines
35 KiB
Vue
1220 lines
35 KiB
Vue
<template>
|
||
<div class="p-2 page-container">
|
||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||
<div ref="searchRef" v-show="showSearch" class="mb-[10px]">
|
||
<el-card shadow="hover">
|
||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="100px">
|
||
<el-form-item label="报告厅ID" prop="hallId">
|
||
<el-input v-model="queryParams.hallId" placeholder="请输入报告厅ID" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="报告厅名称" prop="hallName">
|
||
<el-input v-model="queryParams.hallName" placeholder="请输入报告厅名称" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="预约开始时间" prop="startTime">
|
||
<el-date-picker clearable v-model="queryParams.startTime" type="date" placeholder="请选择开始时间" value-format="YYYY-MM-DD" />
|
||
</el-form-item>
|
||
<el-form-item label="预约结束时间" prop="endTime">
|
||
<el-date-picker clearable v-model="queryParams.endTime" type="date" placeholder="请选择结束时间" value-format="YYYY-MM-DD" />
|
||
</el-form-item>
|
||
<el-form-item label="申请人姓名" prop="applicantName">
|
||
<el-input v-model="queryParams.applicantName" placeholder="请输入申请人姓名" clearable @keyup.enter="handleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="预约状态" prop="status">
|
||
<el-select v-model="queryParams.status" placeholder="请选择预约状态" clearable>
|
||
<el-option v-for="dict in hall_reserv_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
</transition>
|
||
|
||
<el-card shadow="never" class="button-card table-card">
|
||
<template #header>
|
||
<el-row ref="toolbarRef" :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="success" plain icon="Check" :disabled="multiple" @click="handleApprove()" v-hasPermi="['hq:hallReservation:audit']"
|
||
>批准</el-button
|
||
>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button type="danger" plain icon="Close" :disabled="multiple" @click="handleReject()" v-hasPermi="['hq:hallReservation:audit']"
|
||
>拒绝</el-button
|
||
>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="Download" @click="handleExport" v-hasPermi="['hq:hallReservation:export']">导出</el-button>
|
||
</el-col>
|
||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||
</el-row>
|
||
</template>
|
||
|
||
<el-table v-loading="loading" border :data="hallReservationList" @selection-change="handleSelectionChange" style="margin-bottom: 0">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="报告厅ID" align="center" prop="hallId" />
|
||
<el-table-column label="报告厅名称" align="center" prop="hallName" />
|
||
<el-table-column label="申请人" align="center" prop="applicantName" />
|
||
<el-table-column label="申请人电话" align="center" prop="applicantPhone" />
|
||
<el-table-column label="预约开始时间" align="center" prop="startTime" width="200">
|
||
<template #default="scope">
|
||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="预约结束时间" align="center" prop="endTime" width="200">
|
||
<template #default="scope">
|
||
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="预约用途" align="center" prop="reservationPurpose" min-width="120">
|
||
<template #default="scope">
|
||
<el-tooltip
|
||
:content="scope.row.reservationPurpose"
|
||
placement="top"
|
||
:disabled="!scope.row.reservationPurpose || scope.row.reservationPurpose.length <= 100"
|
||
popper-class="purpose-tooltip"
|
||
:show-after="300"
|
||
:hide-after="100"
|
||
effect="dark"
|
||
>
|
||
<span class="purpose-text">
|
||
{{
|
||
scope.row.reservationPurpose && scope.row.reservationPurpose.length > 100
|
||
? scope.row.reservationPurpose.substring(0, 100) + '...'
|
||
: scope.row.reservationPurpose
|
||
}}
|
||
</span>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="预约状态" align="center" prop="status">
|
||
<template #default="scope">
|
||
<dict-tag :options="hall_reserv_status" :value="scope.row.status" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="审核人" align="center" prop="auditName" />
|
||
<el-table-column label="审核时间" align="center" prop="auditTime" width="200">
|
||
<template #default="scope">
|
||
<span>{{ parseTime(scope.row.auditTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="审核备注" align="center" prop="auditRemark" min-width="120">
|
||
<template #default="scope">
|
||
<el-tooltip
|
||
:content="scope.row.auditRemark"
|
||
placement="top"
|
||
:disabled="!scope.row.auditRemark || scope.row.auditRemark.length <= 100"
|
||
popper-class="audit-remark-tooltip"
|
||
:show-after="300"
|
||
:hide-after="100"
|
||
effect="dark"
|
||
>
|
||
<span class="audit-remark-text">
|
||
{{
|
||
scope.row.auditRemark && scope.row.auditRemark.length > 100
|
||
? scope.row.auditRemark.substring(0, 100) + '...'
|
||
: scope.row.auditRemark
|
||
}}
|
||
</span>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
|
||
<template #default="scope">
|
||
<!-- 查看按钮,所有记录都可以查看 -->
|
||
<el-tooltip content="查看" placement="top">
|
||
<el-button
|
||
link
|
||
type="info"
|
||
icon="View"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleView(scope.row)"
|
||
></el-button>
|
||
</el-tooltip>
|
||
|
||
<!-- 只有审核中的记录可以进行审核操作 -->
|
||
<template v-if="scope.row.status === '0'">
|
||
<el-tooltip content="批准" placement="top">
|
||
<el-button
|
||
link
|
||
type="success"
|
||
icon="Check"
|
||
size="default"
|
||
style="font-size: 16px; margin-right: 8px"
|
||
@click="handleApprove(scope.row)"
|
||
v-hasPermi="['hq:hallReservation:audit']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="拒绝" placement="top">
|
||
<el-button
|
||
link
|
||
type="danger"
|
||
icon="Close"
|
||
size="default"
|
||
style="font-size: 16px"
|
||
@click="handleReject(scope.row)"
|
||
v-hasPermi="['hq:hallReservation:audit']"
|
||
></el-button>
|
||
</el-tooltip>
|
||
</template>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<div class="pagination-container">
|
||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 查看报告厅预约详情对话框 -->
|
||
<el-dialog :title="viewDialog.title" v-model="viewDialog.visible" width="700px" append-to-body>
|
||
<el-form :model="viewForm" label-width="120px" class="view-form">
|
||
<el-form-item label="报告厅ID">
|
||
<span class="form-text">{{ viewForm.hallId }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="报告厅名称">
|
||
<span class="form-text">{{ viewForm.hallName }}</span>
|
||
</el-form-item>
|
||
<!-- 报告厅图片展示区域 -->
|
||
<el-form-item label="报告厅图片" v-if="viewHallImages.length > 0">
|
||
<div class="view-hall-images">
|
||
<div class="image-gallery">
|
||
<div v-for="(image, index) in viewHallImages" :key="index" class="image-item" @click="previewImage(image, index)">
|
||
<img :src="image" class="gallery-image" @error="handleImageError" />
|
||
<div class="image-overlay">
|
||
<el-icon class="preview-icon"><ZoomIn /></el-icon>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="预约开始时间">
|
||
<span class="form-text">{{ parseTime(viewForm.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="预约结束时间">
|
||
<span class="form-text">{{ parseTime(viewForm.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="预约用途">
|
||
<div class="purpose-view">
|
||
{{ viewForm.reservationPurpose }}
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="申请人信息">
|
||
<div class="applicant-info">
|
||
<p><strong>姓名:</strong>{{ viewForm.applicantName }}</p>
|
||
<p><strong>电话:</strong>{{ viewForm.applicantPhone }}</p>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="预约状态">
|
||
<dict-tag :options="hall_reserv_status" :value="viewForm.status" />
|
||
</el-form-item>
|
||
<el-form-item label="审核人" v-if="viewForm.auditName">
|
||
<span class="form-text">{{ viewForm.auditName }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="审核时间" v-if="viewForm.auditTime">
|
||
<span class="form-text">{{ parseTime(viewForm.auditTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="审核备注" v-if="viewForm.auditRemark">
|
||
<div class="audit-remark">
|
||
{{ viewForm.auditRemark }}
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="closeView">关 闭</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 审核对话框 -->
|
||
<el-dialog :title="auditDialog.title" v-model="auditDialog.visible" width="800px" append-to-body>
|
||
<el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="120px" class="audit-form">
|
||
<!-- 按报告厅分组显示预约信息 -->
|
||
<el-form-item label="审核详情">
|
||
<div class="audit-details">
|
||
<div v-for="(hallGroup, hallName) in groupedReservations" :key="`hall-group-${hallName}`" class="hall-group">
|
||
<!-- 报告厅信息 -->
|
||
<div class="hall-info-section">
|
||
<div class="hall-header">
|
||
<div class="hall-name">{{ hallName }}</div>
|
||
<div class="hall-id">ID: {{ hallGroup.hallId }}</div>
|
||
</div>
|
||
<div class="hall-images" v-if="getHallImagesByName(hallName).length > 0">
|
||
<div class="image-gallery">
|
||
<div v-for="(image, index) in getHallImagesByName(hallName)" :key="index" class="image-item" @click="previewImage(image, index)">
|
||
<img :src="image" class="gallery-image" @error="handleImageError" />
|
||
<div class="image-overlay">
|
||
<el-icon class="preview-icon"><ZoomIn /></el-icon>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="no-images">暂无图片</div>
|
||
</div>
|
||
|
||
<!-- 该报告厅的预约申请列表 -->
|
||
<div class="reservation-list">
|
||
<div class="reservation-list-title">预约申请({{ hallGroup.reservations.length }}个)</div>
|
||
<div v-for="(record, index) in hallGroup.reservations" :key="record.id" class="reservation-item">
|
||
<div class="reservation-index">申请 {{ index + 1 }}</div>
|
||
<div class="reservation-details">
|
||
<div class="detail-row">
|
||
<span class="detail-label">申请人:</span>
|
||
<span class="detail-value">{{ record.applicantName }}</span>
|
||
<span class="detail-phone">({{ record.applicantPhone || '手机号暂无' }})</span>
|
||
</div>
|
||
<div class="detail-row">
|
||
<span class="detail-label">预约时间:</span>
|
||
<span class="detail-value">
|
||
{{ parseTime(record.startTime, '{y}-{m}-{d} {h}:{i}') }} - {{ parseTime(record.endTime, '{y}-{m}-{d} {h}:{i}') }}
|
||
</span>
|
||
</div>
|
||
<div class="detail-row">
|
||
<span class="detail-label">预约用途:</span>
|
||
<span class="detail-value purpose-content">{{ record.reservationPurpose }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<!-- 审核信息 -->
|
||
<el-form-item label="审核信息">
|
||
<div class="audit-info">
|
||
<div class="audit-status">
|
||
<span class="audit-label">审核状态:</span>
|
||
<el-tag :type="auditForm.status === '1' ? 'success' : 'danger'" size="large">
|
||
{{ auditForm.status === '1' ? '批准' : '拒绝' }}
|
||
</el-tag>
|
||
</div>
|
||
<div class="audit-remark-section">
|
||
<span class="audit-label">审核备注:</span>
|
||
<el-input
|
||
v-model="auditForm.auditRemark"
|
||
type="textarea"
|
||
:placeholder="auditForm.status === '1' ? '请输入审核备注(选填)' : '请输入拒绝原因'"
|
||
class="audit-remark-textarea"
|
||
resize="none"
|
||
maxlength="500"
|
||
show-word-limit
|
||
/>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button :loading="auditLoading" type="primary" @click="confirmAudit">确 定</el-button>
|
||
<el-button @click="cancelAudit">取 消</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="HallAudit" lang="ts">
|
||
import { listHallReservation, getHallReservation, auditHallReservation, HallReservationAuditForm } from '@/api/hq/hallAudit';
|
||
import { listHallInfo } from '@/api/hq/hallInfo';
|
||
import { HallReservationVO, HallReservationQuery, HallReservationForm } from '@/api/hq/hallReservation/types';
|
||
import { HallInfoVO } from '@/api/hq/hallInfo/types';
|
||
import { useUserStore } from '@/store/modules/user';
|
||
|
||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||
const { hall_reserv_status } = toRefs<any>(proxy?.useDict('hall_reserv_status'));
|
||
const userStore = useUserStore();
|
||
|
||
const hallReservationList = ref<HallReservationVO[]>([]);
|
||
const hallList = ref<HallInfoVO[]>([]);
|
||
const viewHallImages = ref<string[]>([]);
|
||
const loading = ref(true);
|
||
const showSearch = ref(true);
|
||
const ids = ref<Array<string | number>>([]);
|
||
const single = ref(true);
|
||
const multiple = ref(true);
|
||
const total = ref(0);
|
||
const windowHeight = ref(window.innerHeight);
|
||
|
||
const queryFormRef = ref<ElFormInstance>();
|
||
const auditFormRef = ref<ElFormInstance>();
|
||
|
||
// 审核表单验证规则
|
||
const auditRules = computed(() => ({
|
||
auditRemark: auditForm.value.status === '2' ? [{ required: true, message: '拒绝原因不能为空', trigger: 'blur' }] : []
|
||
}));
|
||
|
||
const viewDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const auditDialog = reactive<DialogOption>({
|
||
visible: false,
|
||
title: ''
|
||
});
|
||
|
||
const viewForm = ref<HallReservationForm>({
|
||
id: undefined,
|
||
hallId: undefined,
|
||
hallName: undefined,
|
||
startTime: undefined,
|
||
endTime: undefined,
|
||
reservationPurpose: undefined,
|
||
applicantId: undefined,
|
||
applicantName: undefined,
|
||
applicantPhone: undefined,
|
||
status: undefined,
|
||
auditBy: undefined,
|
||
auditName: undefined,
|
||
auditTime: undefined,
|
||
auditRemark: undefined
|
||
});
|
||
|
||
const auditForm = ref({
|
||
status: '1', // 1-批准, 2-拒绝
|
||
auditRemark: '',
|
||
ids: [] as Array<string | number>
|
||
});
|
||
|
||
const selectedRecords = ref<HallReservationVO[]>([]);
|
||
const auditLoading = ref(false);
|
||
|
||
const queryParams = ref<HallReservationQuery>({
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
hallId: undefined,
|
||
hallName: undefined,
|
||
startTime: undefined,
|
||
endTime: undefined,
|
||
applicantId: undefined, // 审核界面显示所有用户的预约记录
|
||
applicantName: undefined, // 申请人姓名搜索条件
|
||
status: '0', // 默认查询审核中的记录
|
||
params: {
|
||
orderByColumn: 'create_time',
|
||
isAsc: 'desc'
|
||
}
|
||
});
|
||
|
||
/** 获取报告厅列表 */
|
||
const getHallList = async () => {
|
||
try {
|
||
const res = await listHallInfo({
|
||
pageNum: 1,
|
||
pageSize: 1000,
|
||
hallStatus: '1'
|
||
}); // 获取正常状态的报告厅
|
||
hallList.value = res.rows || [];
|
||
|
||
// 调试信息:检查每个报告厅的图片信息
|
||
console.log('报告厅列表数据:', hallList.value);
|
||
hallList.value.forEach((hall, index) => {
|
||
console.log(`报告厅 ${index + 1} - ${hall.hallName}:`, {
|
||
id: hall.id,
|
||
hallImage: hall.hallImage,
|
||
images: getHallImages(hall.hallImage || '')
|
||
});
|
||
});
|
||
} catch (error) {
|
||
console.error('获取报告厅列表失败:', error);
|
||
hallList.value = [];
|
||
}
|
||
};
|
||
|
||
/** 处理报告厅图片 - 可能有多张图片,用逗号分隔 */
|
||
const getHallImages = (imageUrl: string): string[] => {
|
||
if (!imageUrl || imageUrl.trim() === '') return [];
|
||
// 支持多种分隔符:逗号、分号、空格
|
||
const separators = /[,;|\s]+/;
|
||
const images = imageUrl
|
||
.split(separators)
|
||
.map((url) => url.trim())
|
||
.filter((url) => url && url !== 'null' && url !== 'undefined');
|
||
|
||
console.log('处理图片URL:', imageUrl, '结果:', images);
|
||
return images;
|
||
};
|
||
|
||
/** 获取记录对应的报告厅图片 */
|
||
const getRecordHallImages = (record: HallReservationVO): string[] => {
|
||
const selectedHall = hallList.value.find((hall) => hall.hallName === record.hallName);
|
||
if (selectedHall && selectedHall.hallImage) {
|
||
return getHallImages(selectedHall.hallImage);
|
||
}
|
||
return [];
|
||
};
|
||
|
||
/** 根据报告厅名称获取图片 */
|
||
const getHallImagesByName = (hallName: string): string[] => {
|
||
const selectedHall = hallList.value.find((hall) => hall.hallName === hallName);
|
||
if (selectedHall && selectedHall.hallImage) {
|
||
return getHallImages(selectedHall.hallImage);
|
||
}
|
||
return [];
|
||
};
|
||
|
||
/** 获取选中记录中的唯一报告厅名称 */
|
||
const uniqueHallNames = computed(() => {
|
||
return [...new Set(selectedRecords.value.map((record) => record.hallName))];
|
||
});
|
||
|
||
/** 按报告厅分组的预约记录 */
|
||
const groupedReservations = computed(() => {
|
||
const groups: Record<string, { hallId: string; reservations: HallReservationVO[] }> = {};
|
||
|
||
selectedRecords.value.forEach((record) => {
|
||
const hallName = record.hallName;
|
||
if (!groups[hallName]) {
|
||
groups[hallName] = {
|
||
hallId: String(record.hallId || ''),
|
||
reservations: []
|
||
};
|
||
}
|
||
groups[hallName].reservations.push(record);
|
||
});
|
||
|
||
return groups;
|
||
});
|
||
|
||
/** 处理图片加载成功 */
|
||
const handleImageLoad = (event: Event) => {
|
||
const img = event.target as HTMLImageElement;
|
||
console.log('图片加载成功:', img.src);
|
||
};
|
||
|
||
/** 处理图片加载错误 */
|
||
const handleImageError = (event: Event) => {
|
||
const img = event.target as HTMLImageElement;
|
||
console.log('图片加载失败:', img.src);
|
||
img.style.display = 'none';
|
||
};
|
||
|
||
/** 预览图片 */
|
||
const previewImage = (imageUrl: string, index: number) => {
|
||
// 创建一个临时的图片预览对话框
|
||
const imageViewer = document.createElement('div');
|
||
imageViewer.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 9999;
|
||
cursor: pointer;
|
||
`;
|
||
|
||
const img = document.createElement('img');
|
||
img.src = imageUrl;
|
||
img.style.cssText = `
|
||
max-width: 90%;
|
||
max-height: 90%;
|
||
object-fit: contain;
|
||
`;
|
||
|
||
imageViewer.appendChild(img);
|
||
document.body.appendChild(imageViewer);
|
||
|
||
// 点击关闭预览
|
||
imageViewer.addEventListener('click', () => {
|
||
document.body.removeChild(imageViewer);
|
||
});
|
||
};
|
||
|
||
/** 查询报告厅预约列表 */
|
||
const getList = async () => {
|
||
loading.value = true;
|
||
try {
|
||
// 构建查询参数,处理时间格式
|
||
const params = { ...queryParams.value };
|
||
|
||
// 审核界面显示所有用户的预约记录,不限制申请人
|
||
// 注释掉原来的用户限制逻辑
|
||
// params.applicantId = userStore.userId;
|
||
|
||
console.log('查询参数:', params);
|
||
|
||
// 将时间参数转换为字符串格式的参数
|
||
if (params.startTime) {
|
||
params.startTimeStr = params.startTime + ' 00:00:00';
|
||
params.startTime = undefined; // 清除原始的Date参数
|
||
}
|
||
|
||
if (params.endTime) {
|
||
params.endTimeStr = params.endTime + ' 23:59:59';
|
||
params.endTime = undefined; // 清除原始的Date参数
|
||
}
|
||
|
||
console.log('最终查询参数:', params);
|
||
|
||
const res = await listHallReservation(params);
|
||
console.log('查询结果:', res);
|
||
|
||
hallReservationList.value = res.rows;
|
||
total.value = res.total;
|
||
} catch (error) {
|
||
console.error('查询报告厅预约列表失败:', error);
|
||
hallReservationList.value = [];
|
||
total.value = 0;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
/** 搜索按钮操作 */
|
||
const handleQuery = () => {
|
||
queryParams.value.pageNum = 1;
|
||
getList();
|
||
};
|
||
|
||
/** 重置按钮操作 */
|
||
const resetQuery = () => {
|
||
queryFormRef.value?.resetFields();
|
||
// 审核界面重置后不限制申请人
|
||
queryParams.value.applicantId = undefined;
|
||
handleQuery();
|
||
};
|
||
|
||
/** 多选框选中数据 */
|
||
const handleSelectionChange = (selection: HallReservationVO[]) => {
|
||
ids.value = selection.map((item) => item.id);
|
||
|
||
// 只有审核中的记录可以进行编辑和删除操作
|
||
const editableSelection = selection.filter((item) => item.status === '0');
|
||
|
||
single.value = editableSelection.length != 1;
|
||
multiple.value = !editableSelection.length;
|
||
};
|
||
|
||
/** 查看按钮操作 */
|
||
const handleView = async (row: HallReservationVO) => {
|
||
const res = await getHallReservation(row.id);
|
||
Object.assign(viewForm.value, res.data);
|
||
|
||
// 查找对应的报告厅并显示图片
|
||
if (viewForm.value.hallName) {
|
||
const selectedHall = hallList.value.find((hall) => hall.hallName === viewForm.value.hallName);
|
||
if (selectedHall && selectedHall.hallImage) {
|
||
viewHallImages.value = getHallImages(selectedHall.hallImage);
|
||
} else {
|
||
viewHallImages.value = [];
|
||
}
|
||
} else {
|
||
viewHallImages.value = [];
|
||
}
|
||
|
||
viewDialog.visible = true;
|
||
viewDialog.title = '查看报告厅预约详情';
|
||
};
|
||
|
||
/** 关闭查看对话框 */
|
||
const closeView = () => {
|
||
viewDialog.visible = false;
|
||
viewHallImages.value = [];
|
||
};
|
||
|
||
/** 批准按钮操作 */
|
||
const handleApprove = async (row?: HallReservationVO) => {
|
||
let _ids;
|
||
let records: HallReservationVO[] = [];
|
||
|
||
if (row) {
|
||
// 单个批准时检查状态
|
||
if (row.status !== '0') {
|
||
proxy?.$modal.msgWarning('该记录已审核,不能重复操作');
|
||
return;
|
||
}
|
||
_ids = [row.id];
|
||
records = [row];
|
||
} else {
|
||
// 批量批准时过滤出可操作的记录
|
||
const auditableRecords = hallReservationList.value.filter((item) => ids.value.includes(item.id) && item.status === '0');
|
||
|
||
if (auditableRecords.length === 0) {
|
||
proxy?.$modal.msgWarning('所选记录已审核,不能重复操作');
|
||
return;
|
||
}
|
||
_ids = auditableRecords.map((item) => item.id);
|
||
records = auditableRecords;
|
||
}
|
||
|
||
// 显示审核对话框
|
||
openAuditDialog('1', _ids, records, '批准预约申请');
|
||
};
|
||
|
||
/** 拒绝按钮操作 */
|
||
const handleReject = async (row?: HallReservationVO) => {
|
||
let _ids;
|
||
let records: HallReservationVO[] = [];
|
||
|
||
if (row) {
|
||
// 单个拒绝时检查状态
|
||
if (row.status !== '0') {
|
||
proxy?.$modal.msgWarning('该记录已审核,不能重复操作');
|
||
return;
|
||
}
|
||
_ids = [row.id];
|
||
records = [row];
|
||
} else {
|
||
// 批量拒绝时过滤出可操作的记录
|
||
const auditableRecords = hallReservationList.value.filter((item) => ids.value.includes(item.id) && item.status === '0');
|
||
|
||
if (auditableRecords.length === 0) {
|
||
proxy?.$modal.msgWarning('所选记录已审核,不能重复操作');
|
||
return;
|
||
}
|
||
_ids = auditableRecords.map((item) => item.id);
|
||
records = auditableRecords;
|
||
}
|
||
|
||
// 显示审核对话框
|
||
openAuditDialog('2', _ids, records, '拒绝预约申请');
|
||
};
|
||
|
||
/** 打开审核对话框 */
|
||
const openAuditDialog = (status: string, ids: Array<string | number>, records: HallReservationVO[], title: string) => {
|
||
auditForm.value.status = status;
|
||
auditForm.value.auditRemark = '';
|
||
auditForm.value.ids = ids;
|
||
selectedRecords.value = records;
|
||
auditDialog.title = title;
|
||
auditDialog.visible = true;
|
||
};
|
||
|
||
/** 确认审核操作 */
|
||
const confirmAudit = async () => {
|
||
auditFormRef.value?.validate(async (valid: boolean) => {
|
||
if (valid) {
|
||
auditLoading.value = true;
|
||
try {
|
||
// 构建审核数据
|
||
const auditData: HallReservationAuditForm = {
|
||
ids: auditForm.value.ids,
|
||
status: auditForm.value.status,
|
||
auditRemark: auditForm.value.auditRemark || ''
|
||
};
|
||
|
||
console.log('提交审核数据:', auditData);
|
||
|
||
// 调用审核接口
|
||
await auditHallReservation(auditData);
|
||
|
||
const actionText = auditForm.value.status === '1' ? '批准' : '拒绝';
|
||
proxy?.$modal.msgSuccess(`${actionText}成功`);
|
||
|
||
// 关闭对话框并刷新列表
|
||
auditDialog.visible = false;
|
||
await getList();
|
||
|
||
// 清空选中状态
|
||
ids.value = [];
|
||
selectedRecords.value = [];
|
||
} catch (error) {
|
||
console.error('审核失败:', error);
|
||
const actionText = auditForm.value.status === '1' ? '批准' : '拒绝';
|
||
proxy?.$modal.msgError(`${actionText}失败,请稍后重试`);
|
||
} finally {
|
||
auditLoading.value = false;
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
/** 取消审核操作 */
|
||
const cancelAudit = () => {
|
||
// 重置表单数据
|
||
auditForm.value.auditRemark = '';
|
||
auditForm.value.ids = [];
|
||
selectedRecords.value = [];
|
||
|
||
// 清除表单验证状态
|
||
auditFormRef.value?.resetFields();
|
||
|
||
// 关闭对话框
|
||
auditDialog.visible = false;
|
||
};
|
||
|
||
/** 导出按钮操作 */
|
||
const handleExport = () => {
|
||
proxy?.download(
|
||
'hq/hallReservation/export',
|
||
{
|
||
...queryParams.value
|
||
},
|
||
`hallReservation_${new Date().getTime()}.xlsx`
|
||
);
|
||
};
|
||
|
||
onMounted(() => {
|
||
getList();
|
||
getHallList();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 页面容器样式 */
|
||
.page-container {
|
||
padding: 8px;
|
||
}
|
||
|
||
/* 表格卡片布局 */
|
||
.table-card {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.table-card :deep(.el-card__body) {
|
||
padding: 0px 20px 20px 20px !important;
|
||
}
|
||
|
||
/* 移除按钮栏底部边框 */
|
||
.button-card :deep(.el-card__header) {
|
||
border-bottom: none !important;
|
||
}
|
||
|
||
/* 分页容器样式 */
|
||
.pagination-container {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
padding: 16px 20px 0 0 !important;
|
||
background-color: #fff;
|
||
margin-top: 0px !important;
|
||
}
|
||
|
||
.hall-option {
|
||
padding: 8px 0;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.hall-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.hall-name {
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.hall-id {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
.hall-description {
|
||
font-size: 12px;
|
||
color: #606266;
|
||
margin-bottom: 4px;
|
||
line-height: 1.3;
|
||
max-height: 36px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.hall-images {
|
||
display: flex;
|
||
gap: 4px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.hall-image {
|
||
width: 40px;
|
||
height: 30px;
|
||
object-fit: cover;
|
||
border-radius: 4px;
|
||
border: 1px solid #dcdfe6;
|
||
}
|
||
|
||
.no-image-text {
|
||
font-size: 11px;
|
||
color: #c0c4cc;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* 预约用途固定宽高 */
|
||
.purpose-textarea :deep(.el-textarea__inner) {
|
||
width: 100%;
|
||
max-width: 450px;
|
||
height: 120px;
|
||
resize: none;
|
||
min-height: 120px;
|
||
max-height: 120px;
|
||
}
|
||
|
||
/* 表格中预约用途文本样式 */
|
||
.purpose-text {
|
||
display: inline-block;
|
||
max-width: 100%;
|
||
word-break: break-all;
|
||
word-wrap: break-word;
|
||
line-height: 1.4;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* 表格中审核备注文本样式 */
|
||
.audit-remark-text {
|
||
display: inline-block;
|
||
max-width: 100%;
|
||
word-break: break-all;
|
||
word-wrap: break-word;
|
||
line-height: 1.4;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* 查看详情表单样式 */
|
||
.view-form .form-text {
|
||
color: #303133;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.purpose-view {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
padding: 12px;
|
||
width: 450px;
|
||
height: 120px;
|
||
line-height: 1.6;
|
||
color: #303133;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.applicant-info p {
|
||
margin: 0 0 8px 0;
|
||
color: #303133;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.applicant-info p:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.audit-remark {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
padding: 12px;
|
||
width: 450px;
|
||
height: 80px;
|
||
line-height: 1.6;
|
||
color: #303133;
|
||
white-space: pre-wrap;
|
||
word-break: break-word;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.view-hall-images {
|
||
width: 100%;
|
||
}
|
||
|
||
/* 选中报告厅的图片展示区域 */
|
||
.selected-hall-images {
|
||
width: 100%;
|
||
}
|
||
|
||
.image-gallery {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
|
||
.image-item {
|
||
position: relative;
|
||
width: 120px;
|
||
height: 90px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: transform 0.2s ease;
|
||
border: 2px solid #e4e7ed;
|
||
}
|
||
|
||
.image-item:hover {
|
||
transform: scale(1.05);
|
||
border-color: #409eff;
|
||
}
|
||
|
||
.gallery-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
}
|
||
|
||
.image-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 0;
|
||
transition: opacity 0.2s ease;
|
||
}
|
||
|
||
.image-item:hover .image-overlay {
|
||
opacity: 1;
|
||
}
|
||
|
||
.preview-icon {
|
||
color: white;
|
||
font-size: 20px;
|
||
}
|
||
|
||
/* 审核对话框样式 */
|
||
.audit-form .form-text {
|
||
color: #303133;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 审核详情分组样式 */
|
||
.audit-details {
|
||
width: 100%;
|
||
}
|
||
|
||
.hall-group {
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
border: 2px solid #e9ecef;
|
||
background: #fafbfc;
|
||
}
|
||
|
||
.hall-group:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
/* 报告厅信息区域 */
|
||
.hall-info-section {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
margin-bottom: 16px;
|
||
border: 1px solid #e4e7ed;
|
||
}
|
||
|
||
.hall-header {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.hall-name {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1e40af;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.hall-id {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
background: #f0f2f5;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.hall-images {
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.no-images {
|
||
color: #9ca3af;
|
||
font-style: italic;
|
||
text-align: center;
|
||
padding: 16px;
|
||
background: #f9fafb;
|
||
border-radius: 6px;
|
||
border: 2px dashed #d1d5db;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
/* 预约申请列表 */
|
||
.reservation-list {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
border: 1px solid #e4e7ed;
|
||
}
|
||
|
||
.reservation-list-title {
|
||
background: #f5f7fa;
|
||
padding: 12px 16px;
|
||
font-weight: 600;
|
||
color: #606266;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
border-radius: 8px 8px 0 0;
|
||
}
|
||
|
||
.reservation-item {
|
||
padding: 16px;
|
||
border-bottom: 1px solid #f0f2f5;
|
||
position: relative;
|
||
}
|
||
|
||
.reservation-item:last-child {
|
||
border-bottom: none;
|
||
border-radius: 0 0 8px 8px;
|
||
}
|
||
|
||
.reservation-index {
|
||
position: absolute;
|
||
top: 12px;
|
||
right: 16px;
|
||
background: #1e40af;
|
||
color: white;
|
||
padding: 4px 8px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.reservation-details {
|
||
line-height: 1.8;
|
||
padding-right: 80px; /* 为右上角标签留出空间 */
|
||
}
|
||
|
||
.detail-row {
|
||
display: flex;
|
||
margin-bottom: 8px;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.detail-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.detail-label {
|
||
font-weight: 600;
|
||
color: #606266;
|
||
min-width: 80px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.detail-value {
|
||
color: #303133;
|
||
flex: 1;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.detail-phone {
|
||
color: #909399;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
.purpose-content {
|
||
line-height: 1.6;
|
||
max-height: 60px;
|
||
overflow-y: auto;
|
||
padding: 4px 0;
|
||
}
|
||
|
||
/* 审核信息样式 */
|
||
.audit-info {
|
||
width: 100%;
|
||
}
|
||
|
||
.audit-status {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.audit-label {
|
||
font-weight: 600;
|
||
color: #606266;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.audit-remark-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.audit-remark-section .audit-label {
|
||
margin-bottom: 8px;
|
||
margin-right: 0;
|
||
}
|
||
|
||
.audit-remark-textarea :deep(.el-textarea__inner) {
|
||
width: 100%;
|
||
height: 120px !important;
|
||
min-height: 120px !important;
|
||
max-height: 120px !important;
|
||
line-height: 1.6;
|
||
font-size: 14px;
|
||
resize: none !important;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* 全局样式,用于美化下拉框 */
|
||
.hall-select-dropdown .el-select-dropdown__item.hall-option-item {
|
||
height: auto !important;
|
||
padding: 12px 20px !important;
|
||
line-height: normal !important;
|
||
}
|
||
|
||
.hall-select-dropdown .el-select-dropdown__item.hall-option-item:hover {
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
.hall-select-dropdown .el-select-dropdown__item.hall-option-item.selected {
|
||
background-color: #ecf5ff;
|
||
}
|
||
|
||
.hall-select-dropdown {
|
||
max-height: 400px;
|
||
}
|
||
|
||
/* 预约用途tooltip美化 */
|
||
.purpose-tooltip {
|
||
max-width: 400px !important;
|
||
width: auto !important;
|
||
}
|
||
|
||
.purpose-tooltip .el-tooltip__content {
|
||
max-width: 400px !important;
|
||
white-space: pre-wrap !important;
|
||
word-break: break-word !important;
|
||
line-height: 1.6 !important;
|
||
padding: 12px 16px !important;
|
||
font-size: 14px !important;
|
||
border-radius: 8px !important;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
|
||
}
|
||
|
||
/* 审核备注tooltip美化 */
|
||
.audit-remark-tooltip {
|
||
max-width: 400px !important;
|
||
width: auto !important;
|
||
}
|
||
|
||
.audit-remark-tooltip .el-tooltip__content {
|
||
max-width: 400px !important;
|
||
white-space: pre-wrap !important;
|
||
word-break: break-word !important;
|
||
line-height: 1.6 !important;
|
||
padding: 12px 16px !important;
|
||
font-size: 14px !important;
|
||
border-radius: 8px !important;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
|
||
}
|
||
</style>
|