<template>
  <div class="edit-program-layout">
    <!-- 左侧组件栏及返回按钮 -->
    <div class="sidebar">
      <el-button class="back-btn" type="default" @click="goBack" circle>
        <el-icon>
          <ArrowLeft />
        </el-icon>
      </el-button>
      <div class="sidebar-title">组件</div>
      <template v-for="(item, idx) in editorContent.elements.slice().sort((a, b) => b.depth - a.depth)"
        :key="item.depth + '-' + item.type">
        <div class="sidebar-item component-item"
          :class="{ 'selected': selectedComponent === item || (item.type === 'canvas' && selectedComponent && selectedComponent.type === 'canvas') }"
          @click="item.type === 'canvas' ? selectCanvasFromSidebar() : selectComponent(item)" draggable="true"
          @dragstart="onSidebarDragStart(item, idx)" @dragover.prevent="onSidebarDragOver(item, idx, $event)"
          @drop.prevent="onSidebarDrop(item, idx, $event)">
          <div class="component-icon-text">
            <template v-if="item.type === 'canvas'">画布</template>
            <template v-else-if="item.type === 'text'">文本</template>
            <template v-else-if="item.type === 'scrollingText'">滚动文本</template>
            <template v-else-if="item.type === 'mediaAsset'">媒资</template>
            <template v-else-if="item.type === 'live'">直播</template>
            <template v-else-if="item.type === 'webPage'">网页</template>
            <!-- 未来可扩展图片等类型 -->
          </div>
        </div>
      </template>
    </div>

    <!-- 中间编辑区 -->
    <div class="main-editor">
      <div class="toolbar">
        <div class="toolbar-item" draggable="true" @dragstart="onToolbarDragStart('text')">文本</div>
        <div class="toolbar-item" draggable="true" @dragstart="onToolbarDragStart('scrollingText')">滚动文本</div>
        <div class="toolbar-item" draggable="true" @dragstart="onToolbarDragStart('mediaAsset')">媒资</div>
        <div class="toolbar-item" draggable="true" @dragstart="onToolbarDragStart('live')">直播</div>
        <div class="toolbar-item" draggable="true" @dragstart="onToolbarDragStart('webPage')">网页</div>
        <!-- 可扩展更多组件 -->
      </div>
      <div class="editor-canvas" ref="editorCanvasRef" @dragover.prevent @drop="onCanvasDrop">
        <CanvasBoard v-if="canvasItem" :width="canvasItem.width" :height="canvasItem.height" :bg="canvasItem.bg"
          :scale="canvasScale" @click.stop="selectComponent(canvasItem)"
          :class="{ selected: selectedComponent === canvasItem }">
          <template #default>
            <template
              v-for="item in editorContent.elements.filter((el) => el.type !== 'canvas').sort((a, b) => a.depth - b.depth)"
              :key="item.depth + '-' + item.type">
              <div :style="{
                position: 'absolute',
                left: (item.x || 0) * canvasScale + 'px',
                top: (item.y || 0) * canvasScale + 'px',
                width: (item.width || 200) * canvasScale + 'px',
                height: (item.height || 40) * canvasScale + 'px',
                cursor: draggingId === item.depth ? 'grabbing' : 'move',
                zIndex: 10
              }" @mousedown="onElementMouseDown($event, item)">
                <TextBoard v-if="item.type === 'text'" :text="item.text" :color="item.color" :font-size="item.fontSize"
                  :font-weight="item.fontWeight" :align="item.align" :width="item.width * canvasScale"
                  :height="item.height * canvasScale" :selected="selectedComponent === item" @resize="
                    ({ width, height }) => {
                      item.width = width / canvasScale;
                      item.height = height / canvasScale;
                    }
                  " @click.stop="selectComponent(item)" :class="{ selected: selectedComponent === item }" />
                <ScrollingTextBoard v-if="item.type === 'scrollingText'" :text="item.text" :color="item.color"
                  :font-size="item.fontSize" :font-weight="item.fontWeight" :align="item.align"
                  :width="item.width * canvasScale" :height="item.height * canvasScale" :speed="item.speed"
                  :selected="selectedComponent === item" @resize="
                    ({ width, height }) => {
                      item.width = width / canvasScale;
                      item.height = height / canvasScale;
                    }
                  " @click.stop="selectComponent(item)" :class="{ selected: selectedComponent === item }" />
                <MediaAssetBoard v-if="item.type === 'mediaAsset'" :width="item.width * canvasScale"
                  :height="item.height * canvasScale" :media-id="item.mediaId" :selected="selectedComponent === item"
                  @resize="
                    ({ width, height }) => {
                      item.width = width / canvasScale;
                      item.height = height / canvasScale;
                    }
                  " @click.stop="selectComponent(item)" :class="{ selected: selectedComponent === item }" />
                <LiveBoard v-if="item.type === 'live'" :width="item.width * canvasScale"
                  :height="item.height * canvasScale" :live-url="item.liveUrl" :play-audio="item.playAudio"
                  :selected="selectedComponent === item" @resize="
                    ({ width, height }) => {
                      item.width = width / canvasScale;
                      item.height = height / canvasScale;
                    }
                  " @click.stop="selectComponent(item)" :class="{ selected: selectedComponent === item }" />
                <WebPageBoard v-if="item.type === 'webPage'" :width="item.width * canvasScale"
                  :height="item.height * canvasScale" :url="item.url" :selected="selectedComponent === item" @resize="
                    ({ width, height }) => {
                      item.width = width / canvasScale;
                      item.height = height / canvasScale;
                    }
                  " @click.stop="selectComponent(item)" :class="{ selected: selectedComponent === item }" />
                <!-- 未来可扩展更多类型 -->
              </div>
            </template>
          </template>
        </CanvasBoard>
      </div>
      <el-button type="primary" class="save-btn" :loading="saveLoading" :disabled="saveLoading"
        @click="handleSave">保存</el-button>
    </div>

    <!-- 右侧属性栏 -->
    <div class="property-panel">
      <div class="property-title">属性</div>
      <div class="property-form-area">
        <div class="property-info">
          <div class="property-info-row">
            <span class="property-info-label">节目名称：</span>
            <span>{{ programName }}</span>
          </div>
          <div class="property-info-row">
            <span class="property-info-label">分辨率：</span>
            <span>{{ programResolution }}</span>
          </div>
        </div>
        <!-- 动态显示选中组件的可编辑属性 -->
        <template v-if="selectedComponent">
          <div style="margin-bottom: 8px; font-weight: bold">组件属性</div>
          <template v-for="[key, value] in Object.entries(selectedComponent || {})" :key="key">
            <el-form-item v-if="showEditableProp(key)" :label="getPropLabel(key)">
              <template v-if="selectedComponent.type === 'live' && key === 'playAudio'">
                <el-switch v-model="selectedComponent[key]" active-text="开" inactive-text="关" />
              </template>
              <template v-else-if="key === 'mediaId'">
                <MediaFileSelector v-model="selectedComponent[key]" />
              </template>
              <template v-else>
                <el-input v-model="selectedComponent[key]" />
              </template>
            </el-form-item>
          </template>
          <!-- 对齐尺寸操作区 -->
          <div style="margin: 12px 0">
            <div style="font-weight: bold; margin-bottom: 10px">对齐尺寸</div>
            <!-- 第一组 2x2 -->
            <div
              style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 12px; padding-left: 2px">
              <el-button size="small" style="width: 100%" @click="alignComponent('left')">水平靠左</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('right')">水平靠右</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('top')">垂直靠上</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('bottom')">垂直靠下</el-button>
            </div>
            <hr style="border: none; border-top: 1px solid #eee; margin: 8px 0" />
            <!-- 第二组 2x2 -->
            <div
              style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 12px; padding-left: 2px">
              <el-button size="small" style="width: 100%" @click="alignComponent('width-full')">宽铺满</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('width-half')">宽半屏</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('width-third')">宽1/3屏</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('width-quarter')">宽1/4屏</el-button>
            </div>
            <hr style="border: none; border-top: 1px solid #eee; margin: 8px 0" />
            <!-- 第三组 2x2 -->
            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 4px; padding-left: 2px">
              <el-button size="small" style="width: 100%" @click="alignComponent('height-full')">高铺满</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('height-half')">高半屏</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('height-third')">高1/3屏</el-button>
              <el-button size="small" style="width: 100%" @click="alignComponent('height-quarter')">高1/4屏</el-button>
            </div>
          </div>
        </template>
        <template v-else>
          <div style="color: #bbb">请点击编辑区中的组件以编辑属性</div>
        </template>
      </div>
      <hr class="property-divider" />
      <template v-if="isLocalDev">
        <div class="json-debug-title">当前JSON</div>
        <el-input class="json-debug" type="textarea" :rows="8" :model-value="JSON.stringify(editorContent, null, 2)"
          readonly />
      </template>
    </div>
  </div>
