✨ feat: 默认改为暗色模式
This commit is contained in:
@@ -16,22 +16,21 @@
|
||||
|
||||
<!-- 配置内容区 -->
|
||||
<div class="settings-content">
|
||||
<!-- 原有图表配置表格 -->
|
||||
<div class="charts-config">
|
||||
<h3 class="section-title">
|
||||
<el-icon><Grid /></el-icon>
|
||||
图表配置
|
||||
</h3>
|
||||
|
||||
<!-- 直接保留表格,移除draggable包裹 -->
|
||||
<el-table
|
||||
border
|
||||
size="small"
|
||||
: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">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
@@ -48,8 +47,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 布局配置列 -->
|
||||
<el-table-column label="超小屏 (≤768px)" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
@@ -57,14 +54,13 @@
|
||||
size="small"
|
||||
append-to="#full-dashboard-container"
|
||||
:disabled="saving">
|
||||
<el-option :value="24">独占一行</el-option>
|
||||
<el-option :value="12">半行宽度</el-option>
|
||||
<el-option :value="8">三分之一</el-option>
|
||||
<el-option :value="6">四分之一</el-option>
|
||||
<el-option value="24">独占一行</el-option>
|
||||
<el-option value="12">半行宽度</el-option>
|
||||
<el-option value="8">三分之一</el-option>
|
||||
<el-option value="6">四分之一</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="小屏 (≥768px)" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
@@ -72,14 +68,13 @@
|
||||
size="small"
|
||||
append-to="#full-dashboard-container"
|
||||
:disabled="saving">
|
||||
<el-option :value="24">独占一行</el-option>
|
||||
<el-option :value="12">半行宽度</el-option>
|
||||
<el-option :value="8">三分之一</el-option>
|
||||
<el-option :value="6">四分之一</el-option>
|
||||
<el-option value="24">独占一行</el-option>
|
||||
<el-option value="12">半行宽度</el-option>
|
||||
<el-option value="8">三分之一</el-option>
|
||||
<el-option value="6">四分之一</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="中屏 (≥992px)" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
@@ -87,14 +82,13 @@
|
||||
size="small"
|
||||
append-to="#full-dashboard-container"
|
||||
:disabled="saving">
|
||||
<el-option :value="24">独占一行</el-option>
|
||||
<el-option :value="12">半行宽度</el-option>
|
||||
<el-option :value="8">三分之一</el-option>
|
||||
<el-option :value="6">四分之一</el-option>
|
||||
<el-option value="24">独占一行</el-option>
|
||||
<el-option value="12">半行宽度</el-option>
|
||||
<el-option value="8">三分之一</el-option>
|
||||
<el-option value="6">四分之一</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="大屏 (≥1200px)" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
@@ -109,7 +103,6 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="超大屏 (≥1920px)" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
@@ -124,7 +117,6 @@
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
@@ -147,6 +139,61 @@
|
||||
</el-table-column>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
<!-- 提示信息 -->
|
||||
@@ -164,11 +211,11 @@
|
||||
import { ref, onMounted, reactive } from 'vue';
|
||||
import { useStorage } from '@vueuse/core';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Loading, ArrowUp, ArrowDown, Grid } from '@element-plus/icons-vue';
|
||||
// 移除draggable组件导入
|
||||
import { ElMessage, ElMessageBox, ElEmpty, ElRow, ElCol, ElCard } from 'element-plus';
|
||||
// 新增导入预览用图标
|
||||
import { Loading, Grid, Eye } from '@element-plus/icons-vue';
|
||||
|
||||
// 接收存储键名props
|
||||
// 原有逻辑保持不变,仅新增预览相关变量(如有需要)
|
||||
const props = defineProps({
|
||||
storageKey: {
|
||||
type: String,
|
||||
@@ -177,7 +224,6 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
// 从storage读取配置
|
||||
const persistedChartConfigs = useStorage(
|
||||
props.storageKey,
|
||||
[],
|
||||
@@ -186,12 +232,8 @@ const persistedChartConfigs = useStorage(
|
||||
serializer: {
|
||||
read: (v) => {
|
||||
try {
|
||||
if (typeof v === 'string') {
|
||||
return JSON.parse(v);
|
||||
}
|
||||
if (Array.isArray(v)) {
|
||||
return v;
|
||||
}
|
||||
if (typeof v === 'string') return JSON.parse(v);
|
||||
if (Array.isArray(v)) return v;
|
||||
return [];
|
||||
} catch (e) {
|
||||
console.error('解析存储的图表配置失败,使用空配置:', e);
|
||||
@@ -204,41 +246,25 @@ const persistedChartConfigs = useStorage(
|
||||
}
|
||||
);
|
||||
|
||||
// 临时配置
|
||||
const tempChartConfigs = ref([]);
|
||||
// 初始配置快照
|
||||
const initialChartConfigs = ref([]);
|
||||
// 保存状态
|
||||
const saving = ref(false);
|
||||
// 移除拖拽状态变量:isDragging
|
||||
|
||||
// 窗口尺寸响应
|
||||
const windowWidth = ref(window.innerWidth);
|
||||
const tableHeight = ref(300);
|
||||
// 高度输入错误标记
|
||||
const heightErrorIndex = ref(-1);
|
||||
// 批量设置弹窗
|
||||
const showBatchSettings = ref(false);
|
||||
// 批量设置数据
|
||||
const batchSettings = reactive({
|
||||
applyRange: 'all', // 'all' 或 'visible'
|
||||
applyRange: 'all',
|
||||
height: null,
|
||||
layout: {
|
||||
xs: null,
|
||||
sm: null,
|
||||
md: null,
|
||||
lg: null,
|
||||
xl: null
|
||||
}
|
||||
layout: { xs: null, sm: null, md: null, lg: null, xl: null }
|
||||
});
|
||||
|
||||
// 监听窗口尺寸变化
|
||||
// 原有方法(onMounted、calculateTableHeight、resetToSession等)保持不变
|
||||
useEventListener('resize', () => {
|
||||
windowWidth.value = window.innerWidth;
|
||||
calculateTableHeight();
|
||||
});
|
||||
|
||||
// 初始化:补充height默认值
|
||||
onMounted(() => {
|
||||
if (!Array.isArray(persistedChartConfigs.value)) {
|
||||
persistedChartConfigs.value = [];
|
||||
@@ -247,21 +273,14 @@ onMounted(() => {
|
||||
calculateTableHeight();
|
||||
});
|
||||
|
||||
// 计算表格高度
|
||||
const calculateTableHeight = () => {
|
||||
// 根据屏幕高度动态计算表格高度,确保整体布局合理
|
||||
const panelHeight = window.innerHeight - 220;
|
||||
tableHeight.value = Math.max(300, Math.min(600, panelHeight));
|
||||
const panelHeight = window.innerHeight - 400; // 预留预览区域高度
|
||||
tableHeight.value = Math.max(300, Math.min(500, panelHeight));
|
||||
};
|
||||
|
||||
// 重置临时配置:补充height默认值
|
||||
const resetToSession = () => {
|
||||
try {
|
||||
const source = Array.isArray(persistedChartConfigs.value)
|
||||
? persistedChartConfigs.value
|
||||
: [];
|
||||
|
||||
// 深拷贝 + 补充height默认值和visible属性
|
||||
const source = Array.isArray(persistedChartConfigs.value) ? persistedChartConfigs.value : [];
|
||||
const configCopy = JSON.parse(JSON.stringify(source)).map(config => ({
|
||||
...config,
|
||||
height: config.height || 400,
|
||||
@@ -274,7 +293,6 @@ const resetToSession = () => {
|
||||
xl: config.layout?.xl || 6
|
||||
}
|
||||
}));
|
||||
|
||||
tempChartConfigs.value = configCopy;
|
||||
initialChartConfigs.value = JSON.parse(JSON.stringify(configCopy));
|
||||
ElMessage.info('已恢复到打开设置时的状态');
|
||||
@@ -284,7 +302,6 @@ const resetToSession = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 高度输入验证
|
||||
const validateHeight = (row, index) => {
|
||||
heightErrorIndex.value = -1;
|
||||
if (isNaN(row.height) || row.height < 100 || row.height > 1000) {
|
||||
@@ -294,48 +311,34 @@ const validateHeight = (row, index) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// 移动图表位置(按钮控制)- 保留按钮功能,移除拖拽相关逻辑
|
||||
const moveChart = (index, direction) => {
|
||||
let newIndex;
|
||||
if (direction === 'up' && index > 0) {
|
||||
newIndex = index - 1;
|
||||
// 交换位置
|
||||
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] =
|
||||
[tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
||||
// 触发重绘
|
||||
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] = [tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
||||
tempChartConfigs.value = [...tempChartConfigs.value];
|
||||
// 添加动画效果
|
||||
highlightRow(index);
|
||||
highlightRow(newIndex);
|
||||
} else if (direction === 'down' && index < tempChartConfigs.value.length - 1) {
|
||||
newIndex = index + 1;
|
||||
// 交换位置
|
||||
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] =
|
||||
[tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
||||
// 触发重绘
|
||||
[tempChartConfigs.value[index], tempChartConfigs.value[newIndex]] = [tempChartConfigs.value[newIndex], tempChartConfigs.value[index]];
|
||||
tempChartConfigs.value = [...tempChartConfigs.value];
|
||||
// 添加动画效果
|
||||
highlightRow(index);
|
||||
highlightRow(newIndex);
|
||||
}
|
||||
};
|
||||
|
||||
// 高亮行(动画效果)- 保留按钮移动后的高亮效果
|
||||
const highlightRow = (index) => {
|
||||
const rowEl = document.querySelector(`.el-table__row:nth-child(${index + 1})`);
|
||||
if (rowEl) {
|
||||
rowEl.classList.add('row-highlight');
|
||||
setTimeout(() => {
|
||||
rowEl.classList.remove('row-highlight');
|
||||
}, 600);
|
||||
setTimeout(() => rowEl.classList.remove('row-highlight'), 600);
|
||||
}
|
||||
};
|
||||
|
||||
// 应用批量设置
|
||||
const applyBatchSettings = () => {
|
||||
// 过滤需要应用设置的图表
|
||||
const targetCharts = batchSettings.applyRange === 'visible'
|
||||
? tempChartConfigs.value.filter(chart => chart.visible)
|
||||
const targetCharts = batchSettings.applyRange === 'visible'
|
||||
? tempChartConfigs.value.filter(chart => chart.visible)
|
||||
: tempChartConfigs.value;
|
||||
|
||||
if (targetCharts.length === 0) {
|
||||
@@ -343,15 +346,10 @@ const applyBatchSettings = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用设置
|
||||
targetCharts.forEach(chart => {
|
||||
// 应用高度设置
|
||||
if (batchSettings.height !== null && !isNaN(batchSettings.height) &&
|
||||
batchSettings.height >= 100 && batchSettings.height <= 1000) {
|
||||
if (batchSettings.height !== null && !isNaN(batchSettings.height) && batchSettings.height >= 100 && batchSettings.height <= 1000) {
|
||||
chart.height = batchSettings.height;
|
||||
}
|
||||
|
||||
// 应用布局设置
|
||||
Object.keys(batchSettings.layout).forEach(key => {
|
||||
if (batchSettings.layout[key] !== null) {
|
||||
chart.layout[key] = batchSettings.layout[key];
|
||||
@@ -359,35 +357,25 @@ const applyBatchSettings = () => {
|
||||
});
|
||||
});
|
||||
|
||||
// 刷新表格
|
||||
tempChartConfigs.value = [...tempChartConfigs.value];
|
||||
showBatchSettings.value = false;
|
||||
ElMessage.success(`已对 ${targetCharts.length} 个图表应用批量设置`);
|
||||
};
|
||||
|
||||
// 应用设置
|
||||
const applySettings = async () => {
|
||||
try {
|
||||
// 验证所有高度输入
|
||||
const hasInvalidHeight = tempChartConfigs.value.some((row, index) =>
|
||||
!validateHeight(row, index)
|
||||
);
|
||||
const hasInvalidHeight = tempChartConfigs.value.some((row, index) => !validateHeight(row, index));
|
||||
if (hasInvalidHeight) {
|
||||
ElMessage.error('存在无效的高度配置,请修正后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有可见图表
|
||||
const visibleCharts = tempChartConfigs.value.filter(chart => chart.visible);
|
||||
if (visibleCharts.length === 0) {
|
||||
await ElMessageBox.confirm(
|
||||
'所有图表都处于隐藏状态,确定要应用设置吗?',
|
||||
'确认提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -401,7 +389,7 @@ const applySettings = async () => {
|
||||
throw new Error('配置数据格式错误,必须为数组');
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') { // 忽略取消操作的错误
|
||||
if (error !== 'cancel') {
|
||||
console.error('保存配置失败:', error);
|
||||
ElMessage.error('保存配置失败,请重试');
|
||||
}
|
||||
@@ -410,18 +398,12 @@ const applySettings = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 定义事件
|
||||
const emits = defineEmits(['config-updated', 'close']);
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
resetToSession,
|
||||
applySettings
|
||||
});
|
||||
defineExpose({ resetToSession, applySettings });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 基础样式 */
|
||||
/* 原有样式保持不变,新增预览区域样式 */
|
||||
.chart-settings-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -430,14 +412,11 @@ defineExpose({
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* 移除所有拖拽相关样式 */
|
||||
|
||||
/* 表格行动画效果 */
|
||||
.el-table__body .el-table-row {
|
||||
transition: transform 0.3s ease, opacity 0.3s ease, background-color 0.3s ease;
|
||||
}
|
||||
|
||||
/* 行高亮动画 - 保留按钮移动后的高亮效果 */
|
||||
.row-highlight {
|
||||
animation: rowHighlight 0.6s ease;
|
||||
}
|
||||
@@ -447,7 +426,6 @@ defineExpose({
|
||||
100% { background-color: transparent; }
|
||||
}
|
||||
|
||||
/* 高度错误提示 */
|
||||
.height-error {
|
||||
font-size: 12px;
|
||||
color: #ef4444;
|
||||
@@ -461,7 +439,6 @@ defineExpose({
|
||||
20%, 40%, 60%, 80% { transform: translateX(2px); }
|
||||
}
|
||||
|
||||
/* 其他样式 */
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -512,6 +489,92 @@ defineExpose({
|
||||
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 {
|
||||
@@ -544,5 +607,9 @@ defineExpose({
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.layout-preview {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user