Browse Source

jeecgboot-vue 1.0.0 版本发布

zhangdaiscott 3 years ago
parent
commit
a2d39b4363
100 changed files with 7881 additions and 1291 deletions
  1. 18 4
      .env
  2. 10 8
      .env.development
  3. 7 8
      .env.production
  4. 14 19
      .env.test
  5. 1 1
      README.md
  6. 1 1
      build/config/themeConfig.ts
  7. 10 9
      build/script/buildConf.ts
  8. 7 7
      build/utils.ts
  9. 5 1
      build/vite/plugin/index.ts
  10. 46 1
      build/vite/plugin/styleImport.ts
  11. 1 1
      build/vite/plugin/theme.ts
  12. 1 1
      build/vite/proxy.ts
  13. 0 137
      doc/修改日志.md
  14. 0 5
      doc/升级sql
  15. 6 2
      index.html
  16. 3 0
      mock/_util.ts
  17. 12 4
      mock/demo/account.ts
  18. 6 6
      mock/demo/select-demo.ts
  19. 93 93
      mock/demo/system.ts
  20. 3 3
      mock/demo/table-demo.ts
  21. 2 2
      mock/demo/tree-demo.ts
  22. 3 3
      mock/sys/menu.ts
  23. 10 11
      mock/sys/user.ts
  24. 68 65
      package.json
  25. 0 1
      prettier.config.js
  26. BIN
      public/logo.png
  27. BIN
      public/resource/img/logo.png
  28. 711 0
      public/resource/tinymce/skins/ui/jeecg/content.css
  29. 705 0
      public/resource/tinymce/skins/ui/jeecg/content.inline.css
  30. 6 0
      public/resource/tinymce/skins/ui/jeecg/content.inline.min.css
  31. 6 0
      public/resource/tinymce/skins/ui/jeecg/content.min.css
  32. 29 0
      public/resource/tinymce/skins/ui/jeecg/content.mobile.css
  33. 7 0
      public/resource/tinymce/skins/ui/jeecg/content.mobile.min.css
  34. BIN
      public/resource/tinymce/skins/ui/jeecg/fonts/tinymce-mobile.woff
  35. 3045 0
      public/resource/tinymce/skins/ui/jeecg/skin.css
  36. 6 0
      public/resource/tinymce/skins/ui/jeecg/skin.min.css
  37. 677 0
      public/resource/tinymce/skins/ui/jeecg/skin.mobile.css
  38. 6 0
      public/resource/tinymce/skins/ui/jeecg/skin.mobile.min.css
  39. 98 0
      src/api/common/api.ts
  40. 5 2
      src/api/demo/account.ts
  41. 1 1
      src/api/demo/select.ts
  42. 10 38
      src/api/demo/system.ts
  43. 1 1
      src/api/demo/table.ts
  44. 1 1
      src/api/demo/tree.ts
  45. 2 2
      src/api/sys/menu.ts
  46. 10 1
      src/api/sys/model/userModel.ts
  47. 15 0
      src/api/sys/upload.ts
  48. 70 3
      src/api/sys/user.ts
  49. BIN
      src/assets/images/cms_bpm.png
  50. BIN
      src/assets/images/cms_oa.png
  51. BIN
      src/assets/images/daiban.png
  52. BIN
      src/assets/images/duban.png
  53. BIN
      src/assets/images/guaz.png
  54. BIN
      src/assets/images/logo.png
  55. BIN
      src/assets/images/nodata.png
  56. BIN
      src/assets/images/pdf4.jpg
  57. BIN
      src/assets/images/zaiban.png
  58. 1 0
      src/components/Application/src/AppLogo.vue
  59. 2 0
      src/components/Button/index.ts
  60. 0 1
      src/components/Button/src/BasicButton.vue
  61. 41 0
      src/components/Button/src/UploadButton.vue
  62. 2 0
      src/components/Button/src/props.ts
  63. 4 0
      src/components/CardList/index.ts
  64. 178 0
      src/components/CardList/src/CardList.vue
  65. 25 0
      src/components/CardList/src/data.ts
  66. 5 0
      src/components/CodeEditor/src/typing.ts
  67. 1 1
      src/components/Cropper/src/CopperModal.vue
  68. 2 2
      src/components/Cropper/src/CropperAvatar.vue
  69. 60 71
      src/components/Dropdown/src/Dropdown.vue
  70. 39 8
      src/components/Excel/src/ImportExcel.vue
  71. 0 4
      src/components/FlowChart/index.ts
  72. 0 158
      src/components/FlowChart/src/FlowChart.vue
  73. 0 162
      src/components/FlowChart/src/FlowChartToolbar.vue
  74. 0 75
      src/components/FlowChart/src/adpterForTurbo.ts
  75. 0 96
      src/components/FlowChart/src/config.ts
  76. 0 11
      src/components/FlowChart/src/enum.ts
  77. 0 14
      src/components/FlowChart/src/types.ts
  78. 0 17
      src/components/FlowChart/src/useFlowContext.ts
  79. 22 1
      src/components/Form/index.ts
  80. 6 22
      src/components/Form/src/BasicForm.vue
  81. 59 3
      src/components/Form/src/componentMap.ts
  82. 130 0
      src/components/Form/src/components/ApiRadioGroup.vue
  83. 9 12
      src/components/Form/src/components/ApiSelect.vue
  84. 1 1
      src/components/Form/src/components/ApiTreeSelect.vue
  85. 9 5
      src/components/Form/src/components/FormAction.vue
  86. 59 53
      src/components/Form/src/components/FormItem.vue
  87. 24 8
      src/components/Form/src/hooks/useAdvanced.ts
  88. 115 96
      src/components/Form/src/hooks/useForm.ts
  89. 12 1
      src/components/Form/src/hooks/useFormEvents.ts
  90. 2 25
      src/components/Form/src/hooks/useFormValues.ts
  91. 7 1
      src/components/Form/src/hooks/useLabelWidth.ts
  92. 119 0
      src/components/Form/src/jeecg/components/JAddInput.vue
  93. 1 1
      src/components/Form/src/jeecg/components/JAreaLinkage.vue
  94. 153 0
      src/components/Form/src/jeecg/components/JAreaSelect.vue
  95. 256 0
      src/components/Form/src/jeecg/components/JCategorySelect.vue
  96. 78 0
      src/components/Form/src/jeecg/components/JCheckbox.vue
  97. 246 0
      src/components/Form/src/jeecg/components/JCodeEditor.vue
  98. 107 0
      src/components/Form/src/jeecg/components/JDictSelectTag.vue
  99. 305 0
      src/components/Form/src/jeecg/components/JEasyCron/EasyCronInner.vue
  100. 63 0
      src/components/Form/src/jeecg/components/JEasyCron/EasyCronInput.vue

+ 18 - 4
.env

@@ -1,8 +1,22 @@
 # port
 VITE_PORT = 3100
 
-# spa-title
-VITE_GLOB_APP_TITLE = Jeecg Boot
+#  网站标题
+VITE_GLOB_APP_TITLE = JeecgBoot 企业级低代码平台
+
+# 简称,用于配置文件名字 不要出现空格、数字开头等特殊字符
+VITE_GLOB_APP_SHORT_NAME = JeecgBootAdmin
+
+# 单点登录服务端地址
+VITE_GLOBE_APP_CAS_BASE_URL=http://cas.test.com:8443/cas
+
+# 是否开启单点登录
+VITE_GLOB_APP_OPEN_SSO = false
+
+# 开启微前端模式
+VITE_GLOB_APP_OPEN_QIANKUN=true
+
+# 文件预览地址
+VITE_GLOB_ONLINE_VIEW_URL=http://fileview.jeecg.com/onlinePreview
+
 
-# spa shortname
-VITE_GLOB_APP_SHORT_NAME = Jeecg-Boot-Admin

+ 10 - 8
.env.development

@@ -1,21 +1,23 @@
 # 是否打开mock
-VITE_USE_MOCK = false
+VITE_USE_MOCK = true
 
-# 公共路径
+# 发布路径
 VITE_PUBLIC_PATH = /
 
 # 跨域代理,您可以配置多个 ,请注意,没有换行符
- VITE_PROXY = [["/jeecg-boot","http://localhost:8080/jeecg-boot"],["/upload","http://localhost:3300/upload"]]
-# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
+VITE_PROXY = [["/jeecgboot","http://localhost:8080/jeecg-boot"],["/upload","http://localhost:3300/upload"]]
 
 # 控制台不输出
 VITE_DROP_CONSOLE = false
 
-# 接口父路径
-VITE_GLOB_API_URL= /jeecg-boot
+#后台接口父地址(必填)
+VITE_GLOB_API_URL=/jeecgboot
 
-# 文件上次地址(可选)
-VITE_GLOB_UPLOAD_URL=/upload
+#后台接口全路径地址(必填)
+VITE_GLOB_DOMAIN_URL=http://localhost:8080/jeecg-boot
 
 # 接口前缀
 VITE_GLOB_API_URL_PREFIX=
+
+#微前端qiankun应用,命名必须以VITE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径
+VITE_APP_SUB_jeecg-app-1 = '//localhost:8092'

+ 7 - 8
.env.production

@@ -1,7 +1,7 @@
 # 是否启用mock
 VITE_USE_MOCK = true
 
-# 公共路径
+# 发布路径
 VITE_PUBLIC_PATH = /
 
 # 控制台不输出
@@ -10,19 +10,18 @@ VITE_DROP_CONSOLE = true
 # 是否启用gzip或brotli压缩
 # 选项值: gzip | brotli | none
 # 如果需要多个可以使用“,”分隔
-VITE_BUILD_COMPRESS = 'none'
+VITE_BUILD_COMPRESS = 'gzip'
 
 # 使用压缩时是否删除原始文件,默认为false
 VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
 
-# 接口父路径
-VITE_GLOB_API_URL=/basic-api
+#后台接口父地址(必填)
+VITE_GLOB_API_URL=/jeecgboot
 
-# 文件上次地址(可选)
-# 它可以由nginx转发,也可以直接写入实际地址
-VITE_GLOB_UPLOAD_URL=/upload
+#后台接口全路径地址(必填)
+VITE_GLOB_DOMAIN_URL=http://api3.boot.jeecg.com
 
-# 接口父路径
+# 接口父路径前缀
 VITE_GLOB_API_URL_PREFIX=
 
 # 是否启用图像压缩

+ 14 - 19
.env.test

@@ -1,36 +1,31 @@
-NODE_ENV=production
-# Whether to open mock
+# 是否启用mock
 VITE_USE_MOCK = true
 
-# public path
+# 发布路径
 VITE_PUBLIC_PATH = /
 
-# Delete console
+# 控制台不输出
 VITE_DROP_CONSOLE = true
 
-# Whether to enable gzip or brotli compression
-# Optional: gzip | brotli | none
-# If you need multiple forms, you can use `,` to separate
-VITE_BUILD_COMPRESS = 'none'
+# 是否启用gzip或brotli压缩
+# 选项值: gzip | brotli | none
+# 如果需要多个可以使用“,”分隔
+VITE_BUILD_COMPRESS = 'gzip'
 
-# Whether to delete origin files when using compress, default false
+# 使用压缩时是否删除原始文件,默认为false
 VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
 
-# Basic interface address SPA
-VITE_GLOB_API_URL=/basic-api
+#后台接口地址(必填)
+VITE_GLOB_DOMAIN_URL=http://localhost:8080/jeecg-boot
 
-# File upload address, optional
-# It can be forwarded by nginx or write the actual address directly
-VITE_GLOB_UPLOAD_URL=/upload
-
-# Interface prefix
+# 接口父路径前缀
 VITE_GLOB_API_URL_PREFIX=
 
-# Whether to enable image compression
+# 是否启用图像压缩
 VITE_USE_IMAGEMIN= true
 
-# use pwa
+# 使用pwa
 VITE_USE_PWA = false
 
-# Is it compatible with older browsers
+# 是否兼容旧浏览器
 VITE_LEGACY = false

+ 1 - 1
README.md

@@ -5,7 +5,7 @@
 JEECG BOOT 低代码平台(Vue3前端版本)
 ===============
 
-当前最新版本: 1.0.0-beta(发布日期:未正式发布
+当前最新版本: 1.0.0-beta(预计发布日期 20220321
 
 
 ## 简介

+ 1 - 1
build/config/themeConfig.ts

@@ -1,6 +1,6 @@
 import { generate } from '@ant-design/colors';
 
-export const primaryColor = '#0960bd';
+export const primaryColor = '#1890FF';
 
 export const darkMode = 'light';
 

+ 10 - 9
build/script/buildConf.ts

@@ -5,18 +5,19 @@ import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
 import fs, { writeFileSync } from 'fs-extra';
 import chalk from 'chalk';
 
-import { getRootPath, getEnvConfig } from '../utils';
+import { getEnvConfig, getRootPath } from '../utils';
 import { getConfigFileName } from '../getConfigFileName';
 
 import pkg from '../../package.json';
 
-function createConfig(
-  {
-    configName,
-    config,
-    configFileName = GLOB_CONFIG_FILE_NAME,
-  }: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} }
-) {
+interface CreateConfigParams {
+  configName: string;
+  config: any;
+  configFileName?: string;
+}
+
+function createConfig(params: CreateConfigParams) {
+  const { configName, config, configFileName } = params;
   try {
     const windowConf = `window.${configName}`;
     // Ensure that the variable will not be modified
@@ -40,5 +41,5 @@ function createConfig(
 export function runBuildConfig() {
   const config = getEnvConfig();
   const configFileName = getConfigFileName(config);
-  createConfig({ config, configName: configFileName });
+  createConfig({ config, configName: configFileName, configFileName: GLOB_CONFIG_FILE_NAME });
 }

+ 7 - 7
build/utils.ts

@@ -28,12 +28,12 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
         if (envName === 'VITE_PORT') {
             realName = Number(realName);
         }
-        if (envName === 'VITE_PROXY') {
-            try {
-                realName = JSON.parse(realName);
-            } catch (error) {
-                realName = '';
-            }
+        if (envName === 'VITE_PROXY' && realName) {
+          try {
+            realName = JSON.parse(realName.replace(/'/g, '"'));
+          } catch (error) {
+            realName = '';
+          }
         }
         ret[envName] = realName;
         if (typeof realName === 'string') {
@@ -50,7 +50,7 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
  */
 function getConfFiles() {
     const script = process.env.npm_lifecycle_script;
-    const reg = new RegExp('--mode ([a-z]+)');
+    const reg = new RegExp('--mode ([a-z_\\d]+)');
     const result = reg.exec(script as string) as any;
     if (result) {
         const mode = result[1] as string;

+ 5 - 1
build/vite/plugin/index.ts

@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
 import legacy from '@vitejs/plugin-legacy';
 import purgeIcons from 'vite-plugin-purge-icons';
 import windiCSS from 'vite-plugin-windicss';
+import vueSetupExtend from 'vite-plugin-vue-setup-extend';
 import { configHtmlPlugin } from './html';
 import { configPwaConfig } from './pwa';
 import { configMockPlugin } from './mock';
@@ -29,7 +30,10 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
         vue(),
         // have to
         vueJsx(),
+        // support name
+        vueSetupExtend(),
     ];
+
     // vite-plugin-windicss
     vitePlugins.push(windiCSS());
 
@@ -67,7 +71,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
 
         // rollup-plugin-gzip
         vitePlugins.push(
-            configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE)
+            configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE),
         );
 
         // vite-plugin-pwa

+ 46 - 1
build/vite/plugin/styleImport.ts

@@ -14,7 +14,52 @@ export function configStyleImportPlugin(isBuild: boolean) {
         libraryName: 'ant-design-vue',
         esModule: true,
         resolveStyle: (name) => {
-          return `ant-design-vue/es/${name}/style/index`;
+          // 这里是“子组件”列表,无需额外引入样式文件
+          const ignoreList = [
+            'typography-text',
+            'typography-title',
+            'typography-paragraph',
+            'typography-link',
+            'anchor-link',
+            'sub-menu',
+            'menu-item',
+            'menu-item-group',
+            'dropdown-button',
+            'breadcrumb-item',
+            'breadcrumb-separator',
+            'input-password',
+            'input-search',
+            'input-group',
+            'form-item',
+            'radio-group',
+            'checkbox-group',
+            'layout-sider',
+            'layout-content',
+            'layout-footer',
+            'layout-header',
+            'step',
+            'select-option',
+            'select-opt-group',
+            'card-grid',
+            'card-meta',
+            'collapse-panel',
+            'descriptions-item',
+            'list-item',
+            'list-item-meta',
+            'table-column',
+            'table-column-group',
+            'tab-pane',
+            'tab-content',
+            'timeline-item',
+            'tree-node',
+            'skeleton-input',
+            'skeleton-avatar',
+            'skeleton-title',
+            'skeleton-paragraph',
+            'skeleton-image',
+            'skeleton-button',
+          ];
+          return ignoreList.includes(name) ? '' : `ant-design-vue/es/${name}/style/index`;
         },
       },
     ],

+ 1 - 1
build/vite/plugin/theme.ts

@@ -66,7 +66,7 @@ export function configThemePlugin(isBuild: boolean): Plugin[] {
                 'border-color-base': '#303030',
                 // 'border-color-split': '#30363d',
                 'item-active-bg': '#111b26',
-                'app-content-background': 'rgb(255 255 255 / 4%)',
+                'app-content-background': '#1e1e1e',
                 'tree-node-selected-bg': '#11263c',
 
                 'alert-success-border-color': '#274916',

+ 1 - 1
build/vite/proxy.ts

@@ -7,7 +7,7 @@ type ProxyItem = [string, string];
 
 type ProxyList = ProxyItem[];
 
-type ProxyTargetList = Record<string, ProxyOptions & { rewrite: (path: string) => string }>;
+type ProxyTargetList = Record<string, ProxyOptions>;
 
 const httpsRE = /^https:\/\//;
 

+ 0 - 137
doc/修改日志.md

@@ -1,137 +0,0 @@
-## ✨ 功能优化---zhangyafei---2021-07-19
-- **添加增删改查demo**
-  - src/views/demo/system/test/TestDrawer.vue
-  - src/views/demo/system/test/test.data.ts
-  - src/api/demo/model/systemModel.ts
-  - src/api/demo/system.ts
-  - mock/demo/system.ts
-  - src/views/demo/system/test/index.vue
-- **添加代码高亮编辑器** 
-  - src/views/demo/codemirror/index.vue
-- **添加日历组件** 
-  - src/views/demo/fullcalendar/event-utils.ts
-  - src/views/demo/fullcalendar/index.vue
-- **添加vexTable示例** 
-  - src/views/demo/vextable/index.vue
-- **添加JAreaLinkage示例** 
-  - src/components/Form/src/componentMap.ts
-  - src/components/Form/src/types/index.ts
-  - src/components/Form/index.ts
-  - src/views/demo/form/index.vue
-  - src/assets/less/JAreaLinkage.less
-  - src/components/Form/src/components/JAreaLinkage.vue
-- **修改示例路由配置** 
-  - src/router/routes/modules/demo/feat.ts
-  - src/router/routes/modules/demo/comp.ts
-- **修改路由国际化** 
-  - src/locales/lang/zh_CN/routes/demo.ts
-- **全局组件注册** 
-  - src/main.ts
-- **package添加组件依赖** 
-  - package.json
-- **路由跳转暂时屏蔽动画效果有bug冲突** 
-  - src/layouts/page/index.vue
-  
-## ✨ 功能优化---zhangyafei---2021-07-29
-- **添加一对多,一对一示例**
-  - src/views/demo/vextable/VexTableModal.vue
-  - src/views/demo/vextable/OneToOneModal.vue
-  - src/views/demo/vextable/modal.vue
-  - src/views/demo/vextable/index2.vue
-  - src/views/demo/vextable/index.vue
-  - src/views/demo/vextable/drawer.vue
-- **添加嵌套子表格示例**  
-  - src/views/demo/table/NestedTable.vue
-- **常用antd 组件全局注入**
-  - src/components/registerGlobComp.ts
-- **添加权限,指令用法示例代码**
-  - src/views/demo/permission/front/Btn.vue
-- **添加三方组件注册文件**
-  - src/settings/registerThirdComp.ts
-- **main.ts注释汉化**
-  - src/main.ts
-## ✨ 功能优化---liusq---2021-08-19
-- **增加接口token**
-  - src/enums/httpEnum.ts
-  - src/utils/http/axios/index.ts
-- **新增用户管理、角色管理**  
-  - src/views/demo/system/roles/*
-  - src/views/demo/system/user/*
-- **登录验证码功能**  
-  - src/api/demo/model/systemModel.ts
-  - src/api/demo/system.ts
-  - src/api/model/baseModel.ts
-  - src/api/sys/model/userModel.ts
-  - src/api/sys/user.ts
-  - src/locales/lang/zh_CN/routes/demo.ts
-  - src/locales/lang/zh_CN/sys.ts
-  - src/router/routes/modules/demo/system.ts
-  - src/store/modules/user.ts
-- **接口和moke路径修改**
-  - src/mock/demo/account.ts
-  - src/mock/demo/select-demo.ts
-  - src/mock/demo/system.ts
-  - src/mock/demo/table-demo.ts
-  - src/mock/demo/tree-demo.ts
-  - src/mock/sys/menu.ts
-  - src/mock/sys/user.ts
-  - src/mock/_util.ts
-  - src/api/sys/menu.ts
-  - src/api/sys/user.ts
-- **table配置项修改**
-  - src/settings/componentSetting.ts
-- **上传返回值修改**
-  - src/components/Upload/src/UploadModal.vue
-## ✨ 功能完善---zhangyafei---2021-08-27
-- **添加租户功能**
-  -src/views/system/tenant/**
-- **修改antd注册方式,改为全局注册**
-  -src/main.ts
-  -src/settings/registerThirdComp.ts
-- **网络请求类翻译,添加全局操作成功顶部消息提示**
-  -src/utils/http/axios/Axios.ts
-  -src/utils/http/axios/index.ts
-  -src/-types/axios.d.ts
-- **表格选择工具类样式修改**
-  -src/components/Table/src/BasicTable.vue
-- **底层代码优化**
-  - src/api/demo/system.ts
-  - src/api/sys/user.ts
-  - src/router/guard/permissionGuard.ts
-  - src/store/modules/user.ts
-  - src/settings/projectSetting.ts
-  - src/main.ts
-  - mock/sys/user.ts
-  - package.json
-## ✨ 功能完善---liusq---2021-08-27
-- **完善用户管理、角色管理功能**
-  - system/user/UserRecycleBinModal.vue
-  - system/user/UserDrawer.vue
-  - system/user/user.data.ts
-  - system/user/user.api.ts
-  - system/user/index.vue
-  - system/role/UserRoleDrawer.vue
-  - system/role/RoleDrawer.vue
-  - system/role/role.data.ts
-  - system/role/role.api.ts
-  - system/role/index.vue
-  - /locales/lang/zh-CN/routes/demo.ts
-  - /locales/lang/zh-CN/sys.ts
-  - /api/demo/system.ts
-- **完善登录注册功能**
-  - src/api/sys/user.ts
-  - src/store/modules/user.ts
-  - src/views/sys/forget-password/step1.vue
-  - src/views/sys/forget-password/step2.vue
-  - src/views/sys/forget-password/step3.vue
-  - src/views/sys/login/ForgetPasswordForm.vue
-  - src/views/sys/login/MobileForm.vue
-  - src/views/sys/login/RegisterForm.vue
-  - src/views/sys/login/useLogin.ts
-  - src/assets/images/checkcode.png
-  
-## ✨ 还原路由走本地---scott---2021-08-31
--  src\settings\projectSetting.ts
-
-## ✨ 功能完善---zyf---2021-08-31
-测试

+ 0 - 5
doc/升级sql

@@ -1,5 +0,0 @@
-###---zhangyafei---2021-08-31 租户、用户、角色
-update sys_permission set url='/system/tenant' ,component='/system/tenant/index' where id='1280350452934307841';
-update sys_permission set url='/system/user' ,component='/system/user/index' where id='3f915b2769fc80648e92d04e84ca059d';
-update sys_permission set url='/system/role' ,component='/system/role/index' where id='190c2b43bec6a5f7a4194a85db67d96a';
-###---zhangyafei---2021-08-31

+ 6 - 2
index.html

@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html lang="en" id="htmlRoot">
+<html lang="zh_CN" id="htmlRoot">
   <head>
     <meta charset="UTF-8" />
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
@@ -10,7 +10,11 @@
     />
 
     <title><%= title %></title>
-    <link rel="icon" href="/favicon.ico" />
+    <link rel="icon" href="/logo.png" />
+    <!-- 全局配置 -->
+    <script>
+      window._CONFIG = {};
+    </script>
   </head>
   <body>
     <script>

+ 3 - 0
mock/_util.ts

@@ -58,3 +58,6 @@ export interface requestParams {
 export function getRequestToken({ headers }: requestParams): string | undefined {
   return headers?.authorization;
 }
+
+//TODO 接口父路径(写死不够灵活)
+export const baseUrl = '/jeecgboot/mock';

+ 12 - 4
mock/demo/account.ts

@@ -1,6 +1,6 @@
 import { MockMethod } from 'vite-plugin-mock';
-import { resultSuccess, resultError } from '../_util';
-
+import { resultSuccess, resultError, baseUrl } from '../_util';
+import { ResultEnum } from '../../src/enums/httpEnum';
 const userInfo = {
   name: 'Jeecg',
   userid: '00000001',
@@ -44,7 +44,7 @@ const userInfo = {
 
 export default [
   {
-    url: '/jeecg-boot/account/getAccountInfo',
+    url: `${baseUrl}/account/getAccountInfo`,
     timeout: 1000,
     method: 'get',
     response: () => {
@@ -52,11 +52,19 @@ export default [
     },
   },
   {
-    url: '/jeecg-boot/user/sessionTimeout',
+    url: `${baseUrl}/user/sessionTimeout`,
     method: 'post',
     statusCode: 401,
     response: () => {
       return resultError();
     },
   },
+  {
+    url: '/basic-api/user/tokenExpired',
+    method: 'post',
+    statusCode: 200,
+    response: () => {
+      return resultError('Token Expired!', { code: ResultEnum.TIMEOUT as number });
+    },
+  },
 ] as MockMethod[];

+ 6 - 6
mock/demo/select-demo.ts

@@ -1,11 +1,11 @@
 import { MockMethod } from 'vite-plugin-mock';
-import { resultSuccess } from '../_util';
+import { resultSuccess, baseUrl } from '../_util';
 
-const demoList = (keyword) => {
+const demoList = (keyword, count = 20) => {
   const result = {
     list: [] as any[],
   };
-  for (let index = 0; index < 20; index++) {
+  for (let index = 0; index < count; index++) {
     result.list.push({
       name: `${keyword ?? ''}选项${index}`,
       id: `${index}`,
@@ -16,13 +16,13 @@ const demoList = (keyword) => {
 
 export default [
   {
-    url: '/jeecg-boot/select/getDemoOptions',
+    url: `${baseUrl}/select/getDemoOptions`,
     timeout: 1000,
     method: 'get',
     response: ({ query }) => {
-      const { keyword } = query;
+      const { keyword,count} = query;
       console.log(keyword);
-      return resultSuccess(demoList(keyword));
+      return resultSuccess(demoList(keyword,count));
     },
   },
 ] as MockMethod[];

+ 93 - 93
mock/demo/system.ts

@@ -1,5 +1,5 @@
-import {MockMethod} from 'vite-plugin-mock';
-import {resultError, resultPageSuccess, resultSuccess} from '../_util';
+import { MockMethod } from 'vite-plugin-mock';
+import { resultError, resultPageSuccess, resultSuccess, baseUrl } from '../_util';
 
 const accountList = (() => {
     const result: any[] = [];
@@ -150,7 +150,7 @@ const menuList = (() => {
                         permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index],
                         component: [
                             '/dashboard/welcome/index',
-                            '/dashboard/analysis/index',
+                            '/dashboard/Analysis/index',
                             '/dashboard/workbench/index',
                             '/dashboard/test/index',
                         ][j],
@@ -172,7 +172,7 @@ const menuList = (() => {
                                         (k + 1),
                                     component: [
                                         '/dashboard/welcome/index',
-                                        '/dashboard/analysis/index',
+                                        '/dashboard/Analysis/index',
                                         '/dashboard/workbench/index',
                                         '/dashboard/test/index',
                                     ][j],
@@ -195,104 +195,104 @@ const menuList = (() => {
 })();
 
 export default [
-    {
-        url: '/jeecg-boot/system/getAccountList',
-        timeout: 100,
-        method: 'get',
-        response: ({query}) => {
-            const {page = 1, pageSize = 20} = query;
-            return resultPageSuccess(page, pageSize, accountList);
-        },
+  {
+    url: `${baseUrl}/system/getAccountList`,
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, accountList);
     },
-    {
-      url: '/jeecg-boot/sys/user/list',
-      timeout: 100,
-      method: 'get',
-      response: ({query}) => {
-        const {page = 1, pageSize = 20} = query;
-        return resultPageSuccess(page, pageSize, userList);
-      },
+  },
+  {
+    url: `${baseUrl}/sys/user/list`,
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, userList);
     },
-    {
-        url: '/jeecg-boot/system/getRoleListByPage',
-        timeout: 100,
-        method: 'get',
-        response: ({query}) => {
-            const {page = 1, pageSize = 20} = query;
-            return resultPageSuccess(page, pageSize, roleList);
-        },
+  },
+  {
+    url: `${baseUrl}/system/getRoleListByPage`,
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, roleList);
     },
-    {
-        url: '/jeecg-boot/sys/role/list',
-        timeout: 100,
-        method: 'get',
-        response: ({query}) => {
-            const {page = 1, pageSize = 20} = query;
-            return resultPageSuccess(page, pageSize, newRoleList);
-        },
+  },
+  {
+    url: `${baseUrl}/sys/role/list`,
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, newRoleList);
     },
-    {
-        url: '/jeecg-boot/system/getTestListByPage',
-        timeout: 100,
-        method: 'get',
-        response: ({query}) => {
-            const {page = 1, pageSize = 20} = query;
-            return resultPageSuccess(page, pageSize, testList);
-        },
+  },
+  {
+    url: `${baseUrl}/system/getTestListByPage`,
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, testList);
     },
-    {
-        url: '/jeecg-boot/system/getDemoTableListByPage',
-        timeout: 100,
-        method: 'get',
-        response: ({query}) => {
-            const {page = 1, pageSize = 20} = query;
-            return resultPageSuccess(page, pageSize, tableDemoList);
-        },
+  },
+  {
+    url: `${baseUrl}/system/getDemoTableListByPage`,
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, tableDemoList);
     },
-    {
-        url: '/jeecg-boot/system/setRoleStatus',
-        timeout: 500,
-        method: 'post',
-        response: ({query}) => {
-            const {id, status} = query;
-            return resultSuccess({id, status});
-        },
+  },
+  {
+    url: `${baseUrl}/system/setRoleStatus`,
+    timeout: 500,
+    method: 'post',
+    response: ({ query }) => {
+      const { id, status } = query;
+      return resultSuccess({ id, status });
     },
-    {
-        url: '/jeecg-boot/system/getAllRoleList',
-        timeout: 100,
-        method: 'get',
-        response: () => {
-            return resultSuccess(roleList);
-        },
+  },
+  {
+    url: `${baseUrl}/system/getAllRoleList`,
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(roleList);
     },
-    {
-        url: '/jeecg-boot/system/getDeptList',
-        timeout: 100,
-        method: 'get',
-        response: () => {
-            return resultSuccess(deptList);
-        },
+  },
+  {
+    url: `${baseUrl}/system/getDeptList`,
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(deptList);
     },
-    {
-        url: '/jeecg-boot/system/getMenuList',
-        timeout: 100,
-        method: 'get',
-        response: () => {
-            return resultSuccess(menuList);
-        },
+  },
+  {
+    url: `${baseUrl}/system/getMenuList`,
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(menuList);
     },
-    {
-        url: '/jeecg-boot/system/accountExist',
-        timeout: 500,
-        method: 'post',
-        response: ({body}) => {
-            const {account} = body || {};
-            if (account && account.indexOf('admin') !== -1) {
-                return resultError('该字段不能包含admin');
-            } else {
-                return resultSuccess(`${account} can use`);
-            }
-        },
+  },
+  {
+    url: `${baseUrl}/system/accountExist`,
+    timeout: 500,
+    method: 'post',
+    response: ({ body }) => {
+      const { account } = body || {};
+      if (account && account.indexOf('admin') !== -1) {
+        return resultError('该字段不能包含admin');
+      } else {
+        return resultSuccess(`${account} can use`);
+      }
     },
+  },
 ] as MockMethod[];

+ 3 - 3
mock/demo/table-demo.ts

@@ -1,6 +1,6 @@
 import { MockMethod } from 'vite-plugin-mock';
 import { Random } from 'mockjs';
-import { resultPageSuccess } from '../_util';
+import { resultPageSuccess, baseUrl } from '../_util';
 
 function getRandomPics(count = 10): string[] {
   const arr: string[] = [];
@@ -12,7 +12,7 @@ function getRandomPics(count = 10): string[] {
 
 const demoList = (() => {
   const result: any[] = [];
-  for (let index = 0; index < 60; index++) {
+  for (let index = 0; index < 200; index++) {
     result.push({
       id: `${index}`,
       beginTime: '@datetime',
@@ -41,7 +41,7 @@ const demoList = (() => {
 
 export default [
   {
-    url: '/jeecg-boot/table/getDemoList',
+    url: `${baseUrl}/table/getDemoList`,
     timeout: 100,
     method: 'get',
     response: ({ query }) => {

+ 2 - 2
mock/demo/tree-demo.ts

@@ -1,5 +1,5 @@
 import { MockMethod } from 'vite-plugin-mock';
-import { resultSuccess } from '../_util';
+import { resultSuccess, baseUrl } from '../_util';
 
 const demoTreeList = (keyword) => {
   const result = {
@@ -26,7 +26,7 @@ const demoTreeList = (keyword) => {
 
 export default [
   {
-    url: '/jeecg-boot/tree/getDemoOptions',
+    url: `${baseUrl}/tree/getDemoOptions`,
     timeout: 1000,
     method: 'get',
     response: ({ query }) => {

+ 3 - 3
mock/sys/menu.ts

@@ -1,4 +1,4 @@
-import { resultSuccess, resultError, getRequestToken, requestParams } from '../_util';
+import { resultSuccess, resultError, getRequestToken, requestParams,baseUrl} from '../_util';
 import { MockMethod } from 'vite-plugin-mock';
 import { createFakeUserList } from './user';
 
@@ -17,7 +17,7 @@ const dashboardRoute = {
     {
       path: 'analysis',
       name: 'Analysis',
-      component: '/dashboard/analysis/index',
+      component: '/dashboard/Analysis/index',
       meta: {
         hideMenu: true,
         hideBreadcrumb: true,
@@ -237,7 +237,7 @@ const linkRoute = {
 
 export default [
   {
-    url: '/jeecg-boot/sys/permission/getUserPermissionByToken',
+    url: `${baseUrl}/sys/permission/getUserPermissionByToken`,
     timeout: 1000,
     method: 'get',
     response: (request: requestParams) => {

File diff suppressed because it is too large
+ 10 - 11
mock/sys/user.ts


+ 68 - 65
package.json

@@ -1,10 +1,10 @@
 {
-  "name": "jeecg-boot-vue3",
+  "name": "jeecgboot-vue3",
   "version": "1.0.0",
   "author": {
     "name": "jeecg",
     "email": "jeecgos@163.com",
-    "url": "https://gitee.com/jeecg/jeecg-boot-vue3"
+    "url": "https://github.com/jeecgboot/jeecgboot-vue3"
   },
   "scripts": {
     "bootstrap": "yarn install",
@@ -35,28 +35,26 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.0.4",
-    "@logicflow/core": "^0.6.15",
-    "@logicflow/extension": "^0.6.15",
     "@fullcalendar/core": "^5.8.0",
     "@fullcalendar/daygrid": "^5.8.0",
     "@fullcalendar/interaction": "^5.8.0",
     "@fullcalendar/timegrid": "^5.8.0",
     "@fullcalendar/vue3": "^5.8.0",
-    "@vueuse/core": "^6.0.0",
+    "@vueuse/core": "^6.6.2",
     "@zxcvbn-ts/core": "^1.0.0-beta.0",
-    "ant-design-vue": "^2.2.6",
-    "axios": "^0.21.1",
+    "ant-design-vue": "2.2.8",
+    "axios": "^0.23.0",
     "china-area-data": "^5.0.1",
     "clipboard": "^2.0.8",
-    "codemirror": "^5.62.3",
+    "codemirror": "^5.63.3",
     "cron-parser": "^3.5.0",
     "cropperjs": "^1.5.12",
     "crypto-js": "^4.1.1",
     "dayjs": "^1.10.6",
     "dom-align": "^1.12.2",
-    "echarts": "^5.1.2",
+    "echarts": "^5.2.1",
     "enquire.js": "^2.1.6",
-    "intro.js": "^4.1.0",
+    "intro.js": "^4.2.2",
     "js-cookie": "^2.2.1",
     "lodash-es": "^4.17.21",
     "lodash.get": "^4.4.2",
@@ -65,98 +63,103 @@
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.2.0",
-    "pinia": "2.0.0-rc.6",
+    "pinia": "2.0.0-rc.14",
     "print-js": "^1.6.0",
     "qrcode": "^1.4.4",
     "qrcodejs2": "0.0.2",
     "resize-observer-polyfill": "^1.5.1",
+    "showdown": "^1.9.1",
     "sortablejs": "^1.14.0",
-    "tinymce": "^5.8.2",
-    "vditor": "^3.8.6",
-    "vue": "3.2.4",
+    "tinymce": "^5.10.0",
+    "vditor": "^3.8.7",
+    "vue": "^3.2.20",
     "vue-cropper": "^0.5.6",
     "vue-cropperjs": "^5.0.0",
-    "vue-i18n": "9.1.7",
+    "vue-i18n": "^9.1.9",
     "vue-infinite-scroll": "^2.0.2",
-    "vue-router": "^4.0.11",
-    "vue-types": "^4.0.3",
-    "vxe-table": "^4.0.24",
+    "vue-print-nb-jeecg": "^1.0.10",
+    "vue-router": "^4.0.12",
+    "vue-types": "^4.1.1",
+    "vxe-table": "4.1.0",
     "vxe-table-plugin-antd": "^3.0.3",
     "xe-utils": "^3.3.1",
-    "xlsx": "^0.17.1",
-    "vue-json-pretty": "1.8.1"
+    "xlsx": "^0.17.3",
+    "qiankun": "^2.5.1",
+    "vue-json-pretty": "^2.0.4"
   },
   "devDependencies": {
-    "@commitlint/cli": "^13.1.0",
-    "@commitlint/config-conventional": "^13.1.0",
-    "@iconify/json": "^1.1.392",
+    "@commitlint/cli": "^13.2.1",
+    "@commitlint/config-conventional": "^13.2.0",
+    "@iconify/json": "^1.1.399",
     "@purge-icons/generated": "^0.7.0",
-    "@types/codemirror": "^5.60.2",
+    "@types/codemirror": "^5.60.5",
     "@types/crypto-js": "^4.0.2",
-    "@types/fs-extra": "^9.0.12",
-    "@types/inquirer": "^7.3.3",
+    "@types/fs-extra": "^9.0.13",
+    "@types/inquirer": "^8.1.3",
     "@types/intro.js": "^3.0.2",
-    "@types/jest": "^27.0.1",
-    "@types/lodash-es": "^4.17.4",
+    "@types/jest": "^27.0.2",
+    "@types/lodash-es": "^4.17.5",
     "@types/mockjs": "^1.0.4",
-    "@types/node": "^16.7.1",
+    "@types/node": "^16.11.1",
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.4.1",
     "@types/qs": "^6.9.7",
+    "@types/showdown": "^1.9.4",
     "@types/sortablejs": "^1.10.7",
-    "@typescript-eslint/eslint-plugin": "^4.29.3",
-    "@typescript-eslint/parser": "^4.29.3",
-    "@vitejs/plugin-legacy": "^1.5.1",
-    "@vitejs/plugin-vue": "^1.4.0",
-    "@vitejs/plugin-vue-jsx": "^1.1.7",
-    "@vue/compiler-sfc": "3.2.4",
-    "@vue/test-utils": "^2.0.0-rc.12",
-    "autoprefixer": "^10.3.2",
+    "@typescript-eslint/eslint-plugin": "^5.1.0",
+    "@typescript-eslint/parser": "^5.1.0",
+    "@vitejs/plugin-legacy": "^1.6.2",
+    "@vitejs/plugin-vue": "^1.9.3",
+    "@vitejs/plugin-vue-jsx": "^1.2.0",
+    "@vue/compiler-sfc": "3.2.20",
+    "@vue/test-utils": "^2.0.0-rc.16",
+    "autoprefixer": "^10.3.7",
     "commitizen": "^4.2.4",
     "conventional-changelog-cli": "^2.1.1",
     "cross-env": "^7.0.3",
     "dotenv": "^10.0.0",
-    "eslint": "^7.32.0",
+    "eslint": "^8.0.1",
     "eslint-config-prettier": "^8.3.0",
-    "eslint-define-config": "^1.0.9",
-    "eslint-plugin-jest": "^24.4.0",
-    "eslint-plugin-prettier": "^3.4.1",
-    "eslint-plugin-vue": "^7.16.0",
-    "esno": "^0.9.1",
+    "eslint-define-config": "^1.1.1",
+    "eslint-plugin-jest": "^25.2.2",
+    "eslint-plugin-prettier": "^4.0.0",
+    "eslint-plugin-vue": "^7.19.1",
+    "esno": "^0.10.1",
     "fs-extra": "^10.0.0",
-    "http-server": "^13.0.1",
-    "husky": "^7.0.1",
-    "inquirer": "^8.1.2",
+    "http-server": "^14.0.0",
+    "husky": "^7.0.2",
+    "inquirer": "^8.2.0",
     "is-ci": "^3.0.0",
-    "jest": "^27.0.6",
-    "less": "^4.1.1",
-    "lint-staged": "^11.1.2",
+    "jest": "^27.3.1",
+    "less": "^4.1.2",
+    "lint-staged": "^11.2.3",
     "npm-run-all": "^4.1.5",
-    "postcss": "^8.3.6",
-    "prettier": "^2.3.2",
+    "postcss": "^8.3.9",
+    "prettier": "^2.4.1",
     "pretty-quick": "^3.1.1",
     "rimraf": "^3.0.2",
     "rollup-plugin-visualizer": "5.5.2",
     "stylelint": "^13.13.1",
-    "stylelint-config-prettier": "^8.0.2",
+    "stylelint-config-prettier": "^9.0.3",
     "stylelint-config-standard": "^22.0.0",
     "stylelint-order": "^4.1.0",
-    "ts-jest": "^27.0.5",
-    "ts-node": "^10.2.1",
-    "typescript": "4.3.5",
-    "vite": "2.5.0",
+    "ts-jest": "^27.0.7",
+    "ts-node": "^10.3.0",
+    "typescript": "^4.4.4",
+    "vite": "^2.6.10",
     "vite-plugin-compression": "^0.3.5",
     "vite-plugin-html": "^2.1.0",
-    "vite-plugin-imagemin": "^0.4.5",
+    "vite-plugin-imagemin": "^0.4.6",
     "vite-plugin-mock": "^2.9.6",
     "vite-plugin-purge-icons": "^0.7.0",
-    "vite-plugin-pwa": "^0.11.0",
+    "vite-plugin-pwa": "^0.11.3",
     "vite-plugin-style-import": "^1.2.1",
-    "vite-plugin-svg-icons": "^1.0.4",
-    "vite-plugin-windicss": "^1.2.8",
+    "vite-plugin-svg-icons": "^1.0.5",
     "vite-plugin-theme": "^0.8.1",
-    "vue-eslint-parser": "^7.10.0",
-    "vue-tsc": "^0.3.0"
+    "vite-plugin-vue-setup-extend": "^0.1.0",
+    "vite-plugin-windicss": "^1.4.12",
+    "vue-eslint-parser": "^8.0.0",
+    "vue-tsc": "^0.28.7"
   },
   "resolutions": {
     "//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
@@ -165,13 +168,13 @@
   },
   "repository": {
     "type": "git",
-    "url": "git+https://github.com/jeecgboot/jeecg-boot-vue3"
+    "url": "git+https://github.com/jeecgboot/jeecgboot-vue3.git"
   },
   "license": "MIT",
   "bugs": {
-    "url": "https://github.com/jeecgboot/jeecg-boot-vue3/issues"
+    "url": "https://github.com/jeecgboot/jeecgboot-vue3/issues"
   },
-  "homepage": "https://github.com/jeecgboot/jeecg-boot-vue3",
+  "homepage": "https://github.com/jeecgboot/jeecgboot-vue3",
   "engines": {
     "node": "^12 || >=14"
   }

+ 0 - 1
prettier.config.js

@@ -8,7 +8,6 @@ module.exports = {
   quoteProps: 'as-needed',
   bracketSpacing: true,
   trailingComma: 'es5',
-  jsxBracketSameLine: false,
   jsxSingleQuote: false,
   arrowParens: 'always',
   insertPragma: false,

BIN
public/logo.png


BIN
public/resource/img/logo.png


+ 711 - 0
public/resource/tinymce/skins/ui/jeecg/content.css

@@ -0,0 +1,711 @@
+/**
+* Copyright (c) Tiny Technologies, Inc. All rights reserved.
+* Licensed under the LGPL or a commercial license.
+* For LGPL see License.txt in the project root for license information.
+* For commercial licenses see https://www.tiny.cloud/
+*/
+.mce-content-body .mce-item-anchor {
+  background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
+  cursor: default;
+  display: inline-block;
+  height: 12px !important;
+  padding: 0 2px;
+  -webkit-user-modify: read-only;
+  -moz-user-modify: read-only;
+  -webkit-user-select: all;
+  -ms-user-select: all;
+      user-select: all;
+  width: 8px !important;
+}
+.mce-content-body .mce-item-anchor[data-mce-selected] {
+  outline-offset: 1px;
+}
+.tox-comments-visible .tox-comment {
+  background-color: #fff0b7;
+}
+.tox-comments-visible .tox-comment--active {
+  background-color: #ffe168;
+}
+.tox-checklist > li:not(.tox-checklist--hidden) {
+  list-style: none;
+  margin: 0.25em 0;
+}
+.tox-checklist > li:not(.tox-checklist--hidden)::before {
+  content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");
+  cursor: pointer;
+  height: 1em;
+  margin-left: -1.5em;
+  margin-top: 0.125em;
+  position: absolute;
+  width: 1em;
+}
+.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {
+  content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");
+}
+[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {
+  margin-left: 0;
+  margin-right: -1.5em;
+}
+/* stylelint-disable */
+/* http://prismjs.com/ */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  font-size: 1em;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+  -moz-tab-size: 4;
+  tab-size: 4;
+  -webkit-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+@media print {
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: 0.5em 0;
+  overflow: auto;
+}
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+  padding: 0.1em;
+  border-radius: 0.3em;
+  white-space: normal;
+}
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+.token.punctuation {
+  color: #999;
+}
+.namespace {
+  opacity: 0.7;
+}
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, 0.5);
+}
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+.token.italic {
+  font-style: italic;
+}
+.token.entity {
+  cursor: help;
+}
+/* stylelint-enable */
+.mce-content-body {
+  overflow-wrap: break-word;
+  word-wrap: break-word;
+}
+.mce-content-body .mce-visual-caret {
+  background-color: black;
+  background-color: currentColor;
+  position: absolute;
+}
+.mce-content-body .mce-visual-caret-hidden {
+  display: none;
+}
+.mce-content-body *[data-mce-caret] {
+  left: -1000px;
+  margin: 0;
+  padding: 0;
+  position: absolute;
+  right: auto;
+  top: 0;
+}
+.mce-content-body .mce-offscreen-selection {
+  left: -2000000px;
+  max-width: 1000000px;
+  position: absolute;
+}
+.mce-content-body *[contentEditable=false] {
+  cursor: default;
+}
+.mce-content-body *[contentEditable=true] {
+  cursor: text;
+}
+.tox-cursor-format-painter {
+  cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
+}
+.mce-content-body figure.align-left {
+  float: left;
+}
+.mce-content-body figure.align-right {
+  float: right;
+}
+.mce-content-body figure.image.align-center {
+  display: table;
+  margin-left: auto;
+  margin-right: auto;
+}
+.mce-preview-object {
+  border: 1px solid gray;
+  display: inline-block;
+  line-height: 0;
+  margin: 0 2px 0 2px;
+  position: relative;
+}
+.mce-preview-object .mce-shim {
+  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.mce-preview-object[data-mce-selected="2"] .mce-shim {
+  display: none;
+}
+.mce-object {
+  background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
+  border: 1px dashed #aaa;
+}
+.mce-pagebreak {
+  border: 1px dashed #aaa;
+  cursor: default;
+  display: block;
+  height: 5px;
+  margin-top: 15px;
+  page-break-before: always;
+  width: 100%;
+}
+@media print {
+  .mce-pagebreak {
+    border: 0;
+  }
+}
+.tiny-pageembed .mce-shim {
+  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.tiny-pageembed[data-mce-selected="2"] .mce-shim {
+  display: none;
+}
+.tiny-pageembed {
+  display: inline-block;
+  position: relative;
+}
+.tiny-pageembed--21by9,
+.tiny-pageembed--16by9,
+.tiny-pageembed--4by3,
+.tiny-pageembed--1by1 {
+  display: block;
+  overflow: hidden;
+  padding: 0;
+  position: relative;
+  width: 100%;
+}
+.tiny-pageembed--21by9 {
+  padding-top: 42.857143%;
+}
+.tiny-pageembed--16by9 {
+  padding-top: 56.25%;
+}
+.tiny-pageembed--4by3 {
+  padding-top: 75%;
+}
+.tiny-pageembed--1by1 {
+  padding-top: 100%;
+}
+.tiny-pageembed--21by9 iframe,
+.tiny-pageembed--16by9 iframe,
+.tiny-pageembed--4by3 iframe,
+.tiny-pageembed--1by1 iframe {
+  border: 0;
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.mce-content-body[data-mce-placeholder] {
+  position: relative;
+}
+.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {
+  color: rgba(84, 111, 94, 0.7);
+  content: attr(data-mce-placeholder);
+  position: absolute;
+}
+.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {
+  left: 1px;
+}
+.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {
+  right: 1px;
+}
+.mce-content-body div.mce-resizehandle {
+  background-color: #4099ff;
+  border-color: #4099ff;
+  border-style: solid;
+  border-width: 1px;
+  box-sizing: border-box;
+  height: 10px;
+  position: absolute;
+  width: 10px;
+  z-index: 10000;
+}
+.mce-content-body div.mce-resizehandle:hover {
+  background-color: #4099ff;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(1) {
+  cursor: nwse-resize;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(2) {
+  cursor: nesw-resize;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(3) {
+  cursor: nwse-resize;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(4) {
+  cursor: nesw-resize;
+}
+.mce-content-body .mce-resize-backdrop {
+  z-index: 10000;
+}
+.mce-content-body .mce-clonedresizable {
+  cursor: default;
+  opacity: 0.5;
+  outline: 1px dashed black;
+  position: absolute;
+  z-index: 10001;
+}
+.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,
+.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {
+  border: 0;
+}
+.mce-content-body .mce-resize-helper {
+  background: #555;
+  background: rgba(0, 0, 0, 0.75);
+  border: 1px;
+  border-radius: 3px;
+  color: white;
+  display: none;
+  font-family: sans-serif;
+  font-size: 12px;
+  line-height: 14px;
+  margin: 5px 10px;
+  padding: 5px;
+  position: absolute;
+  white-space: nowrap;
+  z-index: 10002;
+}
+.tox-rtc-user-selection {
+  position: relative;
+}
+.tox-rtc-user-cursor {
+  bottom: 0;
+  cursor: default;
+  position: absolute;
+  top: 0;
+  width: 2px;
+}
+.tox-rtc-user-cursor::before {
+  background-color: inherit;
+  border-radius: 50%;
+  content: '';
+  display: block;
+  height: 8px;
+  position: absolute;
+  right: -3px;
+  top: -3px;
+  width: 8px;
+}
+.tox-rtc-user-cursor:hover::after {
+  background-color: inherit;
+  border-radius: 100px;
+  box-sizing: border-box;
+  color: #fff;
+  content: attr(data-user);
+  display: block;
+  font-size: 12px;
+  font-weight: normal;
+  left: -5px;
+  min-height: 8px;
+  min-width: 8px;
+  padding: 0 12px;
+  position: absolute;
+  top: -11px;
+  white-space: nowrap;
+  z-index: 1000;
+}
+.tox-rtc-user-selection--1 .tox-rtc-user-cursor {
+  background-color: #2dc26b;
+}
+.tox-rtc-user-selection--2 .tox-rtc-user-cursor {
+  background-color: #e03e2d;
+}
+.tox-rtc-user-selection--3 .tox-rtc-user-cursor {
+  background-color: #f1c40f;
+}
+.tox-rtc-user-selection--4 .tox-rtc-user-cursor {
+  background-color: #3598db;
+}
+.tox-rtc-user-selection--5 .tox-rtc-user-cursor {
+  background-color: #b96ad9;
+}
+.tox-rtc-user-selection--6 .tox-rtc-user-cursor {
+  background-color: #e67e23;
+}
+.tox-rtc-user-selection--7 .tox-rtc-user-cursor {
+  background-color: #aaa69d;
+}
+.tox-rtc-user-selection--8 .tox-rtc-user-cursor {
+  background-color: #f368e0;
+}
+.tox-rtc-remote-image {
+  background: #eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;
+  border: 1px solid #ccc;
+  min-height: 240px;
+  min-width: 320px;
+}
+.mce-match-marker {
+  background: #aaa;
+  color: #fff;
+}
+.mce-match-marker-selected {
+  background: #39f;
+  color: #fff;
+}
+.mce-match-marker-selected::selection {
+  background: #39f;
+  color: #fff;
+}
+.mce-content-body img[data-mce-selected],
+.mce-content-body video[data-mce-selected],
+.mce-content-body audio[data-mce-selected],
+.mce-content-body object[data-mce-selected],
+.mce-content-body embed[data-mce-selected],
+.mce-content-body table[data-mce-selected] {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body hr[data-mce-selected] {
+  outline: 3px solid #b4d7ff;
+  outline-offset: 1px;
+}
+.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body *[contentEditable=false][data-mce-selected] {
+  cursor: not-allowed;
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,
+.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {
+  outline: none;
+}
+.mce-content-body *[data-mce-selected="inline-boundary"] {
+  background-color: #b4d7ff;
+}
+.mce-content-body .mce-edit-focus {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body td[data-mce-selected],
+.mce-content-body th[data-mce-selected] {
+  position: relative;
+}
+.mce-content-body td[data-mce-selected]::selection,
+.mce-content-body th[data-mce-selected]::selection {
+  background: none;
+}
+.mce-content-body td[data-mce-selected] *,
+.mce-content-body th[data-mce-selected] * {
+  outline: none;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+.mce-content-body td[data-mce-selected]::after,
+.mce-content-body th[data-mce-selected]::after {
+  background-color: rgba(180, 215, 255, 0.7);
+  border: 1px solid rgba(180, 215, 255, 0.7);
+  bottom: -1px;
+  content: '';
+  left: -1px;
+  mix-blend-mode: multiply;
+  position: absolute;
+  right: -1px;
+  top: -1px;
+}
+@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+  .mce-content-body td[data-mce-selected]::after,
+  .mce-content-body th[data-mce-selected]::after {
+    border-color: rgba(0, 84, 180, 0.7);
+  }
+}
+.mce-content-body img::selection {
+  background: none;
+}
+.ephox-snooker-resizer-bar {
+  background-color: #b4d7ff;
+  opacity: 0;
+  -webkit-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+.ephox-snooker-resizer-cols {
+  cursor: col-resize;
+}
+.ephox-snooker-resizer-rows {
+  cursor: row-resize;
+}
+.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {
+  opacity: 1;
+}
+.mce-spellchecker-word {
+  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");
+  background-position: 0 calc(100% + 1px);
+  background-repeat: repeat-x;
+  background-size: auto 6px;
+  cursor: default;
+  height: 2rem;
+}
+.mce-spellchecker-grammar {
+  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");
+  background-position: 0 calc(100% + 1px);
+  background-repeat: repeat-x;
+  background-size: auto 6px;
+  cursor: default;
+}
+.mce-toc {
+  border: 1px solid gray;
+}
+.mce-toc h2 {
+  margin: 4px;
+}
+.mce-toc li {
+  list-style-type: none;
+}
+table[style*="border-width: 0px"],
+.mce-item-table:not([border]),
+.mce-item-table[border="0"],
+table[style*="border-width: 0px"] td,
+.mce-item-table:not([border]) td,
+.mce-item-table[border="0"] td,
+table[style*="border-width: 0px"] th,
+.mce-item-table:not([border]) th,
+.mce-item-table[border="0"] th,
+table[style*="border-width: 0px"] caption,
+.mce-item-table:not([border]) caption,
+.mce-item-table[border="0"] caption {
+  border: 1px dashed #bbb;
+}
+.mce-visualblocks p,
+.mce-visualblocks h1,
+.mce-visualblocks h2,
+.mce-visualblocks h3,
+.mce-visualblocks h4,
+.mce-visualblocks h5,
+.mce-visualblocks h6,
+.mce-visualblocks div:not([data-mce-bogus]),
+.mce-visualblocks section,
+.mce-visualblocks article,
+.mce-visualblocks blockquote,
+.mce-visualblocks address,
+.mce-visualblocks pre,
+.mce-visualblocks figure,
+.mce-visualblocks figcaption,
+.mce-visualblocks hgroup,
+.mce-visualblocks aside,
+.mce-visualblocks ul,
+.mce-visualblocks ol,
+.mce-visualblocks dl {
+  background-repeat: no-repeat;
+  border: 1px dashed #bbb;
+  margin-left: 3px;
+  padding-top: 10px;
+}
+.mce-visualblocks p {
+  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);
+}
+.mce-visualblocks h1 {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);
+}
+.mce-visualblocks h2 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);
+}
+.mce-visualblocks h3 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);
+}
+.mce-visualblocks h4 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);
+}
+.mce-visualblocks h5 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);
+}
+.mce-visualblocks h6 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);
+}
+.mce-visualblocks div:not([data-mce-bogus]) {
+  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);
+}
+.mce-visualblocks section {
+  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);
+}
+.mce-visualblocks article {
+  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);
+}
+.mce-visualblocks blockquote {
+  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);
+}
+.mce-visualblocks address {
+  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);
+}
+.mce-visualblocks pre {
+  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);
+}
+.mce-visualblocks figure {
+  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);
+}
+.mce-visualblocks figcaption {
+  border: 1px dashed #bbb;
+}
+.mce-visualblocks hgroup {
+  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);
+}
+.mce-visualblocks aside {
+  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);
+}
+.mce-visualblocks ul {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);
+}
+.mce-visualblocks ol {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);
+}
+.mce-visualblocks dl {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);
+}
+.mce-visualblocks:not([dir=rtl]) p,
+.mce-visualblocks:not([dir=rtl]) h1,
+.mce-visualblocks:not([dir=rtl]) h2,
+.mce-visualblocks:not([dir=rtl]) h3,
+.mce-visualblocks:not([dir=rtl]) h4,
+.mce-visualblocks:not([dir=rtl]) h5,
+.mce-visualblocks:not([dir=rtl]) h6,
+.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),
+.mce-visualblocks:not([dir=rtl]) section,
+.mce-visualblocks:not([dir=rtl]) article,
+.mce-visualblocks:not([dir=rtl]) blockquote,
+.mce-visualblocks:not([dir=rtl]) address,
+.mce-visualblocks:not([dir=rtl]) pre,
+.mce-visualblocks:not([dir=rtl]) figure,
+.mce-visualblocks:not([dir=rtl]) figcaption,
+.mce-visualblocks:not([dir=rtl]) hgroup,
+.mce-visualblocks:not([dir=rtl]) aside,
+.mce-visualblocks:not([dir=rtl]) ul,
+.mce-visualblocks:not([dir=rtl]) ol,
+.mce-visualblocks:not([dir=rtl]) dl {
+  margin-left: 3px;
+}
+.mce-visualblocks[dir=rtl] p,
+.mce-visualblocks[dir=rtl] h1,
+.mce-visualblocks[dir=rtl] h2,
+.mce-visualblocks[dir=rtl] h3,
+.mce-visualblocks[dir=rtl] h4,
+.mce-visualblocks[dir=rtl] h5,
+.mce-visualblocks[dir=rtl] h6,
+.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),
+.mce-visualblocks[dir=rtl] section,
+.mce-visualblocks[dir=rtl] article,
+.mce-visualblocks[dir=rtl] blockquote,
+.mce-visualblocks[dir=rtl] address,
+.mce-visualblocks[dir=rtl] pre,
+.mce-visualblocks[dir=rtl] figure,
+.mce-visualblocks[dir=rtl] figcaption,
+.mce-visualblocks[dir=rtl] hgroup,
+.mce-visualblocks[dir=rtl] aside,
+.mce-visualblocks[dir=rtl] ul,
+.mce-visualblocks[dir=rtl] ol,
+.mce-visualblocks[dir=rtl] dl {
+  background-position-x: right;
+  margin-right: 3px;
+}
+.mce-nbsp,
+.mce-shy {
+  background: #aaa;
+}
+.mce-shy::after {
+  content: '-';
+}
+body {
+  font-family: sans-serif;
+}
+table {
+  border-collapse: collapse;
+}

