Skip to content

Commit 1aa252d

Browse files
committed
refactor: 为导出预览和导出增加了共享的渲染层
- 新增共享导出中间表示和渲染入口exportRender,exportPreview不再单独进行渲染 - 将4个导出格式也改为基于同一份exportRender的中间结构,去掉重复的token展开
1 parent 0397665 commit 1aa252d

5 files changed

Lines changed: 411 additions & 392 deletions

File tree

src/components/workspace/ExportPreview.vue

Lines changed: 73 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545

4646
<template v-else>
4747
<DynamicScroller
48-
ref="scrollerRef"
4948
:items="rowsWithId"
5049
:min-item-size="24"
5150
key-field="rowId"
@@ -56,149 +55,101 @@
5655
:item="row"
5756
:active="active"
5857
:size-dependencies="[
59-
row.content,
58+
row.plainText,
59+
row.trailingPlainText,
6060
activeFormat.messageTemplate,
61+
activeFormat.messageSeparator,
62+
activeFormat.docSeparator,
63+
activeFormat.chunkSeparator,
6164
]"
6265
:data-index="index"
6366
>
6467
<div class="preview-row">
6568
<div
66-
v-if="
67-
row.type === 'documentSeparator' &&
68-
activeFormat.docSeparator
69-
"
69+
v-if="row.type !== 'message'"
7070
class="separator-preview"
7171
>
7272
<span
73-
v-for="(
74-
token, tIdx
75-
) in docSeparatorTokens"
76-
:key="'doc-' + tIdx"
73+
v-for="(segment, tIdx) in row.segments"
74+
:key="'block-' + tIdx"
7775
>
78-
<template v-if="token.type === 'text'">
79-
{{ token.value }}
80-
</template>
81-
<br
82-
v-else-if="token.type === 'newline'"
83-
/>
84-
<span
85-
v-else-if="token.type === 'tab'"
86-
class="tab-space"
87-
></span>
8876
<template
89-
v-else-if="
90-
token.type === 'placeholder' &&
91-
token.value === 'name'
92-
"
77+
v-if="segment.type === 'text'"
9378
>
94-
{{ row.content }}
95-
</template>
96-
</span>
97-
</div>
98-
99-
<div
100-
v-else-if="
101-
row.type === 'chunkSeparator' &&
102-
activeFormat.chunkSeparator
103-
"
104-
class="separator-preview"
105-
>
106-
<span
107-
v-for="(
108-
token, tIdx
109-
) in chunkSeparatorTokens"
110-
:key="'chunk-' + tIdx"
111-
>
112-
<template v-if="token.type === 'text'">
113-
{{ token.value }}
79+
<span
80+
:style="
81+
getSegmentStyle(segment)
82+
"
83+
>
84+
{{ segment.value }}
85+
</span>
11486
</template>
11587
<br
116-
v-else-if="token.type === 'newline'"
88+
v-else-if="
89+
segment.type === 'newline'
90+
"
11791
/>
11892
<span
119-
v-else-if="token.type === 'tab'"
93+
v-else-if="segment.type === 'tab'"
12094
class="tab-space"
12195
></span>
122-
<template
123-
v-else-if="
124-
token.type === 'placeholder' &&
125-
token.value === 'name'
126-
"
127-
>
128-
{{ row.content }}
129-
</template>
13096
</span>
13197
</div>
13298

133-
<div
134-
v-else-if="row.type === 'message'"
135-
class="message-preview"
136-
>
99+
<div v-else class="message-preview">
137100
<span
138-
v-for="(token, tIdx) in messageTokens"
101+
v-for="(segment, tIdx) in row.segments"
139102
:key="'msg-' + tIdx"
140103
>
141-
<template v-if="token.type === 'text'">
142-
{{ token.value }}
104+
<template
105+
v-if="segment.type === 'text'"
106+
>
107+
<span
108+
:style="
109+
getSegmentStyle(segment)
110+
"
111+
>
112+
{{ segment.value }}
113+
</span>
143114
</template>
144115
<br
145-
v-else-if="token.type === 'newline'"
116+
v-else-if="
117+
segment.type === 'newline'
118+
"
146119
/>
147120
<span
148-
v-else-if="token.type === 'tab'"
121+
v-else-if="segment.type === 'tab'"
149122
class="tab-space"
150123
></span>
151-
<span
152-
v-else-if="
153-
token.type === 'placeholder'
154-
"
155-
:style="
156-
getStyleForPlaceholder(
157-
token.value,
158-
row,
159-
)
160-
"
161-
>
162-
<template
163-
v-for="(
164-
segment, segmentIdx
165-
) in getPlaceholderSegments(
166-
token.value,
167-
row,
168-
)"
169-
:key="`placeholder-${tIdx}-${segmentIdx}`"
170-
>
171-
<br
172-
v-if="
173-
token.value ===
174-
'content' &&
175-
segmentIdx > 0
176-
"
177-
/>
178-
{{ segment }}
179-
</template>
180-
</span>
181124
</span>
182125

