Files
hzdyjs/web/src/views/hq/hallAudit/index.vue
T
zlwu 46d21d3e55 style: 应用新主题色到全局组件
1. 更新Element UI相关样式文件
2. 更新布局组件样式
3. 更新所有业务页面组件样式
4. 删除废弃的测试页面
5. 优化主题切换相关代码
2025-07-05 13:53:25 +08:00

1220 lines
35 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="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>