|
|
@@ -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%;
|