share-space-vue/src/views/system/room/room_detail.vue
2025-01-08 15:38:32 +08:00

566 lines
14 KiB
Vue

<template>
<div class="app-container">
<el-card class="box-card" shadow="hover">
<!-- 操作按钮 -->
<div class="operation-buttons">
<el-button type="primary" icon="el-icon-edit" size="small" @click="handleUpdate"
v-hasPermi="['system:room:edit']">修改房间</el-button>
</div>
<!-- 房间基本信息 -->
<div class="room-header">
<el-row :gutter="40">
<el-col :span="8">
<div class="image-wrapper">
<el-image :src="room.picture" fit="cover" class="room-image">
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
</el-col>
<el-col :span="16">
<div class="room-title">
<h2>{{ room.roomName }}</h2>
<el-tag :type="room.status === '1' ? 'success' : 'info'" class="status-tag" effect="dark">
{{ room.status === '1' ? '正常' : '停用' }}
</el-tag>
</div>
<div class="room-info">
<el-descriptions :column="2" border size="medium">
<el-descriptions-item label="所属门店">
<span class="info-text">{{ room.storeName }}</span>
</el-descriptions-item>
<el-descriptions-item label="商户ID">
<span class="info-text">{{ room.merchantId }}</span>
</el-descriptions-item>
<el-descriptions-item label="房间ID">
<span class="info-text">{{ room.roomId }}</span>
</el-descriptions-item>
<el-descriptions-item label="房间类型">
<span class="info-text">{{ getRoomType(room.type) }} / {{ getRoomType2(room.type2) }}</span>
</el-descriptions-item>
<el-descriptions-item label="基础价格">
<span class="price">¥{{ room.hour }}</span>
<span class="unit">/小时</span>
</el-descriptions-item>
<el-descriptions-item label="销售数量">
<span class="info-text">{{ room.soldNum || '暂无数据' }}</span>
</el-descriptions-item>
</el-descriptions>
<div class="tags-section">
<span class="label">房间标签:</span>
<div class="tags-wrapper">
<dict-tag v-for="tag in room.tags" :key="tag" :options="dict.type.ss_room_tags" :value="tag"
class="tag-item" />
</div>
</div>
</div>
</el-col>
</el-row>
</div>
<!-- WiFi信息 -->
<div v-if="room.wifi" class="section-block">
<h3>
<i class="el-icon-connection"></i>
WiFi信息
</h3>
<el-descriptions :column="2" border size="medium">
<el-descriptions-item label="WiFi名称">
<span class="info-text">{{ room.wifi }}</span>
</el-descriptions-item>
<el-descriptions-item label="WiFi密码">
<span class="info-text">{{ room.wifiPassword }}</span>
</el-descriptions-item>
</el-descriptions>
</div>
<!-- 系统信息 -->
<div class="section-block">
<h3>
<i class="el-icon-info"></i>
系统信息
</h3>
<el-descriptions :column="3" border size="medium">
<el-descriptions-item label="创建人">
<span class="info-text">{{ room.createBy || '暂无' }}</span>
</el-descriptions-item>
<el-descriptions-item label="创建时间">
<span class="info-text">{{ room.createTime || '暂无' }}</span>
</el-descriptions-item>
<el-descriptions-item label="更新人">
<span class="info-text">{{ room.updateBy || '暂无' }}</span>
</el-descriptions-item>
<el-descriptions-item label="更新时间">
<span class="info-text">{{ room.updateTime || '暂无' }}</span>
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
<span class="info-text">{{ room.remark || '暂无' }}</span>
</el-descriptions-item>
</el-descriptions>
</div>
</el-card>
<!-- 修改房间弹窗 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="房间名" prop="roomName">
<el-input v-model="form.roomName" placeholder="请输入房间名" />
</el-form-item>
<el-form-item label="店铺" prop="storeId">
<el-select v-model="form.storeId" clearable filterable placeholder="请选择">
<el-option v-for="item in storeOptions" :key="item.storeId" :label="item.name" :value="item.storeId" />
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择类型">
<el-option v-for="dict in dict.type.ss_room_type" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="图片" prop="picture">
<image-upload v-model="form.picture" />
</el-form-item>
<el-form-item label="标签" prop="tags">
<el-select v-model="form.tags" placeholder="请选择标签" clearable multiple style="width: auto; min-width: 240px">
<el-option v-for="dict in dict.type.ss_room_tags" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
<el-tabs v-model="activeTab" class="detail-tabs">
<el-tab-pane label="订单列表" name="orders">
<order :roomId="room.roomId"></order>
</el-tab-pane>
<el-tab-pane label="设备列表" name="devices">
<device :roomId="room.roomId"></device>
</el-tab-pane>
<el-tab-pane label="套餐列表" name="rules">
<rule :roomId="room.roomId"></rule>
</el-tab-pane>
<el-tab-pane label="设施列表" name="equipments">
<equipment :roomId="room.roomId"></equipment>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { getRoom, updateRoom } from '@/api/system/room'
import { getDicts } from '@/api/system/dict/data'
import { listStore } from "@/api/system/store"
import order from '@/views/system/order/index.vue'
import device from '@/views/system/device/index.vue'
import rule from '@/views/system/rule/index.vue'
import equipment from '@/views/system/equipment/index.vue'
export default {
name: 'RoomDetail',
dicts: ['ss_room_type', 'ss_room_tags', 'ss_room_status'],
components: { order, device, rule, equipment },
data() {
return {
// 是否显示弹出层
open: false,
// 弹出层标题
title: "",
room: {},
// 房间类型字典
roomTypeOptions: [],
// 房间类型2字典
roomType2Options: [],
// 房间标签字典
roomTagOptions: [],
// 店铺选项
storeOptions: [],
// 表单参数
form: {},
// 表单校验
rules: {
roomName: [
{ required: true, message: "房间名不能为空", trigger: "blur" }
],
storeId: [
{ required: true, message: "店铺不能为空", trigger: "blur" }
],
type: [
{ required: true, message: "类型不能为空", trigger: "blur" }
],
tags: [
{ required: true, message: "标签不能为空", trigger: "change" }
],
},
activeTab: 'orders',
}
},
created() {
this.getDetail()
this.getDictData()
this.getStoreOptions()
},
methods: {
/** 获取房间详情 */
async getDetail() {
try {
const roomId = this.$route.params.roomId
const response = await getRoom(roomId)
this.room = response.data
} catch (error) {
console.error('获取房间详情失败:', error)
this.$message.error('获取房间详情失败')
}
},
/** 获取字典数据 */
async getDictData() {
try {
const [typeRes, type2Res, tagRes] = await Promise.all([
getDicts('room_type'),
getDicts('room_type2'),
getDicts('room_tag')
])
this.roomTypeOptions = typeRes.data
this.roomType2Options = type2Res.data
this.roomTagOptions = tagRes.data
} catch (error) {
console.error('获取字典数据失败:', error)
this.$message.error('获取字典数据失败')
}
},
/** 获取店铺选项 */
getStoreOptions() {
listStore({ pageNum: 1, pageSize: 999 }).then(response => {
this.storeOptions = response.rows;
});
},
/** 获取房间类型名称 */
getRoomType(type) {
const found = this.roomTypeOptions.find(item => item.dictValue === type)
return found ? found.dictLabel : type
},
/** 获取房间类型2名称 */
getRoomType2(type) {
const found = this.roomType2Options.find(item => item.dictValue === type)
return found ? found.dictLabel : type
},
/** 获取标签名称 */
getTagName(tag) {
const found = this.roomTagOptions.find(item => item.dictValue === tag)
return found ? found.dictLabel : tag
},
/** 修改按钮操作 */
handleUpdate() {
this.reset();
this.form = { ...this.room };
this.open = true;
this.title = "修改房间";
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
roomId: undefined,
roomName: undefined,
storeId: undefined,
type: undefined,
picture: undefined,
tags: []
};
this.resetForm("form");
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
updateRoom(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getDetail();
});
}
});
}
}
}
</script>
<style lang="scss" scoped>
.detail-tabs {
margin-top: 20px;
.search-form {
margin-bottom: 20px;
.el-form-item {
margin-bottom: 10px;
}
}
}
.app-container {
padding: 24px;
background-color: #f5f7fa;
min-height: calc(100vh - 84px);
}
.operation-buttons {
margin-bottom: 20px;
text-align: right;
}
.box-card {
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
.box-card:hover {
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.1);
}
.room-header {
margin-bottom: 48px;
}
.image-wrapper {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
position: relative;
}
.image-wrapper:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.room-image {
width: 100%;
height: 320px;
border-radius: 12px;
object-fit: cover;
}
.room-title {
display: flex;
align-items: center;
margin-bottom: 28px;
padding-bottom: 16px;
border-bottom: 1px solid #EBEEF5;
}
.room-title h2 {
margin: 0;
margin-right: 15px;
font-size: 28px;
color: #303133;
font-weight: 600;
}
.status-tag {
margin-left: auto;
padding: 0 16px;
height: 28px;
line-height: 28px;
font-size: 14px;
}
.room-info {
margin-top: 28px;
}
.info-text {
color: #606266;
font-size: 14px;
}
.tags-section {
margin-top: 28px;
padding: 16px;
background: #f9fafc;
border-radius: 8px;
}
.tags-wrapper {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.label {
color: #606266;
margin-right: 16px;
font-weight: 500;
font-size: 14px;
}
.tag-item {
margin-right: 10px;
margin-bottom: 8px;
border-radius: 4px;
padding: 0 12px;
height: 28px;
line-height: 26px;
}
.section-block {
margin-top: 48px;
padding-top: 32px;
border-top: 1px solid #EBEEF5;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 28px;
}
h3 {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #303133;
display: flex;
align-items: center;
}
h3 i {
margin-right: 12px;
font-size: 24px;
color: #409EFF;
}
.total-rules {
margin-top: 3px;
}
.price {
color: #F56C6C;
font-weight: 600;
font-size: 16px;
}
.unit {
color: #909399;
margin-left: 4px;
font-size: 14px;
}
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: #f5f7fa;
}
.image-slot i {
font-size: 48px;
color: #909399;
}
.custom-table {
margin-top: 16px;
border-radius: 8px;
overflow: hidden;
}
:deep(.el-descriptions) {
padding: 20px;
background-color: #fff;
border-radius: 8px;
}
:deep(.el-descriptions__label) {
font-weight: 500;
color: #606266;
}
:deep(.el-descriptions__content) {
padding: 12px 16px;
}
:deep(.el-table th) {
background-color: #f5f7fa !important;
font-weight: 500;
}
:deep(.el-table--border) {
border-radius: 8px;
overflow: hidden;
}
:deep(.el-descriptions__body) {
background-color: #fff;
}
:deep(.el-descriptions-item__label) {
background-color: #f5f7fa;
font-weight: 500;
}
// 标签选择器样式
.el-select {
:deep(.el-select__tags) {
flex-wrap: wrap;
.el-tag {
margin: 2px;
max-width: none;
}
}
:deep(.el-input__inner) {
height: auto;
min-height: 32px;
padding-top: 4px;
padding-bottom: 4px;
}
}
.el-select-dropdown__item {
padding: 0 10px;
height: 32px;
line-height: 32px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.el-form-item {
margin-bottom: 18px;
.el-select {
width: auto;
min-width: 240px;
max-width: 100%;
}
}
</style>