</template>

<script setup lang="ts">
// 本地开发环境开关，正式环境请设为 false 或用 import.meta.env 读取
const isLocalDev = false;
// 对齐尺寸操作
function alignComponent(type: string) {
  if (!selectedComponent.value || selectedComponent.value.type === 'canvas') return;
  // 找到canvas尺寸
  const canvas = editorContent.value.elements.find((el: any) => el.type === 'canvas');
  if (!canvas) return;
  const cW = Number(canvas.width) || 600;
  const cH = Number(canvas.height) || 400;
  // 只操作当前选中组件
  const comp = selectedComponent.value;
  switch (type) {
    case 'left':
      comp.x = 0;
      break;
    case 'right':
      comp.x = cW - (Number(comp.width) || 0);
      break;
    case 'top':
      comp.y = 0;
      break;
    case 'bottom':
      comp.y = cH - (Number(comp.height) || 0);
      break;
    case 'width-full':
      comp.x = 0;
      comp.width = cW;
      break;
    case 'width-half':
      comp.x = 0;
      comp.width = Math.round(cW / 2);
      break;
    case 'width-third':
      comp.x = 0;
      comp.width = Math.round(cW / 3);
      break;
    case 'width-quarter':
      comp.x = 0;
      comp.width = Math.round(cW / 4);
      break;
    case 'height-full':
      comp.y = 0;
      comp.height = cH;
      break;
    case 'height-half':
      comp.y = 0;
      comp.height = Math.round(cH / 2);
      break;
    case 'height-third':
      comp.y = 0;
      comp.height = Math.round(cH / 3);
      break;
    case 'height-quarter':
      comp.y = 0;
      comp.height = Math.round(cH / 4);
      break;
  }
}

