mirror of
https://github.com/doocs/md.git
synced 2025-01-22 20:04:39 +08:00
feat: enhance post management (#514)
* fix: extension installation check * perf: optimize post dialog * feat: enhance post management * fix: close #513
This commit is contained in:
parent
5094d90b43
commit
8e8080b228
@ -1,40 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import type { Post, PostAccount } from '@/types'
|
||||
import { useStore } from '@/stores'
|
||||
import { Info } from 'lucide-vue-next'
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { Check, Info } from 'lucide-vue-next'
|
||||
import { CheckboxIndicator, CheckboxRoot, Primitive } from 'radix-vue'
|
||||
|
||||
const store = useStore()
|
||||
const { output } = storeToRefs(store)
|
||||
const { output, editor } = storeToRefs(store)
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const extensionInstalled = ref(false)
|
||||
const form = ref<any>({
|
||||
const allAccounts = ref<PostAccount[]>([])
|
||||
const postTaskDialogVisible = ref(false)
|
||||
|
||||
const form = ref<Post>({
|
||||
title: ``,
|
||||
desc: ``,
|
||||
thumb: ``,
|
||||
content: ``,
|
||||
auto: {},
|
||||
markdown: ``,
|
||||
accounts: [] as PostAccount[],
|
||||
})
|
||||
|
||||
function prePost() {
|
||||
let auto = {}
|
||||
const allowPost = computed(() => extensionInstalled.value && form.value.accounts.some(a => a.checked))
|
||||
|
||||
async function prePost() {
|
||||
let auto: Post = {
|
||||
thumb: ``,
|
||||
title: ``,
|
||||
desc: ``,
|
||||
content: ``,
|
||||
markdown: ``,
|
||||
accounts: [],
|
||||
}
|
||||
try {
|
||||
auto = {
|
||||
thumb: document.querySelector<HTMLImageElement>(`#output img`)?.src,
|
||||
thumb: document.querySelector<HTMLImageElement>(`#output img`)?.src ?? ``,
|
||||
title: [1, 2, 3, 4, 5, 6]
|
||||
.map(h => document.querySelector(`#output h${h}`)!)
|
||||
.filter(h => h)[0]
|
||||
.textContent,
|
||||
desc: document.querySelector(`#output p`)!.textContent,
|
||||
.textContent ?? ``,
|
||||
desc: document.querySelector(`#output p`)!.textContent ?? ``,
|
||||
content: output.value,
|
||||
markdown: editor.value?.getValue() ?? ``,
|
||||
accounts: allAccounts.value,
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log(`error`, error)
|
||||
}
|
||||
form.value = {
|
||||
...auto,
|
||||
auto,
|
||||
finally {
|
||||
form.value = {
|
||||
...auto,
|
||||
}
|
||||
console.log(form.value, `====`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,24 +63,49 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
function post() {
|
||||
dialogVisible.value = false;
|
||||
(window.syncPost)({
|
||||
thumb: form.value.thumb || form.value.auto.thumb,
|
||||
title: form.value.title || form.value.auto.title,
|
||||
desc: form.value.desc || form.value.auto.desc,
|
||||
content: form.value.content || form.value.auto.content,
|
||||
async function getAccounts() {
|
||||
await window.$syncer?.getAccounts((resp: PostAccount[]) => {
|
||||
allAccounts.value = resp.map(a => ({ ...a, checked: true }))
|
||||
})
|
||||
}
|
||||
|
||||
function post() {
|
||||
form.value.accounts = form.value.accounts.filter(a => a.checked)
|
||||
postTaskDialogVisible.value = true
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
function onUpdate(val: boolean) {
|
||||
if (!val) {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
extensionInstalled.value = window.$syncer !== undefined
|
||||
function checkExtension() {
|
||||
if (window.$syncer !== undefined) {
|
||||
extensionInstalled.value = true
|
||||
return
|
||||
}
|
||||
|
||||
// 如果插件还没加载,5秒内每 500ms 检查一次
|
||||
let count = 0
|
||||
const timer = setInterval(() => {
|
||||
if (window.$syncer !== undefined) {
|
||||
extensionInstalled.value = true
|
||||
getAccounts()
|
||||
clearInterval(timer)
|
||||
return
|
||||
}
|
||||
|
||||
count++
|
||||
if (count > 10) { // 5秒后还是没有检测到,就停止检查
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
checkExtension()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -81,7 +124,7 @@ onMounted(() => {
|
||||
<Info class="h-4 w-4" />
|
||||
<AlertTitle>提示</AlertTitle>
|
||||
<AlertDescription>
|
||||
此功能由第三方浏览器插件支持,本平台不保证安全性。
|
||||
此功能由第三方浏览器插件支持,本平台不保证安全性及同步准确度。
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@ -91,9 +134,7 @@ onMounted(() => {
|
||||
<AlertDescription>
|
||||
请安装
|
||||
<Primitive
|
||||
as="a"
|
||||
class="text-blue-500"
|
||||
href="https://www.wechatsync.com/?utm_source=syncicon#install"
|
||||
as="a" class="text-blue-500" href="https://www.wechatsync.com/?utm_source=syncicon#install"
|
||||
target="_blank"
|
||||
>
|
||||
文章同步助手
|
||||
@ -121,14 +162,44 @@ onMounted(() => {
|
||||
<Textarea id="desc" v-model="form.desc" placeholder="自动提取第一个段落" />
|
||||
</div>
|
||||
|
||||
<div class="w-full flex items-start gap-4">
|
||||
<Label class="w-10 text-end">
|
||||
账号
|
||||
</Label>
|
||||
<div class="flex flex-1 flex-col gap-2">
|
||||
<div v-for="account in form.accounts" :key="account.uid + account.displayName" class="flex items-center gap-2">
|
||||
<label class="flex flex-row items-center gap-4">
|
||||
<CheckboxRoot
|
||||
v-model:checked="account.checked"
|
||||
class="bg-background hover:bg-muted h-[25px] w-[25px] flex appearance-none items-center justify-center border border-gray-200 rounded-[4px] outline-none"
|
||||
>
|
||||
<CheckboxIndicator>
|
||||
<Check v-if="account.checked" class="h-4 w-4" />
|
||||
</CheckboxIndicator>
|
||||
</CheckboxRoot>
|
||||
<span class="flex items-center gap-2 text-sm">
|
||||
<img
|
||||
:src="account.icon"
|
||||
alt=""
|
||||
class="inline-block h-[20px] w-[20px]"
|
||||
>
|
||||
{{ account.title }} - {{ account.displayName ?? account.home }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="dialogVisible = false">
|
||||
取 消
|
||||
</Button>
|
||||
<Button :disabled="!extensionInstalled" @click="post">
|
||||
<Button :disabled="!allowPost" @click="post">
|
||||
确 定
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<PostTaskDialog v-model:open="postTaskDialogVisible" :post="form" />
|
||||
</template>
|
||||
|
119
src/components/CodemirrorEditor/EditorHeader/PostTaskDialog.vue
Normal file
119
src/components/CodemirrorEditor/EditorHeader/PostTaskDialog.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import type { Post } from '@/types'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
|
||||
const props = defineProps<{
|
||||
post: Post
|
||||
open: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits([`update:open`])
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.open,
|
||||
set: value => emit(`update:open`, value),
|
||||
})
|
||||
|
||||
const taskStatus = ref<any>(null)
|
||||
const submitting = ref(false)
|
||||
|
||||
async function startPost() {
|
||||
if (!props.post)
|
||||
return
|
||||
|
||||
try {
|
||||
window.$syncer?.addTask(
|
||||
{
|
||||
post: {
|
||||
title: props.post.title,
|
||||
content: props.post.content,
|
||||
markdown: props.post.markdown,
|
||||
thumb: props.post.thumb,
|
||||
desc: props.post.desc,
|
||||
},
|
||||
accounts: props.post.accounts.filter(a => a.checked),
|
||||
},
|
||||
(newStatus: any) => {
|
||||
taskStatus.value = newStatus
|
||||
},
|
||||
() => {
|
||||
submitting.value = false
|
||||
},
|
||||
)
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`发布失败:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.open, (newVal) => {
|
||||
if (newVal) {
|
||||
startPost()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="dialogVisible">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>提交发布任务</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="mt-4">
|
||||
<div v-if="!taskStatus" class="py-4 text-center">
|
||||
等待发布..
|
||||
</div>
|
||||
<div v-else class="max-h-[400px] flex flex-col overflow-y-auto">
|
||||
<div
|
||||
v-for="account in taskStatus?.accounts"
|
||||
:key="account.uid + account.displayName"
|
||||
class="border-b py-4 last:border-b-0"
|
||||
>
|
||||
<div class="mb-2 flex items-center gap-2">
|
||||
<img
|
||||
v-if="account.icon"
|
||||
:src="account.icon"
|
||||
class="object-cover h-5 w-5"
|
||||
alt=""
|
||||
>
|
||||
<span>{{ account.title }} - {{ account.displayName || account.home }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="w-full flex-1 gap-2 overflow-auto pl-7 text-sm" :class="{
|
||||
'text-yellow-600': account.status === 'uploading',
|
||||
'text-red-600': account.status === 'failed',
|
||||
'text-green-600': account.status === 'done',
|
||||
}"
|
||||
>
|
||||
<template v-if="account.status === 'uploading'">
|
||||
{{ account.msg || '发布中' }}
|
||||
</template>
|
||||
|
||||
<template v-if="account.status === 'failed'">
|
||||
同步失败, 错误内容:{{ account.error }}
|
||||
</template>
|
||||
|
||||
<template v-if="account.status === 'done' && account.editResp">
|
||||
同步成功
|
||||
<a
|
||||
v-if="account.type !== 'wordpress' && account.editResp"
|
||||
:href="account.editResp.draftLink"
|
||||
class="ml-2 text-blue-500 hover:underline"
|
||||
referrerPolicy="no-referrer"
|
||||
target="_blank"
|
||||
>查看草稿</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.account-item {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
@ -72,3 +72,26 @@ export interface Alert {
|
||||
text: string
|
||||
tokens: Token[]
|
||||
}
|
||||
|
||||
export interface PostAccount {
|
||||
avatar: string
|
||||
displayName: string
|
||||
home: string
|
||||
icon: string
|
||||
supportTypes: string[]
|
||||
title: string
|
||||
type: string
|
||||
uid: string
|
||||
checked: boolean
|
||||
status?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface Post {
|
||||
title: string
|
||||
desc: string
|
||||
thumb: string
|
||||
content: string
|
||||
markdown: string
|
||||
accounts: PostAccount[]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user