Skip to content

Commit

Permalink
feat: 支持文章分类-多选分类
Browse files Browse the repository at this point in the history
  • Loading branch information
terwer committed Aug 19, 2023
1 parent 3d031b9 commit f0bc264
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 42 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ Publish articles from siyuan-note to platforms such as Yuque, Notion, Cnblogs, W

Support features such as fast publishing, image bed management, platform expansion, smart labels, etc.

> **Front Announcement 1: Publish Tool's first version that supports release views `1.9.0` is released!**
> **Front Announcement 1: `1.9.0` version of the Publish Tool, the first to support release view and AI beta early adopter version, is released!**
>
> **Note: Due to cost relationships, AI features will be charged after the end of the Common Test.**
>
> **Front Announcement 2: The version `1.8.0` that fully supports image upload for all platforms and the forward agent using siyuan-note is released!**
Tips: Zhihu uses the image upload of the Zhihu platform, Yuque, Notion, and Hexo need Picgo plugin support, and the Metaweblog series platform supports both Picgo plugin and self-contained image upload (install Picgo plugin to use Picgo plugin, otherwise use their own platform)

We recommend that you use the `uninstall->install` method to update. If the configuration is abnormal, please back up `[workspace]/data/storage/syp/sy-p-plus-cfg.json` , and then delete it, this configuration file will be automatically initialized the first time it is used.
We recommend that you use the `uninstall->install` method to update. If the configuration is abnormal, please backup `[workspace]/data/storage/syp/sy-p-plus-cfg.json` , and then delete it, this configuration file will be automatically initialized the first time it is used.

In later releases, the release configuration will only be backward compatible to `1.6.0+`.

Expand Down
4 changes: 3 additions & 1 deletion README_zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

将思源笔记的文章发布到语雀、Notion、Cnblogs、WordPress、Typecho、Hexo、知乎 等平台,支持极速发布、图床管理、平台扩展、智能标签等特色功能。