// 拖拽排序相关
const sidebarDrag = ref<{ item: any; idx: number } | null>(null);

function onSidebarDragStart(item: any, idx: number) {
  sidebarDrag.value = { item, idx };
}
function onSidebarDragOver(targetItem: any, targetIdx: number, e: DragEvent) {
  e.preventDefault();
}
function onSidebarDrop(targetItem: any, targetIdx: number, e: DragEvent) {
  if (!sidebarDrag.value) return;
  const elements = editorContent.value.elements;
  // 排序前先按 depth 降序
  const sorted = elements.slice().sort((a, b) => b.depth - a.depth);
  const fromIdx = sorted.findIndex((el) => el === sidebarDrag.value!.item);
  const toIdx = sorted.findIndex((el) => el === targetItem);
  if (fromIdx === -1 || toIdx === -1 || fromIdx === toIdx) {
    sidebarDrag.value = null;
    return;
  }
  // 交换depth
  const fromDepth = sorted[fromIdx].depth;
  const toDepth = sorted[toIdx].depth;
  sorted[fromIdx].depth = toDepth;
  sorted[toIdx].depth = fromDepth;
  // 重新赋值到原数组
  for (let i = 0; i < sorted.length; i++) {
    const origIdx = elements.findIndex((el) => el === sorted[i]);
    if (origIdx !== -1) elements[origIdx].depth = sorted[i].depth;
  }
  sidebarDrag.value = null;
}

