瀏覽代碼

feat: add ckeditor

lianghongjie 1 年之前
父節點
當前提交
b7d6fce1b5

+ 11 - 2
package.json

@@ -15,6 +15,15 @@
     "deploy": "npm run build && npm run uploadOss && npm run uploadServer"
   },
   "dependencies": {
+    "@ckeditor/ckeditor5-alignment": "38.0.0",
+    "@ckeditor/ckeditor5-basic-styles": "^38.0.0",
+    "@ckeditor/ckeditor5-editor-inline": "^38.0.0",
+    "@ckeditor/ckeditor5-essentials": "^38.0.0",
+    "@ckeditor/ckeditor5-font": "^38.0.0",
+    "@ckeditor/ckeditor5-link": "^38.0.0",
+    "@ckeditor/ckeditor5-paragraph": "^38.0.0",
+    "@ckeditor/ckeditor5-theme-lark": "^38.0.0",
+    "@ckeditor/ckeditor5-vue": "^5.1.0",
     "@linaria/core": "^4.1.1",
     "@queenjs-modules/auth": "^0.0.18",
     "@queenjs/components": "^0.0.5",
@@ -43,7 +52,6 @@
     "proto.gl": "^1.0.0",
     "queen3d": "^0.0.80",
     "queenjs": "^1.0.0-beta.72",
-    "quill": "^1.3.7",
     "rimraf": "^3.0.2",
     "scp2": "^0.5.0",
     "swiper": "^8.4.4",
@@ -54,12 +62,13 @@
     "vue-types": "^4.2.1"
   },
   "devDependencies": {
+    "@ckeditor/ckeditor5-dev-translations": "^37.0.1",
+    "@ckeditor/ckeditor5-dev-utils": "^37.0.1",
     "@linaria/babel-preset": "^4.1.2",
     "@linaria/webpack-loader": "^4.1.2",
     "@queenjs/webpack-loader": "^0.0.2",
     "@types/color-convert": "^2.0.0",
     "@types/lodash": "^4.14.186",
-    "@types/quill": "^2.0.10",
     "@types/three": "^0.146.0",
     "@typescript-eslint/eslint-plugin": "^5.4.0",
     "@typescript-eslint/parser": "^5.4.0",

+ 8 - 2
src/App.tsx

@@ -16,8 +16,14 @@ const App = defineComponent(() => {
   );
 });
 