> **前排公告1:发布工具首个支持发布视图的版本 `1.9.0` 发布!**
> **前排公告1:发布工具首个支持发布视图以及AI公测尝鲜版的版本 `1.9.0` 发布!**
>
> **注意:鉴于成本关系,AI功能在公测结束后会收费。**
>
> **前排公告2:发布工具完整支持图片上传以及全平台使用思源笔记正向代理的版本 `1.8.0` 发布!**
Expand Down
13 changes: 9 additions & 4 deletions src/adaptors/api/base/metaweblog/metaweblogBlogApiAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,15 @@ class MetaweblogBlogApiAdaptor extends BaseBlogApi {
this.logger.debug("获取的分类信息,dataArr=>", dataArr)

dataArr.forEach((item: any) => {
const cat = new CategoryInfo()
cat.description = item.description
cat.categoryId = item.categoryId
result.push(cat)
const cate = new CategoryInfo()
cate.categoryId = item.categoryId
cate.categoryName = item.categoryName
cate.description = item.description
cate.categoryDescription = item.categoryDescription
cate.htmlUrl = item.htmlUrl
cate.parentId = item.parentId
cate.rssUrl = item.rssUrl
result.push(cate)
})
} catch (e) {
this.logger.error("分类获取失败", e)
Expand Down
46 changes: 40 additions & 6 deletions src/adaptors/api/cnblogs/cnblogsApiAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
* questions.
*/

import { Post, UserBlog } from "zhi-blog-api"
import { CategoryInfo, Post, UserBlog } from "zhi-blog-api"
import { CnblogsConfig } from "~/src/adaptors/api/cnblogs/cnblogsConfig.ts"
import { AppInstance } from "~/src/appInstance.ts"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import { CnblogsConstants } from "~/src/adaptors/api/cnblogs/cnblogsConstants.ts"
import { MetaweblogBlogApiAdaptor } from "~/src/adaptors/api/base/metaweblog/metaweblogBlogApiAdaptor.ts"
import { MetaweblogConstants } from "~/src/adaptors/api/base/metaweblog/metaweblogConstants.ts"

/**
* 博客园 API 适配器
Expand All @@ -40,6 +41,8 @@ import { MetaweblogBlogApiAdaptor } from "~/src/adaptors/api/base/metaweblog/met
* @since 0.9.0
*/
class CnblogsApiAdaptor extends MetaweblogBlogApiAdaptor {
private readonly MD_CATEGORY = "[Markdown]"

/**
* 初始化博客园 API 适配器
*
Expand Down Expand Up @@ -88,12 +91,43 @@ class CnblogsApiAdaptor extends MetaweblogBlogApiAdaptor {
return ret
}

private assignMdCategory(post: Post) {
const MD_CATEGORY = "[Markdown]"
const cats = post.categories ?? []
public override async getCategories(): Promise<CategoryInfo[]> {
const result = [] as CategoryInfo[]

try {
const ret = await this.metaweblogCall(MetaweblogConstants.METHOD_GET_CATEGORIES, [
this.cfg.blogid,
this.cfg.username,
this.cfg.password,
])
const dataArr = ret
this.logger.debug("博客园获取的分类信息,dataArr=>", dataArr)

dataArr.forEach((item: any) => {
const cate = new CategoryInfo()
// MD 分类默认不展示
if (item.title !== this.MD_CATEGORY) {
cate.categoryId = item.categoryid
cate.categoryName = item.title
cate.description = item.description
cate.categoryDescription = item.categoryDescription
cate.htmlUrl = item.htmlUrl
cate.parentId = item.parentId
cate.rssUrl = item.rssUrl
result.push(cate)
}
})
} catch (e) {
this.logger.error("博客园分类获取失败", e)
}

if (cats.length === 0 || cats.some((cat) => cat.toLowerCase() === MD_CATEGORY.toLowerCase())) {
cats.push(MD_CATEGORY)
return result
}

private assignMdCategory(post: Post) {
const cates = post.categories ?? []
if (!cates.some((cate) => cate.toLowerCase() === this.MD_CATEGORY.toLowerCase())) {
cates.push(this.MD_CATEGORY)
}

return post
Expand Down
2 changes: 1 addition & 1 deletion src/adaptors/api/cnblogs/useCnblogsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const useCnblogsApi = async (key?: string, newCfg?: CnblogsConfig) => {
}
}

// 知乎使用单选分类作为专栏
// 博客园使用多选分类
cfg.categoryType = CategoryTypeEnum.CategoryType_Multi
cfg.cateAllowChange = true

Expand Down
5 changes: 5 additions & 0 deletions src/adaptors/api/typecho/useTypechoApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { TypechoConfig } from "~/src/adaptors/api/typecho/typechoConfig.ts"
import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common"
import { getDynPostidKey } from "~/src/platforms/dynamicConfig.ts"
import { TypechoApiAdaptor } from "~/src/adaptors/api/typecho/typechoApiAdaptor.ts"
import {CategoryTypeEnum} from "zhi-blog-api";

/**
* 使用Typecho API的自定义hook
Expand Down Expand Up @@ -82,6 +83,10 @@ export const useTypechoApi = async (key?: string, newCfg?: TypechoConfig) => {
}
}

// Typecho使用多选分类
cfg.categoryType = CategoryTypeEnum.CategoryType_Multi
cfg.cateAllowChange = true

// 创建Typecho API适配器
const blogApi = new TypechoApiAdaptor(appInstance, cfg)
logger.info("Typecho API created successfully.", cfg)
Expand Down
5 changes: 5 additions & 0 deletions src/adaptors/api/wordpress/useWordpressApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common"
import { WordpressConfig } from "~/src/adaptors/api/wordpress/wordpressConfig.ts"
import { WordpressApiAdaptor } from "~/src/adaptors/api/wordpress/wordpressApiAdaptor.ts"
import { getDynPostidKey } from "~/src/platforms/dynamicConfig.ts"
import {CategoryTypeEnum} from "zhi-blog-api";

/**
* 使用Wordpress API的自定义hook
Expand Down Expand Up @@ -82,6 +83,10 @@ export const useWordpressApi = async (key?: string, newCfg?: WordpressConfig) =>
}
}

// WordPress使用多选分类
cfg.categoryType = CategoryTypeEnum.CategoryType_Multi
cfg.cateAllowChange = true

// 创建Wordpress API适配器
const blogApi = new WordpressApiAdaptor(appInstance, cfg)
logger.info("Wordpress API created successfully.", cfg)
Expand Down
11 changes: 9 additions & 2 deletions src/components/publish/BatchPublishIndex.vue
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,12 @@ const syncTags = (val: string[]) => {
logger.debug("syncTags in batch publish")
}
const syncCates = (cates: string[], cateSlugs: string[]) => {
formData.siyuanPost.categories = cates
formData.siyuanPost.cate_slugs = cateSlugs
logger.debug("syncCates in batch publish")
}
const syncPublishTime = (val1: Date, val2: Date) => {
formData.siyuanPost.dateCreated = val1
formData.siyuanPost.dateUpdated = val2
Expand All @@ -261,7 +267,7 @@ onMounted(async () => {
// 分类数据初始化
formData.categoryConfig = {
cateEnabled: true,
readonlyMode: true,
readonlyMode: false,
readonlyModeTip: t("category.batch.not.supported"),
pageId: id,
categories: formData.siyuanPost.categories,
Expand Down Expand Up @@ -362,8 +368,9 @@ onMounted(async () => {

<!-- 分类 -->
<publish-categories
v-model:category-type="CategoryTypeEnum.CategoryType_Single"
v-model:category-type="CategoryTypeEnum.CategoryType_Multi"
v-model:category-config="formData.categoryConfig"
@emitSyncCates="syncCates"
/>

<!-- 发布时间 -->
Expand Down
5 changes: 4 additions & 1 deletion src/components/publish/form/PublishCategories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ const syncPubCates = (cates: string[], cateSlugs: string[]) => {
/>
</div>
<div v-else-if="formData.categoryType === CategoryTypeEnum.CategoryType_Multi">
<multi-categories v-model:category-config="formData.categoryConfig as IMultiCategoriesConfig" />
<multi-categories
v-model:category-config="formData.categoryConfig as IMultiCategoriesConfig"
@emitSyncMultiCates="syncPubCates"
/>
</div>
<div v-else-if="formData.categoryType === CategoryTypeEnum.CategoryType_Tree_Single">
<tree-single-category v-model:category-config="formData.categoryConfig as ITreeSingleCategoryConfig" />
Expand Down
168 changes: 166 additions & 2 deletions src/components/publish/form/category/MultiCategories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,181 @@

<script setup lang="ts">
import { IMultiCategoriesConfig } from "~/src/types/ICategoryConfig.ts"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import { onMounted, reactive, toRaw } from "vue"
import { useVueI18n } from "~/src/composables/useVueI18n.ts"
import { CategoryInfo } from "zhi-blog-api"
import { StrUtil } from "zhi-common"
import Adaptors from "~/src/adaptors"
const logger = createAppLogger("multi-categories")
const { t } = useVueI18n()
const props = defineProps({
categoryConfig: {
type: Object as () => IMultiCategoriesConfig,
default: {},
},
})
const formData = reactive({
categoryConfig: props.categoryConfig,
useRemoteData: !StrUtil.isEmptyString(props.categoryConfig.apiType),
cate: {
categorySelected: <string[]>[],
categoryList: [],
},
})
// emits
const emit = defineEmits(["emitSyncMultiCates"])
// methods
const handleCatNodeClick = (event: any, data: any[], node: any, nodeItem: any) => {
// console.log("data=>", data)
// console.log("node=>", node)
}
const handleCatNodeCheck = (data: any[], status: any) => {
logger.debug("checked item=>", {
data: toRaw(data),
status: toRaw(status),
})
const cates = []
const values = status.checkedKeys
values.forEach((item: any) => {
cates.push(item.toString())
})
formData.categoryConfig.categories = cates
logger.debug(" formData.categories=>", {
categories: toRaw(formData.categoryConfig.categories),
})
emit("emitSyncMultiCates", cates, [])
}
const handleRemoveTag = (val: string) => {
const value = val
logger.debug("准备删除 =>", value)
const cates = formData.categoryConfig.categories.filter((cate) => cate !== value)
formData.categoryConfig.categories = cates
logger.debug("formData.categories=>", {
categories: toRaw(formData.categoryConfig.categories),
})
emit("emitSyncMultiCates", cates, [])
}
const initPage = async () => {
if (formData.categoryConfig.cateEnabled) {
let categoryInfoList: CategoryInfo[] = []
logger.debug(`useRemoteData => ${formData.useRemoteData}`)
if (formData.useRemoteData) {
// 获取远程分类列表
const cfg = formData.categoryConfig.cfg
const api = await Adaptors.getAdaptor(formData.categoryConfig.apiType, cfg)
categoryInfoList = await api.getCategories()
logger.debug("getCategories for multi categories", categoryInfoList)
if (categoryInfoList.length > 0) {
// 分类列表
formData.cate.categoryList = categoryInfoList.map((item: CategoryInfo) => ({
value: item.categoryName,
label: item.categoryName,
}))
logger.debug("get multi categoryList =>", {
categoryList: toRaw(formData.cate.categoryList),
})
// 当前选中
const cates = formData.categoryConfig.categories
// 先读取保存的,否则使用默认
formData.cate.categorySelected = cates
// 默认未设置,获取第一个
emit("emitSyncMultiCates", cates, [])
logger.debug("muti cates is syncing =>", {
cates: toRaw(cates),
})
}
} else {
// 批量分发,直接组装公共分类
const cates = formData.categoryConfig.categories ?? []
categoryInfoList = cates.map((x: string) => {
const categoryInfo = new CategoryInfo()
categoryInfo.categoryId = x
categoryInfo.categoryName = x
categoryInfo.categoryDescription = x
return categoryInfo
})
if (categoryInfoList.length > 0) {
// 分类列表
formData.cate.categoryList = categoryInfoList.map((item: CategoryInfo) => ({
value: item.categoryId,
label: item.categoryName,
}))
// 当前选中
formData.cate.categorySelected = cates
}
logger.debug("公共分类 =>", { cates: toRaw(cates) })
}
}
}
onMounted(async () => {
await initPage()
})
</script>

<template>
<div>简单多选分类</div>
<div class="multi-categories" v-if="formData.cate.categoryList.length > 0">
<el-form-item :label="t('main.cat')">
<el-tree-select
style="width: 100%"
v-model="formData.cate.categorySelected"
:data="formData.cate.categoryList"
multiple
:check-on-click-node="true"
:render-after-expand="false"
show-checkbox
:placeholder="t('main.cat.select')"
:empty-text="t('main.cat.empty')"
:no-data-text="t('main.cat.empty')"
:disabled="formData.categoryConfig.readonlyMode"
@node-click="handleCatNodeClick"
@check="handleCatNodeCheck"
@remove-tag="handleRemoveTag"
/>
</el-form-item>
</div>
</template>

<style scoped lang="stylus"></style>
<style lang="stylus" scoped>
.multi-categories
:deep(.el-tag.el-tag--info)
--el-tag-bg-color var(--el-color-primary-light-9)
--el-tag-border-color var(--el-color-primary-light-8)
--el-tag-hover-color var(--el-color-primary)
--el-tag-text-color var(--el-color-primary)
background-color var(--el-tag-bg-color)
border-color var(--el-tag-border-color)
color var(--el-tag-text-color)
display inline-flex
justify-content center
align-items center
vertical-align middle
height 24px
padding 0 9px
font-size var(--el-tag-font-size)
line-height 1
border-width 1px
border-style solid
border-radius var(--el-tag-border-radius)
box-sizing border-box
white-space nowrap
--el-icon-size 14px
</style>
Loading

0 comments on commit f0bc264

Please sign in to comment.