+ 705 - 0
public/resource/tinymce/skins/ui/jeecg/content.inline.css

@@ -0,0 +1,705 @@
+/**
+* Copyright (c) Tiny Technologies, Inc. All rights reserved.
+* Licensed under the LGPL or a commercial license.
+* For LGPL see License.txt in the project root for license information.
+* For commercial licenses see https://www.tiny.cloud/
+*/
+.mce-content-body .mce-item-anchor {
+  background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
+  cursor: default;
+  display: inline-block;
+  height: 12px !important;
+  padding: 0 2px;
+  -webkit-user-modify: read-only;
+  -moz-user-modify: read-only;
+  -webkit-user-select: all;
+  -ms-user-select: all;
+      user-select: all;
+  width: 8px !important;
+}
+.mce-content-body .mce-item-anchor[data-mce-selected] {
+  outline-offset: 1px;
+}
+.tox-comments-visible .tox-comment {
+  background-color: #fff0b7;
+}
+.tox-comments-visible .tox-comment--active {
+  background-color: #ffe168;
+}
+.tox-checklist > li:not(.tox-checklist--hidden) {
+  list-style: none;
+  margin: 0.25em 0;
+}
+.tox-checklist > li:not(.tox-checklist--hidden)::before {
+  content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");
+  cursor: pointer;
+  height: 1em;
+  margin-left: -1.5em;
+  margin-top: 0.125em;
+  position: absolute;
+  width: 1em;
+}
+.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before {
+  content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");
+}
+[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before {
+  margin-left: 0;
+  margin-right: -1.5em;
+}
+/* stylelint-disable */
+/* http://prismjs.com/ */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  font-size: 1em;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+  -moz-tab-size: 4;
+  tab-size: 4;
+  -webkit-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+@media print {
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: 0.5em 0;
+  overflow: auto;
+}
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+  padding: 0.1em;
+  border-radius: 0.3em;
+  white-space: normal;
+}
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+.token.punctuation {
+  color: #999;
+}
+.namespace {
+  opacity: 0.7;
+}
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, 0.5);
+}
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+.token.italic {
+  font-style: italic;
+}
+.token.entity {
+  cursor: help;
+}
+/* stylelint-enable */
+.mce-content-body {
+  overflow-wrap: break-word;
+  word-wrap: break-word;
+}
+.mce-content-body .mce-visual-caret {
+  background-color: black;
+  background-color: currentColor;
+  position: absolute;
+}
+.mce-content-body .mce-visual-caret-hidden {
+  display: none;
+}
+.mce-content-body *[data-mce-caret] {
+  left: -1000px;
+  margin: 0;
+  padding: 0;
+  position: absolute;
+  right: auto;
+  top: 0;
+}
+.mce-content-body .mce-offscreen-selection {
+  left: -2000000px;
+  max-width: 1000000px;
+  position: absolute;
+}
+.mce-content-body *[contentEditable=false] {
+  cursor: default;
+}
+.mce-content-body *[contentEditable=true] {
+  cursor: text;
+}
+.tox-cursor-format-painter {
+  cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
+}
+.mce-content-body figure.align-left {
+  float: left;
+}
+.mce-content-body figure.align-right {
+  float: right;
+}
+.mce-content-body figure.image.align-center {
+  display: table;
+  margin-left: auto;
+  margin-right: auto;
+}
+.mce-preview-object {
+  border: 1px solid gray;
+  display: inline-block;
+  line-height: 0;
+  margin: 0 2px 0 2px;
+  position: relative;
+}
+.mce-preview-object .mce-shim {
+  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.mce-preview-object[data-mce-selected="2"] .mce-shim {
+  display: none;
+}
+.mce-object {
+  background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
+  border: 1px dashed #aaa;
+}
+.mce-pagebreak {
+  border: 1px dashed #aaa;
+  cursor: default;
+  display: block;
+  height: 5px;
+  margin-top: 15px;
+  page-break-before: always;
+  width: 100%;
+}
+@media print {
+  .mce-pagebreak {
+    border: 0;
+  }
+}
+.tiny-pageembed .mce-shim {
+  background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.tiny-pageembed[data-mce-selected="2"] .mce-shim {
+  display: none;
+}
+.tiny-pageembed {
+  display: inline-block;
+  position: relative;
+}
+.tiny-pageembed--21by9,
+.tiny-pageembed--16by9,
+.tiny-pageembed--4by3,
+.tiny-pageembed--1by1 {
+  display: block;
+  overflow: hidden;
+  padding: 0;
+  position: relative;
+  width: 100%;
+}
+.tiny-pageembed--21by9 {
+  padding-top: 42.857143%;
+}
+.tiny-pageembed--16by9 {
+  padding-top: 56.25%;
+}
+.tiny-pageembed--4by3 {
+  padding-top: 75%;
+}
+.tiny-pageembed--1by1 {
+  padding-top: 100%;
+}
+.tiny-pageembed--21by9 iframe,
+.tiny-pageembed--16by9 iframe,
+.tiny-pageembed--4by3 iframe,
+.tiny-pageembed--1by1 iframe {
+  border: 0;
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.mce-content-body[data-mce-placeholder] {
+  position: relative;
+}
+.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {
+  color: rgba(84, 111, 94, 0.7);
+  content: attr(data-mce-placeholder);
+  position: absolute;
+}
+.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {
+  left: 1px;
+}
+.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before {
+  right: 1px;
+}
+.mce-content-body div.mce-resizehandle {
+  background-color: #4099ff;
+  border-color: #4099ff;
+  border-style: solid;
+  border-width: 1px;
+  box-sizing: border-box;
+  height: 10px;
+  position: absolute;
+  width: 10px;
+  z-index: 10000;
+}
+.mce-content-body div.mce-resizehandle:hover {
+  background-color: #4099ff;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(1) {
+  cursor: nwse-resize;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(2) {
+  cursor: nesw-resize;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(3) {
+  cursor: nwse-resize;
+}
+.mce-content-body div.mce-resizehandle:nth-of-type(4) {
+  cursor: nesw-resize;
+}
+.mce-content-body .mce-resize-backdrop {
+  z-index: 10000;
+}
+.mce-content-body .mce-clonedresizable {
+  cursor: default;
+  opacity: 0.5;
+  outline: 1px dashed black;
+  position: absolute;
+  z-index: 10001;
+}
+.mce-content-body .mce-clonedresizable.mce-resizetable-columns th,
+.mce-content-body .mce-clonedresizable.mce-resizetable-columns td {
+  border: 0;
+}
+.mce-content-body .mce-resize-helper {
+  background: #555;
+  background: rgba(0, 0, 0, 0.75);
+  border: 1px;
+  border-radius: 3px;
+  color: white;
+  display: none;
+  font-family: sans-serif;
+  font-size: 12px;
+  line-height: 14px;
+  margin: 5px 10px;
+  padding: 5px;
+  position: absolute;
+  white-space: nowrap;
+  z-index: 10002;
+}
+.tox-rtc-user-selection {
+  position: relative;
+}
+.tox-rtc-user-cursor {
+  bottom: 0;
+  cursor: default;
+  position: absolute;
+  top: 0;
+  width: 2px;
+}
+.tox-rtc-user-cursor::before {
+  background-color: inherit;
+  border-radius: 50%;
+  content: '';
+  display: block;
+  height: 8px;
+  position: absolute;
+  right: -3px;
+  top: -3px;
+  width: 8px;
+}
+.tox-rtc-user-cursor:hover::after {
+  background-color: inherit;
+  border-radius: 100px;
+  box-sizing: border-box;
+  color: #fff;
+  content: attr(data-user);
+  display: block;
+  font-size: 12px;
+  font-weight: normal;
+  left: -5px;
+  min-height: 8px;
+  min-width: 8px;
+  padding: 0 12px;
+  position: absolute;
+  top: -11px;
+  white-space: nowrap;
+  z-index: 1000;
+}
+.tox-rtc-user-selection--1 .tox-rtc-user-cursor {
+  background-color: #2dc26b;
+}
+.tox-rtc-user-selection--2 .tox-rtc-user-cursor {
+  background-color: #e03e2d;
+}
+.tox-rtc-user-selection--3 .tox-rtc-user-cursor {
+  background-color: #f1c40f;
+}
+.tox-rtc-user-selection--4 .tox-rtc-user-cursor {
+  background-color: #3598db;
+}
+.tox-rtc-user-selection--5 .tox-rtc-user-cursor {
+  background-color: #b96ad9;
+}
+.tox-rtc-user-selection--6 .tox-rtc-user-cursor {
+  background-color: #e67e23;
+}
+.tox-rtc-user-selection--7 .tox-rtc-user-cursor {
+  background-color: #aaa69d;
+}
+.tox-rtc-user-selection--8 .tox-rtc-user-cursor {
+  background-color: #f368e0;
+}
+.tox-rtc-remote-image {
+  background: #eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;
+  border: 1px solid #ccc;
+  min-height: 240px;
+  min-width: 320px;
+}
+.mce-match-marker {
+  background: #aaa;
+  color: #fff;
+}
+.mce-match-marker-selected {
+  background: #39f;
+  color: #fff;
+}
+.mce-match-marker-selected::selection {
+  background: #39f;
+  color: #fff;
+}
+.mce-content-body img[data-mce-selected],
+.mce-content-body video[data-mce-selected],
+.mce-content-body audio[data-mce-selected],
+.mce-content-body object[data-mce-selected],
+.mce-content-body embed[data-mce-selected],
+.mce-content-body table[data-mce-selected] {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body hr[data-mce-selected] {
+  outline: 3px solid #b4d7ff;
+  outline-offset: 1px;
+}
+.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body *[contentEditable=false][data-mce-selected] {
+  cursor: not-allowed;
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body.mce-content-readonly *[contentEditable=true]:focus,
+.mce-content-body.mce-content-readonly *[contentEditable=true]:hover {
+  outline: none;
+}
+.mce-content-body *[data-mce-selected="inline-boundary"] {
+  background-color: #b4d7ff;
+}
+.mce-content-body .mce-edit-focus {
+  outline: 3px solid #b4d7ff;
+}
+.mce-content-body td[data-mce-selected],
+.mce-content-body th[data-mce-selected] {
+  position: relative;
+}
+.mce-content-body td[data-mce-selected]::selection,
+.mce-content-body th[data-mce-selected]::selection {
+  background: none;
+}
+.mce-content-body td[data-mce-selected] *,
+.mce-content-body th[data-mce-selected] * {
+  outline: none;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+.mce-content-body td[data-mce-selected]::after,
+.mce-content-body th[data-mce-selected]::after {
+  background-color: rgba(180, 215, 255, 0.7);
+  border: 1px solid rgba(180, 215, 255, 0.7);
+  bottom: -1px;
+  content: '';
+  left: -1px;
+  mix-blend-mode: multiply;
+  position: absolute;
+  right: -1px;
+  top: -1px;
+}
+@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+  .mce-content-body td[data-mce-selected]::after,
+  .mce-content-body th[data-mce-selected]::after {
+    border-color: rgba(0, 84, 180, 0.7);
+  }
+}
+.mce-content-body img::selection {
+  background: none;
+}
+.ephox-snooker-resizer-bar {
+  background-color: #b4d7ff;
+  opacity: 0;
+  -webkit-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+.ephox-snooker-resizer-cols {
+  cursor: col-resize;
+}
+.ephox-snooker-resizer-rows {
+  cursor: row-resize;
+}
+.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging {
+  opacity: 1;
+}
+.mce-spellchecker-word {
+  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");
+  background-position: 0 calc(100% + 1px);
+  background-repeat: repeat-x;
+  background-size: auto 6px;
+  cursor: default;
+  height: 2rem;
+}
+.mce-spellchecker-grammar {
+  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");
+  background-position: 0 calc(100% + 1px);
+  background-repeat: repeat-x;
+  background-size: auto 6px;
+  cursor: default;
+}
+.mce-toc {
+  border: 1px solid gray;
+}
+.mce-toc h2 {
+  margin: 4px;
+}
+.mce-toc li {
+  list-style-type: none;
+}
+table[style*="border-width: 0px"],
+.mce-item-table:not([border]),
+.mce-item-table[border="0"],
+table[style*="border-width: 0px"] td,
+.mce-item-table:not([border]) td,
+.mce-item-table[border="0"] td,
+table[style*="border-width: 0px"] th,
+.mce-item-table:not([border]) th,
+.mce-item-table[border="0"] th,
+table[style*="border-width: 0px"] caption,
+.mce-item-table:not([border]) caption,
+.mce-item-table[border="0"] caption {
+  border: 1px dashed #bbb;
+}
+.mce-visualblocks p,
+.mce-visualblocks h1,
+.mce-visualblocks h2,
+.mce-visualblocks h3,
+.mce-visualblocks h4,
+.mce-visualblocks h5,
+.mce-visualblocks h6,
+.mce-visualblocks div:not([data-mce-bogus]),
+.mce-visualblocks section,
+.mce-visualblocks article,
+.mce-visualblocks blockquote,
+.mce-visualblocks address,
+.mce-visualblocks pre,
+.mce-visualblocks figure,
+.mce-visualblocks figcaption,
+.mce-visualblocks hgroup,
+.mce-visualblocks aside,
+.mce-visualblocks ul,
+.mce-visualblocks ol,
+.mce-visualblocks dl {
+  background-repeat: no-repeat;
+  border: 1px dashed #bbb;
+  margin-left: 3px;
+  padding-top: 10px;
+}
+.mce-visualblocks p {
+  background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);
+}
+.mce-visualblocks h1 {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);
+}
+.mce-visualblocks h2 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);
+}
+.mce-visualblocks h3 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);
+}
+.mce-visualblocks h4 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);
+}
+.mce-visualblocks h5 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);
+}
+.mce-visualblocks h6 {
+  background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);
+}
+.mce-visualblocks div:not([data-mce-bogus]) {
+  background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);
+}
+.mce-visualblocks section {
+  background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);
+}
+.mce-visualblocks article {
+  background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);
+}
+.mce-visualblocks blockquote {
+  background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);
+}
+.mce-visualblocks address {
+  background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);
+}
+.mce-visualblocks pre {
+  background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);
+}
+.mce-visualblocks figure {
+  background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);
+}
+.mce-visualblocks figcaption {
+  border: 1px dashed #bbb;
+}
+.mce-visualblocks hgroup {
+  background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);
+}
+.mce-visualblocks aside {
+  background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);
+}
+.mce-visualblocks ul {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==);
+}
+.mce-visualblocks ol {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);
+}
+.mce-visualblocks dl {
+  background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);
+}
+.mce-visualblocks:not([dir=rtl]) p,
+.mce-visualblocks:not([dir=rtl]) h1,
+.mce-visualblocks:not([dir=rtl]) h2,
+.mce-visualblocks:not([dir=rtl]) h3,
+.mce-visualblocks:not([dir=rtl]) h4,
+.mce-visualblocks:not([dir=rtl]) h5,
+.mce-visualblocks:not([dir=rtl]) h6,
+.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),
+.mce-visualblocks:not([dir=rtl]) section,
+.mce-visualblocks:not([dir=rtl]) article,
+.mce-visualblocks:not([dir=rtl]) blockquote,
+.mce-visualblocks:not([dir=rtl]) address,
+.mce-visualblocks:not([dir=rtl]) pre,
+.mce-visualblocks:not([dir=rtl]) figure,
+.mce-visualblocks:not([dir=rtl]) figcaption,
+.mce-visualblocks:not([dir=rtl]) hgroup,
+.mce-visualblocks:not([dir=rtl]) aside,
+.mce-visualblocks:not([dir=rtl]) ul,
+.mce-visualblocks:not([dir=rtl]) ol,
+.mce-visualblocks:not([dir=rtl]) dl {
+  margin-left: 3px;
+}
+.mce-visualblocks[dir=rtl] p,
+.mce-visualblocks[dir=rtl] h1,
+.mce-visualblocks[dir=rtl] h2,
+.mce-visualblocks[dir=rtl] h3,
+.mce-visualblocks[dir=rtl] h4,
+.mce-visualblocks[dir=rtl] h5,
+.mce-visualblocks[dir=rtl] h6,
+.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),
+.mce-visualblocks[dir=rtl] section,
+.mce-visualblocks[dir=rtl] article,
+.mce-visualblocks[dir=rtl] blockquote,
+.mce-visualblocks[dir=rtl] address,
+.mce-visualblocks[dir=rtl] pre,
+.mce-visualblocks[dir=rtl] figure,
+.mce-visualblocks[dir=rtl] figcaption,
+.mce-visualblocks[dir=rtl] hgroup,
+.mce-visualblocks[dir=rtl] aside,
+.mce-visualblocks[dir=rtl] ul,
+.mce-visualblocks[dir=rtl] ol,
+.mce-visualblocks[dir=rtl] dl {
+  background-position-x: right;
+  margin-right: 3px;
+}
+.mce-nbsp,
+.mce-shy {
+  background: #aaa;
+}
+.mce-shy::after {
+  content: '-';
+}

