mirror of
https://github.com/doocs/md.git
synced 2025-01-23 12:24:43 +08:00
Compare commits
3 Commits
71dd0d8118
...
fbe6421cbd
Author | SHA1 | Date | |
---|---|---|---|
|
fbe6421cbd | ||
|
8d620bc693 | ||
|
67ef9dc21c |
@ -14,8 +14,8 @@ import {
|
|||||||
themeOptions,
|
themeOptions,
|
||||||
} from '@/config'
|
} from '@/config'
|
||||||
import { useDisplayStore, useStore } from '@/stores'
|
import { useDisplayStore, useStore } from '@/stores'
|
||||||
import { mergeCss, solveWeChatImage } from '@/utils'
|
import { addPrefix, mergeCss, solveWeChatImage } from '@/utils'
|
||||||
import { Moon, PanelLeftClose, PanelLeftOpen, Settings, Sun } from 'lucide-vue-next'
|
import { ChevronDownIcon, Moon, PanelLeftClose, PanelLeftOpen, Settings, Sun } from 'lucide-vue-next'
|
||||||
import PickColors from 'vue-pick-colors'
|
import PickColors from 'vue-pick-colors'
|
||||||
|
|
||||||
const emit = defineEmits([`addFormat`, `formatContent`, `startCopy`, `endCopy`])
|
const emit = defineEmits([`addFormat`, `formatContent`, `startCopy`, `endCopy`])
|
||||||
@ -60,6 +60,10 @@ const { isDark, isCiteStatus, output, primaryColor } = storeToRefs(store)
|
|||||||
|
|
||||||
const { toggleDark, editorRefresh, citeStatusChanged } = store
|
const { toggleDark, editorRefresh, citeStatusChanged } = store
|
||||||
|
|
||||||
|
const copyMode = useStorage(addPrefix(`copyMode`), `txt`)
|
||||||
|
const source = ref(``)
|
||||||
|
const { copy: copyContent } = useClipboard({ source })
|
||||||
|
|
||||||
// 复制到微信公众号
|
// 复制到微信公众号
|
||||||
function copy() {
|
function copy() {
|
||||||
emit(`startCopy`)
|
emit(`startCopy`)
|
||||||
@ -85,7 +89,7 @@ function copy() {
|
|||||||
toggleDark()
|
toggleDark()
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(async () => {
|
||||||
solveWeChatImage()
|
solveWeChatImage()
|
||||||
|
|
||||||
const clipboardDiv = document.getElementById(`output`)!
|
const clipboardDiv = document.getElementById(`output`)!
|
||||||
@ -128,21 +132,30 @@ function copy() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.getSelection()!.removeAllRanges()
|
window.getSelection()!.removeAllRanges()
|
||||||
const range = document.createRange()
|
|
||||||
|
|
||||||
range.setStartBefore(clipboardDiv.firstChild!)
|
const temp = clipboardDiv.innerHTML
|
||||||
range.setEndAfter(clipboardDiv.lastChild!)
|
|
||||||
window.getSelection()!.addRange(range)
|
if (copyMode.value === `txt`) {
|
||||||
document.execCommand(`copy`)
|
const range = document.createRange()
|
||||||
window.getSelection()!.removeAllRanges()
|
range.setStartBefore(clipboardDiv.firstChild!)
|
||||||
|
range.setEndAfter(clipboardDiv.lastChild!)
|
||||||
|
window.getSelection()!.addRange(range)
|
||||||
|
document.execCommand(`copy`)
|
||||||
|
window.getSelection()!.removeAllRanges()
|
||||||
|
}
|
||||||
|
|
||||||
clipboardDiv.innerHTML = output.value
|
clipboardDiv.innerHTML = output.value
|
||||||
|
|
||||||
if (isBeforeDark) {
|
if (isBeforeDark) {
|
||||||
nextTick(() => toggleDark())
|
nextTick(() => toggleDark())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (copyMode.value === `html`) {
|
||||||
|
await copyContent(temp)
|
||||||
|
}
|
||||||
|
|
||||||
// 输出提示
|
// 输出提示
|
||||||
toast.success(`已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴`)
|
toast.success(copyMode.value === `html` ? `已复制 HTML 源码,请进行下一步操作。` : `已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴。`)
|
||||||
|
|
||||||
editorRefresh()
|
editorRefresh()
|
||||||
emit(`endCopy`)
|
emit(`endCopy`)
|
||||||
@ -424,9 +437,34 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
|
|||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
<Button variant="outline" class="mx-2" @click="copy">
|
|
||||||
复制
|
<div class="space-x-1 bg-background text-background-foreground mx-2 flex items-center border rounded-md">
|
||||||
</Button>
|
<Button variant="ghost" class="shadow-none" @click="copy">
|
||||||
|
复制
|
||||||
|
</Button>
|
||||||
|
<Separator orientation="vertical" class="h-5" />
|
||||||
|
<DropdownMenu v-model="copyMode">
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<Button variant="ghost" class="px-2 shadow-none">
|
||||||
|
<ChevronDownIcon class="text-secondary-foreground h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
align="end"
|
||||||
|
:align-offset="-5"
|
||||||
|
class="w-[200px]"
|
||||||
|
>
|
||||||
|
<DropdownMenuRadioGroup v-model="copyMode">
|
||||||
|
<DropdownMenuRadioItem value="txt">
|
||||||
|
公众号格式
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="html">
|
||||||
|
HTML 格式
|
||||||
|
</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
<PostInfo />
|
<PostInfo />
|
||||||
|
|
||||||
|
35
src/components/ui/separator/Separator.vue
Normal file
35
src/components/ui/separator/Separator.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { Separator, type SeparatorProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<
|
||||||
|
SeparatorProps & { class?: HTMLAttributes[`class`], label?: string }
|
||||||
|
>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Separator
|
||||||
|
v-bind="delegatedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'shrink-0 bg-border relative',
|
||||||
|
props.orientation === 'vertical' ? 'w-px h-full' : 'h-px w-full',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="props.label"
|
||||||
|
:class="cn('text-xs text-muted-foreground bg-background absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 flex justify-center items-center',
|
||||||
|
props.orientation === 'vertical' ? 'w-[1px] px-1 py-2' : 'h-[1px] py-1 px-2',
|
||||||
|
)"
|
||||||
|
>{{ props.label }}</span>
|
||||||
|
</Separator>
|
||||||
|
</template>
|
1
src/components/ui/separator/index.ts
Normal file
1
src/components/ui/separator/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Separator } from './Separator.vue'
|
@ -170,12 +170,15 @@ watch(isDark, () => {
|
|||||||
toRaw(editor.value)?.setOption?.(`theme`, theme)
|
toRaw(editor.value)?.setOption?.(`theme`, theme)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const charCount = ref(0)
|
||||||
|
|
||||||
// 初始化编辑器
|
// 初始化编辑器
|
||||||
function initEditor() {
|
function initEditor() {
|
||||||
const editorDom = document.querySelector<HTMLTextAreaElement>(`#editor`)!
|
const editorDom = document.querySelector<HTMLTextAreaElement>(`#editor`)!
|
||||||
|
|
||||||
if (!editorDom.value) {
|
if (!editorDom.value) {
|
||||||
editorDom.value = store.posts[store.currentPostIndex].content
|
editorDom.value = store.posts[store.currentPostIndex].content
|
||||||
|
charCount.value = store.posts[store.currentPostIndex].content.replace(/\s/g, ``).length
|
||||||
}
|
}
|
||||||
editor.value = CodeMirror.fromTextArea(editorDom, {
|
editor.value = CodeMirror.fromTextArea(editorDom, {
|
||||||
mode: `text/x-markdown`,
|
mode: `text/x-markdown`,
|
||||||
@ -222,7 +225,9 @@ function initEditor() {
|
|||||||
clearTimeout(changeTimer.value)
|
clearTimeout(changeTimer.value)
|
||||||
changeTimer.value = setTimeout(() => {
|
changeTimer.value = setTimeout(() => {
|
||||||
onEditorRefresh()
|
onEditorRefresh()
|
||||||
store.posts[store.currentPostIndex].content = e.getValue()
|
const value = e.getValue()
|
||||||
|
store.posts[store.currentPostIndex].content = value
|
||||||
|
charCount.value = value.replace(/\s/g, ``).length
|
||||||
}, 300)
|
}, 300)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -414,23 +419,28 @@ onMounted(() => {
|
|||||||
</ContextMenuContent>
|
</ContextMenuContent>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="relative flex-1">
|
||||||
id="preview"
|
<div
|
||||||
ref="preview"
|
id="preview"
|
||||||
:span="isShowCssEditor ? 8 : 12"
|
ref="preview"
|
||||||
class="preview-wrapper flex-1 p-5"
|
:span="isShowCssEditor ? 8 : 12"
|
||||||
>
|
class="preview-wrapper flex-1 p-5"
|
||||||
<div id="output-wrapper" :class="{ output_night: !backLight }">
|
>
|
||||||
<div class="preview border shadow-xl">
|
<div id="output-wrapper" :class="{ output_night: !backLight }">
|
||||||
<section id="output" v-html="output" />
|
<div class="preview border shadow-xl">
|
||||||
<div v-if="isCoping" class="loading-mask">
|
<section id="output" v-html="output" />
|
||||||
<div class="loading-mask-box">
|
<div v-if="isCoping" class="loading-mask">
|
||||||
<div class="loading__img" />
|
<div class="loading-mask-box">
|
||||||
<span>正在生成</span>
|
<div class="loading__img" />
|
||||||
|
<span>正在生成</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bg-muted absolute bottom-0 left-0 p-2 text-xs shadow">
|
||||||
|
{{ charCount }} 个字符
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CssEditor class="flex-1" />
|
<CssEditor class="flex-1" />
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user