Ver Fonte

Merge remote-tracking branch 'origin/dev_1' into dev_1

# Conflicts:
#	AI_AGENT.md
#	ai-frontend/src/ai/components/ModelFormModal.vue
#	ai-frontend/src/ai/components/blocks/EChartsBlock.vue
#	ai-frontend/src/ai/components/blocks/GraphBlock.vue
#	ai-frontend/src/ai/styles/ai.less
#	ai-frontend/src/ai/utils/useAiChart.ts
#	ai-frontend/src/ai/views/aiAnalysis/index.vue
#	ai-frontend/src/ai/views/aiModel/index.vue
#	ai-frontend/src/case/views/data/cleanProgress.vue
#	ai-frontend/src/case/views/data/cleaning.vue
#	ai-frontend/src/case/views/data/index.vue
#	ai-frontend/src/core/layouts/default/setting/components/ProductInfoModal.vue
#	ai-frontend/src/core/router/routes/index.ts
#	ai-frontend/src/core/store/modules/case.ts
#	ai-frontend/src/plat/views/authorization/index.vue
#	ai-frontend/src/plat/views/case/case.vue
#	ai-frontend/src/trans/views/aiBaseStation/index.vue
#	ai-frontend/uno.config.ts
#	ai-server/src/main/java/com/zsjz/ai/common/config/LicenseFilter.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/rag/EmbeddingModelFactory.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentModelService.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/service/AgentService.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentChatServiceImpl.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentModelServiceImpl.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/tools/GraphRenderTool.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/tools/PythonAnalysisTool.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/tools/RagSchemaSearchTool.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/tools/SqlAnalysisTool.java
#	ai-server/src/main/java/com/zsjz/ai/module/agent/tools/WorkspaceInfoTool.java
#	ai-server/src/main/java/com/zsjz/ai/module/govern/serivce/GovernService.java
cc há 3 semanas atrás
pai
commit
d20e279002

+ 78 - 0
ai-frontend/build/config/appConfig.ts

@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import { PackageJson } from 'pkg-types';
+import { getEnvConfig } from './index';
+import { getEnvConfigName } from './getEnvConfigName';
+import { version } from '../../package.json';
+import colors from 'picocolors';
+
+const PLUGIN_NAME = 'app-config';
+const APP_CONFIG_FILE_NAME = '_app.config.js';
+
+export function appConfigPlugin(isBuild: boolean, viteEnv: ViteEnv): PluginOption {
+  if (!isBuild) {
+    return {
+      name: PLUGIN_NAME,
+    };
+  }
+
+  let publicPath: string;
+  let appConfigContent: string;
+
+  return {
+    name: PLUGIN_NAME,
+    async configResolved(_config) {
+      publicPath = _config.base;
+      appConfigContent = await getConfigContent(viteEnv);
+    },
+    async transformIndexHtml(html) {
+      publicPath = (publicPath.endsWith('/') ? publicPath : `${publicPath}/`) || '/';
+      const src = `${publicPath}${APP_CONFIG_FILE_NAME}?v=${version}-${new Date().getTime()}`;
+      return { html, tags: [{ tag: 'script', attrs: { src } }] };
+    },
+    async generateBundle() {
+      try {
+        this.emitFile({
+          type: 'asset',
+          fileName: APP_CONFIG_FILE_NAME,
+          source: appConfigContent,
+        });
+        this.emitFile({
+          type: 'asset',
+          fileName: 'timestamp.txt',
+          source: `${new Date().getTime()}`,
+        });
+        console.log(colors.cyan(`✨configuration file is build successfully!`));
+      } catch (error) {
+        console.log(colors.red('configuration file configuration file failed to package:\n' + error));
+      }
+    },
+  };
+}
+
+async function getConfigContent(env: ViteEnv) {
+  const config = await getEnvConfig();
+  const envConfigName = getEnvConfigName(env);
+  const windowVariable = `window.${envConfigName}`;
+  // Ensure that the variable will not be modified
+  let source = `${windowVariable}=${JSON.stringify(config)};`;
+  source += `
+    Object.freeze(${windowVariable});
+    Object.defineProperty(window, "${envConfigName}", {
+      configurable: false,
+      writable: false,
+    });
+    if(location.hostname!=='localhost'&&location.hostname!=='127.0.0.1'){
+    var _hmt = _hmt || []; (function() {
+      var hm = document.createElement("script");
+      hm.src = "https://hm.baidu.com/hm.js?65b88e88a94e0118de2962f328f17622";
+      var s = document.getElementsByTagName("script")[0]; 
+      s.parentNode.insertBefore(hm, s);
+    })();}
+  `.replace(/(?!var|\b)\s/g, '');
+  return source;
+}

+ 8 - 0
ai-frontend/build/config/getEnvConfigName.ts

@@ -0,0 +1,8 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+export const getEnvConfigName = (env: Record<string, any>) => {
+  return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
+};

+ 75 - 0
ai-frontend/build/config/index.ts

