feat: 增加图表预览

This commit is contained in:
砂糖
2025-09-06 17:14:54 +08:00
parent 384e1a87c3
commit 75c15a1ec5
3 changed files with 223 additions and 158 deletions

View File

@@ -0,0 +1,212 @@
<!-- ChartLayoutPreview.vue -->
<template>
<div class="chart-layout-preview">
<!-- 预览标题 -->
<h3 class="preview-title">
布局预览同比缩放{{ scaleRatio * 100 }}%
</h3>
<!-- 预览容器通过transform实现同比缩小 -->
<div
class="preview-wrapper"
:style="{
transform: `scale(${scaleRatio})`,
transformOrigin: 'top left', // 缩放原点:左上角(保证布局对齐)
width: `${100 / scaleRatio}%`, // 反向计算宽度,避免缩放后内容溢出
height: `${100 / scaleRatio}%` // 反向计算高度配合overflow隐藏
}"
>
<!-- 实际预览内容与原逻辑一致用el-row+el-col实现布局 -->
<div class="preview-content">
<!-- 空状态提示 -->
<div v-if="!chartConfigs?.length" class="empty-preview">
<el-empty description="暂无图表配置,无法预览" />
</div>
<!-- 图表布局预览 -->
<el-row
v-else
:gutter="16"
class="chart-preview-row"
>
<el-col
v-for="(chart, index) in chartConfigs"
:key="chart.id || `chart-${index}`"
:xs="chart.layout.xs"
:sm="chart.layout.sm"
:md="chart.layout.md"
:lg="chart.layout.lg"
:xl="chart.layout.xl"
class="chart-preview-col"
>
<!-- 模拟图表卡片高度同步配置 -->
<el-card
class="chart-preview-card"
:style="{
height: `${chart.height}px`,
display: 'flex',
flexDirection: 'column',
transition: 'all 0.3s ease'
}"
>
<!-- 卡片头部图表名称+标识 -->
<div class="card-header">
<span class="chart-title">{{ chart.title }}</span>
<span class="chart-id">[{{ chart.id }}]</span>
</div>
<!-- 模拟图表内容区 -->
<div class="chart-placeholder" :style="{ flex: 1, marginTop: '8px' }">
<div class="placeholder-bg"></div>
<!-- 配置信息提示 -->
<div class="placeholder-info">
<p>高度{{ chart.height }}px</p>
<p>布局xs={{ chart.layout.xs }} / sm={{ chart.layout.sm }} / md={{ chart.layout.md }}</p>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<script setup>
import { ElEmpty, ElRow, ElCol, ElCard } from 'element-plus';
// 组件Props接收外部传递的配置数据和缩放比例
const props = defineProps({
/** 图表配置数组与父组件tempChartConfigs结构一致 */
chartConfigs: {
type: Array,
required: true,
default: () => []
},
/** 同比缩放比例0~1之间默认0.5即缩小至50% */
scaleRatio: {
type: Number,
required: false,
default: 0.5,
validator: (val) => val > 0 && val <= 1 // 限制缩放比例为0~1避免放大导致溢出
}
});
</script>
<style scoped>
/* 组件外层容器:控制整体大小和溢出 */
.chart-layout-preview {
width: 100%;
overflow: hidden;
border-radius: 8px;
padding: 12px;
}
/* 预览标题 */
.preview-title {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 500;
color: white;
}
/* 缩放容器:核心缩放逻辑载体 */
.preview-wrapper {
position: relative;
/* 初始宽度100%通过style绑定反向计算实际宽度抵消scale缩小效果 */
transform: scale(0.5);
transform-origin: top left;
transition: transform 0.3s ease;
}
/* 实际预览内容容器 */
.preview-content {
padding: 8px;
border: 1px solid #e5e7eb;
border-radius: 4px;
}
/* 空状态样式 */
.empty-preview {
height: 200px;
display: flex;
justify-content: center;
align-items: center;
}
/* 图表行清除el-row默认margin */
.chart-preview-row {
margin: 0 !important;
margin-bottom: 16px !important;
}
/* 图表列:控制间距 */
.chart-preview-col {
margin-bottom: 16px;
}
/* 图表卡片:模拟实际图表容器 */
.chart-preview-card {
overflow: hidden;
border: 1px solid #e5e7eb !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
/* 卡片头部:名称+标识 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
color: white;
border-bottom: 1px solid #e5e7eb;
}
.chart-title {
font-size: 14px;
font-weight: 500;
color: white;
}
.chart-id {
font-size: 12px;
color: white;
}
/* 模拟图表内容区 */
.chart-placeholder {
position: relative;
overflow: hidden;
}
.placeholder-bg {
width: 100%;
height: 100%;
}
/* 配置信息提示:底部半透明悬浮 */
.placeholder-info {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 6px 12px;
font-size: 12px;
color: white;
}
.placeholder-info p {
margin: 2px 0;
}
/* 响应式调整:小屏幕下优化缩放体验 */
@media (max-width: 768px) {
.chart-layout-preview {
padding: 8px;
}
.preview-title {
font-size: 14px;
}
}
</style>

View File

@@ -5,7 +5,6 @@
<h2 class="panel-title">图表布局设置</h2>
<div class="header-actions">
<el-button size="small" type="primary" @click="applySettings" :loading="saving">
<el-icon v-if="saving"><Loading /></el-icon>
<span>应用设置</span>
</el-button>
<el-button size="small" @click="resetToSession" :disabled="saving">
@@ -14,12 +13,11 @@
</div>
</div>
<!-- 配置内容区 -->
<!-- 配置内容区表格 + 独立预览组件 -->
<div class="settings-content">
<!-- 原有图表配置表格 -->
<div class="charts-config">
<h3 class="section-title">
<el-icon><Grid /></el-icon>
图表配置
</h3>
<el-table
@@ -28,7 +26,7 @@
:height="tableHeight"
row-key="id"
:data="tempChartConfigs">
<!-- 表格列内容保持不变 -->
<!-- 表格列内容保持不变与原代码一致 -->
<el-table-column prop="title" label="图表名称" min-width="120" />
<el-table-column prop="id" label="标识" min-width="100" />
<el-table-column label="图表高度" min-width="120">
@@ -140,60 +138,11 @@
</el-table>
</div>
<!-- 新增布局预览区域 -->
<div class="layout-preview">
<h3 class="section-title">
<el-icon><Eye /></el-icon>
布局预览实时同步配置
</h3>
<!-- 预览容器模拟仪表盘布局 -->
<div class="preview-container">
<!-- 无配置时显示提示 -->
<div v-if="tempChartConfigs.length === 0" class="empty-preview">
<el-empty description="暂无图表配置,无法预览" />
</div>
<!-- 有配置时用el-row+el-col渲染预览 -->
<el-row
v-else
:gutter="16"
class="preview-row"
:style="{ marginBottom: '16px' }">
<el-col
v-for="(chart, index) in tempChartConfigs"
:key="chart.id || index"
:xs="chart.layout.xs"
:sm="chart.layout.sm"
:md="chart.layout.md"
:lg="chart.layout.lg"
:xl="chart.layout.xl"
class="preview-col">
<!-- 用Card模拟图表容器高度同步配置 -->
<el-card
class="preview-card"
:style="{
height: `${chart.height}px`,
display: 'flex',
flexDirection: 'column',
transition: 'all 0.3s ease'
}">
<div class="card-header">
<span class="chart-title">{{ chart.title }}</span>
<span class="chart-id">[{{ chart.id }}]</span>
</div>
<!-- 模拟图表内容区域 -->
<div class="chart-placeholder" :style="{ flex: 1, marginTop: '8px' }">
<div class="placeholder-bg"></div>
<div class="placeholder-info">
<p>高度{{ chart.height }}px</p>
<p>布局xs={{ chart.layout.xs }} / sm={{ chart.layout.sm }} / md={{ chart.layout.md }}</p>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
<!-- 引入独立预览组件传递配置和缩放比例 -->
<ChartLayoutPreview
:chart-configs="tempChartConfigs"
:scale-ratio="0.5"
/>
</div>
<!-- 提示信息 -->
@@ -212,10 +161,10 @@ import { ref, onMounted, reactive } from 'vue';
import { useStorage } from '@vueuse/core';
import { useEventListener } from '@vueuse/core';
import { ElMessage, ElMessageBox, ElEmpty, ElRow, ElCol, ElCard } from 'element-plus';
// 新增导入预览用图标
import { Loading, Grid, Eye } from '@element-plus/icons-vue';
// 引入独立预览组件
import ChartLayoutPreview from './ChartLayoutPreview.vue';
// 原有逻辑保持不变,仅新增预览相关变量(如有需要
// 原有逻辑保持不变(与原代码一致
const props = defineProps({
storageKey: {
type: String,
@@ -259,7 +208,6 @@ const batchSettings = reactive({
layout: { xs: null, sm: null, md: null, lg: null, xl: null }
});
// 原有方法onMounted、calculateTableHeight、resetToSession等保持不变
useEventListener('resize', () => {
windowWidth.value = window.innerWidth;
calculateTableHeight();
@@ -403,7 +351,7 @@ defineExpose({ resetToSession, applySettings });
</script>
<style scoped>
/* 原有样式保持不变,新增预览区域样式 */
/* 原有样式保持不变(删除原预览相关样式,预览样式已封装在子组件中) */
.chart-settings-panel {
display: flex;
flex-direction: column;
@@ -489,92 +437,6 @@ defineExpose({ resetToSession, applySettings });
border-top: 1px solid #eee;
}
/* 新增:预览区域样式 */
.layout-preview {
flex: 1;
display: flex;
flex-direction: column;
padding: 12px;
border: 1px solid #eee;
border-radius: 8px;
background-color: #fafafa;
}
.preview-container {
flex: 1;
overflow: auto;
padding: 8px;
border-radius: 4px;
background-color: #fff;
}
.empty-preview {
height: 200px;
display: flex;
justify-content: center;
align-items: center;
}
.preview-row {
margin: 0 !important; /* 清除el-row默认margin */
}
.preview-col {
margin-bottom: 16px;
}
.preview-card {
overflow: hidden;
border: 1px solid #e5e7eb !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: #f9fafb;
border-bottom: 1px solid #e5e7eb;
}
.chart-title {
font-size: 14px;
font-weight: 500;
color: #333;
}
.chart-id {
font-size: 12px;
color: #666;
}
.chart-placeholder {
position: relative;
overflow: hidden;
}
.placeholder-bg {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #f0f2f5 0%, #e5e6eb 100%);
}
.placeholder-info {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 6px 12px;
background-color: rgba(255, 255, 255, 0.8);
font-size: 12px;
color: #666;
}
.placeholder-info p {
margin: 2px 0;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.chart-settings-panel {
@@ -607,9 +469,5 @@ defineExpose({ resetToSession, applySettings });
.section-title {
font-size: 14px;
}
.layout-preview {
padding: 8px;
}
}
</style>

View File

@@ -11,11 +11,6 @@
style="margin-bottom: 20px;"
/>
<!-- 页面标题补充基础样式 -->
<div style="font-size: 18px; font-weight: 500; margin-bottom: 20px;">
数据分析页面
</div>
<!-- 主内容区数据加载成功才显示 -->
<div v-if="!loading && !errorMsg">
<!-- 第一行四个指标卡 -->