jimmylee 2 месяцев назад
Родитель
Сommit
a63bc12d04
1 измененных файлов с 53 добавлено и 1 удалено
  1. 53 1
      src/views/Editor/EnglishSpeaking/preview/TopicDiscussionPreview.vue

+ 53 - 1
src/views/Editor/EnglishSpeaking/preview/TopicDiscussionPreview.vue

@@ -1,5 +1,6 @@
 <template>
-  <div class="topic-discussion-preview">
+  <div class="topic-discussion-fit-wrapper" ref="fitWrapperRef">
+  <div class="topic-discussion-preview" :style="fitStyle">
     <!-- Ready 阶段:极简首页(参照 enspeak 布局) -->
     <div v-if="dialogueState === 'checking-history'" class="ready-stage">
       <div class="ready-header">
@@ -78,6 +79,7 @@
     </div>
 
   </div>
+  </div>
 </template>
 
 <script lang="ts" setup>
@@ -108,6 +110,40 @@ const props = withDefaults(defineProps<Props>(), {
 
 const speakingStore = useSpeakingStore()
 
+// 让组件按 DESIGN_WIDTH 设计宽度等比放大到任意槽位尺寸:
+// 槽位(type 77 的 frame 默认为整张 slide ~1000×562)若直接装下按手机尺寸设计的 chat
+// 会显得字小、留白大、布局像桌面端。这里用 ResizeObserver + transform: scale 让内部
+// 始终按 DESIGN_WIDTH px 布局,再视觉缩放到槽位实际宽度。
+// 调小 → 内部元素更大;调大 → 内部元素更小(接近槽位实际像素)。
+const DESIGN_WIDTH = 1000
+const fitWrapperRef = ref<HTMLDivElement>()
+const fitScale = ref(1)
+const fitInnerHeight = ref(0)
+
+const fitStyle = computed(() => {
+  if (fitInnerHeight.value === 0) {
+    return { width: '100%', height: '100%' }
+  }
+  return {
+    width: DESIGN_WIDTH + 'px',
+    height: fitInnerHeight.value + 'px',
+    transform: `scale(${fitScale.value})`,
+    transformOrigin: 'top left',
+  }
+})
+
+let fitResizeObserver: ResizeObserver | null = null
+function updateFit() {
+  const el = fitWrapperRef.value
+  if (!el) return
+  const w = el.clientWidth
+  const h = el.clientHeight
+  if (w === 0 || h === 0) return
+  const scale = w / DESIGN_WIDTH
+  fitScale.value = scale
+  fitInnerHeight.value = h / scale
+}
+
 const dialogueState = ref<PreviewDialogueState>('ready')
 const sessionCreating = ref(false)
 const sessionError = ref<string | null>(null)
@@ -452,14 +488,30 @@ watch(() => props.configId, (id) => {
 onMounted(() => {
   speakingStore.setPreviewState(dialogueState.value)
   loadConfigFromBackend(props.configId)
+  if (fitWrapperRef.value) {
+    fitResizeObserver = new ResizeObserver(updateFit)
+    fitResizeObserver.observe(fitWrapperRef.value)
+    updateFit()
+  }
 })
 onUnmounted(() => {
   nextHistoryLoadToken()
   speakingStore.setPreviewState('ready')
+  fitResizeObserver?.disconnect()
+  fitResizeObserver = null
 })
 </script>
 
 <style lang="scss" scoped>
+// 外层 fit-wrapper 始终撑满槽位(type 77 的 frame 默认 ~1000×562 设计 CSS-px)。
+// ResizeObserver 观察它的尺寸,再用 transform: scale() 把内层按 DESIGN_WIDTH 设计尺寸放大填满。
+.topic-discussion-fit-wrapper {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  position: relative;
+}
+
 .topic-discussion-preview {
   width: 100%;
   height: 100%;