11 Commits d58a3b18a8 ... 8c22574ae1

Author SHA1 Message Date
  Soul 8c22574ae1 Merge branch 'dev' 10 months ago
  danch 56101b0725 视频文件最大上传大小改为2g,上传最长时长改为4mins 10 months ago
  Soul 7fc7d11451 Merge branch 'dev' 10 months ago
  danch aac052c647 编辑界面新增校验;标签保存失败修复;布局调整; 10 months ago
  Soul 7a39d56467 Merge branch 'dev' 10 months ago
  danch ac2d1afc9a 超过一G后提示重新上传 10 months ago
  danch d885390509 文件上传时显示loading,上传超过两分钟提示超时 10 months ago
  danch b3e52bccdd 文件上传时显示loading,上传超过两分钟提示超时 10 months ago
  danch 9e5289954b Merge remote-tracking branch 'origin/dev' into dev 10 months ago
  danch 842b63e430 视频只能传MP4 ,大小最大1G;文件最大2G 10 months ago
  luo faa2041d36 知识详情底部样式优化 10 months ago

+ 17 - 15
src/api/common/api.ts

@@ -1,8 +1,10 @@
-import { defHttp } from '/@/utils/http/axios';
-import { message } from 'ant-design-vue';
-import { useGlobSetting } from '/@/hooks/setting';
+import {defHttp} from '/@/utils/http/axios';
+import {message} from 'ant-design-vue';
+import {useGlobSetting} from '/@/hooks/setting';
+
 const globSetting = useGlobSetting();
 const baseUploadUrl = globSetting.uploadUrl;
