Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance post management #514

Merged
merged 3 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 98 additions & 27 deletions src/components/CodemirrorEditor/EditorHeader/PostInfo.vue
Original file line number Diff line number Diff line change
@@ -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, `====`)
}
}

Expand All @@ -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>

Expand All @@ -81,7 +124,7 @@ onMounted(() => {
<Info class="h-4 w-4" />
<AlertTitle>提示</AlertTitle>
<AlertDescription>
此功能由第三方浏览器插件支持,本平台不保证安全性
此功能由第三方浏览器插件支持,本平台不保证安全性及同步准确度
</AlertDescription>
</Alert>

Expand All @@ -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"
>
文章同步助手
Expand Down Expand Up @@ -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 changes: 119 additions & 0 deletions src/components/CodemirrorEditor/EditorHeader/PostTaskDialog.vue
Original file line number Diff line number Diff line change
@@ -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>
23 changes: 23 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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