183126
<span class="message-separator">
184127
<span
185128
v-for="(
186-
token, tIdx
187-
) in messageSeparatorTokens"
129+
segment, tIdx
130+
) in row.trailingSegments"
188131
:key="'sep-' + tIdx"
189132
>
190133
<template
191-
v-if="token.type === 'text'"
134+
v-if="segment.type === 'text'"
192135
>
193-
{{ token.value }}
136+
<span
137+
:style="
138+
getSegmentStyle(segment)
139+
"
140+
>
141+
{{ segment.value }}
142+
</span>
194143
</template>
195144
<br
196145
v-else-if="
197-
token.type === 'newline'
146+
segment.type === 'newline'
198147
"
199148
/>
200149
<span
201-
v-else-if="token.type === 'tab'"
150+
v-else-if="
151+
segment.type === 'tab'
152+
"
202153
class="tab-space"
203154
></span>
204155
</span>
@@ -216,16 +167,19 @@
216167
<script setup lang="ts">
217168
import { computed } from 'vue';
218169
import { Eye, X, SquareSplitHorizontal } from '@lucide/vue';
170+
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
171+
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
172+
173+
import {
174+
renderExportDocument,
175+
type RenderedExportSegment,
176+
} from '@/io/export/exportRender';
177+
import { flattenLogToRows } from '@/io/export/flattener';
178+
import { useExportStore } from '@/stores/exportStore';
219179
import { useLogStore } from '@/stores/logStore';
220180
import { useStyleStore } from '@/stores/styleStore';
221-
import { useExportStore } from '@/stores/exportStore';
222181
import { useUiStore } from '@/stores/uiStore';
223182
import { useWindowStore } from '@/stores/windowStore';
224-
import { flattenLogToRows } from '@/io/export/flattener';
225-
import { parseTemplate, getPlaceholderValue } from '@/io/export/templateParser';
226-
import type { ExportRow } from '@/types/export';
227-
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
228-
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
229183
230184
const logStore = useLogStore();
231185
const styleStore = useStyleStore();
@@ -236,6 +190,7 @@ const props = defineProps<{
236190
windowId: string;
237191
originalId: string;
238192
}>();
193+
239194
const effectiveWindowId = computed(() => props.windowId);
240195
const isActive = computed(() => windowStore.activeFocus === props.windowId);
241196
const activeFormat = computed(
@@ -250,17 +205,19 @@ const canClose = computed(() => {
250205
);
251206
});
252207
253-
// 计算行数据,并为每一行添加 rowId 供虚拟滚动 key 使用
254208
const rowsWithId = computed(() => {
255209
const rawRows = flattenLogToRows(
256210
logStore.documents,
257211
styleStore.viewSettings,
258212
styleStore.activeRules,
259213
);
260-
return rawRows.map((row, index) => ({
261-
...row,
262-
rowId: `${row.type}-${index}`,
263-
}));
214+
215+
return renderExportDocument(rawRows, activeFormat.value).blocks.map(
216+
(block, index) => ({
217+
...block,
218+
rowId: `${block.type}-${index}`,
219+
}),
220+
);
264221
});
265222
266223
// 兼容旧变量名
@@ -289,41 +246,15 @@ function handleClose() {
289246
}
290247
}
291248
292-
const messageTokens = computed(() =>
293-
parseTemplate(activeFormat.value.messageTemplate),
294-
);
295-
const messageSeparatorTokens = computed(() =>
296-
parseTemplate(activeFormat.value.messageSeparator),
297-
);
298-
const docSeparatorTokens = computed(() =>
299-
parseTemplate(activeFormat.value.docSeparator || ''),
300-
);
301-
const chunkSeparatorTokens = computed(() =>
302-
parseTemplate(activeFormat.value.chunkSeparator || ''),
303-
);
304-
305-
function getPlaceholderSegments(key: string, row: ExportRow): string[] {
306-
const value = getPlaceholderValue(key, row, activeFormat.value);
307-
return key === 'content' ? value.split('\n') : [value];
308-
}
309-
310-
function getStyleForPlaceholder(
311-
key: string,
312-
row: ExportRow,
249+
function getSegmentStyle(
250+
segment: RenderedExportSegment,
313251
): Record<string, string> {
314-
const exportStyle =
315-
key === 'name'
316-
? row.nameStyle
317-
: key === 'content'
318-
? row.contentStyle
319-
: undefined;
320-
321-
if (!exportStyle) return {};
252+
if (segment.type !== 'text' || !segment.style) return {};
322253
323254
const css: Record<string, string> = {};
324-
if (exportStyle.color) css.color = exportStyle.color;
325-
if (exportStyle.bold) css.fontWeight = 'bold';
326-
if (exportStyle.italic) css.fontStyle = 'italic';
255+
if (segment.style.color) css.color = segment.style.color;
256+
if (segment.style.bold) css.fontWeight = 'bold';
257+
if (segment.style.italic) css.fontStyle = 'italic';
327258
328259
return css;
329260
}

0 commit comments

Comments
 (0)