+
 enum Api {
   positionList = '/sys/position/list',
   userList = '/sys/user/list',
@@ -26,7 +28,7 @@ export const uploadUrl = `${baseUploadUrl}/sys/common/upload`;
  * @param params
  */
 export const getPositionList = (params) => {
-  return defHttp.get({ url: Api.positionList, params });
+  return defHttp.get({url: Api.positionList, params});
 };
 
 /**
@@ -34,7 +36,7 @@ export const getPositionList = (params) => {
  * @param params
  */
 export const getUserList = (params) => {
-  return defHttp.get({ url: Api.userList, params });
+  return defHttp.get({url: Api.userList, params});
 };
 
 /**
@@ -42,59 +44,59 @@ export const getUserList = (params) => {
  * @param params
  */
 export const getRoleList = (params) => {
-  return defHttp.get({ url: Api.roleList, params });
+  return defHttp.get({url: Api.roleList, params});
 };
 
 /**
  * 异步获取部门树列表
  */
 export const queryDepartTreeSync = (params?) => {
-  return defHttp.get({ url: Api.queryDepartTreeSync, params });
+  return defHttp.get({url: Api.queryDepartTreeSync, params});
 };
 /**
  * 获取部门树列表
  */
 export const queryTreeList = (params?) => {
-  return defHttp.get({ url: Api.queryTreeList, params });
+  return defHttp.get({url: Api.queryTreeList, params});
 };
 
 /**
  * 分类字典树控件 加载节点
  */
 export const loadTreeData = (params?) => {
-  return defHttp.get({ url: Api.loadTreeData, params });
+  return defHttp.get({url: Api.loadTreeData, params});
 };
 
 /**
  * 根据字典code加载字典text
  */
 export const loadDictItem = (params?) => {
-  return defHttp.get({ url: Api.loadDictItem, params });
+  return defHttp.get({url: Api.loadDictItem, params});
 };
 
 /**
  * 根据字典code加载字典text
  */
 export const getDictItems = (dictCode) => {
-  return defHttp.get({ url: Api.getDictItems + dictCode }, { joinTime: false });
+  return defHttp.get({url: Api.getDictItems + dictCode}, {joinTime: false});
 };
 /**
  * 部门用户modal选择列表加载list
  */
 export const getTableList = (params) => {
-  return defHttp.get({ url: Api.getTableList, params });
+  return defHttp.get({url: Api.getTableList, params});
 };
 /**
  * 加载全部分类字典数据
  */
 export const loadCategoryData = (params) => {
-  return defHttp.get({ url: Api.getCategoryData, params });
+  return defHttp.get({url: Api.getCategoryData, params});
 };
 /**
  * 文件上传
  */
 export const uploadFile = (params, success) => {
-  return defHttp.uploadFile({ url: uploadUrl }, params, { success });
+  return defHttp.uploadFile({url: uploadUrl, timeout: 4 * 60 * 1000}, params, {success});
 };
 /**
  * 下载文件
@@ -138,7 +140,7 @@ export const getFileblob = (url, parameter) => {
       params: parameter,
       responseType: 'blob',
     },
-    { isTransformResponse: false }
+    {isTransformResponse: false}
   );
 };
 

+ 224 - 156
src/components/Tinymce/src/Editor.vue

@@ -1,7 +1,5 @@
 <template>
-
   <div :class="prefixCls" :style="{ width: containerWidth }">
-
     <ImgUpload
       :fullscreen="fullscreen"
       @uploading="handleImageUploading"
@@ -10,128 +8,137 @@
       v-show="editorRef"
       :disabled="disabled"
     />
-    <textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline">
-    </textarea>
-    <div class="tinymce-upload-progress-box" v-if="spinning">
-      <span>正在上传</span>
-    </div>
+    <a-spin class="tinymce-upload-progress-box" tip="正在上传..."
+            :spinning="spinning"
+            :delay="500">
+    </a-spin>
+    <textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }"
+              v-if="!initOptions.inline"></textarea>
+    <slot v-else></slot>
 
-    <!--    <slot v-else></slot>-->
 
   </div>
-
 </template>
 
 <script lang="ts">
-import type { Editor, RawEditorSettings } from "tinymce";
-import tinymce from "tinymce/tinymce";
-import "tinymce/themes/silver";
-import "tinymce/icons/default/icons";
-import "tinymce/plugins/advlist";
-import "tinymce/plugins/anchor";
-import "tinymce/plugins/autolink";
-import "tinymce/plugins/autosave";
-import "tinymce/plugins/code";
-import "tinymce/plugins/codesample";
-import "tinymce/plugins/directionality";
-import "tinymce/plugins/fullscreen";
-import "tinymce/plugins/hr";
-import "tinymce/plugins/insertdatetime";
-import "tinymce/plugins/link";
-import "tinymce/plugins/lists";
-import "tinymce/plugins/media";
-import "tinymce/plugins/nonbreaking";
-import "tinymce/plugins/noneditable";
-import "tinymce/plugins/pagebreak";
-import "tinymce/plugins/paste";
-import "tinymce/plugins/preview";
-import "tinymce/plugins/print";
-import "tinymce/plugins/save";
-import "tinymce/plugins/searchreplace";
-import "tinymce/plugins/spellchecker";
-import "tinymce/plugins/tabfocus";
+import type {Editor, RawEditorSettings} from 'tinymce';
+import tinymce from 'tinymce/tinymce';
+import 'tinymce/themes/silver';
+import 'tinymce/icons/default/icons';
+import 'tinymce/plugins/advlist';
+import 'tinymce/plugins/anchor';
+import 'tinymce/plugins/autolink';
+import 'tinymce/plugins/autosave';
+import 'tinymce/plugins/code';
+import 'tinymce/plugins/codesample';
+import 'tinymce/plugins/directionality';
+import 'tinymce/plugins/fullscreen';
+import 'tinymce/plugins/hr';
+import 'tinymce/plugins/insertdatetime';
+import 'tinymce/plugins/link';
+import 'tinymce/plugins/lists';
+import 'tinymce/plugins/media';
+import 'tinymce/plugins/nonbreaking';
+import 'tinymce/plugins/noneditable';
+import 'tinymce/plugins/pagebreak';
+import 'tinymce/plugins/paste';
+import 'tinymce/plugins/preview';
+import 'tinymce/plugins/print';
+import 'tinymce/plugins/save';
+import 'tinymce/plugins/searchreplace';
+import 'tinymce/plugins/spellchecker';
+import 'tinymce/plugins/tabfocus';
 // import 'tinymce/plugins/table';
-import "tinymce/plugins/template";
-import "tinymce/plugins/textpattern";
-import "tinymce/plugins/visualblocks";
-import "tinymce/plugins/visualchars";
-import "tinymce/plugins/wordcount";
-import "tinymce/plugins/image";
-import "tinymce/plugins/table";
-import "tinymce/plugins/textcolor";
-import "tinymce/plugins/contextmenu";
-import { defineComponent, computed, nextTick, ref, unref, watch, onDeactivated, onBeforeUnmount } from "vue";
-import ImgUpload from "./ImgUpload.vue";
-import { toolbar, plugins, simplePlugins, simpleToolbar, menubar } from "./tinymce";
-import { buildShortUUID } from "/@/utils/uuid";
-import { bindHandlers } from "./helper";
-import { onMountedOrActivated } from "/@/hooks/core/onMountedOrActivated";
-import { useDesign } from "/@/hooks/web/useDesign";
-import { isNumber } from "/@/utils/is";
-import { useLocale } from "/@/locales/useLocale";
-import { useAppStore } from "/@/store/modules/app";
-import { uploadFile } from "/@/api/common/api";
-import { getFileAccessHttpUrl } from "/@/utils/common/compUtils";
+import 'tinymce/plugins/template';
+import 'tinymce/plugins/textpattern';
+import 'tinymce/plugins/visualblocks';
+import 'tinymce/plugins/visualchars';
+import 'tinymce/plugins/wordcount';
+import 'tinymce/plugins/image';
+import 'tinymce/plugins/table';
+import 'tinymce/plugins/textcolor';
+import 'tinymce/plugins/contextmenu';
+import {
+  defineComponent,
+  computed,
+  nextTick,
+  ref,
+  unref,
+  watch,
+  onDeactivated,
+  onBeforeUnmount
+} from 'vue';
+import ImgUpload from './ImgUpload.vue';
+import {toolbar, plugins, simplePlugins, simpleToolbar, menubar} from './tinymce';
+import {buildShortUUID} from '/@/utils/uuid';
+import {bindHandlers} from './helper';
+import {onMountedOrActivated} from '/@/hooks/core/onMountedOrActivated';
+import {useDesign} from '/@/hooks/web/useDesign';
+import {isNumber} from '/@/utils/is';
+import {useLocale} from '/@/locales/useLocale';
+import {useAppStore} from '/@/store/modules/app';
+import {uploadFile} from '/@/api/common/api';
+import {getFileAccessHttpUrl} from '/@/utils/common/compUtils';
 import success from "@/views/demo/page/result/success/index.vue";
-
+import {message} from 'ant-design-vue';
 
 const tinymceProps = {
   path: {
-    type: String
+    type: String,
   },
   options: {
     type: Object as PropType<Partial<RawEditorSettings>>,
-    default: {}
+    default: {},
   },
   value: {
-    type: String
+    type: String,
   },
 
   toolbar: {
     type: [Array as PropType<string[]>, String],
-    default: toolbar
+    default: toolbar,
   },
   plugins: {
     type: Array as PropType<string[]>,
-    default: plugins
+    default: plugins,
   },
   menubar: {
     type: [Object, String],
-    default: menubar
+    default: menubar,
   },
   modelValue: {
-    type: String
+    type: String,
   },
   height: {
     type: [Number, String] as PropType<string | number>,
     required: false,
-    default: 400
+    default: 400,
   },
   width: {
     type: [Number, String] as PropType<string | number>,
     required: false,
-    default: "auto"
+    default: 'auto',
   },
   showImageUpload: {
     type: Boolean,
-    default: true
-  }
+    default: true,
+  },
 };
 
 export default defineComponent({
-  name: "Tinymce",
-  components: { ImgUpload },
+  name: 'Tinymce',
+  components: {ImgUpload},
   inheritAttrs: false,
   props: tinymceProps,
-  emits: ["change", "update:modelValue", "inited", "init-error"],
-  setup(props, { emit, attrs }) {
-    const spinning = ref(false);
+  emits: ['change', 'update:modelValue', 'inited', 'init-error'],
+  setup(props, {emit, attrs}) {
+    const spinning = ref<Boolean>(false);
     const editorRef = ref<Nullable<Editor>>(null);
     const fullscreen = ref(false);
-    const tinymceId = ref<string>(buildShortUUID("tiny-vue"));
+    const tinymceId = ref<string>(buildShortUUID('tiny-vue'));
     const elRef = ref<Nullable<HTMLElement>>(null);
 
-    const { prefixCls } = useDesign("tinymce-container");
+    const {prefixCls} = useDesign('tinymce-container');
 
     const appStore = useAppStore();
 
@@ -146,111 +153,171 @@ export default defineComponent({
     });
 
     const skinName = computed(() => {
-      return appStore.getDarkMode === "light" ? "jeecg" : "oxide-dark";
+      return appStore.getDarkMode === 'light' ? 'jeecg' : 'oxide-dark';
     });
 
     const langName = computed(() => {
       const lang = useLocale().getLocale.value;
-      return ["zh_CN", "en"].includes(lang) ? lang : "zh_CN";
+      return ['zh_CN', 'en'].includes(lang) ? lang : 'zh_CN';
     });
 
     const initOptions = computed((): RawEditorSettings => {
-      const { height, options, toolbar, plugins, menubar } = props;
-      const publicPath = import.meta.env.VITE_PUBLIC_PATH || "/";
+      const {height, options, toolbar, plugins, menubar} = props;
+      const publicPath = import.meta.env.VITE_PUBLIC_PATH || '/';
       return {
         selector: `#${unref(tinymceId)}`,
         height,
         toolbar,
         menubar: menubar,
         plugins,
-        language_url: publicPath + "resource/tinymce/langs/" + langName.value + ".js",
+        language_url: publicPath + 'resource/tinymce/langs/' + langName.value + '.js',
         language: langName.value,
         branding: false,
-        default_link_target: "_blank",
+        default_link_target: '_blank',
         link_title: false,
         object_resizing: true,
         relative_urls: false,
         remove_script_host: false,
         convert_urls: false,
-        toolbar_mode: "sliding",
+        toolbar_mode: 'sliding',
         auto_focus: true,
         toolbar_groups: true,
         skin: skinName.value,
-        skin_url: publicPath + "resource/tinymce/skins/ui/" + skinName.value,
+        skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value,
         images_upload_handler: (blobInfo, success) => {
           let params = {
             file: blobInfo.blob(),
             filename: blobInfo.filename(),
-            data: { biz: "jeditor", jeditor: "1" }
+            data: {biz: 'jeditor', jeditor: '1'},
           };
           const uploadSuccess = (res) => {
             if (res.success) {
-              if (res.message == "local") {
-                const img = "data:image/jpeg;base64," + blobInfo.base64();
+              if (res.message == 'local') {
+                const img = 'data:image/jpeg;base64,' + blobInfo.base64();
                 success(img);
               } else {
                 let img = getFileAccessHttpUrl(res.message);
                 success(img);
               }
+            } else {
+              message.error("上传文件超时,请务必在两分钟内上传完毕")
+              return;
             }
           };
           uploadFile(params, uploadSuccess);
         },
         file_picker_callback: (callback, value, meta) => {
+          let fileName = ""
+          spinning.value = true;
+          const tox = document.getElementsByClassName("tox-dialog")[0];
+          tox.style.zIndex = -20;
+          // console.log(meta.filetype)
           const uploadSuccess = (res) => {
             if (res.success) {
-              let fileUrl = "/jeecgboot/sys/common/file/" + res.message;
-              callback(fileUrl);
+              let fileUrl = '/jeecgboot/sys/common/file/' + res.message;
+              callback(fileUrl, {text: fileName})
+            } else {
+              message.error("上传文件超时,请务必在两分钟内上传完毕")
+              return;
             }
           };
-          if (meta.filetype === "file") {
-            const input = document.createElement("input");
-            input.setAttribute("type", "file");
-            input.onchange = async function() {
-              const file = this.files[0]; // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
+          if (meta.filetype === 'file') {
+            const input = document.createElement('input')
+            input.setAttribute('type', 'file')
+            input.oncancel = async function () {
+              spinning.value = false;
+              tox.style.zIndex = 2;
+            }
+            input.onchange = async function () {
+              const file = this.files[0] // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
+              fileName = file.name
+              if (file.size / 1024 / 1024 > 2048) {
+                message.error("文件大小超过2G,请压缩或降低质量")
+                spinning.value = false;
+                tox.style.zIndex = 2;
+                return
+              }
               //后缀名
-              const extension = file.name.substring(file.name.lastIndexOf(".") + 1);
+              const extension = file.name.substring(file.name.lastIndexOf('.') + 1)
               let params = {
                 file: file,
                 filename: file.name,
-                data: { biz: extension, jeditor: "1" }
+                data: {biz: extension, jeditor: '1'},
               };
-              uploadFile(params, uploadSuccess);
-            };
-            input.click();
+              uploadFile(params, uploadSuccess).finally(() => {
+                spinning.value = false;
+                tox.style.zIndex = 2;
+              });
+            }
+            input.click()
           }
-          if (meta.filetype === "media") {
-            const input = document.createElement("input");
-            input.setAttribute("type", "file");
-            input.onchange = async function() {
-              const file = this.files[0]; // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
+          if (meta.filetype === 'media') {
+            const input = document.createElement('input')
+            input.setAttribute('type', 'file')
+            //设置上传视频的类型
+            input.setAttribute('accept', ".mp4");
+            input.oncancel = async function () {
+              spinning.value = false;
+              tox.style.zIndex = 2;
+            }
+            input.onchange = async function () {
+              const file = this.files[0] // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
+              fileName = file.name
+              if (file.size / 1024 / 1024 > 2048) {
+                spinning.value = false;
+                tox.style.zIndex = 2;
+                message.error("视频大小超过2G,请压缩或降低质量")
+                return
+              }
               let params = {
                 file: file,
                 filename: file.name,
-                data: { biz: "video", jeditor: "1" }
+                data: {biz: 'video', jeditor: '1'},
               };
-              const tox = document.getElementsByClassName("tox-dialog")[0];
-              const spin = document.getElementById("spinId");
-
-              tox.style.zIndex = -20;
-              spinning.value = true;
-              console.log(tox);
-              uploadFile(params, uploadSuccess).then(res => {
+              uploadFile(params, uploadSuccess).finally(() => {
+                spinning.value = false;
+                tox.style.zIndex = 2;
+              });
+            }
+            input.click()
+          }
+          if (meta.filetype === 'image') {
+            const input = document.createElement('input')
+            input.setAttribute('type', 'file')
+            //设置上传视频的类型
+            input.setAttribute('accept', ".jpg, .jpeg, .png, .gif");
+            input.oncancel = async function () {
+              spinning.value = false;
+              tox.style.zIndex = 2;
+            }
+            input.onchange = async function () {
+              const file = this.files[0] // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
+              fileName = file.name
+              if (file.size / 1024 / 1024 > 100) {
+                message.error("图片大小超过100MB,请压缩或降低质量")
                 spinning.value = false;
                 tox.style.zIndex = 2;
-              }).catch(error => {
+                return
+              }
+              let params = {
+                file: file,
+                filename: file.name,
+                data: {biz: 'jeditor', jeditor: '1'},
+              };
+              uploadFile(params, uploadSuccess).finally(() => {
                 spinning.value = false;
                 tox.style.zIndex = 2;
               });
-            };
-            input.click();
+            }
+            input.click()
           }
+
         },
-        file_url_resolver: function(data, resolve) {
+        file_url_resolver: function (data, resolve) {
 
         },
         //自定义逻辑替换 Tinymce 的默认媒体嵌入逻辑
-        media_url_resolver: function(data, resolve) {
+        media_url_resolver: function (data, resolve) {
           try {
             let videoUri = encodeURI(data.url);
             let embedHtml = `<p>
@@ -263,33 +330,33 @@ export default defineComponent({
                 data-mce-p-allowfullscreen="true"
                 data-mce-p-oncontextmenu="return false;"
                 data-mce-p-controlslist="nodownload"
-                data-mce-p-src="${videoUri}" >
-                <video src="${data.url}" width="800" height="600" controls="controls" >
+                data-mce-p-src=${videoUri} >
+                <video src=${data.url} width="800" height="600" controls="controls" >
                 </video>
             </span>
         </p>
         <p style="text-align: left;"></p>`;
-            resolve({ html: embedHtml });
+            resolve({html: embedHtml});
           } catch (e) {
-            resolve({ html: "" });
+            resolve({html: ""});
           }
         },
-        content_css: publicPath + "resource/tinymce/skins/ui/" + skinName.value + "/content.min.css",
+        content_css: publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css',
         ...options,
         setup: (editor: Editor) => {
           editorRef.value = editor;
-          editor.on("init", (e) => initSetup(e));
-        }
+          editor.on('init', (e) => initSetup(e));
+        },
       };
     });
 
     const disabled = computed(() => {
-      const { options } = props;
-      const getdDisabled = options && Reflect.get(options, "readonly");
+      const {options} = props;
+      const getdDisabled = options && Reflect.get(options, 'readonly');
       const editor = unref(editorRef);
       // update-begin-author:taoyan date:20220407 for: 设置disabled,图片上传没有被禁用
       if (editor) {
-        editor.setMode(getdDisabled || attrs.disabled === true ? "readonly" : "design");
+        editor.setMode(getdDisabled || attrs.disabled === true ? 'readonly' : 'design');
       }
       if (attrs.disabled === true) {
         return true;
@@ -305,13 +372,13 @@ export default defineComponent({
         if (!editor) {
           return;
         }
-        editor.setMode(attrs.disabled ? "readonly" : "design");
+        editor.setMode(attrs.disabled ? 'readonly' : 'design');
       }
     );
 
     onMountedOrActivated(() => {
       if (!initOptions.value.inline) {
-        tinymceId.value = buildShortUUID("tiny-vue");
+        tinymceId.value = buildShortUUID('tiny-vue');
       }
       nextTick(() => {
         setTimeout(() => {
@@ -337,15 +404,15 @@ export default defineComponent({
     function initEditor() {
       const el = unref(elRef);
       if (el) {
-        el.style.visibility = "";
+        el.style.visibility = '';
       }
       tinymce
         .init(unref(initOptions))
         .then((editor) => {
-          emit("inited", editor);
+          emit('inited', editor);
         })
         .catch((err) => {
-          emit("init-error", err);
+          emit('init-error', err);
         });
     }
 
@@ -354,7 +421,7 @@ export default defineComponent({
       if (!editor) {
         return;
       }
-      const value = props.modelValue || "";
+      const value = props.modelValue || '';
 
       editor.setContent(value);
       bindModelHandlers(editor);
@@ -362,14 +429,14 @@ export default defineComponent({
     }
 
     function setValue(editor: Recordable, val: string, prevVal?: string) {
-      if (editor && typeof val === "string" && val !== prevVal && val !== editor.getContent({ format: attrs.outputFormat })) {
+      if (editor && typeof val === 'string' && val !== prevVal && val !== editor.getContent({format: attrs.outputFormat})) {
         editor.setContent(val);
       }
     }
 
     function bindModelHandlers(editor: any) {
       const modelEvents = attrs.modelEvents ? attrs.modelEvents : null;
-      const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(" ") : modelEvents;
+      const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents;
 
       watch(
         () => props.modelValue,
@@ -384,17 +451,17 @@ export default defineComponent({
           setValue(editor, val, prevVal);
         },
         {
-          immediate: true
+          immediate: true,
         }
       );
 
-      editor.on(normalizedEvents ? normalizedEvents : "change keyup undo redo", () => {
-        const content = editor.getContent({ format: attrs.outputFormat });
-        emit("update:modelValue", content);
-        emit("change", content);
+      editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
+        const content = editor.getContent({format: attrs.outputFormat});
+        emit('update:modelValue', content);
+        emit('change', content);
       });
 
-      editor.on("FullscreenStateChanged", (e) => {
+      editor.on('FullscreenStateChanged', (e) => {
         fullscreen.value = e.state;
       });
     }
@@ -404,8 +471,8 @@ export default defineComponent({
       if (!editor) {
         return;
       }
-      editor.execCommand("mceInsertContent", false, getUploadingImgName(name));
-      const content = editor?.getContent() ?? "";
+      editor.execCommand('mceInsertContent', false, getUploadingImgName(name));
+      const content = editor?.getContent() ?? '';
       setValue(editor, content);
     }
 
@@ -414,8 +481,8 @@ export default defineComponent({
       if (!editor) {
         return;
       }
-      const content = editor?.getContent() ?? "";
-      const val = content?.replace(getUploadingImgName(name), `<img src="${url}"/>`) ?? "";
+      const content = editor?.getContent() ?? '';
+      const val = content?.replace(getUploadingImgName(name), `<img src="${url}"/>`) ?? '';
       setValue(editor, val);
     }
 
@@ -436,9 +503,9 @@ export default defineComponent({
       editorRef,
       fullscreen,
       disabled,
-      spinning
+      spinning,
     };
-  }
+  },
 });
 </script>
 
@@ -451,18 +518,19 @@ export default defineComponent({
   position: relative;
   line-height: normal;
 
-  .tinymce-upload-progress-box {
-    position: absolute !important;
-    text-align: center;
-    top: 50%;
-    left: 50%;
-    margin-top: -62px !important;;
-    margin-left: -60px !important;;
-  }
-
   textarea {
     z-index: -1;
     visibility: hidden;
   }
 }
+
+.tinymce-upload-progress-box {
+  position: absolute !important;
+  text-align: center;
+  top: 50%;
+  left: 50%;
+  z-index: 200;
+  margin-top: -350px !important;;
+  margin-left: -100px !important;;
+}
 </style>

+ 29 - 10
src/views/kms/knowledge/article/edit.vue

@@ -27,7 +27,7 @@
                 placeholder="请选择标签分类"
               ></JSelectMultiple>
             </a-col>
-            <a-col :span="4" :offset="2">
+            <a-col :span="5" :offset="1">
               <a-button @click="addArticle">新增</a-button>
               <a-button @click="saveArticle" style="margin-left: 5px">保存</a-button>
               <a-popconfirm
@@ -73,13 +73,15 @@
                       showCount
                       :auto-size="{ minRows: 5, maxRows: 5 }"
                       style="width: 100%;margin-left: 10px"
+                      @change="change"
                     />
                   </div>
                 </a-row>
               </div>
               <div class="article-edit-title" style="margin-top:5px;margin-bottom:5px">
                 <a-row>
-                  <a-input v-model:value="title" placeholder="请输入标题"></a-input>
+                  <a-input v-model:value="title" placeholder="请输入标题" @change="change"
+                  ></a-input>
                 </a-row>
               </div>
               <div class="article-edit-content">
@@ -87,6 +89,8 @@
                   path="kms/video"
                   height="500px"
                   v-model:value="content"
+                  @change="change"
+
                 >
                 </JEditor>
               </div>
@@ -117,6 +121,7 @@ let title = ref<string>("");
 let content = ref<string>("");
 let cover = ref<string[]>([]);
 let categoryId = ref<string>("");
+let status = ref<string>("0")
 const route = useRoute();
 const articleId = ref(route.params?.articleId)
 let tags = ref<string>();
@@ -131,6 +136,10 @@ onMounted(() => {
   loadArticleData(articleId.value);
 });
 
+function change() {
+  status.value = "-1"
+}
+
 async function loadArticleData(articleId) {
   let param = {id: articleId};
   await loadArticleById(param).then(res => {
@@ -140,7 +149,7 @@ async function loadArticleData(articleId) {
     categoryId.value = res.categoryId;
     guide.value = res.guide;
     tags.value = res.tags.join(",")
-    // console.log("tags  "+tags.value+"typeof   "+typeof tags.value)
+    console.log("tags  " + tags.value + "typeof   " + typeof tags.value)
   });
 }
 
@@ -149,10 +158,14 @@ function showDraftBox() {
 }
 
 function addArticle() {
-  setTimeout(() => {
-    go("/kms/knowledge/article/write")
-    // message.success("文章发布成功!请等待管理员审核~");
-  }, 500);
+  if (status.value == "-1") {
+    message.warning("请先保存/发布填写的内容后再新增")
+  } else {
+    setTimeout(() => {
+      go("/kms/knowledge/article/write")
+      // message.success("文章发布成功!请等待管理员审核~");
+    }, 500);
+  }
 }
 
 function saveArticle() {
@@ -165,12 +178,14 @@ function saveArticle() {
     param.value.cover = cover.value;
     param.value.categoryId = categoryId.value;
     param.value.status = 0;
+    param.value.tags = tags.value?.split(",")
     saveOrUpdate(param.value, true).then(res => {
       message.success("文章保存成功!");
       articleId.value = res.id;
-      // setTimeout(() => {
-      //   go("/kms/knowledge/article/draftbox");
-      // }, 500);
+      status.value = "0";
+      setTimeout(() => {
+        go("/kms/knowledge/article/draftbox");
+      }, 500);
     });
   }
 }
@@ -185,7 +200,9 @@ function release() {
     param.value.cover = cover.value;
     param.value.categoryId = categoryId.value;
     param.value.status = 1;
+    param.value.tags = tags.value?.split(",")
     saveOrUpdate(param.value, true).then(res => {
+      status.value = "0";
       handleSave()
     });
   }
@@ -233,7 +250,9 @@ function cancel() {
     param.value.cover = cover.value;
     param.value.categoryId = categoryId.value;
     param.value.status = 1;
+    param.value.tags = tags.value?.split(",")
     saveOrUpdate(param.value, true).then(res => {
+      status.value = "0";
       setTimeout(() => {
         go("/kms/index")
         message.success("文章发布成功!请等待管理员审核~");

+ 4 - 7
src/views/kms/knowledge/article/index.vue

@@ -101,12 +101,10 @@
 <!--            </div>-->
 
           </div>
-          <a-row style="height: 200px;">
-            <a-col>
-              <div style="text-align: center">
+          <div style="text-align: center">
                 <div class="nr_center">
                   <div class="foot_rgt">
-                    <a href="http://www.lttc.cn/stepin/" target=“_blank>走进鲁泰</a> |
+                    <a href="http://www.lttc.cn/stepin/" target=“_blank>走进鲁泰</a> | 
                     <a href="http://www.lttc.cn/culture/" target=“_blank>企业文化</a> |
                     <a href="http://www.lttc.cn/news/" target=“_blank>新闻中心</a> |
                     <a href="http://www.lttc.cn/investor/" target=“_blank>投资者关系</a> |
@@ -121,8 +119,7 @@
                   </div>
                 </div>
               </div>
-            </a-col>
-          </a-row>
+          
           <!-- <a-divider/> -->
           <!-- <div class="comment">
           </div> -->
@@ -313,8 +310,8 @@ onMounted(() => {
 
 .nr_center {
   width: 1000px;
+  bottom: 0;
   margin: 0 auto;
-  margin-top: 35px;
 }
 
 .foot_rgt {

+ 3 - 3
src/views/kms/knowledge/article/write.vue

@@ -42,7 +42,7 @@
                 placeholder="请选择标签分类"
               ></JSelectMultiple>
             </a-col>
-            <a-col :span="4" :offset="2">
+            <a-col :span="5" :offset="1">
               <a-button @click="addArticle">新增</a-button>
               <a-button @click="saveArticle" style="margin-left: 5px">保存</a-button>
               <a-popconfirm
@@ -153,7 +153,7 @@ function change() {
 
 function addArticle() {
   if (status.value == "-1") {
-    message.warning("请先保存/提交填写的内容后再新增")
+    message.warning("请先保存/发布填写的内容后再新增")
   } else {
     reload()
   }
@@ -262,7 +262,7 @@ function validate() {
   //   message.error("请选择文章封面!");
   //   return false;
 
-  if(!tags.value){
+  if (!tags.value) {
     message.error("请选择标签分类!");
     return false;
   }