diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7c983080..b8213e5a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,33 +2,27 @@ module.exports = { extends: [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "plugin:svelte/recommended", + "plugin:vue/vue3-recommended", "turbo", "prettier", ], - parser: "@typescript-eslint/parser", - - overrides: [ - { - files: ["*.svelte"], - parser: "svelte-eslint-parser", - // Parse the script in `.svelte` as TypeScript by adding the following configuration. - parserOptions: { - parser: "@typescript-eslint/parser", - }, - }, - ], + // https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser + parser: "vue-eslint-parser", + parserOptions: { + parser: "@typescript-eslint/parser", + }, - plugins: ["@typescript-eslint", "prettier"], + plugins: ["@typescript-eslint", "vue", "prettier"], rules: { // Note: you must disable the base rule as it can report incorrect errors semi: "off", quotes: "off", "no-undef": "off", - "no-async-promise-executor": "off", + "vue/no-v-htm": "off", "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/no-this-alias": "off", "@typescript-eslint/no-non-null-assertion": "off", diff --git a/.prettierrc.cjs b/.prettierrc.cjs index e75550a8..34161526 100644 --- a/.prettierrc.cjs +++ b/.prettierrc.cjs @@ -26,6 +26,5 @@ module.exports = { semi: false, singleQuote: false, - printWidth: 120, - plugins: ["prettier-plugin-svelte"] + printWidth: 120 } diff --git a/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts b/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts index 8d35ac99..ffaad435 100644 --- a/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts +++ b/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts @@ -29,6 +29,7 @@ import { AppInstance } from "~/src/appInstance.ts" import { createAppLogger } from "~/src/utils/appLogger.ts" import { CnblogsConstants } from "~/src/adaptors/api/cnblogs/cnblogsConstants.ts" import { MetaweblogBlogApi } from "~/src/adaptors/api/base/metaweblog/metaweblogBlogApi.ts" +import { StrUtil } from "zhi-common" /** * 博客园 API 适配器 @@ -64,7 +65,8 @@ class CnblogsApiAdaptor extends MetaweblogBlogApi { } public override async getPreviewUrl(postid: string): Promise { - return this.cfg.previewUrl.replace(/\[postid\]/g, postid) + const previewUrl = this.cfg.previewUrl.replace(/\[postid\]/g, postid) + return StrUtil.pathJoin(this.cfg.home ?? "", previewUrl) } public override async deletePost(postid: string): Promise { diff --git a/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts b/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts index c90bb86e..6e923653 100644 --- a/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts +++ b/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts @@ -23,11 +23,11 @@ * questions. */ -import { BlogApi, CategoryInfo, Post, UserBlog } from "zhi-blog-api" -import { YuqueApi } from "~/src/adaptors/api/yuque/adaptor/yuqueApi.ts" -import { YuqueConfig } from "~/src/adaptors/api/yuque/config/yuqueConfig.ts" -import { createAppLogger } from "~/src/utils/appLogger.ts" -import { StrUtil } from "zhi-common" +import {BlogApi, CategoryInfo, Post, UserBlog} from "zhi-blog-api" +import {YuqueApi} from "~/src/adaptors/api/yuque/adaptor/yuqueApi.ts" +import {YuqueConfig} from "~/src/adaptors/api/yuque/config/yuqueConfig.ts" +import {createAppLogger} from "~/src/utils/appLogger.ts" +import {StrUtil} from "zhi-common" /** * Yuque API 适配器 @@ -146,7 +146,6 @@ class YuqueApiAdaptor extends BlogApi { const docId = yuquePostidKey.docId const repo = yuquePostidKey.docRepo ?? this.cfg.blogid ?? "" const postUrl = purl.replace("[postid]", docId).replace("[notebook]", repo) - // 路径组合 return StrUtil.pathJoin(this.cfg.home ?? "", postUrl) } diff --git a/src/components/publish/PublishIndex.vue b/src/components/publish/PublishIndex.vue index 304e586e..2090dc0e 100644 --- a/src/components/publish/PublishIndex.vue +++ b/src/components/publish/PublishIndex.vue @@ -27,28 +27,34 @@ import { createAppLogger } from "~/src/utils/appLogger.ts" import PublishTips from "~/src/components/publish/form/PublishTips.vue" import PublishPlatform from "~/src/components/publish/form/PublishPlatform.vue" -import PublishTags from "~/src/components/publish/form/PublishTags.vue" -import { onMounted, reactive } from "vue" +import { markRaw, onMounted, reactive } from "vue" import { useRoute } from "vue-router" import { usePublish } from "~/src/composables/usePublish.ts" import { getWidgetId } from "~/src/utils/widgetUtils.ts" import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts" import { Post } from "zhi-blog-api" import { useVueI18n } from "~/src/composables/useVueI18n.ts" -import { ElMessage } from "element-plus" +import { ElMessage, ElMessageBox } from "element-plus" +import { StrUtil } from "zhi-common" +import { pre } from "~/src/utils/import/pre.ts" +import { Delete } from "@element-plus/icons-vue" const logger = createAppLogger("publisher-index") // uses const { t } = useVueI18n() -const { doSinglePublish } = usePublish() +const { doSinglePublish, doSingleDelete } = usePublish() const { blogApi } = useSiyuanApi() const { query } = useRoute() // datas +const sysKeys = pre.systemCfg.map((item) => { + return item.platformKey +}) const id = (query.id ?? getWidgetId()) as string const formData = reactive({ isPublishLoading: false, + isDeleteLoading: false, showProcessResult: false, doc: {} as Post, @@ -92,6 +98,54 @@ const handlePublish = async () => { } } +const handleDelete = async () => { + ElMessageBox.confirm(`确认要删除平台 ${formData.dynList.join("、")} 下面的文章吗,此平台文章数据也将永久删除 [注意:系统内置平台会忽略,不做删除] ?`, "温馨提示", { + type: "error", + icon: markRaw(Delete), + confirmButtonText: t("main.opt.ok"), + cancelButtonText: t("main.opt.cancel"), + }) + .then(async () => { + await doDelete() + }) + .catch(() => {}) +} + +const doDelete = async () => { + try { + formData.isDeleteLoading = true + if (formData.dynList.length === 0) { + throw new Error("必须选择一个分发平台") + } + + formData.errCount = 0 + formData.failBatchResults = [] + formData.successBatchResults = [] + for (const key of formData.dynList) { + if (sysKeys.includes(key)) { + logger.info(`[${key}] 系统内置平台,不可删除,跳过`) + continue + } + const batchResult = await doSingleDelete(key, id) + if (!batchResult.status) { + formData.failBatchResults.push(batchResult) + formData.errCount++ + } + } + + formData.showProcessResult = true + if (formData.errCount === 0) { + ElMessage.success("多平台文章删除成功") + } else { + ElMessage.error(`多平台文章删除失败,失败个数:${formData.errCount}`) + } + } catch (error) { + ElMessage.error(error.message) + } finally { + formData.isDeleteLoading = false + } +} + const syncDynList = (selectedKeys: string[]) => { formData.dynList = selectedKeys } @@ -118,7 +172,7 @@ onMounted(async () => { 错误平台个数 [{{ formData.errCount }}] ,选择的总平台数 [{{ formData.dynList.length }}] 。
- [{{ errRet.key }}] [{{ errRet.name }}] {{ errRet.errMsg }} + [{{ errRet.key }}] {{ StrUtil.isEmptyString(errRet.name) ? "" : `[${errRet.name}]` }} {{ errRet.errMsg }}
已分发成功的结果如下: @@ -153,11 +207,13 @@ onMounted(async () => { --> - + {{ t("main.publish") }} - {{ t("main.cancel") }} + + {{ t("main.cancel") }} +
@@ -173,6 +229,9 @@ onMounted(async () => { .platform color var(--el-color-info) +:deep(.form-action .el-form-item__content) + margin-left 0 !important + .error-total-msg margin 10px 0 @@ -183,4 +242,4 @@ onMounted(async () => { color var(--el-color-success) .fail-tips color var(--el-color-error) - + \ No newline at end of file diff --git a/src/components/publish/form/PublishPlatform.vue b/src/components/publish/form/PublishPlatform.vue index b4f231e3..e6a8a2b7 100644 --- a/src/components/publish/form/PublishPlatform.vue +++ b/src/components/publish/form/PublishPlatform.vue @@ -50,6 +50,10 @@ const formData = reactive({ const emit = defineEmits(["emitSyncDynList"]) +if (emit) { + logger.info("selectedKeys=>", formData.selectedKeys) + emit("emitSyncDynList", formData.selectedKeys) +} const handleCheck = (key: string) => { if (formData.selectedKeys.includes(key)) { // 如果 formData.selectedKeys 数组中包含 key,则从数组中删除 key diff --git a/src/components/publish/form/PublishTips.vue b/src/components/publish/form/PublishTips.vue index 5d8a8c2d..77db53ef 100644 --- a/src/components/publish/form/PublishTips.vue +++ b/src/components/publish/form/PublishTips.vue @@ -27,7 +27,7 @@ diff --git a/src/composables/usePublish.ts b/src/composables/usePublish.ts index 036db3f5..7eeabfd2 100644 --- a/src/composables/usePublish.ts +++ b/src/composables/usePublish.ts @@ -109,7 +109,8 @@ const usePublish = () => { logger.info("new post=>", result) } const previewUrl = await api.getPreviewUrl(singleFormData.postid) - singleFormData.previewUrl = `${singleFormData.cfg.home}${previewUrl}` + const isAbsoluteUrl = /^http/.test(previewUrl); + singleFormData.previewUrl = isAbsoluteUrl ? previewUrl : `${singleFormData.cfg.home}${previewUrl}`; singleFormData.publishProcessStatus = true } catch (e) { @@ -132,9 +133,66 @@ const usePublish = () => { } } + const doSingleDelete = async (key: string, id: string) => { + try { + // 加载配置 + singleFormData.setting = await getSetting() + singleFormData.cfg = JsonUtil.safeParse(singleFormData.setting[key], {} as any) + + // 检测是否发布 + const posidKey = singleFormData.cfg.posidKey + if (StrUtil.isEmptyString(posidKey)) { + throw new Error("配置错误,posidKey不能为空,请检查配置") + } + + const postMeta = singleFormData.setting[id] ?? {} + const postid = postMeta[posidKey] ?? "" + if (StrUtil.isEmptyString(postid)) { + throw new Error("未找到postid,无法删除,请手动在平台删除") + } + + // 初始化API + const appInstance = new AppInstance() + const apiAdaptor = await Adaptors.getAdaptor(key) + const api = Utils.blogApi(appInstance, apiAdaptor) + logger.info("api=>", api) + + singleFormData.publishProcessStatus = await api.deletePost(postid) + } catch (e) { + singleFormData.errMsg = t("main.opt.failure") + "=>" + e + logger.error(e) + // ElMessage.error(singleFormData.errMsg) + await kernelApi.pushErrMsg({ + msg: singleFormData.errMsg, + timeout: 7000, + }) + } + + // 移除文章发布信息 + const posidKey = singleFormData.cfg.posidKey + if (!StrUtil.isEmptyString(posidKey)) { + const postMeta = singleFormData.setting[id] ?? {}; + const updatedPostMeta = { ...postMeta }; + if (updatedPostMeta.hasOwnProperty(posidKey)) { + delete updatedPostMeta[posidKey]; + } + + singleFormData.setting[id] = updatedPostMeta; + await updateSetting(singleFormData.setting) + logger.info(`[${key}] [${id}] 文章发布信息已移除`) + } + + return { + key: key, + status: singleFormData.publishProcessStatus, + errMsg: singleFormData.errMsg, + } + } + return { singleFormData, doSinglePublish, + doSingleDelete, } }