Compare commits
2 Commits
35ab6ec79f
...
f2fa1992e6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2fa1992e6 | ||
|
|
94ebd22875 |
10
package-lock.json
generated
10
package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"md-editor-v3": "^5.5.0",
|
||||
"scriptjs": "^2.5.9",
|
||||
"view-ui-plus": "^1.3.19",
|
||||
@ -1641,6 +1642,15 @@
|
||||
"integrity": "sha512-YIfAvArSFVXmWvoF+DEGD0FhkhVNcCtVWWkfYtj76eSrwHh/wuEEFhiEubg1XLNM3tChO8FH8xJCT/hnizjgFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vavt/v3-extension": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vavt/v3-extension/-/v3-extension-3.0.0.tgz",
|
||||
"integrity": "sha512-R3XFrihlk+FmoTEivToszgcv7D168hHkfMyS6pkHWyrvQy0MNP8m8b6qIWuINfvpbhr0OiMa9ktzyUH2DLFWfA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"md-editor-v3": ">=5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"@tauri-apps/plugin-dialog": "^2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@vavt/v3-extension": "^3.0.0",
|
||||
"md-editor-v3": "^5.5.0",
|
||||
"scriptjs": "^2.5.9",
|
||||
"view-ui-plus": "^1.3.19",
|
||||
|
||||
42
src/App.vue
42
src/App.vue
@ -12,7 +12,7 @@
|
||||
<template #right>
|
||||
<div ref="splitRight" class="split-right">
|
||||
<MainEditor ref="mainEditor" v-model:markdownCode="markdownCode"
|
||||
v-model:leftSidebarState="leftSidebarState" :save="writeFileContent">
|
||||
v-model:leftSidebarState="leftSidebarState" :save="writeFileContent" @exportPDF="exportMarkdownPDF">
|
||||
</MainEditor>
|
||||
</div>
|
||||
</template>
|
||||
@ -24,8 +24,9 @@ import LeftSidebar from './components/LeftSidebar.vue'
|
||||
import MainEditor from './components/MainEditor.vue'
|
||||
import { ref, watch, useTemplateRef, nextTick, onMounted, onUnmounted } from "vue";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { readTextFile, writeTextFile, exists } from '@tauri-apps/plugin-fs';
|
||||
import { Message } from 'view-ui-plus'
|
||||
import { readTextFile, writeTextFile, writeFile, exists } from '@tauri-apps/plugin-fs';
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import { Message, Notice } from 'view-ui-plus'
|
||||
|
||||
// 原始示例数据,仅供参考
|
||||
const greetMsg = ref("");
|
||||
@ -79,6 +80,12 @@ watch(currentFilePath, async (newFilePath) => {
|
||||
localStorage.setItem('currentSelectedFilePath', newFilePath);
|
||||
await readFileContent(newFilePath);
|
||||
})
|
||||
const getFileNameFromFilePath = (filePath) => {
|
||||
const fullFileName = filePath.split('/').pop();
|
||||
const splitResult = fullFileName.split(".");
|
||||
splitResult.pop(); // 丢掉后缀
|
||||
return splitResult.join('.');
|
||||
}
|
||||
|
||||
const mainEditor = ref(null);
|
||||
const markdownCode = ref("# Hello Markdown");
|
||||
@ -122,6 +129,35 @@ async function writeFileContent(markdownCode) {
|
||||
Message.error('文件更新失败:' + err);
|
||||
}
|
||||
}
|
||||
async function exportMarkdownPDF(pdfBuffer) {
|
||||
let fileName = 'markdown.pdf';
|
||||
if (currentFilePath.value.length > 0) {
|
||||
fileName = getFileNameFromFilePath(currentFilePath.value) + '.pdf';
|
||||
}
|
||||
|
||||
try {
|
||||
const filePath = await save({
|
||||
title: '保存 PDF', // 对话框标题
|
||||
defaultPath: fileName, // 保存的文件名称
|
||||
});
|
||||
|
||||
if (filePath) {
|
||||
await writeFile(filePath, new Uint8Array(pdfBuffer));
|
||||
Notice.success({
|
||||
title: '导出成功',
|
||||
desc: 'PDF 已保存至 ' + filePath,
|
||||
});
|
||||
} else {
|
||||
console.log('用户主动取消');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Notice.error({
|
||||
title: '导出失败',
|
||||
desc: '请将文件保存在用户有权限的目录之下',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resizeHandler = () => {
|
||||
// splitRight 的宽度为视口宽度右边百分比,再减去 splitTrigger 的宽度
|
||||
|
||||
@ -18,33 +18,15 @@
|
||||
size="22" />
|
||||
</template>
|
||||
</NormalToolbar>
|
||||
<ExportPDF :modelValue="editorState.text" :customize="customizePDF"
|
||||
@onProgress="handleExportProgress" />
|
||||
<NormalToolbar title="发布文章" @onClick="publish">
|
||||
<template #trigger>
|
||||
<Icon class="md-editor-icon" type="md-cloud-upload" size="22" />
|
||||
</template>
|
||||
</NormalToolbar>
|
||||
<NormalToolbar title="切换编辑器主题" @onClick="toggleEditorTheme">
|
||||
<template #trigger>
|
||||
<Icon v-if="editorState.theme == 'dark'" type="ios-sunny-outline" size="22" />
|
||||
<Icon v-else type="md-moon" size="22" />
|
||||
</template>
|
||||
</NormalToolbar>
|
||||
<DropdownToolbar title="切换预览主题" :visible="editorState.previewThemeListVisible"
|
||||
:onChange="showPreviewThemeList">
|
||||
<template #overlay>
|
||||
<div>
|
||||
<List border size="small">
|
||||
<ListItem v-for="theme in editorState.previewThemeList" :key="theme"
|
||||
:class="theme == editorState.previewTheme ? 'md-editor-toolbar-active' : ''"
|
||||
@click="togglePreviewTheme(theme)">{{ theme }}
|
||||
</ListItem>
|
||||
</List>
|
||||
</div>
|
||||
</template>
|
||||
<template #trigger>
|
||||
<Icon class="md-editor-icon" type="ios-color-filter-outline" :size="22" />
|
||||
</template>
|
||||
</DropdownToolbar>
|
||||
<ThemeSwitch v-model="editorState.theme" />
|
||||
<PreviewThemeSwitch v-model="editorState.previewTheme" />
|
||||
</template>
|
||||
</MdEditor>
|
||||
</template>
|
||||
@ -61,7 +43,10 @@
|
||||
|
||||
<script setup>
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import { MdEditor, MdCatalog, NormalToolbar, DropdownToolbar } from 'md-editor-v3';
|
||||
import '@vavt/v3-extension/lib/asset/PreviewThemeSwitch.css';
|
||||
import '@vavt/v3-extension/lib/asset/ExportPDF.css';
|
||||
import { MdEditor, MdCatalog, NormalToolbar } from 'md-editor-v3';
|
||||
import { ThemeSwitch, PreviewThemeSwitch, ExportPDF } from '@vavt/v3-extension';
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { Message } from 'view-ui-plus'
|
||||
|
||||
@ -88,6 +73,7 @@ const props = defineProps({
|
||||
const emit = defineEmits([
|
||||
'update:markdownCode',
|
||||
'update:leftSidebarState',
|
||||
'exportPDF',
|
||||
]);
|
||||
|
||||
// 定义布局,主要用于在右边显示文章目录
|
||||
@ -111,10 +97,8 @@ watch(split, (newSplit) => {
|
||||
const editorRef = ref(null);
|
||||
const editorState = reactive({
|
||||
id: 'markdown-editor',
|
||||
theme: '',
|
||||
theme: 'light',
|
||||
previewTheme: 'default',
|
||||
previewThemeList: ['default', 'github', 'vuepress', 'mk-cute', 'smart-blue', 'cyanosis'],
|
||||
previewThemeListVisible: false,
|
||||
height: '100vh',
|
||||
text: props.markdownCode,
|
||||
toolbars: [
|
||||
@ -146,6 +130,7 @@ const editorState = reactive({
|
||||
'next',
|
||||
'save',
|
||||
2,
|
||||
3,
|
||||
'=',
|
||||
'prettier',
|
||||
'pageFullscreen',
|
||||
@ -155,10 +140,11 @@ const editorState = reactive({
|
||||
'htmlPreview',
|
||||
'catalog',
|
||||
'github',
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
],
|
||||
toolbarsExclude: ['fullscreen', 'github'], // 工具栏排除全屏和 github 等
|
||||
pdfIns: null, // 用于存储 PDF 导出时候的 jsPDF 实例
|
||||
});
|
||||
// 监听 markdownCode(由父组件更新) 和 text(本组件更新)以实现双向更新
|
||||
watch(() => props.markdownCode, (newCode) => {
|
||||
@ -186,19 +172,6 @@ const toggleCatalog = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const toggleEditorTheme = () => {
|
||||
editorState.theme = editorState.theme == '' ? 'dark' : '';
|
||||
}
|
||||
|
||||
const showPreviewThemeList = (visible) => {
|
||||
editorState.previewThemeListVisible = visible;
|
||||
}
|
||||
|
||||
const togglePreviewTheme = (theme) => {
|
||||
editorState.previewTheme = theme;
|
||||
editorState.previewThemeListVisible = false;
|
||||
}
|
||||
|
||||
const onSave = (v, h) => {
|
||||
// 调用 save 方法保存代码(参数 v 为 markdown code)
|
||||
props.save(v);
|
||||
@ -213,6 +186,26 @@ const getPreviewedHTML = (html) => {
|
||||
console.log(html);
|
||||
}
|
||||
|
||||
const customizePDF = (ins) => {
|
||||
// 将 jsPDF 的实例记录到 editorState.pdfIns 便于后面获取
|
||||
editorState.pdfIns = ins;
|
||||
}
|
||||
|
||||
const handleExportProgress = (progress) => {
|
||||
// console.log(`Export progress: ${progress.ratio * 100}%`);
|
||||
// 当进度到达 100% 时,触发保存事件。
|
||||
if (progress.ratio == 1) {
|
||||
// 对于浏览器环境,ExportPDF 插件中已实现调用保存文件的窗口
|
||||
if (window.__TAURI_INTERNALS__ === undefined) {
|
||||
return;
|
||||
}
|
||||
// 非浏览器环境(tauri 环境)需要主动调用系统 API,调出对话框并保存文件
|
||||
if (editorState.pdfIns !== null) {
|
||||
emit('exportPDF', editorState.pdfIns.output('arraybuffer'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const publish = () => {
|
||||
// TODO 调用接口将 markdown 源码或者 HTML 代码发布到云端
|
||||
Message.warning('抱歉,该功能暂未开放');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user