diff --git a/packages/vue-generator/package.json b/packages/vue-generator/package.json
index b7a94ecdcc..edffb26153 100644
--- a/packages/vue-generator/package.json
+++ b/packages/vue-generator/package.json
@@ -53,6 +53,7 @@
},
"peerDependencies": {
"@babel/parser": "^7.18.13",
+ "@babel/generator": "^7.18.13",
"@babel/traverse": "^7.18.13"
}
}
diff --git a/packages/vue-generator/src/generator/generateApp.js b/packages/vue-generator/src/generator/generateApp.js
index b602a1c9e7..ad8bb84647 100644
--- a/packages/vue-generator/src/generator/generateApp.js
+++ b/packages/vue-generator/src/generator/generateApp.js
@@ -9,7 +9,8 @@ import {
genUtilsPlugin,
formatCodePlugin,
parseSchemaPlugin,
- genGlobalState
+ genGlobalState,
+ appendElePlusStylePlugin
} from '../plugins'
import CodeGenerator from './codeGenerator'
@@ -63,6 +64,11 @@ export function generateApp(config = {}) {
globalState: globalState || defaultPlugins.globalState
}
+ // 默认支持 element-plus 注入样式
+ if (config?.customContext?.injectElementPlusStyle !== false) {
+ transformEnd.push(appendElePlusStylePlugin(config?.customContext?.injectElementPlusStyle || {}))
+ }
+
const codeGenInstance = new CodeGenerator({
plugins: {
transformStart: [parseSchema || defaultPlugins.parseSchema, ...transformStart],
diff --git a/packages/vue-generator/src/plugins/appendElePlusStylePlugin.js b/packages/vue-generator/src/plugins/appendElePlusStylePlugin.js
new file mode 100644
index 0000000000..1d77975df5
--- /dev/null
+++ b/packages/vue-generator/src/plugins/appendElePlusStylePlugin.js
@@ -0,0 +1,89 @@
+import prettier from 'prettier'
+import { parse } from '@babel/parser'
+import traverse from '@babel/traverse'
+import generate from '@babel/generator'
+import parserBabel from 'prettier/parser-babel'
+import { mergeOptions } from '../utils/mergeOptions'
+
+const defaultOption = {
+ fileName: 'package.json',
+ path: '.',
+ prettierOption: {
+ singleQuote: true,
+ printWidth: 120,
+ semi: false,
+ trailingComma: 'none'
+ }
+}
+
+function genElementPlusStyleDeps(options = {}) {
+ const realOptions = mergeOptions(defaultOption, options)
+
+ const { prettierOption, fileName, path } = realOptions
+
+ return {
+ name: 'tinyEngine-generateCode-plugin-element-plus-style',
+ description: 'import element-plus style',
+ /**
+ * 注入 element-plus 全局样式依赖
+ * @param {tinyEngineDslVue.IAppSchema} schema
+ * @returns
+ */
+ run() {
+ const originPackageItem = this.getFile(path, fileName)
+
+ if (!originPackageItem) {
+ return
+ }
+
+ let originPackageJSON = JSON.parse(originPackageItem.fileContent)
+ const hasElementPlusDeps = Object.keys(originPackageJSON.dependencies).includes('element-plus')
+
+ if (!hasElementPlusDeps) {
+ return
+ }
+
+ const mainJsFile = this.getFile('./src', 'main.js') || {}
+
+ if (!mainJsFile.fileContent) {
+ return
+ }
+
+ const ast = parse(mainJsFile.fileContent, { sourceType: 'module' })
+ let lastImport = null
+ let hasElementPlusStyleImport = false
+
+ traverse(ast, {
+ ImportDeclaration(path) {
+ lastImport = path
+
+ if (path.node.source.value === 'element-plus/dist/index.css') {
+ hasElementPlusStyleImport = true
+ }
+ }
+ })
+
+ // 已经存在 element-plus 的 import,不再插入
+ if (hasElementPlusStyleImport) {
+ return
+ }
+
+ // 引入 element-plus 样式依赖
+ if (lastImport) {
+ lastImport.insertAfter(parse("import 'element-plus/dist/index.css'", { sourceType: 'module' }).program.body[0])
+ }
+
+ const newFileContent = generate(ast).code
+
+ const formattedContent = prettier.format(newFileContent, {
+ parser: 'babel',
+ plugins: [parserBabel],
+ ...prettierOption
+ })
+
+ this.replaceFile({ ...mainJsFile, fileContent: formattedContent })
+ }
+ }
+}
+
+export default genElementPlusStyleDeps
diff --git a/packages/vue-generator/src/plugins/index.js b/packages/vue-generator/src/plugins/index.js
index 1980ba6025..cfdfeab5a4 100644
--- a/packages/vue-generator/src/plugins/index.js
+++ b/packages/vue-generator/src/plugins/index.js
@@ -9,3 +9,4 @@ export { default as genTemplatePlugin } from './genTemplatePlugin'
export { default as formatCodePlugin } from './formatCodePlugin'
export { default as genGlobalState } from './genGlobalState'
export { default as parseSchemaPlugin } from './parseSchemaPlugin'
+export { default as appendElePlusStylePlugin } from './appendElePlusStylePlugin'
diff --git a/packages/vue-generator/test/testcases/element-plus-case/element-plus-case.test.js b/packages/vue-generator/test/testcases/element-plus-case/element-plus-case.test.js
new file mode 100644
index 0000000000..b86f2672c0
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/element-plus-case.test.js
@@ -0,0 +1,43 @@
+import { expect, test, describe } from 'vitest'
+import path from 'path'
+import fs from 'fs'
+import dirCompare from 'dir-compare'
+import { generateApp } from '@/generator/generateApp'
+import { appSchemaDemo01 } from './mockData'
+import { logDiffResult } from '../../utils/logDiffResult'
+
+describe('generate element-plus material project correctly', () => {
+ test('should generate element-plus css import statement 预期生成 element-plus 样式依赖引入', async () => {
+ const instance = generateApp()
+
+ const res = await instance.generate(appSchemaDemo01)
+ const { genResult } = res
+
+ // 写入文件
+ genResult.forEach(({ fileName, path: filePath, fileContent }) => {
+ fs.mkdirSync(path.resolve(__dirname, `./result/appdemo01/${filePath}`), { recursive: true })
+ fs.writeFileSync(
+ path.resolve(__dirname, `./result/appdemo01/${filePath}/${fileName}`),
+ // 这里需要将换行符替换成 CRLF 格式的
+ fileContent.replace(/\r?\n/g, '\r\n')
+ )
+ })
+
+ const compareOptions = {
+ compareContent: true,
+ ignoreLineEnding: true,
+ ignoreAllWhiteSpaces: true,
+ ignoreEmptyLines: true
+ }
+
+ const path1 = path.resolve(__dirname, './expected/appdemo01')
+ const path2 = path.resolve(__dirname, './result/appdemo01')
+
+ // 对比文件差异
+ const diffResult = dirCompare.compareSync(path1, path2, compareOptions)
+
+ logDiffResult(diffResult)
+
+ expect(diffResult.same).toBe(true)
+ })
+})
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/.gitignore b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/.gitignore
new file mode 100644
index 0000000000..9961aac9d4
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/.gitignore
@@ -0,0 +1,13 @@
+node_modules
+dist/
+
+# local env files
+.env.local
+.env.*.local
+
+# Editor directories and files
+.vscode
+.idea
+
+yarn.lock
+package-lock.json
\ No newline at end of file
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/README.md b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/README.md
new file mode 100644
index 0000000000..275a5e79cd
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/README.md
@@ -0,0 +1,19 @@
+## portal-app
+
+本工程是使用 TinyEngine 低代码引擎搭建之后得到的出码工程。
+
+## 使用
+
+安装依赖:
+
+```bash
+npm install
+```
+
+本地启动项目:
+
+```bash
+npm run dev
+```
+
+
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/index.html b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/index.html
new file mode 100644
index 0000000000..2f10f362d1
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ portal-app
+
+
+
+
+
+
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/package.json b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/package.json
new file mode 100644
index 0000000000..4d9b7824d9
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "portal-app",
+ "version": "1.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "main": "dist/index.js",
+ "module": "dist/index.js",
+ "dependencies": {
+ "@opentiny/tiny-engine-i18n-host": "^1.0.0",
+ "@opentiny/vue": "^3.10.0",
+ "@opentiny/vue-icon": "^3.10.0",
+ "axios": "^0.21.1",
+ "axios-mock-adapter": "^1.19.0",
+ "vue": "^3.3.9",
+ "vue-i18n": "^9.2.0-beta.3",
+ "vue-router": "^4.2.5",
+ "pinia": "^2.1.7",
+ "element-plus": "^2.4.2"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^4.5.1",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "vite": "^4.3.7"
+ }
+}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/App.vue b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/App.vue
new file mode 100644
index 0000000000..72b6032dea
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/App.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/axios.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/axios.js
new file mode 100644
index 0000000000..4b2d6e4208
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/axios.js
@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+import axios from 'axios'
+import MockAdapter from 'axios-mock-adapter'
+
+export default (config) => {
+ const instance = axios.create(config)
+ const defaults = {}
+ let mock
+
+ if (typeof MockAdapter.prototype.proxy === 'undefined') {
+ MockAdapter.prototype.proxy = function ({ url, config = {}, proxy, response, handleData } = {}) {
+ let stream = this
+ const request = (proxy, any) => {
+ return (setting) => {
+ return new Promise((resolve) => {
+ config.responseType = 'json'
+ axios
+ .get(any ? proxy + setting.url + '.json' : proxy, config)
+ .then(({ data }) => {
+ /* eslint-disable no-useless-call */
+ typeof handleData === 'function' && (data = handleData.call(null, data, setting))
+ resolve([200, data])
+ })
+ .catch((error) => {
+ resolve([error.response.status, error.response.data])
+ })
+ })
+ }
+ }
+
+ if (url === '*' && proxy && typeof proxy === 'string') {
+ stream = proxy === '*' ? this.onAny().passThrough() : this.onAny().reply(request(proxy, true))
+ } else {
+ if (proxy && typeof proxy === 'string') {
+ stream = this.onAny(url).reply(request(proxy))
+ } else if (typeof response === 'function') {
+ stream = this.onAny(url).reply(response)
+ }
+ }
+
+ return stream
+ }
+ }
+
+ return {
+ request(config) {
+ return instance(config)
+ },
+ get(url, config) {
+ return instance.get(url, config)
+ },
+ delete(url, config) {
+ return instance.delete(url, config)
+ },
+ head(url, config) {
+ return instance.head(url, config)
+ },
+ post(url, data, config) {
+ return instance.post(url, data, config)
+ },
+ put(url, data, config) {
+ return instance.put(url, data, config)
+ },
+ patch(url, data, config) {
+ return instance.patch(url, data, config)
+ },
+ all(iterable) {
+ return axios.all(iterable)
+ },
+ spread(callback) {
+ return axios.spread(callback)
+ },
+ defaults(key, value) {
+ if (key && typeof key === 'string') {
+ if (typeof value === 'undefined') {
+ return instance.defaults[key]
+ }
+ instance.defaults[key] = value
+ defaults[key] = value
+ } else {
+ return instance.defaults
+ }
+ },
+ defaultSettings() {
+ return defaults
+ },
+ interceptors: {
+ request: {
+ use(fnHandle, fnError) {
+ return instance.interceptors.request.use(fnHandle, fnError)
+ },
+ eject(id) {
+ return instance.interceptors.request.eject(id)
+ }
+ },
+ response: {
+ use(fnHandle, fnError) {
+ return instance.interceptors.response.use(fnHandle, fnError)
+ },
+ eject(id) {
+ return instance.interceptors.response.eject(id)
+ }
+ }
+ },
+ mock(config) {
+ if (!mock) {
+ mock = new MockAdapter(instance)
+ }
+
+ if (Array.isArray(config)) {
+ config.forEach((item) => {
+ mock.proxy(item)
+ })
+ }
+
+ return mock
+ },
+ disableMock() {
+ mock && mock.restore()
+ mock = undefined
+ },
+ isMock() {
+ return typeof mock !== 'undefined'
+ },
+ CancelToken: axios.CancelToken,
+ isCancel: axios.isCancel
+ }
+}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/config.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/config.js
new file mode 100644
index 0000000000..cfa3714e17
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/config.js
@@ -0,0 +1,15 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+export default {
+ withCredentials: false
+}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/index.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/index.js
new file mode 100644
index 0000000000..b0a08546a6
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/http/index.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+import axios from './axios'
+import config from './config'
+
+export default (dataHandler) => {
+ const http = axios(config)
+
+ http.interceptors.response.use(dataHandler, (error) => {
+ const response = error.response
+ if (response.status === 403 && response.headers && response.headers['x-login-url']) {
+ // TODO 处理无权限时,重新登录再发送请求
+ }
+ })
+
+ return http
+}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/i18n/index.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/i18n/index.js
new file mode 100644
index 0000000000..043eac53ea
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/i18n/index.js
@@ -0,0 +1,7 @@
+import i18n from '@opentiny/tiny-engine-i18n-host'
+import lowcode from '../lowcodeConfig/lowcode'
+import locale from './locale.js'
+
+i18n.lowcode = lowcode
+
+export default i18n
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/i18n/locale.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/i18n/locale.js
new file mode 100644
index 0000000000..b1c6ea436a
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/i18n/locale.js
@@ -0,0 +1 @@
+export default {}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/bridge.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/bridge.js
new file mode 100644
index 0000000000..7a19e4a116
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/bridge.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+export default () => {}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/dataSource.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/dataSource.js
new file mode 100644
index 0000000000..80e1e88e7b
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/dataSource.js
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+import useHttp from '../http'
+import dataSources from './dataSource.json'
+
+const dataSourceMap = {}
+
+// 暂时使用 eval 解析 JSON 数据里的函数
+const createFn = (fnContent) => {
+ return (...args) => {
+ // eslint-disable-next-line no-eval
+ window.eval('var fn = ' + fnContent)
+ // eslint-disable-next-line no-undef
+ return fn.apply(this, args)
+ }
+}
+
+const globalDataHandle = dataSources.dataHandler ? createFn(dataSources.dataHandler.value) : (res) => res
+
+const load = (http, options, dataSource, shouldFetch) => (params, customUrl) => {
+ // 如果没有配置远程请求,则直接返回静态数据,返回前可能会有全局数据处理
+ if (!options) {
+ return globalDataHandle(dataSource.config.data)
+ }
+
+ if (!shouldFetch()) {
+ return
+ }
+
+ dataSource.status = 'loading'
+
+ const { method, uri: url, params: defaultParams, timeout, headers } = options
+ const config = { method, url, headers, timeout }
+
+ const data = params || defaultParams
+
+ config.url = customUrl || config.url
+
+ if (method.toLowerCase() === 'get') {
+ config.params = data
+ } else {
+ config.data = data
+ }
+
+ return http.request(config)
+}
+
+dataSources.list.forEach((config) => {
+ const http = useHttp(globalDataHandle)
+ const dataSource = { config }
+
+ dataSourceMap[config.name] = dataSource
+
+ const shouldFetch = config.shouldFetch?.value ? createFn(config.shouldFetch.value) : () => true
+ const willFetch = config.willFetch?.value ? createFn(config.willFetch.value) : (options) => options
+
+ const dataHandler = (res) => {
+ const data = config.dataHandler?.value ? createFn(config.dataHandler.value)(res) : res
+ dataSource.status = 'loaded'
+ dataSource.data = data
+ return data
+ }
+
+ const errorHandler = (error) => {
+ config.errorHandler?.value && createFn(config.errorHandler.value)(error)
+ dataSource.status = 'error'
+ dataSource.error = error
+ }
+
+ http.interceptors.request.use(willFetch, errorHandler)
+ http.interceptors.response.use(dataHandler, errorHandler)
+
+ if (import.meta.env.VITE_APP_MOCK === 'mock') {
+ http.mock([
+ {
+ url: config.options?.uri,
+ response() {
+ return Promise.resolve([200, { data: config.data }])
+ }
+ },
+ {
+ url: '*',
+ proxy: '*'
+ }
+ ])
+ }
+
+ dataSource.status = 'init'
+ dataSource.load = load(http, config.options, dataSource, shouldFetch)
+})
+
+export default dataSourceMap
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/dataSource.json b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/dataSource.json
new file mode 100644
index 0000000000..2546e9ed93
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/dataSource.json
@@ -0,0 +1,3 @@
+{
+ "list": []
+}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/lowcode.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/lowcode.js
new file mode 100644
index 0000000000..29da8186b5
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/lowcode.js
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+import { getCurrentInstance, nextTick, provide, inject } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
+import { I18nInjectionKey } from 'vue-i18n'
+import dataSourceMap from './dataSource'
+import * as utils from '../utils'
+import * as bridge from './bridge'
+import { useStores } from './store'
+
+export const lowcodeWrap = (props, context) => {
+ const global = {}
+ const instance = getCurrentInstance()
+ const router = useRouter()
+ const route = useRoute()
+ const { t, locale } = inject(I18nInjectionKey).global
+ const emit = context.emit
+ const ref = (ref) => instance.refs[ref]
+
+ const setState = (newState, callback) => {
+ Object.assign(global.state, newState)
+ nextTick(() => callback.apply(global))
+ }
+
+ const getLocale = () => locale.value
+ const setLocale = (val) => {
+ locale.value = val
+ }
+
+ const location = () => window.location
+ const history = () => window.history
+
+ Object.defineProperties(global, {
+ props: { get: () => props },
+ emit: { get: () => emit },
+ setState: { get: () => setState },
+ router: { get: () => router },
+ route: { get: () => route },
+ i18n: { get: () => t },
+ getLocale: { get: () => getLocale },
+ setLocale: { get: () => setLocale },
+ location: { get: location },
+ history: { get: history },
+ utils: { get: () => utils },
+ bridge: { get: () => bridge },
+ dataSourceMap: { get: () => dataSourceMap },
+ $: { get: () => ref }
+ })
+
+ const wrap = (fn) => {
+ if (typeof fn === 'function') {
+ return (...args) => fn.apply(global, args)
+ }
+
+ Object.entries(fn).forEach(([name, value]) => {
+ Object.defineProperty(global, name, {
+ get: () => value
+ })
+ })
+
+ fn.t = t
+
+ return fn
+ }
+
+ return wrap
+}
+
+export default () => {
+ const i18n = inject(I18nInjectionKey)
+ provide(I18nInjectionKey, i18n)
+
+ const stores = useStores()
+
+ return { t: i18n.global.t, lowcodeWrap, stores }
+}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/store.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/store.js
new file mode 100644
index 0000000000..f7f39c7a84
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/lowcodeConfig/store.js
@@ -0,0 +1,13 @@
+import * as useDefinedStores from '@/stores'
+
+const useStores = () => {
+ const stores = {}
+
+ Object.values({ ...useDefinedStores }).forEach((store) => {
+ stores[store.$id] = store()
+ })
+
+ return stores
+}
+
+export { useStores }
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/main.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/main.js
new file mode 100644
index 0000000000..311640fed6
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/main.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2023 - present TinyEngine Authors.
+ * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
+ *
+ * Use of this source code is governed by an MIT-style license.
+ *
+ * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
+ * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
+ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
+ *
+ */
+
+import { createApp } from 'vue'
+import router from './router'
+import { createPinia } from 'pinia'
+import App from './App.vue'
+import 'element-plus/dist/index.css'
+const pinia = createPinia()
+createApp(App).use(pinia).use(router).mount('#app')
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/router/index.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/router/index.js
new file mode 100644
index 0000000000..c76f722207
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/router/index.js
@@ -0,0 +1,10 @@
+import { createRouter, createWebHashHistory } from 'vue-router'
+const routes = [
+ { path: '/', redirect: '/demopage' },
+ { path: '/demopage', component: () => import('@/views/DemoPage.vue') }
+]
+
+export default createRouter({
+ history: createWebHashHistory(),
+ routes
+})
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/stores/index.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/stores/index.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/utils.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/utils.js
new file mode 100644
index 0000000000..336ce12bb9
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/utils.js
@@ -0,0 +1 @@
+export {}
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/views/DemoPage.vue b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/views/DemoPage.vue
new file mode 100644
index 0000000000..274e305a42
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/src/views/DemoPage.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/vite.config.js b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/vite.config.js
new file mode 100644
index 0000000000..e1e57978b1
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/expected/appdemo01/vite.config.js
@@ -0,0 +1,23 @@
+import { defineConfig } from 'vite'
+import path from 'path'
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+
+export default defineConfig({
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, 'src')
+ }
+ },
+ plugins: [vue(), vueJsx()],
+ define: {
+ 'process.env': { ...process.env }
+ },
+ build: {
+ minify: true,
+ commonjsOptions: {
+ transformMixedEsModules: true
+ },
+ cssCodeSplit: false
+ }
+})
diff --git a/packages/vue-generator/test/testcases/element-plus-case/mockData.js b/packages/vue-generator/test/testcases/element-plus-case/mockData.js
new file mode 100644
index 0000000000..8cb3ac8af3
--- /dev/null
+++ b/packages/vue-generator/test/testcases/element-plus-case/mockData.js
@@ -0,0 +1,87 @@
+export const appSchemaDemo01 = {
+ dataSource: {
+ list: []
+ },
+ globalState: [],
+ utils: [],
+ i18n: {},
+ pageSchema: [
+ {
+ state: {},
+ methods: {},
+ componentName: 'Page',
+ css: '',
+ props: {},
+ lifeCycles: {},
+ children: [
+ {
+ componentName: 'div',
+ props: {},
+ id: '85375559',
+ children: [
+ {
+ componentName: 'ElInput',
+ props: {},
+ id: '33433546'
+ }
+ ]
+ }
+ ],
+ dataSource: {
+ list: []
+ },
+ utils: [],
+ bridge: [],
+ inputs: [],
+ outputs: [],
+ fileName: 'DemoPage',
+ meta: {
+ name: 'DemoPage',
+ id: '5bhD7p5FUsUOTFRN',
+ app: '918',
+ router: 'demopage',
+ tenant: 1,
+ isBody: false,
+ parentId: '0',
+ group: 'staticPages',
+ depth: 0,
+ isPage: true,
+ isDefault: false,
+ occupier: {
+ id: 86,
+ username: '开发者',
+ email: 'developer@lowcode.com',
+ resetPasswordToken: 'developer',
+ confirmationToken: 'dfb2c162-351f-4f44-ad5f-8998',
+ is_admin: true
+ },
+ isHome: false,
+ message: 'Page auto save',
+ _id: '5bhD7p5FUsUOTFRN'
+ }
+ }
+ ],
+ componentsMap: [
+ {
+ componentName: 'ElInput',
+ package: 'element-plus',
+ exportName: 'ElInput',
+ destructuring: true,
+ version: '^2.4.2'
+ }
+ ],
+ meta: {
+ name: 'portal-app',
+ tenant: 1,
+ git_group: '',
+ project_name: '',
+ description: 'demo应用',
+ branch: 'develop',
+ is_demo: null,
+ global_state: [],
+ appId: '918',
+ creator: '',
+ gmt_create: '2022-06-08 03:19:01',
+ gmt_modified: '2023-08-23 10:22:28'
+ }
+}