diff --git a/apps/app/package.json b/apps/app/package.json index 24ca724c7f2d0b86bbab7167b8659dbea5492eca..aabdd534ef18f2225b79a53960c54329c1ce683e 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -15,6 +15,11 @@ "build:prod": "vue-tsc && cross-env ENV_TYPE=live vite build", "preview": "vite preview" }, + "vtj": { + "saveToProject": false, + "projectSavePath": "./src/views", + "configPath": "" + }, "dependencies": { "@vtj/example-ui": "~0.2.0", "@vtj/plugin-ckeditor": "~0.2.1", diff --git a/packages/core/src/models/project.ts b/packages/core/src/models/project.ts index 5ecd64b4c2699dc73de4a57620f7951acb845564..87319cf7fa8b11e882d1ceb3726d8ed4a9ce8f31 100644 --- a/packages/core/src/models/project.ts +++ b/packages/core/src/models/project.ts @@ -83,6 +83,9 @@ export class ProjectModel { config: ProjectConfig = {}; uniConfig: UniConfig = {}; __BASE_PATH__: string = '/'; + saveToProject?: boolean; + projectSavePath?: string; + configPath?: string; static attrs: string[] = [ 'platform', 'name', @@ -95,7 +98,10 @@ export class ProjectModel { 'meta', 'config', 'uniConfig', - '__BASE_PATH__' + '__BASE_PATH__', + 'saveToProject', + 'projectSavePath', + 'configPath' // 添加 ]; constructor(schema: ProjectSchema) { const { id } = schema; diff --git a/packages/core/src/protocols/schemas/project.ts b/packages/core/src/protocols/schemas/project.ts index 18c4e345401fb07f88269ff0f923719e5a33b4b1..0e5d0a40542643b788c2b761c48f4ade7ab5a63f 100644 --- a/packages/core/src/protocols/schemas/project.ts +++ b/packages/core/src/protocols/schemas/project.ts @@ -27,6 +27,21 @@ export interface ProjectSchema { */ description?: string; + /** + * 是否将生成的文件保存到自定义项目中 + */ + saveToProject?: boolean; + + /** + * 自定义项目保存路径 + */ + projectSavePath?: string; + + /** + * 配置文件路径,用于分析项目文件层级结构 + */ + configPath?: string; + /** * 项目页面 */ diff --git a/packages/local/src/repository/vue.ts b/packages/local/src/repository/vue.ts index cbc09a644cb81d5b55fddb1293f88ec8b1d5c7c6..64f15eb2ab1a398fc7df16d5098e472a93b1103f 100644 --- a/packages/local/src/repository/vue.ts +++ b/packages/local/src/repository/vue.ts @@ -1,29 +1,320 @@ -import { resolve, join } from 'path'; +import { resolve, join, dirname } from 'path'; import { pathExistsSync, removeSync, outputFileSync, - ensureFileSync + ensureFileSync, + ensureDirSync } from '@vtj/node'; +import * as fs from 'fs'; -import type { PlatformType } from '@vtj/core'; +import type { PlatformType, ProjectSchema } from '@vtj/core'; export class VueRepository { private path: string; - constructor(platform: PlatformType = 'web') { + private projectConfig: { + enabled: boolean; + path?: string; + configPath?: string; // 项目配置文件路径 + } = { enabled: false }; + + constructor( + platform: PlatformType = 'web', + projectConfig?: { + enabled: boolean; + path?: string; + configPath?: string; // 项目配置文件路径 + } + ) { const dir = platform === 'uniapp' ? 'src/pages' : '.vtj/vue'; this.path = resolve(dir); + + if (projectConfig) { + this.projectConfig = projectConfig; + } + } + + /** + * 获取项目文件保存路径 + * 根据项目结构动态计算文件的保存路径 + * @param project 项目配置 + * @param fileId 文件ID + * @param forceRefresh 是否强制刷新项目配置 + * @returns 文件保存路径或null + */ + getProjectSavePath(project: ProjectSchema, fileId: string, forceRefresh: boolean = false): string | null { + if (!this.projectConfig.enabled || !this.projectConfig.path) { + return null; + } + + // 从项目配置中解析保存路径 + const customPath = this.projectConfig.path; + const basePath = resolve(customPath); + + // 查找文件信息 + const file = project.pages?.find(p => p.id === fileId) || + project.blocks?.find(b => b.id === fileId); + + if (!file) { + return null; + } + + // 尝试从文件名推断目录结构 + // 例如:Sysindex -> Sys, Elderindex -> Elder, Aindex -> A, Bindex -> B + const fileName = file.name; + const match = fileName.match(/^([A-Z][a-z]+)index$/); + + if (match) { + const dirName = match[1].toLowerCase(); // 提取目录名并转为小写 + return join(basePath, dirName, `${fileName.toLowerCase()}.vue`); + } + + // 尝试从vtj-project-app.json文件中读取完整的项目结构 + // 如果forceRefresh为true,则强制重新读取文件 + try { + // 使用配置路径,如果未配置则使用默认路径 + const configPath = resolve(this.projectConfig.configPath || './.vtj/projects/vtj-project-app.json'); + + // 检查配置文件是否存在 + if (!pathExistsSync(configPath)) { + throw new Error('未找到项目配置文件'); + } + + // 读取配置文件内容 + const projectJsonContent = fs.readFileSync(configPath, 'utf8'); + + // 解析JSON内容 + const projectJson = JSON.parse(projectJsonContent); + + if (projectJson && projectJson.pages) { + + // 递归查找文件 + const findItemById = (items: Array, id: string): any => { + if (!items || !Array.isArray(items)) return null; + + for (const item of items) { + if (!item) continue; + if (item.id === id) return item; + + if (item.dir === true && item.children) { + const found = findItemById(item.children, id); + if (found) return found; + } + } + return null; + }; + + // 构建父子关系映射 + const buildParentMap = (items: Array): Map => { + const parentMap = new Map(); + + const processItems = (items: Array) => { + if (!items || !Array.isArray(items)) return; + + for (const item of items) { + if (!item) continue; + + if (item.dir === true && item.children && Array.isArray(item.children)) { + for (const child of item.children) { + if (!child) continue; + parentMap.set(child.id, item); + } + + // 递归处理子目录 + processItems(item.children); + } + } + }; + + processItems(items); + return parentMap; + }; + + // 构建从文件到根目录的路径 + const buildPathFromFileToRoot = (items: Array, targetId: string): string[] => { + // 构建父子关系映射 + const parentMap = buildParentMap(items); + + // 查找目标文件 + const targetItem = findItemById(items, targetId); + if (!targetItem) { + return []; + } + + // 从目标文件开始,向上查找父级目录 + const path: string[] = []; + let currentId = targetId; + + // 添加目标文件名 + path.unshift(targetItem.name); + + // 向上查找父级目录 + while (parentMap.has(currentId)) { + const parent = parentMap.get(currentId); + path.unshift(parent.name); + currentId = parent.id; + } + + return path; + }; + + // 构建父子关系映射并获取路径 + const pathParts = buildPathFromFileToRoot(projectJson.pages, fileId); + + if (pathParts.length > 0) { + // 最后一个元素是文件名,转为小写 + const fileName = pathParts[pathParts.length - 1].toLowerCase(); + + if (pathParts.length > 1) { + // 有目录结构,构建目录路径,目录名转为小写 + const dirPath = pathParts.slice(0, -1).map(part => part.toLowerCase()).join('/'); + return join(basePath, dirPath, `${fileName}.vue`); + } else { + // 没有目录结构,直接在根目录下,文件名转为小写 + return join(basePath, `${fileName}.vue`); + } + } + } + } catch (error: any) { + // 错误已处理,继续尝试其他方法 + } + + // 如果无法从项目配置文件中解析路径,尝试使用传入的project参数 + if (project.pages && Array.isArray(project.pages)) { + + // 递归查找文件 + const findItemById = (items: Array, id: string): any => { + if (!items || !Array.isArray(items)) return null; + + for (const item of items) { + if (!item) continue; + if (item.id === id) return item; + + if (item.dir === true && item.children) { + const found = findItemById(item.children, id); + if (found) return found; + } + } + return null; + }; + + // 构建父子关系映射 + const buildParentMap = (items: Array): Map => { + const parentMap = new Map(); + + const processItems = (items: Array) => { + if (!items || !Array.isArray(items)) return; + + for (const item of items) { + if (!item) continue; + + if (item.dir === true && item.children && Array.isArray(item.children)) { + for (const child of item.children) { + if (!child) continue; + parentMap.set(child.id, item); + } + + // 递归处理子目录 + processItems(item.children); + } + } + }; + + processItems(items); + return parentMap; + }; + + // 构建从文件到根目录的路径 + const buildPathFromFileToRoot = (items: Array, targetId: string): string[] => { + // 构建父子关系映射 + const parentMap = buildParentMap(items); + + // 查找目标文件 + const targetItem = findItemById(items, targetId); + if (!targetItem) { + return []; + } + + // 从目标文件开始,向上查找父级目录 + const path: string[] = []; + let currentId = targetId; + + // 添加目标文件名 + path.unshift(targetItem.name); + + // 向上查找父级目录 + while (parentMap.has(currentId)) { + const parent = parentMap.get(currentId); + path.unshift(parent.name); + currentId = parent.id; + } + + return path; + }; + + // 构建父子关系映射并获取路径 + const pathParts = buildPathFromFileToRoot(project.pages, fileId); + + if (pathParts.length > 0) { + // 最后一个元素是文件名,转为小写 + const fileName = pathParts[pathParts.length - 1].toLowerCase(); + + if (pathParts.length > 1) { + // 有目录结构,构建目录路径,目录名转为小写 + const dirPath = pathParts.slice(0, -1).map(part => part.toLowerCase()).join('/'); + return join(basePath, dirPath, `${fileName}.vue`); + } else { + // 没有目录结构,直接在根目录下,文件名转为小写 + return join(basePath, `${fileName}.vue`); + } + } + } + + // 文件ID是随机生成的,不使用预定义映射 + + // 如果所有方法都失败,使用默认的文件名,转为小写 + return join(basePath, `${file.name.toLowerCase()}.vue`); } + exist(name: string) { const filePath = join(this.path, `${name}.vue`); return pathExistsSync(filePath); } - save(name: string, content: any) { + /** + * 保存Vue文件 + * 将文件保存到默认路径和项目自定义路径(如果启用) + * @param name 文件ID + * @param content 文件内容 + * @param project 项目配置 + * @returns 是否保存成功 + */ + save(name: string, content: any, project?: ProjectSchema) { + // 保存到默认路径 const filePath = join(this.path, `${name}.vue`); if (!this.exist(name)) { ensureFileSync(filePath); } outputFileSync(filePath, content, 'utf-8'); + + // 如果启用了项目自定义保存,则同时保存到项目路径 + if (project && this.projectConfig.enabled && this.projectConfig.path) { + try { + // 使用getProjectSavePath方法获取完整路径,强制刷新项目配置 + const projectPath = this.getProjectSavePath(project, name, true); + + if (projectPath) { + // 确保目标目录存在 + const dirPath = dirname(projectPath); + ensureDirSync(dirPath); + + // 保存文件 + outputFileSync(projectPath, content, 'utf-8'); + } + } catch (error) { + // 错误已处理 + } + } + return true; } remove(name: string) { diff --git a/packages/local/src/service.ts b/packages/local/src/service.ts index 1c12dc13728244a460705d7adfbf92e0dd72922d..5beff9e9e21fcd5d39a1d96b3a81709bffd0fa43 100644 --- a/packages/local/src/service.ts +++ b/packages/local/src/service.ts @@ -71,7 +71,7 @@ export async function init(_body: any, opts: DevToolsOptions) { const pkg = readJsonSync(resolve(root, 'package.json')); const pluginPepository = new PluginRepository(pkg, opts); // 从项目的 package.json 中读取项目信息 - const { vtj = {} } = pkg || {}; + const { vtj = {} } = pkg || {}; // 读取 vtj 配置 const id = vtj.id || pkg.name; const name = vtj.name || pkg.description || upperFirstCamelCase(id); const description = vtj.description || pkg.description || ''; @@ -81,33 +81,43 @@ export async function init(_body: any, opts: DevToolsOptions) { // 如果项目文件已经存在,则直接返回文件内容 let dsl: ProjectSchema = repository.get(id); const plugins = pluginPepository.getPlugins(); - if (dsl) { + if (dsl) { // 处理已存在项目 const blocks = (dsl.blocks || []).filter((n) => !n.preset); dsl.blocks = plugins.concat(blocks); - Object.assign(dsl, { id, name, description, platform }); + // 使用 package.json 中的 vtj 配置更新 dsl + Object.assign(dsl, { + id, + name, + description, + platform, + saveToProject: vtj.saveToProject, // 更新 saveToProject + projectSavePath: vtj.projectSavePath // 更新 projectSavePath + }); if (platform === 'uniapp') { dsl.uniConfig = await getUniConfig(dsl); } if (!isInit) { isInit = true; - repository.save(id, dsl); + repository.save(id, dsl); // 保存更新后的 dsl } dsl.__BASE_PATH__ = opts.staticBase; return success(dsl); - } else { + } else { // 处理新项目 const model = new ProjectModel({ id, name, description, platform, - blocks: plugins + blocks: plugins, + saveToProject: vtj.saveToProject, // 从 vtj 配置传递 + projectSavePath: vtj.projectSavePath // 从 vtj 配置传递 }); dsl = model.toDsl(); if (platform === 'uniapp') { dsl.uniConfig = await getUniConfig(dsl); } - repository.save(id, dsl); + repository.save(id, dsl); // 保存包含新属性的 dsl dsl.__BASE_PATH__ = opts.staticBase; return success(dsl); } @@ -212,6 +222,13 @@ export async function publishFile( file: PageFile | BlockFile, componentMap?: Map ) { + // 发布文件 + + // 读取package.json中的vtj配置 + const root = resolve('./'); + const pkg = readJsonSync(resolve(root, 'package.json')); + const { vtj = {} } = pkg || {}; // 读取 vtj 配置 + const materialsRepository = new JsonRepository('materials', project.platform); const materials = materialsRepository.get(project.id as string); componentMap = @@ -237,8 +254,12 @@ export async function publishFile( } catch (e) {} throw e; }); - const vueRepository = new VueRepository(_platform); - vueRepository.save(file.id as string, content); + const vueRepository = new VueRepository(project.platform, { + enabled: project.saveToProject || false, + path: project.projectSavePath || './src/views', + configPath: vtj.configPath || './.vtj/projects/vtj-project-app.json' // 从package.json中读取,如果未配置则使用默认路径 + }); + vueRepository.save(file.id as string, content, project); return success(true); } else { return fail('文件不存在'); @@ -302,9 +323,25 @@ export async function genVueContent(project: ProjectSchema, dsl: BlockSchema) { } export async function createRawPage(file: PageFile) { - const repository = new VueRepository(_platform); + // 尝试获取默认项目信息 + const projectId = 'vtj-project-app'; // 默认项目ID + const projectRepository = new JsonRepository('projects', _platform); + const project = projectRepository.get(projectId); + + // 读取package.json中的vtj配置 + const root = resolve('./'); + const pkg = readJsonSync(resolve(root, 'package.json')); + const { vtj = {} } = pkg || {}; // 读取 vtj 配置 + + // 创建 VueRepository 实例,传入项目配置 + const repository = new VueRepository(_platform, project ? { + enabled: project.saveToProject || false, + path: project.projectSavePath || './src/views', + configPath: vtj.configPath || './.vtj/projects/vtj-project-app.json' // 从package.json中读取,如果未配置则使用默认路径 + } : undefined); + const page = await createEmptyPage(file); - repository.save(file.id as string, page); + repository.save(file.id as string, page, project || undefined); return success(true); } diff --git "a/yudaovue3\351\233\206\346\210\220\350\277\207\347\250\213.md" "b/yudaovue3\351\233\206\346\210\220\350\277\207\347\250\213.md" new file mode 100644 index 0000000000000000000000000000000000000000..e2341f41c941e6bec2e088cac0a2d1e6e47523bf --- /dev/null +++ "b/yudaovue3\351\233\206\346\210\220\350\277\207\347\250\213.md" @@ -0,0 +1,447 @@ +# VTJ低代码设计器集成过程 + +本文档记录了将VTJ低代码设计器集成到Vue3项目中的详细过程,包括每个步骤的修改内容和代码片段。 + +## 一、添加依赖 + +### 1. 添加@vtj/web到生产依赖 + +```json +// package.json +"dependencies": { + "@element-plus/icons-vue": "^2.1.0", + "@form-create/designer": "^3.2.6", + "@form-create/element-ui": "^3.2.11", + "@iconify/iconify": "^3.1.1", + "@microsoft/fetch-event-source": "^2.0.1", + "@videojs-player/vue": "^1.0.0", + "@vtj/web": "latest", // [低代码集成] 添加VTJ Web依赖,用于低代码运行时支持 + "@vueuse/core": "^10.9.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.10", + "@zxcvbn-ts/core": "^3.0.4", + // ...其他依赖 +} +``` + +### 2. 添加@vtj/cli、@vtj/pro和@vtj/materials到开发依赖 + +```json +// package.json +"devDependencies": { + "@commitlint/cli": "^19.0.1", + "@commitlint/config-conventional": "^19.0.0", + "@iconify/json": "^2.2.187", + "@intlify/unplugin-vue-i18n": "^2.0.0", + "@purge-icons/generated": "^0.9.0", + "@vtj/cli": "latest", // [低代码集成] 添加VTJ CLI依赖,用于低代码命令行工具 + "@vtj/pro": "latest", // [低代码集成] 添加VTJ Pro依赖,提供开发工具插件 + "@vtj/materials": "latest", // [低代码集成] 添加VTJ Materials依赖,提供物料资源 + // ...其他依赖 +} +``` + +### 3. 安装依赖 + +```bash +pnpm i +``` + +如果遇到物料资源加载问题,可以单独安装@vtj/materials: + +```bash +pnpm install @vtj/materials +``` + +## 二、添加插件 + +由于@vtj/pro/vite是ESM模块,我们需要使用动态导入的方式处理。 + +### 1. 修改build/vite/index.ts + +```typescript +// build/vite/index.ts +import { resolve } from 'path' +import Vue from '@vitejs/plugin-vue' +import VueJsx from '@vitejs/plugin-vue-jsx' +import progress from 'vite-plugin-progress' +import EslintPlugin from 'vite-plugin-eslint' +import PurgeIcons from 'vite-plugin-purge-icons' +import { ViteEjsPlugin } from 'vite-plugin-ejs' +// @ts-ignore +import ElementPlus from 'unplugin-element-plus/vite' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' +import viteCompression from 'vite-plugin-compression' +import topLevelAwait from 'vite-plugin-top-level-await' +import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' +import UnoCSS from 'unocss/vite' +// [低代码集成] 动态导入ESM模块,解决VTJ插件的ESM兼容性问题 +const createDevTools = async () => { + const module = await import('@vtj/pro/vite') + return module.createDevTools() +} + +export function createVitePlugins() { + const root = process.cwd() + + // 路径查找 + function pathResolve(dir: string) { + return resolve(root, '.', dir) + } + + return [ + Vue(), + VueJsx(), + UnoCSS(), + progress(), + PurgeIcons(), + ElementPlus({}), + // 注释掉直接调用,稍后我们会在vite.config.ts中处理 + // createDevTools(), // 添加VTJ开发工具插件 + // ...其他插件 + ] +} +``` + +### 2. 修改vite.config.ts + +```typescript +// vite.config.ts +import {resolve} from 'path' +import type {ConfigEnv, UserConfig, Plugin} from 'vite' +import {loadEnv} from 'vite' +import {createVitePlugins} from './build/vite' +import {exclude, include} from "./build/vite/optimize" +// 当前执行node命令时文件夹的地址(工作目录) +const root = process.cwd() + +// 路径查找 +function pathResolve(dir: string) { + return resolve(root, '.', dir) +} + +// [低代码集成] 动态导入VTJ开发工具插件,处理ESM模块兼容性 +async function createVTJDevTools(): Promise { + try { + const { createDevTools } = await import('@vtj/pro/vite') + return createDevTools() + } catch (error) { + console.error('Failed to load VTJ DevTools:', error) + return { + name: 'vtj-devtools-fallback', + // 返回一个空插件作为降级处理 + } as Plugin + } +} + +export default async ({command, mode}: ConfigEnv): Promise => { + let env = {} as any + const isBuild = command === 'build' + if (!isBuild) { + env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root) + } else { + env = loadEnv(mode, root) + } + + // [低代码集成] 加载VTJ开发工具插件,提供设计器功能 + const vtjDevTools = await createVTJDevTools() + + return { + base: env.VITE_BASE_PATH, + root: root, + // 服务端渲染 + server: { + port: env.VITE_PORT, // 端口号 + host: "0.0.0.0", + open: env.VITE_OPEN === 'true', + // 本地跨域代理. 目前注释的原因:暂时没有用途,server 端已经支持跨域 + // proxy: { + // ['/admin-api']: { + // target: env.VITE_BASE_URL, + // ws: false, + // changeOrigin: true, + // rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''), + // }, + // }, + }, + // 项目使用的vite插件。 单独提取到build/vite/plugin中管理 + plugins: [ + ...createVitePlugins(), + vtjDevTools // [低代码集成] 添加VTJ开发工具插件 + ], + // ...其他配置 + } +} +``` + +## 三、改造入口程序 + +### 修改src/main.ts + +```typescript +// src/main.ts +// 引入unocss css +import '@/plugins/unocss' + +// 导入全局的svg图标 +import '@/plugins/svgIcon' + +// 初始化多语言 +import { setupI18n } from '@/plugins/vueI18n' + +// 引入状态管理 +import { setupStore } from '@/store' + +// 全局组件 +import { setupGlobCom } from '@/components' + +// 引入 element-plus +import { setupElementPlus } from '@/plugins/elementPlus' + +// 引入 form-create +import { setupFormCreate } from '@/plugins/formCreate' + +// 引入全局样式 +import '@/styles/index.scss' + +// 引入动画 +import '@/plugins/animate.css' + +// 路由 +import router, { setupRouter } from '@/router' + +// 指令 +import { setupAuth, setupMountedFocus } from '@/directives' + +import { createApp, reactive } from 'vue' // [低代码集成] 导入reactive API,用于低代码组件 + +import App from './App.vue' + +import './permission' + +import '@/plugins/tongji' // 百度统计 +import Logger from '@/utils/Logger' + +import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患 + +// [低代码集成] 1、引入 VTJ 相关功能 +import { createProvider, LocalService, createModules } from '@vtj/web' +// [低代码集成] 2、引用组件样式 +import '@vtj/web/src/index.scss' + +// 创建实例 +const setupAll = async () => { + // [低代码集成] 3、实例化低代码服务类 + const service = new LocalService() + + // [低代码集成] 4、创建低代码提供者实例 + const { provider, onReady } = createProvider({ + nodeEnv: import.meta.env.MODE, // 使用环境变量的MODE值 + modules: createModules({ + // [低代码集成] 提供Vue响应式API,解决reactive未定义问题 + vue: { reactive }, + // [低代码集成] 配置物料路径,确保能正确加载charts等模块 + materialOptions: { + basePath: '/', + // [低代码集成] 使用本地物料,避免网络请求404 + useLocal: true + } + }), + service, + router, + materialPath: '/' // [低代码集成] 设置物料路径,解决History模式下404问题 + }) + + // [低代码集成] 5、低代码提供者初始化完成后注册 + onReady(async () => { + const app = createApp(App) + + await setupI18n(app) + + setupStore(app) + + setupGlobCom(app) + + setupElementPlus(app) + + setupFormCreate(app) + + setupRouter(app) + + // directives 指令 + setupAuth(app) + setupMountedFocus(app) + + await router.isReady() + + app.use(VueDOMPurifyHTML) + app.use(router) + app.use(provider) // [低代码集成] 注册VTJ提供者 + + app.mount('#app') + }) +} + +setupAll() + +Logger.prettyPrimary(`欢迎使用`, import.meta.env.VITE_APP_TITLE) +``` + +## 四、添加异步依赖 + +### 修改src/App.vue + +```vue + + +``` + +## 五、修改tsconfig.json + +```json +// tsconfig.json +{ + "compilerOptions": { + // ...其他配置 + }, + "include": [ + "src", + "types/**/*.d.ts", + "src/types/auto-imports.d.ts", + "src/types/auto-components.d.ts" + ], + "exclude": ["dist", "target", "node_modules", ".vtj"] // [低代码集成] 排除.vtj文件夹,避免TypeScript检查生成的低代码文件 +} +``` + +## 六、遇到的问题及解决方案 + +### 1. ESM模块兼容性问题 + +**问题描述**: +在运行项目时遇到了错误:`"@vtj/pro/vite" resolved to an ESM file. ESM file cannot be loaded by require`。这是因为@vtj/pro/vite是一个ESM模块,但项目尝试使用CommonJS的require方式加载它。 + +**解决方案**: +1. 在build/vite/index.ts中使用动态导入方式处理@vtj/pro/vite +2. 在vite.config.ts中实现了异步配置和插件加载 + +### 2. TypeScript类型错误 + +**问题描述**: +在修改vite.config.ts文件时,遇到了TypeScript错误:`类型 "Plugin[]" 中缺少属性 "name",但类型 "Plugin" 中需要该属性`。 + +**解决方案**: +调整createVTJDevTools函数的返回类型为`Promise`,以支持返回单个插件或插件数组。 + +### 3. 组件拖拽问题 + +**问题描述**: +设计器可以打开,但无法拖拽组件到页面上,控制台报错: +1. `GET http://localhost/@vtj/materials/assets/charts/index.umd.js?v=0.11.12 net::ERR_ABORTED 404 (Not Found)` +2. `Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'reactive')` + +**解决方案**: +1. 导入Vue的响应式API:`import { createApp, reactive } from 'vue'` +2. 在createModules函数中添加配置: + ```javascript + createModules({ + // 提供Vue响应式API + vue: { reactive }, + // 配置物料路径 + materialOptions: { + basePath: '/', + useLocal: true // 使用本地物料 + } + }) + ``` + +### 4. 物料资源加载问题 + +**问题描述**: +设计器打开后,加载物料资源时报错: +`GET http://localhost/@vtj/materials/deps/element-plus/zh-cn.js?v=0.11.12 net::ERR_ABORTED 404 (Not Found)` + +**解决方案**: +安装@vtj/materials依赖,提供物料资源: +```bash +pnpm install @vtj/materials +``` + +## 七、运行项目 + +```bash +npm run dev +``` + +成功启动项目后,可以在页面右下角看到低代码设计器的入口。 + +## 八、注意事项 + +1. **路由模式配置**: + - 由于本项目使用的是createWebHistory路由模式(History模式),必须在createProvider时设置materialPath参数为'/' + - 如果不设置此参数,会导致通过设计器入口打开的低代码页面出现404错误 + - 这是因为History模式下,浏览器会将URL解析为真实的路径,需要正确设置物料路径 + +2. **版本兼容性**: + - 如果后续升级VTJ版本,可能需要重新检查兼容性 + - 特别注意ESM模块的处理方式可能随版本变化 + +3. **文件管理**: + - 低代码设计器生成的文件会存储在.vtj文件夹中 + - 已在tsconfig.json中排除.vtj文件夹,避免TypeScript检查生成的低代码文件 + + + **命令:** + # 安装 pnpm,提升依赖的安装速度 +npm config set registry https://registry.npmmirror.com +npm install -g pnpm +# 安装依赖 +pnpm install + +# 运行前端 +npm run dev + +#打包前端 +npm run build:dev + +清理: +npm cache clean --force +pnpm store prune +rm -rf node_modules +rm pnpm-lock.yaml \ No newline at end of file