-export function startApp(router: Router, hooks: any[] = []) {
+export function startApp(
+  router: Router,
+  hooks: any[] = [],
+  callback?: (app: ReturnType<typeof createApp>) => void
+) {
   setModuleHooks = hooks;
   queenApi.router = router;
-  createApp(App).use(router).mount("#app");
+  const app = createApp(App);
+  callback?.(app);
+  app.use(router).mount("#app");
 }

+ 65 - 0
src/modules/editor/components/CompUI/baseUI/CkEditor.tsx

@@ -0,0 +1,65 @@
+import { useEditor } from "@/modules/editor";
+import { Bold, Italic } from "@ckeditor/ckeditor5-basic-styles";
+import { InlineEditor } from "@ckeditor/ckeditor5-editor-inline";
+import { Essentials } from "@ckeditor/ckeditor5-essentials";
+import { Alignment } from "@ckeditor/ckeditor5-alignment";
+import { FontFamily, FontSize } from "@ckeditor/ckeditor5-font";
+import { Link } from "@ckeditor/ckeditor5-link";
+import { Paragraph } from "@ckeditor/ckeditor5-paragraph";
+import { defineComponent } from "vue";
+import { string } from "vue-types";
+
+export const CkEditor = defineComponent({
+  props: {
+    value: string().def("请输入文本"),
+  },
+  emits: ["change"],
+  setup(props, { emit }) {
+    const { store } = useEditor();
+    const config = {
+      plugins: [
+        Essentials,
+        Bold,
+        Italic,
+        Link,
+        Paragraph,
+        FontSize,
+        FontFamily,
+        Alignment,
+      ],
+      fontSize: {
+        options: [9, 11, 13, "default", 17, 19, 21],
+      },
+      toolbar: {
+        items: [
+          "undo",
+          "redo",
+          "|",
+          "fontFamily",
+          "fontsize",
+          "bold",
+          "italic",
+          "|",
+          "alignment",
+          "|",
+          "link",
+        ],
+      },
+    };
+    return () => (
+      <ckeditor
+        editor={InlineEditor}
+        onBlur={(e: any, editor: InlineEditor) =>
+          emit("change", editor.getData())
+        }
+        onReady={(editor: InlineEditor) => {
+          editor.setData(props.value);
+          if (!store.isEditMode) {
+            editor.enableReadOnlyMode("editor");
+          }
+        }}
+        config={config}
+      />
+    );
+  },
+});

+ 8 - 45
src/modules/editor/components/CompUI/baseUI/Text.tsx

@@ -1,58 +1,21 @@
-import { useEditor } from "@/modules/editor";
-import { css } from "@linaria/core";
-import { defineComponent, ref } from "vue";
+import { defineComponent } from "vue";
 import { string } from "vue-types";
+import { CkEditor } from "./CkEditor";
 import { View } from "./View";
 
 export const Text = defineComponent({
   props: {
-    value: string().def("请输入文本"),
+    value: string(),
   },
   emits: ["update:value"],
   setup(props, { emit }) {
-    const { store } = useEditor();
-    const textRef = ref<HTMLTextAreaElement>();
-
-    function textareaResize() {
-      const textEl = textRef.value;
-      if (textEl) {
-        textEl.style.height = "0";
-        textEl.style.height = textEl.scrollHeight + "px";
-      }
-    }
-
     return () => (
-      <View class={textStyle}>
-        {store.isEditMode ? (
-          <textarea
-            ref={textRef}
-            value={props.value}
-            rows={1}
-            onInput={(e) => {
-              emit("update:value", (e.target as HTMLInputElement).value);
-              textareaResize();
-            }}
-          />
-        ) : (
-          <div class="whitespace-pre">{props.value}</div>
-        )}
+      <View>
+        <CkEditor
+          value={props.value}
+          onChange={(v) => emit("update:value", v)}
+        />
       </View>
     );
   },
 });
-
-const textStyle = css`
-  text-align: left;
-  textarea {
-    resize: none;
-  }
-  textarea,
-  div {
-    display: block;
-    width: 100%;
-    height: 100%;
-    padding: 4px;
-    outline: none;
-    border: none;
-  }
-`;

+ 1 - 0
src/modules/editor/components/CompUI/baseUI/Textarea.tsx

@@ -5,6 +5,7 @@ import { defineComponent, onMounted, ref, watchEffect } from "vue";
 import { string } from "vue-types";
 import { View } from "./View";
 
+
 export const Textarea = defineComponent({
   props: {
     value: string().def(""),

+ 4 - 1
src/pages/editor/index.ts

@@ -1,5 +1,8 @@
 import { startApp } from "@/App";
 import { initAuthDef } from "@/hooks/initAuthDef";
+import CKEditor from "@ckeditor/ckeditor5-vue";
 import router from "./router";
 
-startApp(router, [initAuthDef]);
+startApp(router, [initAuthDef], (app) => {
+  app.use(CKEditor);
+});

+ 4 - 0
src/styles/global.less

@@ -54,3 +54,7 @@ each(@direction, {
     background: @inf-primary-hover-bg;
   }
 }
+
+.ck-powered-by {
+  display: none;
+}

+ 57 - 1
vue.config.js

@@ -2,6 +2,10 @@ const { defineConfig } = require("@vue/cli-service");
 const linariaLessLoader = require("@queenjs/webpack-loader/linaria-loader");
 const modifyVars = require("./src/styles/theme-antd");
 const path = require("path");
+const {
+  CKEditorTranslationsPlugin,
+} = require("@ckeditor/ckeditor5-dev-translations");
+const { styles } = require("@ckeditor/ckeditor5-dev-utils");
 
 // const publicPath =
 //   process.env.NODE_ENV === "production"
@@ -15,7 +19,7 @@ module.exports = defineConfig({
     editor: "src/pages/editor/index.ts",
   },
   publicPath: publicPath,
-  transpileDependencies: true,
+  transpileDependencies: [/ckeditor5-[^/\\]+[/\\]src[/\\].+\.js$/],
   devServer: {
     client: {
       overlay: {
@@ -25,6 +29,17 @@ module.exports = defineConfig({
       },
     },
   },
+  configureWebpack: {
+    plugins: [
+      // CKEditor 5 needs its own plugin to be built using webpack.
+      new CKEditorTranslationsPlugin({
+        // See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
+        language: "zh",
+        // Append translations to the file matching the `app` name.
+        // translationsOutputFile: /ckeditor/,
+      }),
+    ],
+  },
   chainWebpack: (config) => {
     const tsRule = config.module.rule("ts");
     tsRule
@@ -48,6 +63,47 @@ module.exports = defineConfig({
         preprocessor: linariaLessLoader("@/styles/theme"),
       })
       .before("ts-loader");
+
+    const svgRule = config.module.rule("svg");
+    // Then you can either:
+    //
+    // * clear all loaders for existing 'svg' rule:
+    //
+    //		svgRule.uses.clear();
+    //
+    // * or exclude ckeditor directory from node_modules:
+    svgRule.exclude.add(path.join(__dirname, "node_modules", "@ckeditor"));
+
+    // Add an entry for *.svg files belonging to CKEditor. You can either:
+    //
+    // * modify the existing 'svg' rule:
+    //
+    //		svgRule.use( 'raw-loader' ).loader( 'raw-loader' );
+    //
+    // * or add a new one:
+    config.module
+      .rule("cke-svg")
+      .test(/ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/)
+      .use("raw-loader")
+      .loader("raw-loader");
+
+    // (2.) Transpile the .css files imported by the editor using PostCSS.
+    // Make sure only the CSS belonging to ckeditor5-* packages is processed this way.
+    config.module
+      .rule("cke-css")
+      .test(/ckeditor5-[^/\\]+[/\\].+\.css$/)
+      .use("postcss-loader")
+      .loader("postcss-loader")
+      .tap(() => {
+        return {
+          postcssOptions: styles.getPostCssConfig({
+            themeImporter: {
+              themePath: require.resolve("@ckeditor/ckeditor5-theme-lark"),
+            },
+            minify: true,
+          }),
+        };
+      });
   },
   css: {
     loaderOptions: {

文件差異過大導致無法顯示
+ 598 - 92
yarn.lock


部分文件因文件數量過多而無法顯示