File diff suppressed because it is too large
+ 6 - 0
public/resource/tinymce/skins/ui/jeecg/content.inline.min.css


File diff suppressed because it is too large
+ 6 - 0
public/resource/tinymce/skins/ui/jeecg/content.min.css


+ 29 - 0
public/resource/tinymce/skins/ui/jeecg/content.mobile.css

@@ -0,0 +1,29 @@
+/**
+* Copyright (c) Tiny Technologies, Inc. All rights reserved.
+* Licensed under the LGPL or a commercial license.
+* For LGPL see License.txt in the project root for license information.
+* For commercial licenses see https://www.tiny.cloud/
+*/
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection {
+  /* Note: this file is used inside the content, so isn't part of theming */
+  background-color: green;
+  display: inline-block;
+  opacity: 0.5;
+  position: absolute;
+}
+body {
+  -webkit-text-size-adjust: none;
+}
+body img {
+  /* this is related to the content margin */
+  max-width: 96vw;
+}
+body table img {
+  max-width: 95%;
+}
+body {
+  font-family: sans-serif;
+}
+table {
+  border-collapse: collapse;
+}

+ 7 - 0
public/resource/tinymce/skins/ui/jeecg/content.mobile.min.css

@@ -0,0 +1,7 @@
+/**
+* Copyright (c) Tiny Technologies, Inc. All rights reserved.
+* Licensed under the LGPL or a commercial license.
+* For LGPL see License.txt in the project root for license information.
+* For commercial licenses see https://www.tiny.cloud/
+*/
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}

BIN
public/resource/tinymce/skins/ui/jeecg/fonts/tinymce-mobile.woff


+ 3045 - 0
public/resource/tinymce/skins/ui/jeecg/skin.css