import { ref, onMounted, computed, nextTick } from 'vue';
import CanvasBoard from './component/CanvasBoard.vue';
import TextBoard from './component/TextBoard.vue';
import ScrollingTextBoard from './component/ScrollingTextBoard.vue';
import MediaAssetBoard from './component/MediaAssetBoard.vue';
import LiveBoard from './component/LiveBoard.vue';
import WebPageBoard from './component/WebPageBoard.vue';
import {
  canvasPropNameMap,
  textPropNameMap,
  scrollingTextPropNameMap,
  mediaAssetPropNameMap,
  livePropNameMap,
  webPagePropNameMap
} from './component/propNameMaps';
// 拖拽类型
const dragType = ref<string | null>(null);
// 获取最大 depth
function getMaxDepth() {
  if (!editorContent.value.elements.length) return 0;
  return Math.max(...editorContent.value.elements.map((el: any) => el.depth || 0));
}
import { useRoute, useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { ArrowLeft } from '@element-plus/icons-vue';
import { getItemProgram, updateItemProgram } from '@/api/smsb/source/item_program';

const route = useRoute();

// 当前选中组件
const selectedComponent = ref<any>(null);

// 选中组件方法
function selectComponent(item: any) {
  selectedComponent.value = item;
}

// 左侧栏点击选中画布
function selectCanvasFromSidebar() {
  const canvas = editorContent.value.elements.find((el: any) => el.type === 'canvas');
  if (canvas) {
    selectedComponent.value = canvas;
  }
}

// 属性栏显示哪些属性可编辑（可根据实际需求过滤）
function showEditableProp(key: string) {
  // 明确排除 type 字段，防止被编辑
  if (!selectedComponent.value) return false;
  // 对于文本和滚动文本组件，移除 'fontWeight' 属性
  if ((selectedComponent.value.type === 'text' || selectedComponent.value.type === 'scrollingText') && (key === 'fontWeight' || key === 'align')) {
    return false;
  }
  return !['type', 'depth'].includes(key);
}

// 获取属性中文名
function getPropLabel(key: string) {
  if (selectedComponent.value?.type === 'canvas') {
    return canvasPropNameMap[key] || key;
  } else if (selectedComponent.value?.type === 'text') {
    return textPropNameMap[key] || key;
  } else if (selectedComponent.value?.type === 'scrollingText') {
    return scrollingTextPropNameMap[key] || key;
  } else if (selectedComponent.value?.type === 'mediaAsset') {
    return mediaAssetPropNameMap[key] || key;
  } else if (selectedComponent.value?.type === 'live') {
    return livePropNameMap[key] || key;
  } else if (selectedComponent.value?.type === 'webPage') {
    return webPagePropNameMap[key] || key;
  }
  return key;
}

const router = useRouter();
// 自动修正 id 类型，确保为 string 或 number
const rawId = route.params.id;
const id = ref<string | number>(Array.isArray(rawId) ? rawId[0] : rawId);

// 自动填充画布分辨率
onMounted(async () => {
  try {
    const res = await getItemProgram(id.value);
    const data = res.data;
    let resolutionRatio = '';
    if (data && data.resolutionRatio) {
      resolutionRatio = data.resolutionRatio;
    }
    // 优先使用后端返回的 itemJsonStr 字段
    let parsed = { elements: [] };
    if (data && data.itemJsonStr) {
      try {
        parsed = JSON.parse(data.itemJsonStr);
      } catch (err) {
        // 解析失败则回退到空布局
        parsed = { elements: [] };
      }
    }
    editorContent.value = parsed;
    ensureCanvasAndDepth(editorContent.value.elements, resolutionRatio);
  } catch (e) {
    // fallback: 初始化 elements 并插入默认画布
    editorContent.value = { elements: [] };
    ensureCanvasAndDepth(editorContent.value.elements);
  }
});

function ensureCanvasAndDepth(elements, resolutionRatio?: string) {
  let width = 600,
    height = 400;
  if (resolutionRatio) {
    const [w, h] = resolutionRatio.split('x').map(Number);
    if (w && h) {
      width = w;
      height = h;
      // console.log('#136: ', width, height);
    }
  }
  // 检查是否有 type: 'canvas' 的组件
  let idx = elements.findIndex((el) => el.type === 'canvas');
  if (idx === -1) {
    // console.log('#141: ', width, height);
    elements.unshift({ type: 'canvas', width, height, bg: '#fff', depth: 0 });
  } else {
    let canvas = elements[idx];
    let changed = false;
    if (!canvas.width) {
      canvas = { ...canvas, width };
      changed = true;
    }
    if (!canvas.height) {
      canvas = { ...canvas, height };
      changed = true;
    }
    if (changed) {
      elements[idx] = canvas; // 替换整个对象，确保响应式
      // console.log('#145: ', width, height, '响应式canvas:', canvas);
    } else {
      // console.log('#145: ', width, height);
    }
  }
  // 按 depth 排序，如果没有 depth 则补齐
  elements.forEach((el, idx) => {
    if (typeof el.depth !== 'number') {
      el.depth = el.type === 'canvas' ? 0 : idx + 1;
    }
  });
  elements.sort((a, b) => a.depth - b.depth);
  return elements;
}

interface EditorContent {
  name?: string;
  resolutionRatio?: string;
  elements: any[];
  [key: string]: any;
}
const editorContent = ref<EditorContent>({ elements: [] });

// editor-canvas 缩放逻辑
const editorCanvasRef = ref<HTMLElement | null>(null);
const containerSize = ref({ width: 0, height: 0 });

const draggingId = ref<number | null>(null);
let dragStart = { x: 0, y: 0, offsetX: 0, offsetY: 0 };

function onElementMouseDown(e: MouseEvent, item: any) {
  e.stopPropagation();
  draggingId.value = item.depth;
  dragStart = {
    x: e.clientX,
    y: e.clientY,
    offsetX: item.x || 0,
    offsetY: item.y || 0
  };
  document.addEventListener('mousemove', onElementMouseMove);
  document.addEventListener('mouseup', onElementMouseUp);
}

function onElementMouseMove(e: MouseEvent) {
  if (draggingId.value === null) return;
  const item = editorContent.value.elements.find((el) => el.depth === draggingId.value);
  if (!item) return;
  // 拖拽时坐标除以缩放比例，保证拖拽速度和鼠标一致
  item.x = dragStart.offsetX + (e.clientX - dragStart.x) / canvasScale.value;
  item.y = dragStart.offsetY + (e.clientY - dragStart.y) / canvasScale.value;
}

function onElementMouseUp() {
  draggingId.value = null;
  document.removeEventListener('mousemove', onElementMouseMove);
  document.removeEventListener('mouseup', onElementMouseUp);
}

function onToolbarDragStart(type: string) {
  dragType.value = type;
}

function onCanvasDrop(e: DragEvent) {
  if (!dragType.value) return;
  const rect = editorCanvasRef.value?.getBoundingClientRect();
  const x = e.clientX - (rect?.left || 0);
  const y = e.clientY - (rect?.top || 0);
  if (dragType.value === 'text') {
    const newText = {
      type: 'text',
      text: '新文本',
      color: '#222',
      fontSize: 24,
      fontWeight: 'normal',
      align: 'center',
      x: x,
      y: y,
      width: 200,
      height: 40,
      depth: getMaxDepth() + 1
    };
    editorContent.value.elements.push(newText);
    nextTick(() => selectComponent(newText));
  } else if (dragType.value === 'scrollingText') {
    const newScrollingText = {
      type: 'scrollingText',
      text: '新滚动文本',
      color: '#222',
      fontSize: 24,
      fontWeight: 'normal',
      align: 'center',
      speed: 50,
      x: x,
      y: y,
      width: 300,
      height: 40,
      depth: getMaxDepth() + 1
    };
    editorContent.value.elements.push(newScrollingText);
    nextTick(() => selectComponent(newScrollingText));
  } else if (dragType.value === 'mediaAsset') {
    const newMediaAsset = {
      type: 'mediaAsset',
      mediaId: '',
      x: x,
      y: y,
      width: 120,
      height: 120,
      depth: getMaxDepth() + 1
    };
    editorContent.value.elements.push(newMediaAsset);
    nextTick(() => selectComponent(newMediaAsset));
  } else if (dragType.value === 'live') {
    const newLive = {
      type: 'live',
      liveUrl: '',
      playAudio: true,
      x: x,
      y: y,
      width: 200,
      height: 120,
      depth: getMaxDepth() + 1
    };
    editorContent.value.elements.push(newLive);
    nextTick(() => selectComponent(newLive));
  } else if (dragType.value === 'webPage') {
    const newWebPage = {
      type: 'webPage',
      url: '',
      x: x,
      y: y,
      width: 120,
      height: 120,
      depth: getMaxDepth() + 1
    };
    editorContent.value.elements.push(newWebPage);
    nextTick(() => selectComponent(newWebPage));
  }
  dragType.value = null;
}

function updateContainerSize() {
  if (editorCanvasRef.value) {
    containerSize.value.width = editorCanvasRef.value.clientWidth;
    containerSize.value.height = editorCanvasRef.value.clientHeight;
  }
}

onMounted(async () => {
  nextTick(updateContainerSize);
  window.addEventListener('resize', updateContainerSize);
  // 获取节目详细信息并补充到 editorContent
  try {
    const res = await getItemProgram(id.value);
    let name = res.data?.name || '';
    let resolutionRatio = res.data?.resolutionRatio || '';
    let parsed: any = { elements: [] };
    if (res.data && res.data.itemJsonStr) {
      try {
        parsed = JSON.parse(res.data.itemJsonStr);
      } catch (err) {
        parsed = { elements: [] };
      }
    }
    // 合并 name、resolutionRatio 字段，保证结构完整
    editorContent.value = {
      ...parsed,
      name,
      resolutionRatio,
      elements: Array.isArray(parsed.elements) ? parsed.elements : []
    };
    ensureCanvasAndDepth(editorContent.value.elements, editorContent.value.resolutionRatio);
  } catch (e) {
    // fallback: 初始化 elements 并插入默认画布，且补齐基础字段
    editorContent.value = {
      name: '',
      resolutionRatio: '',
      elements: []
    };
    ensureCanvasAndDepth(editorContent.value.elements);
  }
});

const canvas = computed(() => editorContent.value.elements.find((el: any) => el.type === 'canvas'));

const canvasScale = computed(() => {
  if (!canvas.value) return 1;
  const cW = Number(canvas.value.width) || 600;
  const cH = Number(canvas.value.height) || 400;
  const boxW = containerSize.value.width;
  const boxH = containerSize.value.height;
  if (!boxW || !boxH) return 1;
  return Math.min(boxW / cW, boxH / cH, 1);
});

// 修复：为模板提供 canvasItem 变量
const canvasItem = computed(() => editorContent.value.elements.find((el: any) => el.type === 'canvas'));

// 右侧属性栏：节目名称和分辨率
console.log(editorContent.value);
const programName = computed(() => editorContent.value.name || '-');
const programResolution = computed(() => {
  // 优先取 editorContent.value.resolutionRatio，其次 canvas 宽高
  if (editorContent.value.resolutionRatio) return editorContent.value.resolutionRatio;
  const canvas = editorContent.value.elements?.find((el: any) => el.type === 'canvas');
  if (canvas && canvas.width && canvas.height) return `${canvas.width}x${canvas.height}`;
  return '-';
});

const saveLoading = ref(false);
const handleSave = async () => {
  saveLoading.value = true;
  try {
    // 先获取后端原始数据，避免遗漏字段
    const res = await getItemProgram(id.value);
    const data = res.data || {};
    // 用最新 JSON 覆盖
    data.itemJsonStr = JSON.stringify(editorContent.value);
    await updateItemProgram(data);
    ElMessage.success('保存成功，所有数据已同步到数据库');
  } catch (e) {
    ElMessage.error('保存失败，请重试');
  } finally {
    saveLoading.value = false;
  }
};

const goBack = () => {
  router.push('/source/program');
};
</script>

<style scoped>
.edit-program-layout {
  display: flex;
  flex-direction: row;
  height: 100vh;
  background: #f6f8fa;
  min-width: 900px;
}

.sidebar {
  width: 90px;
  background: #232a36;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 18px;
  padding-bottom: 24px;
  box-sizing: border-box;
  height: 100vh;
}

.sidebar-title {
  font-size: 16px;
  margin-bottom: 18px;
}

.sidebar-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 18px;
  cursor: pointer;
  width: 90%;
}

