share-space-vue/src/views/system/store/store_detail.vue

992 lines
26 KiB
Vue
Raw Normal View History

2025-01-06 20:59:31 +08:00
<template>
<div class="store-detail">
2025-01-07 17:45:22 +08:00
<!-- 基本信息卡片 -->
<el-card class="info-card" shadow="hover">
<div slot="header" class="card-header">
<div class="header-left">
<span class="title">店铺详情</span>
2025-01-16 18:02:37 +08:00
<el-tag size="small" :type="getStatusType(storeData.status)">
{{ getStatusText(storeData.status) }}
2025-01-07 17:45:22 +08:00
</el-tag>
</div>
<div class="action-buttons">
<el-button type="danger" size="small" @click="handleDelete">删除</el-button>
</div>
</div>
<el-row :gutter="20">
2025-01-10 17:51:42 +08:00
<el-col :span="6">
2025-01-07 17:45:22 +08:00
<div class="store-image">
2025-01-16 18:02:37 +08:00
<el-carousel height="200px" indicator-position="outside" :autoplay="true" trigger="click" arrow="always">
2025-01-10 17:51:42 +08:00
<el-carousel-item v-for="(url, index) in storeData.pictures" :key="index">
2025-01-16 18:02:37 +08:00
<el-image
:src="url"
2025-01-10 17:51:42 +08:00
fit="fill"
style="width: 100%; height: 100%"
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</el-carousel-item>
</el-carousel>
2025-01-07 17:45:22 +08:00
</div>
</el-col>
2025-01-16 18:02:37 +08:00
<el-col :span="18">
<div class="store-title">
<h2>{{ storeData.name }}</h2>
<!-- <el-tag :type="getStatusType(storeData.status)" class="status-tag" effect="dark">
<dict-tag :options="dict.type.ss_store_status" :value="storeData.status" />
</el-tag> -->
2025-01-07 17:45:22 +08:00
</div>
2025-01-16 18:02:37 +08:00
<div class="info-content">
<el-descriptions :column="3" border size="medium">
<el-descriptions-item label="联系人">
<span class="info-text">{{ storeData.contactName || '--' }}</span>
</el-descriptions-item>
<el-descriptions-item label="联系电话">
<span class="info-text">{{ storeData.contactMobile || '--' }}</span>
</el-descriptions-item>
2025-01-21 16:56:57 +08:00
<el-descriptions-item label="二维码">
<el-popover placement="top" trigger="hover">
<div class="qr-code-box">
<qr-code :text="getCodeText(storeData)" :width="150" :height="150" />
<p>扫描二维码进入店铺</p>
</div>
<el-button slot="reference" type="text" icon="el-icon-picture">查看</el-button>
</el-popover>
</el-descriptions-item>
2025-01-21 09:48:54 +08:00
<el-descriptions-item label="商户" class="device-detail2">
<router-link
v-if="storeData.merchantId"
:to="'/user/detail/' + storeData.merchantId"
class="link-type">
<span >{{ storeData.merchantName || '--' }}</span>
</router-link>
2025-01-16 18:02:37 +08:00
</el-descriptions-item>
<el-descriptions-item label="客服电话">
<span class="info-text">{{ storeData.serverPhone || '--' }}</span>
</el-descriptions-item>
<el-descriptions-item label="营业时间">
<span class="info-text">{{ formatBusinessTime(storeData.businessTimeStart, storeData.businessTimeEnd) }}</span>
</el-descriptions-item>
<el-descriptions-item label="身份证号">
<span class="info-text">{{ storeData.idcard || '--' }}</span>
</el-descriptions-item>
2025-01-21 09:48:54 +08:00
<el-descriptions-item label="大门" >
2025-01-18 09:35:06 +08:00
<router-link
v-if="storeData.gateSn"
:to="`/system/deviceDetail/index/${storeData.gateId}`"
class="link-type"
>
{{ storeData.gateSn }}
</router-link>
2025-01-21 09:48:54 +08:00
<el-tag
v-if="storeData.gateSn"
type="text"
size="mini"
effect="dark"
@click="handleUnbindGate"
style="cursor: pointer; margin-left: 10px;">解绑
</el-tag>
2025-01-18 09:35:06 +08:00
<el-button v-else type="primary" @click="showBindGateDialog = true">去绑定</el-button>
</el-descriptions-item>
2025-01-21 16:56:57 +08:00
<el-descriptions-item label="地址" :span="3">
<span class="info-text">{{ storeData.address || '--' }}</span>
</el-descriptions-item>
<el-descriptions-item label="标签" :span="2">
<dict-tag :options="dict.type.ss_store_tags" :value="storeData.tags" />
</el-descriptions-item>
<el-descriptions-item label="类型">
<dict-tag :options="dict.type.ss_room_type" :value="storeData.typeTags" />
</el-descriptions-item>
2025-01-16 18:02:37 +08:00
</el-descriptions>
2025-01-07 17:45:22 +08:00
</div>
</el-col>
</el-row>
</el-card>
<!-- 顶部统计卡片 -->
<el-row :gutter="20" class="stat-cards">
2025-01-16 18:02:37 +08:00
<!-- 今日营收 -->
2025-01-07 17:45:22 +08:00
<el-col :span="4">
<el-card shadow="hover" class="stat-card">
<div class="stat-icon income">
<i class="el-icon-money"></i>
</div>
<div class="stat-content">
2025-01-16 18:02:37 +08:00
<div class="stat-label">今日营收</div>
<div class="stat-value">¥ {{ storeData.todayIncome || '0.00' }}</div>
2025-01-07 17:45:22 +08:00
</div>
</el-card>
</el-col>
2025-01-16 18:02:37 +08:00
<!-- 本月营收 -->
2025-01-07 17:45:22 +08:00
<el-col :span="4">
<el-card shadow="hover" class="stat-card">
<div class="stat-icon income-month">
<i class="el-icon-wallet"></i>
</div>
<div class="stat-content">
2025-01-16 18:02:37 +08:00
<div class="stat-label">本月营收</div>
<div class="stat-value">¥ {{ storeData.monthIncome || '0.00' }}</div>
2025-01-07 17:45:22 +08:00
</div>
</el-card>
</el-col>
<!-- 总营收 -->
<el-col :span="4">
<el-card shadow="hover" class="stat-card">
<div class="stat-icon total-income">
<i class="el-icon-bank-card"></i>
</div>
<div class="stat-content">
<div class="stat-label">总营收</div>
2025-01-16 18:02:37 +08:00
<div class="stat-value">¥ {{ storeData.totalIncome || '0.00' }}</div>
2025-01-07 17:45:22 +08:00
</div>
</el-card>
</el-col>
<!-- 房间统计 -->
<el-col :span="4">
<el-card shadow="hover" class="stat-card">
<div class="stat-icon room">
<i class="el-icon-office-building"></i>
</div>
<div class="stat-content">
<div class="stat-label">房间使用</div>
<div class="stat-numbers">
2025-01-16 18:02:37 +08:00
<span class="current">{{ storeData.usingRoomNum || 0 }}</span>
2025-01-07 17:45:22 +08:00
<span class="divider">/</span>
2025-01-16 18:02:37 +08:00
<span class="total">{{ storeData.totalRoomNum || 0 }}</span>
2025-01-07 17:45:22 +08:00
</div>
</div>
</el-card>
</el-col>
<!-- 设施统计 -->
<el-col :span="4">
<el-card shadow="hover" class="stat-card">
<div class="stat-icon facility">
2025-01-16 18:02:37 +08:00
<i class="el-icon-s-platform"></i>
2025-01-07 17:45:22 +08:00
</div>
<div class="stat-content">
<div class="stat-label">设施使用</div>
<div class="stat-numbers">
2025-01-16 18:02:37 +08:00
<span class="current">{{ storeData.usingFacilityNum || 0 }}</span>
2025-01-07 17:45:22 +08:00
<span class="divider">/</span>
2025-01-16 18:02:37 +08:00
<span class="total">{{ storeData.totalFacilities || 0 }}</span>
</div>
</div>
</el-card>
</el-col>
<!-- 设备数量 -->
<el-col :span="4">
<el-card shadow="hover" class="stat-card">
<div class="stat-icon device">
<i class="el-icon-cpu"></i>
</div>
<div class="stat-content">
<div class="stat-label">设备数量</div>
<div class="stat-numbers">
<span class="total">{{ storeData.totalDeviceNum || 0 }}</span>
2025-01-07 17:45:22 +08:00
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表和地图区域 -->
<el-row :gutter="20" class="chart-section">
<el-col :span="16">
<el-card class="chart-card" shadow="hover">
2025-01-06 20:59:31 +08:00
<div slot="header" class="card-header">
2025-01-16 18:02:37 +08:00
<span class="title">营收趋势</span>
2025-01-07 17:45:22 +08:00
<el-radio-group v-model="chartTimeRange" size="small" @change="handleChartRangeChange">
<el-radio-button label="week">近7天</el-radio-button>
<el-radio-button label="month">近30天</el-radio-button>
</el-radio-group>
2025-01-06 20:59:31 +08:00
</div>
2025-01-07 17:45:22 +08:00
<div class="chart-container" ref="incomeChart"></div>
2025-01-06 20:59:31 +08:00
</el-card>
2025-01-07 17:45:22 +08:00
</el-col>
2025-01-06 20:59:31 +08:00
2025-01-07 17:45:22 +08:00
<el-col :span="8">
<el-card class="map-card" shadow="hover">
2025-01-06 20:59:31 +08:00
<div slot="header" class="card-header">
2025-01-07 17:45:22 +08:00
<span class="title">店铺位置</span>
2025-01-06 20:59:31 +08:00
</div>
2025-01-07 17:45:22 +08:00
<div class="map-container" id="mapContainer"></div>
2025-01-06 20:59:31 +08:00
</el-card>
</el-col>
</el-row>
2025-01-10 17:51:42 +08:00
<el-tabs v-model="activeTab" class="detail-tabs">
2025-01-07 17:45:22 +08:00
<el-tab-pane label="订单列表" name="orders">
2025-01-16 18:02:37 +08:00
<order :storeId="Number(storeId)"></order>
2025-01-07 17:45:22 +08:00
</el-tab-pane>
<el-tab-pane label="房间列表" name="rooms">
2025-01-21 09:48:54 +08:00
<room :storeId="Number(storeId)" :merchantId="storeData.merchantId"></room>
2025-01-07 17:45:22 +08:00
</el-tab-pane>
<el-tab-pane label="设施列表" name="equipments">
2025-01-21 09:48:54 +08:00
<equipment :storeId="Number(storeId)" :merchantId="storeData.merchantId" />
2025-01-07 17:45:22 +08:00
</el-tab-pane>
<el-tab-pane label="设备列表" name="devices">
2025-01-16 18:02:37 +08:00
<device :storeId="Number(storeId)"></device>
2025-01-07 17:45:22 +08:00
</el-tab-pane>
<el-tab-pane label="员工列表" name="users">
2025-01-16 18:02:37 +08:00
<user :storeId="Number(storeId)"></user>
</el-tab-pane>
<el-tab-pane label="卫生间列表" name="toilets">
<toilet :storeId="Number(storeId)"></toilet>
2025-01-07 17:45:22 +08:00
</el-tab-pane>
</el-tabs>
2025-01-18 09:35:06 +08:00
<BindGateDialog
:visible.sync="showBindGateDialog"
:storeName="storeData.storeName"
:storeId="Number(storeId)"
@bind-success="getStoreData"
/>
2025-01-06 20:59:31 +08:00
</div>
</template>
<script>
2025-01-21 09:48:54 +08:00
import {getStore, delStore, bindGateApi, unbindDevice} from "@/api/system/store";
2025-01-07 17:45:22 +08:00
import * as echarts from 'echarts';
import AMapLoader from "@amap/amap-jsapi-loader";
import globalConfig from '@/utils/config/globalConfig';
import order from '@/views/system/order/index.vue';
import room from '@/views/system/room/index.vue';
2025-01-21 09:48:54 +08:00
import equipment from '@/views/system/hallEqu/index.vue';
2025-01-07 17:45:22 +08:00
import device from '@/views/system/device/index.vue';
import user from '@/views/user/user/index.vue';
2025-01-16 18:02:37 +08:00
import toilet from '@/views/system/toilet/index.vue';
2025-01-18 09:35:06 +08:00
import BindGateDialog from './components/BindGateDialog.vue';
2025-01-21 16:56:57 +08:00
import QrCode from '@/components/QrCode';
import { getDomain } from "@/api/common/common";
2025-01-16 18:02:37 +08:00
2025-01-06 20:59:31 +08:00
export default {
name: 'StoreDetail',
2025-01-16 18:02:37 +08:00
dicts: ['ss_store_tags', 'ss_room_type'],
2025-01-07 17:45:22 +08:00
components: {
order,
room,
equipment,
device,
2025-01-16 18:02:37 +08:00
user,
2025-01-18 09:35:06 +08:00
toilet,
2025-01-21 16:56:57 +08:00
BindGateDialog,
QrCode
2025-01-06 20:59:31 +08:00
},
data() {
return {
2025-01-16 18:02:37 +08:00
storeId: Number(this.$route.params.id),
2025-01-06 20:59:31 +08:00
storeData: {},
map: null,
2025-01-07 17:45:22 +08:00
marker: null,
stats: {
todayIncome: 0,
monthIncome: 0,
totalIncome: 0,
totalWithdraw: 0,
2025-01-16 18:02:37 +08:00
totalRoomNum: 0,
usingRoomNum: 0,
2025-01-07 17:45:22 +08:00
totalFacilities: 0,
2025-01-16 18:02:37 +08:00
usingFacilityNum: 0
2025-01-07 17:45:22 +08:00
},
chartTimeRange: 'week',
incomeChart: null,
activeTab: 'orders',
2025-01-18 09:35:06 +08:00
showBindGateDialog: false,
2025-01-21 16:56:57 +08:00
equipmentList: [],
domain: ''
2025-01-06 20:59:31 +08:00
}
},
created() {
2025-01-07 17:45:22 +08:00
this.storeId = this.$route.params.storeId;
this.getStoreData();
2025-01-18 09:35:06 +08:00
this.loadEquipmentList();
2025-01-21 16:56:57 +08:00
this.getDicts();
this.getDomain();
2025-01-06 20:59:31 +08:00
},
mounted() {
this.$nextTick(() => {
this.initMap();
2025-01-07 17:45:22 +08:00
this.initChart();
2025-01-16 18:02:37 +08:00
if (this.$refs.incomeChart) {
this.$refs.incomeChart.addEventListener('scroll', this.handleScroll);
}
2025-01-06 20:59:31 +08:00
});
},
2025-01-07 17:45:22 +08:00
beforeDestroy() {
if (this.incomeChart) {
this.incomeChart.dispose();
}
if (this.map) {
2025-01-16 18:02:37 +08:00
this.map.clearMap();
2025-01-07 17:45:22 +08:00
this.map.destroy();
2025-01-16 18:02:37 +08:00
this.map = null;
2025-01-07 17:45:22 +08:00
}
window.removeEventListener('resize', this.resizeChart);
2025-01-16 18:02:37 +08:00
if (this.$refs.incomeChart) {
this.$refs.incomeChart.removeEventListener('scroll', this.handleScroll);
}
2025-01-07 17:45:22 +08:00
},
2025-01-06 20:59:31 +08:00
methods: {
2025-01-16 18:02:37 +08:00
handleScroll() {
if (!this.$refs.incomeChart || !this.incomeChart) return;
const scrollTop = this.$refs.incomeChart.scrollTop;
const scrollHeight = this.$refs.incomeChart.scrollHeight;
const clientHeight = this.$refs.incomeChart.clientHeight;
if (scrollTop + clientHeight >= scrollHeight) {
console.log('Reached bottom');
}
},
2025-01-07 17:45:22 +08:00
getStatusType(status) {
const statusMap = {
0: 'info',
1: 'success',
2: 'danger'
};
return statusMap[status] || 'info';
},
2025-01-16 18:02:37 +08:00
getStatusText(status) {
const statusMap = {
0: '停用',
1: '正常',
2: '禁用'
};
return statusMap[status] || '未知';
},
2025-01-07 17:45:22 +08:00
initChart() {
if (!this.$refs.incomeChart) return;
this.incomeChart = echarts.init(this.$refs.incomeChart);
const option = {
tooltip: {
trigger: 'axis',
formatter: '{b}<br />{a}: ¥{c}'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLine: {
lineStyle: {
color: '#DCDFE6'
}
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '¥{value}'
},
axisLine: {
show: false
},
splitLine: {
lineStyle: {
color: '#EBEEF5'
}
}
},
series: [{
2025-01-16 18:02:37 +08:00
name: '营收',
2025-01-07 17:45:22 +08:00
type: 'line',
smooth: true,
data: [820, 932, 901, 934, 1290, 1330, 1320],
itemStyle: {
color: '#409EFF'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(64,158,255,0.3)'
}, {
offset: 1,
color: 'rgba(64,158,255,0.1)'
}])
}
}]
};
this.incomeChart.setOption(option);
window.addEventListener('resize', this.resizeChart);
},
resizeChart() {
if (this.incomeChart) {
this.incomeChart.resize();
}
},
getStoreData() {
getStore(this.storeId).then(response => {
2025-01-06 20:59:31 +08:00
this.storeData = response.data;
2025-01-16 18:02:37 +08:00
// 重新初始化地图
if (this.map) {
this.map.clearMap();
this.map.destroy();
this.map = null;
}
2025-01-06 20:59:31 +08:00
this.$nextTick(() => {
2025-01-16 18:02:37 +08:00
this.initMap();
2025-01-06 20:59:31 +08:00
});
});
},
2025-01-07 17:45:22 +08:00
getStoreStats() {
// Mock数据实际项目中替换为API调用
2025-01-16 18:02:37 +08:00
// this.stats = {
// todayIncome: 1234.56,
// monthIncome: 45678.90,
// totalDeviceNum: 100,
// onlineDevices: 85
// };
2025-01-07 17:45:22 +08:00
},
handleChartRangeChange(range) {
// Mock数据实际项目中替换为API调用
const mockData = {
week: {
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
series: [820, 932, 901, 934, 1290, 1330, 1320]
},
month: {
xAxis: Array.from({ length: 30 }, (_, i) => `${i + 1}`),
series: Array.from({ length: 30 }, () => Math.floor(Math.random() * 2000 + 500))
}
};
const data = mockData[range];
this.incomeChart.setOption({
xAxis: { data: data.xAxis },
series: [{ data: data.series }]
});
},
2025-01-06 20:59:31 +08:00
formatBusinessTime(start, end) {
if (!start && !end) return '--';
return `${start || '--'} - ${end || '--'}`;
},
2025-01-07 17:45:22 +08:00
async initMap() {
try {
2025-01-16 18:02:37 +08:00
if (!window._AMapSecurityConfig) {
window._AMapSecurityConfig = {
securityJsCode: process.env.VUE_APP_AMAP_SECURITY_CODE,
}
}
const AMap = await AMapLoader.load({
2025-01-07 17:45:22 +08:00
key: globalConfig.aMap.key,
version: "2.0",
2025-01-16 18:02:37 +08:00
plugins: []
2025-01-07 17:45:22 +08:00
});
2025-01-16 18:02:37 +08:00
if (!document.getElementById('mapContainer')) {
console.error('地图容器不存在');
return;
}
this.map = new AMap.Map("mapContainer", {
2025-01-07 17:45:22 +08:00
zoom: 15,
2025-01-16 18:02:37 +08:00
center: [
parseFloat(this.storeData.lng) || 116.397428,
parseFloat(this.storeData.lat) || 39.90923
],
resizeEnable: true,
viewMode: '2D'
2025-01-07 17:45:22 +08:00
});
2025-01-06 20:59:31 +08:00
2025-01-16 18:02:37 +08:00
// 如果有经纬度,添加标记点
if (parseFloat(this.storeData.lng) && parseFloat(this.storeData.lat)) {
// 创建标记点
this.marker = new AMap.Marker({
position: [parseFloat(this.storeData.lng), parseFloat(this.storeData.lat)],
title: this.storeData.name
});
// 将标记点添加到地图
this.map.add(this.marker);
// 创建信息窗体
const infoWindow = new AMap.InfoWindow({
content: `
<div class="info-window">
<h4>${this.storeData.name || '未命名店铺'}</h4>
<p>${this.storeData.address || '暂无地址'}</p>
<p>经度${this.storeData.lng}</p>
<p>纬度${this.storeData.lat}</p>
</div>
`,
offset: new AMap.Pixel(0, -30)
});
// 点击标记点时打开信息窗体
this.marker.on('click', () => {
infoWindow.open(this.map, this.marker.getPosition());
});
// 自动适应标记点位置
this.map.setFitView();
2025-01-07 17:45:22 +08:00
}
} catch (e) {
2025-01-16 18:02:37 +08:00
console.error("地图加载失败", e);
2025-01-07 17:45:22 +08:00
}
2025-01-06 20:59:31 +08:00
},
2025-01-07 17:45:22 +08:00
handleEdit() {
this.$router.push(`/system/store/edit/${this.storeId}`);
},
handleDelete() {
this.$modal.confirm('是否确认删除该店铺?').then(() => {
return delStore(this.storeId);
}).then(() => {
this.$modal.msgSuccess("删除成功");
this.$router.push('/system/store/list');
}).catch(() => { });
2025-01-18 09:35:06 +08:00
},
bindGate() {
// 调用绑定大门的接口
bindGateApi().then(response => {
this.$modal.msgSuccess("绑定成功");
this.fetchStoreDetail(); // 重新获取店铺详情
}).catch(error => {
this.$modal.msgError("绑定失败");
});
},
loadEquipmentList() {
// 实现加载设施列表的逻辑
// 例如this.equipmentList = response.data;
2025-01-21 09:48:54 +08:00
},
handleUnbindGate() {
this.$modal.confirm('确定要解除该店铺的大门绑定吗?').then(() => {
// 调用解绑接口
unbindDevice(this.storeData.gateId).then(() => {
this.$modal.msgSuccess("解绑成功");
this.getStoreData(); // 重新获取店铺数据
}).catch(() => {
this.$modal.msgError("解绑失败");
});
}).catch(() => {});
2025-01-21 16:56:57 +08:00
},
getDomain() {
getDomain().then(response => {
if (response.data) {
this.domain = response.data;
}
}).catch(() => {
this.$message.error("获取全局域名失败")
})
},
getCodeText(store) {
let url = this.domain + `?storeId=` + store.qrText;
return this.domain ? url : '';
2025-01-06 20:59:31 +08:00
}
}
}
</script>
<style lang="scss" scoped>
.store-detail {
padding: 20px;
2025-01-16 18:02:37 +08:00
background-color: #f5f7fa;
min-height: calc(100vh - 84px);
.el-row {
height: 100%;
.el-col {
height: 100%;
}
}
2025-01-10 17:51:42 +08:00
.store-image {
2025-01-16 18:02:37 +08:00
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
height: 200px;
&:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
2025-01-10 17:51:42 +08:00
:deep(.el-carousel) {
.el-carousel__indicators {
bottom: -20px;
}
.el-carousel__arrow {
2025-01-16 18:02:37 +08:00
background-color: rgba(0, 0, 0, 0.3);
2025-01-10 17:51:42 +08:00
&:hover {
2025-01-16 18:02:37 +08:00
background-color: rgba(0, 0, 0, 0.5);
2025-01-10 17:51:42 +08:00
}
}
.el-carousel__item {
overflow: hidden;
2025-01-16 18:02:37 +08:00
2025-01-10 17:51:42 +08:00
.el-image {
width: 100%;
height: 100%;
display: block;
2025-01-16 18:02:37 +08:00
2025-01-10 17:51:42 +08:00
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #909399;
font-size: 30px;
}
}
}
}
}
2025-01-06 20:59:31 +08:00
2025-01-16 18:02:37 +08:00
.info-content {
flex: 1;
display: flex;
flex-direction: column;
height: calc(100% - 80px); // 减去标题的高度
:deep(.el-descriptions) {
height: 100%;
display: flex;
flex-direction: column;
.el-descriptions__body {
flex: 1;
display: flex;
flex-direction: column;
.el-descriptions-item {
flex: 1;
display: flex;
.el-descriptions-item__container {
flex: 1;
display: flex;
}
}
}
}
}
.store-title {
display: flex;
align-items: center;
padding-bottom: 16px;
height: 40px;
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: 12px;
}
}
: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;
}
}
.info-text {
color: #606266;
font-size: 14px;
}
2025-01-06 20:59:31 +08:00
.info-card {
margin-bottom: 20px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
2025-01-07 17:45:22 +08:00
.header-left {
display: flex;
align-items: center;
.title {
font-size: 16px;
font-weight: bold;
margin-right: 12px;
}
.status-tag {
margin-left: 8px;
}
}
2025-01-06 20:59:31 +08:00
.action-buttons {
.el-button {
margin-left: 10px;
}
}
}
.info-item {
2025-01-07 17:45:22 +08:00
margin-bottom: 16px;
2025-01-06 20:59:31 +08:00
display: flex;
align-items: center;
.label {
2025-01-07 17:45:22 +08:00
color: #909399;
margin-right: 12px;
min-width: 80px;
2025-01-06 20:59:31 +08:00
font-size: 14px;
}
.value {
2025-01-07 17:45:22 +08:00
color: #303133;
2025-01-06 20:59:31 +08:00
font-size: 14px;
}
}
}
2025-01-07 17:45:22 +08:00
.detail-tabs {
2025-01-06 20:59:31 +08:00
margin-top: 20px;
2025-01-07 17:45:22 +08:00
.search-form {
margin-bottom: 20px;
.el-form-item {
margin-bottom: 10px;
}
}
2025-01-06 20:59:31 +08:00
}
2025-01-07 17:45:22 +08:00
.stat-cards {
margin-bottom: 20px;
.stat-card {
height: 120px;
display: flex;
padding: 0 10px;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
.stat-icon {
width: 48px;
height: 48px;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
margin-right: 16px;
i {
font-size: 24px;
color: #fff;
}
&.income {
background: linear-gradient(135deg, #36d1dc, #5b86e5);
}
&.income-month {
background: linear-gradient(135deg, #ff9a9e, #fad0c4);
}
&.total-income {
background: linear-gradient(135deg, #a8edea, #fed6e3);
}
&.withdraw {
background: linear-gradient(135deg, #84fab0, #8fd3f4);
}
&.room {
background: linear-gradient(135deg, #fbc2eb, #a6c1ee);
}
&.facility {
background: linear-gradient(135deg, #f6d365, #fda085);
}
2025-01-16 18:02:37 +08:00
&.device {
background: linear-gradient(135deg, #84fab0, #8fd3f4);
}
2025-01-07 17:45:22 +08:00
}
.stat-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.stat-value {
font-size: 22px;
font-weight: bold;
color: #303133;
margin-bottom: 8px;
line-height: 1.2;
}
.stat-numbers {
font-size: 22px;
font-weight: bold;
color: #303133;
margin-bottom: 8px;
line-height: 1.2;
.current {
color: #409EFF;
}
.divider {
margin: 0 4px;
color: #909399;
}
.total {
color: #606266;
}
}
.stat-label {
font-size: 14px;
color: #909399;
}
}
}
}
.chart-section {
margin-bottom: 20px;
.card-header {
.title {
font-size: 16px;
font-weight: bold;
}
}
.chart-card {
.chart-container {
height: 350px;
padding: 10px;
}
}
.map-card {
.map-container {
height: 350px;
2025-01-16 18:02:37 +08:00
width: 100%;
position: relative;
border: 1px solid #EBEEF5;
border-radius: 4px;
overflow: hidden;
2025-01-07 17:45:22 +08:00
}
2025-01-06 20:59:31 +08:00
}
}
}
:deep(.info-window) {
2025-01-07 17:45:22 +08:00
padding: 12px;
2025-01-16 18:02:37 +08:00
min-width: 200px;
2025-01-06 20:59:31 +08:00
h4 {
2025-01-07 17:45:22 +08:00
margin: 0 0 8px;
color: #303133;
font-size: 16px;
2025-01-16 18:02:37 +08:00
font-weight: bold;
2025-01-06 20:59:31 +08:00
}
p {
2025-01-16 18:02:37 +08:00
margin: 4px 0;
2025-01-07 17:45:22 +08:00
color: #606266;
font-size: 14px;
2025-01-16 18:02:37 +08:00
line-height: 1.4;
2025-01-06 20:59:31 +08:00
}
}
:deep(.el-tag) {
2025-01-07 17:45:22 +08:00
margin-right: 8px;
2025-01-06 20:59:31 +08:00
}
2025-01-18 09:35:06 +08:00
.link-type {
color: #409EFF;
text-decoration: underline;
cursor: pointer;
}
.link-type:hover {
color: #66b1ff;
}
2025-01-21 09:48:54 +08:00
.device-detail2 {
padding: 20px;
.link-type {
color: #11A983;
text-decoration: none;
cursor: pointer;
}
.link-type:hover {
text-decoration: underline;
}
}
2025-01-21 16:56:57 +08:00
.qr-code-box {
text-align: center;
padding: 10px;
p {
margin: 10px 0 0;
color: #666;
font-size: 14px;
}
}
2025-01-16 18:02:37 +08:00
</style>