feat: support html copy (#489)
All checks were successful
Build and Deploy / build-and-deploy (push) Has been skipped

close #488
This commit is contained in:
YangFong 2024-12-22 21:24:01 +08:00 committed by GitHub
parent 8a0a6ea4ba
commit 8d620bc693
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 13 deletions

View File

@ -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 />

View 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>

View File

@ -0,0 +1 @@
export { default as Separator } from './Separator.vue'