.sidebar-icon {
  width: 34px;
  height: 34px;
  margin-bottom: 4px;
}

.sidebar-icon.text {
  width: 34px;
  height: 34px;
  background: #fff;
  color: #232a36;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  font-size: 22px;
  border-radius: 6px;
  margin-bottom: 4px;
}

.main-editor {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 0;
  position: relative;
}

.editor-canvas {
  width: 90%;
  height: 75%;
  background: #e9eef3;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 24px;
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.06);
  position: relative;
  overflow: hidden;
}

.toolbar {
  display: flex;
  flex-direction: row;
  align-self: flex-start;
  align-items: center;
  height: 50px;
  width: 700px;
  margin-left: 5%;
  margin-top: -5%;
  margin-bottom: 1%;
  background: #fafbfc;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  gap: 16px;
  padding: 0 16px;
}

.toolbar-item {
  user-select: none;
  cursor: grab;
  background: #fff;
  border-radius: 4px;
  /* margin: 8px 0 8px 8px; */
  width: 92px;
  min-height: 50px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  font-weight: 500;
  letter-spacing: 1px;
  text-align: center;
  transition: background 0.2s;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);
  /* padding: 0 8px; */
}

.toolbar-item:active {
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.13);
  border-color: #409eff;
}

.canvas-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.canvas-icon {
  width: 120px;
  height: 120px;
  margin-bottom: 18px;
}

