|
@@ -106,51 +106,56 @@
|
|
|
<div class="property-panel">
|
|
<div class="property-panel">
|
|
|
<div class="property-title">属性</div>
|
|
<div class="property-title">属性</div>
|
|
|
<div class="property-form-area">
|
|
<div class="property-form-area">
|
|
|
- <el-form label-width="80px">
|
|
|
|
|
- <el-form-item label="节目ID">
|
|
|
|
|
- <el-input v-model="id" disabled />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <!-- 动态显示选中组件的可编辑属性 -->
|
|
|
|
|
- <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>
|
|
|
|
|
- <el-input v-model="selectedComponent[key]" />
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <el-input v-model="selectedComponent[key]" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
|
|
|
- <!-- 对齐尺寸操作区 -->
|
|
|
|
|
- <div style="margin: 12px 0">
|
|
|
|
|
- <div style="font-weight: bold; margin-bottom: 4px">对齐尺寸</div>
|
|
|
|
|
- <div style="display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px">
|
|
|
|
|
- <el-button size="small" @click="alignComponent('left')">水平靠左</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('right')">水平靠右</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('top')">垂直靠上</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('bottom')">垂直靠下</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div style="display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px">
|
|
|
|
|
- <el-button size="small" @click="alignComponent('width-full')">宽铺满</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('width-half')">宽半屏</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('width-third')">宽1/3屏</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('width-quarter')">宽1/4屏</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div style="display: flex; flex-wrap: wrap; gap: 4px">
|
|
|
|
|
- <el-button size="small" @click="alignComponent('height-full')">高铺满</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('height-half')">高半屏</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('height-third')">高1/3屏</el-button>
|
|
|
|
|
- <el-button size="small" @click="alignComponent('height-quarter')">高1/4屏</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <!-- 对齐尺寸操作区 -->
|
|
|
|
|
+ <div style="margin: 12px 0">
|
|
|
|
|
+ <div style="font-weight: bold; margin-bottom: 4px">对齐尺寸</div>
|
|
|
|
|
+ <div style="display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px">
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('left')">水平靠左</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('right')">水平靠右</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('top')">垂直靠上</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('bottom')">垂直靠下</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- </template>
|
|
|
|
|
- <template v-else>
|
|
|
|
|
- <div style="color: #bbb">请点击编辑区中的组件以编辑属性</div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-form>
|
|
|
|
|
|
|
+ <div style="display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px">
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('width-full')">宽铺满</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('width-half')">宽半屏</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('width-third')">宽1/3屏</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('width-quarter')">宽1/4屏</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="display: flex; flex-wrap: wrap; gap: 4px">
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('height-full')">高铺满</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('height-half')">高半屏</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('height-third')">高1/3屏</el-button>
|
|
|
|
|
+ <el-button size="small" @click="alignComponent('height-quarter')">高1/4屏</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template v-else>
|
|
|
|
|
+ <div style="color: #bbb">请点击编辑区中的组件以编辑属性</div>
|
|
|
|
|
+ </template>
|
|
|
</div>
|
|
</div>
|
|
|
<hr class="property-divider" />
|
|
<hr class="property-divider" />
|
|
|
<div class="json-debug-title">当前JSON</div>
|
|
<div class="json-debug-title">当前JSON</div>
|
|
@@ -322,7 +327,9 @@ function getPropLabel(key: string) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const router = useRouter();
|
|
const router = useRouter();
|
|
|
-const id = ref(route.params.id);
|
|
|
|
|
|
|
+// 自动修正 id 类型,确保为 string 或 number
|
|
|
|
|
+const rawId = route.params.id;
|
|
|
|
|
+const id = ref<string | number>(Array.isArray(rawId) ? rawId[0] : rawId);
|
|
|
|
|
|
|
|
// 自动填充画布分辨率
|
|
// 自动填充画布分辨率
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
@@ -403,12 +410,17 @@ function ensureCanvasAndDepth(elements, resolutionRatio?: string) {
|
|
|
return elements;
|
|
return elements;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const editorContent = ref({ elements: [] });
|
|
|
|
|
|
|
+interface EditorContent {
|
|
|
|
|
+ name?: string;
|
|
|
|
|
+ resolutionRatio?: string;
|
|
|
|
|
+ elements: any[];
|
|
|
|
|
+ [key: string]: any;
|
|
|
|
|
+}
|
|
|
|
|
+const editorContent = ref<EditorContent>({ elements: [] });
|
|
|
|
|
|
|
|
// editor-canvas 缩放逻辑
|
|
// editor-canvas 缩放逻辑
|
|
|
const editorCanvasRef = ref<HTMLElement | null>(null);
|
|
const editorCanvasRef = ref<HTMLElement | null>(null);
|
|
|
const containerSize = ref({ width: 0, height: 0 });
|
|
const containerSize = ref({ width: 0, height: 0 });
|
|
|
-const dragComponentType = ref<string | null>(null);
|
|
|
|
|
|
|
|
|
|
const draggingId = ref<number | null>(null);
|
|
const draggingId = ref<number | null>(null);
|
|
|
let dragStart = { x: 0, y: 0, offsetX: 0, offsetY: 0 };
|
|
let dragStart = { x: 0, y: 0, offsetX: 0, offsetY: 0 };
|
|
@@ -531,9 +543,32 @@ function updateContainerSize() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
|
|
+onMounted(async () => {
|
|
|
nextTick(updateContainerSize);
|
|
nextTick(updateContainerSize);
|
|
|
window.addEventListener('resize', updateContainerSize);
|
|
window.addEventListener('resize', updateContainerSize);
|
|
|
|
|
+ // 获取节目详细信息并补充到 editorContent
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getItemProgram(id.value);
|
|
|
|
|
+ if (res.data) {
|
|
|
|
|
+ // itemJsonStr 是画布内容
|
|
|
|
|
+ let content = {};
|
|
|
|
|
+ try {
|
|
|
|
|
+ content = res.data.itemJsonStr ? JSON.parse(res.data.itemJsonStr) : {};
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ content = {};
|
|
|
|
|
+ }
|
|
|
|
|
+ // 合并 name、resolutionRatio 等基础字段
|
|
|
|
|
+ editorContent.value = {
|
|
|
|
|
+ ...content,
|
|
|
|
|
+ name: res.data.name,
|
|
|
|
|
+ resolutionRatio: res.data.resolutionRatio,
|
|
|
|
|
+ elements: content.elements || []
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // 错误处理
|
|
|
|
|
+ editorContent.value = { elements: [] };
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
const canvas = computed(() => editorContent.value.elements.find((el: any) => el.type === 'canvas'));
|
|
const canvas = computed(() => editorContent.value.elements.find((el: any) => el.type === 'canvas'));
|
|
@@ -551,6 +586,17 @@ const canvasScale = computed(() => {
|
|
|
// 修复:为模板提供 canvasItem 变量
|
|
// 修复:为模板提供 canvasItem 变量
|
|
|
const canvasItem = computed(() => editorContent.value.elements.find((el: any) => el.type === 'canvas'));
|
|
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 saveLoading = ref(false);
|
|
|
const handleSave = async () => {
|
|
const handleSave = async () => {
|
|
|
saveLoading.value = true;
|
|
saveLoading.value = true;
|
|
@@ -733,6 +779,22 @@ const goBack = () => {
|
|
|
margin-right: 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 {
|
|
.property-panel {
|
|
|
width: 260px;
|
|
width: 260px;
|
|
|
background: #fff;
|
|
background: #fff;
|