|
@@ -0,0 +1,582 @@
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+// 当前的户型号是262b90e13929c5d589a1c111b9fe30b7 后面会从外面传入 先暂时写死这一个
|
|
|
|
|
+
|
|
|
|
|
+import { onMounted, ref, reactive } from 'vue'
|
|
|
|
|
+import { clientGet, clientPost, clientPostFormData } from '@/utils/request.ts'
|
|
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
|
+import { Plus, Edit, Delete, ArrowLeft, HomeFilled, View } from '@element-plus/icons-vue'
|
|
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
|
|
+
|
|
|
|
|
+interface AddAndUpdateRoomType {
|
|
|
|
|
+ id?: string; // 主键
|
|
|
|
|
+ roomNumber: string; // 房间
|
|
|
|
|
+ multipartFile?: File; // 图片改为可选(新增 ? 符号)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface Room {
|
|
|
|
|
+ id?: string; // 主键
|
|
|
|
|
+ houseTypeId: string; // 户型主键
|
|
|
|
|
+ roomNumber: string; // 房间
|
|
|
|
|
+ roomPictureUrl?: string; // 房间图片url
|
|
|
|
|
+ attribute?: string // 按钮属性
|
|
|
|
|
+ createTime?: Date; // 创建时间
|
|
|
|
|
+ createBy?: string; // 创建人
|
|
|
|
|
+ updateTime?: Date; // 修改时间
|
|
|
|
|
+ updateBy?: string; // 修改人
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface RoomListResponse extends BaseResponse{
|
|
|
|
|
+ data: Room[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 响应式数据
|
|
|
|
|
+const roomList = ref<Room[]>([])
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const dialogVisible = ref(false)
|
|
|
|
|
+const dialogTitle = ref('新增房间')
|
|
|
|
|
+const currentEditId = ref('')
|
|
|
|
|
+const selectedIds = ref<string[]>([])
|
|
|
|
|
+const previewVisible = ref(false)
|
|
|
|
|
+const previewImageUrl = ref('')
|
|
|
|
|
+const localPreviewUrl = ref('') // 本地预览URL
|
|
|
|
|
+const currentRoom = ref<Room | null>(null) // 当前编辑的房间数据
|
|
|
|
|
+const MINIO_BASE_URL = import.meta.env.VITE_MINIO_BASE_URL
|
|
|
|
|
+const houseTypeId = ref('');
|
|
|
|
|
+
|
|
|
|
|
+const route = useRoute()
|
|
|
|
|
+houseTypeId.value = route.query.houseTypeId as string;
|
|
|
|
|
+
|
|
|
|
|
+const router = useRouter();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// 表单数据(multipartFile 改为可选)
|
|
|
|
|
+const formData = reactive<AddAndUpdateRoomType>({
|
|
|
|
|
+ roomNumber: '',
|
|
|
|
|
+ multipartFile: null as any
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 表单规则(移除图片必填验证)
|
|
|
|
|
+const formRules = {
|
|
|
|
|
+ roomNumber: [
|
|
|
|
|
+ { required: true, message: '请输入房间', trigger: 'blur' }
|
|
|
|
|
+ // 移除 multipartFile 的必填规则
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const formRef = ref()
|
|
|
|
|
+const uploadRef = ref()
|
|
|
|
|
+
|
|
|
|
|
+//新增
|
|
|
|
|
+const addRoom = async (room: AddAndUpdateRoomType) => {
|
|
|
|
|
+ const formData = new FormData ()
|
|
|
|
|
+ formData.append('houseTypeId', houseTypeId.value);
|
|
|
|
|
+ formData.append('roomNumber', room.roomNumber);//这个必填
|
|
|
|
|
+
|
|
|
|
|
+ // 新增时图片为必填,需判断是否存在
|
|
|
|
|
+ if (room.multipartFile) {
|
|
|
|
|
+ formData.append('multipartFile', room.multipartFile);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.error('新增房间时请上传图片')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const res = await clientPostFormData<FormData, BaseResponse>('/aroom/save', formData);
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
|
|
+ ElMessage.error(res.msg)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success(res.msg)
|
|
|
|
|
+ getRoomList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+//批量删除
|
|
|
|
|
+const deleteRoom = async (ids: string[]) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await ElMessageBox.confirm('确定要删除选中的房间吗?', '提示', {
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
|
+ type: 'warning'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const res = await clientPost<string, BaseResponse>('/aroom/deleteBatch', JSON.stringify(ids));
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
|
|
+ ElMessage.error(res.msg)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success(res.msg)
|
|
|
|
|
+ selectedIds.value = []
|
|
|
|
|
+ getRoomList()
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ // 用户取消删除
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+//查询当前户型图的房间
|
|
|
|
|
+const getRoomList = async () => {
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await clientGet<string, RoomListResponse>('/aroom/getById/'+houseTypeId.value);
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
|
|
+ ElMessage.error(res.msg)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ //这就是当前户型下的房间
|
|
|
|
|
+ roomList.value = res.data || []
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ ElMessage.error('获取房间列表失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+//修改
|
|
|
|
|
+const updateRoom = async (roomId:string,room: AddAndUpdateRoomType) => {
|
|
|
|
|
+ const formData = new FormData ()
|
|
|
|
|
+ formData.append('id', roomId);
|
|
|
|
|
+ formData.append('houseTypeId', houseTypeId.value);
|
|
|
|
|
+ formData.append('roomNumber', room.roomNumber);//这个会修改
|
|
|
|
|
+
|
|
|
|
|
+ // 编辑时图片可选,存在时才上传
|
|
|
|
|
+ if (room.multipartFile) {
|
|
|
|
|
+ formData.append('multipartFile', room.multipartFile);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const res = await clientPostFormData<FormData, BaseResponse>('/aroom/update', formData);
|
|
|
|
|
+ if (res.code !== 200) {
|
|
|
|
|
+ ElMessage.error(res.msg)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success(res.msg)
|
|
|
|
|
+ getRoomList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 打开新增对话框
|
|
|
|
|
+const openAddDialog = () => {
|
|
|
|
|
+ dialogTitle.value = '新增房间'
|
|
|
|
|
+ currentEditId.value = ''
|
|
|
|
|
+ currentRoom.value = null
|
|
|
|
|
+ resetForm()
|
|
|
|
|
+ dialogVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 打开编辑对话框
|
|
|
|
|
+const openEditDialog = (item: Room) => {
|
|
|
|
|
+ dialogTitle.value = '编辑房间'
|
|
|
|
|
+ currentEditId.value = item.id || ''
|
|
|
|
|
+ currentRoom.value = item
|
|
|
|
|
+
|
|
|
|
|
+ formData.roomNumber = item.roomNumber || ''
|
|
|
|
|
+ formData.multipartFile = null as any
|
|
|
|
|
+
|
|
|
|
|
+ dialogVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 重置表单
|
|
|
|
|
+const resetForm = () => {
|
|
|
|
|
+ formData.roomNumber = ''
|
|
|
|
|
+ formData.multipartFile = null as any
|
|
|
|
|
+ localPreviewUrl.value = ''
|
|
|
|
|
+ formRef.value?.clearValidate()
|
|
|
|
|
+ uploadRef.value?.clearFiles()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 提交表单(调整验证逻辑)
|
|
|
|
|
+const submitForm = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await formRef.value.validate()
|
|
|
|
|
+
|
|
|
|
|
+ // 新增时必须上传图片,编辑时可选
|
|
|
|
|
+ if (!currentEditId.value && !formData.multipartFile) {
|
|
|
|
|
+ ElMessage.warning('新增房间时请上传房间图片')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (currentEditId.value) {
|
|
|
|
|
+ await updateRoom(currentEditId.value, formData)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await addRoom(formData)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ dialogVisible.value = false
|
|
|
|
|
+ resetForm()
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('表单验证失败', error)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 文件上传处理(保持原有逻辑)
|
|
|
|
|
+const handleFileChange = (file: any) => {
|
|
|
|
|
+ // 检查文件大小(10MB = 10 * 1024 * 1024 bytes)
|
|
|
|
|
+ const maxSize = 10 * 1024 * 1024
|
|
|
|
|
+ if (file.raw && file.raw.size > maxSize) {
|
|
|
|
|
+ ElMessage.error('上传文件大小不能超过 10MB!')
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ formData.multipartFile = file.raw
|
|
|
|
|
+
|
|
|
|
|
+ // 创建本地预览URL
|
|
|
|
|
+ if (file.raw) {
|
|
|
|
|
+ // 清理之前的URL
|
|
|
|
|
+ if (localPreviewUrl.value) {
|
|
|
|
|
+ URL.revokeObjectURL(localPreviewUrl.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ localPreviewUrl.value = URL.createObjectURL(file.raw)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return false // 阻止自动上传
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 删除单个项目
|
|
|
|
|
+const deleteItem = (item: Room) => {
|
|
|
|
|
+ if (item.id) {
|
|
|
|
|
+ deleteRoom([item.id])
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 批量删除选中项
|
|
|
|
|
+const deleteSelected = () => {
|
|
|
|
|
+ if (selectedIds.value.length === 0) {
|
|
|
|
|
+ ElMessage.warning('请选择要删除的项目')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ deleteRoom(selectedIds.value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 选择项目
|
|
|
|
|
+const toggleSelection = (id: string) => {
|
|
|
|
|
+ const index = selectedIds.value.indexOf(id)
|
|
|
|
|
+ if (index > -1) {
|
|
|
|
|
+ selectedIds.value.splice(index, 1)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ selectedIds.value.push(id)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 全选/取消全选
|
|
|
|
|
+const toggleSelectAll = () => {
|
|
|
|
|
+ if (selectedIds.value.length === roomList.value.length) {
|
|
|
|
|
+ selectedIds.value = []
|
|
|
|
|
+ } else {
|
|
|
|
|
+ selectedIds.value = roomList.value.map(item => item.id || '').filter(id => id)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 查看房间图片
|
|
|
|
|
+const viewRoomImage = (item: Room) => {
|
|
|
|
|
+ if (item.roomPictureUrl) {
|
|
|
|
|
+ previewImageUrl.value = MINIO_BASE_URL + item.roomPictureUrl
|
|
|
|
|
+ previewVisible.value = true
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.warning('该房间暂无图片')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 进入房间编辑
|
|
|
|
|
+// const enterRoomEdit = (item: Room) => {
|
|
|
|
|
+// console.log('进入房间编辑:', item)
|
|
|
|
|
+// ElMessage.info(`进入房间编辑: ${item.roomNumber}`)
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+// 返回户型列表
|
|
|
|
|
+const goBack = () => {
|
|
|
|
|
+ router.push('/ar/edit');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 清理URL对象
|
|
|
|
|
+const cleanupPreviewUrl = () => {
|
|
|
|
|
+ if (localPreviewUrl.value) {
|
|
|
|
|
+ URL.revokeObjectURL(localPreviewUrl.value)
|
|
|
|
|
+ localPreviewUrl.value = ''
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 关闭对话框时清理资源
|
|
|
|
|
+const handleDialogClose = () => {
|
|
|
|
|
+ cleanupPreviewUrl()
|
|
|
|
|
+ resetForm()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const init = () => {
|
|
|
|
|
+ getRoomList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(()=>{
|
|
|
|
|
+ init()
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="room-management-page h-screen bg-gray-50 p-6 flex flex-col overflow-hidden">
|
|
|
|
|
+ <!-- 页面头部 -->
|
|
|
|
|
+ <div class="mb-6">
|
|
|
|
|
+ <!-- 面包屑导航 -->
|
|
|
|
|
+ <div class="flex items-center mb-4">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ :icon="ArrowLeft"
|
|
|
|
|
+ @click="goBack"
|
|
|
|
|
+ class="mr-3 text-gray-600 hover:text-blue-600"
|
|
|
|
|
+ >
|
|
|
|
|
+ 返回户型列表
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <div class="flex items-center gap-2 text-sm text-gray-500">
|
|
|
|
|
+ <span>全景图管理</span>
|
|
|
|
|
+ <span>/</span>
|
|
|
|
|
+ <span class="text-gray-800 font-medium">房间管理</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="flex items-center justify-between mb-4">
|
|
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
|
|
+ <el-icon class="text-2xl text-blue-600"><HomeFilled /></el-icon>
|
|
|
|
|
+ <h1 class="text-2xl font-bold text-gray-800">房间管理</h1>
|
|
|
|
|
+ <span class="text-sm text-gray-500 bg-gray-100 px-2 py-1 rounded">
|
|
|
|
|
+ 户型ID: {{ houseTypeId }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
|
|
+ <el-button type="primary" :icon="Plus" @click="openAddDialog">
|
|
|
|
|
+ 新增房间
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 操作栏 -->
|
|
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
|
|
+ <el-checkbox
|
|
|
|
|
+ :indeterminate="selectedIds.length > 0 && selectedIds.length < roomList.length"
|
|
|
|
|
+ :model-value="selectedIds.length === roomList.length && roomList.length > 0"
|
|
|
|
|
+ @change="toggleSelectAll"
|
|
|
|
|
+ >
|
|
|
|
|
+ 全选
|
|
|
|
|
+ </el-checkbox>
|
|
|
|
|
+ <span class="text-sm text-gray-600">
|
|
|
|
|
+ 已选择 {{ selectedIds.length }} 项
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ v-if="selectedIds.length > 0"
|
|
|
|
|
+ type="danger"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ :icon="Delete"
|
|
|
|
|
+ @click="deleteSelected"
|
|
|
|
|
+ >
|
|
|
|
|
+ 批量删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="text-sm text-gray-600">
|
|
|
|
|
+ 共 {{ roomList.length }} 个房间
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 房间列表 -->
|
|
|
|
|
+ <div v-loading="loading" class="flex-1 overflow-y-auto mb-6">
|
|
|
|
|
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="item in roomList"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ class="bg-white rounded-lg shadow-md hover:shadow-lg transition-all duration-300 overflow-hidden cursor-pointer group relative"
|
|
|
|
|
+ :class="{ 'ring-2 ring-blue-500': selectedIds.includes(item.id || '') }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <!-- 房间图片 -->
|
|
|
|
|
+ <div class="relative h-48 bg-gray-200 overflow-hidden">
|
|
|
|
|
+ <!-- 图片区域的遮罩和操作按钮 -->
|
|
|
|
|
+ <div class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center gap-4 opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-20">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ round
|
|
|
|
|
+ :icon="View"
|
|
|
|
|
+ @click.stop="viewRoomImage(item)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 查看全景图
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+<!-- <el-button-->
|
|
|
|
|
+<!-- type="success"-->
|
|
|
|
|
+<!-- round-->
|
|
|
|
|
+<!-- :icon="Setting"-->
|
|
|
|
|
+<!-- @click.stop="enterRoomEdit(item)"-->
|
|
|
|
|
+<!-- >-->
|
|
|
|
|
+<!-- 编辑房间-->
|
|
|
|
|
+<!-- </el-button>-->
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <img
|
|
|
|
|
+ v-if="item.roomPictureUrl"
|
|
|
|
|
+ :src="MINIO_BASE_URL + item.roomPictureUrl"
|
|
|
|
|
+ :alt="item.roomNumber"
|
|
|
|
|
+ class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div v-else class="w-full h-full flex items-center justify-center text-gray-400">
|
|
|
|
|
+ <el-icon class="text-4xl"><HomeFilled /></el-icon>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 房间信息 -->
|
|
|
|
|
+ <div class="p-4">
|
|
|
|
|
+ <!-- 选择框 -->
|
|
|
|
|
+ <div class="flex items-center justify-between mb-3">
|
|
|
|
|
+ <el-checkbox
|
|
|
|
|
+ :model-value="selectedIds.includes(item.id || '')"
|
|
|
|
|
+ @change="toggleSelection(item.id || '')"
|
|
|
|
|
+ @click.stop
|
|
|
|
|
+ >
|
|
|
|
|
+ <span class="text-sm text-gray-600">选择</span>
|
|
|
|
|
+ </el-checkbox>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="flex items-center justify-between mb-2">
|
|
|
|
|
+ <h3 class="text-lg font-semibold text-gray-800 truncate">
|
|
|
|
|
+ {{ item.roomNumber || '未命名房间' }}
|
|
|
|
|
+ </h3>
|
|
|
|
|
+ <!-- <span v-if="item.attribute" class="text-xs bg-green-100 text-green-600 px-2 py-1 rounded">-->
|
|
|
|
|
+ <!-- {{ item.attribute }}-->
|
|
|
|
|
+ <!-- </span>-->
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="flex items-center justify-between text-xs text-gray-500 mb-3">
|
|
|
|
|
+ <span>创建时间</span>
|
|
|
|
|
+ <span>{{ item.createTime ? new Date(item.createTime).toLocaleDateString() : '-' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 操作按钮 -->
|
|
|
|
|
+ <div class="flex justify-end gap-2">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ round
|
|
|
|
|
+ :icon="Edit"
|
|
|
|
|
+ @click.stop="openEditDialog(item)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="danger"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ round
|
|
|
|
|
+ :icon="Delete"
|
|
|
|
|
+ @click.stop="deleteItem(item)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 空状态 -->
|
|
|
|
|
+ <div v-if="!loading && roomList.length === 0" class="text-center py-12">
|
|
|
|
|
+ <el-icon class="text-6xl text-gray-300 mb-4"><HomeFilled /></el-icon>
|
|
|
|
|
+ <p class="text-gray-500 text-lg mb-4">暂无房间数据</p>
|
|
|
|
|
+ <el-button type="primary" :icon="Plus" @click="openAddDialog">
|
|
|
|
|
+ 添加第一个房间
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 新增/编辑对话框 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="dialogVisible"
|
|
|
|
|
+ :title="dialogTitle"
|
|
|
|
|
+ width="600px"
|
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
|
+ @close="handleDialogClose"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="formRef"
|
|
|
|
|
+ :model="formData"
|
|
|
|
|
+ :rules="formRules"
|
|
|
|
|
+ label-width="80px"
|
|
|
|
|
+ label-position="left"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form-item label="房间" prop="roomNumber">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.roomNumber"
|
|
|
|
|
+ placeholder="请输入房间"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form-item label="房间图片"> <!-- 移除 required 属性 -->
|
|
|
|
|
+ <div class="w-full">
|
|
|
|
|
+ <!-- 当前图片预览(编辑模式下显示原图) -->
|
|
|
|
|
+ <div v-if="currentEditId && currentRoom?.roomPictureUrl && !localPreviewUrl" class="mb-4">
|
|
|
|
|
+ <div class="text-sm text-gray-600 mb-2">当前房间图片:</div>
|
|
|
|
|
+ <div class="w-32 h-32 border border-gray-300 rounded-lg overflow-hidden">
|
|
|
|
|
+ <img
|
|
|
|
|
+ :src="MINIO_BASE_URL + currentRoom.roomPictureUrl"
|
|
|
|
|
+ :alt="currentRoom.roomNumber"
|
|
|
|
|
+ class="w-full h-full object-cover"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 本地预览图片 -->
|
|
|
|
|
+ <div v-if="localPreviewUrl" class="mb-4">
|
|
|
|
|
+ <div class="text-sm text-gray-600 mb-2">{{ currentEditId ? '新上传的图片:' : '预览图片:' }}</div>
|
|
|
|
|
+ <div class="w-32 h-32 border border-gray-300 rounded-lg overflow-hidden">
|
|
|
|
|
+ <img
|
|
|
|
|
+ :src="localPreviewUrl"
|
|
|
|
|
+ alt="预览图片"
|
|
|
|
|
+ class="w-full h-full object-cover"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 上传组件(提示编辑时可选) -->
|
|
|
|
|
+ <el-upload
|
|
|
|
|
+ ref="uploadRef"
|
|
|
|
|
+ :auto-upload="false"
|
|
|
|
|
+ :show-file-list="true"
|
|
|
|
|
+ :limit="1"
|
|
|
|
|
+ accept="image/*"
|
|
|
|
|
+ :on-change="handleFileChange"
|
|
|
|
|
+ drag
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-icon class="text-4xl text-gray-400 mb-2"><Plus /></el-icon>
|
|
|
|
|
+ <div class="text-gray-600">
|
|
|
|
|
+ {{ currentEditId ? '可选:点击或拖拽替换房间图片' : '点击或拖拽上传房间图片' }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <template #tip>
|
|
|
|
|
+ <div class="text-xs text-gray-500 mt-2">
|
|
|
|
|
+ 支持 jpg、png 格式,文件大小不能超过 10MB(编辑时可不上传)
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-upload>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="flex justify-end gap-3">
|
|
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="submitForm">确定</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 图片预览对话框 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="previewVisible"
|
|
|
|
|
+ title="房间图片预览"
|
|
|
|
|
+ width="80%"
|
|
|
|
|
+ :close-on-click-modal="true"
|
|
|
|
|
+ append-to-body
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="flex justify-center">
|
|
|
|
|
+ <img
|
|
|
|
|
+ :src="previewImageUrl"
|
|
|
|
|
+ alt="房间图片预览"
|
|
|
|
|
+ class="max-w-full max-h-96 object-contain"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.room-management-page {
|
|
|
|
|
+ font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|