.canvas-text {
  color: #222;
  font-size: 22px;
}

.save-btn {
  align-self: flex-end;
  margin-right: 8vw;
}

.back-btn {
  margin-bottom: 16px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
  border: none;
  margin-left: auto;
  margin-right: auto;
}

.property-info {
  margin-bottom: 18px;
}

.property-info-row {
  display: flex;
  align-items: center;
  margin-bottom: 6px;
}

.property-info-label {
  color: #888;
  min-width: 72px;
  font-weight: 500;
}

.property-panel {
  width: 260px;
  background: #fff;
  box-shadow: -2px 0 8px rgba(0, 0, 0, 0.03);
  padding: 32px 18px 0 18px;
  display: flex;
  flex-direction: column;
  height: 100vh;
  position: relative;
  border-left: 1px solid #ececec;
  box-sizing: border-box;
  justify-content: flex-start;
}

.property-form-area {
  flex: 0 0 auto;
}

.property-divider {
  height: 1px;
  background: #ececec;
  margin: 18px 0 12px 0;
  width: 100%;
  border: none;
}

.el-button+.el-button {
  margin-left: 0px;
}

.json-debug-title {
  margin-top: 30px;
  font-size: 14px;
  color: #888;
  font-weight: bold;
}

.json-debug {
  margin-top: 8px;
  font-size: 13px;
  background: #f6f8fa;
  color: #222;
  font-family: 'Fira Mono', 'Consolas', monospace;
}

.property-title {
  font-size: 16px;
  margin-bottom: 18px;
}

.canvas-default {
  width: 600px;
  height: 400px;
  background: #fff;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  color: #aaa;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.component-item {
  border: 2px solid #e3e3e3;
  border-radius: 10px;
  background: #fafbfc;
  margin-bottom: 16px;
  padding: 18px 0;
  text-align: center;
  cursor: pointer;
  transition:
    border-color 0.2s,
    box-shadow 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 500;
}

.component-item.selected {
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.15);
  background: #eaf6ff;
}

.component-icon-text {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  font-size: 18px;
  color: #222;
}

.sidebar-item-disabled {
  pointer-events: none;
  opacity: 0.6;
}
</style>