@@ -0,0 +1,3045 @@
+/**
+* Copyright (c) Tiny Technologies, Inc. All rights reserved.
+* Licensed under the LGPL or a commercial license.
+* For LGPL see License.txt in the project root for license information.
+* For commercial licenses see https://www.tiny.cloud/
+*/
+.tox {
+  box-shadow: none;
+  box-sizing: content-box;
+  color: rgba(84, 111, 94, 0.85);
+  cursor: auto;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  font-size: 10px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: normal;
+  -webkit-tap-highlight-color: transparent;
+  text-decoration: none;
+  text-shadow: none;
+  text-transform: none;
+  vertical-align: initial;
+  white-space: normal;
+}
+.tox *:not(svg):not(rect) {
+  box-sizing: inherit;
+  color: inherit;
+  cursor: inherit;
+  direction: inherit;
+  font-family: inherit;
+  font-size: inherit;
+  font-style: inherit;
+  font-weight: inherit;
+  line-height: inherit;
+  -webkit-tap-highlight-color: inherit;
+  text-align: inherit;
+  text-decoration: inherit;
+  text-shadow: inherit;
+  text-transform: inherit;
+  vertical-align: inherit;
+  white-space: inherit;
+}
+.tox *:not(svg):not(rect) {
+  /* stylelint-disable-line no-duplicate-selectors */
+  background: transparent;
+  border: 0;
+  box-shadow: none;
+  float: none;
+  height: auto;
+  margin: 0;
+  max-width: none;
+  outline: 0;
+  padding: 0;
+  position: static;
+  width: auto;
+}
+.tox:not([dir=rtl]) {
+  direction: ltr;
+  text-align: left;
+}
+.tox[dir=rtl] {
+  direction: rtl;
+  text-align: right;
+}
+.tox-tinymce {
+  border: 1px solid #d9d9d9;
+  border-radius: 0px;
+  box-shadow: none;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  overflow: hidden;
+  position: relative;
+  visibility: inherit !important;
+}
+.tox-tinymce-inline {
+  border: none;
+  box-shadow: none;
+}
+.tox-tinymce-inline .tox-editor-header {
+  background-color: transparent;
+  border: 1px solid #d9d9d9;
+  border-radius: 0px;
+  box-shadow: none;
+}
+.tox-tinymce-aux {
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  z-index: 1300;
+}
+.tox-tinymce *:focus,
+.tox-tinymce-aux *:focus {
+  outline: none;
+}
+button::-moz-focus-inner {
+  border: 0;
+}
+.tox[dir=rtl] .tox-icon--flip svg {
+  transform: rotateY(180deg);
+}
+.tox .accessibility-issue__header {
+  align-items: center;
+  display: flex;
+  margin-bottom: 2.5px;
+}
+.tox .accessibility-issue__description {
+  align-items: stretch;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  display: flex;
+  justify-content: space-between;
+}
+.tox .accessibility-issue__description > div {
+  padding-bottom: 2.5px;
+}
+.tox .accessibility-issue__description > div > div {
+  align-items: center;
+  display: flex;
+  margin-bottom: 2.5px;
+}
+.tox .accessibility-issue__description > *:last-child:not(:only-child) {
+  border-color: #d9d9d9;
+  border-style: solid;
+}
+.tox .accessibility-issue__repair {
+  margin-top: 16px;
+}
+.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description {
+  background-color: rgba(10, 143, 233, 0.1);
+  border-color: rgba(10, 143, 233, 0.4);
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description > *:last-child {
+  border-color: rgba(10, 143, 233, 0.4);
+}
+.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 {
+  color: #0a8fe9;
+}
+.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg {
+  fill: #0a8fe9;
+}
+.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon {
+  color: #0a8fe9;
+}
+.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description {
+  background-color: rgba(255, 165, 0, 0.1);
+  border-color: rgba(255, 165, 0, 0.5);
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description > *:last-child {
+  border-color: rgba(255, 165, 0, 0.5);
+}
+.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 {
+  color: #cc8500;
+}
+.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg {
+  fill: #cc8500;
+}
+.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon {
+  color: #cc8500;
+}
+.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description {
+  background-color: rgba(204, 0, 0, 0.1);
+  border-color: rgba(204, 0, 0, 0.4);
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description > *:last-child {
+  border-color: rgba(204, 0, 0, 0.4);
+}
+.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 {
+  color: #c00;
+}
+.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg {
+  fill: #c00;
+}
+.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon {
+  color: #c00;
+}
+.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description {
+  background-color: rgba(120, 171, 70, 0.1);
+  border-color: rgba(120, 171, 70, 0.4);
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child {
+  border-color: rgba(120, 171, 70, 0.4);
+}
+.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 {
+  color: #78AB46;
+}
+.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg {
+  fill: #78AB46;
+}
+.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon {
+  color: #78AB46;
+}
+.tox .tox-dialog__body-content .accessibility-issue__header h1,
+.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 {
+  margin-top: 0;
+}
+.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button {
+  margin-left: 2.5px;
+}
+.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {
+  margin-left: auto;
+}
+.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description {
+  padding: 2.5px 2.5px 2.5px 5px;
+}
+.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description > *:last-child {
+  border-left-width: 1px;
+  padding-left: 2.5px;
+}
+.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button {
+  margin-right: 2.5px;
+}
+.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) {
+  margin-right: auto;
+}
+.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description {
+  padding: 2.5px 5px 2.5px 2.5px;
+}
+.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description > *:last-child {
+  border-right-width: 1px;
+  padding-right: 2.5px;
+}
+.tox .tox-anchorbar {
+  display: flex;
+  flex: 0 0 auto;
+}
+.tox .tox-bar {
+  display: flex;
+  flex: 0 0 auto;
+}
+.tox .tox-button {
+  background-color: #0a8fe9;
+  background-image: none;
+  background-position: 0 0;
+  background-repeat: repeat;
+  border-color: #0a8fe9;
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: none;
+  box-sizing: border-box;
+  color: #fff;
+  cursor: pointer;
+  display: inline-block;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  letter-spacing: normal;
+  line-height: 24px;
+  margin: 0;
+  outline: none;
+  padding: 2.5px 10px;
+  text-align: center;
+  text-decoration: none;
+  text-transform: none;
+  white-space: nowrap;
+}
+.tox .tox-button[disabled] {
+  background-color: #0a8fe9;
+  background-image: none;
+  border-color: #0a8fe9;
+  box-shadow: none;
+  color: rgba(255, 255, 255, 0.5);
+  cursor: not-allowed;
+}
+.tox .tox-button:focus:not(:disabled) {
+  background-color: #0980d1;
+  background-image: none;
+  border-color: #0980d1;
+  box-shadow: none;
+  color: #fff;
+}
+.tox .tox-button:hover:not(:disabled) {
+  background-color: #0980d1;
+  background-image: none;
+  border-color: #0980d1;
+  box-shadow: none;
+  color: #fff;
+}
+.tox .tox-button:active:not(:disabled) {
+  background-color: #0871b8;
+  background-image: none;
+  border-color: #0871b8;
+  box-shadow: none;
+  color: #fff;
+}
+.tox .tox-button--secondary {
+  background-color: #f0f0f0;
+  background-image: none;
+  background-position: 0 0;
+  background-repeat: repeat;
+  border-color: #f0f0f0;
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  letter-spacing: normal;
+  outline: none;
+  padding: 2.5px 10px;
+  text-decoration: none;
+  text-transform: none;
+}
+.tox .tox-button--secondary[disabled] {
+  background-color: #f0f0f0;
+  background-image: none;
+  border-color: #f0f0f0;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.5);
+}
+.tox .tox-button--secondary:focus:not(:disabled) {
+  background-color: #e3e3e3;
+  background-image: none;
+  border-color: #e3e3e3;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--secondary:hover:not(:disabled) {
+  background-color: #e3e3e3;
+  background-image: none;
+  border-color: #e3e3e3;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--secondary:active:not(:disabled) {
+  background-color: #d6d6d6;
+  background-image: none;
+  border-color: #d6d6d6;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--icon,
+.tox .tox-button.tox-button--icon,
+.tox .tox-button.tox-button--secondary.tox-button--icon {
+  padding: 2.5px;
+}
+.tox .tox-button--icon .tox-icon svg,
+.tox .tox-button.tox-button--icon .tox-icon svg,
+.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg {
+  display: block;
+  fill: currentColor;
+}
+.tox .tox-button-link {
+  background: 0;
+  border: none;
+  box-sizing: border-box;
+  cursor: pointer;
+  display: inline-block;
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  font-size: 10px;
+  font-weight: normal;
+  line-height: 1.3;
+  margin: 0;
+  padding: 0;
+  white-space: nowrap;
+}
+.tox .tox-button-link--sm {
+  font-size: 8.75px;
+}
+.tox .tox-button--naked {
+  background-color: transparent;
+  border-color: transparent;
+  box-shadow: unset;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--naked[disabled] {
+  background-color: #f0f0f0;
+  border-color: #f0f0f0;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.5);
+}
+.tox .tox-button--naked:hover:not(:disabled) {
+  background-color: #e3e3e3;
+  border-color: #e3e3e3;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--naked:focus:not(:disabled) {
+  background-color: #e3e3e3;
+  border-color: #e3e3e3;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--naked:active:not(:disabled) {
+  background-color: #d6d6d6;
+  border-color: #d6d6d6;
+  box-shadow: none;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-button--naked .tox-icon svg {
+  fill: currentColor;
+}
+.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-checkbox {
+  align-items: center;
+  border-radius: 3px;
+  cursor: pointer;
+  display: flex;
+  height: 36px;
+  min-width: 36px;
+}
+.tox .tox-checkbox__input {
+  /* Hide from view but visible to screen readers */
+  height: 1px;
+  overflow: hidden;
+  position: absolute;
+  top: auto;
+  width: 1px;
+}
+.tox .tox-checkbox__icons {
+  align-items: center;
+  border-radius: 3px;
+  box-shadow: 0 0 0 2px transparent;
+  box-sizing: content-box;
+  display: flex;
+  height: 24px;
+  justify-content: center;
+  padding: calc(2.5px - 1px);
+  width: 24px;
+}
+.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {
+  display: block;
+  fill: rgba(84, 111, 94, 0.3);
+}
+.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {
+  display: none;
+  fill: #0a8fe9;
+}
+.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg {
+  display: none;
+  fill: #0a8fe9;
+}
+.tox .tox-checkbox--disabled {
+  color: rgba(84, 111, 94, 0.5);
+  cursor: not-allowed;
+}
+.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg {
+  fill: rgba(84, 111, 94, 0.5);
+}
+.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {
+  fill: rgba(84, 111, 94, 0.5);
+}
+.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {
+  fill: rgba(84, 111, 94, 0.5);
+}
+.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {
+  display: none;
+}
+.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg {
+  display: block;
+}
+.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg {
+  display: none;
+}
+.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg {
+  display: block;
+}
+.tox input.tox-checkbox__input:focus + .tox-checkbox__icons {
+  border-radius: 3px;
+  box-shadow: inset 0 0 0 1px #0a8fe9;
+  padding: calc(2.5px - 1px);
+}
+.tox:not([dir=rtl]) .tox-checkbox__label {
+  margin-left: 2.5px;
+}
+.tox:not([dir=rtl]) .tox-checkbox__input {
+  left: -10000px;
+}
+.tox:not([dir=rtl]) .tox-bar .tox-checkbox {
+  margin-left: 2.5px;
+}
+.tox[dir=rtl] .tox-checkbox__label {
+  margin-right: 2.5px;
+}
+.tox[dir=rtl] .tox-checkbox__input {
+  right: -10000px;
+}
+.tox[dir=rtl] .tox-bar .tox-checkbox {
+  margin-right: 2.5px;
+}
+.tox {
+  /* stylelint-disable-next-line no-descending-specificity */
+}
+.tox .tox-collection--toolbar .tox-collection__group {
+  display: flex;
+  padding: 0;
+}
+.tox .tox-collection--grid .tox-collection__group {
+  display: flex;
+  flex-wrap: wrap;
+  max-height: 208px;
+  overflow-x: hidden;
+  overflow-y: auto;
+  padding: 0;
+}
+.tox .tox-collection--list .tox-collection__group {
+  border-bottom-width: 0;
+  border-color: #d9d9d9;
+  border-left-width: 0;
+  border-right-width: 0;
+  border-style: solid;
+  border-top-width: 1px;
+  padding: 2.5px 0;
+}
+.tox .tox-collection--list .tox-collection__group:first-child {
+  border-top-width: 0;
+}
+.tox .tox-collection__group-heading {
+  background-color: #f3f3f3;
+  color: rgba(84, 111, 94, 0.7);
+  cursor: default;
+  font-size: 12px;
+  font-style: normal;
+  font-weight: normal;
+  margin-bottom: 2.5px;
+  margin-top: -2.5px;
+  padding: 2.5px 5px;
+  text-transform: none;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+.tox .tox-collection__item {
+  align-items: center;
+  color: rgba(84, 111, 94, 0.85);
+  cursor: pointer;
+  display: flex;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+.tox .tox-collection--list .tox-collection__item {
+  padding: 2.5px 5px;
+}
+.tox .tox-collection--toolbar .tox-collection__item {
+  border-radius: 3px;
+  padding: 2.5px;
+}
+.tox .tox-collection--grid .tox-collection__item {
+  border-radius: 3px;
+  padding: 2.5px;
+}
+.tox .tox-collection--list .tox-collection__item--enabled {
+  background-color: #fff;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection--list .tox-collection__item--active {
+  background-color: #e5e9e7;
+}
+.tox .tox-collection--toolbar .tox-collection__item--enabled {
+  background-color: #e5e9e7;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection--toolbar .tox-collection__item--active {
+  background-color: #e5e9e7;
+}
+.tox .tox-collection--grid .tox-collection__item--enabled {
+  background-color: #e5e9e7;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) {
+  background-color: #e5e9e7;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection__item-icon,
+.tox .tox-collection__item-checkmark {
+  align-items: center;
+  display: flex;
+  height: 24px;
+  justify-content: center;
+  width: 24px;
+}
+.tox .tox-collection__item-icon svg,
+.tox .tox-collection__item-checkmark svg {
+  fill: currentColor;
+}
+.tox .tox-collection--toolbar-lg .tox-collection__item-icon {
+  height: 48px;
+  width: 48px;
+}
+.tox .tox-collection__item-label {
+  color: currentColor;
+  display: inline-block;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 24px;
+  text-transform: none;
+  word-break: break-all;
+}
+.tox .tox-collection__item-accessory {
+  color: rgba(84, 111, 94, 0.7);
+  display: inline-block;
+  font-size: 8.75px;
+  height: 24px;
+  line-height: 24px;
+  text-transform: none;
+}
+.tox .tox-collection__item-caret {
+  align-items: center;
+  display: flex;
+  min-height: 24px;
+}
+.tox .tox-collection__item-caret::after {
+  content: '';
+  font-size: 0;
+  min-height: inherit;
+}
+.tox .tox-collection__item-caret svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-collection__item--state-disabled {
+  background-color: transparent;
+  color: rgba(84, 111, 94, 0.5);
+  cursor: not-allowed;
+}
+.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg {
+  fill: rgba(84, 111, 94, 0.5);
+}
+.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg {
+  display: none;
+}
+.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark {
+  display: none;
+}
+.tox .tox-collection--horizontal {
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
+  display: flex;
+  flex: 0 0 auto;
+  flex-shrink: 0;
+  flex-wrap: nowrap;
+  margin-bottom: 0;
+  overflow-x: auto;
+  padding: 0;
+}
+.tox .tox-collection--horizontal .tox-collection__group {
+  align-items: center;
+  display: flex;
+  flex-wrap: nowrap;
+  margin: 0;
+  padding: 0 2.5px;
+}
+.tox .tox-collection--horizontal .tox-collection__item {
+  height: 34px;
+  margin: 2px 0 3px 0;
+  padding: 0 4px;
+}
+.tox .tox-collection--horizontal .tox-collection__item-label {
+  white-space: nowrap;
+}
+.tox .tox-collection--horizontal .tox-collection__item-caret {
+  margin-left: 4px;
+}
+.tox .tox-collection__item-container {
+  display: flex;
+}
+.tox .tox-collection__item-container--row {
+  align-items: center;
+  flex: 1 1 auto;
+  flex-direction: row;
+}
+.tox .tox-collection__item-container--row.tox-collection__item-container--align-left {
+  margin-right: auto;
+}
+.tox .tox-collection__item-container--row.tox-collection__item-container--align-right {
+  justify-content: flex-end;
+  margin-left: auto;
+}
+.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top {
+  align-items: flex-start;
+  margin-bottom: auto;
+}
+.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle {
+  align-items: center;
+}
+.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom {
+  align-items: flex-end;
+  margin-top: auto;
+}
+.tox .tox-collection__item-container--column {
+  -ms-grid-row-align: center;
+      align-self: center;
+  flex: 1 1 auto;
+  flex-direction: column;
+}
+.tox .tox-collection__item-container--column.tox-collection__item-container--align-left {
+  align-items: flex-start;
+}
+.tox .tox-collection__item-container--column.tox-collection__item-container--align-right {
+  align-items: flex-end;
+}
+.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top {
+  align-self: flex-start;
+}
+.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle {
+  -ms-grid-row-align: center;
+      align-self: center;
+}
+.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom {
+  align-self: flex-end;
+}
+.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {
+  border-right: 1px solid #d9d9d9;
+}
+.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) {
+  margin-left: 5px;
+}
+.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {
+  margin-left: 2.5px;
+}
+.tox:not([dir=rtl]) .tox-collection__item-accessory {
+  margin-left: 10px;
+  text-align: right;
+}
+.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret {
+  margin-left: 10px;
+}
+.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) {
+  border-left: 1px solid #d9d9d9;
+}
+.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) {
+  margin-right: 5px;
+}
+.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child {
+  margin-right: 2.5px;
+}
+.tox[dir=rtl] .tox-collection__item-accessory {
+  margin-right: 10px;
+  text-align: left;
+}
+.tox[dir=rtl] .tox-collection .tox-collection__item-caret {
+  margin-right: 10px;
+  transform: rotateY(180deg);
+}
+.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret {
+  margin-right: 4px;
+}
+.tox .tox-color-picker-container {
+  display: flex;
+  flex-direction: row;
+  height: 225px;
+  margin: 0;
+}
+.tox .tox-sv-palette {
+  box-sizing: border-box;
+  display: flex;
+  height: 100%;
+}
+.tox .tox-sv-palette-spectrum {
+  height: 100%;
+}
+.tox .tox-sv-palette,
+.tox .tox-sv-palette-spectrum {
+  width: 225px;
+}
+.tox .tox-sv-palette-thumb {
+  background: none;
+  border: 1px solid black;
+  border-radius: 50%;
+  box-sizing: content-box;
+  height: 12px;
+  position: absolute;
+  width: 12px;
+}
+.tox .tox-sv-palette-inner-thumb {
+  border: 1px solid white;
+  border-radius: 50%;
+  height: 10px;
+  position: absolute;
+  width: 10px;
+}
+.tox .tox-hue-slider {
+  box-sizing: border-box;
+  height: 100%;
+  width: 25px;
+}
+.tox .tox-hue-slider-spectrum {
+  background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00);
+  height: 100%;
+  width: 100%;
+}
+.tox .tox-hue-slider,
+.tox .tox-hue-slider-spectrum {
+  width: 20px;
+}
+.tox .tox-hue-slider-thumb {
+  background: white;
+  border: 1px solid black;
+  box-sizing: content-box;
+  height: 4px;
+  width: 100%;
+}
+.tox .tox-rgb-form {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+.tox .tox-rgb-form div {
+  align-items: center;
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 5px;
+  width: inherit;
+}
+.tox .tox-rgb-form input {
+  width: 6em;
+}
+.tox .tox-rgb-form input.tox-invalid {
+  /* Need !important to override Chrome's focus styling unfortunately */
+  border: 1px solid red !important;
+}
+.tox .tox-rgb-form .tox-rgba-preview {
+  border: 1px solid black;
+  flex-grow: 2;
+  margin-bottom: 0;
+}
+.tox:not([dir=rtl]) .tox-sv-palette {
+  margin-right: 15px;
+}
+.tox:not([dir=rtl]) .tox-hue-slider {
+  margin-right: 15px;
+}
+.tox:not([dir=rtl]) .tox-hue-slider-thumb {
+  margin-left: -1px;
+}
+.tox:not([dir=rtl]) .tox-rgb-form label {
+  margin-right: 0.5em;
+}
+.tox[dir=rtl] .tox-sv-palette {
+  margin-left: 15px;
+}
+.tox[dir=rtl] .tox-hue-slider {
+  margin-left: 15px;
+}
+.tox[dir=rtl] .tox-hue-slider-thumb {
+  margin-right: -1px;
+}
+.tox[dir=rtl] .tox-rgb-form label {
+  margin-left: 0.5em;
+}
+.tox .tox-toolbar .tox-swatches,
+.tox .tox-toolbar__primary .tox-swatches,
+.tox .tox-toolbar__overflow .tox-swatches {
+  margin: 2px 0 3px 4px;
+}
+.tox .tox-collection--list .tox-collection__group .tox-swatches-menu {
+  border: 0;
+  margin: -2.5px 0;
+}
+.tox .tox-swatches__row {
+  display: flex;
+}
+.tox .tox-swatch {
+  height: 30px;
+  transition: transform 0.15s, box-shadow 0.15s;
+  width: 30px;
+}
+.tox .tox-swatch:hover,
+.tox .tox-swatch:focus {
+  box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset;
+  transform: scale(0.8);
+}
+.tox .tox-swatch--remove {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+.tox .tox-swatch--remove svg path {
+  stroke: #e74c3c;
+}
+.tox .tox-swatches__picker-btn {
+  align-items: center;
+  background-color: transparent;
+  border: 0;
+  cursor: pointer;
+  display: flex;
+  height: 30px;
+  justify-content: center;
+  outline: none;
+  padding: 0;
+  width: 30px;
+}
+.tox .tox-swatches__picker-btn svg {
+  height: 24px;
+  width: 24px;
+}
+.tox .tox-swatches__picker-btn:hover {
+  background: #e5e9e7;
+}
+.tox:not([dir=rtl]) .tox-swatches__picker-btn {
+  margin-left: auto;
+}
+.tox[dir=rtl] .tox-swatches__picker-btn {
+  margin-right: auto;
+}
+.tox .tox-comment-thread {
+  background: #fff;
+  position: relative;
+}
+.tox .tox-comment-thread > *:not(:first-child) {
+  margin-top: 5px;
+}
+.tox .tox-comment {
+  background: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  box-shadow: 0 4px 8px 0 rgba(84, 111, 94, 0.1);
+  padding: 5px 5px 10px 5px;
+  position: relative;
+}
+.tox .tox-comment__header {
+  align-items: center;
+  color: rgba(84, 111, 94, 0.85);
+  display: flex;
+  justify-content: space-between;
+}
+.tox .tox-comment__date {
+  color: rgba(84, 111, 94, 0.7);
+  font-size: 12px;
+}
+.tox .tox-comment__body {
+  color: rgba(84, 111, 94, 0.85);
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.3;
+  margin-top: 5px;
+  position: relative;
+  text-transform: initial;
+}
+.tox .tox-comment__body textarea {
+  resize: none;
+  white-space: normal;
+  width: 100%;
+}
+.tox .tox-comment__expander {
+  padding-top: 5px;
+}
+.tox .tox-comment__expander p {
+  color: rgba(84, 111, 94, 0.7);
+  font-size: 8.75px;
+  font-style: normal;
+}
+.tox .tox-comment__body p {
+  margin: 0;
+}
+.tox .tox-comment__buttonspacing {
+  padding-top: 10px;
+  text-align: center;
+}
+.tox .tox-comment-thread__overlay::after {
+  background: #fff;
+  bottom: 0;
+  content: "";
+  display: flex;
+  left: 0;
+  opacity: 0.9;
+  position: absolute;
+  right: 0;
+  top: 0;
+  z-index: 5;
+}
+.tox .tox-comment__reply {
+  display: flex;
+  flex-shrink: 0;
+  flex-wrap: wrap;
+  justify-content: flex-end;
+  margin-top: 5px;
+}
+.tox .tox-comment__reply > *:first-child {
+  margin-bottom: 5px;
+  width: 100%;
+}
+.tox .tox-comment__edit {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: flex-end;
+  margin-top: 10px;
+}
+.tox .tox-comment__gradient::after {
+  background: linear-gradient(rgba(255, 255, 255, 0), #fff);
+  bottom: 0;
+  content: "";
+  display: block;
+  height: 5em;
+  margin-top: -40px;
+  position: absolute;
+  width: 100%;
+}
+.tox .tox-comment__overlay {
+  background: #fff;
+  bottom: 0;
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  left: 0;
+  opacity: 0.9;
+  position: absolute;
+  right: 0;
+  text-align: center;
+  top: 0;
+  z-index: 5;
+}
+.tox .tox-comment__loading-text {
+  align-items: center;
+  color: rgba(84, 111, 94, 0.85);
+  display: flex;
+  flex-direction: column;
+  position: relative;
+}
+.tox .tox-comment__loading-text > div {
+  padding-bottom: 10px;
+}
+.tox .tox-comment__overlaytext {
+  bottom: 0;
+  flex-direction: column;
+  font-size: 8.75px;
+  left: 0;
+  padding: 1em;
+  position: absolute;
+  right: 0;
+  top: 0;
+  z-index: 10;
+}
+.tox .tox-comment__overlaytext p {
+  background-color: #fff;
+  box-shadow: 0 0 8px 8px #fff;
+  color: rgba(84, 111, 94, 0.85);
+  text-align: center;
+}
+.tox .tox-comment__overlaytext div:nth-of-type(2) {
+  font-size: 0.8em;
+}
+.tox .tox-comment__busy-spinner {
+  align-items: center;
+  background-color: #fff;
+  bottom: 0;
+  display: flex;
+  justify-content: center;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  z-index: 20;
+}
+.tox .tox-comment__scroll {
+  display: flex;
+  flex-direction: column;
+  flex-shrink: 1;
+  overflow: auto;
+}
+.tox .tox-conversations {
+  margin: 5px;
+}
+.tox:not([dir=rtl]) .tox-comment__edit {
+  margin-left: 5px;
+}
+.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child,
+.tox:not([dir=rtl]) .tox-comment__edit > *:last-child,
+.tox:not([dir=rtl]) .tox-comment__reply > *:last-child {
+  margin-left: 5px;
+}
+.tox[dir=rtl] .tox-comment__edit {
+  margin-right: 5px;
+}
+.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child,
+.tox[dir=rtl] .tox-comment__edit > *:last-child,
+.tox[dir=rtl] .tox-comment__reply > *:last-child {
+  margin-right: 5px;
+}
+.tox .tox-user {
+  align-items: center;
+  display: flex;
+}
+.tox .tox-user__avatar svg {
+  fill: rgba(84, 111, 94, 0.7);
+}
+.tox .tox-user__name {
+  color: rgba(84, 111, 94, 0.7);
+  font-size: 12px;
+  font-style: normal;
+  font-weight: normal;
+  text-transform: uppercase;
+}
+.tox:not([dir=rtl]) .tox-user__avatar svg {
+  margin-right: 5px;
+}
+.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {
+  margin-left: 5px;
+}
+.tox[dir=rtl] .tox-user__avatar svg {
+  margin-left: 5px;
+}
+.tox[dir=rtl] .tox-user__avatar + .tox-user__name {
+  margin-right: 5px;
+}
+.tox .tox-dialog-wrap {
+  align-items: center;
+  bottom: 0;
+  display: flex;
+  justify-content: center;
+  left: 0;
+  position: fixed;
+  right: 0;
+  top: 0;
+  z-index: 1100;
+}
+.tox .tox-dialog-wrap__backdrop {
+  background-color: rgba(255, 255, 255, 0.75);
+  bottom: 0;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  z-index: 1;
+}
+.tox .tox-dialog-wrap__backdrop--opaque {
+  background-color: #fff;
+}
+.tox .tox-dialog {
+  background-color: #fff;
+  border-color: #d9d9d9;
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: 0 16px 16px -10px rgba(84, 111, 94, 0.15), 0 0 40px 1px rgba(84, 111, 94, 0.15);
+  display: flex;
+  flex-direction: column;
+  max-height: 100%;
+  max-width: 480px;
+  overflow: hidden;
+  position: relative;
+  width: 95vw;
+  z-index: 2;
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox .tox-dialog {
+    align-self: flex-start;
+    margin: 5px auto;
+    width: calc(100vw - 10px);
+  }
+}
+.tox .tox-dialog-inline {
+  z-index: 1100;
+}
+.tox .tox-dialog__header {
+  align-items: center;
+  background-color: #fff;
+  border-bottom: none;
+  color: rgba(84, 111, 94, 0.85);
+  display: flex;
+  font-size: 10px;
+  justify-content: space-between;
+  padding: 5px 10px 0 10px;
+  position: relative;
+}
+.tox .tox-dialog__header .tox-button {
+  z-index: 1;
+}
+.tox .tox-dialog__draghandle {
+  cursor: grab;
+  height: 100%;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.tox .tox-dialog__draghandle:active {
+  cursor: grabbing;
+}
+.tox .tox-dialog__dismiss {
+  margin-left: auto;
+}
+.tox .tox-dialog__title {
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  font-size: 12.5px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.3;
+  margin: 0;
+  text-transform: none;
+}
+.tox .tox-dialog__body {
+  color: rgba(84, 111, 94, 0.85);
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  font-size: 10px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.3;
+  min-width: 0;
+  text-align: left;
+  text-transform: none;
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox .tox-dialog__body {
+    flex-direction: column;
+  }
+}
+.tox .tox-dialog__body-nav {
+  align-items: flex-start;
+  display: flex;
+  flex-direction: column;
+  padding: 10px 10px;
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {
+    flex-direction: row;
+    -webkit-overflow-scrolling: touch;
+    overflow-x: auto;
+    padding-bottom: 0;
+  }
+}
+.tox .tox-dialog__body-nav-item {
+  border-bottom: 2px solid transparent;
+  color: rgba(84, 111, 94, 0.7);
+  display: inline-block;
+  font-size: 8.75px;
+  line-height: 1.3;
+  margin-bottom: 5px;
+  text-decoration: none;
+  white-space: nowrap;
+}
+.tox .tox-dialog__body-nav-item:focus {
+  background-color: rgba(10, 143, 233, 0.1);
+}
+.tox .tox-dialog__body-nav-item--active {
+  border-bottom: 2px solid #0a8fe9;
+  color: #0a8fe9;
+}
+.tox .tox-dialog__body-content {
+  box-sizing: border-box;
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  -ms-flex-preferred-size: auto;
+  max-height: 650px;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch;
+  padding: 10px 10px;
+}
+.tox .tox-dialog__body-content > * {
+  margin-bottom: 0;
+  margin-top: 10px;
+}
+.tox .tox-dialog__body-content > *:first-child {
+  margin-top: 0;
+}
+.tox .tox-dialog__body-content > *:last-child {
+  margin-bottom: 0;
+}
+.tox .tox-dialog__body-content > *:only-child {
+  margin-bottom: 0;
+  margin-top: 0;
+}
+.tox .tox-dialog__body-content a {
+  color: #0a8fe9;
+  cursor: pointer;
+  text-decoration: none;
+}
+.tox .tox-dialog__body-content a:hover,
+.tox .tox-dialog__body-content a:focus {
+  color: #0871b8;
+  text-decoration: none;
+}
+.tox .tox-dialog__body-content a:active {
+  color: #0871b8;
+  text-decoration: none;
+}
+.tox .tox-dialog__body-content svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-dialog__body-content ul {
+  display: block;
+  list-style-type: disc;
+  margin-bottom: 10px;
+  -webkit-margin-end: 0;
+          margin-inline-end: 0;
+  -webkit-margin-start: 0;
+          margin-inline-start: 0;
+  -webkit-padding-start: 2.5rem;
+          padding-inline-start: 2.5rem;
+}
+.tox .tox-dialog__body-content .tox-form__group h1 {
+  color: rgba(84, 111, 94, 0.85);
+  font-size: 12.5px;
+  font-style: normal;
+  font-weight: normal;
+  letter-spacing: normal;
+  margin-bottom: 10px;
+  margin-top: 2rem;
+  text-transform: none;
+}
+.tox .tox-dialog__body-content .tox-form__group h2 {
+  color: rgba(84, 111, 94, 0.85);
+  font-size: 10px;
+  font-style: normal;
+  font-weight: normal;
+  letter-spacing: normal;
+  margin-bottom: 10px;
+  margin-top: 2rem;
+  text-transform: none;
+}
+.tox .tox-dialog__body-content .tox-form__group p {
+  margin-bottom: 10px;
+}
+.tox .tox-dialog__body-content .tox-form__group h1:first-child,
+.tox .tox-dialog__body-content .tox-form__group h2:first-child,
+.tox .tox-dialog__body-content .tox-form__group p:first-child {
+  margin-top: 0;
+}
+.tox .tox-dialog__body-content .tox-form__group h1:last-child,
+.tox .tox-dialog__body-content .tox-form__group h2:last-child,
+.tox .tox-dialog__body-content .tox-form__group p:last-child {
+  margin-bottom: 0;
+}
+.tox .tox-dialog__body-content .tox-form__group h1:only-child,
+.tox .tox-dialog__body-content .tox-form__group h2:only-child,
+.tox .tox-dialog__body-content .tox-form__group p:only-child {
+  margin-bottom: 0;
+  margin-top: 0;
+}
+.tox .tox-dialog--width-lg {
+  height: 650px;
+  max-width: 1200px;
+}
+.tox .tox-dialog--width-md {
+  max-width: 800px;
+}
+.tox .tox-dialog--width-md .tox-dialog__body-content {
+  overflow: auto;
+}
+.tox .tox-dialog__body-content--centered {
+  text-align: center;
+}
+.tox .tox-dialog__footer {
+  align-items: center;
+  background-color: #fff;
+  border-top: 1px solid #d9d9d9;
+  display: flex;
+  justify-content: space-between;
+  padding: 5px 10px;
+}
+.tox .tox-dialog__footer-start,
+.tox .tox-dialog__footer-end {
+  display: flex;
+}
+.tox .tox-dialog__busy-spinner {
+  align-items: center;
+  background-color: rgba(255, 255, 255, 0.75);
+  bottom: 0;
+  display: flex;
+  justify-content: center;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  z-index: 3;
+}
+.tox .tox-dialog__table {
+  border-collapse: collapse;
+  width: 100%;
+}
+.tox .tox-dialog__table thead th {
+  font-weight: normal;
+  padding-bottom: 5px;
+}
+.tox .tox-dialog__table tbody tr {
+  border-bottom: 1px solid #d9d9d9;
+}
+.tox .tox-dialog__table tbody tr:last-child {
+  border-bottom: none;
+}
+.tox .tox-dialog__table td {
+  padding-bottom: 5px;
+  padding-top: 5px;
+}
+.tox .tox-dialog__popups {
+  position: absolute;
+  width: 100%;
+  z-index: 1100;
+}
+.tox .tox-dialog__body-iframe {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-dialog__body-iframe .tox-navobj {
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) {
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  height: 100%;
+}
+.tox .tox-dialog-dock-fadeout {
+  opacity: 0;
+  visibility: hidden;
+}
+.tox .tox-dialog-dock-fadein {
+  opacity: 1;
+  visibility: visible;
+}
+.tox .tox-dialog-dock-transition {
+  transition: visibility 0s linear 0.3s, opacity 0.3s ease;
+}
+.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {
+  transition-delay: 0s;
+}
+.tox.tox-platform-ie {
+  /* IE11 CSS styles go here */
+}
+.tox.tox-platform-ie .tox-dialog-wrap {
+  position: -ms-device-fixed;
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {
+    margin-right: 0;
+  }
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {
+    margin-left: 5px;
+  }
+}
+.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *,
+.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * {
+  margin-left: 5px;
+}
+.tox[dir=rtl] .tox-dialog__body {
+  text-align: right;
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {
+    margin-left: 0;
+  }
+}
+@media only screen and (max-width:767px) {
+  body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {
+    margin-right: 5px;
+  }
+}
+.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *,
+.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * {
+  margin-right: 5px;
+}
+body.tox-dialog__disable-scroll {
+  overflow: hidden;
+}
+.tox .tox-dropzone-container {
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-dropzone {
+  align-items: center;
+  background: #fff;
+  border: 2px dashed #d9d9d9;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  justify-content: center;
+  min-height: 100px;
+  padding: 10px;
+}
+.tox .tox-dropzone p {
+  color: rgba(84, 111, 94, 0.7);
+  margin: 0 0 10px 0;
+}
+.tox .tox-edit-area {
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  overflow: hidden;
+  position: relative;
+}
+.tox .tox-edit-area__iframe {
+  background-color: #fff;
+  border: 0;
+  box-sizing: border-box;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  height: 100%;
+  position: absolute;
+  width: 100%;
+}
+.tox.tox-inline-edit-area {
+  border: 1px dotted #d9d9d9;
+}
+.tox .tox-editor-container {
+  display: flex;
+  flex: 1 1 auto;
+  flex-direction: column;
+  overflow: hidden;
+}
+.tox .tox-editor-header {
+  z-index: 1;
+}
+.tox:not(.tox-tinymce-inline) .tox-editor-header {
+  box-shadow: none;
+  transition: box-shadow 0.5s;
+}
+.tox.tox-tinymce--toolbar-bottom .tox-editor-header,
+.tox.tox-tinymce-inline .tox-editor-header {
+  margin-bottom: -1px;
+}
+.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header {
+  background-color: transparent;
+  box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25);
+}
+.tox-editor-dock-fadeout {
+  opacity: 0;
+  visibility: hidden;
+}
+.tox-editor-dock-fadein {
+  opacity: 1;
+  visibility: visible;
+}
+.tox-editor-dock-transition {
+  transition: visibility 0s linear 0.25s, opacity 0.25s ease;
+}
+.tox-editor-dock-transition.tox-editor-dock-fadein {
+  transition-delay: 0s;
+}
+.tox .tox-control-wrap {
+  flex: 1;
+  position: relative;
+}
+.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,
+.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,
+.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid {
+  display: none;
+}
+.tox .tox-control-wrap svg {
+  display: block;
+}
+.tox .tox-control-wrap__status-icon-wrap {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+}
+.tox .tox-control-wrap__status-icon-invalid svg {
+  fill: #c00;
+}
+.tox .tox-control-wrap__status-icon-unknown svg {
+  fill: orange;
+}
+.tox .tox-control-wrap__status-icon-valid svg {
+  fill: green;
+}
+.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,
+.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,
+.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield {
+  padding-right: 20px;
+}
+.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap {
+  right: 2.5px;
+}
+.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,
+.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,
+.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield {
+  padding-left: 20px;
+}
+.tox[dir=rtl] .tox-control-wrap__status-icon-wrap {
+  left: 2.5px;
+}
+.tox .tox-autocompleter {
+  max-width: 25em;
+}
+.tox .tox-autocompleter .tox-menu {
+  max-width: 25em;
+}
+.tox .tox-autocompleter .tox-autocompleter-highlight {
+  font-weight: normal;
+}
+.tox .tox-color-input {
+  display: flex;
+  position: relative;
+  z-index: 1;
+}
+.tox .tox-color-input .tox-textfield {
+  z-index: -1;
+}
+.tox .tox-color-input span {
+  border-color: rgba(84, 111, 94, 0.2);
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: none;
+  box-sizing: border-box;
+  height: 24px;
+  position: absolute;
+  top: 6px;
+  width: 24px;
+}
+.tox .tox-color-input span:hover:not([aria-disabled=true]),
+.tox .tox-color-input span:focus:not([aria-disabled=true]) {
+  border-color: #0a8fe9;
+  cursor: pointer;
+}
+.tox .tox-color-input span::before {
+  background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%);
+  background-position: 0 0, 0 6px, 6px -6px, -6px 0;
+  background-size: 12px 12px;
+  border: 1px solid #fff;
+  border-radius: 3px;
+  box-sizing: border-box;
+  content: '';
+  height: 24px;
+  left: -1px;
+  position: absolute;
+  top: -1px;
+  width: 24px;
+  z-index: -1;
+}
+.tox .tox-color-input span[aria-disabled=true] {
+  cursor: not-allowed;
+}
+.tox:not([dir=rtl]) .tox-color-input {
+  /* stylelint-disable-next-line no-descending-specificity */
+}
+.tox:not([dir=rtl]) .tox-color-input .tox-textfield {
+  padding-left: 36px;
+}
+.tox:not([dir=rtl]) .tox-color-input span {
+  left: 6px;
+}
+.tox[dir="rtl"] .tox-color-input {
+  /* stylelint-disable-next-line no-descending-specificity */
+}
+.tox[dir="rtl"] .tox-color-input .tox-textfield {
+  padding-right: 36px;
+}
+.tox[dir="rtl"] .tox-color-input span {
+  right: 6px;
+}
+.tox .tox-label,
+.tox .tox-toolbar-label {
+  color: rgba(84, 111, 94, 0.7);
+  display: block;
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1.3;
+  padding: 0 5px 0 0;
+  text-transform: none;
+  white-space: nowrap;
+}
+.tox .tox-toolbar-label {
+  padding: 0 5px;
+}
+.tox[dir=rtl] .tox-label {
+  padding: 0 0 0 5px;
+}
+.tox .tox-form {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-form__group {
+  box-sizing: border-box;
+  margin-bottom: 2.5px;
+}
+.tox .tox-form-group--maximize {
+  flex: 1;
+}
+.tox .tox-form__group--error {
+  color: #c00;
+}
+.tox .tox-form__group--collection {
+  display: flex;
+}
+.tox .tox-form__grid {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between;
+}
+.tox .tox-form__grid--2col > .tox-form__group {
+  width: calc(50% - (5px / 2));
+}
+.tox .tox-form__grid--3col > .tox-form__group {
+  width: calc(100% / 3 - (5px / 2));
+}
+.tox .tox-form__grid--4col > .tox-form__group {
+  width: calc(25% - (5px / 2));
+}
+.tox .tox-form__controls-h-stack {
+  align-items: center;
+  display: flex;
+}
+.tox .tox-form__group--inline {
+  align-items: center;
+  display: flex;
+}
+.tox .tox-form__group--stretched {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-form__group--stretched .tox-textarea {
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-form__group--stretched .tox-navobj {
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-form__group--stretched .tox-navobj :nth-child(2) {
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  height: 100%;
+}
+.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) {
+  margin-left: 2.5px;
+}
+.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) {
+  margin-right: 2.5px;
+}
+.tox .tox-lock.tox-locked .tox-lock-icon__unlock,
+.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock {
+  display: none;
+}
+.tox .tox-textfield,
+.tox .tox-toolbar-textfield,
+.tox .tox-listboxfield .tox-listbox--select,
+.tox .tox-textarea {
+  -webkit-appearance: none;
+     -moz-appearance: none;
+          appearance: none;
+  background-color: #fff;
+  border-color: #d9d9d9;
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: none;
+  box-sizing: border-box;
+  color: rgba(84, 111, 94, 0.85);
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  font-size: 10px;
+  line-height: 24px;
+  margin: 0;
+  min-height: 34px;
+  outline: none;
+  padding: 5px 3.25px;
+  resize: none;
+  width: 100%;
+}
+.tox .tox-textfield[disabled],
+.tox .tox-textarea[disabled] {
+  background-color: #f2f2f2;
+  color: rgba(84, 111, 94, 0.85);
+  cursor: not-allowed;
+}
+.tox .tox-textfield:focus,
+.tox .tox-listboxfield .tox-listbox--select:focus,
+.tox .tox-textarea:focus {
+  background-color: #fff;
+  border-color: #0a8fe9;
+  box-shadow: none;
+  outline: none;
+}
+.tox .tox-toolbar-textfield {
+  border-width: 0;
+  margin-bottom: 3px;
+  margin-top: 2px;
+  max-width: 250px;
+}
+.tox .tox-naked-btn {
+  background-color: transparent;
+  border: 0;
+  border-color: transparent;
+  box-shadow: unset;
+  color: #0a8fe9;
+  cursor: pointer;
+  display: block;
+  margin: 0;
+  padding: 0;
+}
+.tox .tox-naked-btn svg {
+  display: block;
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox:not([dir=rtl]) .tox-toolbar-textfield + * {
+  margin-left: 2.5px;
+}
+.tox[dir=rtl] .tox-toolbar-textfield + * {
+  margin-right: 2.5px;
+}
+.tox .tox-listboxfield {
+  cursor: pointer;
+  position: relative;
+}
+.tox .tox-listboxfield .tox-listbox--select[disabled] {
+  background-color: #f2f2f2;
+  color: rgba(84, 111, 94, 0.85);
+  cursor: not-allowed;
+}
+.tox .tox-listbox__select-label {
+  cursor: default;
+  flex: 1;
+  margin: 0 4px;
+}
+.tox .tox-listbox__select-chevron {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  width: 10px;
+}
+.tox .tox-listbox__select-chevron svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-listboxfield .tox-listbox--select {
+  align-items: center;
+  display: flex;
+}
+.tox:not([dir=rtl]) .tox-listboxfield svg {
+  right: 5px;
+}
+.tox[dir=rtl] .tox-listboxfield svg {
+  left: 5px;
+}
+.tox .tox-selectfield {
+  cursor: pointer;
+  position: relative;
+}
+.tox .tox-selectfield select {
+  -webkit-appearance: none;
+     -moz-appearance: none;
+          appearance: none;
+  background-color: #fff;
+  border-color: #d9d9d9;
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: none;
+  box-sizing: border-box;
+  color: rgba(84, 111, 94, 0.85);
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+  font-size: 10px;
+  line-height: 24px;
+  margin: 0;
+  min-height: 34px;
+  outline: none;
+  padding: 5px 3.25px;
+  resize: none;
+  width: 100%;
+}
+.tox .tox-selectfield select[disabled] {
+  background-color: #f2f2f2;
+  color: rgba(84, 111, 94, 0.85);
+  cursor: not-allowed;
+}
+.tox .tox-selectfield select::-ms-expand {
+  display: none;
+}
+.tox .tox-selectfield select:focus {
+  background-color: #fff;
+  border-color: #0a8fe9;
+  box-shadow: none;
+  outline: none;
+}
+.tox .tox-selectfield svg {
+  pointer-events: none;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+}
+.tox:not([dir=rtl]) .tox-selectfield select[size="0"],
+.tox:not([dir=rtl]) .tox-selectfield select[size="1"] {
+  padding-right: 15px;
+}
+.tox:not([dir=rtl]) .tox-selectfield svg {
+  right: 5px;
+}
+.tox[dir=rtl] .tox-selectfield select[size="0"],
+.tox[dir=rtl] .tox-selectfield select[size="1"] {
+  padding-left: 15px;
+}
+.tox[dir=rtl] .tox-selectfield svg {
+  left: 5px;
+}
+.tox .tox-textarea {
+  -webkit-appearance: textarea;
+     -moz-appearance: textarea;
+          appearance: textarea;
+  white-space: pre-wrap;
+}
+.tox-fullscreen {
+  border: 0;
+  height: 100%;
+  left: 0;
+  margin: 0;
+  overflow: hidden;
+  -ms-scroll-chaining: none;
+      overscroll-behavior: none;
+  padding: 0;
+  position: fixed;
+  top: 0;
+  touch-action: pinch-zoom;
+  width: 100%;
+}
+.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle {
+  display: none;
+}
+.tox.tox-tinymce.tox-fullscreen {
+  background-color: transparent;
+  z-index: 1200;
+}
+.tox-shadowhost.tox-fullscreen {
+  z-index: 1200;
+}
+.tox-fullscreen .tox.tox-tinymce-aux,
+.tox-fullscreen ~ .tox.tox-tinymce-aux {
+  z-index: 1201;
+}
+.tox .tox-help__more-link {
+  list-style: none;
+  margin-top: 1em;
+}
+.tox .tox-image-tools {
+  width: 100%;
+}
+.tox .tox-image-tools__toolbar {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+.tox .tox-image-tools__image {
+  background-color: #666;
+  height: 380px;
+  overflow: auto;
+  position: relative;
+  width: 100%;
+}
+.tox .tox-image-tools__image,
+.tox .tox-image-tools__image + .tox-image-tools__toolbar {
+  margin-top: 5px;
+}
+.tox .tox-image-tools__image-bg {
+  background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==);
+}
+.tox .tox-image-tools__toolbar > .tox-spacer {
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-croprect-block {
+  background: black;
+  filter: alpha(opacity=50);
+  opacity: 0.5;
+  position: absolute;
+  zoom: 1;
+}
+.tox .tox-croprect-handle {
+  border: 2px solid white;
+  height: 20px;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 20px;
+}
+.tox .tox-croprect-handle-move {
+  border: 0;
+  cursor: move;
+  position: absolute;
+}
+.tox .tox-croprect-handle-nw {
+  border-width: 2px 0 0 2px;
+  cursor: nw-resize;
+  left: 100px;
+  margin: -2px 0 0 -2px;
+  top: 100px;
+}
+.tox .tox-croprect-handle-ne {
+  border-width: 2px 2px 0 0;
+  cursor: ne-resize;
+  left: 200px;
+  margin: -2px 0 0 -20px;
+  top: 100px;
+}
+.tox .tox-croprect-handle-sw {
+  border-width: 0 0 2px 2px;
+  cursor: sw-resize;
+  left: 100px;
+  margin: -20px 2px 0 -2px;
+  top: 200px;
+}
+.tox .tox-croprect-handle-se {
+  border-width: 0 2px 2px 0;
+  cursor: se-resize;
+  left: 200px;
+  margin: -20px 0 0 -20px;
+  top: 200px;
+}
+.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-slider:not(:first-of-type) {
+  margin-left: 5px;
+}
+.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-button + .tox-slider {
+  margin-left: 20px;
+}
+.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-slider + .tox-button {
+  margin-left: 20px;
+}
+.tox[dir=rtl] .tox-image-tools__toolbar > .tox-slider:not(:first-of-type) {
+  margin-right: 5px;
+}
+.tox[dir=rtl] .tox-image-tools__toolbar > .tox-button + .tox-slider {
+  margin-right: 20px;
+}
+.tox[dir=rtl] .tox-image-tools__toolbar > .tox-slider + .tox-button {
+  margin-right: 20px;
+}
+.tox .tox-insert-table-picker {
+  display: flex;
+  flex-wrap: wrap;
+  width: 110px;
+}
+.tox .tox-insert-table-picker > div {
+  border-color: #d9d9d9;
+  border-style: solid;
+  border-width: 0 1px 1px 0;
+  box-sizing: border-box;
+  height: 11px;
+  width: 11px;
+}
+.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker {
+  margin: -2.5px 0;
+}
+.tox .tox-insert-table-picker .tox-insert-table-picker__selected {
+  background-color: rgba(10, 143, 233, 0.5);
+  border-color: rgba(10, 143, 233, 0.5);
+}
+.tox .tox-insert-table-picker__label {
+  color: rgba(84, 111, 94, 0.7);
+  display: block;
+  font-size: 8.75px;
+  padding: 2.5px;
+  text-align: center;
+  width: 100%;
+}
+.tox:not([dir=rtl]) {
+  /* stylelint-disable-next-line no-descending-specificity */
+}
+.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) {
+  border-right: 0;
+}
+.tox[dir=rtl] {
+  /* stylelint-disable-next-line no-descending-specificity */
+}
+.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) {
+  border-right: 0;
+}
+.tox {
+  /* stylelint-disable */
+  /* stylelint-enable */
+}
+.tox .tox-menu {
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  box-shadow: 0 4px 8px 0 rgba(84, 111, 94, 0.1);
+  display: inline-block;
+  overflow: hidden;
+  vertical-align: top;
+  z-index: 1150;
+}
+.tox .tox-menu.tox-collection.tox-collection--list {
+  padding: 0;
+}
+.tox .tox-menu.tox-collection.tox-collection--toolbar {
+  padding: 2.5px;
+}
+.tox .tox-menu.tox-collection.tox-collection--grid {
+  padding: 2.5px;
+}
+.tox .tox-menu__label h1,
+.tox .tox-menu__label h2,
+.tox .tox-menu__label h3,
+.tox .tox-menu__label h4,
+.tox .tox-menu__label h5,
+.tox .tox-menu__label h6,
+.tox .tox-menu__label p,
+.tox .tox-menu__label blockquote,
+.tox .tox-menu__label code {
+  margin: 0;
+}
+.tox .tox-menubar {
+  background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23d9d9d9'/%3E%3C/svg%3E") left 0 top 0 #fff;
+  background-color: #fff;
+  display: flex;
+  flex: 0 0 auto;
+  flex-shrink: 0;
+  flex-wrap: wrap;
+  padding: 0 4px 0 4px;
+}
+.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
+  border-top: 1px solid #d9d9d9;
+}
+/* Deprecated. Remove in next major release */
+.tox .tox-mbtn {
+  align-items: center;
+  background: transparent;
+  border: 0;
+  border-radius: 3px;
+  box-shadow: none;
+  color: #817f7c;
+  display: flex;
+  flex: 0 0 auto;
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  height: 34px;
+  justify-content: center;
+  margin: 2px 0 3px 0;
+  outline: none;
+  overflow: hidden;
+  padding: 0 4px;
+  text-transform: none;
+  width: auto;
+}
+.tox .tox-mbtn[disabled] {
+  background-color: transparent;
+  border: 0;
+  box-shadow: none;
+  color: rgba(129, 127, 124, 0.5);
+  cursor: not-allowed;
+}
+.tox .tox-mbtn:focus:not(:disabled) {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+  color: #0a9fe5;
+}
+.tox .tox-mbtn--active {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+  color: rgba(41, 159, 250, 0.88);
+}
+.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+  color: #0a9fe5;
+}
+.tox .tox-mbtn__select-label {
+  cursor: default;
+  font-weight: normal;
+  margin: 0 4px;
+}
+.tox .tox-mbtn[disabled] .tox-mbtn__select-label {
+  cursor: not-allowed;
+}
+.tox .tox-mbtn__select-chevron {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  width: 16px;
+  display: none;
+}
+.tox .tox-notification {
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  box-shadow: none;
+  box-sizing: border-box;
+  display: -ms-grid;
+  display: grid;
+  font-size: 8.75px;
+  font-weight: normal;
+  -ms-grid-columns: minmax(40px, 1fr) auto minmax(40px, 1fr);
+  grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr);
+  margin-top: 2.5px;
+  opacity: 0;
+  padding: 2.5px;
+  transition: transform 100ms ease-in, opacity 150ms ease-in;
+}
+.tox .tox-notification p {
+  font-size: 8.75px;
+  font-weight: normal;
+}
+.tox .tox-notification a {
+  cursor: pointer;
+  text-decoration: underline;
+}
+.tox .tox-notification--in {
+  opacity: 1;
+}
+.tox .tox-notification--success {
+  background-color: #e4eeda;
+  border-color: #d7e6c8;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--success p {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--success a {
+  color: #547831;
+}
+.tox .tox-notification--success svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--error {
+  background-color: #f8dede;
+  border-color: #f2bfbf;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--error p {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--error a {
+  color: #c00;
+}
+.tox .tox-notification--error svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--warn,
+.tox .tox-notification--warning {
+  background-color: #fffaea;
+  border-color: #ffe89d;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--warn p,
+.tox .tox-notification--warning p {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--warn a,
+.tox .tox-notification--warning a {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--warn svg,
+.tox .tox-notification--warning svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--info {
+  background-color: #d9edf7;
+  border-color: #779ecb;
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--info p {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--info a {
+  color: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification--info svg {
+  fill: rgba(84, 111, 94, 0.85);
+}
+.tox .tox-notification__body {
+  -ms-grid-row-align: center;
+      align-self: center;
+  color: rgba(84, 111, 94, 0.85);
+  font-size: 14px;
+  -ms-grid-column-span: 1;
+  grid-column-end: 3;
+  -ms-grid-column: 2;
+      grid-column-start: 2;
+  -ms-grid-row-span: 1;
+  grid-row-end: 2;
+  -ms-grid-row: 1;
+      grid-row-start: 1;
+  text-align: center;
+  white-space: normal;
+  word-break: break-all;
+  word-break: break-word;
+}
+.tox .tox-notification__body > * {
+  margin: 0;
+}
+.tox .tox-notification__body > * + * {
+  margin-top: 1rem;
+}
+.tox .tox-notification__icon {
+  -ms-grid-row-align: center;
+      align-self: center;
+  -ms-grid-column-span: 1;
+  grid-column-end: 2;
+  -ms-grid-column: 1;
+      grid-column-start: 1;
+  -ms-grid-row-span: 1;
+  grid-row-end: 2;
+  -ms-grid-row: 1;
+      grid-row-start: 1;
+  -ms-grid-column-align: end;
+      justify-self: end;
+}
+.tox .tox-notification__icon svg {
+  display: block;
+}
+.tox .tox-notification__dismiss {
+  -ms-grid-row-align: start;
+      align-self: start;
+  -ms-grid-column-span: 1;
+  grid-column-end: 4;
+  -ms-grid-column: 3;
+      grid-column-start: 3;
+  -ms-grid-row-span: 1;
+  grid-row-end: 2;
+  -ms-grid-row: 1;
+      grid-row-start: 1;
+  -ms-grid-column-align: end;
+      justify-self: end;
+}
+.tox .tox-notification .tox-progress-bar {
+  -ms-grid-column-span: 3;
+  grid-column-end: 4;
+  -ms-grid-column: 1;
+      grid-column-start: 1;
+  -ms-grid-row-span: 1;
+  grid-row-end: 3;
+  -ms-grid-row: 2;
+      grid-row-start: 2;
+  -ms-grid-column-align: center;
+      justify-self: center;
+}
+.tox .tox-pop {
+  display: inline-block;
+  position: relative;
+}
+.tox .tox-pop--resizing {
+  transition: width 0.1s ease;
+}
+.tox .tox-pop--resizing .tox-toolbar,
+.tox .tox-pop--resizing .tox-toolbar__group {
+  flex-wrap: nowrap;
+}
+.tox .tox-pop--transition {
+  transition: 0.15s ease;
+  transition-property: left, right, top, bottom;
+}
+.tox .tox-pop--transition::before,
+.tox .tox-pop--transition::after {
+  transition: all 0.15s, visibility 0s, opacity 0.075s ease 0.075s;
+}
+.tox .tox-pop__dialog {
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
+  min-width: 0;
+  overflow: hidden;
+}
+.tox .tox-pop__dialog > *:not(.tox-toolbar) {
+  margin: 2.5px 2.5px 2.5px 5px;
+}
+.tox .tox-pop__dialog .tox-toolbar {
+  background-color: transparent;
+  margin-bottom: -1px;
+}
+.tox .tox-pop::before,
+.tox .tox-pop::after {
+  border-style: solid;
+  content: '';
+  display: block;
+  height: 0;
+  opacity: 1;
+  position: absolute;
+  width: 0;
+}
+.tox .tox-pop.tox-pop--inset::before,
+.tox .tox-pop.tox-pop--inset::after {
+  opacity: 0;
+  transition: all 0s 0.15s, visibility 0s, opacity 0.075s ease;
+}
+.tox .tox-pop.tox-pop--bottom::before,
+.tox .tox-pop.tox-pop--bottom::after {
+  left: 50%;
+  top: 100%;
+}
+.tox .tox-pop.tox-pop--bottom::after {
+  border-color: #fff transparent transparent transparent;
+  border-width: 8px;
+  margin-left: -8px;
+  margin-top: -1px;
+}
+.tox .tox-pop.tox-pop--bottom::before {
+  border-color: #d9d9d9 transparent transparent transparent;
+  border-width: 9px;
+  margin-left: -9px;
+}
+.tox .tox-pop.tox-pop--top::before,
+.tox .tox-pop.tox-pop--top::after {
+  left: 50%;
+  top: 0;
+  transform: translateY(-100%);
+}
+.tox .tox-pop.tox-pop--top::after {
+  border-color: transparent transparent #fff transparent;
+  border-width: 8px;
+  margin-left: -8px;
+  margin-top: 1px;
+}
+.tox .tox-pop.tox-pop--top::before {
+  border-color: transparent transparent #d9d9d9 transparent;
+  border-width: 9px;
+  margin-left: -9px;
+}
+.tox .tox-pop.tox-pop--left::before,
+.tox .tox-pop.tox-pop--left::after {
+  left: 0;
+  top: calc(50% - 1px);
+  transform: translateY(-50%);
+}
+.tox .tox-pop.tox-pop--left::after {
+  border-color: transparent #fff transparent transparent;
+  border-width: 8px;
+  margin-left: -15px;
+}
+.tox .tox-pop.tox-pop--left::before {
+  border-color: transparent #d9d9d9 transparent transparent;
+  border-width: 10px;
+  margin-left: -19px;
+}
+.tox .tox-pop.tox-pop--right::before,
+.tox .tox-pop.tox-pop--right::after {
+  left: 100%;
+  top: calc(50% + 1px);
+  transform: translateY(-50%);
+}
+.tox .tox-pop.tox-pop--right::after {
+  border-color: transparent transparent transparent #fff;
+  border-width: 8px;
+  margin-left: -1px;
+}
+.tox .tox-pop.tox-pop--right::before {
+  border-color: transparent transparent transparent #d9d9d9;
+  border-width: 10px;
+  margin-left: -1px;
+}
+.tox .tox-pop.tox-pop--align-left::before,
+.tox .tox-pop.tox-pop--align-left::after {
+  left: 20px;
+}
+.tox .tox-pop.tox-pop--align-right::before,
+.tox .tox-pop.tox-pop--align-right::after {
+  left: calc(100% - 20px);
+}
+.tox .tox-sidebar-wrap {
+  display: flex;
+  flex-direction: row;
+  flex-grow: 1;
+  -ms-flex-preferred-size: 0;
+  min-height: 0;
+}
+.tox .tox-sidebar {
+  background-color: #fff;
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-end;
+}
+.tox .tox-sidebar__slider {
+  display: flex;
+  overflow: hidden;
+}
+.tox .tox-sidebar__pane-container {
+  display: flex;
+}
+.tox .tox-sidebar__pane {
+  display: flex;
+}
+.tox .tox-sidebar--sliding-closed {
+  opacity: 0;
+}
+.tox .tox-sidebar--sliding-open {
+  opacity: 1;
+}
+.tox .tox-sidebar--sliding-growing,
+.tox .tox-sidebar--sliding-shrinking {
+  transition: width 0.5s ease, opacity 0.5s ease;
+}
+.tox .tox-selector {
+  background-color: #4099ff;
+  border-color: #4099ff;
+  border-style: solid;
+  border-width: 1px;
+  box-sizing: border-box;
+  display: inline-block;
+  height: 10px;
+  position: absolute;
+  width: 10px;
+}
+.tox.tox-platform-touch .tox-selector {
+  height: 12px;
+  width: 12px;
+}
+.tox .tox-slider {
+  align-items: center;
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+  height: 24px;
+  justify-content: center;
+  position: relative;
+}
+.tox .tox-slider__rail {
+  background-color: transparent;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  height: 10px;
+  min-width: 120px;
+  width: 100%;
+}
+.tox .tox-slider__handle {
+  background-color: #0a8fe9;
+  border: 2px solid #0871b8;
+  border-radius: 3px;
+  box-shadow: none;
+  height: 24px;
+  left: 50%;
+  position: absolute;
+  top: 50%;
+  transform: translateX(-50%) translateY(-50%);
+  width: 14px;
+}
+.tox .tox-source-code {
+  overflow: auto;
+}
+.tox .tox-spinner {
+  display: flex;
+}
+.tox .tox-spinner > div {
+  animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both;
+  background-color: rgba(84, 111, 94, 0.7);
+  border-radius: 100%;
+  height: 5px;
+  width: 5px;
+}
+.tox .tox-spinner > div:nth-child(1) {
+  animation-delay: -0.32s;
+}
+.tox .tox-spinner > div:nth-child(2) {
+  animation-delay: -0.16s;
+}
+@keyframes tam-bouncing-dots {
+  0%,
+  80%,
+  100% {
+    transform: scale(0);
+  }
+  40% {
+    transform: scale(1);
+  }
+}
+.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) {
+  margin-left: 2.5px;
+}
+.tox[dir=rtl] .tox-spinner > div:not(:first-child) {
+  margin-right: 2.5px;
+}
+.tox .tox-statusbar {
+  align-items: center;
+  background-color: #fff;
+  border-top: 1px solid #d9d9d9;
+  color: rgba(84, 111, 94, 0.7);
+  display: flex;
+  flex: 0 0 auto;
+  font-size: 12px;
+  font-weight: normal;
+  height: 18px;
+  overflow: hidden;
+  padding: 0 5px;
+  position: relative;
+  text-transform: uppercase;
+}
+.tox .tox-statusbar__text-container {
+  display: flex;
+  flex: 1 1 auto;
+  justify-content: flex-end;
+  overflow: hidden;
+}
+.tox .tox-statusbar__path {
+  display: flex;
+  flex: 1 1 auto;
+  margin-right: auto;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.tox .tox-statusbar__path > * {
+  display: inline;
+  white-space: nowrap;
+}
+.tox .tox-statusbar__wordcount {
+  flex: 0 0 auto;
+  margin-left: 1ch;
+}
+.tox .tox-statusbar a,
+.tox .tox-statusbar__path-item,
+.tox .tox-statusbar__wordcount {
+  color: rgba(84, 111, 94, 0.7);
+  text-decoration: none;
+}
+.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),
+.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),
+.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]),
+.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),
+.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),
+.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) {
+  cursor: pointer;
+  text-decoration: underline;
+}
+.tox .tox-statusbar__resize-handle {
+  align-items: flex-end;
+  align-self: stretch;
+  cursor: nwse-resize;
+  display: flex;
+  flex: 0 0 auto;
+  justify-content: flex-end;
+  margin-left: auto;
+  margin-right: -5px;
+  padding-left: 1ch;
+}
+.tox .tox-statusbar__resize-handle svg {
+  display: block;
+  fill: rgba(84, 111, 94, 0.7);
+}
+.tox .tox-statusbar__resize-handle:focus svg {
+  background-color: #e5e9e7;
+  border-radius: 1px;
+  box-shadow: 0 0 0 2px #e5e9e7;
+}
+.tox:not([dir=rtl]) .tox-statusbar__path > * {
+  margin-right: 2.5px;
+}
+.tox:not([dir=rtl]) .tox-statusbar__branding {
+  margin-left: 1ch;
+}
+.tox[dir=rtl] .tox-statusbar {
+  flex-direction: row-reverse;
+}
+.tox[dir=rtl] .tox-statusbar__path > * {
+  margin-left: 2.5px;
+}
+.tox .tox-throbber {
+  z-index: 1299;
+}
+.tox .tox-throbber__busy-spinner {
+  align-items: center;
+  background-color: rgba(255, 255, 255, 0.6);
+  bottom: 0;
+  display: flex;
+  justify-content: center;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+}
+.tox .tox-tbtn {
+  align-items: center;
+  background: transparent;
+  border: 0;
+  border-radius: 3px;
+  box-shadow: none;
+  color: #817f7c;
+  display: flex;
+  flex: 0 0 auto;
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  height: 34px;
+  justify-content: center;
+  margin: 2px 0 3px 0;
+  outline: none;
+  overflow: hidden;
+  padding: 0;
+  text-transform: none;
+  width: 34px;
+}
+.tox .tox-tbtn svg {
+  display: block;
+  fill: #817f7c;
+}
+.tox .tox-tbtn.tox-tbtn-more {
+  padding-left: 5px;
+  padding-right: 5px;
+  width: inherit;
+}
+.tox .tox-tbtn:focus {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+}
+.tox .tox-tbtn:hover {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+  color: #0a9fe5;
+}
+.tox .tox-tbtn:hover svg {
+  fill: #0a9fe5;
+}
+.tox .tox-tbtn:active {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+  color: rgba(41, 159, 250, 0.88);
+}
+.tox .tox-tbtn:active svg {
+  fill: rgba(41, 159, 250, 0.88);
+}
+.tox .tox-tbtn--disabled,
+.tox .tox-tbtn--disabled:hover,
+.tox .tox-tbtn:disabled,
+.tox .tox-tbtn:disabled:hover {
+  background: transparent;
+  border: 0;
+  box-shadow: none;
+  color: rgba(129, 127, 124, 0.5);
+  cursor: not-allowed;
+}
+.tox .tox-tbtn--disabled svg,
+.tox .tox-tbtn--disabled:hover svg,
+.tox .tox-tbtn:disabled svg,
+.tox .tox-tbtn:disabled:hover svg {
+  /* stylelint-disable-line no-descending-specificity */
+  fill: rgba(129, 127, 124, 0.5);
+}
+.tox .tox-tbtn--enabled,
+.tox .tox-tbtn--enabled:hover {
+  background: #e5e9e7;
+  border: 0;
+  box-shadow: none;
+  color: rgba(41, 159, 250, 0.88);
+}
+.tox .tox-tbtn--enabled > *,
+.tox .tox-tbtn--enabled:hover > * {
+  transform: none;
+}
+.tox .tox-tbtn--enabled svg,
+.tox .tox-tbtn--enabled:hover svg {
+  /* stylelint-disable-line no-descending-specificity */
+  fill: rgba(41, 159, 250, 0.88);
+}
+.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) {
+  color: #ee930e;
+}
+.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg {
+  fill: #ee930e;
+}
+.tox .tox-tbtn:active > * {
+  transform: none;
+}
+.tox .tox-tbtn--md {
+  height: 51px;
+  width: 51px;
+}
+.tox .tox-tbtn--lg {
+  flex-direction: column;
+  height: 68px;
+  width: 68px;
+}
+.tox .tox-tbtn--return {
+  -ms-grid-row-align: stretch;
+      align-self: stretch;
+  height: unset;
+  width: 16px;
+}
+.tox .tox-tbtn--labeled {
+  padding: 0 4px;
+  width: unset;
+}
+.tox .tox-tbtn__vlabel {
+  display: block;
+  font-size: 10px;
+  font-weight: normal;
+  letter-spacing: -0.025em;
+  margin-bottom: 2.5px;
+  white-space: nowrap;
+}
+.tox .tox-tbtn--select {
+  margin: 2px 0 3px 0;
+  padding: 0 4px;
+  width: auto;
+}
+.tox .tox-tbtn__select-label {
+  cursor: default;
+  font-weight: normal;
+  margin: 0 4px;
+}
+.tox .tox-tbtn__select-chevron {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  width: 10px;
+}
+.tox .tox-tbtn__select-chevron svg {
+  fill: rgba(129, 127, 124, 0.5);
+}
+.tox .tox-tbtn--bespoke .tox-tbtn__select-label {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: 7em;
+}
+.tox .tox-split-button {
+  border: 0;
+  border-radius: 3px;
+  box-sizing: border-box;
+  display: flex;
+  margin: 2px 0 3px 0;
+  overflow: hidden;
+}
+.tox .tox-split-button:hover {
+  box-shadow: 0 0 0 1px #e5e9e7 inset;
+}
+.tox .tox-split-button:focus {
+  background: #e5e9e7;
+  box-shadow: none;
+  color: #ee930e;
+}
+.tox .tox-split-button > * {
+  border-radius: 0;
+}
+.tox .tox-split-button__chevron {
+  width: 10px;
+}
+.tox .tox-split-button__chevron svg {
+  fill: rgba(129, 127, 124, 0.5);
+}
+.tox .tox-split-button .tox-tbtn {
+  margin: 0;
+}
+.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child {
+  width: 30px;
+}
+.tox.tox-platform-touch .tox-split-button__chevron {
+  width: 14px;
+}
+.tox .tox-split-button.tox-tbtn--disabled:hover,
+.tox .tox-split-button.tox-tbtn--disabled:focus,
+.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,
+.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus {
+  background: transparent;
+  box-shadow: none;
+  color: rgba(129, 127, 124, 0.5);
+}
+.tox .tox-toolbar-overlord {
+  background-color: #fff;
+}
+.tox .tox-toolbar,
+.tox .tox-toolbar__primary,
+.tox .tox-toolbar__overflow {
+  background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23d9d9d9'/%3E%3C/svg%3E") left 0 top 0 #fff;
+  background-color: #fff;
+  display: flex;
+  flex: 0 0 auto;
+  flex-shrink: 0;
+  flex-wrap: wrap;
+  padding: 0 0;
+}
+.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed {
+  height: 0;
+  opacity: 0;
+  padding-bottom: 0;
+  padding-top: 0;
+  visibility: hidden;
+}
+.tox .tox-toolbar__overflow--growing {
+  transition: height 0.3s ease, opacity 0.2s linear 0.1s;
+}
+.tox .tox-toolbar__overflow--shrinking {
+  transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;
+}
+.tox .tox-menubar + .tox-toolbar,
+.tox .tox-menubar + .tox-toolbar-overlord .tox-toolbar__primary {
+  border-top: 1px solid #d9d9d9;
+  margin-top: -1px;
+}
+.tox .tox-toolbar--scrolling {
+  flex-wrap: nowrap;
+  overflow-x: auto;
+}
+.tox .tox-pop .tox-toolbar {
+  border-width: 0;
+}
+.tox .tox-toolbar--no-divider {
+  background-image: none;
+}
+.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child,
+.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary {
+  border-top: 1px solid #d9d9d9;
+}
+.tox.tox-tinymce-aux .tox-toolbar__overflow {
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
+}
+.tox .tox-toolbar__group {
+  align-items: center;
+  display: flex;
+  flex-wrap: wrap;
+  margin: 0 0;
+  padding: 0 4px 0 4px;
+}
+.tox .tox-toolbar__group--pull-right {
+  margin-left: auto;
+}
+.tox .tox-toolbar--scrolling .tox-toolbar__group {
+  flex-shrink: 0;
+  flex-wrap: nowrap;
+}
+.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) {
+  border-right: 1px solid #d9d9d9;
+}
+.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) {
+  border-left: 1px solid #d9d9d9;
+}
+.tox .tox-tooltip {
+  display: inline-block;
+  padding: 5px;
+  position: relative;
+}
+.tox .tox-tooltip__body {
+  background-color: rgba(84, 111, 94, 0.85);
+  border-radius: 3px;
+  box-shadow: 0 2px 4px rgba(84, 111, 94, 0.3);
+  color: rgba(255, 255, 255, 0.75);
+  font-size: 8.75px;
+  font-style: normal;
+  font-weight: normal;
+  padding: 2.5px 5px;
+  text-transform: none;
+}
+.tox .tox-tooltip__arrow {
+  position: absolute;
+}
+.tox .tox-tooltip--down .tox-tooltip__arrow {
+  border-left: 5px solid transparent;
+  border-right: 5px solid transparent;
+  border-top: 5px solid rgba(84, 111, 94, 0.85);
+  bottom: 0;
+  left: 50%;
+  position: absolute;
+  transform: translateX(-50%);
+}
+.tox .tox-tooltip--up .tox-tooltip__arrow {
+  border-bottom: 5px solid rgba(84, 111, 94, 0.85);
+  border-left: 5px solid transparent;
+  border-right: 5px solid transparent;
+  left: 50%;
+  position: absolute;
+  top: 0;
+  transform: translateX(-50%);
+}
+.tox .tox-tooltip--right .tox-tooltip__arrow {
+  border-bottom: 5px solid transparent;
+  border-left: 5px solid rgba(84, 111, 94, 0.85);
+  border-top: 5px solid transparent;
+  position: absolute;
+  right: 0;
+  top: 50%;
+  transform: translateY(-50%);
+}
+.tox .tox-tooltip--left .tox-tooltip__arrow {
+  border-bottom: 5px solid transparent;
+  border-right: 5px solid rgba(84, 111, 94, 0.85);
+  border-top: 5px solid transparent;
+  left: 0;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+}
+.tox .tox-well {
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  padding: 5px;
+  width: 100%;
+}
+.tox .tox-well > *:first-child {
+  margin-top: 0;
+}
+.tox .tox-well > *:last-child {
+  margin-bottom: 0;
+}
+.tox .tox-well > *:only-child {
+  margin: 0;
+}
+.tox .tox-custom-editor {
+  border: 1px solid #d9d9d9;
+  border-radius: 3px;
+  display: flex;
+  flex: 1;
+  position: relative;
+}
+/* stylelint-disable */
+.tox {
+  /* stylelint-enable */
+}
+.tox .tox-dialog-loading::before {
+  background-color: rgba(0, 0, 0, 0.5);
+  content: "";
+  height: 100%;
+  position: absolute;
+  width: 100%;
+  z-index: 1000;
+}
+.tox .tox-tab {
+  cursor: pointer;
+}
+.tox .tox-dialog__content-js {
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-dialog__body-content .tox-collection {
+  display: flex;
+  flex: 1;
+  -ms-flex-preferred-size: auto;
+}
+.tox .tox-image-tools-edit-panel {
+  height: 60px;
+}
+.tox .tox-image-tools__sidebar {
+  height: 60px;
+}

File diff suppressed because it is too large
+ 6 - 0
public/resource/tinymce/skins/ui/jeecg/skin.min.css


+ 677 - 0
public/resource/tinymce/skins/ui/jeecg/skin.mobile.css

@@ -0,0 +1,677 @@
+/**
+* Copyright (c) Tiny Technologies, Inc. All rights reserved.
+* Licensed under the LGPL or a commercial license.
+* For LGPL see License.txt in the project root for license information.
+* For commercial licenses see https://www.tiny.cloud/
+*/
+/* RESET all the things! */
+.tinymce-mobile-outer-container {
+  all: initial;
+  display: block;
+}
+.tinymce-mobile-outer-container * {
+  border: 0;
+  box-sizing: initial;
+  cursor: inherit;
+  float: none;
+  line-height: 1;
+  margin: 0;
+  outline: 0;
+  padding: 0;
+  -webkit-tap-highlight-color: transparent;
+  /* TBIO-3691, stop the gray flicker on touch. */
+  text-shadow: none;
+  white-space: nowrap;
+}
+.tinymce-mobile-icon-arrow-back::before {
+  content: "\e5cd";
+}
+.tinymce-mobile-icon-image::before {
+  content: "\e412";
+}
+.tinymce-mobile-icon-cancel-circle::before {
+  content: "\e5c9";
+}
+.tinymce-mobile-icon-full-dot::before {
+  content: "\e061";
+}
+.tinymce-mobile-icon-align-center::before {
+  content: "\e234";
+}
+.tinymce-mobile-icon-align-left::before {
+  content: "\e236";
+}
+.tinymce-mobile-icon-align-right::before {
+  content: "\e237";
+}
+.tinymce-mobile-icon-bold::before {
+  content: "\e238";
+}
+.tinymce-mobile-icon-italic::before {
+  content: "\e23f";
+}
+.tinymce-mobile-icon-unordered-list::before {
+  content: "\e241";
+}
+.tinymce-mobile-icon-ordered-list::before {
+  content: "\e242";
+}
+.tinymce-mobile-icon-font-size::before {
+  content: "\e245";
+}
+.tinymce-mobile-icon-underline::before {
+  content: "\e249";
+}
+.tinymce-mobile-icon-link::before {
+  content: "\e157";
+}
+.tinymce-mobile-icon-unlink::before {
+  content: "\eca2";
+}
+.tinymce-mobile-icon-color::before {
+  content: "\e891";
+}
+.tinymce-mobile-icon-previous::before {
+  content: "\e314";
+}
+.tinymce-mobile-icon-next::before {
+  content: "\e315";
+}
+.tinymce-mobile-icon-large-font::before,
+.tinymce-mobile-icon-style-formats::before {
+  content: "\e264";
+}
+.tinymce-mobile-icon-undo::before {
+  content: "\e166";
+}
+.tinymce-mobile-icon-redo::before {
+  content: "\e15a";
+}
+.tinymce-mobile-icon-removeformat::before {
+  content: "\e239";
+}
+.tinymce-mobile-icon-small-font::before {
+  content: "\e906";
+}
+.tinymce-mobile-icon-readonly-back::before,
+.tinymce-mobile-format-matches::after {
+  content: "\e5ca";
+}
+.tinymce-mobile-icon-small-heading::before {
+  content: "small";
+}
+.tinymce-mobile-icon-large-heading::before {
+  content: "large";
+}
+.tinymce-mobile-icon-small-heading::before,
+.tinymce-mobile-icon-large-heading::before {
+  font-family: sans-serif;
+  font-size: 80%;
+}
+.tinymce-mobile-mask-edit-icon::before {
+  content: "\e254";
+}
+.tinymce-mobile-icon-back::before {
+  content: "\e5c4";
+}
+.tinymce-mobile-icon-heading::before {
+  /* TODO: Translate */
+  content: "Headings";
+  font-family: sans-serif;
+  font-size: 80%;
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h1::before {
+  content: "H1";
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h2::before {
+  content: "H2";
+  font-weight: bold;
+}
+.tinymce-mobile-icon-h3::before {
+  content: "H3";
+  font-weight: bold;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  background: rgba(51, 51, 51, 0.5);
+  height: 100%;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container {
+  align-items: center;
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  font-family: sans-serif;
+  font-size: 1em;
+  justify-content: space-between;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  border-radius: 50%;
+  height: 2.1em;
+  width: 2.1em;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+  font-size: 1em;
+}
+@media only screen and (min-device-width:700px) {
+  .tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section {
+    font-size: 1.2em;
+  }
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  border-radius: 50%;
+  height: 2.1em;
+  width: 2.1em;
+  background-color: white;
+  color: #207ab7;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before {
+  content: "\e900";
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon {
+  z-index: 2;
+}
+.tinymce-mobile-android-container.tinymce-mobile-android-maximized {
+  background: #ffffff;
+  border: none;
+  bottom: 0;
+  display: flex;
+  flex-direction: column;
+  left: 0;
+  position: fixed;
+  right: 0;
+  top: 0;
+}
+.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized) {
+  position: relative;
+}
+.tinymce-mobile-android-container .tinymce-mobile-editor-socket {
+  display: flex;
+  flex-grow: 1;
+}
+.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe {
+  display: flex !important;
+  flex-grow: 1;
+  height: auto !important;
+}
+.tinymce-mobile-android-scroll-reload {
+  overflow: hidden;
+}
+:not(.tinymce-mobile-readonly-mode) > .tinymce-mobile-android-selection-context-toolbar {
+  margin-top: 23px;
+}
+.tinymce-mobile-toolstrip {
+  background: #fff;
+  display: flex;
+  flex: 0 0 auto;
+  z-index: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar {
+  align-items: center;
+  background-color: #fff;
+  border-bottom: 1px solid #cccccc;
+  display: flex;
+  flex: 1;
+  height: 2.5em;
+  width: 100%;
+  /* Make it no larger than the toolstrip, so that it needs to scroll */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex-shrink: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group > div {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container {
+  background: #f44336;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group {
+  flex-grow: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item {
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button {
+  align-items: center;
+  display: flex;
+  height: 80%;
+  margin-left: 2px;
+  margin-right: 2px;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected {
+  background: #d4dbd7;
+  color: #cccccc;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type {
+  background: #207ab7;
+  color: #eceff1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar {
+  /* Note, this file is imported inside .tinymce-mobile-context-toolbar, so that prefix is on everything here. */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+  padding-bottom: 0.4em;
+  padding-top: 0.4em;
+  /* Make any buttons appearing on the left and right display in the centre (e.g. color edges) */
+  /* For widgets like the colour picker, use the whole height */
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog {
+  display: flex;
+  min-height: 1.5em;
+  overflow: hidden;
+  padding-left: 0;
+  padding-right: 0;
+  position: relative;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain {
+  display: flex;
+  height: 100%;
+  transition: left cubic-bezier(0.4, 0, 1, 1) 0.15s;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen {
+  display: flex;
+  flex: 0 0 auto;
+  justify-content: space-between;
+  width: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input {
+  font-family: Sans-serif;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container {
+  display: flex;
+  flex-grow: 1;
+  position: relative;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x {
+  -ms-grid-row-align: center;
+      align-self: center;
+  background: inherit;
+  border: none;
+  border-radius: 50%;
+  color: #888;
+  font-size: 0.6em;
+  font-weight: bold;
+  height: 100%;
+  padding-right: 2px;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x {
+  display: none;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next {
+  align-items: center;
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before {
+  align-items: center;
+  display: flex;
+  font-weight: bold;
+  height: 100%;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before {
+  visibility: hidden;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item {
+  color: #cccccc;
+  font-size: 10px;
+  line-height: 10px;
+  margin: 0 2px;
+  padding-top: 3px;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active {
+  color: #d4dbd7;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before {
+  margin-left: 0.5em;
+  margin-right: 0.9em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before {
+  margin-left: 0.9em;
+  margin-right: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider {
+  display: flex;
+  flex: 1;
+  margin-left: 0;
+  margin-right: 0;
+  padding: 0.28em 0;
+  position: relative;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container {
+  align-items: center;
+  display: flex;
+  flex-grow: 1;
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line {
+  background: #cccccc;
+  display: flex;
+  flex: 1;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container {
+  padding-left: 2em;
+  padding-right: 2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container {
+  align-items: center;
+  display: flex;
+  flex-grow: 1;
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient {
+  background: linear-gradient(to right, hsl(0, 100%, 50%) 0%, hsl(60, 100%, 50%) 17%, hsl(120, 100%, 50%) 33%, hsl(180, 100%, 50%) 50%, hsl(240, 100%, 50%) 67%, hsl(300, 100%, 50%) 83%, hsl(0, 100%, 50%) 100%);
+  display: flex;
+  flex: 1;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black {
+  /* Not part of theming */
+  background: black;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+  width: 1.2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white {
+  /* Not part of theming */
+  background: white;
+  height: 0.2em;
+  margin-bottom: 0.3em;
+  margin-top: 0.3em;
+  width: 1.2em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb {
+  /* vertically centering trick (margin: auto, top: 0, bottom: 0). On iOS and Safari, if you leave
+     * out these values, then it shows the thumb at the top of the spectrum. This is probably because it is
+     * absolutely positioned with only a left value, and not a top. Note, on Chrome it seems to be fine without
+     * this approach.
+    */
+  align-items: center;
+  background-clip: padding-box;
+  background-color: #455a64;
+  border: 0.5em solid rgba(136, 136, 136, 0);
+  border-radius: 3em;
+  bottom: 0;
+  color: #fff;
+  display: flex;
+  height: 0.5em;
+  justify-content: center;
+  left: -10px;
+  margin: auto;
+  position: absolute;
+  top: 0;
+  transition: border 120ms cubic-bezier(0.39, 0.58, 0.57, 1);
+  width: 0.5em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active {
+  border: 0.5em solid rgba(136, 136, 136, 0.39);
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group > div {
+  align-items: center;
+  display: flex;
+  height: 100%;
+  flex: 1;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper {
+  flex-direction: column;
+  justify-content: center;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item {
+  align-items: center;
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog) {
+  height: 100%;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container {
+  display: flex;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input {
+  background: #ffffff;
+  border: none;
+  border-radius: 0;
+  color: #455a64;
+  flex-grow: 1;
+  font-size: 0.85em;
+  padding-bottom: 0.1em;
+  padding-left: 5px;
+  padding-top: 0.1em;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input:-ms-input-placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder {
+  /* WebKit, Blink, Edge */
+  color: #888;
+}
+/* dropup */
+.tinymce-mobile-dropup {
+  background: white;
+  display: flex;
+  overflow: hidden;
+  width: 100%;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking {
+  transition: height 0.3s ease-out;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-growing {
+  transition: height 0.3s ease-in;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-closed {
+  flex-grow: 0;
+}
+.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing) {
+  flex-grow: 1;
+}
+/* TODO min-height for device size and orientation */
+.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+  min-height: 200px;
+}
+@media only screen and (orientation: landscape) {
+  .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+    min-height: 200px;
+  }
+}
+@media only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) {
+  .tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) {
+    min-height: 150px;
+  }
+}
+/* styles menu */
+.tinymce-mobile-styles-menu {
+  font-family: sans-serif;
+  outline: 4px solid black;
+  overflow: hidden;
+  position: relative;
+  width: 100%;
+}
+.tinymce-mobile-styles-menu [role="menu"] {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  position: absolute;
+  width: 100%;
+}
+.tinymce-mobile-styles-menu [role="menu"].transitioning {
+  transition: transform 0.5s ease-in-out;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item {
+  border-bottom: 1px solid #ddd;
+  color: #455a64;
+  cursor: pointer;
+  display: flex;
+  padding: 1em 1em;
+  position: relative;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before {
+  color: #455a64;
+  content: "\e314";
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after {
+  color: #455a64;
+  content: "\e315";
+  font-family: 'tinymce-mobile', sans-serif;
+  padding-left: 1em;
+  padding-right: 1em;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after {
+  font-family: 'tinymce-mobile', sans-serif;
+  padding-left: 1em;
+  padding-right: 1em;
+  position: absolute;
+  right: 0;
+}
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator,
+.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser {
+  align-items: center;
+  background: #fff;
+  border-top: #455a64;
+  color: #455a64;
+  display: flex;
+  min-height: 2.5em;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="before"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="before"] {
+  transform: translate(-100%);
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="current"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="current"] {
+  transform: translate(0%);
+}
+.tinymce-mobile-styles-menu [data-transitioning-destination="after"][data-transitioning-state],
+.tinymce-mobile-styles-menu [data-transitioning-state="after"] {
+  transform: translate(100%);
+}
+@font-face {
+  font-family: 'tinymce-mobile';
+  font-style: normal;
+  font-weight: normal;
+  src: url('fonts/tinymce-mobile.woff?8x92w3') format('woff');
+}
+@media (min-device-width: 700px) {
+  .tinymce-mobile-outer-container,
+  .tinymce-mobile-outer-container input {
+    font-size: 25px;
+  }
+}
+@media (max-device-width: 700px) {
+  .tinymce-mobile-outer-container,
+  .tinymce-mobile-outer-container input {
+    font-size: 18px;
+  }
+}
+.tinymce-mobile-icon {
+  font-family: 'tinymce-mobile', sans-serif;
+}
+.mixin-flex-and-centre {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+.mixin-flex-bar {
+  align-items: center;
+  display: flex;
+  height: 100%;
+}
+.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe {
+  background-color: #fff;
+  width: 100%;
+}
+.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+  /* Note, on the iPod touch in landscape, this isn't visible when the navbar appears */
+  background-color: #207ab7;
+  border-radius: 50%;
+  bottom: 1em;
+  color: white;
+  font-size: 1em;
+  height: 2.1em;
+  position: fixed;
+  right: 2em;
+  width: 2.1em;
+  align-items: center;
+  display: flex;
+  justify-content: center;
+}
+@media only screen and (min-device-width:700px) {
+  .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+    font-size: 1.2em;
+  }
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket {
+  height: 300px;
+  overflow: hidden;
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe {
+  height: 100%;
+}
+.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip {
+  display: none;
+}
+/*
+  Note, that if you don't include this (::-webkit-file-upload-button), the toolbar width gets
+  increased and the whole body becomes scrollable. It's important!
+ */
+input[type="file"]::-webkit-file-upload-button {
+  display: none;
+}
+@media only screen and (min-device-width : 320px) and (max-device-width : 568px) and (orientation : landscape) {
+  .tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon {
+    bottom: 50%;
+  }
+}

File diff suppressed because it is too large
+ 6 - 0
public/resource/tinymce/skins/ui/jeecg/skin.mobile.min.css


+ 98 - 0
src/api/common/api.ts

@@ -0,0 +1,98 @@
+import { defHttp } from '/@/utils/http/axios';
+import { useGlobSetting } from '/@/hooks/setting';
+const globSetting = useGlobSetting();
+const baseUploadUrl = globSetting.uploadUrl;
+enum Api {
+  positionList = '/sys/position/list',
+  userList = '/sys/user/list',
+  roleList = '/sys/role/list',
+  queryDepartTreeSync = '/sys/sysDepart/queryDepartTreeSync',
+  queryTreeList = '/sys/sysDepart/queryTreeList',
+  loadTreeData = '/sys/category/loadTreeData',
+  loadDictItem = '/sys/category/loadDictItem/',
+  getDictItems = '/sys/dict/getDictItems/',
+  getTableList = '/sys/user/queryUserComponentData',
+  getCategoryData = '/sys/category/loadAllData',
+}
+
+/**
+ * 上传父路径
+ */
+export const uploadUrl=`${baseUploadUrl}/sys/common/upload`;
+
+/**
+ * 职务列表
+ * @param params
+ */
+export const getPositionList = (params) => {
+  return defHttp.get({ url: Api.positionList, params });
+};
+
+/**
+ * 用户列表
+ * @param params
+ */
+export const getUserList = (params) => {
+  return defHttp.get({ url: Api.userList, params });
+};
+
+/**
+ * 角色列表
+ * @param params
+ */
+export const getRoleList = (params) => {
+  return defHttp.get({ url: Api.roleList, params });
+};
+
+/**
+ * 异步获取部门树列表
+ */
+export const queryDepartTreeSync = (params?) =>{
+  return defHttp.get({ url: Api.queryDepartTreeSync, params });
+}
+/**
+ * 获取部门树列表
+ */
+export const queryTreeList = (params?) =>{
+  return defHttp.get({ url: Api.queryTreeList, params });
+}
+
+/**
+ * 分类字典树控件 加载节点
+ */
+export const loadTreeData = (params?) =>{
+  return defHttp.get({ url: Api.loadTreeData, params });
+}
+
+
+/**
+ * 根据字典code加载字典text
+ */
+export const loadDictItem = (params?) =>{
+  return defHttp.get({ url: Api.loadDictItem, params });
+}
+
+/**
+ * 根据字典code加载字典text
+ */
+export const getDictItems = (dictCode) =>{
+  return defHttp.get({ url: Api.getDictItems+dictCode},{joinTime:false});
+}
+/**
+ * 部门用户modal选择列表加载list
+ */
+export const getTableList = (params)=>{
+    return defHttp.get({url:Api.getTableList,params})
+}
+/**
+ * 加载全部分类字典数据
+ */
+export const loadCategoryData = (params)=>{
+    return defHttp.get({url:Api.getCategoryData,params})
+}
+/**
+ * 文件上传
+ */
+export const uploadFile = (params,success)=>{
+    return defHttp.uploadFile({url:uploadUrl}, params,{success})
+}

+ 5 - 2
src/api/demo/account.ts

@@ -2,8 +2,9 @@ import { defHttp } from '/@/utils/http/axios';
 import { GetAccountInfoModel } from './model/accountModel';
 
 enum Api {
-  ACCOUNT_INFO = '/account/getAccountInfo',
-  SESSION_TIMEOUT = '/user/sessionTimeout',
+  ACCOUNT_INFO = '/mock/account/getAccountInfo',
+  SESSION_TIMEOUT = '/mock/user/sessionTimeout',
+  TOKEN_EXPIRED = '/mock/user/tokenExpired',
 }
 
 // Get personal center-basic settings
@@ -11,3 +12,5 @@ enum Api {
 export const accountInfoApi = () => defHttp.get<GetAccountInfoModel>({ url: Api.ACCOUNT_INFO });
 
 export const sessionTimeoutApi = () => defHttp.post<void>({ url: Api.SESSION_TIMEOUT });
+
+export const tokenExpiredApi = () => defHttp.post<void>({ url: Api.TOKEN_EXPIRED });

+ 1 - 1
src/api/demo/select.ts

@@ -1,7 +1,7 @@
 import { defHttp } from '/@/utils/http/axios';
 import { DemoOptionsItem, selectParams } from './model/optionsModel';
 enum Api {
-  OPTIONS_LIST = '/select/getDemoOptions',
+  OPTIONS_LIST = '/mock/select/getDemoOptions',
 }
 
 /**

+ 10 - 38
src/api/demo/system.ts

@@ -4,28 +4,26 @@ import {
     MenuParams,
     RoleParams,
     TestPageParams,
-    UserPageParams,
     RolePageParams,
     MenuListGetResultModel,
     DeptListGetResultModel,
     AccountListGetResultModel,
     RolePageListGetResultModel,
     RoleListGetResultModel,
-    TestListGetResultModel,
-    UserListGetResultModel,
+    TestListGetResultModel
 } from './model/systemModel';
 import {defHttp} from '/@/utils/http/axios';
 
 enum Api {
-    AccountList = '/system/getAccountList',
-    IsAccountExist = '/system/accountExist',
-    DeptList = '/system/getDeptList',
-    setRoleStatus = '/system/setRoleStatus',
-    MenuList = '/system/getMenuList',
-    RolePageList = '/system/getRoleListByPage',
-    DemoTableList = '/system/getDemoTableListByPage',
-    TestPageList = '/system/getTestListByPage',
-    GetAllRoleList = '/system/getAllRoleList',
+    AccountList = '/mock/system/getAccountList',
+    IsAccountExist = '/mock/system/accountExist',
+    DeptList = '/mock/system/getDeptList',
+    setRoleStatus = '/mock/system/setRoleStatus',
+    MenuList = '/mock/system/getMenuList',
+    RolePageList = '/mock/system/getRoleListByPage',
+    DemoTableList = '/mock/system/getDemoTableListByPage',
+    TestPageList = '/mock/system/getTestListByPage',
+    GetAllRoleList = '/mock/system/getAllRoleList',
 }
 
 export const getAccountList = (params: AccountParams) =>
@@ -54,29 +52,3 @@ export const getDemoTableListByPage = (params) =>
 
 export const isAccountExist = (account: string) =>
     defHttp.post({url: Api.IsAccountExist, params: {account}}, {errorMessageMode: 'none'});
-
-export const isRoleExist = (params) =>
-    defHttp.get({url: Api.isRoleExist, params},{isTransformResponse:false});
-
-export const getUserListByPage = (params?: UserPageParams) =>
-    defHttp.get({url: Api.UserList, params});
-
-export const getRolesListByPage = (params?: RolePageParams) =>
-    defHttp.get<RolePageListGetResultModel>({url: Api.RolesList, params});
-
-export const getAllRolesList = (params?: RoleParams) =>
-    defHttp.get<RoleListGetResultModel>({url: Api.allRolesList, params});
-
-export const getAllTenantList = (params?: RoleParams) =>
-    defHttp.get({url: Api.allTenantList, params});
-
-export const getUserRoles = (params) =>
-    defHttp.get({url: Api.getUserRole, params}, {errorMessageMode: 'none'});
-
-export const getAllPostList = (params) => {
-    return new Promise((resolve, reject) => {
-        defHttp.get({url: Api.allPostList, params}).then(res => {
-            resolve(res.records)
-        });
-    })
-}

+ 1 - 1
src/api/demo/table.ts

@@ -2,7 +2,7 @@ import { defHttp } from '/@/utils/http/axios';
 import { DemoParams, DemoListGetResultModel } from './model/tableModel';
 
 enum Api {
-  DEMO_LIST = '/table/getDemoList',
+  DEMO_LIST = '/mock/table/getDemoList',
 }
 
 /**

+ 1 - 1
src/api/demo/tree.ts

@@ -1,7 +1,7 @@
 import { defHttp } from '/@/utils/http/axios';
 
 enum Api {
-  TREE_OPTIONS_LIST = '/tree/getDemoOptions',
+  TREE_OPTIONS_LIST = '/mock/tree/getDemoOptions',
 }
 
 /**

+ 2 - 2
src/api/sys/menu.ts

@@ -10,13 +10,13 @@ enum Api {
  */
 
 export const getMenuList = () => {
-  return new Promise((resolve, reject) => {
+  return new Promise((resolve) => {
     //为了兼容mock和接口数据
     defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList }).then(res=>{
       if(Array.isArray(res)){
         resolve(res)
       }else{
-        resolve(res.menu)
+        resolve(res['menu'])
       }
     });
   })

+ 10 - 1
src/api/sys/model/userModel.ts

@@ -6,6 +6,11 @@ export interface LoginParams {
   password: string;
 }
 
+export interface ThirdLoginParams {
+  token: string;
+  thirdType: string;
+}
+
 export interface RoleInfo {
   roleName: string;
   value: string;
@@ -30,11 +35,15 @@ export interface GetUserInfoModel {
   // 用户名
   username: string;
   // 真实名字
-  realName: string;
+  realname: string;
   // 头像
   avatar: string;
   // 介绍
   desc?: string;
+  // 用户信息
+  userInfo?: any;
+  // 缓存字典项
+  sysAllDictItems?: any;
 }
 
 /**

+ 15 - 0
src/api/sys/upload.ts

@@ -20,3 +20,18 @@ export function uploadApi(
     params
   );
 }
+/**
+ * @description: Upload interface
+ */
+export function uploadImg(
+  params: UploadFileParams,
+  onUploadProgress: (progressEvent: ProgressEvent) => void
+) {
+  return defHttp.uploadFile<UploadApiResult>(
+    {
+      url: `${uploadUrl}/sys/common/upload`,
+      onUploadProgress,
+    },
+    params, {isReturnResponse:true}
+  );
+}

+ 70 - 3
src/api/sys/user.ts

@@ -11,6 +11,10 @@ enum Api {
   phoneLogin = '/sys/phoneLogin',
   Logout = '/sys/logout',
   GetUserInfo = '/sys/user/getUserInfo',
+  // 获取系统权限
+  // 1、查询用户拥有的按钮/表单访问权限
+  // 2、所有权限
+  // 3、系统安全模式
   GetPermCode = '/sys/permission/getPermCode',
   //新加的获取图形验证码的接口
   getInputCode = '/sys/randomImage',
@@ -20,10 +24,20 @@ enum Api {
   registerApi = '/sys/user/register',
   //校验用户接口
   checkOnlyUser = '/sys/user/checkOnlyUser',
+  //SSO登录校验
+  validateCasLogin = '/sys/cas/client/validateLogin',
   //校验手机号
   phoneVerify = '/sys/user/phoneVerification',
   //修改密码
   passwordChange = '/sys/user/passwordChange',
+  //第三方登录
+  thirdLogin = '/sys/thirdLogin/getLoginUser',
+  //第三方登录
+  getThirdCaptcha = '/sys/thirdSms',
+  //获取二维码信息
+  getLoginQrcode = '/sys/getLoginQrcode',
+  //监控二维码扫描状态
+  getQrcodeToken = '/sys/getQrcodeToken',
 }
 
 /**
@@ -60,11 +74,11 @@ export function phoneLoginApi(params: LoginParams, mode: ErrorMessageMode = 'mod
  * @description: getUserInfo
  */
 export function getUserInfo() {
-  return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo });
+  return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' });
 }
 
 export function getPermCode() {
-  return defHttp.get<string[]>({ url: Api.GetPermCode });
+  return defHttp.get({ url: Api.GetPermCode });
 }
 
 export function doLogout() {
@@ -80,7 +94,7 @@ export function getCodeInfo(currdatetime) {
  */
 export function getCaptcha(params) {
   return new Promise((resolve, reject) => {
-    defHttp.post({url: Api.getCaptcha,params},{isTransformResponse: false}).then(res=>{
+    defHttp.post({url:Api.getCaptcha,params},{isTransformResponse: false}).then(res=>{
       console.log(res)
       if(res.success){
         resolve(true)
@@ -117,3 +131,56 @@ export const phoneVerify = (params) =>
  */
 export const passwordChange = (params) =>
   defHttp.get({url: Api.passwordChange, params},{isTransformResponse:false});
+/**
+ * @description: 第三方登录
+ */
+export function thirdLogin(params, mode: ErrorMessageMode = 'modal') {
+    return defHttp.get<LoginResultModel>(
+        {
+            url: `${Api.thirdLogin}/${params.token}/${params.thirdType}`,
+        },
+        {
+            errorMessageMode: mode,
+        }
+    );
+}
+/**
+ * @description: 获取第三方短信验证码
+ */
+export function setThirdCaptcha(params) {
+    return new Promise((resolve, reject) => {
+        defHttp.post({url:Api.getThirdCaptcha,params},{isTransformResponse: false}).then(res=>{
+            console.log(res)
+            if(res.success){
+                resolve(true)
+            }else{
+                createErrorModal({ title: '错误提示', content: res.message||'未知问题' });
+                reject()
+            }
+        });
+    })
+}
+
+/**
+ * 获取登录二维码信息
+ */
+export function getLoginQrcode() {
+  let url = Api.getLoginQrcode
+  return defHttp.get({ url: url });
+}
+
+/**
+ * 监控扫码状态
+ */
+export function getQrcodeToken(params) {
+  let url = Api.getQrcodeToken
+  return defHttp.get({ url: url,params});
+}
+
+/**
+ * SSO登录校验
+ */
+export async function validateCasLogin(params) {
+  let url = Api.validateCasLogin
+  return defHttp.get({ url: url,params});
+}

BIN
src/assets/images/cms_bpm.png


BIN
src/assets/images/cms_oa.png


BIN
src/assets/images/daiban.png


BIN
src/assets/images/duban.png


BIN
src/assets/images/guaz.png


BIN
src/assets/images/logo.png


BIN
src/assets/images/nodata.png


BIN
src/assets/images/pdf4.jpg


BIN
src/assets/images/zaiban.png


+ 1 - 0
src/components/Application/src/AppLogo.vue

@@ -87,6 +87,7 @@
       font-size: 16px;
       font-weight: 700;
       transition: all 0.5s;
+      line-height: normal;
     }
   }
 </style>

+ 2 - 0
src/components/Button/index.ts

@@ -1,9 +1,11 @@
 import { withInstall } from '/@/utils';
 import type { ExtractPropTypes } from 'vue';
 import button from './src/BasicButton.vue';
+import uploadButton from './src/UploadButton.vue';
 import popConfirmButton from './src/PopConfirmButton.vue';
 import { buttonProps } from './src/props';
 
 export const Button = withInstall(button);
+export const UploadButton = withInstall(uploadButton);
 export const PopConfirmButton = withInstall(popConfirmButton);
 export declare type ButtonProps = Partial<ExtractPropTypes<typeof buttonProps>>;

+ 0 - 1
src/components/Button/src/BasicButton.vue

@@ -21,7 +21,6 @@
     import Icon from '/@/components/Icon/src/Icon.vue';
     import { buttonProps } from './props';
     import { useAttrs } from '/@/hooks/core/useAttrs';
-
     const props = defineProps(buttonProps);
     // get component class
     const attrs = useAttrs({ excludeDefaultKeys: false });

+ 41 - 0
src/components/Button/src/UploadButton.vue

@@ -0,0 +1,41 @@
+<template>
+    <a-upload name="file" :showUploadList="false"  :customRequest="(file)=>onClick(file)">
+      <Button :type="type" :class="getButtonClass" >
+        <template #default="data">
+          <Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
+          <slot v-bind="data || {}"></slot>
+          <Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
+        </template>
+      </Button>
+    </a-upload>
+</template>
+
+<script lang="ts">
+    import { defineComponent } from 'vue';
+    export default defineComponent({
+        name: 'JUploadButton',
+        inheritAttrs: false,
+    });
+</script>
+<script lang="ts" setup>
+    import { computed, unref } from 'vue';
+    import { Button } from 'ant-design-vue';
+    import Icon from '/@/components/Icon/src/Icon.vue';
+    import { buttonProps } from './props';
+    import { useAttrs } from '/@/hooks/core/useAttrs';
+    const props = defineProps(buttonProps);
+    // get component class
+    const attrs = useAttrs({ excludeDefaultKeys: false });
+    const getButtonClass = computed(() => {
+        const { color, disabled } = props;
+        return [
+            {
+                [`ant-btn-${color}`]: !!color,
+                [`is-disabled`]: disabled,
+            },
+        ];
+    });
+
+    // get inherit binding value
+    const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
+</script>

+ 2 - 0
src/components/Button/src/props.ts

@@ -10,10 +10,12 @@ export const buttonProps = {
    * Text after icon.
    */
   postIcon: { type: String },
+  type: { type: String },
   /**
    * preIcon and postIcon icon size.
    * @default: 14
    */
   iconSize: { type: Number, default: 14 },
+  isUpload:{type:Boolean,default:false},
   onClick: { type: Function as PropType<(...args) => any>, default: null },
 };

+ 4 - 0
src/components/CardList/index.ts

@@ -0,0 +1,4 @@
+import { withInstall } from '/@/utils';
+import cardList from './src/CardList.vue';
+
+export const CardList = withInstall(cardList);

+ 178 - 0
src/components/CardList/src/CardList.vue

@@ -0,0 +1,178 @@
+<template>
+    <div class="p-2">
+        <div class="bg-white mb-2 p-4">
+            <BasicForm @register="registerForm" />
+        </div>
+        {{ sliderProp.width }}
+        <div class="bg-white p-2">
+            <List
+                    :grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: grid }"
+                    :data-source="data"
+                    :pagination="paginationProp"
+            >
+                <template #header>
+                    <div class="flex justify-end space-x-2"
+                    ><slot name="header"></slot>
+                        <Tooltip>
+                            <template #title>
+                                <div class="w-50">每行显示数量</div
+                                ><Slider
+                                    id="slider"
+                                    v-bind="sliderProp"
+                                    v-model:value="grid"
+                                    @change="sliderChange"
+                            /></template>
+                            <Button><TableOutlined /></Button>
+                        </Tooltip>
+                        <Tooltip @click="fetch">
+                            <template #title>刷新</template>
+                            <Button><RedoOutlined /></Button>
+                        </Tooltip>
+                    </div>
+                </template>
+                <template #renderItem="{ item }">
+                    <ListItem>
+                        <Card>
+                            <template #title></template>
+                            <template #cover>
+                                <div :class="height">
+                                    <Image :src="item.imgs[0]" />
+                                </div>
+                            </template>
+                            <template class="ant-card-actions" #actions>
+                                <!--              <SettingOutlined key="setting" />-->
+                                <EditOutlined key="edit" />
+                                <Dropdown
+                                        :trigger="['hover']"
+                                        :dropMenuList="[
+                    {
+                      text: '删除',
+                      event: '1',
+                      popConfirm: {
+                        title: '是否确认删除',
+                        confirm: handleDelete.bind(null, item.id),
+                      },
+                    },
+                  ]"
+                                        popconfirm
+                                >
+                                    <EllipsisOutlined key="ellipsis" />
+                                </Dropdown>
+                            </template>
+
+                            <CardMeta>
+                                <template #title>
+                                    <TypographyText :content="item.name" :ellipsis="{ tooltip: item.address }" />
+                                </template>
+                                <template #avatar>
+                                    <Avatar :src="item.avatar" />
+                                </template>
+                                <template #description>{{ item.time }}</template>
+                            </CardMeta>
+                        </Card>
+                    </ListItem>
+                </template>
+            </List>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+    import { computed, onMounted, ref } from 'vue';
+    import {
+        EditOutlined,
+        EllipsisOutlined,
+        RedoOutlined,
+        TableOutlined,
+    } from '@ant-design/icons-vue';
+    import { List, Card, Image, Typography, Tooltip, Slider, Avatar } from 'ant-design-vue';
+    import { Dropdown } from '/@/components/Dropdown';
+    import { BasicForm, useForm } from '/@/components/Form';
+    import { propTypes } from '/@/utils/propTypes';
+    import { Button } from '/@/components/Button';
+    import { isFunction } from '/@/utils/is';
+    import { useSlider, grid } from './data';
+    const ListItem = List.Item;
+    const CardMeta = Card.Meta;
+    const TypographyText = Typography.Text;
+    // 获取slider属性
+    const sliderProp = computed(() => useSlider(4));
+    // 组件接收参数
+    const props = defineProps({
+        // 请求API的参数
+        params: propTypes.object.def({}),
+        //api
+        api: propTypes.func,
+    });
+    //暴露内部方法
+    const emit = defineEmits(['getMethod', 'delete']);
+    //数据
+    const data = ref([]);
+    // 切换每行个数
+    // cover图片自适应高度
+    //修改pageSize并重新请求数据
+
+    const height = computed(() => {
+        return `h-${120 - grid.value * 6}`;
+    });
+    //表单
+    const [registerForm, { validate }] = useForm({
+        schemas: [{ field: 'type', component: 'Input', label: '类型' }],
+        labelWidth: 80,
+        baseColProps: { span: 6 },
+        actionColOptions: { span: 24 },
+        autoSubmitOnEnter: true,
+        submitFunc: handleSubmit,
+    });
+    //表单提交
+    async function handleSubmit() {
+        const data = await validate();
+        await fetch(data);
+    }
+    function sliderChange(n) {
+        pageSize.value = n * 4;
+        fetch();
+    }
+
+    // 自动请求并暴露内部方法
+    onMounted(() => {
+        fetch();
+        emit('getMethod', fetch);
+    });
+
+    async function fetch(p = {}) {
+        const { api, params } = props;
+        if (api && isFunction(api)) {
+            const res = await api({ ...params, page: page.value, pageSize: pageSize.value, ...p });
+            data.value = res.items;
+            total.value = res.total;
+        }
+    }
+    //分页相关
+    const page = ref(1);
+    const pageSize = ref(36);
+    const total = ref(0);
+    const paginationProp = ref({
+        showSizeChanger: false,
+        showQuickJumper: true,
+        pageSize,
+        current: page,
+        total,
+        showTotal: (total) => `总 ${total} 条`,
+        onChange: pageChange,
+        onShowSizeChange: pageSizeChange,
+    });
+
+    function pageChange(p, pz) {
+        page.value = p;
+        pageSize.value = pz;
+        fetch();
+    }
+    function pageSizeChange(current, size) {
+        pageSize.value = size;
+        fetch();
+    }
+
+    async function handleDelete(id) {
+        emit('delete', id);
+    }
+</script>

+ 25 - 0
src/components/CardList/src/data.ts

@@ -0,0 +1,25 @@
+import { ref } from 'vue';
+//每行个数
+export const grid = ref(12);
+// slider属性
+export const useSlider = (min = 6, max = 12) => {
+  // 每行显示个数滑动条
+  const getMarks = () => {
+    const l = {};
+    for (let i = min; i < max + 1; i++) {
+      l[i] = {
+        style: {
+          color: '#fff',
+        },
+        label: i,
+      };
+    }
+    return l;
+  };
+  return {
+    min,
+    max,
+    marks: getMarks(),
+    step: 1,
+  };
+};

+ 5 - 0
src/components/CodeEditor/src/typing.ts

@@ -0,0 +1,5 @@
+export enum MODE {
+  JSON = 'application/json',
+  HTML = 'htmlmixed',
+  JS = 'javascript',
+}

+ 1 - 1
src/components/Cropper/src/CopperModal.vue

@@ -186,7 +186,7 @@
                     try {
                         setModalProps({ confirmLoading: true });
                         const result = await uploadApi({ name: 'file', file: blob, filename });
-                        emit('uploadSuccess', { source: previewSource.value, data: result.data });
+                        emit('uploadSuccess', { source: previewSource.value, data: result.data || result.message });
                         closeModal();
                     } finally {
                         setModalProps({ confirmLoading: false });

+ 2 - 2
src/components/Cropper/src/CropperAvatar.vue

@@ -91,9 +91,9 @@
               }
       );
 
-      function handleUploadSuccess({ source }) {
+      function handleUploadSuccess({ source,data }) {
         sourceValue.value = source;
-        emit('change', source);
+        emit('change', source, data);
         createMessage.success(t('component.cropper.uploadSuccess'));
       }
 

+ 60 - 71
src/components/Dropdown/src/Dropdown.vue

@@ -1,19 +1,19 @@
 <template>
-  <Dropdown :trigger="trigger" v-bind="$attrs">
+  <a-dropdown :trigger="trigger" v-bind="$attrs">
     <span>
       <slot></slot>
     </span>
     <template #overlay>
-      <Menu :selectedKeys="selectedKeys">
+      <a-menu :selectedKeys="selectedKeys">
         <template v-for="item in dropMenuList" :key="`${item.event}`">
-          <MenuItem
-            v-bind="getAttr(item.event)"
-            @click="handleClickMenu(item)"
-            :disabled="item.disabled"
+          <a-menu-item
+                  v-bind="getAttr(item.event)"
+                  @click="handleClickMenu(item)"
+                  :disabled="item.disabled"
           >
-            <Popconfirm
-              v-if="popconfirm && item.popConfirm"
-              v-bind="getPopConfirmAttrs(item.popConfirm)"
+            <a-popconfirm
+                    v-if="popconfirm && item.popConfirm"
+                    v-bind="getPopConfirmAttrs(item.popConfirm)"
             >
               <template #icon v-if="item.popConfirm.icon">
                 <Icon :icon="item.popConfirm.icon" />
@@ -22,86 +22,75 @@
                 <Icon :icon="item.icon" v-if="item.icon" />
                 <span class="ml-1">{{ item.text }}</span>
               </div>
-            </Popconfirm>
+            </a-popconfirm>
             <template v-else>
               <Icon :icon="item.icon" v-if="item.icon" />
               <span class="ml-1">{{ item.text }}</span>
             </template>
-          </MenuItem>
-          <MenuDivider v-if="item.divider" :key="`d-${item.event}`" />
+          </a-menu-item>
+          <a-menu-divider v-if="item.divider" :key="`d-${item.event}`" />
         </template>
-      </Menu>
+      </a-menu>
     </template>
-  </Dropdown>
+  </a-dropdown>
 </template>
 
-<script lang="ts">
+<script lang="ts" setup>
   import { computed, PropType } from 'vue';
   import type { DropMenu } from './typing';
-
-  import { defineComponent } from 'vue';
   import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
   import { Icon } from '/@/components/Icon';
   import { omit } from 'lodash-es';
   import { isFunction } from '/@/utils/is';
 
-  export default defineComponent({
-    name: 'BasicDropdown',
-    components: {
-      Dropdown,
-      Menu,
-      MenuItem: Menu.Item,
-      MenuDivider: Menu.Divider,
-      Icon,
-      Popconfirm,
-    },
-    props: {
-      popconfirm: Boolean,
-      /**
-       * the trigger mode which executes the drop-down action
-       * @default ['hover']
-       * @type string[]
-       */
-      trigger: {
-        type: [Array] as PropType<('contextmenu' | 'click' | 'hover')[]>,
-        default: () => {
-          return ['contextmenu'];
-        },
-      },
-      dropMenuList: {
-        type: Array as PropType<(DropMenu & Recordable)[]>,
-        default: () => [],
-      },
-      selectedKeys: {
-        type: Array as PropType<string[]>,
-        default: () => [],
+  const ADropdown = Dropdown;
+  const AMenu = Menu;
+  const AMenuItem = Menu.Item;
+  const AMenuDivider = Menu.Divider;
+  const APopconfirm = Popconfirm;
+
+  const props = defineProps({
+    popconfirm: Boolean,
+    /**
+     * the trigger mode which executes the drop-down action
+     * @default ['hover']
+     * @type string[]
+     */
+    trigger: {
+      type: [Array] as PropType<('contextmenu' | 'click' | 'hover')[]>,
+      default: () => {
+        return ['contextmenu'];
       },
     },
-    emits: ['menuEvent'],
-    setup(props, { emit }) {
-      function handleClickMenu(item: DropMenu) {
-        const { event } = item;
-        const menu = props.dropMenuList.find((item) => `${item.event}` === `${event}`);
-        emit('menuEvent', menu);
-        item.onClick?.();
-      }
+    dropMenuList: {
+      type: Array as PropType<(DropMenu & Recordable)[]>,
+      default: () => [],
+    },
+    selectedKeys: {
+      type: Array as PropType<string[]>,
+      default: () => [],
+    },
+  });
 
-      const getPopConfirmAttrs = computed(() => {
-        return (attrs) => {
-          const originAttrs = omit(attrs, ['confirm', 'cancel', 'icon']);
-          if (!attrs.onConfirm && attrs.confirm && isFunction(attrs.confirm))
-            originAttrs['onConfirm'] = attrs.confirm;
-          if (!attrs.onCancel && attrs.cancel && isFunction(attrs.cancel))
-            originAttrs['onCancel'] = attrs.cancel;
-          return originAttrs;
-        };
-      });
+  const emit = defineEmits(['menuEvent']);
 
-      return {
-        handleClickMenu,
-        getPopConfirmAttrs,
-        getAttr: (key: string | number) => ({ key }),
-      };
-    },
+  function handleClickMenu(item: DropMenu) {
+    const { event } = item;
+    const menu = props.dropMenuList.find((item) => `${item.event}` === `${event}`);
+    emit('menuEvent', menu);
+    item.onClick?.();
+  }
+
+  const getPopConfirmAttrs = computed(() => {
+    return (attrs) => {
+      const originAttrs = omit(attrs, ['confirm', 'cancel', 'icon']);
+      if (!attrs.onConfirm && attrs.confirm && isFunction(attrs.confirm))
+        originAttrs['onConfirm'] = attrs.confirm;
+      if (!attrs.onCancel && attrs.cancel && isFunction(attrs.cancel))
+        originAttrs['onCancel'] = attrs.cancel;
+      return originAttrs;
+    };
   });
+
+  const getAttr = (key: string | number) => ({ key });
 </script>

+ 39 - 8
src/components/Excel/src/ImportExcel.vue

@@ -1,11 +1,11 @@
 <template>
   <div>
     <input
-      ref="inputRef"
-      type="file"
-      v-show="false"
-      accept=".xlsx, .xls"
-      @change="handleInputClick"
+            ref="inputRef"
+            type="file"
+            v-show="false"
+            accept=".xlsx, .xls"
+            @change="handleInputClick"
     />
     <div @click="handleUpload">
       <slot></slot>
@@ -15,12 +15,25 @@
 <script lang="ts">
   import { defineComponent, ref, unref } from 'vue';
   import XLSX from 'xlsx';
+  import { dateUtil } from '/@/utils/dateUtil';
 
   import type { ExcelData } from './typing';
   export default defineComponent({
     name: 'ImportExcel',
+    props: {
+      // 日期时间格式。如果不提供或者提供空值,将返回原始Date对象
+      dateFormat: {
+        type: String,
+      },
+      // 时区调整。实验性功能,仅为了解决读取日期时间值有偏差的问题。目前仅提供了+08:00时区的偏差修正值
+      // https://github.com/SheetJS/sheetjs/issues/1470#issuecomment-501108554
+      timeZone: {
+        type: Number,
+        default: 8,
+      },
+    },
     emits: ['success', 'error'],
-    setup(_, { emit }) {
+    setup(props, { emit }) {
       const inputRef = ref<HTMLInputElement | null>(null);
       const loadingRef = ref<Boolean>(false);
 
@@ -51,10 +64,28 @@
        */
       function getExcelData(workbook: XLSX.WorkBook) {
         const excelData: ExcelData[] = [];
+        const { dateFormat, timeZone } = props;
         for (const sheetName of workbook.SheetNames) {
           const worksheet = workbook.Sheets[sheetName];
           const header: string[] = getHeaderRow(worksheet);
-          const results = XLSX.utils.sheet_to_json(worksheet);
+          let results = XLSX.utils.sheet_to_json(worksheet, {
+            raw: true,
+            dateNF: dateFormat, //Not worked
+          }) as object[];
+          results = results.map((row: object) => {
+            for (let field in row) {
+              if (row[field] instanceof Date) {
+                if (timeZone === 8) {
+                  row[field].setSeconds(row[field].getSeconds() + 43);
+                }
+                if (dateFormat) {
+                  row[field] = dateUtil(row[field]).format(dateFormat);
+                }
+              }
+            }
+            return row;
+          });
+
           excelData.push({
             header,
             results,
@@ -76,7 +107,7 @@
           reader.onload = async (e) => {
             try {
               const data = e.target && e.target.result;
-              const workbook = XLSX.read(data, { type: 'array' });
+              const workbook = XLSX.read(data, { type: 'array', cellDates: true });
               // console.log(workbook);
               /* DO SOMETHING WITH workbook HERE */
               const excelData = getExcelData(workbook);

+ 0 - 4
src/components/FlowChart/index.ts

@@ -1,4 +0,0 @@
-import { withInstall } from '/@/utils';
-import flowChart from './src/FlowChart.vue';
-
-export const FlowChart = withInstall(flowChart);

+ 0 - 158
src/components/FlowChart/src/FlowChart.vue

@@ -1,158 +0,0 @@
-<template>
-  <div class="h-full" :class="prefixCls">
-    <FlowChartToolbar :prefixCls="prefixCls" v-if="toolbar" @view-data="handlePreview" />
-    <div ref="lfElRef" class="h-full"></div>
-    <BasicModal @register="register" title="流程数据" width="50%">
-      <JsonPreview :data="graphData" />
-    </BasicModal>
-  </div>
-</template>
-<script lang="ts">
-  import type { Ref } from 'vue';
-  import type { Definition } from '@logicflow/core';
-  import { defineComponent, ref, onMounted, unref, nextTick, computed, watch } from 'vue';
-  import FlowChartToolbar from './FlowChartToolbar.vue';
-  import LogicFlow from '@logicflow/core';
-  import { Snapshot, BpmnElement, Menu, DndPanel, SelectionSelect } from '@logicflow/extension';
-  import { useDesign } from '/@/hooks/web/useDesign';
-  import { useAppStore } from '/@/store/modules/app';
-  import { createFlowChartContext } from './useFlowContext';
-  import { toLogicFlowData } from './adpterForTurbo';
-  import { useModal, BasicModal } from '/@/components/Modal';
-  import { JsonPreview } from '/@/components/CodeEditor';
-  import { configDefaultDndPanel } from './config';
-  import '@logicflow/core/dist/style/index.css';
-  import '@logicflow/extension/lib/style/index.css';
-
-  export default defineComponent({
-    name: 'FlowChart',
-    components: { BasicModal, FlowChartToolbar, JsonPreview },
-    props: {
-      flowOptions: {
-        type: Object as PropType<Definition>,
-        default: () => ({}),
-      },
-
-      data: {
-        type: Object as PropType<any>,
-        default: () => ({}),
-      },
-
-      toolbar: {
-        type: Boolean,
-        default: true,
-      },
-      patternItems: {
-        type: Array,
-      },
-    },
-    setup(props) {
-      const lfElRef = ref(null);
-      const graphData = ref({});
-
-      const lfInstance = ref(null) as Ref<LogicFlow | null>;
-
-      const { prefixCls } = useDesign('flow-chart');
-      const appStore = useAppStore();
-      const [register, { openModal }] = useModal();
-      createFlowChartContext({
-        logicFlow: lfInstance as unknown as LogicFlow,
-      });
-
-      const getFlowOptions = computed(() => {
-        const { flowOptions } = props;
-
-        const defaultOptions: Partial<Definition> = {
-          grid: true,
-          background: {
-            color: appStore.getDarkMode === 'light' ? '#f7f9ff' : '#151515',
-          },
-          keyboard: {
-            enabled: true,
-          },
-          ...flowOptions,
-        };
-        return defaultOptions as Definition;
-      });
-
-      watch(
-              () => props.data,
-              () => {
-                onRender();
-              }
-      );
-
-      // TODO
-      // watch(
-      //   () => appStore.getDarkMode,
-      //   () => {
-      //     init();
-      //   }
-      // );
-
-      watch(
-              () => unref(getFlowOptions),
-              (options) => {
-                unref(lfInstance)?.updateEditConfig(options);
-              }
-      );
-
-      // init logicFlow
-      async function init() {
-        await nextTick();
-
-        const lfEl = unref(lfElRef);
-        if (!lfEl) {
-          return;
-        }
-        LogicFlow.use(DndPanel);
-
-        // Canvas configuration
-        LogicFlow.use(Snapshot);
-        // Use the bpmn plug-in to introduce bpmn elements, which can be used after conversion in turbo
-        LogicFlow.use(BpmnElement);
-        // Start the right-click menu
-        LogicFlow.use(Menu);
-        LogicFlow.use(SelectionSelect);
-
-        lfInstance.value = new LogicFlow({
-          ...unref(getFlowOptions),
-          container: lfEl,
-        });
-        const lf = unref(lfInstance)!;
-        lf?.setDefaultEdgeType('line');
-        onRender();
-        lf?.setPatternItems(props.patternItems || configDefaultDndPanel(lf));
-      }
-
-      async function onRender() {
-        await nextTick();
-        const lf = unref(lfInstance);
-        if (!lf) {
-          return;
-        }
-        const lFData = toLogicFlowData(props.data);
-        lf.render(lFData);
-      }
-
-      function handlePreview() {
-        const lf = unref(lfInstance);
-        if (!lf) {
-          return;
-        }
-        graphData.value = unref(lf).getGraphData();
-        openModal();
-      }
-
-      onMounted(init);
-
-      return {
-        register,
-        prefixCls,
-        lfElRef,
-        handlePreview,
-        graphData,
-      };
-    },
-  });
-</script>

+ 0 - 162
src/components/FlowChart/src/FlowChartToolbar.vue

@@ -1,162 +0,0 @@
-<template>
-  <div :class="`${prefixCls}-toolbar`" class="flex items-center px-2 py-1">
-    <template v-for="item in toolbarItemList" :key="item.type">
-      <Tooltip placement="bottom" v-bind="item.disabled ? { visible: false } : {}">
-        <template #title>{{ item.tooltip }}</template>
-        <span :class="`${prefixCls}-toolbar__icon`" v-if="item.icon" @click="onControl(item)">
-          <Icon
-            :icon="item.icon"
-            :class="item.disabled ? 'cursor-not-allowed disabeld' : 'cursor-pointer'"
-          />
-        </span>
-      </Tooltip>
-      <Divider v-if="item.separate" type="vertical" />
-    </template>
-  </div>
-</template>
-<script lang="ts">
-  import type { ToolbarConfig } from './types';
-
-  import { defineComponent, ref, onUnmounted, unref, nextTick, watchEffect } from 'vue';
-  import { Divider, Tooltip } from 'ant-design-vue';
-  import { Icon } from '/@/components/Icon';
-
-  import { useFlowChartContext } from './useFlowContext';
-  import { ToolbarTypeEnum } from './enum';
-
-  export default defineComponent({
-    name: 'FlowChartToolbar',
-    components: { Icon, Divider, Tooltip },
-    props: {
-      prefixCls: String,
-    },
-    emits: ['view-data'],
-    setup(_, { emit }) {
-      const toolbarItemList = ref<ToolbarConfig[]>([
-        {
-          type: ToolbarTypeEnum.ZOOM_IN,
-          icon: 'codicon:zoom-out',
-          tooltip: '缩小',
-        },
-        {
-          type: ToolbarTypeEnum.ZOOM_OUT,
-          icon: 'codicon:zoom-in',
-          tooltip: '放大',
-        },
-        {
-          type: ToolbarTypeEnum.RESET_ZOOM,
-          icon: 'codicon:screen-normal',
-          tooltip: '重置比例',
-        },
-        { separate: true },
-        {
-          type: ToolbarTypeEnum.UNDO,
-          icon: 'ion:arrow-undo-outline',
-          tooltip: '后退',
-          disabled: true,
-        },
-        {
-          type: ToolbarTypeEnum.REDO,
-          icon: 'ion:arrow-redo-outline',
-          tooltip: '前进',
-          disabled: true,
-        },
-        { separate: true },
-        {
-          type: ToolbarTypeEnum.SNAPSHOT,
-          icon: 'ion:download-outline',
-          tooltip: '下载',
-        },
-        {
-          type: ToolbarTypeEnum.VIEW_DATA,
-          icon: 'carbon:document-view',
-          tooltip: '查看数据',
-        },
-      ]);
-
-      const { logicFlow } = useFlowChartContext();
-
-      function onHistoryChange({ data: { undoAble, redoAble } }) {
-        const itemsList = unref(toolbarItemList);
-        const undoIndex = itemsList.findIndex((item) => item.type === ToolbarTypeEnum.UNDO);
-        const redoIndex = itemsList.findIndex((item) => item.type === ToolbarTypeEnum.REDO);
-        if (undoIndex !== -1) {
-          unref(toolbarItemList)[undoIndex].disabled = !undoAble;
-        }
-        if (redoIndex !== -1) {
-          unref(toolbarItemList)[redoIndex].disabled = !redoAble;
-        }
-      }
-
-      const onControl = (item) => {
-        const lf = unref(logicFlow);
-        if (!lf) {
-          return;
-        }
-        switch (item.type) {
-          case ToolbarTypeEnum.ZOOM_IN:
-            lf.zoom();
-            break;
-          case ToolbarTypeEnum.ZOOM_OUT:
-            lf.zoom(true);
-            break;
-          case ToolbarTypeEnum.RESET_ZOOM:
-            lf.resetZoom();
-            break;
-          case ToolbarTypeEnum.UNDO:
-            lf.undo();
-            break;
-          case ToolbarTypeEnum.REDO:
-            lf.redo();
-            break;
-          case ToolbarTypeEnum.SNAPSHOT:
-            lf.getSnapshot();
-            break;
-          case ToolbarTypeEnum.VIEW_DATA:
-            emit('view-data');
-            break;
-        }
-      };
-
-      watchEffect(async () => {
-        if (unref(logicFlow)) {
-          await nextTick();
-          unref(logicFlow)?.on('history:change', onHistoryChange);
-        }
-      });
-
-      onUnmounted(() => {
-        unref(logicFlow)?.off('history:change', onHistoryChange);
-      });
-      return { toolbarItemList, onControl };
-    },
-  });
-</script>
-<style lang="less">
-  @prefix-cls: ~'@{namespace}-flow-chart-toolbar';
-
-  html[data-theme='dark'] {
-    .lf-dnd {
-      background: #080808;
-    }
-  }
-  .@{prefix-cls} {
-    height: 36px;
-    background-color: @app-content-background;
-    border-bottom: 1px solid @border-color-base;
-
-    .disabeld {
-      color: @disabled-color;
-    }
-
-    &__icon {
-      display: inline-block;
-      padding: 2px 4px;
-      margin-right: 10px;
-
-      &:hover {
-        color: @primary-color;
-      }
-    }
-  }
-</style>

+ 0 - 75
src/components/FlowChart/src/adpterForTurbo.ts

@@ -1,75 +0,0 @@
-const TurboType = {
-  SEQUENCE_FLOW: 1,
-  START_EVENT: 2,
-  END_EVENT: 3,
-  USER_TASK: 4,
-  SERVICE_TASK: 5,
-  EXCLUSIVE_GATEWAY: 6,
-};
-
-function convertFlowElementToEdge(element) {
-  const { incoming, outgoing, properties, key } = element;
-  const { text, startPoint, endPoint, pointsList, logicFlowType } = properties;
-  const edge = {
-    id: key,
-    type: logicFlowType,
-    sourceNodeId: incoming[0],
-    targetNodeId: outgoing[0],
-    text,
-    startPoint,
-    endPoint,
-    pointsList,
-    properties: {},
-  };
-  const excludeProperties = ['startPoint', 'endPoint', 'pointsList', 'text', 'logicFlowType'];
-  Object.keys(element.properties).forEach((property) => {
-    if (excludeProperties.indexOf(property) === -1) {
-      edge.properties[property] = element.properties[property];
-    }
-  });
-  return edge;
-}
-
-function convertFlowElementToNode(element) {
-  const { properties, key } = element;
-  const { x, y, text, logicFlowType } = properties;
-  const node = {
-    id: key,
-    type: logicFlowType,
-    x,
-    y,
-    text,
-    properties: {},
-  };
-  const excludeProperties = ['x', 'y', 'text', 'logicFlowType'];
-  Object.keys(element.properties).forEach((property) => {
-    if (excludeProperties.indexOf(property) === -1) {
-      node.properties[property] = element.properties[property];
-    }
-  });
-  return node;
-}
-
-export function toLogicFlowData(data) {
-  const lfData: {
-    // TODO type
-    nodes: any[];
-    edges: any[];
-  } = {
-    nodes: [],
-    edges: [],
-  };
-  const list = data.flowElementList;
-  list &&
-    list.length > 0 &&
-    list.forEach((element) => {
-      if (element.type === TurboType.SEQUENCE_FLOW) {
-        const edge = convertFlowElementToEdge(element);
-        lfData.edges.push(edge);
-      } else {
-        const node = convertFlowElementToNode(element);
-        lfData.nodes.push(node);
-      }
-    });
-  return lfData;
-}

+ 0 - 96
src/components/FlowChart/src/config.ts

@@ -1,96 +0,0 @@
-export const nodeList = [
-  {
-    text: '开始',
-    type: 'start',
-    class: 'node-start',
-  },
-  {
-    text: '矩形',
-    type: 'rect',
-    class: 'node-rect',
-  },
-  {
-    type: 'user',
-    text: '用户',
-    class: 'node-user',
-  },
-  {
-    type: 'push',
-    text: '推送',
-    class: 'node-push',
-  },
-  {
-    type: 'download',
-    text: '位置',
-    class: 'node-download',
-  },
-  {
-    type: 'end',
-    text: '结束',
-    class: 'node-end',
-  },
-];
-
-export const BpmnNode = [
-  {
-    type: 'bpmn:startEvent',
-    text: '开始',
-    class: 'bpmn-start',
-  },
-  {
-    type: 'bpmn:endEvent',
-    text: '结束',
-    class: 'bpmn-end',
-  },
-  {
-    type: 'bpmn:exclusiveGateway',
-    text: '网关',
-    class: 'bpmn-exclusiveGateway',
-  },
-  {
-    type: 'bpmn:userTask',
-    text: '用户',
-    class: 'bpmn-user',
-  },
-];
-
-export function configDefaultDndPanel(lf) {
-  return [
-    {
-      text: '选区',
-      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP++2026ETdpv10iy7WFbqFyyW6GBywLCv5gI+Dw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3+QJ+AKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAW+Gu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLda+DUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxm+v7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII=',
-      callback: () => {
-        lf.updateEditConfig({
-          stopMoveGraph: true,
-        });
-      },
-    },
-    {
-      type: 'circle',
-      text: '开始',
-      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==',
-    },
-    {
-      type: 'rect',
-      text: '用户任务',
-      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
-      cls: 'important-node',
-    },
-    {
-      type: 'rect',
-      text: '系统任务',
-      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
-      cls: 'import_icon',
-    },
-    {
-      type: 'diamond',
-      text: '条件判断',
-      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=',
-    },
-    {
-      type: 'circle',
-      text: '结束',
-      icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC',
-    },
-  ];
-}

+ 0 - 11
src/components/FlowChart/src/enum.ts

@@ -1,11 +0,0 @@
-export enum ToolbarTypeEnum {
-  ZOOM_IN = 'zoomIn',
-  ZOOM_OUT = 'zoomOut',
-  RESET_ZOOM = 'resetZoom',
-
-  UNDO = 'undo',
-  REDO = 'redo',
-
-  SNAPSHOT = 'snapshot',
-  VIEW_DATA = 'viewData',
-}

+ 0 - 14
src/components/FlowChart/src/types.ts

@@ -1,14 +0,0 @@
-import { NodeConfig } from '@logicflow/core';
-import { ToolbarTypeEnum } from './enum';
-
-export interface NodeItem extends NodeConfig {
-  icon: string;
-}
-
-export interface ToolbarConfig {
-  type?: string | ToolbarTypeEnum;
-  tooltip?: string | boolean;
-  icon?: string;
-  disabled?: boolean;
-  separate?: boolean;
-}

+ 0 - 17
src/components/FlowChart/src/useFlowContext.ts

@@ -1,17 +0,0 @@
-import type LogicFlow from '@logicflow/core';
-
-import { provide, inject } from 'vue';
-
-const key = Symbol('flow-chart');
-
-type Instance = {
-  logicFlow: LogicFlow;
-};
-
-export function createFlowChartContext(instance: Instance) {
-  provide(key, instance);
-}
-
-export function useFlowChartContext(): Instance {
-  return inject(key) as Instance;
-}

+ 22 - 1
src/components/Form/index.ts

@@ -7,8 +7,29 @@ export { useComponentRegister } from './src/hooks/useComponentRegister';
 export { useForm } from './src/hooks/useForm';
 
 export { default as ApiSelect } from './src/components/ApiSelect.vue';
-export { default as JAreaLinkage } from './src/components/JAreaLinkage.vue';
 export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
 export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
+export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
+//Jeecg自定义组件
+export { default as JAreaLinkage } from './src/jeecg/components/JAreaLinkage.vue';
+export { default as JSelectUser } from './src/jeecg/components/JSelectUser.vue';
+export { default as JSelectDept } from './src/jeecg/components/JSelectDept.vue';
+export { default as JCodeEditor } from './src/jeecg/components/JCodeEditor.vue';
+export { default as JCategorySelect } from './src/jeecg/components/JCategorySelect.vue';
+export { default as JSelectMultiple } from './src/jeecg/components/JSelectMultiple.vue';
+export { default as JPopup } from './src/jeecg/components/JPopup.vue';
+export { default as JAreaSelect } from './src/jeecg/components/JAreaSelect.vue';
+export { JEasyCron, JEasyCronInner, JEasyCronModal } from '/@/components/Form/src/jeecg/components/JEasyCron'
+export { default as JCheckbox } from './src/jeecg/components/JCheckbox.vue';
+export { default as JInput } from './src/jeecg/components/JInput.vue';
+export { default as JEllipsis } from './src/jeecg/components/JEllipsis.vue';
+export { default as JDictSelectTag } from './src/jeecg/components/JDictSelectTag.vue';
+export { default as JTreeSelect } from './src/jeecg/components/JTreeSelect.vue';
+export { default as JSearchSelect } from './src/jeecg/components/JSearchSelect.vue';
+export { default as JSelectUserByDept } from './src/jeecg/components/JSelectUserByDept.vue';
+export { default as JEditor } from './src/jeecg/components/JEditor.vue';
+export { default as JImageUpload } from './src/jeecg/components/JImageUpload.vue';
+// Jeecg自定义校验
+export { JCronValidator } from '/@/components/Form/src/jeecg/components/JEasyCron'
 
 export { BasicForm };

+ 6 - 22
src/components/Form/src/BasicForm.vue

@@ -1,23 +1,9 @@
 <template>
-    <Form
-            v-bind="getBindValue"
-            :class="getFormClass"
-            ref="formElRef"
-            :model="formModel"
-            @keypress.enter="handleEnterPress"
-    >
+    <Form v-bind="getBindValue" :class="getFormClass" ref="formElRef" :model="formModel" @keypress.enter="handleEnterPress">
         <Row v-bind="getRow">
             <slot name="formHeader"></slot>
             <template v-for="schema in getSchema" :key="schema.field">
-                <FormItem
-                        :tableAction="tableAction"
-                        :formActionType="formActionType"
-                        :schema="schema"
-                        :formProps="getProps"
-                        :allDefaultValues="defaultValueRef"
-                        :formModel="formModel"
-                        :setFormModel="setFormModel"
-                >
+                <FormItem :tableAction="tableAction" :formActionType="formActionType" :schema="schema" :formProps="getProps" :allDefaultValues="defaultValueRef" :formModel="formModel" :setFormModel="setFormModel">
                     <template #[item]="data" v-for="item in Object.keys($slots)">
                         <slot :name="item" v-bind="data || {}"></slot>
                     </template>
@@ -25,10 +11,7 @@
             </template>
 
             <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
-                <template
-                        #[item]="data"
-                        v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
-                >
+                <template #[item]="data" v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']">
                     <slot :name="item" v-bind="data || {}"></slot>
                 </template>
             </FormAction>
@@ -72,8 +55,9 @@
             const modalFn = useModalContext();
 
             const advanceState = reactive<AdvanceState>({
-                isAdvanced: true,
-                hideAdvanceBtn: false,
+                // 默认是收起状态
+                isAdvanced: false,
+                hideAdvanceBtn: true,
                 isLoad: false,
                 actionSpan: 6,
             });

+ 59 - 3
src/components/Form/src/componentMap.ts

@@ -20,15 +20,42 @@ import {
   Rate,
   Divider,
 } from 'ant-design-vue';
-
+import ApiRadioGroup from './components/ApiRadioGroup.vue';
 import RadioButtonGroup from './components/RadioButtonGroup.vue';
 import ApiSelect from './components/ApiSelect.vue';
-import JAreaLinkage from './components/JAreaLinkage.vue';
 import ApiTreeSelect from './components/ApiTreeSelect.vue';
 import { BasicUpload } from '/@/components/Upload';
 import { StrengthMeter } from '/@/components/StrengthMeter';
 import { IconPicker } from '/@/components/Icon';
 import { CountdownInput } from '/@/components/CountDown';
+//自定义组件
+import JAreaLinkage from './jeecg/components/JAreaLinkage.vue';
+import JSelectUser from './jeecg/components/JSelectUser.vue';
+import JSelectPosition from './jeecg/components/JSelectPosition.vue';
+import JSelectRole from './jeecg/components/JSelectRole.vue';
+import JImageUpload from './jeecg/components/JImageUpload.vue';
+import JDictSelectTag from './jeecg/components/JDictSelectTag.vue';
+import JSelectDept from './jeecg/components/JSelectDept.vue';
+import JAreaSelect from './jeecg/components/JAreaSelect.vue';
+import JEditor from './jeecg/components/JEditor.vue';
+import JMarkdownEditor from './jeecg/components/JMarkdownEditor.vue';
+import JSelectInput from './jeecg/components/JSelectInput.vue';
+import JCodeEditor from './jeecg/components/JCodeEditor.vue';
+import JCategorySelect from './jeecg/components/JCategorySelect.vue';
+import JSelectMultiple from './jeecg/components/JSelectMultiple.vue';
+import JPopup from './jeecg/components/JPopup.vue';
+import JSwitch from './jeecg/components/JSwitch.vue';
+import JTreeDict from './jeecg/components/JTreeDict.vue';
+import JInputPop from './jeecg/components/JInputPop.vue';
+import { JEasyCron } from './jeecg/components/JEasyCron'
+import JCheckbox from './jeecg/components/JCheckbox.vue';
+import JInput from './jeecg/components/JInput.vue';
+import JTreeSelect from './jeecg/components/JTreeSelect.vue';
+import JEllipsis from './jeecg/components/JEllipsis.vue';
+import JSelectUserByDept from './jeecg/components/JSelectUserByDept.vue';
+import JUpload from './jeecg/components/JUpload/JUpload.vue'
+import JSearchSelect from './jeecg/components/JSearchSelect.vue'
+import JAddInput from './jeecg/components/JAddInput.vue'
 
 const componentMap = new Map<ComponentType, Component>();
 
@@ -42,9 +69,9 @@ componentMap.set('AutoComplete', AutoComplete);
 
 componentMap.set('Select', Select);
 componentMap.set('ApiSelect', ApiSelect);
-componentMap.set('JAreaLinkage', JAreaLinkage);
 componentMap.set('TreeSelect', TreeSelect);
 componentMap.set('ApiTreeSelect', ApiTreeSelect);
+componentMap.set('ApiRadioGroup', ApiRadioGroup);
 componentMap.set('Switch', Switch);
 componentMap.set('RadioButtonGroup', RadioButtonGroup);
 componentMap.set('RadioGroup', Radio.Group);
@@ -66,6 +93,35 @@ componentMap.set('InputCountDown', CountdownInput);
 componentMap.set('Upload', BasicUpload);
 componentMap.set('Divider', Divider);
 
+//注册自定义组件
+componentMap.set('JAreaLinkage', JAreaLinkage);
+componentMap.set('JSelectPosition', JSelectPosition);
+componentMap.set('JSelectUser', JSelectUser);
+componentMap.set('JSelectRole', JSelectRole);
+componentMap.set('JImageUpload', JImageUpload);
+componentMap.set('JDictSelectTag', JDictSelectTag);
+componentMap.set('JSelectDept', JSelectDept);
+componentMap.set('JAreaSelect', JAreaSelect);
+componentMap.set('JEditor', JEditor);
+componentMap.set('JMarkdownEditor', JMarkdownEditor);
+componentMap.set('JSelectInput', JSelectInput);
+componentMap.set('JCodeEditor', JCodeEditor);
+componentMap.set('JCategorySelect', JCategorySelect);
+componentMap.set('JSelectMultiple', JSelectMultiple);
+componentMap.set('JPopup', JPopup);
+componentMap.set('JSwitch', JSwitch);
+componentMap.set('JTreeDict', JTreeDict);
+componentMap.set('JInputPop', JInputPop);
+componentMap.set('JEasyCron', JEasyCron);
+componentMap.set('JCheckbox', JCheckbox);
+componentMap.set('JInput', JInput);
+componentMap.set('JTreeSelect', JTreeSelect);
+componentMap.set('JEllipsis', JEllipsis);
+componentMap.set('JSelectUserByDept', JSelectUserByDept);
+componentMap.set('JUpload', JUpload);
+componentMap.set('JSearchSelect', JSearchSelect);
+componentMap.set('JAddInput', JAddInput);
+
 export function add(compName: ComponentType, component: Component) {
   componentMap.set(compName, component);
 }

+ 130 - 0
src/components/Form/src/components/ApiRadioGroup.vue

@@ -0,0 +1,130 @@
+<!--
+ * @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
+-->
+<template>
+  <RadioGroup v-bind="attrs" v-model:value="state" button-style="solid" @change="handleChange">
+    <template v-for="item in getOptions" :key="`${item.value}`">
+      <RadioButton v-if="props.isBtn" :value="item.value" :disabled="item.disabled">
+        {{ item.label }}
+      </RadioButton>
+      <Radio v-else :value="item.value" :disabled="item.disabled">
+        {{ item.label }}
+      </Radio>
+    </template>
+  </RadioGroup>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType, ref, watchEffect, computed, unref, watch } from 'vue';
+  import { Radio } from 'ant-design-vue';
+  import { isFunction } from '/@/utils/is';
+  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
+  import { useAttrs } from '/@/hooks/core/useAttrs';
+  import { propTypes } from '/@/utils/propTypes';
+  import { get, omit } from 'lodash-es';
+  import { useI18n } from '/@/hooks/web/useI18n';
+  type OptionsItem = { label: string; value: string | number | boolean; disabled?: boolean };
+
+  export default defineComponent({
+    name: 'ApiRadioGroup',
+    components: {
+      RadioGroup: Radio.Group,
+      RadioButton: Radio.Button,
+      Radio,
+    },
+    props: {
+      api: {
+        type: Function as PropType<(arg?: Recordable | string) => Promise<OptionsItem[]>>,
+        default: null,
+      },
+      params: {
+        type: [Object, String] as PropType<Recordable | string>,
+        default: () => ({}),
+      },
+      value: {
+        type: [String, Number, Boolean] as PropType<string | number | boolean>,
+      },
+      isBtn: {
+        type: [Boolean] as PropType<boolean>,
+        default: false,
+      },
+      numberToString: propTypes.bool,
+      resultField: propTypes.string.def(''),
+      labelField: propTypes.string.def('label'),
+      valueField: propTypes.string.def('value'),
+      immediate: propTypes.bool.def(true),
+    },
+    emits: ['options-change', 'change'],
+    setup(props, { emit }) {
+      const options = ref<OptionsItem[]>([]);
+      const loading = ref(false);
+      const isFirstLoad = ref(true);
+      const emitData = ref<any[]>([]);
+      const attrs = useAttrs();
+      const { t } = useI18n();
+      // Embedded in the form, just use the hook binding to perform form verification
+      const [state] = useRuleFormItem(props);
+
+      // Processing options value
+      const getOptions = computed(() => {
+        const { labelField, valueField, numberToString } = props;
+
+        return unref(options).reduce((prev, next: Recordable) => {
+          if (next) {
+            const value = next[valueField];
+            prev.push({
+              label: next[labelField],
+              value: numberToString ? `${value}` : value,
+              ...omit(next, [labelField, valueField]),
+            });
+          }
+          return prev;
+        }, [] as OptionsItem[]);
+      });
+
+      watchEffect(() => {
+        props.immediate && fetch();
+      });
+
+      watch(
+        () => props.params,
+        () => {
+          !unref(isFirstLoad) && fetch();
+        },
+        { deep: true },
+      );
+
+      async function fetch() {
+        const api = props.api;
+        if (!api || !isFunction(api)) return;
+        options.value = [];
+        try {
+          loading.value = true;
+          const res = await api(props.params);
+          if (Array.isArray(res)) {
+            options.value = res;
+            emitChange();
+            return;
+          }
+          if (props.resultField) {
+            options.value = get(res, props.resultField) || [];
+          }
+          emitChange();
+        } catch (error) {
+          console.warn(error);
+        } finally {
+          loading.value = false;
+        }
+      }
+
+      function emitChange() {
+        emit('options-change', unref(getOptions));
+      }
+
+      function handleChange(_, ...args) {
+        emitData.value = args;
+      }
+
+      return { state, getOptions, attrs, loading, t, handleChange, props };
+    },
+  });
+</script>

+ 9 - 12
src/components/Form/src/components/ApiSelect.vue

@@ -1,7 +1,7 @@
 <template>
     <Select
             @dropdownVisibleChange="handleFetch"
-            v-bind="attrs"
+            v-bind="$attrs"
             @change="handleChange"
             :options="getOptions"
             v-model:value="state"
@@ -41,12 +41,7 @@
         },
         inheritAttrs: false,
         props: {
-            value: propTypes.oneOfType([
-                propTypes.object,
-                propTypes.number,
-                propTypes.string,
-                propTypes.array,
-            ]),
+            value: [Array, Object, String, Number],
             numberToString: propTypes.bool,
             api: {
                 type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
@@ -73,18 +68,17 @@
             const { t } = useI18n();
 
             // Embedded in the form, just use the hook binding to perform form verification
-            const [state] = useRuleFormItem(props, 'value', 'change', emitData);
+            const [state, setState] = useRuleFormItem(props, 'value', 'change', emitData);
 
             const getOptions = computed(() => {
                 const { labelField, valueField, numberToString } = props;
-
                 return unref(options).reduce((prev, next: Recordable) => {
                     if (next) {
                         const value = next[valueField];
                         prev.push({
-                            label: next[labelField],
-                            value: numberToString ? `${value}` : value,
-                            ...omit(next, [labelField, valueField]),
+                          ...omit(next, [labelField, valueField]),
+                          label: next[labelField],
+                          value: numberToString ? `${value}` : value,
                         });
                     }
                     return prev;
@@ -123,6 +117,9 @@
                     console.warn(error);
                 } finally {
                     loading.value = false;
+                    //--@updateBy-begin----author:liusq---date:20210914------for:判断选择模式,multiple多选情况下的value值空的情况下需要设置为数组------
+                    unref(attrs).mode == 'multiple' && !Array.isArray(unref(state)) && setState([])
+                    //--@updateBy-end----author:liusq---date:20210914------for:判断选择模式,multiple多选情况下的value值空的情况下需要设置为数组------
                 }
             }
 

+ 1 - 1
src/components/Form/src/components/ApiTreeSelect.vue

@@ -44,7 +44,7 @@
             watch(
                 () => props.params,
                 () => {
-                    isFirstLoaded.value && fetch();
+                   !unref(isFirstLoaded) && fetch();
                 },
                 { deep: true }
             );

+ 9 - 5
src/components/Form/src/components/FormAction.vue

@@ -2,15 +2,19 @@
   <a-col v-bind="actionColOpt" v-if="showActionButtonGroup">
     <div style="width: 100%" :style="{ textAlign: actionColOpt.style.textAlign }">
       <FormItem>
+       <!-- update-begin-author:zyf   Date:20211213  for:调换按钮前后位置-->
+        <slot name="submitBefore"></slot>  
+        <Button type="primary" class="mr-2" v-bind="getSubmitBtnOptions" @click="submitAction" v-if="showSubmitButton">
+            <Icon icon="ant-design:search-outlined"></Icon>
+          {{ getSubmitBtnOptions.text }}
+        </Button>
+
         <slot name="resetBefore"></slot>
         <Button type="default" class="mr-2" v-bind="getResetBtnOptions" @click="resetAction" v-if="showResetButton">
+          <Icon icon="ic:baseline-restart-alt"></Icon>
           {{ getResetBtnOptions.text }}
         </Button>
-        <slot name="submitBefore"></slot>
-
-        <Button type="primary" class="mr-2" v-bind="getSubmitBtnOptions" @click="submitAction" v-if="showSubmitButton">
-          {{ getSubmitBtnOptions.text }}
-        </Button>
+       <!-- update-end-author:zyf    Date:20211213  for:调换按钮前后位置-->
 
         <slot name="advanceBefore"></slot>
         <Button type="link" size="small" @click="toggleAdvanced" v-if="showAdvancedButton && !hideAdvanceBtn">

+ 59 - 53
src/components/Form/src/components/FormItem.vue

@@ -1,19 +1,19 @@
 <script lang="tsx">
-    import type { PropType, Ref } from 'vue';
-    import type { FormActionType, FormProps } from '../types/form';
-    import type { FormSchema } from '../types/form';
-    import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
-    import type { TableActionType } from '/@/components/Table';
-    import { defineComponent, computed, unref, toRefs } from 'vue';
-    import { Form, Col, Divider } from 'ant-design-vue';
-    import { componentMap } from '../componentMap';
-    import { BasicHelp } from '/@/components/Basic';
-    import { isBoolean, isFunction, isNull } from '/@/utils/is';
-    import { getSlot } from '/@/utils/helper/tsxHelper';
-    import { createPlaceholderMessage, setComponentRuleType } from '../helper';
-    import { upperFirst, cloneDeep } from 'lodash-es';
-    import { useItemLabelWidth } from '../hooks/useLabelWidth';
-    import { useI18n } from '/@/hooks/web/useI18n';
+    import type {PropType, Ref} from 'vue';
+    import type {FormActionType, FormProps} from '../types/form';
+    import type {FormSchema} from '../types/form';
+    import type {ValidationRule} from 'ant-design-vue/lib/form/Form';
+    import type {TableActionType} from '/@/components/Table';
+    import {defineComponent, computed, unref, toRefs} from 'vue';
+    import {Form, Col, Divider} from 'ant-design-vue';
+    import {componentMap} from '../componentMap';
+    import {BasicHelp} from '/@/components/Basic';
+    import {isBoolean, isFunction, isNull} from '/@/utils/is';
+    import {getSlot} from '/@/utils/helper/tsxHelper';
+    import {createPlaceholderMessage, setComponentRuleType} from '../helper';
+    import {upperFirst, cloneDeep} from 'lodash-es';
+    import {useItemLabelWidth} from '../hooks/useLabelWidth';
+    import {useI18n} from '/@/hooks/web/useI18n';
 
     export default defineComponent({
         name: 'BasicFormItem',
@@ -46,10 +46,10 @@
                 type: Object as PropType<FormActionType>,
             },
         },
-        setup(props, { slots }) {
-            const { t } = useI18n();
+        setup(props, {slots}) {
+            const {t} = useI18n();
 
-            const { schema, formProps } = toRefs(props) as {
+            const {schema, formProps} = toRefs(props) as {
                 schema: Ref<FormSchema>;
                 formProps: Ref<FormProps>;
             };
@@ -57,8 +57,8 @@
             const itemLabelWidthProp = useItemLabelWidth(schema, formProps);
 
             const getValues = computed(() => {
-                const { allDefaultValues, formModel, schema } = props;
-                const { mergeDynamicData } = props.formProps;
+                const {allDefaultValues, formModel, schema} = props;
+                const {mergeDynamicData} = props.formProps;
                 return {
                     field: schema.field,
                     model: formModel,
@@ -72,13 +72,13 @@
             });
 
             const getComponentsProps = computed(() => {
-                const { schema, tableAction, formModel, formActionType } = props;
-                let { componentProps = {} } = schema;
+                const {schema, tableAction, formModel, formActionType} = props;
+                let {componentProps = {}} = schema;
                 if (isFunction(componentProps)) {
-                    componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
+                    componentProps = componentProps({schema, tableAction, formModel, formActionType}) ?? {};
                 }
                 if (schema.component === 'Divider') {
-                    componentProps = Object.assign({ type: 'horizontal' }, componentProps, {
+                    componentProps = Object.assign({type: 'horizontal'}, componentProps, {
                         orientation: 'left',
                         plain: true,
                     });
@@ -87,9 +87,9 @@
             });
 
             const getDisable = computed(() => {
-                const { disabled: globDisabled } = props.formProps;
-                const { dynamicDisabled } = props.schema;
-                const { disabled: itemDisabled = false } = unref(getComponentsProps);
+                const {disabled: globDisabled} = props.formProps;
+                const {dynamicDisabled} = props.schema;
+                const {disabled: itemDisabled = false} = unref(getComponentsProps);
                 let disabled = !!globDisabled || itemDisabled;
                 if (isBoolean(dynamicDisabled)) {
                     disabled = dynamicDisabled;
@@ -101,8 +101,8 @@
             });
 
             function getShow(): { isShow: boolean; isIfShow: boolean } {
-                const { show, ifShow } = props.schema;
-                const { showAdvancedButton } = props.formProps;
+                const {show, ifShow} = props.schema;
+                const {showAdvancedButton} = props.formProps;
                 const itemIsAdvanced = showAdvancedButton
                     ? isBoolean(props.schema.isAdvanced)
                         ? props.schema.isAdvanced
@@ -125,7 +125,7 @@
                     isIfShow = ifShow(unref(getValues));
                 }
                 isShow = isShow && itemIsAdvanced;
-                return { isShow, isIfShow };
+                return {isShow, isIfShow};
             }
 
             function handleRules(): ValidationRule[] {
@@ -143,7 +143,7 @@
                 }
 
                 let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
-                const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;
+                const {rulesMessageJoinLabel: globalRulesMessageJoinLabel} = props.formProps;
 
                 const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel')
                     ? rulesMessageJoinLabel
@@ -179,7 +179,7 @@
                 const getRequired = isFunction(required) ? required(unref(getValues)) : required;
 
                 if ((!rules || rules.length === 0) && getRequired) {
-                    rules = [{ required: getRequired, validator }];
+                    rules = [{required: getRequired, validator}];
                 }
 
                 const requiredRuleIndex: number = rules.findIndex(
@@ -188,7 +188,7 @@
 
                 if (requiredRuleIndex !== -1) {
                     const rule = rules[requiredRuleIndex];
-                    const { isShow } = getShow();
+                    const {isShow} = getShow();
                     if (!isShow) {
                         rule.required = false;
                     }
@@ -243,7 +243,7 @@
                 };
                 const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
 
-                const { autoSetPlaceHolder, size } = props.formProps;
+                const {autoSetPlaceHolder, size} = props.formProps;
                 const propsData: Recordable = {
                     allowClear: true,
                     getPopupContainer: (trigger: Element) => trigger.parentNode,
@@ -252,14 +252,16 @@
                     disabled: unref(getDisable),
                 };
 
-                const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
-                // RangePicker place is an array
-                if (isCreatePlaceholder && component !== 'RangePicker' && component) {
-                    propsData.placeholder =
-                        unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component);
-                }
-                propsData.codeField = field;
-                propsData.formValues = unref(getValues);
+        const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
+        // RangePicker place是一个数组
+        if (isCreatePlaceholder && component !== 'RangePicker' && component) {
+          //自动设置placeholder
+          propsData.placeholder =
+            unref(getComponentsProps)?.placeholder ||
+            createPlaceholderMessage(component) + props.schema.label;
+        }
+        propsData.codeField = field;
+        propsData.formValues = unref(getValues);
 
                 const bindValue: Recordable = {
                     [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
@@ -275,15 +277,19 @@
                     return <Comp {...compAttr} />;
                 }
                 const compSlot = isFunction(renderComponentContent)
-                    ? { ...renderComponentContent(unref(getValues)) }
+                    ? {...renderComponentContent(unref(getValues))}
                     : {
                         default: () => renderComponentContent,
                     };
                 return <Comp {...compAttr}>{compSlot}</Comp>;
             }
 
+            /**
+             *渲染Label
+             * @updateBy:zyf
+             */
             function renderLabelHelpMessage() {
-                const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
+                const {label, helpMessage, helpComponentProps, subLabel} = props.schema;
                 const renderLabel = subLabel ? (
                     <span>
             {label} <span class="text-secondary">{subLabel}</span>
@@ -306,9 +312,9 @@
             }
 
             function renderItem() {
-                const { itemProps, slot, render, field, suffix, component } = props.schema;
-                const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
-                const { colon } = props.formProps;
+                const {itemProps, slot, render, field, suffix, component} = props.schema;
+                const {labelCol, wrapperCol} = unref(itemLabelWidthProp);
+                const {colon} = props.formProps;
 
                 if (component === 'Divider') {
                     return (
@@ -332,7 +338,7 @@
                         <Form.Item
                             name={field}
                             colon={colon}
-                            class={{ 'suffix-item': showSuffix }}
+                            class={{'suffix-item': showSuffix}}
                             {...(itemProps as Recordable)}
                             label={renderLabelHelpMessage()}
                             rules={handleRules()}
@@ -340,7 +346,7 @@
                             wrapperCol={wrapperCol}
                         >
                             <div style="display:flex">
-                                <div style="flex:1">{getContent()}</div>
+                                <div style="flex:1;">{getContent()}</div>
                                 {showSuffix && <span class="suffix">{getSuffix}</span>}
                             </div>
                         </Form.Item>
@@ -349,14 +355,14 @@
             }
 
             return () => {
-                const { colProps = {}, colSlot, renderColContent, component } = props.schema;
+                const {colProps = {}, colSlot, renderColContent, component} = props.schema;
                 if (!componentMap.has(component)) {
                     return null;
                 }
 
-                const { baseColProps = {} } = props.formProps;
-                const realColProps = { ...baseColProps, ...colProps };
-                const { isIfShow, isShow } = getShow();
+                const {baseColProps = {}} = props.formProps;
+                const realColProps = {...baseColProps, ...colProps};
+                const {isIfShow, isShow} = getShow();
                 const values = unref(getValues);
 
                 const getContent = () => {

+ 24 - 8
src/components/Form/src/hooks/useAdvanced.ts

@@ -61,7 +61,7 @@ export default function ({
       { immediate: true }
   );
 
-  function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) {
+  function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false, index = 0) {
     const width = unref(realWidthRef);
 
     const mdWidth =
@@ -84,27 +84,41 @@ export default function ({
       itemColSum += xxlWidth;
     }
 
+    let autoAdvancedCol = (unref(getProps).autoAdvancedCol ?? 3)
+
     if (isLastAction) {
-      advanceState.hideAdvanceBtn = false;
-      if (itemColSum <= BASIC_COL_LEN * 2) {
-        // When less than or equal to 2 lines, the collapse and expand buttons are not displayed
+      advanceState.hideAdvanceBtn = unref(getSchema).length <= autoAdvancedCol;
+      // update-begin--author:sunjianlei---date:20211108---for: 注释掉该逻辑,使小于等于2行时,也显示展开收起按钮
+      /* if (itemColSum <= BASIC_COL_LEN * 2) {
+        // 小于等于2行时,不显示折叠和展开按钮
         advanceState.hideAdvanceBtn = true;
         advanceState.isAdvanced = true;
-      } else if (
+      } else */
+      // update-end--author:sunjianlei---date:20211108---for: 注释掉该逻辑,使小于等于2行时,也显示展开收起按钮
+      if (
           itemColSum > BASIC_COL_LEN * 2 &&
           itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)
       ) {
         advanceState.hideAdvanceBtn = false;
 
-        // More than 3 lines collapsed by default
+        // 默认超过 3 行折叠
       } else if (!advanceState.isLoad) {
         advanceState.isLoad = true;
         advanceState.isAdvanced = !advanceState.isAdvanced;
+        // update-begin--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol,就默认折叠
+        if (unref(getSchema).length > autoAdvancedCol) {
+          advanceState.hideAdvanceBtn = false
+          advanceState.isAdvanced = false
+        }
+        // update-end--author:sunjianlei---date:20211108---for: 如果总列数大于 autoAdvancedCol,就默认折叠
       }
       return { isAdvanced: advanceState.isAdvanced, itemColSum };
     }
     if (itemColSum > BASIC_COL_LEN * (unref(getProps).alwaysShowLines || 1)) {
       return { isAdvanced: advanceState.isAdvanced, itemColSum };
+    } else if (!advanceState.isAdvanced && (index + 1) > autoAdvancedCol) {
+      // 如果当前是收起状态,并且当前列下标 > autoAdvancedCol,就隐藏
+      return { isAdvanced: false, itemColSum }
     } else {
       // The first line is always displayed
       return { isAdvanced: true, itemColSum };
@@ -116,7 +130,9 @@ export default function ({
     let realItemColSum = 0;
     const { baseColProps = {} } = unref(getProps);
 
-    for (const schema of unref(getSchema)) {
+    const schemas  = unref(getSchema)
+    for (let i = 0; i < schemas.length; i++) {
+      const schema = schemas[i]
       const { show, colProps } = schema;
       let isShow = true;
 
@@ -139,7 +155,7 @@ export default function ({
       if (isShow && (colProps || baseColProps)) {
         const { itemColSum: sum, isAdvanced } = getAdvanced(
             { ...baseColProps, ...colProps },
-            itemColSum
+            itemColSum, false, i,
         );
 
         itemColSum = sum || 0;

+ 115 - 96
src/components/Form/src/hooks/useForm.ts

@@ -1,117 +1,136 @@
 import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
 import type { NamePath } from 'ant-design-vue/lib/form/interface';
 import type { DynamicProps } from '/#/utils';
+import { handleRangeValue } from '../utils/formUtils';
 import { ref, onUnmounted, unref, nextTick, watch } from 'vue';
 import { isProdMode } from '/@/utils/env';
 import { error } from '/@/utils/log';
-import { getDynamicProps } from '/@/utils';
+import { getDynamicProps, getValueType } from '/@/utils';
 
 export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
 
 type Props = Partial<DynamicProps<FormProps>>;
 
 export function useForm(props?: Props): UseFormReturnType {
-  const formRef = ref<Nullable<FormActionType>>(null);
-  const loadedRef = ref<Nullable<boolean>>(false);
-
-  async function getForm() {
-    const form = unref(formRef);
-    if (!form) {
-      error(
-        'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!'
-      );
+    const formRef = ref<Nullable<FormActionType>>(null);
+    const loadedRef = ref<Nullable<boolean>>(false);
+
+    async function getForm() {
+        const form = unref(formRef);
+        if (!form) {
+            error(
+                'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!'
+            );
+        }
+        await nextTick();
+        return form as FormActionType;
     }
-    await nextTick();
-    return form as FormActionType;
-  }
-
-  function register(instance: FormActionType) {
-    isProdMode() &&
-      onUnmounted(() => {
-        formRef.value = null;
-        loadedRef.value = null;
-      });
-    if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return;
-
-    formRef.value = instance;
-    loadedRef.value = true;
-
-    watch(
-      () => props,
-      () => {
-        props && instance.setProps(getDynamicProps(props));
-      },
-      {
-        immediate: true,
-        deep: true,
-      }
-    );
-  }
-
-  const methods: FormActionType = {
-    scrollToField: async (name: NamePath, options?: ScrollOptions | undefined) => {
-      const form = await getForm();
-      form.scrollToField(name, options);
-    },
-    setProps: async (formProps: Partial<FormProps>) => {
-      const form = await getForm();
-      form.setProps(formProps);
-    },
-
-    updateSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
-      const form = await getForm();
-      form.updateSchema(data);
-    },
-
-    resetSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
-      const form = await getForm();
-      form.resetSchema(data);
-    },
-
-    clearValidate: async (name?: string | string[]) => {
-      const form = await getForm();
-      form.clearValidate(name);
-    },
-
-    resetFields: async () => {
-      getForm().then(async (form) => {
-        await form.resetFields();
-      });
-    },
-
-    removeSchemaByFiled: async (field: string | string[]) => {
-      unref(formRef)?.removeSchemaByFiled(field);
-    },
-
-    // TODO promisify
-    getFieldsValue: <T>() => {
-      return unref(formRef)?.getFieldsValue() as T;
-    },
-
-    setFieldsValue: async <T>(values: T) => {
-      const form = await getForm();
-      form.setFieldsValue<T>(values);
-    },
 
-    appendSchemaByField: async (
-      schema: FormSchema,
-      prefixField: string | undefined,
-      first: boolean
-    ) => {
-      const form = await getForm();
-      form.appendSchemaByField(schema, prefixField, first);
-    },
-
-    submit: async (): Promise<any> => {
-      const form = await getForm();
-      return form.submit();
-    },
+    function register(instance: FormActionType) {
+        isProdMode() &&
+        onUnmounted(() => {
+            formRef.value = null;
+            loadedRef.value = null;
+        });
+        if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return;
+
+        formRef.value = instance;
+        loadedRef.value = true;
+
+        watch(
+            () => props,
+            () => {
+                props && instance.setProps(getDynamicProps(props));
+            },
+            {
+                immediate: true,
+                deep: true,
+            }
+        );
+    }
 
+    const methods: FormActionType = {
+        scrollToField: async (name: NamePath, options?: ScrollOptions | undefined) => {
+            const form = await getForm();
+            form.scrollToField(name, options);
+        },
+        setProps: async (formProps: Partial<FormProps>) => {
+            const form = await getForm();
+            form.setProps(formProps);
+        },
+
+        updateSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
+            const form = await getForm();
+            form.updateSchema(data);
+        },
+
+        resetSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
+            const form = await getForm();
+            form.resetSchema(data);
+        },
+
+        clearValidate: async (name?: string | string[]) => {
+            const form = await getForm();
+            form.clearValidate(name);
+        },
+
+        resetFields: async () => {
+            getForm().then(async (form) => {
+                await form.resetFields();
+            });
+        },
+
+        removeSchemaByFiled: async (field: string | string[]) => {
+            unref(formRef)?.removeSchemaByFiled(field);
+        },
+
+        // TODO promisify
+        getFieldsValue: <T>() => {
+            return unref(formRef)?.getFieldsValue() as T;
+        },
+
+        setFieldsValue: async <T>(values: T) => {
+            const form = await getForm();
+            form.setFieldsValue<T>(values);
+        },
+
+        appendSchemaByField: async (
+            schema: FormSchema,
+            prefixField: string | undefined,
+            first: boolean
+        ) => {
+            const form = await getForm();
+            form.appendSchemaByField(schema, prefixField, first);
+        },
+
+        submit: async (): Promise<any> => {
+            const form = await getForm();
+            return form.submit();
+        },
+
+    /**
+     * 表单验证并返回表单值
+     * @update:添加表单值转换逻辑
+     * @updateBy:zyf
+     * @updateDate:2021-09-02
+     */
     validate: async (nameList?: NamePath[]): Promise<Recordable> => {
       const form = await getForm();
-      return form.validate(nameList);
+      let values = form.validate(nameList).then((values) => {
+        for (let key in values) {
+          if (values[key] instanceof Array) {
+            let valueType = getValueType(props, key);
+            if (valueType === 'string') {
+              values[key] = values[key].join(',');
+            }
+          }
+        }
+       //--@updateBy-begin----author:liusq---date:20210916------for:处理区域事件字典信息------
+        return handleRangeValue(props,values);
+       //--@updateBy-end----author:liusq---date:20210916------for:处理区域事件字典信息------
+      });
+      return values;
     },
-
     validateFields: async (nameList?: NamePath[]): Promise<Recordable> => {
       const form = await getForm();
       return form.validateFields(nameList);

+ 12 - 1
src/components/Form/src/hooks/useFormEvents.ts

@@ -3,7 +3,7 @@ import type { FormProps, FormSchema, FormActionType } from '../types/form';
 import type { NamePath } from 'ant-design-vue/lib/form/interface';
 import { unref, toRaw } from 'vue';
 import { isArray, isFunction, isObject, isString } from '/@/utils/is';
-import { deepMerge } from '/@/utils';
+import { deepMerge, getValueType } from '/@/utils';
 import { dateItemType, handleInputNumberValue } from '../helper';
 import { dateUtil } from '/@/utils/dateUtil';
 import { cloneDeep, uniqBy } from 'lodash-es';
@@ -240,6 +240,17 @@ export function useFormEvents({
         if (!formEl) return;
         try {
             const values = await validate();
+            //update-begin---author:zhangdaihao   Date:20140212  for:[bug号]树机构调整------------
+            //--updateBy-begin----author:zyf---date:20211206------for:对查询表单提交的数组处理成字符串------
+            for (let key in values) {
+              if (values[key] instanceof Array) {
+                let valueType = getValueType(getProps, key);
+                if (valueType === 'string') {
+                  values[key] = values[key].join(',');
+                }
+              }
+            }
+            //--updateBy-end----author:zyf---date:20211206------for:对查询表单提交的数组处理成字符串------
             const res = handleFormValues(values);
             emit('submit', res);
         } catch (error) {

+ 2 - 25
src/components/Form/src/hooks/useFormValues.ts

@@ -4,6 +4,7 @@ import { unref } from 'vue';
 import type { Ref, ComputedRef } from 'vue';
 import type { FormProps, FormSchema } from '../types/form';
 import { set } from 'lodash-es';
+import { handleRangeValue } from '/@/components/Form/src/utils/formUtils';
 
 interface UseFormValuesContext {
   defaultValueRef: Ref<any>;
@@ -42,33 +43,9 @@ export function useFormValues({
       }
       set(res, key, value);
     }
-    return handleRangeTimeValue(res);
+    return handleRangeValue(getProps,res);
   }
 
-  /**
-   * @description: Processing time interval parameters
-   */
-  function handleRangeTimeValue(values: Recordable) {
-    const fieldMapToTime = unref(getProps).fieldMapToTime;
-
-    if (!fieldMapToTime || !Array.isArray(fieldMapToTime)) {
-      return values;
-    }
-
-    for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) {
-      if (!field || !startTimeKey || !endTimeKey || !values[field]) {
-        continue;
-      }
-
-      const [startTime, endTime]: string[] = values[field];
-
-      values[startTimeKey] = dateUtil(startTime).format(format);
-      values[endTimeKey] = dateUtil(endTime).format(format);
-      Reflect.deleteProperty(values, field);
-    }
-
-    return values;
-  }
 
   function initDefault() {
     const schemas = unref(getSchema);

+ 7 - 1
src/components/Form/src/hooks/useLabelWidth.ts

@@ -16,8 +16,14 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
       wrapperCol: globWrapperCol,
     } = unref(propsRef);
 
+    // update-begin--author:sunjianlei---date:20211104---for: 禁用全局 labelWidth,不自动设置 textAlign --------
+    if (disabledLabelWidth) {
+      return { labelCol, wrapperCol }
+    }
+    // update-begin--author:sunjianlei---date:20211104---for: 禁用全局 labelWidth,不自动设置 textAlign --------
+
     // If labelWidth is set globally, all items setting
-    if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
+    if ((!globalLabelWidth && !labelWidth && !globalLabelCol)) {
       labelCol.style = {
         textAlign: 'left',
       };

+ 119 - 0
src/components/Form/src/jeecg/components/JAddInput.vue

@@ -0,0 +1,119 @@
+<template>
+    <div v-for="(param, index) in dynamicInput.params" :key="index" style="display: flex">
+        <a-input placeholder="请输入参数key" v-model:value="param.label" style="width: 30%;margin-bottom: 5px" @input="emitChange"/>
+        <a-input placeholder="请输入参数value" v-model:value="param.value" style="width: 30%;margin: 0 0 5px 5px" @input="emitChange"/>
+        <MinusCircleOutlined
+                v-if="dynamicInput.params.length > 1"
+                class="dynamic-delete-button"
+                @click="remove(param)"
+                style="width: 50px"
+        ></MinusCircleOutlined>
+    </div>
+    <div>
+        <a-button type="dashed" style="width: 60%" @click="add">
+            <PlusOutlined/>
+            新增
+        </a-button>
+    </div>
+</template>
+<script lang="ts">
+    import {MinusCircleOutlined, PlusOutlined} from '@ant-design/icons-vue';
+    import {defineComponent, reactive, ref, UnwrapRef, watchEffect} from 'vue';
+    import {propTypes} from '/@/utils/propTypes';
+    import { isEmpty } from '/@/utils/is'
+    import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
+    interface Params {
+        label: string;
+        value: string;
+    }
+
+    export default defineComponent({
+        name: 'JAddInput',
+        props: {
+            value: propTypes.string.def('')
+        },
+        emits: ['change', 'update:value'],
+        setup(props, {emit}) {
+            //input动态数据
+            const dynamicInput: UnwrapRef<{ params: Params[] }> = reactive({params: []});
+            //删除Input
+            const remove = (item: Params) => {
+                let index = dynamicInput.params.indexOf(item);
+                if (index !== -1) {
+                    dynamicInput.params.splice(index, 1);
+                }
+                emitChange()
+            };
+            //新增Input
+            const add = () => {
+                dynamicInput.params.push({
+                    label: '',
+                    value: '',
+                });
+                emitChange()
+            };
+
+            //监听传入数据value
+            watchEffect(() => {
+                initVal();
+            });
+
+            /**
+             * 初始化数值
+             */
+            function initVal() {
+                console.log("props.value",props.value)
+                dynamicInput.params = [];
+                if(props.value && props.value.indexOf("{")==0){
+                    let jsonObj = JSON.parse(props.value);
+                    Object.keys(jsonObj).forEach((key) => {
+                        dynamicInput.params.push({label: key, value: jsonObj[key]});
+                    });
+                }
+            }
+            /**
+             * 数值改变
+             */
+            function emitChange() {
+                let obj = {};
+                if (dynamicInput.params.length > 0) {
+                    dynamicInput.params.forEach(item => {
+                       obj[item['label']] = item['value']
+                    })
+                }
+                emit("change", isEmpty(obj)?'': JSON.stringify(obj));
+                emit("update:value",isEmpty(obj)?'': JSON.stringify(obj))
+            }
+            
+            return {
+                dynamicInput,
+                emitChange,
+                remove,
+                add,
+            };
+        },
+        components: {
+            MinusCircleOutlined,
+            PlusOutlined,
+        },
+    });
+</script>
+<style scoped>
+    .dynamic-delete-button {
+        cursor: pointer;
+        position: relative;
+        top: 4px;
+        font-size: 24px;
+        color: #999;
+        transition: all 0.3s;
+    }
+
+    .dynamic-delete-button:hover {
+        color: #777;
+    }
+
+    .dynamic-delete-button[disabled] {
+        cursor: not-allowed;
+        opacity: 0.5;
+    }
+</style>

+ 1 - 1
src/components/Form/src/components/JAreaLinkage.vue → src/components/Form/src/jeecg/components/JAreaLinkage.vue

@@ -4,7 +4,7 @@
 <script lang="ts">
     import {defineComponent, PropType, ref,reactive, watchEffect, computed, unref, watch, onMounted} from 'vue';
     import {Cascader} from 'ant-design-vue';
-    import {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus, CodeToText, TextToCode} from "../utils/areaDataUtil";
+    import {provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus, CodeToText, TextToCode} from "../../utils/areaDataUtil";
     import {useRuleFormItem} from "/@/hooks/component/useFormItem";
     import {propTypes} from "/@/utils/propTypes";
     import {useAttrs} from "/@/hooks/core/useAttrs";

+ 153 - 0
src/components/Form/src/jeecg/components/JAreaSelect.vue

@@ -0,0 +1,153 @@
+<template>
+    <div class="area-select">
+        <!--省份-->
+        <a-select v-model:value="province" @change="proChange" allowClear>
+            <template v-for="item in provinceOptions" :key="`${item.value}`">
+                <a-select-option :value="item.value">{{ item.label }}</a-select-option>
+            </template>
+        </a-select>
+        <!--城市-->
+        <a-select v-if="level>=2" v-model:value="city" @change="cityChange">
+            <template v-for="item in cityOptions" :key="`${item.value}`">
+                <a-select-option :value="item.value">{{ item.label }}</a-select-option>
+            </template>
+        </a-select>
+        <!--地区-->
+        <a-select v-if="level>=3" v-model:value="area" @change="areaChange">
+            <template v-for="item in areaOptions" :key="`${item.value}`">
+                <a-select-option :value="item.value">{{ item.label }}</a-select-option>
+            </template>
+        </a-select>
+    </div>
+</template>
+<script lang="ts">
+    import {defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted, onUnmounted, toRefs} from 'vue';
+    import {propTypes} from "/@/utils/propTypes";
+    import {useRuleFormItem} from '/@/hooks/component/useFormItem';
+    import {provinceOptions, getDataByCode, getRealCode} from "../../utils/areaDataUtil";
+
+    export default defineComponent({
+        name: 'JAreaSelect',
+        props: {
+            value: [Array, String],
+            province: [String],
+            city: [String],
+            area: [String],
+            level: propTypes.number.def(3),
+        },
+        emits: ['change','update:value'],
+        setup(props, {emit, refs}) {
+            const emitData = ref<any[]>([]);
+            //下拉框的选择值
+            const pca = reactive({
+                province: '',
+                city: '',
+                area: '',
+            });
+            //表单值
+            const [state] = useRuleFormItem(props, 'value', 'change', emitData);
+            //城市下拉框的选项
+            const cityOptions = computed(() => {
+                return pca.province ? getDataByCode(pca.province) : [];
+            });
+            //地区下拉框的选项
+            const areaOptions = computed(() => {
+                return pca.city ? getDataByCode(pca.city) : [];
+            });
+            /**
+             * 监听props值
+             */
+            watchEffect(() => {
+                props && initValue();
+            });
+
+            /**
+             * 监听组件值变化
+             */
+            watch(pca, (newVal) => {
+                if(!props.value){
+                    emit("update:province",pca.province);
+                    emit("update:city",pca.city);
+                    emit("update:area",pca.area);
+                }
+            });
+            /**
+             * 数据初始化
+             */
+            function initValue() {
+                if (props.value) {
+                    //传参是数组的情况下的处理
+                    if (Array.isArray(props.value)) {
+                        pca.province = props.value[0];
+                        pca.city = props.value[1] ? props.value[1] : '';
+                        pca.area = props.value[2] ? props.value[2] : '';
+                    } else {
+                        //传参是数值
+                        let valueArr = getRealCode(props.value, props.level);
+                        if (valueArr) {
+                            pca.province = valueArr[0];
+                            pca.city = props.level >= 2 && valueArr[1] ? valueArr[1] : '';
+                            pca.area = props.level >= 3 && valueArr[2] ? valueArr[2] : '';
+                        }
+                    }
+                }else{
+                    //绑定三个数据的情况
+                    pca.province = props.province?props.province:'';
+                    pca.city = props.city?props.city:'';
+                    pca.area = props.area?props.area:'';
+                }
+            }
+
+            /**
+             * 省份change事件
+             */
+            function proChange(val) {
+                pca.city = (val && getDataByCode(val)[0]?.value);
+                pca.area = (pca.city && getDataByCode(pca.city)[0]?.value);
+                state.value = props.level <= 1 ? val : (props.level <= 2 ? pca.city : pca.area);
+                emit("update:value",unref(state));
+            }
+
+            /**
+             * 城市change事件
+             */
+            function cityChange(val) {
+                pca.area = (val && getDataByCode(val)[0]?.value);
+                state.value = props.level <= 2 ? val : pca.area;
+                emit("update:value",unref(state));
+            }
+
+            /**
+             * 区域change事件
+             */
+            function areaChange(val) {
+                state.value = val;
+                emit("update:value",unref(state));
+            }
+
+            return {
+                ...toRefs(pca),
+                provinceOptions,
+                cityOptions,
+                areaOptions,
+                proChange,
+                cityChange,
+                areaChange
+            };
+        },
+    });
+</script>
+<style lang="less" scoped>
+    .area-select {
+        width: 100%;
+        display: flex;
+
+        .ant-select{
+            width: 33.3%;
+        }
+        
+        .ant-select:not(:first-child) {
+            margin-left: 10px;
+        }
+    }
+</style>

+ 256 - 0
src/components/Form/src/jeecg/components/JCategorySelect.vue

@@ -0,0 +1,256 @@
+<!--下拉树-->
+<template>
+    <a-tree-select
+            allowClear
+            labelInValue
+            style="width: 100%"
+            :disabled="disabled"
+            :dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
+            :placeholder="placeholder"
+            :loadData="asyncLoadTreeData"
+            :value="treeValue"
+            :treeData="treeData"
+            :multiple="multiple"
+            @change="onChange">
+    </a-tree-select>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, unref, watch } from 'vue';
+  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
+  import { propTypes } from '/@/utils/propTypes';
+  import { useAttrs } from '/@/hooks/core/useAttrs';
+  import { loadDictItem, loadTreeData } from '/@/api/common/api';
+  import { useMessage } from '/@/hooks/web/useMessage';
+
+  const { createMessage, createErrorModal } = useMessage();
+  export default defineComponent({
+    name: 'JCategorySelect',
+    components: {},
+    inheritAttrs: false,
+    props: {
+      value: propTypes.oneOfType([
+        propTypes.string,
+        propTypes.array,
+      ]),
+      placeholder: {
+        type: String,
+        default: '请选择',
+        required: false,
+      },
+      disabled: {
+        type: Boolean,
+        default: false,
+        required: false,
+      },
+      condition: {
+        type: String,
+        default: '',
+        required: false,
+      },
+      // 是否支持多选
+      multiple: {
+        type: [Boolean,String],
+        default: false,
+      },
+      loadTriggleChange: {
+        type: Boolean,
+        default: false,
+        required: false,
+      },
+      pid: {
+        type: String,
+        default: '',
+        required: false,
+      },
+      pcode: {
+        type: String,
+        default: '',
+        required: false,
+      },
+      back: {
+        type: String,
+        default: '',
+        required: false,
+      },
+    },
+    emits: ['options-change', 'change'],
+    setup(props, { emit, refs }) {
+      console.info(props);
+      const emitData = ref<any[]>([]);
+      const treeData = ref<any[]>([]);
+      const treeValue = ref('');
+      const attrs = useAttrs();
+      const [state] = useRuleFormItem(props, 'value', 'change', emitData);
+      watch(
+        () => props.value,
+        () => {
+          loadItemByCode();
+        },
+        { deep: true },
+      );
+      watch(
+        () => props.pcode,
+        () => {
+          loadRoot();
+        },
+        { deep: true, immediate: true },
+      );
+
+      function loadRoot() {
+        let param = {
+          pid: props.pid,
+          pcode: !props.pcode ? '0' : props.pcode,
+          condition: props.condition,
+        };
+        console.info(param);
+        loadTreeData(param).then(res => {
+          for (let i of res) {
+            i.value = i.key;
+            if (i.leaf == false) {
+              i.isLeaf = false;
+            } else if (i.leaf == true) {
+              i.isLeaf = true;
+            }
+          }
+          treeData.value = res;
+        });
+
+      }
+
+
+      function loadItemByCode() {
+        if (!props.value || props.value == '0') {
+          treeValue.value = [];
+        } else {
+          loadDictItem({ ids: props.value }).then(res => {
+            let values = props.value.split(',');
+            treeValue.value = res.map((item, index) => ({
+              key: values[index],
+              value: values[index],
+              label: item,
+            }));
+            onLoadTriggleChange(res[0]);
+          });
+        }
+      }
+
+      function onLoadTriggleChange(text) {
+        //只有单选才会触发
+        if (!props.multiple && props.loadTriggleChange) {
+          backValue(props.value, text);
+        }
+
+      }
+
+      function backValue(value, label) {
+        let obj = {};
+        if (props.back) {
+          obj[props.back] = label;
+        }
+        emit('change', value, obj);
+      }
+
+      function asyncLoadTreeData(treeNode) {
+        let dataRef = treeNode.dataRef;
+        return new Promise((resolve) => {
+          if (treeNode.children.length > 0) {
+            resolve();
+            return;
+          }
+          let pid = dataRef.key;
+          console.info(treeNode);
+          let param = {
+            pid: pid,
+            condition: props.condition,
+          };
+          loadTreeData(param).then(res => {
+            if (res) {
+              for (let i of res) {
+                i.value = i.key;
+                if (i.leaf == false) {
+                  i.isLeaf = false;
+                } else if (i.leaf == true) {
+                  i.isLeaf = true;
+                }
+              }
+              addChildren(pid, res, treeData.value);
+              resolve();
+            }
+          });
+        });
+      }
+
+      function addChildren(pid, children, treeArray) {
+        console.info('treeArray', treeArray);
+        if (treeArray && treeArray.length > 0) {
+          for (let item of treeArray) {
+            if (item.key == pid) {
+              if (!children || children.length == 0) {
+                item.isLeaf = true;
+              } else {
+                item.children = children;
+              }
+              break;
+            } else {
+              addChildren(pid, children, item.children);
+            }
+          }
+        }
+      }
+
+      function onChange(value) {
+        if (!value) {
+          emit('change', '');
+          treeValue.value = '';
+        } else if (Array.isArray(value)) {
+          let labels = [];
+          let values = value.map(item => {
+            labels.push(item.label);
+            return item.value;
+          });
+          backValue(values.join(','), labels.join(','));
+          treeValue.value = value;
+        } else {
+          backValue(value.value, value.label);
+          treeValue.value = value;
+        }
+      }
+
+      function getCurrTreeData() {
+        return treeData;
+      }
+
+      function validateProp() {
+        let mycondition = props.condition;
+        return new Promise((resolve, reject) => {
+          if (!mycondition) {
+            resolve();
+          } else {
+            try {
+              let test = JSON.parse(mycondition);
+              if (typeof test == 'object' && test) {
+                resolve();
+              } else {
+                createMessage.error('组件JTreeSelect-condition传值有误,需要一个json字符串!');
+                reject();
+              }
+            } catch (e) {
+              createMessage.error('组件JTreeSelect-condition传值有误,需要一个json字符串!');
+              reject();
+            }
+          }
+        });
+      }
+
+      return {
+        state,
+        attrs,
+        onChange,
+        treeData,
+        treeValue,
+        asyncLoadTreeData,
+      };
+    },
+  })
+  ;
+</script>

+ 78 - 0
src/components/Form/src/jeecg/components/JCheckbox.vue

@@ -0,0 +1,78 @@
+<template>
+    <a-checkbox-group v-bind="attrs" v-model:value="checkboxArray" :options="checkOptions" @change="handleChange"></a-checkbox-group>
+</template>
+
+<script lang="ts">
+    import {defineComponent, computed, watch, watchEffect, ref, unref} from 'vue';
+    import {propTypes} from "/@/utils/propTypes";
+    import {useAttrs} from '/@/hooks/core/useAttrs';
+    import {initDictOptions} from "/@/utils/dict/index"
+
+    export default defineComponent({
+        name: 'JCheckbox',
+        props: {
+            value: propTypes.string,
+            dictCode: propTypes.string,
+            options: {
+                type: Array,
+                default: () => [],
+            },
+        },
+        emits: ['change', 'update:value'],
+        setup(props, {emit}) {
+            const attrs = useAttrs();
+            //checkbox选项
+            const checkOptions = ref<any[]>([]);
+            //checkbox数值
+            const checkboxArray = ref<any[]>([]);
+            /**
+             * 监听value
+             */
+            watchEffect(() => {
+                props.value && (checkboxArray.value = props.value ? props.value.split(",") : []);
+            });
+            /**
+             * 监听字典code
+             */
+            watchEffect(() => {
+                props && initOptions();
+            });
+
+            /**
+             * 初始化选项
+             */
+            async function initOptions() {
+                //根据options, 初始化选项
+                if (props.options && props.options.length > 0) {
+                    checkOptions.value = props.options;
+                    return;
+                }
+                //根据字典Code, 初始化选项
+                if(props.dictCode){
+                    const dictData = await initDictOptions(props.dictCode);
+                    checkOptions.value = dictData.reduce((prev, next) => {
+                        if (next) {
+                            const value = next['value'];
+                            prev.push({
+                                label: next['text'],
+                                value: value,
+                            });
+                        }
+                        return prev;
+                    }, []);
+                }
+            }
+
+            /**
+             * change事件
+             * @param $event
+             */
+            function handleChange($event) {
+                emit('update:value', $event.join(','));
+                emit('change', $event.join(','));
+            }
+
+            return {checkboxArray, checkOptions, attrs, handleChange};
+        },
+    });
+</script>

+ 246 - 0
src/components/Form/src/jeecg/components/JCodeEditor.vue

@@ -0,0 +1,246 @@
+<template>
+    <div v-bind="boxBindProps">
+        <!-- 全屏按钮 -->
+        <a-icon v-if="fullScreen" class="full-screen-icon" :type="fullScreenIcon" @click="onToggleFullScreen"/>
+        <textarea ref="textarea" v-bind="getBindValue"></textarea>
+    </div>
+</template>
+
+<script lang="ts">
+  import {defineComponent, onMounted, reactive, ref, watch, unref, computed} from 'vue';
+  import { propTypes } from '/@/utils/propTypes';
+  import { useRuleFormItem } from '/@/hooks/component/useFormItem';
+  // 引入全局实例
+  import _CodeMirror, { EditorFromTextArea } from 'codemirror'
+  // 核心样式
+  import 'codemirror/lib/codemirror.css';
+  // 引入主题后还需要在 options 中指定主题才会生效
+  import 'codemirror/theme/cobalt.css';
+  // 需要引入具体的语法高亮库才会有对应的语法高亮效果
+  import 'codemirror/mode/javascript/javascript.js';
+  import 'codemirror/mode/css/css.js';
+  import 'codemirror/mode/xml/xml.js';
+  import 'codemirror/mode/clike/clike.js';
+  import 'codemirror/mode/markdown/markdown.js';
+  import 'codemirror/mode/python/python.js';
+  import 'codemirror/mode/r/r.js';
+  import 'codemirror/mode/shell/shell.js';
+  import 'codemirror/mode/sql/sql.js';
+  import 'codemirror/mode/swift/swift.js';
+  import 'codemirror/mode/vue/vue.js';
+  // 折叠资源引入:开始
+  import "codemirror/addon/fold/foldgutter.css";
+  import "codemirror/addon/fold/foldcode.js";
+  import "codemirror/addon/fold/brace-fold.js";
+  import "codemirror/addon/fold/comment-fold.js";
+  import "codemirror/addon/fold/indent-fold.js";
+  import "codemirror/addon/fold/foldgutter.js";
+  // 折叠资源引入:结束
+  //光标行背景高亮,配置里面也需要styleActiveLine设置为true
+  import "codemirror/addon/selection/active-line.js";
+  // 支持代码自动补全
+  import "codemirror/addon/hint/show-hint.css";
+  import "codemirror/addon/hint/show-hint.js";
+  import "codemirror/addon/hint/anyword-hint.js";
+  import { useAttrs } from '/@/hooks/core/useAttrs';
+  import {useDesign} from '/@/hooks/web/useDesign'
+
+  export default defineComponent({
+    name: 'JCodeEditor',
+    // 不将 attrs 的属性绑定到 html 标签上
+    inheritAttrs: false,
+    components: {},
+    props: {
+      value: propTypes.string.def(''),
+      height:propTypes.string.def('auto'),
+      disabled: propTypes.bool.def(false),
+      // 是否显示全屏按钮
+      fullScreen: propTypes.bool.def(false),
+      // 全屏以后的z-index
+      zIndex: propTypes.any.def(999),
+    },
+    emits: ['change', 'update:value'],
+    setup(props, { emit }) {
+      const { prefixCls } = useDesign('code-editer');
+      const CodeMirror = window.CodeMirror || _CodeMirror;
+      const emitData = ref<object>();
+      //表单值
+      const [state] = useRuleFormItem(props, 'value', 'change', emitData);
+      const textarea = ref<HTMLTextAreaElement>();
+      let coder: Nullable<EditorFromTextArea> = null
+      const attrs = useAttrs();
+      const height =ref(props.height);
+      const options = reactive({
+        // 缩进格式
+        tabSize: 2,
+        // 主题,对应主题库 JS 需要提前引入
+        theme: 'cobalt',
+        smartIndent: true, // 是否智能缩进  
+        // 显示行号
+        lineNumbers: true,
+        line: true,
+        // 启用代码折叠相关功能:开始
+        foldGutter: true,
+        lineWrapping: true,
+        gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
+        // 启用代码折叠相关功能:结束
+        // 光标行高亮  
+        styleActiveLine: true,
+      });
+      let innerValue = ''
+      // 全屏状态
+      const isFullScreen = ref(false)
+      const fullScreenIcon = computed(() => isFullScreen.value ? 'fullscreen-exit' : 'fullscreen')
+      // 外部盒子参数
+      const boxBindProps = computed(() => {
+        let _props = {
+          class: [
+            prefixCls, 'full-screen-parent', 'auto-height',
+            {
+              'full-screen': isFullScreen.value,
+            },
+          ],
+          style: {},
+        }
+        if (isFullScreen.value) {
+          _props.style['z-index'] = props.zIndex
+        }
+        return _props
+      })
+      /**
+       * 监听组件值
+       */
+      watch(() => props.value, () => {
+        if (innerValue != props.value) {
+          setValue(props.value, false);
+        }
+      });
+      onMounted(() => {
+        initialize();
+      });
+
+      /**
+       * 组件赋值
+       * @param value
+       * @param trigger 是否触发 change 事件
+       */
+      function setValue(value: string, trigger = true) {
+        coder?.setValue(value ?? '')
+        innerValue = value
+        trigger && emitChange(innerValue)
+      }
+
+      //编辑器值修改事件
+      function onChange(obj) {
+        innerValue = obj.getValue() ?? '';
+        if (props.value != innerValue) {
+          emitChange(innerValue)
+        }
+      }
+
+      function emitChange(value) {
+        emit('change', value);
+        emit('update:value', value);
+      }
+
+      //组件初始化
+      function initialize() {
+        coder = CodeMirror.fromTextArea(textarea.value!, options);
+        //绑定值修改事件
+        coder.on('change', onChange);
+        // 初始化成功时赋值一次
+        setValue(innerValue, false)
+      }
+
+      // 切换全屏状态
+      function onToggleFullScreen() {
+        isFullScreen.value = !isFullScreen.value
+      }
+
+      const getBindValue = Object.assign({}, unref(props), unref(attrs));
+      return {
+        state,
+        textarea,
+        boxBindProps,
+        getBindValue,
+        setValue,
+        isFullScreen,
+        fullScreenIcon,
+        onToggleFullScreen,
+      };
+    },
+  });
+</script>
+
+<style lang="less">
+//noinspection LessUnresolvedVariable
+@prefix-cls: ~'@{namespace}-code-editer';
+.@{prefix-cls} {
+
+  &.auto-height {
+    .CodeMirror {
+      height: v-bind(height) !important;
+      min-height: 100px;
+    }
+  }
+
+  /* 全屏样式 */
+
+  &.full-screen-parent {
+    position: relative;
+
+    .full-screen-icon {
+      opacity: 0;
+      color: black;
+      width: 20px;
+      height: 20px;
+      line-height: 24px;
+      background-color: white;
+      position: absolute;
+      top: 2px;
+      right: 2px;
+      z-index: 9;
+      cursor: pointer;
+      transition: opacity 0.3s;
+      padding: 2px 0 0 1.5px;
+    }
+
+    &:hover {
+      .full-screen-icon {
+        opacity: 1;
+
+        &:hover {
+          background-color: rgba(255, 255, 255, 0.88);
+        }
+      }
+    }
+
+    &.full-screen {
+      position: fixed;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+      padding: 8px;
+      background-color: #f5f5f5;
+
+      .full-screen-icon {
+        top: 12px;
+        right: 12px;
+      }
+
+      .full-screen-child,
+      .CodeMirror {
+        height: 100%;
+        max-height: 100%;
+        min-height: 100%;
+      }
+    }
+
+    .full-screen-child {
+      height: 100%;
+    }
+
+  }
+}
+</style>

+ 107 - 0
src/components/Form/src/jeecg/components/JDictSelectTag.vue

@@ -0,0 +1,107 @@
+<template>
+    <a-radio-group v-if="compType===CompTypeEnum.Radio" v-bind="attrs" v-model:value="state" @change="handleChange">
+        <template v-for="item in dictOptions" :key="`${item.value}`">
+            <a-radio :value="item.value">
+                {{ item.label }}
+            </a-radio>
+        </template>
+    </a-radio-group>
+
+    <a-radio-group v-else-if="compType===CompTypeEnum.RadioButton" v-bind="attrs" v-model:value="state" buttonStyle="solid" @change="handleChange">
+        <template v-for="item in dictOptions" :key="`${item.value}`">
+            <a-radio-button :value="item.value">
+                {{ item.label }}
+            </a-radio-button>
+        </template>
+    </a-radio-group>
+
+    <a-select v-else-if="compType===CompTypeEnum.Select" :placeholder="placeholder" v-bind="attrs" v-model:value="state" @change="handleChange">
+        <a-select-option v-if="showChooseOption" :value="undefined">请选择</a-select-option>
+        <template v-for="item in dictOptions" :key="`${item.value}`">
+            <a-select-option :value="item.value">
+          <span style="display: inline-block;width: 100%" :title=" item.label ">
+            {{ item.label }}
+          </span>
+            </a-select-option>
+        </template>
+    </a-select>
+</template>
+<script lang="ts">
+    import {defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted} from 'vue';
+    import {propTypes} from "/@/utils/propTypes";
+    import {useAttrs} from "/@/hooks/core/useAttrs";
+    import {initDictOptions} from "/@/utils/dict/index"
+    import {get, omit} from 'lodash-es';
+    import {useRuleFormItem} from '/@/hooks/component/useFormItem';
+    import {CompTypeEnum} from '/@/enums/CompTypeEnum.ts';
+    export default defineComponent({
+        name: 'JDictSelectTag',
+        inheritAttrs: false,
+        props: {
+            value: propTypes.oneOfType([
+                propTypes.string,
+                propTypes.number,
+                propTypes.array,
+            ]),
+            dictCode: propTypes.string,
+            type: propTypes.string,
+            placeholder: propTypes.string,
+            stringToNumber: propTypes.bool,
+            getPopupContainer: {
+                type: Function,
+                default: (node) => node.parentNode
+            },
+          // 是否显示【请选择】选项
+          showChooseOption: propTypes.bool.def(true),
+        },
+        emits: ['options-change', 'change'],
+        setup(props, {emit, refs}) {
+            const emitData = ref<any[]>([]);
+            const dictOptions = ref<any[]>([]);
+            const attrs = useAttrs();
+            const [state] = useRuleFormItem(props, 'value', 'change', emitData);
+            const getBindValue = Object.assign({}, unref(props), unref(attrs));
+            //组件类型
+            const compType = computed(() => {
+                return (!props.type || props.type === "list") ? 'select' : props.type;
+            });
+            /**
+             * 监听字典code
+             */
+            watchEffect(() => {
+                props.dictCode && initDictData();
+            });
+
+            async function initDictData() {
+                let {dictCode, stringToNumber} = props;
+                //根据字典Code, 初始化字典数组
+                const dictData = await initDictOptions(dictCode);
+                dictOptions.value = dictData.reduce((prev, next) => {
+                    if (next) {
+                        const value = next['value'];
+                        prev.push({
+                            label: next['text'] || next['label'],
+                            value: stringToNumber ? +value : value,
+                            ...omit(next, ['text', 'value']),
+                        });
+                    }
+                    return prev;
+                }, []);
+            }
+
+          function handleChange(e) {
+            emitData.value = [e?.target?.value || e];
+          }
+
+            return {
+                state,
+                compType,
+                attrs,
+                getBindValue,
+                dictOptions,
+                CompTypeEnum,
+                handleChange
+            };
+        },
+    });
+</script>

+ 305 - 0
src/components/Form/src/jeecg/components/JEasyCron/EasyCronInner.vue

@@ -0,0 +1,305 @@
+<template>
+  <div :class="`${prefixCls}`">
+    <div class="content">
+      <a-tabs :size="`small`" v-model:activeKey="activeKey">
+        <a-tab-pane tab="秒" key="second" v-if="!hideSecond">
+          <SecondUI v-model:value="second" :disabled="disabled"/>
+        </a-tab-pane>
+        <a-tab-pane tab="分" key="minute">
+          <MinuteUI v-model:value="minute" :disabled="disabled"/>
+        </a-tab-pane>
+        <a-tab-pane tab="时" key="hour">
+          <HourUI v-model:value="hour" :disabled="disabled"/>
+        </a-tab-pane>
+        <a-tab-pane tab="日" key="day">
+          <DayUI v-model:value="day" :week="week" :disabled="disabled"/>
+        </a-tab-pane>
+        <a-tab-pane tab="月" key="month">
+          <MonthUI v-model:value="month" :disabled="disabled"/>
+        </a-tab-pane>
+        <a-tab-pane tab="周" key="week">
+          <WeekUI v-model:value="week" :day="day" :disabled="disabled"/>
+        </a-tab-pane>
+        <a-tab-pane tab="年" key="year" v-if="!hideYear && !hideSecond">
+          <YearUI v-model:value="year" :disabled="disabled"/>
+        </a-tab-pane>
+      </a-tabs>
+      <a-divider/>
+      <!-- 执行时间预览 -->
+      <a-row :gutter="8">
+        <a-col :span="18" style="margin-top: 22px;">
+          <a-row :gutter="8">
+            <a-col :span="8" style="margin-bottom: 12px;">
+              <a-input v-model:value="inputValues.second" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='second'">秒</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="8" style="margin-bottom: 12px;">
+              <a-input v-model:value="inputValues.minute" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='minute'">分</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="8" style="margin-bottom: 12px;">
+              <a-input v-model:value="inputValues.hour" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='hour'">时</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="8" style="margin-bottom: 12px;">
+              <a-input v-model:value="inputValues.day" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='day'">日</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="8" style="margin-bottom: 12px;">
+              <a-input v-model:value="inputValues.month" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='month'">月</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="8" style="margin-bottom: 12px;">
+              <a-input v-model:value="inputValues.week" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='week'">周</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="8">
+              <a-input v-model:value="inputValues.year" @blur="onInputBlur">
+                <template #addonBefore>
+                  <span class="allow-click" @click="activeKey='year'">年</span>
+                </template>
+              </a-input>
+            </a-col>
+            <a-col :span="16">
+              <a-input v-model:value="inputValues.cron" @blur="onInputCronBlur">
+                <template #addonBefore>
+                  <a-tooltip title="Cron表达式">式</a-tooltip>
+                </template>
+              </a-input>
+            </a-col>
+          </a-row>
+        </a-col>
+        <a-col :span="6">
+          <div>近十次执行时间(不含年)</div>
+          <a-textarea type="textarea" :value="preTimeList" :rows="5"/>
+        </a-col>
+      </a-row>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive, ref, watch, provide } from 'vue'
+import { useDesign } from '/@/hooks/web/useDesign'
+import CronParser from 'cron-parser'
+import SecondUI from './tabs/SecondUI.vue'
+import MinuteUI from './tabs/MinuteUI.vue'
+import HourUI from './tabs/HourUI.vue'
+import DayUI from './tabs/DayUI.vue'
+import MonthUI from './tabs/MonthUI.vue'
+import WeekUI from './tabs/WeekUI.vue'
+import YearUI from './tabs/YearUI.vue'
+import { cronEmits, cronProps } from './easy.cron.data'
+import { dateFormat, simpleDebounce } from '/@/utils/common/compUtils'
+
+const { prefixCls } = useDesign('easy-cron-inner')
+provide('prefixCls', prefixCls)
+const emit = defineEmits([...cronEmits])
+const props = defineProps({ ...cronProps })
+const activeKey = ref(props.hideSecond ? 'minute' : 'second')
+const second = ref('*')
+const minute = ref('*')
+const hour = ref('*')
+const day = ref('*')
+const month = ref('*')
+const week = ref('?')
+const year = ref('*')
+const inputValues = reactive({ second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: '' })
+const preTimeList = ref('执行预览,会忽略年份参数。')
+
+// cron表达式
+const cronValueInner = computed(() => {
+  let result: string[] = []
+  if (!props.hideSecond) {
+    result.push(second.value ? second.value : '*')
+  }
+  result.push(minute.value ? minute.value : '*')
+  result.push(hour.value ? hour.value : '*')
+  result.push(day.value ? day.value : '*')
+  result.push(month.value ? month.value : '*')
+  result.push(week.value ? week.value : '?')
+  if (!props.hideYear && !props.hideSecond) result.push(year.value ? year.value : '*')
+  return result.join(' ')
+})
+// 不含年
+const cronValueNoYear = computed(() => {
+  const v = cronValueInner.value
+  if (props.hideYear || props.hideSecond) return v
+  const vs = v.split(' ')
+  if (vs.length >= 6) {
+    // 转成 Quartz 的规则
+    vs[5] = convertWeekToQuartz(vs[5])
+  }
+  return vs.slice(0, vs.length - 1).join(' ')
+})
+const calTriggerList = simpleDebounce(calTriggerListInner, 500)
+
+watch(() => props.value, (newVal) => {
+  if (newVal === cronValueInner.value) {
+    return
+  }
+  formatValue()
+})
+
+
+watch(cronValueInner, (newValue) => {
+  calTriggerList()
+  emitValue(newValue)
+  assignInput()
+})
+
+// watch(minute, () => {
+//   if (second.value === '*') {
+//     second.value = '0'
+//   }
+// })
+// watch(hour, () => {
+//   if (minute.value === '*') {
+//     minute.value = '0'
+//   }
+// })
+// watch(day, () => {
+//   if (day.value !== '?' && hour.value === '*') {
+//     hour.value = '0'
+//   }
+// })
+// watch(week, () => {
+//   if (week.value !== '?' && hour.value === '*') {
+//     hour.value = '0'
+//   }
+// })
+// watch(month, () => {
+//   if (day.value === '?' && week.value === '*') {
+//     week.value = '1'
+//   } else if (week.value === '?' && day.value === '*') {
+//     day.value = '1'
+//   }
+// })
+// watch(year, () => {
+//   if (month.value === '*') {
+//     month.value = '1'
+//   }
+// })
+
+assignInput()
+formatValue()
+calTriggerListInner()
+
+function assignInput() {
+  inputValues.second = second.value
+  inputValues.minute = minute.value
+  inputValues.hour = hour.value
+  inputValues.day = day.value
+  inputValues.month = month.value
+  inputValues.week = week.value
+  inputValues.year = year.value
+  inputValues.cron = cronValueInner.value
+}
+
+function formatValue() {
+  if (!props.value) return
+  const values = props.value.split(' ').filter(item => !!item)
+  if (!values || values.length <= 0) return
+  let i = 0
+  if (!props.hideSecond) second.value = values[i++]
+  if (values.length > i) minute.value = values[i++]
+  if (values.length > i) hour.value = values[i++]
+  if (values.length > i) day.value = values[i++]
+  if (values.length > i) month.value = values[i++]
+  if (values.length > i) week.value = values[i++]
+  if (values.length > i) year.value = values[i]
+  assignInput()
+}
+
+// Quartz 的规则:
+// 1 = 周日,2 = 周一,3 = 周二,4 = 周三,5 = 周四,6 = 周五,7 = 周六
+function convertWeekToQuartz(week: string) {
+  let convert = (v: string) => {
+    if (v === '0') {
+      return '1'
+    }
+    if (v === '1') {
+      return '0'
+    }
+    return (Number.parseInt(v) - 1).toString()
+  }
+  // 匹配示例 1-7 or 1/7
+  let patten1 = /^([0-7])([-/])([0-7])$/
+  // 匹配示例 1,4,7
+  let patten2 = /^([0-7])(,[0-7])+$/
+  if (/^[0-7]$/.test(week)) {
+    return convert(week)
+  } else if (patten1.test(week)) {
+    return week.replace(patten1, ($0, before, separator, after) => {
+      if (separator === '/') {
+        return convert(before) + separator + after
+      } else {
+        return convert(before) + separator + convert(after)
+      }
+    })
+  } else if (patten2.test(week)) {
+    return week.split(',').map(v => convert(v)).join(',')
+  }
+  return week
+}
+
+function calTriggerListInner() {
+  // 设置了回调函数
+  if (props.remote) {
+    props.remote(cronValueInner.value, +new Date(), v => {
+      preTimeList.value = v
+    })
+    return
+  }
+  const format = 'yyyy-MM-dd hh:mm:ss'
+  const options = {
+    currentDate: dateFormat(new Date(), format),
+  }
+  const iter = CronParser.parseExpression(cronValueNoYear.value, options)
+  const result: string[] = []
+  for (let i = 1; i <= 10; i++) {
+    result.push(dateFormat(new Date(iter.next() as any), format))
+  }
+  preTimeList.value = result.length > 0 ? result.join('\n') : '无执行时间'
+}
+
+function onInputBlur() {
+  second.value = inputValues.second
+  minute.value = inputValues.minute
+  hour.value = inputValues.hour
+  day.value = inputValues.day
+  month.value = inputValues.month
+  week.value = inputValues.week
+  year.value = inputValues.year
+}
+
+function onInputCronBlur(event) {
+  emitValue(event.target.value)
+}
+
+function emitValue(value) {
+  emit('change', value)
+  emit('update:value', value)
+}
+</script>
+<style lang="less">
+@import "easy.cron.inner";
+</style>

+ 63 - 0
src/components/Form/src/jeecg/components/JEasyCron/EasyCronInput.vue

@@ -0,0 +1,63 @@
+<template>
+  <div :class="`${prefixCls}`">
+    <a-input :placeholder="placeholder" v-model:value="editCronValue" :disabled="disabled">
+      <template #addonAfter>
+        <a class="open-btn" :disabled="disabled?'disabled':null" @click="showConfigModal">
+          <Icon icon="ant-design:setting-outlined"/>
+          <span>选择</span>
+        </a>
+      </template>
+    </a-input>
+    <EasyCronModal
+        @register="registerModal"
+        v-model:value="editCronValue"
+        :exeStartTime="exeStartTime"
+        :hideYear="hideYear"
+        :remote="remote"
+        :hideSecond="hideSecond"/>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+import { useDesign } from '/@/hooks/web/useDesign'
+import { useModal } from '/@/components/Modal'
+import { propTypes } from '/@/utils/propTypes'
+import Icon from '/@/components/Icon/src/Icon.vue'
+import EasyCronModal from './EasyCronModal.vue'
+import { cronEmits, cronProps } from './easy.cron.data'
+
+const { prefixCls } = useDesign('easy-cron-input')
+const emit = defineEmits([...cronEmits])
+const props = defineProps({
+  ...cronProps,
+  placeholder: propTypes.string.def('请输入cron表达式'),
+  exeStartTime: propTypes.oneOfType([
+    propTypes.number,
+    propTypes.string,
+    propTypes.object,
+  ]).def(0),
+})
+const [registerModal, { openModal }] = useModal()
+const editCronValue = ref(props.value)
+
+watch(() => props.value, (newVal) => {
+  if (newVal !== editCronValue.value) {
+    editCronValue.value = newVal
+  }
+})
+watch(editCronValue, (newVal) => {
+  emit('change', newVal)
+  emit('update:value', newVal)
+})
+
+function showConfigModal() {
+  if (!props.disabled) {
+    openModal()
+  }
+}
+</script>
+
+<style lang="less">
+@import "easy.cron.input";
+</style>

Some files were not shown because too many files changed in this diff