Skip to content
This repository was archived by the owner on Jun 17, 2025. It is now read-only.

Commit ce07ffd

Browse files
authored
feat: support session markdown export, close #115 (#187)
1 parent e5eb123 commit ce07ffd

File tree

9 files changed

+97
-30
lines changed

9 files changed

+97
-30
lines changed

.eslintrc-auto-import.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
"showWindow": true,
101101
"specialKeys": true,
102102
"storeToRefs": true,
103+
"throttle": true,
103104
"toRaw": true,
104105
"toRef": true,
105106
"toRefs": true,

src/components/Function/components/Export.vue

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,55 @@
11
<script setup lang="ts">
2-
const { isThinking, sessionDataList } = storeToRefs(useSessionStore())
2+
import { sep } from '@tauri-apps/api/path'
3+
import type { SessionData } from '@/types'
4+
5+
const { isThinking, sessionDataList, currentSession } = storeToRefs(
6+
useSessionStore()
7+
)
8+
const { currentRole } = storeToRefs(useRoleStore())
39
410
const disabled = computed(
511
() => isThinking.value || !sessionDataList.value.length
612
)
713
8-
const handleSelect = (value: string) => {
14+
const getRoleName = (item: SessionData) => {
15+
const name = item.is_ask ? '' : currentRole.value?.name
16+
17+
return name + ''
18+
}
19+
20+
const getMDContent = async (item: SessionData) => {
21+
let content = ''
22+
23+
const { message, message_type } = item
24+
25+
if (currentSession.value?.type === 'text') {
26+
content = message.content
27+
} else {
28+
if (message_type === 'text') {
29+
content = message.content.prompt
30+
} else {
31+
for (const image of message.content) {
32+
const imageName = await saveImageFromFile(image.file, true)
33+
34+
content += `\n![图片](.${sep}markdown-images${sep}${imageName})`
35+
}
36+
}
37+
}
38+
39+
return getRoleName(item) + content + '\n\n'
40+
}
41+
42+
const handleSelect = async (value: string) => {
943
if (value === 'image') {
1044
saveImage('session-list')
45+
} else if (value === 'markdown') {
46+
let mdResult = ''
47+
48+
for await (const item of sessionDataList.value) {
49+
mdResult += await getMDContent(item)
50+
}
51+
52+
saveMarkdown(mdResult)
1153
}
1254
}
1355
</script>
@@ -25,7 +67,7 @@ const handleSelect = (value: string) => {
2567
</a-button>
2668
<template #content>
2769
<a-doption value="image">导出图片</a-doption>
28-
<!-- <a-doption value="markdown">导出 Markdown</a-doption> -->
70+
<a-doption value="markdown"> 导出 Markdown </a-doption>
2971
</template>
3072
</a-dropdown>
3173
</template>

src/components/Session/components/SessionContent.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ const position = computed(() => (props.data.is_ask ? 'left' : 'right'))
6969
<a-tooltip content="导出 Markdown">
7070
<div
7171
class="markdown"
72-
:id="`markdown-${data.id}`"
73-
@click="saveMarkdown($event, data.message.content)"
72+
@click="saveMarkdown(data.message.content)"
7473
></div>
7574
</a-tooltip>
7675

src/components/Update/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ onMounted(() => {
7878
7979
listen('update-app', () => checkUpdate(false))
8080
81-
setTimeout(checkUpdate, 1000 * 60 * 60 * 24)
81+
setInterval(checkUpdate, 1000 * 60 * 60 * 24)
8282
})
8383
</script>
8484

src/sqls/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { sep } from '@tauri-apps/api/path'
44
import type { TableName, TablePayload, WherePayload } from '@/types'
55

66
const dbFile = import.meta.env.DEV ? 'sql.dev.db' : 'sql.db'
7-
const db = await Database.load(`sqlite:${await appConfigDir()}${sep}${dbFile}`)
7+
const db = await Database.load(`sqlite:${await appConfigDir()}${dbFile}`)
88

99
/**
1010
* sql 的字符串参数需要在加一个冒号

src/types/shared.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ export type RequestHost = 'OPENAI' | 'GITHUB'
55
export type SaveType = 'system' | 'image' | 'markdown'
66

77
export type TokenUnit = 'TK' | '¢'
8+
9+
export type Timeout = ReturnType<typeof setTimeout> | null

src/utils/saveImage.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
readDir,
77
createDir
88
} from '@tauri-apps/api/fs'
9-
import { appConfigDir, sep } from '@tauri-apps/api/path'
9+
import { appConfigDir, downloadDir, sep } from '@tauri-apps/api/path'
1010
import html2canvas from 'html2canvas'
1111
import type { SaveType } from '@/types'
1212

@@ -58,17 +58,27 @@ export const saveImageFromBase64 = async (base64: string) => {
5858
return fileName
5959
}
6060

61-
export const saveImageFromFile = async (file: string) => {
62-
const targetFile = `${await getName()}-${file}`
61+
export const saveImageFromFile = async (file: string, renderMd = false) => {
62+
let targetFile = `${await getName()}-${file}`
63+
64+
if (renderMd) {
65+
const folder = (await downloadDir()) + 'markdown-images'
66+
67+
await readDir(folder).catch(async () => {
68+
await createDir(folder)
69+
})
70+
71+
targetFile = folder + sep + file
72+
}
6373

6474
// 把对应的图片复制到下载文件夹
65-
await copyFile(
66-
`${await appConfigDir()}${sep}images/${sep}${file}`,
67-
targetFile,
68-
{
69-
dir: BaseDirectory.Download
70-
}
71-
)
75+
await copyFile(`${await appConfigDir()}images${sep}${file}`, targetFile, {
76+
dir: BaseDirectory.Download
77+
})
78+
79+
if (renderMd) {
80+
return file
81+
}
7282

7383
openFilePath(targetFile)
7484

@@ -85,7 +95,7 @@ const writeImage = async (
8595
if (type === 'system') {
8696
if (!fileName) return
8797

88-
const folder = (await appConfigDir()) + sep + 'images'
98+
const folder = (await appConfigDir()) + 'images'
8999

90100
await readDir(folder).catch(async () => {
91101
await createDir(folder)

src/utils/saveMarkdown.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,19 @@
11
import { writeTextFile, BaseDirectory } from '@tauri-apps/api/fs'
22

3-
export const saveMarkdown = async (event: MouseEvent, content: any) => {
3+
export const saveMarkdown = throttle(async (content: any) => {
44
try {
55
const { currentSession } = useSessionStore()
66

7-
const element = event.target as HTMLElement
8-
const uuid = '_' + element.getAttribute('id')
9-
10-
if (window[uuid]) return
11-
window[uuid] = uuid
12-
137
const file = `${currentSession?.title.slice(0, 10)}-${Date.now()}.md`
148

159
await writeTextFile(file, content?.prompt || content, {
1610
dir: BaseDirectory.Download
1711
})
1812

19-
setTimeout(() => {
20-
window[uuid] = null
21-
}, 3000)
22-
2313
openFilePath(file)
2414

2515
Message.success('Markdown 文件导出成功')
2616
} catch (error) {
2717
Message.error('Markdown 文件导出失败,请重试!')
2818
}
29-
}
19+
})

src/utils/shared.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Timeout } from '@/types'
2+
13
/**
24
* 判断数据是否为 null 或者 undefined
35
* @param value 数据
@@ -27,3 +29,24 @@ export const NOOP = () => {
2729
* 获取数组/字符串最后一个元素
2830
*/
2931
export const getLastItem = <T>(arr: T[]) => arr[arr.length - 1]
32+
33+
/**
34+
* 节流
35+
* @param func 回调函数
36+
* @param delay 延迟时间
37+
*/
38+
export const throttle = <T extends (...args: any[]) => void>(
39+
func: T,
40+
delay = 3000
41+
): T => {
42+
let lastTime = 0
43+
44+
return ((...args: any[]) => {
45+
const currentTime = Date.now()
46+
if (currentTime - lastTime > delay) {
47+
lastTime = currentTime
48+
49+
func.apply(this, args)
50+
}
51+
}) as T
52+
}

0 commit comments

Comments
 (0)