1113 lines
31 KiB
Vue
1113 lines
31 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<el-card class="box-card" shadow="hover">
|
||
<!-- 操作按钮 -->
|
||
<div class="operation-buttons">
|
||
<el-popover placement="top" trigger="hover">
|
||
<div class="qr-code-box">
|
||
<qr-code :text="getCodeText(room)" :width="150" :height="150" />
|
||
<p>扫描二维码进入房间</p>
|
||
</div>
|
||
<el-button slot="reference" type="primary" icon="el-icon-picture">查看</el-button>
|
||
</el-popover>
|
||
<el-button
|
||
type="success"
|
||
icon="el-icon-unlock"
|
||
size="small"
|
||
v-if="!isEquipment"
|
||
@click="handleOpenDoor"
|
||
>开门</el-button>
|
||
<el-button
|
||
type="primary"
|
||
icon="el-icon-unlock"
|
||
size="small"
|
||
@click="handleSwitchAll(true)"
|
||
>全开</el-button>
|
||
<el-button
|
||
type="warning"
|
||
icon="el-icon-lock"
|
||
size="small"
|
||
@click="handleSwitchAll(false)"
|
||
>全关</el-button>
|
||
<el-button type="primary" icon="el-icon-edit" size="small" @click="handleUpdate"
|
||
v-hasPermi="['system:room:edit']">修改</el-button>
|
||
<el-button
|
||
type="success"
|
||
icon="el-icon-plus"
|
||
size="small"
|
||
@click="handleBandRule"
|
||
v-hasPermi="['system:room:edit']"
|
||
>应用套餐</el-button>
|
||
</div>
|
||
|
||
<!-- 基本信息 -->
|
||
<div class="room-header">
|
||
<el-row :gutter="40">
|
||
<el-col :span="6">
|
||
<div class="image-wrapper">
|
||
<el-carousel
|
||
ref="carousel"
|
||
height="200px"
|
||
indicator-position="outside"
|
||
:autoplay="true"
|
||
trigger="click"
|
||
arrow="always"
|
||
>
|
||
<el-carousel-item v-for="(url, index) in room.pictures" :key="index">
|
||
<el-image
|
||
:src="url"
|
||
class="carousel-image"
|
||
fit="fill"
|
||
>
|
||
<div slot="error" class="image-slot">
|
||
<i class="el-icon-picture-outline"></i>
|
||
</div>
|
||
</el-image>
|
||
</el-carousel-item>
|
||
</el-carousel>
|
||
</div>
|
||
</el-col>
|
||
<el-col :span="18">
|
||
<div class="room-title">
|
||
<h2>{{ room.roomName }}</h2>
|
||
<dict-tag :options="dict.type.ss_room_status" :value="room.status"/>
|
||
</div>
|
||
<div class="room-info">
|
||
<el-descriptions :column="4" border size="medium"
|
||
:content-style="{ textAlign: 'left', padding: '8px' }"
|
||
:label-style="{ textAlign: 'right', padding: '8px', fontWeight: 'bold' }"
|
||
class="compact-descriptions"
|
||
>
|
||
<el-descriptions-item label="所属门店">
|
||
<router-link
|
||
:to="`/system/storeDetail/index/${room.storeId}`"
|
||
class="link-type">
|
||
{{ room.storeName }}
|
||
</router-link>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="ID">
|
||
<span class="info-text">{{ room.roomId }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="商户">
|
||
<router-link
|
||
:to="`/user/detail/${room.merchantId}`"
|
||
class="link-type">
|
||
{{ room.merchantName }}
|
||
</router-link>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="类型">
|
||
<div class="type-tags">
|
||
<dict-tag :options="dict.type.ss_room_type" :value="room.type" />
|
||
<dict-tag :options="dict.type.ss_room_type2" :value="room.type2" />
|
||
</div>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="标签" :span="2">
|
||
<div class="tags-container">
|
||
<dict-tag
|
||
v-for="tag in room.tags"
|
||
:key="tag"
|
||
:options="dict.type.ss_room_tags"
|
||
:value="tag"
|
||
/>
|
||
</div>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="WiFi名称" :span="2">
|
||
<span class="info-text" v-if="room.wifi">
|
||
{{ room.wifi }}({{ room.wifiPassword }})
|
||
</span>
|
||
<span class="info-text" v-else>
|
||
--
|
||
</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="总营收" :span="2">
|
||
<span class="info-text red">{{ room.totalIncome || '0' }}元</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="总订单数">
|
||
<span class="info-text">{{ room.soldNum || '0' }}</span>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<!-- <!– WiFi信息 –>-->
|
||
<!-- <div class="section-block">-->
|
||
<!-- <h3>-->
|
||
<!-- <i class="el-icon-connection"></i>-->
|
||
<!-- WiFi信息-->
|
||
<!-- </h3>-->
|
||
<!-- -->
|
||
<!-- </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="800px" 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="isEquipment?'设施码':'房间码'" prop="code">
|
||
<el-input
|
||
v-model="form.code"
|
||
placeholder="请输入房间码"
|
||
style="width: calc(100% - 70px); margin-right: 10px;"
|
||
/>
|
||
<el-button type="success" @click="generateAndSetCode(isEquipment?'3':'2')">生成</el-button>
|
||
</el-form-item>
|
||
<el-form-item label="图片" prop="picture">
|
||
<image-upload
|
||
v-model="form.picture"
|
||
:value="form.picture || (Array.isArray(form.pictures) ? form.pictures.join(',') : '')"
|
||
:limit="5"
|
||
:fileSize="5"
|
||
:fileType="['png', 'jpg', 'jpeg']"
|
||
tip="建议尺寸:750px * 750px,大小不超过5M"
|
||
@input="val => {
|
||
form.picture = val;
|
||
form.pictures = val ? val.split(',').filter(Boolean) : [];
|
||
}"
|
||
/>
|
||
</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-item label="套餐" prop="ruleIds">-->
|
||
<!-- <el-select-->
|
||
<!-- v-model="form.ruleIds"-->
|
||
<!-- multiple-->
|
||
<!-- filterable-->
|
||
<!-- placeholder="请选择套餐"-->
|
||
<!-- style="width: 100%"-->
|
||
<!-- >-->
|
||
<!-- <el-option-->
|
||
<!-- v-for="item in ruleOptions"-->
|
||
<!-- :key="item.ruleId"-->
|
||
<!-- :label="item.name"-->
|
||
<!-- :value="item.ruleId"-->
|
||
<!-- />-->
|
||
<!-- </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" :lazy="true">
|
||
<order v-if="room.roomId != null"
|
||
:query="{
|
||
roomId: room.roomId,
|
||
storeId: Number(room.storeId),
|
||
merchantId: room.merchantId
|
||
}" />
|
||
</el-tab-pane>
|
||
<el-tab-pane label="套餐列表" name="rules" :lazy="true">
|
||
<rule :query="{
|
||
roomId: room.roomId,
|
||
storeId: Number(room.storeId),
|
||
merchantId: room.merchantId
|
||
}" />
|
||
</el-tab-pane>
|
||
<el-tab-pane v-if="!isEquipment" label="设施列表" name="equipments" :lazy="true">
|
||
<equipment :query="{
|
||
roomId: room.roomId,
|
||
storeId: Number(room.storeId),
|
||
merchantId: room.merchantId
|
||
}" />
|
||
</el-tab-pane>
|
||
<el-tab-pane label="设备列表" name="devices" :lazy="true">
|
||
<device :query="{
|
||
roomId: room.roomId,
|
||
storeId: Number(room.storeId),
|
||
userId: room.merchantId
|
||
}" />
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<!-- 套餐选择弹窗 -->
|
||
<el-dialog
|
||
title="选择套餐"
|
||
:visible.sync="ruleDialogVisible"
|
||
width="800px"
|
||
append-to-body
|
||
@opened="handleRuleDialogOpen"
|
||
>
|
||
<div class="dialog-content">
|
||
<el-form :model="ruleQuery" ref="ruleQuery" :inline="true" class="search-form">
|
||
<el-form-item label="套餐名称" prop="explain">
|
||
<el-input v-model="ruleQuery.explain" placeholder="请输入套餐名称" clearable size="small" @change="handleRuleQuery" />
|
||
</el-form-item>
|
||
<el-form-item label="收费模式" prop="mode">
|
||
<el-select v-model="ruleQuery.mode" placeholder="请选择收费模式" @change="handleRuleQuery" clearable size="small">
|
||
<el-option
|
||
v-for="dict in dict.type.ss_fee_rule_mode"
|
||
:key="dict.value"
|
||
:label="dict.label"
|
||
:value="dict.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleRuleQuery">搜索</el-button>
|
||
<el-button icon="el-icon-refresh" size="mini" @click="resetRuleQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<el-table
|
||
ref="ruleTable"
|
||
v-loading="ruleLoading"
|
||
:data="ruleList"
|
||
height="400"
|
||
@selection-change="handleRuleSelectionChange"
|
||
highlight-current-row
|
||
style="width: 100%"
|
||
>
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="套餐名称" prop="explain" />
|
||
<el-table-column label="收费模式" prop="mode" align="center">
|
||
<template slot-scope="{row}">
|
||
<dict-tag :options="dict.type.ss_fee_rule_mode" :value="row.mode" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="小时" prop="hours" align="center">
|
||
<template slot-scope="{row}">
|
||
{{ row.hours !== null && row.hours !== undefined ? row.hours + ' 小时' : '--' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="单价" prop="price" align="center">
|
||
<template slot-scope="{row}">
|
||
{{ row.price !== null && row.price !== undefined ? row.price + ' 元' : '--' }}
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination
|
||
v-show="ruleTotal > 0"
|
||
:total="ruleTotal"
|
||
:page.sync="ruleQuery.pageNum"
|
||
:limit.sync="ruleQuery.pageSize"
|
||
@pagination="loadRuleList"
|
||
/>
|
||
</div>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button type="primary" @click="confirmSelectRule" :loading="bandLoading">确 定</el-button>
|
||
<el-button @click="ruleDialogVisible = false">取 消</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import {bandRules, getRoom, updateRoom, bandRule, openRoomGate, switchAllDevices} 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'
|
||
import { listRule } from "@/api/system/rule"
|
||
import room from "@/views/system/room/index.vue";
|
||
import QrCode from '@/components/QrCode'
|
||
import { getDomain } from "@/api/common/common";
|
||
import Log from "@/views/system/commandLog/index.vue";
|
||
import { deviceSwitch } from "@/api/system/device";
|
||
import {generateCode} from "@/utils/ruoyi";
|
||
|
||
export default {
|
||
name: 'RoomDetail',
|
||
dicts: ['ss_room_type', 'ss_room_type2', 'ss_room_tags','ss_fee_rule_mode','ss_room_status'],
|
||
components: {Log, room, order, device, rule, equipment, QrCode },
|
||
data() {
|
||
return {
|
||
domain: '', // 域名
|
||
// 是否显示弹出层
|
||
open: false,
|
||
// 弹出层标题
|
||
title: "",
|
||
room: {
|
||
feeRules: []
|
||
},
|
||
// 标签字典
|
||
roomTagOptions: [],
|
||
// 店铺选项
|
||
storeOptions: [],
|
||
ruleOptions: [],
|
||
// 表单参数
|
||
form: {
|
||
roomId: undefined,
|
||
roomName: undefined,
|
||
storeId: undefined,
|
||
type: undefined,
|
||
picture: undefined,
|
||
pictures: [],
|
||
tags: []
|
||
},
|
||
// 表单校验
|
||
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',
|
||
// 套餐选择相关
|
||
ruleDialogVisible: false,
|
||
ruleLoading: false,
|
||
bandLoading: false,
|
||
ruleList: [],
|
||
ruleTotal: 0,
|
||
selectedRules: [],
|
||
ruleQuery: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
merchantId: null,
|
||
explain: undefined,
|
||
mode: undefined
|
||
},
|
||
}
|
||
},
|
||
computed: {
|
||
isEquipment() {
|
||
return this.$route.query.type === 'equipment';
|
||
}
|
||
},
|
||
created() {
|
||
this.handleRuleQuery()
|
||
this.getDetail()
|
||
this.getDictData()
|
||
this.getStoreOptions()
|
||
this.getDomain2()
|
||
},
|
||
|
||
methods: {
|
||
generateAndSetCode(type) {
|
||
this.form.code = generateCode(type);
|
||
},
|
||
handleRuleQuery() {
|
||
this.ruleQuery.pageNum = 1;
|
||
this.loadRuleList();
|
||
},
|
||
/** 获取房间详情 */
|
||
async getDetail() {
|
||
try {
|
||
const roomId = this.$route.params.roomId
|
||
const response = await getRoom(roomId)
|
||
this.room = response.data
|
||
console.log('房间详情数据:', this.room)
|
||
// 确保 billingMode 是数组
|
||
if (!Array.isArray(this.room.billingMode)) {
|
||
this.room.billingMode = this.room.billingMode ? [this.room.billingMode] : ['1'];
|
||
}
|
||
|
||
// 处理图片回显
|
||
if (Array.isArray(this.room.pictures) && this.room.pictures.length > 0) {
|
||
this.room.picture = this.room.pictures.join(',');
|
||
} else {
|
||
this.room.picture = '';
|
||
this.room.pictures = [];
|
||
}
|
||
// this.handleRuleQuery();
|
||
//
|
||
// // 获取套餐列表并设置选中状态
|
||
// listRule({ pageSize: 999, merchantId: this.room.merchantId }).then(response => {
|
||
// const ruleList = response.rows;
|
||
// // console.log('获取到的套餐列表:', ruleList)
|
||
// // console.log('房间已选套餐IDs:', this.room.ruleIds)
|
||
// // 根据 ruleIds 找到对应的完整套餐数据
|
||
// this.selectedRules = ruleList.filter(rule =>
|
||
// this.room.ruleIds && this.room.ruleIds.includes(rule.ruleId)
|
||
// );
|
||
// console.log('初始化选中的套餐:', this.selectedRules)
|
||
// if (this.selectedRules) {
|
||
// this.selectedRules.forEach(row => {
|
||
// this.$refs.ruleTable.toggleRowSelection(row,true);
|
||
// });
|
||
// }
|
||
// });
|
||
} catch (error) {
|
||
console.error('获取房间/设施详情失败:', error)
|
||
this.$message.error('获取房间/设施详情失败')
|
||
}
|
||
},
|
||
|
||
/** 获取字典数据 */
|
||
async getDictData() {
|
||
try {
|
||
const [typeRes, type2Res, tagRes] = await Promise.all([
|
||
getDicts('ss_room_type'),
|
||
getDicts('ss_room_type2'),
|
||
getDicts('ss_room_tags')
|
||
])
|
||
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;
|
||
});
|
||
},
|
||
|
||
loadRuleOptions() {
|
||
listRule({ pageSize: 999 ,roomId: this.room.roomId}).then(response => {
|
||
this.ruleOptions = response.rows;
|
||
});
|
||
},
|
||
|
||
/** 获取标签名称 */
|
||
getTagName(tag) {
|
||
const found = this.roomTagOptions.find(item => item.dictValue === tag)
|
||
return found ? found.dictLabel : tag
|
||
},
|
||
|
||
/** 修改按钮操作 */
|
||
handleUpdate() {
|
||
this.open = true;
|
||
this.title = "修改" + (this.isEquipment ? "设施" : "房间");
|
||
this.form = {
|
||
...this.room,
|
||
pictures: this.room.pictures || []
|
||
};
|
||
},
|
||
|
||
// 取消按钮
|
||
cancel() {
|
||
this.open = false;
|
||
this.reset();
|
||
},
|
||
|
||
// 表单重置
|
||
reset() {
|
||
this.form = {
|
||
roomId: undefined,
|
||
roomName: undefined,
|
||
storeId: undefined,
|
||
type: undefined,
|
||
picture: undefined,
|
||
pictures: [],
|
||
tags: []
|
||
};
|
||
this.resetForm("form");
|
||
},
|
||
|
||
resetRuleQuery() {
|
||
this.ruleQuery = {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
merchantId: this.room.merchantId,
|
||
explain: undefined,
|
||
mode: undefined
|
||
};
|
||
this.loadRuleList();
|
||
},
|
||
|
||
/** 表单提交 */
|
||
submitForm() {
|
||
this.$refs["form"].validate(valid => {
|
||
if (valid) {
|
||
// 如果是设施且修改了开锁方式,先发送开关命令
|
||
if (this.isEquipment &&
|
||
this.form.deviceId &&
|
||
this.form.unlockMode !== this.room.unlockMode) {
|
||
// 通电开锁(1)时发送断电命令(false),断电开锁(2)时发送通电命令(true)
|
||
const open = this.form.unlockMode === "2";
|
||
deviceSwitch({
|
||
deviceId: this.form.deviceId,
|
||
open: open
|
||
}).then(() => {
|
||
// 开关命令成功后,保存配置
|
||
this.saveForm();
|
||
}).catch(() => {
|
||
this.$modal.msgError("设备开关操作失败");
|
||
});
|
||
} else {
|
||
// 不需要发送开关命令,直接保存
|
||
this.saveForm();
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
/** 保存表单 */
|
||
saveForm() {
|
||
let data = {
|
||
...this.form
|
||
};
|
||
updateRoom(data).then(response => {
|
||
this.$modal.msgSuccess("修改成功");
|
||
this.open = false;
|
||
this.getDetail();
|
||
});
|
||
},
|
||
|
||
/** 获取状态对应的类型 */
|
||
getStatusType(status) {
|
||
const statusMap = {
|
||
'1': 'success', // 空闲中
|
||
'2': 'warning', // 打扫中
|
||
'3': 'info' // 离线
|
||
};
|
||
return statusMap[status] || 'info';
|
||
},
|
||
|
||
/** 获取状态文本 */
|
||
getStatusText(status) {
|
||
const statusMap = {
|
||
'1': '空闲中',
|
||
'2': '未打扫',
|
||
'3': '使用中',
|
||
'4': '打扫中',
|
||
'8': '下架'
|
||
};
|
||
return statusMap[status] || '未知状态';
|
||
},
|
||
|
||
/** 打开套餐选择弹窗 */
|
||
handleBandRule: function () {
|
||
this.ruleDialogVisible = true;
|
||
// 等待弹窗和表格渲染完成后再设置选中状态
|
||
this.$nextTick(() => {
|
||
// 获取套餐列表
|
||
listRule({ pageSize: 999, mode: 2, merchantId: this.room.merchantId }).then(response => {
|
||
this.ruleList = response.rows;
|
||
// 找到需要选中的行
|
||
const selectedRules = this.ruleList.filter(rule =>
|
||
this.room.ruleIds && this.room.ruleIds.includes(rule.ruleId)
|
||
);
|
||
// 确保表格组件已经挂载
|
||
if (this.$refs.ruleTable) {
|
||
this.toggleSelection(selectedRules);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
|
||
/** 加载套餐列表 */
|
||
loadRuleList() {
|
||
if(this.ruleQuery.merchantId){
|
||
this.ruleLoading = true;
|
||
listRule(this.ruleQuery).then(response => {
|
||
this.ruleList = response.rows;
|
||
this.ruleTotal = response.total;
|
||
this.ruleLoading = false;
|
||
}).catch(() => {
|
||
this.ruleLoading = false;
|
||
});
|
||
}
|
||
},
|
||
|
||
/** 选择套餐 */
|
||
handleRuleSelect(row) {
|
||
this.selectedRules = [row];
|
||
},
|
||
|
||
/** 确认应用套餐 */
|
||
confirmBandRule() {
|
||
if (this.selectedRules.length === 0) {
|
||
this.$modal.msgError("请选择要应用的套餐");
|
||
return;
|
||
}
|
||
|
||
this.bandLoading = true;
|
||
bandRule({
|
||
ruleIds: this.selectedRules.map(rule => rule.ruleId),
|
||
roomId: this.room.roomId
|
||
}).then(() => {
|
||
this.$modal.msgSuccess("套餐应用成功");
|
||
this.ruleDialogVisible = false;
|
||
this.getDetail(); // 刷新详情
|
||
}).finally(() => {
|
||
this.bandLoading = false;
|
||
});
|
||
},
|
||
|
||
handleRuleDialogOpen() {
|
||
this.ruleQuery.merchantId = this.room.merchantId;
|
||
this.loadRuleList();
|
||
},
|
||
|
||
toggleSelection(rows) {
|
||
// 确保表格组件存在
|
||
if (!this.$refs.ruleTable) {
|
||
console.warn('表格组件未初始化');
|
||
return;
|
||
}
|
||
// 先清除所有选择
|
||
this.$refs.ruleTable.clearSelection();
|
||
// 如果有需要选中的行,则选中它们
|
||
if (rows && rows.length > 0) {
|
||
rows.forEach(row => {
|
||
this.$refs.ruleTable.toggleRowSelection(row, true);
|
||
});
|
||
}
|
||
},
|
||
|
||
handleRuleSelectionChange(selection) {
|
||
this.selectedRules = selection;
|
||
},
|
||
|
||
confirmSelectRule() {
|
||
if (this.selectedRules.length === 0) {
|
||
this.$message.warning('请选择套餐');
|
||
return;
|
||
}
|
||
|
||
console.log('确认选择的套餐:', this.selectedRules)
|
||
this.bandLoading = true;
|
||
bandRules({
|
||
ruleIds: this.selectedRules.map(rule => rule.ruleId),
|
||
roomId: this.room.roomId
|
||
}).then(() => {
|
||
this.$modal.msgSuccess("套餐应用成功");
|
||
this.ruleDialogVisible = false;
|
||
this.getDetail(); // 刷新详情
|
||
}).finally(() => {
|
||
this.bandLoading = false;
|
||
});
|
||
},
|
||
|
||
// 获取域名
|
||
getDomain2() {
|
||
getDomain().then(response => {
|
||
if (response.data) {
|
||
this.domain = response.data;
|
||
}
|
||
}).catch(() => {
|
||
this.$message.error("获取全局域名失败")
|
||
})
|
||
},
|
||
|
||
// 获取二维码文本
|
||
getCodeText(room) {
|
||
let url = this.domain + `?f=` + room.qrText;
|
||
return this.domain ? url : '';
|
||
},
|
||
|
||
/** 开门按钮操作 */
|
||
handleOpenDoor() {
|
||
this.$modal.confirm('是否确认为"' + this.room.roomName + '"开门?').then(() => {
|
||
return openRoomGate(this.room.roomId);
|
||
}).then(() => {
|
||
this.$modal.msgSuccess("开门成功");
|
||
}).catch(() => {});
|
||
},
|
||
|
||
/** 全开/全关按钮操作 */
|
||
handleSwitchAll(open) {
|
||
const actionText = open ? '打开' : '关闭';
|
||
this.$modal.confirm(`是否确认${actionText}房间"${this.room.roomName}"的所有设备?`).then(() => {
|
||
return switchAllDevices(this.room.roomId, open);
|
||
}).then(() => {
|
||
this.$modal.msgSuccess(`${actionText}成功`);
|
||
}).catch(() => {});
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.app-container {
|
||
padding: 24px;
|
||
background-color: #f5f7fa;
|
||
min-height: calc(100vh - 84px);
|
||
}
|
||
|
||
.operation-buttons {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 5px;
|
||
justify-content: flex-end;
|
||
margin-bottom: 20px;
|
||
text-align: right;
|
||
.el-button {
|
||
margin: 0;
|
||
padding: 10px 10px;
|
||
}
|
||
}
|
||
|
||
.box-card {
|
||
border-radius: 12px;
|
||
padding: 0px 24px;
|
||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||
|
||
&:hover {
|
||
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.1);
|
||
}
|
||
}
|
||
|
||
.room-header {
|
||
margin-bottom: -40px;
|
||
}
|
||
|
||
.image-wrapper {
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
transition: all 0.3s ease;
|
||
|
||
:deep(.el-carousel) {
|
||
.el-carousel__indicators {
|
||
bottom: -20px;
|
||
}
|
||
|
||
.el-carousel__arrow {
|
||
background-color: rgba(0, 0, 0, 0.3);
|
||
|
||
&:hover {
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
}
|
||
}
|
||
|
||
.el-carousel__item {
|
||
background-color: #f5f7fa;
|
||
overflow: hidden;
|
||
|
||
.carousel-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
|
||
:deep(.el-image__inner) {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.image-slot {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #f5f7fa;
|
||
color: #909399;
|
||
font-size: 30px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
&:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
|
||
}
|
||
}
|
||
|
||
.room-title {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 28px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #EBEEF5;
|
||
|
||
h2 {
|
||
margin: 0;
|
||
margin-right: 15px;
|
||
font-size: 28px;
|
||
color: #303133;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.status-tag {
|
||
padding: 0 12px;
|
||
height: 24px;
|
||
line-height: 24px;
|
||
font-size: 12px;
|
||
}
|
||
}
|
||
|
||
.room-info {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.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;
|
||
|
||
h3 {
|
||
margin: 0;
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
i {
|
||
margin-right: 12px;
|
||
font-size: 24px;
|
||
color: #409EFF;
|
||
}
|
||
}
|
||
}
|
||
|
||
.price {
|
||
color: #F56C6C;
|
||
font-weight: 600;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.unit {
|
||
color: #909399;
|
||
margin-left: 4px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
:deep(.el-descriptions) {
|
||
padding: 20px;
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||
|
||
.el-descriptions__label {
|
||
font-weight: 500;
|
||
color: #606266;
|
||
min-width: 90px;
|
||
}
|
||
|
||
.el-descriptions__content {
|
||
padding: 16px;
|
||
}
|
||
|
||
.el-descriptions__body {
|
||
background-color: #fff;
|
||
}
|
||
|
||
.el-descriptions-item__label {
|
||
background-color: #f5f7fa;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.el-descriptions-item__content {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.detail-tabs {
|
||
margin-top: 20px;
|
||
|
||
.search-form {
|
||
margin-bottom: 20px;
|
||
|
||
.el-form-item {
|
||
margin-bottom: 10px;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 标签选择器样式
|
||
.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%;
|
||
}
|
||
}
|
||
|
||
.type-tags {
|
||
display: flex;
|
||
gap: 5px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.tag-item {
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.el-descriptions-item {
|
||
.el-tag {
|
||
margin: 2px;
|
||
}
|
||
}
|
||
|
||
.link-type {
|
||
color: #11A983;
|
||
text-decoration: none;
|
||
|
||
&:hover {
|
||
color: #11A98330;
|
||
text-decoration: underline;
|
||
}
|
||
}
|
||
|
||
.dialog-content {
|
||
padding: 0 20px;
|
||
|
||
.el-table {
|
||
margin: 15px 0;
|
||
|
||
.el-table__row {
|
||
cursor: pointer;
|
||
|
||
&.current-row {
|
||
background-color: #f0f9eb;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.tags-container {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
|
||
:deep(.el-tag) {
|
||
margin: 0;
|
||
height: 22px;
|
||
line-height: 20px;
|
||
padding: 0 8px;
|
||
}
|
||
}
|
||
|
||
.qr-code-box {
|
||
text-align: center;
|
||
padding: 10px;
|
||
|
||
p {
|
||
margin: 10px 0 0;
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
/* 紧凑布局 */
|
||
.compact-descriptions {
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.compact-descriptions ::v-deep .el-descriptions-item__label {
|
||
text-align: right;
|
||
padding-right: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.compact-descriptions ::v-deep .el-descriptions-item__content {
|
||
text-align: left;
|
||
padding-left: 12px;
|
||
}
|
||
.red{
|
||
color: red;
|
||
font-weight: bold;
|
||
}
|
||
</style>
|