@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import fs from 'fs-extra';
+import dotenv from 'dotenv';
+import { join } from 'node:path';
+
+// Read all environment variable configuration files to process.env
+export function wrapperEnv(envConf: Recordable): ViteEnv {
+  const ret: any = {};
+
+  for (const envName of Object.keys(envConf)) {
+    let realName = envConf[envName].replace(/\\n/g, '\n');
+    realName = realName === 'true' ? true : realName === 'false' ? false : realName;
+
+    if (envName === 'VITE_PORT') {
+      realName = Number(realName);
+    }
+    if (envName === 'VITE_PROXY' && realName) {
+      try {
+        realName = JSON.parse(realName.replace(/'/g, '"'));
+      } catch (error) {
+        realName = '';
+      }
+    }
+    ret[envName] = realName;
+    if (typeof realName === 'string') {
+      process.env[envName] = realName;
+    } else if (typeof realName === 'object') {
+      process.env[envName] = JSON.stringify(realName);
+    }
+  }
+  return ret;
+}
+
+/**
+ * 获取当前环境下生效的配置文件名
+ */
+function getEnvConfigFiles() {
+  const script = process.env.npm_lifecycle_script;
+  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;
+    return ['.env', `.env.${mode}`];
+  }
+  return ['.env', '.env.production'];
+}
+
+/**
+ * Get the environment variables starting with the specified prefix
+ * @param match prefix
+ * @param confFiles ext
+ */
+export async function getEnvConfig(match = 'VITE_(GLOB|FILE)_', confFiles = getEnvConfigFiles()) {
+  let envConfig = {};
+  for (const item of confFiles) {
+    try {
+      const envPath = await fs.readFile(join(process.cwd(), item), { encoding: 'utf8' });
+      const env = dotenv.parse(envPath);
+      envConfig = { ...envConfig, ...env };
+    } catch (e) {
+      console.error(`Error in parsing ${item}`, e);
+    }
+  }
+  const reg = new RegExp(`^(${match})`);
+  Object.keys(envConfig).forEach((key) => {
+    if (!reg.test(key)) {
+      Reflect.deleteProperty(envConfig, key);
+    }
+  });
+  return envConfig;
+}

+ 8 - 0
ai-frontend/build/index.ts

@@ -0,0 +1,8 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+export * from './config';
+export * from './options';
+export * from './plugins';

+ 42 - 0
ai-frontend/build/options/build.ts

@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import type { BuildOptions } from 'vite';
+
+// 现代浏览器支持列表 https://cn.vitejs.dev/config/build-options.html#build-target
+export const viteTarget = ['chrome107', 'edge107', 'firefox104', 'safari16'];
+// 低版本浏览器支持列表,VITE_LEGACY 参数开启时有效 https://www.npmjs.com/package/@vitejs/plugin-legacy
+export const legacyTargets = ['chrome>=87', 'edge>=88', 'firefox>=78', 'safari>=14'];
+
+export function createBuildOptions(viteEnv: ViteEnv): BuildOptions {
+  const timestamp = new Date().getTime();
+  return {
+    target: viteTarget,
+    cssTarget: viteTarget,
+    outDir: viteEnv.VITE_OUTPUT_DIR ?? 'dist',
+    // 启用 terser 缩小器,当设置 terserOptions 时才会有效
+    // minify: 'terser',
+    // terserOptions: {
+    //   compress: {
+    //     keep_infinity: true,
+    //     drop_console: viteEnv.VITE_DROP_CONSOLE,
+    //   },
+    // },
+    // 禁用报告压缩块大小,可以稍微提高构建速度
+    reportCompressedSize: false,
+    chunkSizeWarningLimit: 9000,
+    rollupOptions: {
+      maxParallelFileOps: 50,
+      output: {
+        entryFileNames: `assets/[name]-[hash]-${timestamp}.js`,
+        experimentalMinChunkSize: 12288,
+        // manualChunks: {
+        //   vue: ['vue', 'vue-router'],
+        //   antd: ['ant-design-vue', '@ant-design/icons-vue'],
+        // },
+      },
+    },
+  };
+}

+ 18 - 0
ai-frontend/build/options/css.ts

@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import type { CSSOptions } from 'vite';
+import { generateModifyVars } from '../theme/modifyVars';
+
+export function createCSSOptions(): CSSOptions {
+  return {
+    preprocessorOptions: {
+      less: {
+        modifyVars: generateModifyVars(),
+        javascriptEnabled: true,
+      },
+    },
+  };
+}

+ 33 - 0
ai-frontend/build/options/define.ts

@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { PackageJson, readPackageJSON } from 'pkg-types';
+import dayjs from 'dayjs';
+
+export async function createDefineOptions(): Promise<Record<string, any>> {
+  const appPkg: PackageJson = (await readPackageJSON(process.cwd())) ?? {};
+  try {
+    const __APP_INFO__ = {
+      pkg: {
+        dependencies: Object.fromEntries(
+          Object.entries(appPkg.dependencies ?? {}).filter(([key]) => !key.endsWith('-lib')),
+        ),
+        devDependencies: Object.fromEntries(
+          Object.entries(appPkg.devDependencies ?? {}).filter(([key]) => !key.endsWith('-lib')),
+        ),
+        name: appPkg.name,
+        version: appPkg.version,
+      },
+      lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+    };
+    return {
+      // __INTLIFY_PROD_DEVTOOLS__: 'false',
+      __APP_INFO__: JSON.stringify(__APP_INFO__),
+    };
+  } catch (error) {
+    console.log('createDefine', error);
+    return {};
+  }
+}

+ 12 - 0
ai-frontend/build/options/esBuild.ts

@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import type { ESBuildOptions } from 'vite';
+
+export function createEsBuildOptions(viteEnv: ViteEnv): ESBuildOptions {
+  return {
+    drop: viteEnv.VITE_DROP_CONSOLE ? ['console', 'debugger'] : [],
+  };
+}

+ 10 - 0
ai-frontend/build/options/index.ts

@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+export * from './build';
+export * from './css';
+export * from './define';
+export * from './esBuild';
+export * from './server';

+ 52 - 0
ai-frontend/build/options/server.ts

@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import type { ServerOptions, ProxyOptions } from 'vite';
+import { IncomingMessage } from 'node:http';
+
+type ProxyItem = [string, string, boolean];
+type ProxyList = ProxyItem[];
+type ProxyTargetList = Record<string, ProxyOptions>;
+const httpsRE = /^https:\/\//;
+
+export function createServerOptions(viteEnv: ViteEnv): ServerOptions {
+  return {
+    https: false as any,
+    open: false,
+    host: true,
+    port: viteEnv.VITE_PORT,
+    // Electron dev startup checks a fixed URL, so do not let Vite silently hop ports.
+    strictPort: true,
+    proxy: createProxy(viteEnv.VITE_PROXY),
+    warmup: {
+      clientFiles: ['./index.html', './src/{views,components}/*'],
+    },
+  };
+}
+
+/**
+ * Used to parse the .env.development proxy configuration
+ */
+export function createProxy(list: ProxyList = []) {
+  const ret: ProxyTargetList = {};
+  for (const [prefix, target, changeOrigin] of list) {
+    const isHttps = httpsRE.test(target);
+    // https://github.com/http-party/node-http-proxy#options
+    ret[prefix] = {
+      target: target,
+      changeOrigin,
+      ws: true,
+      // https is require secure=false
+      ...(isHttps ? { secure: false } : {}),
+      rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
+      bypass: (req: IncomingMessage) => {
+        if (req.method === 'GET') {
+          req['url'] = req['originalUrl'];
+        }
+      },
+    };
+  }
+  return ret;
+}

+ 42 - 0
ai-frontend/build/plugins/compress.ts

@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import compressPlugin from 'vite-plugin-compression';
+
+/**
+ * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
+ * https://github.com/anncwb/vite-plugin-compression
+ */
+export function configCompressPlugin(isBuild: boolean, viteEnv: ViteEnv): PluginOption {
+  if (!isBuild) {
+    return [];
+  }
+  const plugins: PluginOption = [];
+  const compress: 'gzip' | 'brotli' | 'none' = viteEnv.VITE_BUILD_COMPRESS;
+  const deleteOriginFile = viteEnv.VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE || false;
+
+  const compressList = compress.split(',');
+
+  if (compressList.includes('gzip')) {
+    plugins.push(
+      compressPlugin({
+        ext: '.gz',
+        deleteOriginFile,
+      }),
+    );
+  }
+
+  if (compressList.includes('brotli')) {
+    plugins.push(
+      compressPlugin({
+        ext: '.br',
+        algorithm: 'brotliCompress',
+        deleteOriginFile,
+      }),
+    );
+  }
+  return plugins;
+}

+ 17 - 0
ai-frontend/build/plugins/html.ts

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import { createHtmlPlugin } from 'vite-plugin-html';
+
+/**
+ * Plugin to minimize and use ejs template syntax in index.html.
+ * https://github.com/anncwb/vite-plugin-html
+ */
+export function configHtmlPlugin(isBuild: boolean): PluginOption {
+  return createHtmlPlugin({
+    minify: isBuild,
+  });
+}

+ 66 - 0
ai-frontend/build/plugins/index.ts

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import vueSetupExtend from 'vite-plugin-vue-setup-extend';
+import vitePluginCertificate from 'vite-plugin-mkcert';
+import { appConfigPlugin } from '../config/appConfig';
+import { configCompressPlugin } from './compress';
+import { configHtmlPlugin } from './html';
+import { configLegacyPlugin } from './legacy';
+import { configThemePlugin } from '../theme';
+import { configUnoCSSPlugin } from './unocss';
+import { configMonacoEditorPlugin } from './monacoEditor';
+import { configVisualizerPlugin } from './visualizer';
+
+export function createVitePlugins(isBuild: boolean, viteEnv: ViteEnv) {
+  const vitePlugins: PluginOption[] = [
+    vue(),
+    vueJsx(),
+    vueSetupExtend(),
+    vitePluginCertificate({
+      source: 'coding',
+    }),
+  ];
+
+  // app-config-plugin
+  vitePlugins.push(appConfigPlugin(isBuild, viteEnv));
+
+  // UnoCSS-vite-plugin
+  vitePlugins.push(configUnoCSSPlugin());
+
+  // vite-plugin-html
+  vitePlugins.push(configHtmlPlugin(isBuild));
+
+  // vite-plugin-monaco-editor
+  vitePlugins.push(configMonacoEditorPlugin());
+
+  // rollup-plugin-visualizer
+  vitePlugins.push(configVisualizerPlugin());
+
+  // vite-plugin-theme-vite3
+  vitePlugins.push(configThemePlugin(isBuild));
+
+  // rollup-plugin-gzip
+  vitePlugins.push(configCompressPlugin(isBuild, viteEnv));
+
+  // @vitejs/plugin-legacy
+  vitePlugins.push(configLegacyPlugin(isBuild, viteEnv));
+
+  return vitePlugins;
+}
+
+export {
+  appConfigPlugin,
+  configCompressPlugin,
+  configHtmlPlugin,
+  configLegacyPlugin,
+  configThemePlugin,
+  configUnoCSSPlugin,
+  configMonacoEditorPlugin,
+  configVisualizerPlugin,
+};

+ 18 - 0
ai-frontend/build/plugins/legacy.ts

@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import legacy from '@vitejs/plugin-legacy';
+import { legacyTargets } from '../options/build';
+
+export function configLegacyPlugin(isBuild: boolean, viteEnv: ViteEnv): PluginOption {
+  if (!(isBuild && viteEnv.VITE_LEGACY)) {
+    return [];
+  }
+  return legacy({
+    targets: legacyTargets,
+    modernPolyfills: true,
+  });
+}

+ 313 - 0
ai-frontend/build/plugins/monacoEditor.ts

@@ -0,0 +1,313 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { HtmlTagDescriptor, Connect, ResolvedConfig, PluginOption } from 'vite';
+import fs from 'fs';
+import path from 'path';
+import esbuild from 'esbuild';
+
+export function configMonacoEditorPlugin(): PluginOption {
+  return monacoEditorPluginJeeSite({
+    languageWorkers: ['editorWorkerService', 'json', 'html'],
+    customDistPath: (root, buildOutDir) => `${buildOutDir}/monaco`,
+    publicPath: 'monaco',
+  });
+}
+
+// Copyright (c) 2021 vdesjs, thinkgem
+//
+// This file contains code derived from an MIT-licensed project.
+// The MIT License applies **only to the content of this file** and does not
+// extend to the rest of the project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// src/languageWork.ts
+
+interface IWorkerDefinition {
+  label: string;
+  entry: string;
+}
+
+const languageWorkAttr: IWorkerDefinition[] = [
+  {
+    label: 'editorWorkerService',
+    entry: 'monaco-editor/esm/vs/editor/editor.worker.js',
+  },
+  {
+    label: 'css',
+    entry: 'monaco-editor/esm/vs/language/css/css.worker.js',
+  },
+  {
+    label: 'html',
+    entry: 'monaco-editor/esm/vs/language/html/html.worker.js',
+  },
+  {
+    label: 'json',
+    entry: 'monaco-editor/esm/vs/language/json/json.worker.js',
+  },
+  {
+    label: 'typescript',
+    entry: 'monaco-editor/esm/vs/language/typescript/ts.worker.js',
+  },
+];
+
+const languageWorksByLabel: { [language: string]: IWorkerDefinition } = {};
+languageWorkAttr.forEach((languageWork) => (languageWorksByLabel[languageWork.label] = languageWork));
+
+type EditorLanguageWorks = 'css' | 'html' | 'json' | 'typescript' | 'editorWorkerService';
+
+// src/workerMiddleware.ts
+
+function getFilenameByEntry(entry: string) {
+  entry = path.basename(entry, '.js');
+  return entry + '.bundle.js';
+}
+
+const cacheDir = 'node_modules/.monaco/';
+
+function getWorkPath(works: IWorkerDefinition[], options: IMonacoEditorOpts, config: ResolvedConfig) {
+  const workerPaths: Record<string, any> = {};
+  for (const work of works) {
+    if (isCDN(options.publicPath ?? '')) {
+      workerPaths[work.label] = options.publicPath + '/' + getFilenameByEntry(work.entry);
+    } else {
+      workerPaths[work.label] = config.base + options.publicPath + '/' + getFilenameByEntry(work.entry);
+    }
+  }
+
+  if (workerPaths['typescript']) {
+    // javascript shares the same worker
+    workerPaths['javascript'] = workerPaths['typescript'];
+  }
+  if (workerPaths['css']) {
+    // scss and less share the same worker
+    workerPaths['less'] = workerPaths['css'];
+    workerPaths['scss'] = workerPaths['css'];
+  }
+  if (workerPaths['html']) {
+    // handlebars, razor and html share the same worker
+    workerPaths['handlebars'] = workerPaths['html'];
+    workerPaths['razor'] = workerPaths['html'];
+  }
+
+  return workerPaths;
+}
+
+function workerMiddleware(middlewares: Connect.Server, config: ResolvedConfig, options: IMonacoEditorOpts): void {
+  const works = getWorks(options);
+  if (!works) {
+    throw new Error('No work definition found.');
+  }
+  // clear cacheDir
+
+  if (fs.existsSync(cacheDir)) {
+    fs.rmSync(cacheDir, { recursive: true, force: true } as fs.RmDirOptions);
+  }
+
+  for (const work of works) {
+    middlewares.use(config.base + options.publicPath + '/' + getFilenameByEntry(work.entry), function (req, res, next) {
+      if (!fs.existsSync(cacheDir + getFilenameByEntry(work.entry))) {
+        esbuild.buildSync({
+          entryPoints: [resolveMonacoPath(work.entry)],
+          bundle: true,
+          outfile: cacheDir + getFilenameByEntry(work.entry),
+        });
+      }
+      const contentBuffer = fs.readFileSync(cacheDir + getFilenameByEntry(work.entry));
+      res.setHeader('Content-Type', 'text/javascript');
+      res.end(contentBuffer);
+    });
+  }
+}
+
+// src/index.ts
+
+/**
+ * Return a resolved path for a given Monaco file.
+ */
+function resolveMonacoPath(filePath: string): string {
+  try {
+    const fullFilePath = path.resolve(path.join(process.cwd(), 'node_modules', filePath));
+    if (fs.existsSync(fullFilePath)) {
+      return fullFilePath;
+    }
+    return filePath;
+  } catch (err) {
+    throw err;
+  }
+}
+
+function getWorks(options: IMonacoEditorOpts) {
+  let works: IWorkerDefinition[] | undefined = options.languageWorkers?.map((work) => languageWorksByLabel[work]);
+
+  works && options.customWorkers && works.push(...options.customWorkers);
+
+  return works;
+}
+
+interface IMonacoEditorOpts {
+  /**
+   * include only a subset of the languageWorkers supported.
+   */
+  languageWorkers?: EditorLanguageWorks[];
+
+  customWorkers?: IWorkerDefinition[];
+
+  /**
+   * Override the public path from which files generated by this plugin will be served.
+   * This wins out over Webpack's dynamic runtime path and can be useful to avoid attempting to load workers cross-
+   * origin when using a CDN for other static resources.
+   * Use e.g. '/' if you want to load your resources from the current origin.
+   */
+  publicPath?: string;
+
+  customDistPath?: (root: string, buildOutDir: string, base: string) => string;
+
+  forceBuildCDN?: boolean;
+
+  /**
+   * Specify whether the editor API should be exposed through a global `monaco` object or not. This
+   * option is applicable to `0.22.0` and newer version of `monaco-editor`. Since `0.22.0`, the ESM
+   * version of the monaco editor does no longer define a global `monaco` object unless
+   * `global.MonacoEnvironment = { globalAPI: true }` is set ([change
+   * log](https://github.com/microsoft/monaco-editor/blob/main/CHANGELOG.md#0220-29012021)).
+   */
+  globalAPI?: boolean;
+}
+
+function monacoEditorPluginJeeSite(options?: IMonacoEditorOpts): PluginOption {
+  const languageWorkers = options?.languageWorkers ?? (Object.keys(languageWorksByLabel) as EditorLanguageWorks[]);
+  const publicPath = options?.publicPath ?? 'monacoeditorwork';
+  const globalAPI = options?.globalAPI ?? false;
+  const customWorkers = options?.customWorkers ?? [];
+  const forceBuildCDN = options?.forceBuildCDN ?? false;
+
+  options = {
+    ...options,
+    languageWorkers,
+    publicPath,
+    globalAPI,
+    customWorkers,
+    forceBuildCDN,
+  };
+
+  let resolvedConfig: ResolvedConfig;
+
+  return {
+    name: 'vite-plugin-moncao-editor-jeesite',
+    configResolved(getResolvedConfig) {
+      resolvedConfig = getResolvedConfig;
+    },
+    configureServer(server) {
+      if (isCDN(publicPath)) {
+        return;
+      }
+
+      workerMiddleware(server.middlewares, resolvedConfig, options);
+    },
+    transformIndexHtml(html) {
+      const works = getWorks(options);
+      if (!works) {
+        throw new Error('No work definition found.');
+      }
+      const workerPaths = getWorkPath(works, options, resolvedConfig);
+
+      const globals = {
+        MonacoEnvironment: `(function (paths) {
+          return {
+            globalAPI: ${globalAPI},
+            getWorkerUrl : function (moduleId, label) {
+              var result =  paths[label];
+              if (/^((http:)|(https:)|(file:)|(\\/\\/))/.test(result)) {
+                var currentUrl = String(window.location);
+                var currentOrigin = currentUrl.substr(0, currentUrl.length - window.location.hash.length - window.location.search.length - window.location.pathname.length);
+                if (result.substring(0, currentOrigin.length) !== currentOrigin) {
+                  var js = '/*' + label + '*/importScripts("' + result + '");';
+                  var blob = new Blob([js], { type: 'application/javascript' });
+                  return URL.createObjectURL(blob);
+                }
+              }
+              return result;
+            }
+          };
+        })(${JSON.stringify(workerPaths, null, 2)})`,
+      } as Record<string, any>;
+
+      const descriptor: HtmlTagDescriptor[] = [
+        {
+          tag: 'script',
+          children: Object.keys(globals)
+            .map((key) => `self[${JSON.stringify(key)}] = ${globals[key]};`)
+            .join('\n'),
+          injectTo: 'head-prepend',
+        },
+      ];
+      return descriptor;
+    },
+
+    writeBundle() {
+      // 是cdn地址并且没有强制构建worker cdn则返回
+      if (isCDN(publicPath) && !forceBuildCDN) {
+        return;
+      }
+
+      const works = getWorks(options);
+
+      const distPath = options.customDistPath
+        ? options.customDistPath(resolvedConfig.root, resolvedConfig.build.outDir, resolvedConfig.base)
+        : path.join(resolvedConfig.root, resolvedConfig.build.outDir, resolvedConfig.base, options.publicPath ?? '');
+
+      //  console.log("distPath", distPath)
+
+      // write publicPath
+      if (!fs.existsSync(distPath)) {
+        fs.mkdirSync(distPath, {
+          recursive: true,
+        });
+      }
+
+      if (!works) {
+        throw new Error('No work definition found.');
+      }
+      for (const work of works) {
+        if (!fs.existsSync(cacheDir + getFilenameByEntry(work.entry))) {
+          const entryPath = resolveMonacoPath(work.entry);
+          esbuild.buildSync({
+            entryPoints: [entryPath],
+            bundle: true,
+            outfile: cacheDir + getFilenameByEntry(work.entry),
+          });
+        }
+        const contentBuffer = fs.readFileSync(cacheDir + getFilenameByEntry(work.entry));
+        const workDistPath = path.resolve(distPath, getFilenameByEntry(work.entry));
+        fs.writeFileSync(workDistPath, contentBuffer);
+      }
+    },
+  };
+}
+
+function isCDN(publicPath: string) {
+  if (/^((http:)|(https:)|(file:)|(\/\/))/.test(publicPath)) {
+    return true;
+  }
+  return false;
+}

+ 12 - 0
ai-frontend/build/plugins/unocss.ts

@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import UnoCSS from 'unocss/vite';
+import UnoCSSConfig from '../../uno.config';
+
+export function configUnoCSSPlugin(): PluginOption {
+  return UnoCSS(UnoCSSConfig);
+}

+ 23 - 0
ai-frontend/build/plugins/visualizer.ts

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import visualizer from 'rollup-plugin-visualizer';
+
+export function isReportMode(): boolean {
+  return process.env.REPORT === 'true';
+}
+
+export function configVisualizerPlugin(): PluginOption {
+  if (!isReportMode()) {
+    return [];
+  }
+  return visualizer({
+    filename: './node_modules/.cache/visualizer/stats.html',
+    open: true,
+    gzipSize: true,
+    brotliSize: true,
+  });
+}

+ 129 - 0
ai-frontend/build/theme/index.ts

@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { type PluginOption } from 'vite';
+import path from 'path';
+import { createRequire } from 'node:module';
+import { mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme-vite3/es/colorUtils';
+import { getThemeColors, generateColors } from './themeConfig';
+import { generateModifyVars } from './modifyVars';
+
+// 该插件的 ESM 产物(dist/index.js)内联了 CJS 依赖,依赖调用方存在全局 require,
+// 在原生 ESM 环境下会抛 “Dynamic require of "fs" is not supported”,故从这里加载其 CJS 产物。
+const requireCjs = createRequire(import.meta.url);
+const { viteThemePlugin, antdDarkThemePlugin } = requireCjs(
+  'vite-plugin-theme-vite3/dist/index.cjs',
+) as typeof import('vite-plugin-theme-vite3');
+
+/**
+ * Vite plugin for website theme color switching
+ * https://gitee.com/thinkgem/vite-plugin-theme-vite3
+ */
+export function configThemePlugin(isBuild: boolean): PluginOption[] {
+  const colors = generateColors({
+    mixDarken,
+    mixLighten,
+    tinycolor,
+  });
+  // console.log(
+  //   [...getThemeColors(), ...colors]
+  //     .map((e) => `<a style="background:${e}">${e}</a>`)
+  //     .join('&nbsp;'),
+  // );
+  // 抽取出viteThemePlugin插件,下方会根据不同环境设置enforce
+  const vite_theme_plugin = viteThemePlugin({
+    injectClientPath: JSON.stringify(path.resolve(__dirname, '../../node_modules/vite-plugin-theme-vite3/es/client')),
+    resolveSelector: (s) => {
+      s = s.trim();
+      if (s.includes('const __vite__css = "')) {
+        return s.replace(/(.*)const __vite__css = "/g, '');
+      }
+      return s;
+      // switch (s) {
+      //   case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
+      //     return '.ant-steps-item-icon > .ant-steps-icon';
+      //   case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)':
+      //   case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover':
+      //   case '.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active':
+      //   case '.ant-steps-item-icon > .ant-steps-icon':
+      //   case '.ant-select-item-option-selected:not(.ant-select-item-option-disabled)':
+      //   case '.ant-menu-item-selected':
+      //   case '.ant-drawer .jeesite-basic-drawer .ant-drawer-body':
+      //   case '.jeesite-basic-drawer-footer':
+      //     return s;
+      //   default:
+      //     // 按钮被重新定制过,需要过滤掉class防止覆盖
+      //     if (s.indexOf('.ant-btn') >= 0) {
+      //       return s;
+      //     }
+      // }
+      // return s.startsWith('[data-theme') ? s : `[data-theme] ${s}`;
+    },
+    colorVariables: [...getThemeColors(), ...colors],
+  });
+  //console.log('vite_theme_plugin:'+JSON.stringify(vite_theme_plugin));
+  ((vite_theme_plugin || []) as any[]).forEach(function (item) {
+    //对vite:theme插件特殊配置
+    if ('vite:theme' === item.name) {
+      //console.log(item);
+      // 打包时去除enforce: "post",vite 2.6.x适配,否则生成app-theme-style为空,
+      // 因为 async transform(code, id) { 的code没有正确获取
+      if (isBuild) {
+        delete item.enforce;
+      }
+      //console.log(item);
+    }
+  });
+  //console.log('vite_theme_plugin后:'+JSON.stringify(vite_theme_plugin));
+  const plugin = [
+    vite_theme_plugin,
+    antdDarkThemePlugin({
+      // preloadFiles: [
+      //   // path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.less'),
+      //   // path.resolve(process.cwd(), 'node_modules/ant-design-vue/dist/antd.dark.less'),
+      //   path.resolve(process.cwd(), 'src/design/index.less'),
+      // ],
+      // filter: (id) => (isBuild ? !id.endsWith('antd.less') : true),
+      // extractCss: false,
+      darkModifyVars: {
+        ...generateModifyVars(true),
+        // 'text-color': '#c9d1d9',
+        // 'primary-1': 'rgb(255 255 255 / 8%)',
+        // 'text-color-base': '#c9d1d9',
+        // 'component-background': '#151515',
+        // 'heading-color': 'rgb(255 255 255 / 65%)',
+        // // black: '#0e1117',
+        // // #8b949e
+        // 'text-color-secondary': '#8b949e',
+        // 'border-color-base': '#303030',
+        // // 'border-color-split': '#30363d',
+        // 'item-active-bg': '#111b26',
+        // // 'tree-node-selected-bg': '#11263c',
+        // 'alert-success-border-color': '#274916',
+        // 'alert-success-bg-color': '#162312',
+        // 'alert-success-icon-color': '#49aa19',
+        // 'alert-info-border-color': '#153450',
+        // 'alert-info-bg-color': '#111b26',
+        // 'alert-info-icon-color': '#177ddc',
+        // 'alert-warning-border-color': '#594214',
+        // 'alert-warning-bg-color': '#2b2111',
+        // 'alert-warning-icon-color': '#d89614',
+        // 'alert-error-border-color': '#58181c',
+        // 'alert-error-bg-color': '#2a1215',
+        // 'alert-error-icon-color': '#a61d24',
+        'content-bg': '#141414',
+        'content-bg-striped': '#1e1e1e',
+        'button-cancel-color': '#c9d1d9',
+        'button-cancel-bg-color': '#2d2d2d',
+        'button-cancel-border-color': '#303030',
+        'button-cancel-hover-color': '#c9d1d9',
+        'button-cancel-hover-bg-color': '#4a4a4a',
+        'button-cancel-hover-border-color': '#4a4a4a',
+        'header-light-bottom-border-color': '#303030',
+      },
+    } as any),
+  ];
+  return plugin as unknown as PluginOption[];
+}

+ 58 - 0
ai-frontend/build/theme/modifyVars.ts

@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { createRequire } from 'node:module';
+import { generateAntColors, primaryColor, darkPrimaryColor } from './themeConfig';
+// import { getThemeVariables } from 'ant-design-vue/dist/theme';
+
+import { theme } from 'ant-design-vue';
+
+// ant-design-vue 仅在 CJS 产物(lib)中提供该方法的默认导出,直接用 import 会被包装成模块对象,
+// 这里通过 require 取回真正的默认导出。
+const requireCjs = createRequire(import.meta.url);
+const convertLegacyToken = (
+  requireCjs('ant-design-vue/lib/theme/convertLegacyToken') as {
+    default: (mapToken: any) => Record<string, any>;
+  }
+).default;
+
+/**
+ * less global variable
+ */
+export function generateModifyVars(dark = false) {
+  const palettes = generateAntColors(primaryColor);
+  const primary = dark ? darkPrimaryColor : palettes[5];
+
+  // const primaryColorObj: Record<string, string> = {};
+  // for (let index = 0; index < 10; index++) {
+  //   primaryColorObj[`primary-${index + 1}`] = palettes[index];
+  // }
+
+  // const modifyVars = getThemeVariables({ dark: false });
+  const { darkAlgorithm, defaultAlgorithm, defaultSeed } = theme;
+  const mapToken = dark ? darkAlgorithm(defaultSeed) : defaultAlgorithm(defaultSeed);
+  const modifyVars = convertLegacyToken(mapToken);
+  // const modifyVars = convertLegacyToken(mapToken);
+  // !!dark && console.log('modifyVars', dark, modifyVars);
+
+  return {
+    ...modifyVars,
+    // Used for global import to avoid the need to import each style file separately
+    // reference:  Avoid repeated references
+    // hack: `${modifyVars.hack} @import (reference) "${__dirname + '/../../core/design/var/index.less'}";`,
+    hack: `true; @import (reference) "${__dirname + '/../../src/core/design/var/index.less'}";`,
+    // ...primaryColorObj,
+    'primary-color': primary,
+    'link-color': primary,
+    'info-color': primary,
+    // 'processing-color': primary,
+    // 'success-color': '#55D187', //  Success color
+    // 'error-color': '#ED6F6F', //  False color
+    // 'warning-color': '#EFBD47', //   Warning color
+    // 'font-size-base': '14px', //  Main font size
+    'border-radius-base': '6px',
+    // 'content-bg-striped': '#fafafa',
+  };
+}

+ 84 - 0
ai-frontend/build/theme/themeConfig.ts

@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2013-Now https://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { generate } from '@ant-design/colors';
+// import { APP_PRESET_COLOR_LIST } from '../../../src/settings/designSetting';
+
+export const primaryColor = '#2a50ec'; // APP_PRESET_COLOR_LIST[0];
+export const darkPrimaryColor = '#2a50ec';
+
+export const darkMode = 'light';
+
+type Fn = (...arg: any) => any;
+
+type GenerateTheme = 'default' | 'dark';
+
+export interface GenerateColorsParams {
+  mixLighten: Fn;
+  mixDarken: Fn;
+  tinycolor: any;
+  color?: string;
+}
+
+export function generateAntColors(color: string, theme: GenerateTheme = 'default') {
+  return generate(color, {
+    theme,
+  });
+}
+
+export function getThemeColors(color?: string) {
+  const primary = color || primaryColor;
+  const lightColors = generateAntColors(primary);
+  const darkPrimary = darkPrimaryColor; //lightColors[5];
+  const darkColors = generateAntColors(darkPrimary, 'dark');
+  return [...lightColors, ...darkColors];
+}
+
+export function generateColors({ color = primaryColor, mixLighten, mixDarken, tinycolor }: GenerateColorsParams) {
+  const arr = new Array(19).fill(0);
+
+  const lightens = arr.map((_t, i) => {
+    return mixLighten(color, i / 5);
+  });
+
+  const darkens = arr
+    .map((_t, i) => {
+      return mixDarken(color, i / 5);
+    })
+    .filter((item) => !item.includes('-'));
+
+  const alphaColors = arr.map((_t, i) => {
+    return tinycolor(color)
+      .setAlpha(i / 20)
+      .toRgbString();
+  });
+
+  const shortAlphaColors = alphaColors.map((item) => item.replace(/\s/g, '').replace(/0\./g, '.'));
+
+  const tinycolorLightens = arr
+    .map((_t, i) => {
+      return tinycolor(color)
+        .lighten(i * 5)
+        .toHexString();
+    })
+    .filter((item) => item !== '#ffffff');
+
+  const tinycolorDarkens = arr
+    .map((_t, i) => {
+      return tinycolor(color)
+        .darken(i * 5)
+        .toHexString();
+    })
+    .filter((item) => item !== '#000000');
+
+  // console.log('lightens', lightens);
+  // console.log('darkens', darkens);
+  // console.log('alphaColors', alphaColors);
+  // console.log('shortAlphaColors', shortAlphaColors);
+  // console.log('tinycolorLightens', tinycolorLightens);
+  // console.log('tinycolorDarkens', tinycolorDarkens);
+
+  return [...lightens, ...darkens, ...alphaColors, ...shortAlphaColors, ...tinycolorDarkens, ...tinycolorLightens];
+}