From 4165d8baaefaeb04ff69d1cf960015e7d1d2f831 Mon Sep 17 00:00:00 2001 From: Sun668 <947692259@qq.com> Date: Fri, 26 Apr 2024 08:42:01 +0800 Subject: [PATCH] feat: add plugin-field-markdown-vditor (#4065) * feat: create vditor field type, use Vditor as Markdown Editor * feat: clear Markdown Vditor value when set props.value to null * feat: add plugin-field-markdown-field to preset local plugin * fix: fix the plugin-field-markdown-vditor name in preset * fix: fix the plugin-field-markdown-vditor version in preset * feat: set vditor disable if props.disable is true after init * feat: use data from localstorage as vditor upload request headers * fix: plugin-field-markdown-vditor version to 0.21.0-alpha.11 * feat: when fileCollection is not defined, remove upload from vditor toolbar * feat: add temp function to reset vditor value * fix: temp function to reset vditor value may include reset tag * feat: update plugin-field-markdown-vditor i18n * fix: i18n * feat: temp disable fullscreen * fix: remove useless file * fix: plugin description * fix: plugin description * fix: plugin-field-markdown-vditor componentCls * fix: plugin-field-markdown-vditadd default toobar config * fix: use long text to save mardkwon * fix: vditor fullscreen style * feat: change vditor field datatype * fix: code review * fix: code review * feat: change import method of katex in plugin-field-markdown-vditor * fix: version * fix: resize will cause blur * fix: vditor base font-size * fix: vditor base font-size * feat: use style config from token as vditor base size * fix: plugin-field-markdown-vditor i18n * fix: toobar config tooltip can not be seen * fix: vditor toobar default config * feat: plugin-field-markdown-vditor doc url * feat: move cursor to end when reset vditor value * fix: value change will not set vditor * feat: support getHeaders * fix: improve component * fix: enhance vditor init --------- Co-authored-by: chenos --- .../core/client/src/api-client/APIClient.ts | 26 ++++ .../client/src/application/Application.tsx | 4 + packages/core/sdk/src/APIClient.ts | 17 +++ .../plugin-field-markdown-vditor/.npmignore | 2 + .../plugin-field-markdown-vditor/README.md | 1 + .../plugin-field-markdown-vditor/client.d.ts | 2 + .../plugin-field-markdown-vditor/client.js | 1 + .../plugin-field-markdown-vditor/package.json | 30 ++++ .../plugin-field-markdown-vditor/server.d.ts | 2 + .../plugin-field-markdown-vditor/server.js | 1 + .../src/client/components/Display.tsx | 110 +++++++++++++++ .../src/client/components/Edit.tsx | 130 ++++++++++++++++++ .../src/client/components/index.tsx | 8 ++ .../src/client/components/style.ts | 15 ++ .../src/client/index.tsx | 22 +++ .../src/client/interfaces/markdown-vditor.tsx | 106 ++++++++++++++ .../src/client/locale/index.ts | 7 + .../src/client/utils/index.ts | 0 .../plugin-field-markdown-vditor/src/index.ts | 2 + .../src/locale/en-US.ts | 33 +++++ .../src/locale/ko_KR.ts | 33 +++++ .../src/locale/zh-CN.ts | 33 +++++ .../src/server/collections/.gitkeep | 0 .../src/server/index.ts | 1 + .../src/server/markdown-vditor-field.ts | 7 + .../src/server/plugin.ts | 19 +++ packages/presets/nocobase/package.json | 1 + packages/presets/nocobase/src/server/index.ts | 1 + 28 files changed, 614 insertions(+) create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/.npmignore create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/README.md create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/client.d.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/client.js create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/server.d.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/server.js create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Display.tsx create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Edit.tsx create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/index.tsx create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/style.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/index.tsx create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/interfaces/markdown-vditor.tsx create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/locale/index.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/utils/index.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/index.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/en-US.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/ko_KR.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/zh-CN.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/collections/.gitkeep create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/index.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/markdown-vditor-field.ts create mode 100644 packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/plugin.ts diff --git a/packages/core/client/src/api-client/APIClient.ts b/packages/core/client/src/api-client/APIClient.ts index f4a232f717..e1ef3ca448 100644 --- a/packages/core/client/src/api-client/APIClient.ts +++ b/packages/core/client/src/api-client/APIClient.ts @@ -28,6 +28,22 @@ const handleErrorMessage = (error, notification) => { }; }; +function offsetToTimeZone(offset) { + const hours = Math.floor(Math.abs(offset)); + const minutes = Math.abs((offset % 1) * 60); + + const formattedHours = (hours < 10 ? '0' : '') + hours; + const formattedMinutes = (minutes < 10 ? '0' : '') + minutes; + + const sign = offset >= 0 ? '+' : '-'; + return sign + formattedHours + ':' + formattedMinutes; +} + +const getCurrentTimezone = () => { + const timezoneOffset = new Date().getTimezoneOffset() / -60; + return offsetToTimeZone(timezoneOffset); +}; + const errorCache = new Map(); export class APIClient extends APIClientSDK { services: Record> = {}; @@ -36,6 +52,16 @@ export class APIClient extends APIClientSDK { /** 该值会在 AntdAppProvider 中被重新赋值 */ notification: any = notification; + getHeaders() { + const headers = super.getHeaders(); + if (this.app) { + headers['X-App'] = this.app.getName(); + } + headers['X-Timezone'] = getCurrentTimezone(); + headers['X-Hostname'] = window?.location?.hostname; + return headers; + } + service(uid: string) { return this.services[uid]; } diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index 428de4a0f8..cc2c70f7e4 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -167,6 +167,10 @@ export class Application { return this.options; } + getName() { + return getSubAppName(this.getPublicPath()) || null; + } + getPublicPath() { let publicPath = this.options.publicPath || '/'; if (!publicPath.endsWith('/')) { diff --git a/packages/core/sdk/src/APIClient.ts b/packages/core/sdk/src/APIClient.ts index 7d86a78be1..7ce009d1ff 100644 --- a/packages/core/sdk/src/APIClient.ts +++ b/packages/core/sdk/src/APIClient.ts @@ -266,6 +266,23 @@ export class APIClient { auth: Auth; storage: Storage; + getHeaders() { + const headers = {}; + if (this.auth.locale) { + headers['X-Locale'] = this.auth.locale; + } + if (this.auth.role) { + headers['X-Role'] = this.auth.role; + } + if (this.auth.authenticator) { + headers['X-Authenticator'] = this.auth.authenticator; + } + if (this.auth.token) { + headers['Authorization'] = `Bearer ${this.auth.token}`; + } + return headers; + } + constructor(instance?: APIClientOptions) { if (typeof instance === 'function') { this.axios = instance; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/.npmignore b/packages/plugins/@nocobase/plugin-field-markdown-vditor/.npmignore new file mode 100644 index 0000000000..65f5e8779f --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/README.md b/packages/plugins/@nocobase/plugin-field-markdown-vditor/README.md new file mode 100644 index 0000000000..c186374aee --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/README.md @@ -0,0 +1 @@ +# @nocobase/plugin-field-markdown-vditor diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/client.d.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/client.d.ts new file mode 100644 index 0000000000..6c459cbac4 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/client.js b/packages/plugins/@nocobase/plugin-field-markdown-vditor/client.js new file mode 100644 index 0000000000..b6e3be70e6 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json b/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json new file mode 100644 index 0000000000..3f2e0dc817 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/package.json @@ -0,0 +1,30 @@ +{ + "name": "@nocobase/plugin-field-markdown-vditor", + "displayName": "Collection field: Markdown(Vditor)", + "displayName.zh-CN": "数据表字段:Markdown(Vditor)", + "description": "Used to store Markdown and render it using Vditor editor, supports common Markdown syntax such as list, code, quote, etc., and supports uploading images, recordings, etc.It also allows for instant rendering, where what you see is what you get.", + "description.zh-CN": "用于存储 Markdown,并使用 Vditor 编辑器渲染,支持常见 Markdown 语法,如列表,代码,引用等,并支持上传图片,录音等。同时可以做到即时渲染,所见即所得。", + "version": "0.21.0-alpha.15", + "license": "AGPL-3.0", + "main": "dist/server/index.js", + "homepage": "https://docs.nocobase.com/handbook/field-markdown-vditor", + "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/field-markdown-vditor", + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + }, + "devDependencies": { + "@ant-design/icons": "5.x", + "@formily/antd-v5": "1.x", + "@formily/core": "2.x", + "@formily/react": "2.x", + "@formily/shared": "2.x", + "antd": "5.x", + "katex": "^0.16.10", + "vditor": "^3.10.3" + }, + "keywords": [ + "Collection fields" + ] +} diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/server.d.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/server.d.ts new file mode 100644 index 0000000000..c41081ddc6 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/server.js b/packages/plugins/@nocobase/plugin-field-markdown-vditor/server.js new file mode 100644 index 0000000000..972842039a --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Display.tsx b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Display.tsx new file mode 100644 index 0000000000..e449ba1d23 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Display.tsx @@ -0,0 +1,110 @@ +import { Field } from '@formily/core'; +import { useField } from '@formily/react'; +import { Popover } from 'antd'; +import React, { CSSProperties, useCallback, useEffect, useRef, useState } from 'react'; +import Vditor from 'vditor'; +import useStyle from './style'; + +function convertToText(markdownText: string) { + const content = markdownText; + let temp = document.createElement('div'); + temp.innerHTML = content; + const text = temp.innerText; + temp = null; + return text?.replace(/[\n\r]/g, '') || ''; +} + +const getContentWidth = (element) => { + if (element) { + const range = document.createRange(); + range.selectNodeContents(element); + const contentWidth = range.getBoundingClientRect().width; + return contentWidth; + } +}; + +function DisplayInner(props: { value: string; style?: CSSProperties }) { + const containerRef = useRef(); + const { wrapSSR, componentCls, hashId } = useStyle(); + + useEffect(() => { + if (!props.value) return; + Vditor.preview(containerRef.current, props.value, { mode: 'light' }); + }, [props.value]); + + return wrapSSR( +
+
+
, + ); +} + +export const Display = (props) => { + const field = useField(); + const value = props.value ?? field.value; + + const containerRef = useRef(); + + const [popoverVisible, setPopoverVisible] = useState(false); + const [ellipsis, setEllipsis] = useState(false); + + const [text, setText] = useState(''); + + const elRef = useRef(); + + useEffect(() => { + if (!props.value || !field.value) return; + if (props.ellipsis) { + Vditor.md2html(props.value, { mode: 'light' }) + .then((html) => { + setText(convertToText(html)); + }) + .catch(() => setText('')); + } else { + Vditor.preview(containerRef.current, props.value ?? field.value, { + mode: 'light', + }); + } + }, [props.value, props.ellipsis, field.value]); + + const isOverflowTooltip = useCallback(() => { + if (!elRef.current) return false; + const contentWidth = getContentWidth(elRef.current); + const offsetWidth = elRef.current?.offsetWidth; + return contentWidth > offsetWidth; + }, [elRef]); + + if (props.ellipsis) { + return ( + { + setPopoverVisible(ellipsis && visible); + }} + content={} + > +
{ + const el = e.target as any; + const isShowTooltips = isOverflowTooltip(); + if (isShowTooltips) { + setEllipsis(el.scrollWidth >= el.clientWidth); + } + }} + > + {text} +
+
+ ); + } + + return ; +}; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Edit.tsx b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Edit.tsx new file mode 100644 index 0000000000..91246f44c0 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/Edit.tsx @@ -0,0 +1,130 @@ +import React, { useRef, useEffect, useLayoutEffect } from 'react'; +import Vditor from 'vditor'; +import { useAPIClient, withDynamicSchemaProps, useApp } from '@nocobase/client'; +import useStyle from './style'; +import { defaultToolbar } from '../interfaces/markdown-vditor'; + +export const Edit = withDynamicSchemaProps((props) => { + const { disabled, onChange, value, fileCollection, toolbar } = props; + + const vdRef = useRef(); + const vdFullscreen = useRef(false); + const containerRef = useRef(); + const containerParentRef = useRef(); + const app = useApp(); + const apiClient = useAPIClient(); + const { wrapSSR, hashId, componentCls: containerClassName } = useStyle(); + + useEffect(() => { + const uploadFileCollection = fileCollection ?? 'attachments'; + const toolbarConfig = toolbar ?? defaultToolbar; + const vditor = new Vditor(containerRef.current, { + value, + lang: apiClient.auth.locale.replaceAll('-', '_') as any, + cache: { + enable: false, + }, + undoDelay: 0, + preview: { + math: { + engine: 'KaTeX', + }, + }, + toolbar: toolbarConfig, + fullscreen: { + index: 1200, + }, + minHeight: 200, + after: () => { + vdRef.current = vditor; + if (disabled) { + vditor.disabled(); + } else { + vditor.enable(); + } + }, + input(value) { + onChange(value); + }, + upload: { + url: app.getApiUrl(`${uploadFileCollection ?? 'attachments'}:create`), + headers: apiClient.getHeaders(), + multiple: false, + fieldName: 'file', + format(files, responseText) { + const response = JSON.parse(responseText); + const formatResponse = { + msg: '', + code: 0, + data: { + errFiles: [], + succMap: { + [response.data.filename]: response.data.url, + }, + }, + }; + return JSON.stringify(formatResponse); + }, + }, + }); + return () => { + vdRef.current?.destroy(); + vdRef.current = undefined; + }; + }, [fileCollection, toolbar]); + + useEffect(() => { + if (value === vdRef?.current?.getValue()) { + return; + } + vdRef.current?.setValue(value); + vdRef.current?.focus(); + // 移动光标到末尾 + const preArea = containerRef.current.querySelector('div.vditor-content > div.vditor-ir > pre') as HTMLPreElement; + if (preArea) { + const range = document.createRange(); + const selection = window.getSelection(); + if (selection) { + range.selectNodeContents(preArea); + range.collapse(false); // 将光标移动到内容末尾 + selection.removeAllRanges(); + selection.addRange(range); + } + } + }, [value]); + + useEffect(() => { + if (disabled) { + vdRef.current?.disabled(); + } else { + vdRef.current?.enable(); + } + }, [disabled]); + + useLayoutEffect(() => { + const observer = new ResizeObserver((entries) => { + for (const entry of entries) { + const target = entry.target; + if (target.className.includes('vditor--fullscreen')) { + document.body.appendChild(target); + vdFullscreen.current = true; + } else if (vdFullscreen.current) { + containerParentRef.current?.appendChild(target); + vdFullscreen.current = false; + } + } + }); + + observer.observe(containerRef.current); + + return () => { + observer.unobserve(containerRef.current); + }; + }, []); + + return wrapSSR( +
+
+
, + ); +}); diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/index.tsx b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/index.tsx new file mode 100644 index 0000000000..1d988f8cc7 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/index.tsx @@ -0,0 +1,8 @@ +import { connect, mapReadPretty } from '@formily/react'; +import { withDynamicSchemaProps } from '@nocobase/client'; +import { Display } from './Display'; +import { Edit } from './Edit'; + +export const MarkdownVditor = withDynamicSchemaProps(connect(Edit, mapReadPretty(Display))); + +export default MarkdownVditor; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/style.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/style.ts new file mode 100644 index 0000000000..6362855af4 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/components/style.ts @@ -0,0 +1,15 @@ +import { genStyleHook } from '@nocobase/client'; + +export default genStyleHook('nb-field-markdown-vditor', (token) => { + const { componentCls } = token; + + return { + [componentCls]: { + '.vditor-reset': { fontSize: `${token.fontSize}px !important` }, + '.vditor': { borderRadius: 8 }, + '.vditor .vditor-content': { borderRadius: '0 0 8px 8px', overflow: 'hidden' }, + '.vditor .vditor-toolbar': { paddingLeft: ' 16px !important', borderRadius: '8px 8px 0 0' }, + '.vditor .vditor-content .vditor-ir .vditor-reset': { paddingLeft: ' 16px !important' }, + }, + }; +}); diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/index.tsx b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/index.tsx new file mode 100644 index 0000000000..17bbb5680c --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/index.tsx @@ -0,0 +1,22 @@ +import { Plugin } from '@nocobase/client'; +import { MarkdownVditor } from './components'; +import { MarkdownVditorFieldInterface } from './interfaces/markdown-vditor'; +import 'vditor/dist/index.css'; +import katex from 'katex'; +export class PluginFieldMarkdownVditorClient extends Plugin { + async afterAdd() {} + + async beforeLoad() {} + + async load() { + this.app.addComponents({ MarkdownVditor }); + this.initKatexDependency(); + this.app.dataSourceManager.addFieldInterfaces([MarkdownVditorFieldInterface]); + } + + initKatexDependency() { + window['katex'] = katex; + } +} + +export default PluginFieldMarkdownVditorClient; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/interfaces/markdown-vditor.tsx b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/interfaces/markdown-vditor.tsx new file mode 100644 index 0000000000..2e4d1b8ffe --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/interfaces/markdown-vditor.tsx @@ -0,0 +1,106 @@ +import { CollectionFieldInterface, interfacesProperties } from '@nocobase/client'; +import { generateNTemplate } from '../locale'; +import { ISchema } from '@formily/react'; + +const { defaultProps, operators } = interfacesProperties; + +export const defaultToolbar = [ + 'headings', + 'bold', + 'italic', + 'strike', + 'link', + 'list', + 'ordered-list', + 'check', + 'quote', + 'line', + 'code', + 'inline-code', + 'upload', + 'fullscreen', +]; + +export class MarkdownVditorFieldInterface extends CollectionFieldInterface { + name = 'vditor'; + type = 'object'; + group = 'media'; + order = 1; + title = generateNTemplate('Vditor'); + sortable = true; + default = { + type: 'text', + length: 'long', + uiSchema: { + type: 'string', + 'x-component': 'MarkdownVditor', + }, + }; + properties = { + ...defaultProps, + 'uiSchema.x-component-props.fileCollection': { + type: 'string', + title: generateNTemplate('File collection'), + 'x-component': 'CollectionSelect', + 'x-component-props': { filter: (collection) => collection?.options?.template === 'file' }, + 'x-decorator': 'FormItem', + default: '', + 'x-reactions': { + fulfill: { + schema: { + description: generateNTemplate('Used to store files uploaded in the Markdown editor'), + }, + }, + }, + }, + 'uiSchema.x-component-props.toolbar': { + type: 'array', + title: generateNTemplate('Toolbar'), + 'x-component': 'Select', + 'x-component-props': { + mode: 'multiple', + }, + 'x-decorator': 'FormItem', + default: defaultToolbar, + enum: [ + { value: 'emoji', label: generateNTemplate('Emoji') }, + { value: 'headings', label: generateNTemplate('Headings') }, + { value: 'bold', label: generateNTemplate('Bold') }, + { value: 'italic', label: generateNTemplate('Italic') }, + { value: 'strike', label: generateNTemplate('Strike') }, + { value: 'line', label: generateNTemplate('Line') }, + { value: 'quote', label: generateNTemplate('Quote') }, + { value: 'list', label: generateNTemplate('List') }, + { value: 'ordered-list', label: generateNTemplate('OrderedList') }, + { value: 'check', label: generateNTemplate('Check') }, + { value: 'outdent', label: generateNTemplate('Outdent') }, + { value: 'indent', label: generateNTemplate('Indent') }, + { value: 'code', label: generateNTemplate('Code') }, + { value: 'inline-code', label: generateNTemplate('InlineCode') }, + { value: 'insert-after', label: generateNTemplate('InsertAfter') }, + { value: 'insert-before', label: generateNTemplate('InsertBefore') }, + { value: 'undo', label: generateNTemplate('Undo') }, + { value: 'redo', label: generateNTemplate('Redo') }, + { value: 'upload', label: generateNTemplate('Upload') }, + { value: 'link', label: generateNTemplate('Link') }, + { value: 'record', label: generateNTemplate('Record') }, + { value: 'table', label: generateNTemplate('Table') }, + { value: 'edit-mode', label: generateNTemplate('EditMode') }, + { value: 'both', label: generateNTemplate('Both') }, + { value: 'preview', label: generateNTemplate('Preview') }, + { value: 'fullscreen', label: generateNTemplate('Fullscreen') }, + { value: 'outline', label: generateNTemplate('Outline') }, + ], + }, + }; + schemaInitialize(schema: ISchema, { block }) { + if (['Table', 'Kanban'].includes(block)) { + schema['x-component-props'] = schema['x-component-props'] || {}; + schema['x-component-props']['ellipsis'] = true; + } + } + filterable = { + operators: operators.number, + }; + titleUsable = true; +} diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/locale/index.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/locale/index.ts new file mode 100644 index 0000000000..3527fecf3d --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/locale/index.ts @@ -0,0 +1,7 @@ +import { tval } from '@nocobase/client'; + +const NAMESPACE = 'field-markdown-vditor'; + +export function generateNTemplate(key: string) { + return tval(key, { ns: NAMESPACE }) +} \ No newline at end of file diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/utils/index.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/client/utils/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/index.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/index.ts new file mode 100644 index 0000000000..7e74612df8 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/en-US.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/en-US.ts new file mode 100644 index 0000000000..d58eac3704 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/en-US.ts @@ -0,0 +1,33 @@ +export default { + "Vditor": "Markdown(Vditor)", + "File collection": "File collection", + "Used to store files uploaded in the Markdown editor": "Used to store files uploaded in the Markdown editor", + "Toolbar": "Editor toolbar configuration", + "Emoji": "Emoji", + "Headings": "Headings", + "Bold": "Bold", + "Italic": "Italic", + "Strike": "Strike", + "Record": "Start Record/End Record", + "Line": "Line", + "Quote": "Quote", + "List": "List", + "OrderedList": "Order List", + "Check": "Task List", + "Outdent": "Outdent", + "Indent": "Indent", + "Code": "Code Block", + "InlineCode": "Inline Code", + "InsertAfter": "Insert Line After", + "InsertBefore": "Insert Line Before", + "Undo": "Undo", + "Redo": "Redo", + "Upload": "Upload image or file", + "Link": "Link", + "Table": "Table", + "EditMode": "Edit Mode", + "Both": "Editor & Preview", + "Preview": "Preview", + "Fullscreen": "Toggle Fullscreen", + "Outline": "Outline" +} diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/ko_KR.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/ko_KR.ts new file mode 100644 index 0000000000..3794bb9391 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/ko_KR.ts @@ -0,0 +1,33 @@ +export default { + "Vditor": "Markdown(Vditor)", + "File collection": "파일 데이터 테이블", + "Used to store files uploaded in the Markdown editor": "Markdown 편집기에 업로드된 파일을 저장하는 데 사용됩니다", + "Toolbar": "편집기 도구 모음 구성", + "Emoji": "이모지", + "Headings": "제목크기", + "Bold": "굵게", + "Italic": "기울임꼴", + "Strike": "취소선", + "Record": "녹음시작/녹음종료", + "Line": "문단나눔", + "Quote": "인용단락", + "List": "순서없는 목록", + "OrderedList": "순서있는 목록", + "Check": "체크박스", + "Outdent": "내어쓰기", + "Indent": "들여쓰기", + "Code": "코드블럭삽입", + "InlineCode": "인라인코드", + "InsertAfter": "블락 뒤로 입력", + "InsertBefore": "블락 앞으로 입력", + "Undo": "취소하기", + "Redo": "되돌리기", + "Upload": "이미지 업로드하기", + "Link": "링크", + "Table": "표삽입", + "EditMode": "편집모드", + "Both": "에디터 & 미리보기", + "Preview": "미리보기", + "Fullscreen": "전체화면", + "Outline": "개요" +} diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/zh-CN.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/zh-CN.ts new file mode 100644 index 0000000000..4786181dbe --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/locale/zh-CN.ts @@ -0,0 +1,33 @@ +export default { + "Vditor": "Markdown(Vditor)", + "File collection": "文件数据表", + "Used to store files uploaded in the Markdown editor": "用于存储在Markdown编辑器中上传的文件", + "Toolbar": "编辑器工具栏配置", + "Emoji": "表情", + "Headings": "标题", + "Bold": "粗体", + "Italic": "斜体", + "Strike": "删除线", + "Record": "开始录音/结束录音", + "Line": "分割线", + "Quote": "引用", + "List": "无序列表", + "OrderedList": "有序列表", + "Check": "任务列表", + "Outdent": "列表反向缩进", + "Indent": "列表缩进", + "Code": "代码块", + "InlineCode": "行内代码", + "InsertAfter": "末尾插入行", + "InsertBefore": "起始插入行", + "Undo": "撤销", + "Redo": "重做", + "Upload": "上传图片或文件", + "Link": "链接", + "Table": "表格", + "EditMode": "切换编辑模式", + "Both": "编辑 & 预览", + "Preview": "预览", + "Fullscreen": "全屏切换", + "Outline": "大纲" +} diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/collections/.gitkeep b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/collections/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/index.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/index.ts new file mode 100644 index 0000000000..b68aea57f9 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/markdown-vditor-field.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/markdown-vditor-field.ts new file mode 100644 index 0000000000..c53ca92410 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/markdown-vditor-field.ts @@ -0,0 +1,7 @@ +import { DataTypes, Field } from '@nocobase/database'; + +export class MarkdownVditorField extends Field { + get dataType() { + return DataTypes.TEXT; + } +} diff --git a/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/plugin.ts new file mode 100644 index 0000000000..d6c05326cf --- /dev/null +++ b/packages/plugins/@nocobase/plugin-field-markdown-vditor/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { Plugin } from '@nocobase/server'; + +export class PluginFieldMarkdownVditorServer extends Plugin { + async afterAdd() {} + + async beforeLoad() {} + + async load() {} + + async install() {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginFieldMarkdownVditorServer; diff --git a/packages/presets/nocobase/package.json b/packages/presets/nocobase/package.json index 811766c7c7..843ede2cfd 100644 --- a/packages/presets/nocobase/package.json +++ b/packages/presets/nocobase/package.json @@ -35,6 +35,7 @@ "@nocobase/plugin-kanban": "0.21.0-alpha.15", "@nocobase/plugin-localization-management": "0.21.0-alpha.15", "@nocobase/plugin-logger": "0.21.0-alpha.15", + "@nocobase/plugin-field-markdown-vditor": "0.21.0-alpha.15", "@nocobase/plugin-map": "0.21.0-alpha.15", "@nocobase/plugin-mobile-client": "0.21.0-alpha.15", "@nocobase/plugin-mock-collections": "0.21.0-alpha.15", diff --git a/packages/presets/nocobase/src/server/index.ts b/packages/presets/nocobase/src/server/index.ts index c9f5cee35b..debae8baa5 100644 --- a/packages/presets/nocobase/src/server/index.ts +++ b/packages/presets/nocobase/src/server/index.ts @@ -60,6 +60,7 @@ export class PresetNocoBase extends Plugin { 'api-doc>=0.13.0-alpha.1', 'cas>=0.13.0-alpha.5', 'sms-auth>=0.10.0-alpha.2', + 'field-markdown-vditor>=0.21.0-alpha.11', ]; splitNames(name: string) {