From 02ab0c2006105df799e6d43ae7f15db4dfd3559f Mon Sep 17 00:00:00 2001 From: miaomiao <2410891778@qq.com> Date: Fri, 25 Nov 2022 19:58:14 +0800 Subject: [PATCH 01/80] newCode --- lib/server/conf/data-base.js.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/server/conf/data-base.js.example b/lib/server/conf/data-base.js.example index 82d61c91a..886a1777b 100644 --- a/lib/server/conf/data-base.js.example +++ b/lib/server/conf/data-base.js.example @@ -27,11 +27,11 @@ module.exports = { }, dev: { // 本地开发数据库名 - database: '', + database: 'lesscodedata', // 本地开发数据库用户名 - username: '', + username: 'root', // 本地开发数据库密码 - password: '', + password: '1234', // 本地开发host host: 'localhost', // 本地开发端口 From 0c382f5a05709cc7129bcb11db9c487ffab56872 Mon Sep 17 00:00:00 2001 From: hmohuang <13407923955@163.com> Date: Tue, 6 Dec 2022 15:57:17 +0800 Subject: [PATCH 02/80] =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/components/app-header.vue | 89 +- lib/client/src/components/changelog-data.js | 964 ++++++++++++++++++++ 2 files changed, 1048 insertions(+), 5 deletions(-) create mode 100644 lib/client/src/components/changelog-data.js diff --git a/lib/client/src/components/app-header.vue b/lib/client/src/components/app-header.vue index f016a036f..2374097a4 100644 --- a/lib/client/src/components/app-header.vue +++ b/lib/client/src/components/app-header.vue @@ -49,9 +49,9 @@ 产品文档 - - 版本日志 - + + 版本日志 + 问题反馈 开源社区 @@ -78,14 +78,36 @@ + + + + diff --git a/lib/server/conf/data-base.js.example b/lib/server/conf/data-base.js.example index 886a1777b..82d61c91a 100644 --- a/lib/server/conf/data-base.js.example +++ b/lib/server/conf/data-base.js.example @@ -27,11 +27,11 @@ module.exports = { }, dev: { // 本地开发数据库名 - database: 'lesscodedata', + database: '', // 本地开发数据库用户名 - username: 'root', + username: '', // 本地开发数据库密码 - password: '1234', + password: '', // 本地开发host host: 'localhost', // 本地开发端口 From 6c42fbb3c2dcaaa4a35b2194821c7f4316c1b615 Mon Sep 17 00:00:00 2001 From: hmohuang <13407923955@163.com> Date: Thu, 8 Dec 2022 14:13:09 +0800 Subject: [PATCH 05/80] =?UTF-8?q?format:=E4=BB=A3=E7=A0=81=E8=A7=84?= =?UTF-8?q?=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/components/changelog-version/changelog-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client/src/components/changelog-version/changelog-data.js b/lib/client/src/components/changelog-version/changelog-data.js index 5a999bd18..a23015c44 100644 --- a/lib/client/src/components/changelog-version/changelog-data.js +++ b/lib/client/src/components/changelog-version/changelog-data.js @@ -422,7 +422,7 @@ export default [ ] }, { - reviseTitle: 'notice', + reviseTitle: '说明', logMassage: [ '由于slot相关逻辑重构改动较大,涉及存量数据更新,请用户先自行备份数据库page表数据,并在更新此版本后在浏览器执行调用 域名 / api / db - upgrade - helper /20210722_update_slot更新数据' ] From 729102dbf018ae97053eade034e6f61dd2e9819c Mon Sep 17 00:00:00 2001 From: xuzhan Date: Fri, 9 Dec 2022 15:50:23 +0800 Subject: [PATCH 06/80] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=BA=90=E7=A0=81=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/patch/widget-bk-table.vue | 7 +++++-- .../src/components/patch/widget-table-column.vue | 14 ++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-bk-table.vue b/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-bk-table.vue index 89fb4355f..4097eeeee 100644 --- a/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-bk-table.vue +++ b/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-bk-table.vue @@ -12,7 +12,7 @@ > @@ -256,7 +256,9 @@ }, tableName: String, paginationType: String, - dataValueType: String + dataValueType: String, + bkDataSourceType: String, + showOperationColumn: Boolean }, data () { @@ -456,6 +458,7 @@ pageSize: this.renderPagination?.limit, bkSortKey: this.sortObject.key, bkSortValue: this.sortObject.value, + bkDataSourceType: this.bkDataSourceType, ...this.queryObject } } diff --git a/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-table-column.vue b/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-table-column.vue index 274e7aac4..9df42f71f 100644 --- a/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-table-column.vue +++ b/lib/server/project-template/project-init-code/lib/client/src/components/patch/widget-table-column.vue @@ -1,9 +1,9 @@ @@ -14,8 +14,14 @@ name: 'widget-table-column', props: { + columnType: String, type: String, - item: Object + label: String, + prop: String, + sortable: Boolean, + width: String, + filterable: Boolean, + align: String }, data () { @@ -32,7 +38,7 @@ {}, [ column.label, - this.item.filterable + this.filterable ? h( 'bk-popover', { From adb2f71156670b85fe19a8b0c5a4f75dd6f107e9 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Mon, 12 Dec 2022 20:05:55 +0800 Subject: [PATCH 07/80] =?UTF-8?q?feat:=20=E7=94=9F=E6=88=90=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/store/index.js | 5 +- lib/server/controller/vue-code.js | 6 +- lib/shared/page-code/common/modelMethods.js | 154 ++++++++++ lib/shared/page-code/common/utils.js | 143 +++++++++ lib/shared/page-code/index.js | 197 ++++++++++++ lib/shared/page-code/script/index.js | 10 + .../page-code/script/vue2/components.js | 52 ++++ lib/shared/page-code/script/vue2/computed.js | 44 +++ lib/shared/page-code/script/vue2/data.js | 26 ++ .../script/vue2/handle-var-and-func.js | 145 +++++++++ lib/shared/page-code/script/vue2/import.js | 101 +++++++ lib/shared/page-code/script/vue2/index.js | 48 +++ lib/shared/page-code/script/vue2/lifeCycle.js | 42 +++ lib/shared/page-code/script/vue2/methods.js | 133 +++++++++ lib/shared/page-code/script/vue2/mixins.js | 9 + lib/shared/page-code/script/vue2/watch.js | 17 ++ lib/shared/page-code/script/vue3/index.js | 9 + .../style/default-style/common-style.js | 103 +++++++ .../page-code/style/default-style/index.js | 19 ++ .../style/default-style/mobile-style.js | 7 + .../page-code/style/default-style/pc-style.js | 32 ++ lib/shared/page-code/style/index.js | 33 +++ lib/shared/page-code/style/nav-style/index.js | 17 ++ .../page-code/style/nav-style/mobile-nav.js | 7 + .../page-code/style/nav-style/pc-nav.js | 219 ++++++++++++++ .../style/page-setting-style/index.js | 30 ++ lib/shared/page-code/template/index.js | 24 ++ .../page-code/template/navigation/index.js | 47 +++ .../template/navigation/mobile-navigation.js | 25 ++ .../template/navigation/web-navigation.js | 280 ++++++++++++++++++ .../page-code/template/nocode-page/index.js | 50 ++++ .../template/page/component/index.js | 123 ++++++++ .../page/component/special-type-flag.js | 36 +++ .../page/component/special-type/bk-flow.js | 47 +++ .../page/component/special-type/chart.js | 48 +++ .../template/page/component/special-type/p.js | 32 ++ .../special-type/self-closing-tag.js | 20 ++ .../page/component/special-type/table.js | 19 ++ .../component/special-type/widget-form.js | 18 ++ .../page-code/template/page/directive.js | 56 ++++ lib/shared/page-code/template/page/event.js | 26 ++ lib/shared/page-code/template/page/index.js | 10 + .../page-code/template/page/layout/index.js | 74 +++++ .../template/page/layout/mobile-layout.js | 55 ++++ lib/shared/page-code/template/page/prop.js | 273 +++++++++++++++++ lib/shared/page-code/template/page/slot.js | 108 +++++++ lib/shared/page-code/template/page/style.js | 52 ++++ 47 files changed, 3027 insertions(+), 4 deletions(-) create mode 100644 lib/shared/page-code/common/modelMethods.js create mode 100644 lib/shared/page-code/common/utils.js create mode 100644 lib/shared/page-code/index.js create mode 100644 lib/shared/page-code/script/index.js create mode 100644 lib/shared/page-code/script/vue2/components.js create mode 100644 lib/shared/page-code/script/vue2/computed.js create mode 100644 lib/shared/page-code/script/vue2/data.js create mode 100644 lib/shared/page-code/script/vue2/handle-var-and-func.js create mode 100644 lib/shared/page-code/script/vue2/import.js create mode 100644 lib/shared/page-code/script/vue2/index.js create mode 100644 lib/shared/page-code/script/vue2/lifeCycle.js create mode 100644 lib/shared/page-code/script/vue2/methods.js create mode 100644 lib/shared/page-code/script/vue2/mixins.js create mode 100644 lib/shared/page-code/script/vue2/watch.js create mode 100644 lib/shared/page-code/script/vue3/index.js create mode 100644 lib/shared/page-code/style/default-style/common-style.js create mode 100644 lib/shared/page-code/style/default-style/index.js create mode 100644 lib/shared/page-code/style/default-style/mobile-style.js create mode 100644 lib/shared/page-code/style/default-style/pc-style.js create mode 100644 lib/shared/page-code/style/index.js create mode 100644 lib/shared/page-code/style/nav-style/index.js create mode 100644 lib/shared/page-code/style/nav-style/mobile-nav.js create mode 100644 lib/shared/page-code/style/nav-style/pc-nav.js create mode 100644 lib/shared/page-code/style/page-setting-style/index.js create mode 100644 lib/shared/page-code/template/index.js create mode 100644 lib/shared/page-code/template/navigation/index.js create mode 100644 lib/shared/page-code/template/navigation/mobile-navigation.js create mode 100644 lib/shared/page-code/template/navigation/web-navigation.js create mode 100644 lib/shared/page-code/template/nocode-page/index.js create mode 100644 lib/shared/page-code/template/page/component/index.js create mode 100644 lib/shared/page-code/template/page/component/special-type-flag.js create mode 100644 lib/shared/page-code/template/page/component/special-type/bk-flow.js create mode 100644 lib/shared/page-code/template/page/component/special-type/chart.js create mode 100644 lib/shared/page-code/template/page/component/special-type/p.js create mode 100644 lib/shared/page-code/template/page/component/special-type/self-closing-tag.js create mode 100644 lib/shared/page-code/template/page/component/special-type/table.js create mode 100644 lib/shared/page-code/template/page/component/special-type/widget-form.js create mode 100644 lib/shared/page-code/template/page/directive.js create mode 100644 lib/shared/page-code/template/page/event.js create mode 100644 lib/shared/page-code/template/page/index.js create mode 100644 lib/shared/page-code/template/page/layout/index.js create mode 100644 lib/shared/page-code/template/page/layout/mobile-layout.js create mode 100644 lib/shared/page-code/template/page/prop.js create mode 100644 lib/shared/page-code/template/page/slot.js create mode 100644 lib/shared/page-code/template/page/style.js diff --git a/lib/client/src/store/index.js b/lib/client/src/store/index.js index f0065bac2..489f572c5 100644 --- a/lib/client/src/store/index.js +++ b/lib/client/src/store/index.js @@ -42,7 +42,8 @@ import router from '../router' import { unifyObjectStyle, json2Query, circleJSON } from '@/common/util' import LC from '@/element-materials/core' // 生成源码重构后,需要支持前端单独调用 -import { getPageData } from '../../../server/model/page-code' +import getPageCode from '../../../shared/page-code' +// import { getPageData } from '../../../server/model/page-code' import { IAM_ACTION } from 'shared/constant' Vue.use(Vuex) @@ -212,7 +213,7 @@ const store = new Vuex.Store({ } const funcGroups = JSON.parse(circleJSON(state.functions.funcGroups || [])) const variableList = JSON.parse(circleJSON(state.variable.variableList || [])) - const pageData = getPageData({ + const pageData = getPageCode({ targetData, pageType: 'preview', platform: state.page.pageDetail?.pageType, diff --git a/lib/server/controller/vue-code.js b/lib/server/controller/vue-code.js index f0de728ef..2f6b1dcc6 100644 --- a/lib/server/controller/vue-code.js +++ b/lib/server/controller/vue-code.js @@ -9,7 +9,8 @@ * specific language governing permissions and limitations under the License. */ import VueCodeModel from '../model/vue-code' -import PageCodeModel from '../model/page-code' +// import PageCodeModel from '../model/page-code' +import getPageCode from '../../shared/page-code' import routeModel from '../model/route' import variableModel from '../model/variable' @@ -120,7 +121,8 @@ const VueCode = { } const pagePlatform = platform || 'PC' // platform默认为PC, 但主要以curPage数据库中的标志位为准; 找不到再以platform为准 const pageTargetData = Array.isArray(targetData) && targetData.length > 0 ? targetData : JSON.parse(curPage.content || '[]') - const pageCodeData = await PageCodeModel.getPageData({ + const pageCodeData = await getPageCode({ + // const pageCodeData = await PageCodeModel.getPageData({ targetData: pageTargetData, platform: curPage.platform ? curPage.platform : pagePlatform, pageType, diff --git a/lib/shared/page-code/common/modelMethods.js b/lib/shared/page-code/common/modelMethods.js new file mode 100644 index 000000000..4655d280f --- /dev/null +++ b/lib/shared/page-code/common/modelMethods.js @@ -0,0 +1,154 @@ +import { getValue, getMethodByCode } from './utils' +import { uuid } from '../../util' + +/** + * @desc 追加样式,将页面每个元素的样式追加到css中 + * @param { CodeModel } code + * @param { String } cssStr 需要拼接的css样式 + */ +export function appendCss (code, cssStr) { + code.cssStr += cssStr +} + +/** + * @desc 添加变量到data中 + * @param { CodeModel } code + * @param { String } key 需要添加的变量key + * @param { String } value 需要添加的变量value + */ +export function dataTemplate (code, key, value) { + Object.assign(code.dataObj, { [key]: value }) +} + +/** + * @desc 生成远程函数 + * @param { CodeModel } code + * @param { String } key 远程函数key + * @param { Object } payload 放置远程函数相关的信息 + * @param { Boolean } isChartType 是否是图表组件相关函数 + */ +export function remoteMethodsTemplate (code, key, payload, chartType = false) { + const [method, params] = getMethodByCode(payload, code.funcGroups) + if (method.id) { + if (chartType) { + code.remoteDataStr += key.endsWith('options') + ? `const ${key}Remote = await this.${method.funcName}(${getFuncParamStr(code, method, params, `远程函数【${method.funcName}】`, true)})\nthis.$set(this, '${key}', ${key}Remote)\n` + : `const ${key}Remote = await this.${method.funcName}(${getFuncParamStr(code, method, params, `远程函数【${method.funcName}】`, true)})\nObject.assign(this.${key}, ${key}Remote)\n` + } else { + code.remoteDataStr += `this.${key} = await this.${method.funcName}(${getFuncParamStr(code, method, params, `远程函数【${method.funcName}】`, true)})\n` + } + } + // 处理chartRemote + if (method.funcCode) addUsedFunc(code, method.funcCode) +} + +/** + * @desc 生成数据源函数 + * @param { CodeModel } code + * @param { String } key 数据源函数key + * @param { Object } payload 放置远程函数相关的信息 + */ +export function dataSourceTemplate (code, key, sourceData) { + const hashId = uuid(4) + code.remoteDataStr += `const ${key}Source${hashId} = await this.$http.get('/data-source/user/tableName/${sourceData.tableName}?bkDataSourceType=${sourceData.dataSourceType}')\nthis.${key} = ${key}Source${hashId}.data.list || []\n` +} + +/** + * @desc 获取解析后的函数参数 + * @param { CodeModel } code + * @param { Object } method 函数相关信息 + * @param { Object } params 参数列表 + * @param { String } errMessagePerfix 错误提示 + * @param { Boolean } withThis 是否包含this + * @param { String } defaultFormat 默认格式 + * @returns { String } 解析后的参数字符串 + */ +export function getFuncParamStr (code, method, params, errMessagePerfix, withThis, defaultFormat = 'value') { + const displayParams = method?.funcParams?.map((paramKey, index) => { + let param = { + value: '', + code: '', + format: defaultFormat + } + if (params?.[index]) { + param = params[index] + } + return param + }) || [] + + return displayParams + ?.reduce((acc, cur, index) => { + if (cur.format === 'value') { + acc.push(code.handleUsedVariable(cur.format, cur.value, errMessagePerfix) || '\'\'') + } else if (withThis && cur.format === 'variable') { + acc.push(`this.${code.handleUsedVariable(cur.format, cur.code, errMessagePerfix)}`) + } else if (cur.format === 'event') { + acc.push(`args[${index}]`) + } else { + acc.push(code.handleUsedVariable(cur.format, cur.code, errMessagePerfix)) + } + return acc + }, []) + .join(', ') +} + +/** + * @desc 处理页面用到的变量 + * @param { CodeModel } code + * @param { String } 处理页面用到的变量 + */ +export function handleUsedVariable (code, valType, val, errMessagePerfix, dataSourceId, dataSourceType) { + let disPlayVal = val + switch (valType) { + case 'value': + disPlayVal = getValue(val) + break + case 'variable': + const variable = code.variableList.find(x => x.variableCode === val) + // form表单内的v-model绑定值忽略这个判断 + if (!variable && !(errMessagePerfix.indexOf('v-model') > 0 && val.indexOf('.') > 0)) { + code.codeErrMessage = `${errMessagePerfix}使用了不存在的变量【${val}】,请修改后重试` + } + if (variable) { + addUsedVariable(code, variable) + } + break + case 'expression': + code.variableList.forEach((variable) => { + if (val.includes(variable.variableCode)) { + addUsedVariable(code, variable) + } + }) + break + case 'dataSource': + disPlayVal = dataSourceId + dataTemplate(code, dataSourceId, JSON.stringify([])) + dataSourceTemplate(code, dataSourceId, { tableName: val, dataSourceType }) + break + } + return disPlayVal +} + +/** + * @desc 记录页面用到的函数 + * @param { CodeModel } code + * @param { String } 函数code + */ +export function addUsedFunc (code, funcCode) { + if (code.usingFuncCodes.includes(funcCode)) return + + code.unhandledFunc.push(funcCode) + code.usingFuncCodes.push(funcCode) +} + +/** + * @desc 记录页面用到的变量 + * @param { CodeModel } code + * @param { String } 变量code + */ +export function addUsedVariable (code, variable) { + if (code.usingVariables.find(x => x.variableCode === variable.variableCode)) return + + code.unhandledVariables.push(variable) + code.usingVariables.push(variable) +} diff --git a/lib/shared/page-code/common/utils.js b/lib/shared/page-code/common/utils.js new file mode 100644 index 000000000..d22d282e4 --- /dev/null +++ b/lib/shared/page-code/common/utils.js @@ -0,0 +1,143 @@ +import { paramCase } from 'change-case' +import { unitFilter } from '../../util' + +import safeStringify from '../../../client/src/common/json-safe-stringify' + +/** + * @desc 判断输入值的类型 + * @param { String } val 需要判读的值 + * @returns { String } 类型 + */ +function getValueType (val) { + const Fn = Function + let type = 'undefined' + // 用户输入的不符合规范的json,按照字符串处理 + try { + type = new Fn(`return typeof ${val}`)() + } catch (error) { + } + return type +} + +/** + * @desc 根据methodCode函数函数信息 + * @param { String } methodCode 函数code + * @param { Array } funcGroups 项目用到的函数列表 + * @returns { Array } [函数配置、参数信息] + */ +export function getMethodByCode (methodCode, funcGroups = []) { + let params = [] + + if (typeof methodCode === 'object') { + params = methodCode.params || [] + methodCode = methodCode.methodCode + } + const res = funcGroups.map(group => group.children).flat().find(func => func.funcCode === methodCode) + return [res || {}, params] +} + +/** + * @desc 获取写在template的value + * @param { String } val 需要处理的值 + * @returns { String } 处理后的value + */ +export function getValue (val) { + let value = val + const type = getValueType(val) + switch (type) { + case 'undefined': + value = `'${val}'` + if (val === 'undefined') value = 'undefined' + break + } + if (/[^\.\=><]+[\.\=><\']+[^\.\=><\']+/.test(val)) value = val + return value +} + +/** + * @desc 将不同类型的值转换为字符串 + * @param { Any } val 需要处理的值 + * @returns { String } 处理后的值 + */ +export function transformToString (val) { + const type = typeof val + let res + switch (type) { + case 'object': + res = safeStringify(val) + break + case 'string': + res = `'${val}'` + break + default: + res = val + break + } + return res +} + +/** + * @desc 合并属性面板样式和自定义样式,并统一转成连字符 + * @param { Object } renderStyles 组件的样式配置 + * @returns { Object } 合并处理后的styles + */ +export function handleRenderStyles (renderStyles = {}) { + const styles = {} + if (renderStyles && typeof renderStyles === 'object' && Object.keys(renderStyles).length > 0) { + if (renderStyles['customStyle']) { + Object.assign(renderStyles, renderStyles['customStyle']) + delete renderStyles['customStyle'] + } + for (const key in renderStyles) { + if (renderStyles[key]) { + Object.assign(styles, { [paramCase(key)]: unitFilter(renderStyles[key]) }) + } + } + } + return styles +} + +/** + * @desc 生成处理freeeLayoutItem的css,并处理内部component的styles + * @param { Object } styles 组件的样式配置 + * @returns { Object } { css、 styles } + */ +export function getFreeLayoutItemStyle (styles = {}) { + let css = 'position: absolute;' + delete styles.position + // 自由布局部分属性需要设置在外层div + const containerStyles = ['top', 'left', 'margin-left', 'margin-bottom', 'margin-right', 'margin-top', 'margin', 'z-index'] + containerStyles.forEach(style => { + if (styles[style]) { + css += ` ${style}: ${styles[style]};` + delete styles[style] + } + }) + if (styles.height && styles.height.endsWith('%')) { + css += ` height: ${styles.height};` + styles.height = '100%' + } + if (styles.width && styles.width.endsWith('%')) { + css += ` width: ${styles.width};` + styles.width = '100%' + } + return { css, styles } +} + +/** + * @desc 获取自动对齐相关的样式前缀 + * @param { Object } renderAlign 对齐相关的属性配置 + * @param { Boolean } inFreeLayout 是否在自由布局内 + * @returns { String } 样式前缀 + */ +export function getAlignStr (renderAlign = {}, inFreeLayout = false) { + let alignStr = ' ' + const prefix = inFreeLayout ? 'absolute-' : '' + if (renderAlign.horizontal) { + alignStr += `${prefix}${renderAlign.horizontal} ` + } + if (renderAlign.vertical) { + alignStr += `${prefix}${renderAlign.vertical} ` + } + return alignStr +} diff --git a/lib/shared/page-code/index.js b/lib/shared/page-code/index.js new file mode 100644 index 000000000..7d2e1649f --- /dev/null +++ b/lib/shared/page-code/index.js @@ -0,0 +1,197 @@ +import generateTemplate from './template' +import generateScript from './script' +import generateStyle from './style' +import { + appendCss, + dataTemplate, + remoteMethodsTemplate, + dataSourceTemplate, + getFuncParamStr, + handleUsedVariable, + addUsedFunc, + addUsedVariable +} from './common/modelMethods' + +class NewPageCode { + cssStr = '' // css样式str + dataObj = {} // 暂存使用到的data变量map + remoteDataStr = '' // 远程函数使用列表str + isUseElement = false // 是都使用element组件库 + isUseSwiper = false // 是否使用H5-container容器,使用Swiper组件 + isUseBkCharts = false // 是否使用bkcharts标志位 + chartTypeArr = [] // 使用的echarts图表类型列表 + usingCustomArr = [] // 使用的自定义组件列表 + usingFuncCodes = [] // 使用到的函数code + unhandledFunc = [] // 暂存未处理的函数列表 + usingVariables = [] // 使用到的变量列表 + unhandledVariables = [] // 暂存未处理的变量列表 + projectVariables = [] // 放到store文件的应用级变量列表 + pageDataVariables = [] // 放到data中的非computed类型变量 + pageComputedVariables = [] // computed类型变量 + methodStrList = [] // 使用到的函数字符串列表 + codeErrMessage = '' // 记录生成源码中里的错误信息 + + // 支持设置权限的组件 type 集合 + appPermComponents = { + 'bk-button': 'auth-button' + } + + constructor ({ + platform = 'PC', + targetData = [], + pageType = 'vueCode', + funcGroups = [], + lifeCycle = {}, + styleSetting = {}, + projectId = '', + pageId = '', + layoutContent = {}, + isGenerateNav = false, + isEmpty = false, + layoutType = '', + nocodeType = '', + nocodePayload = {}, + deletePageCodes = [], + variableList = [], + user = {}, + npmConf = {}, + apiList = [], + isRenderAppPermComponents = true + + }) { + // vueCode: 查看页面源码 projectCode:项目源码 preview:预览项目 previewSingle:预览单页面/导航 + this.pageType = pageType + // PC、MOBILE + this.platform = platform || 'PC' + // nocode页面类型及payload + this.nocodeType = nocodeType || '' + this.nocodePayload = nocodePayload || {} + + // 页面内容及配置、包括页面内容targetData、样式设置、生命周期函数 + this.targetData = targetData || [] + this.styleSetting = styleSetting || {} + this.lifeCycle = typeof lifeCycle === 'string' ? JSON.parse(lifeCycle) : lifeCycle + this.exisLifyCycle = Object.values(this.lifeCycle).filter(x => x) || [] + + // 页面导航布局信息 + this.isEmpty = isEmpty + this.isGenerateNav = isGenerateNav + this.layoutType = layoutType + this.layoutContent = layoutContent || {} + this.hasLayout = layoutContent && ((layoutContent.menuList && layoutContent.menuList.length) || (layoutContent.topMenuList && layoutContent.topMenuList.length)) + + // uniqueKey、用作页面唯一标识 + this.projectId = projectId + this.pageId = pageId + this.uniqueKey = `${projectId}-${pageId}` + + // 是否渲染绑定权限操作的组件,默认为渲染,当预览时,不渲染 + this.isRenderAppPermComponents = isRenderAppPermComponents + + // 已删除的页面pagecode + this.deletePageCodes = deletePageCodes || [] + + // 应用函数及变量 + this.variableList = variableList || [] + this.funcGroups = funcGroups || [] + this.apiList = apiList || [] + + // 配置信息 + this.user = user || {} + this.npmConf = npmConf || {} + } + + /** + * @desc 生成页面源码,源码由 template、script、style三部分组成 + * @param { Object } params + * @returns { String } 整个页面源码 + */ + getCode () { + return generateTemplate(this) + generateScript(this) + generateStyle(this) + } + + /** + * @desc 追加样式,将页面每个元素的样式追加到css中 + * @param { String } cssStr 需要拼接的css样式 + */ + appendCss (cssStr) { + appendCss(this, cssStr) + } + + /** + * @desc 添加变量到data中 + * @param { String } key 需要添加的变量key + * @param { String } value 需要添加的变量value + */ + dataTemplate (key, value) { + dataTemplate(this, key, value) + } + + /** + * @desc 生成远程函数 + * @param { String } key 远程函数key + * @param { Object } payload 放置远程函数相关的信息 + * @param { Boolean } isChartType 是否是图表组件相关函数 + */ + remoteMethodsTemplate (key, payload, isChartType = false) { + remoteMethodsTemplate(this, key, payload, isChartType) + } + + /** + * @desc 生成数据源函数 + * @param { String } key 数据源函数key + * @param { Object } payload 放置远程函数相关的信息 + */ + dataSourceTemplate (key, sourceData) { + dataSourceTemplate(this, key, sourceData) + } + + /** + * @desc 获取解析后的函数参数 + * @param { Object } params 参数列表 + * @param { String } errMessagePerfix 错误提示 + * @param { Boolean } withThis 是否包含this + * @returns { String } 解析后的参数字符串 + */ + getFuncParamStr (params, errMessagePerfix, withThis) { + return getFuncParamStr(this, params, errMessagePerfix, withThis) + } + + /** + * @desc 处理页面用到的变量 + * @param { String } 处理页面用到的变量 + */ + handleUsedVariable (valType, val, errMessagePerfix, dataSourceId, dataSourceType) { + return handleUsedVariable(this, valType, val, errMessagePerfix, dataSourceId, dataSourceType) + } + + /** + * @desc 记录页面用到的函数 + * @param { String } 函数code + */ + addUsedFunc (funcCode) { + addUsedFunc(this, funcCode) + } + + /** + * @desc 记录页面用到的变量 + * @param { String } 变量code + */ + addUsedVariable (variable) { + addUsedVariable(this, variable) + } +} + +/** + * @desc 暴露给外部调用的入口函数,接收参数、返回源码、函数列表跟错误提示信息 + * @param { Object } params + * @returns { Object } code: 源码、methodStrList:函数列表、codeErrMessage: 错误提示信息 + */ +export default function (params) { + const pageCode = new NewPageCode(params) + return { + code: pageCode.getCode(), + methodStrList: pageCode.methodStrList || [], + codeErrMessage: pageCode.codeErrMessage || '' + } +} diff --git a/lib/shared/page-code/script/index.js b/lib/shared/page-code/script/index.js new file mode 100644 index 000000000..9e8cfc5ed --- /dev/null +++ b/lib/shared/page-code/script/index.js @@ -0,0 +1,10 @@ +import getVue2Script from './vue2' +import getVue3Scipt from './vue3' + +export default function (code) { + if (code?.vueType === 'vue3') { + return getVue3Scipt(code) + } else { + return getVue2Script(code) + } +} diff --git a/lib/shared/page-code/script/vue2/components.js b/lib/shared/page-code/script/vue2/components.js new file mode 100644 index 000000000..cb406ef80 --- /dev/null +++ b/lib/shared/page-code/script/vue2/components.js @@ -0,0 +1,52 @@ +import { camelCase, camelCaseTransformMerge } from 'change-case' + +/** + * @desc 返回components内容 + * @param { CodeModel } code + * @returns { String } + */ +export default function (code) { + // 预览时,用到的组件都已在工程中全局导入,不需要这部分 + if (['preview', 'previewSingle'].includes(code.pageType)) return '' + + // 工程中使用了charts或者nocode类型组件 + let componentStr = '' + if (code.chartTypeArr && code.chartTypeArr.length) { + componentStr += 'chart: ECharts,\n' + } + if (code.isUseBkCharts) { + componentStr += 'bkCharts: bkCharts,\n' + } + if (['FORM', 'FLOW'].includes(code.nocodeType)) { + componentStr += 'ProcessForm,\n' + } + if (['FORM_MANAGE', 'FLOW_MANAGE'].includes(code.nocodeType)) { + componentStr += 'DataManage,\n' + } + if (code.isUseSwiper) { + componentStr += 'Swiper,\n SwiperSlide\n' + } + + // 引入自定义组件 + if (code.usingCustomArr && code.usingCustomArr.length) { + let customStr = '' + // dev 和 t 环境,npm 包名字前面加了 test- 前缀,生成的变量名字应该去掉 test 前缀 + let forkUsingCustomArr = code.usingCustomArr + if (process.env.BKPAAS_ENVIRONMENT !== 'prod') { + forkUsingCustomArr = code.usingCustomArr.map(item => item.replace(/^test\-/, '')) + } + for (const i in code.usingCustomArr) { + customStr += `${camelCase(forkUsingCustomArr[i], { transform: camelCaseTransformMerge })},\n` + } + componentStr += customStr + } + if (componentStr) { + if (componentStr.endsWith(',\n')) { + componentStr = componentStr.substr(0, componentStr.length - 2) + } + componentStr = `components: { + ${componentStr} + },` + } + return componentStr +} diff --git a/lib/shared/page-code/script/vue2/computed.js b/lib/shared/page-code/script/vue2/computed.js new file mode 100644 index 000000000..66fd8b699 --- /dev/null +++ b/lib/shared/page-code/script/vue2/computed.js @@ -0,0 +1,44 @@ +/** + * @desc 返回computed内容 + * @param { CodeModel } code + * @returns { String } + */ +export default function (code) { + let computed = '' + // isUseSwiper时,添加bkH5Container的compute属性,获取swiper实例 + if ((['vueCode', 'projectCode'].includes(code.pageType) && code.hasLayout) || code.projectVariables.length || code.pageComputedVariables.length || code.isUseSwiper) { + let computedCon = '' + if (['vueCode', 'projectCode'].includes(code.pageType) && code.hasLayout) { + computedCon += '...mapGetters([\'user\']),\n' + } + // 生成项目源码或预览时,项目级变量统一放到store中 + code.projectVariables.forEach((variable) => { + computedCon += `${variable.variableCode}: { + get () { + return this.$store.state.variable.${variable.variableCode} + }, + set (val) { + this.$store.dispatch('variable/setBkProjectVariable', { code: '${variable.variableCode}', val }) + } + }, + ` + }) + // computed类型变量 + code.pageComputedVariables.forEach((variable) => { + computedCon += `${variable.variableCode} () { + ${variable.defaultValue.all} + }, + ` + }) + // 使用了h5容器 + if (code.isUseSwiper) { + computedCon += `bkH5Container() { + return this.$refs['h5-container'].$swiper + }` + } + computed = `computed: { + ${computedCon} + },` + } + return computed +} diff --git a/lib/shared/page-code/script/vue2/data.js b/lib/shared/page-code/script/vue2/data.js new file mode 100644 index 000000000..92f26063f --- /dev/null +++ b/lib/shared/page-code/script/vue2/data.js @@ -0,0 +1,26 @@ +/** + * @desc 返回data内容 + * @param { CodeModel } code + * @returns { String } + */ +export default function (code) { + let dataStr = '' + // 将dataObj中的变量拼接成data字符串 + if (Object.keys(code.dataObj).length || code.pageDataVariables.length) { + let dataCon = '' + for (const key in code.dataObj) { + dataCon += `'${key}': ${code.dataObj[key]},\n` + } + dataStr = `data () { + ${code.pageDataVariables.length ? `function getInitVariableValue (defaultValue, defaultValueType) { + let val = defaultValue.all + if (defaultValueType === 1) val = defaultValue[window.BKPAAS_ENVIRONMENT] + return val + }` : ''} + return { + ${dataCon} + } + },` + } + return dataStr +} diff --git a/lib/shared/page-code/script/vue2/handle-var-and-func.js b/lib/shared/page-code/script/vue2/handle-var-and-func.js new file mode 100644 index 000000000..3b451f22a --- /dev/null +++ b/lib/shared/page-code/script/vue2/handle-var-and-func.js @@ -0,0 +1,145 @@ +import { replaceFuncKeyword, replaceFuncParam, getRemoteFunctionInfo } from '../../../function/helper' +import { VARIABLE_TYPE, VARIABLE_EFFECTIVE_RANGE } from '../../../variable/constant' +import { getMethodByCode } from '../../common/utils' + +/** + * @desc 解析完json以后,处理使用到的函数和变量,方便后续生成源码使用 + * @param { CodeModel } code + */ +export default function handleUsedVarAndFunc (code) { + while (code.unhandledFunc.length > 0 || code.unhandledVariables.length > 0) { + // 处理函数 + for (let index = 0, l = code.unhandledFunc.length; index < l; index++) { + const funcCode = code.unhandledFunc.shift() + const func = getCompleteFuncByCode(code, funcCode) || {} + if (func.code) { + code.methodStrList.push({ + id: func.id, + funcStr: func.code + }) + } + } + + // 处理变量 + for (let index = 0, l = code.unhandledVariables.length; index < l; index++) { + const variable = code.unhandledVariables.shift() + + // 项目级别变量,添加到store中 + if ( + !['vueCode', 'previewSingle'].includes(code.pageType) + && variable.effectiveRange === VARIABLE_EFFECTIVE_RANGE.PROJECT + ) { + code.projectVariables.push(variable) + } + + // 页面级别变量 + if ( + ['vueCode', 'previewSingle'].includes(code.pageType) + || variable.effectiveRange === VARIABLE_EFFECTIVE_RANGE.PAGE + ) { + // 处理非计算变量 + if (variable.valueType !== VARIABLE_TYPE.COMPUTED.VAL) { + const { defaultValue = {}, variableCode, defaultValueType } = variable + if ([VARIABLE_TYPE.ARRAY.VAL, VARIABLE_TYPE.OBJECT.VAL].includes(variable.valueType)) { + ['all', 'prod', 'stag'].forEach((key) => { + const val = defaultValue[key] + if (typeof val === 'string' && val) { + defaultValue[key] = JSON.parse(val) + } + }) + } + code.dataTemplate(variableCode, `getInitVariableValue(${JSON.stringify(defaultValue)}, ${defaultValueType})`) + code.pageDataVariables.push(variable) + } + + // 处理计算变量 + if (variable.valueType === VARIABLE_TYPE.COMPUTED.VAL) { + variable.defaultValue.all = processFuncBody(code.variable.defaultValue.all) + code.pageComputedVariables.push(variable) + } + } + } + } +} + +/** + * @desc 根据methodCode获取函数详情 + * @param { CodeModel } code + * @param { String } methodCode + * @returns { String } + */ +function getCompleteFuncByCode (code, methodCode) { + const [returnMethod] = getMethodByCode(methodCode, code.funcGroups) || { + id: '', + funcName: 'emptyFunc', + previewStr: '', + vueCodeStr: '', + funcBody: '' + } + const paramsStr = (returnMethod.funcParams || []).join(', ') + // 函数体内容有await, 则方法前面需要添加async + const addFuncStr = (funcBody = '') => { + const hasAwait = /await\s/.test(funcBody) + return `${hasAwait ? 'async' : ''} ${returnMethod.funcName} (${paramsStr}) { ${funcBody} }` + } + const funcBody = processFuncBody(code, returnMethod.funcBody) + // 远程函数类型 + if (returnMethod.funcType === 1) { + const remoteParams = (returnMethod.remoteParams || []).join(', ') + const { + apiDataString, + codes + } = getRemoteFunctionInfo(returnMethod) + codes.forEach((funcCode) => { + code.handleVarInFunc(funcCode) + }) + // 构造 url + let funcApiUrl = returnMethod.funcApiUrl + if (returnMethod?.apiChoosePath?.find(path => path.id === 'lesscode-api')) { + const apiData = code.apiList.find(api => api.code === returnMethod.apiChoosePath[2].code) + funcApiUrl = apiData?.url || returnMethod.funcApiUrl + } + const data = `{ + url: \`${processFuncUrl(funcApiUrl)}\`, + type: '${returnMethod.funcMethod}', + apiData: ${apiDataString}, + withToken: ${returnMethod.withToken} + }` + returnMethod.code = addFuncStr(`return this.$store.dispatch('getApiData', ${data}).then((${remoteParams}) => { ${funcBody} }).catch((err) => { console.error(err) })`) + } else { + returnMethod.code = addFuncStr(funcBody) + } + return returnMethod +} + +/** + * @desc 解析处理函数url + * @param { CodeModel } code + * @param { String } str url + * @returns { String } + */ +function processFuncBody (code, bodyCode) { + // 需要删除首行注释和转义尖括号 + const encodeCode = (bodyCode || '') + .replace( + /(<)(\/?)([^>\r\n]+)(>)/gi, + (match, p1, p2, p3, p4, offset, string) => `\\${p1}` + `${p2 && `\\${p2}`}` + p3 + `\\${p4}` + ) + .replace(/^\/\*[\s\S]*?\*\/(\r\n)?/, '') + + return replaceFuncKeyword(encodeCode, (all, first, second, dirKey, funcStr, funcCode) => { + return code.handleVarInFunc(dirKey, funcCode) || all + }) +} + +/** + * @desc 解析处理函数url + * @param { CodeModel } code + * @param { String } str url + * @returns { String } + */ +function processFuncUrl (code, str) { + return replaceFuncParam(str || '', (variableCode) => { + return `\$\{${code.handleVarInFunc(variableCode)}\}` + }) +} diff --git a/lib/shared/page-code/script/vue2/import.js b/lib/shared/page-code/script/vue2/import.js new file mode 100644 index 000000000..e8f40715e --- /dev/null +++ b/lib/shared/page-code/script/vue2/import.js @@ -0,0 +1,101 @@ +import { camelCase, camelCaseTransformMerge } from 'change-case' + +/** + * @desc 返回import内容 + * @param { CodeModel } code + * @returns { String } + */ +export default function (code) { + let importStr = '' + + // preview 模板preview(previewSingle)不需要引入,已经全局引入 + if (['preview', 'previewSingle'].includes(code.pageType)) return importStr + + // 组件库安装提示 + importStr = `/** + * 请先安装 bk-magic-vue 组件库、bkui-vue-complex 复合组件库${code.isUseElement ? '以及 element-ui 组件库' : ''} + * bk-magic-vue 组件库: https://magicbox.bk.tencent.com/static_api/v3/components_vue/2.0/example/index.html#/install + * bkui-vue-complex 复合组件库: https://github.com/TencentBlueKing/lesscode-comp${code.isUseElement ? '\n* element-ui 组件库: https://element.eleme.cn/#/zh-CN/component/installation' : ''} + * + * 如果页面使用了远程函数,单独使用本页面,需要确保项目 store 下有相应的方法,后端有相应的转发接口 + */ + ` + + // 使用了h5容器 + if (code.isUseSwiper) { + importStr += ` + /** + * 请先安装 Swiper 相关依赖: npm install swiper@5.2.0 vue-awesome-swiper@4.1.1 + */ + ` + importStr += ` + import { Swiper, SwiperSlide } from 'vue-awesome-swiper' + import 'swiper/css/swiper.css' + import * as swiperAni from '@/common/swiper.animate.min.js' + import '@/css/animate.min.css'` + } + + // 使用了bkcharts + if (code.isUseBkCharts) { + importStr += ` + /** + * 请先安装 bk-charts 相关依赖: npm install @blueking/bkcharts + */ + ` + importStr += 'const bkCharts = require(\'@/components/bkCharts\')\n' + } + + // 使用了echarts + if (code.chartTypeArr && code.chartTypeArr.length) { + importStr += `/** + * 请先安装 echarts 相关依赖: npm install echarts vue-echarts + * 更多使用请参考:https://github.com/ecomfe/vue-echarts#usage + */ + const ECharts = require('vue-echarts/components/ECharts.vue') + require('echarts/lib/component/tooltip') + require('echarts/lib/component/title') + require('echarts/lib/component/legend') + ` + for (const i in code.chartTypeArr) { + importStr += `require('echarts/lib/chart/${code.chartTypeArr[i]}')\n` + } + } + + // 表单、流程类型页面 + if (['FORM', 'FLOW'].includes(code.nocodeType)) { + importStr += 'import ProcessForm from \'@/components/flow-form-comp/process-form\'\n' + } + if (['FORM_MANAGE', 'FLOW_MANAGE'].includes(code.nocodeType)) { + importStr += 'import DataManage from \'@/components/flow-form-comp/data-manage\'\n' + } + + // 查看源码和生成项目源码时, store相关提示 跟import相关方法 + if (code.hasLayout && ['projectCode', 'vueCode'].includes(code.pageType)) { + if (code.pageType === 'vueCode') { + importStr += `/** + * 请在项目 store 里存入用户相关信息 + * 请在项目 common 里完善退出登陆相关方法 + */ + ` + } + importStr += 'import { mapGetters } from \'vuex\'\n' + importStr += 'import auth from \'@/common/auth\'\n' + } + + // import页面使用到的自定义组件 + if (!['preview', 'previewSingle'].includes(code.pageType) && code.usingCustomArr && code.usingCustomArr.length) { + // dev 和 t 环境,npm 包名字前面加了 test- 前缀,生成的变量名字应该去掉 test 前缀 + let forkUsingCustomArr = code.usingCustomArr + if (process.env.BKPAAS_ENVIRONMENT !== 'prod') { + forkUsingCustomArr = code.usingCustomArr.map(item => item.replace(/^test\-/, '')) + } + for (const i in code.usingCustomArr) { + importStr += `const ${camelCase(forkUsingCustomArr[i], { transform: camelCaseTransformMerge })} = require('${code.npmConf.scopename}/${code.usingCustomArr[i]}')\n` + } + } + // 生成项目源码时,需要import mixins文件 + if (code.pageType === 'projectCode' && (code.usingFuncCodes.length > 0 || code.exisLifyCycle.length > 0)) { + importStr += 'import methodsMixin from \'@/mixins/methods-mixin\'' + } + return importStr +} diff --git a/lib/shared/page-code/script/vue2/index.js b/lib/shared/page-code/script/vue2/index.js new file mode 100644 index 000000000..224ed228d --- /dev/null +++ b/lib/shared/page-code/script/vue2/index.js @@ -0,0 +1,48 @@ +import getImportContent from './import' +import getComponents from './components' +import getMixins from './mixins' +import getData from './data' +import getComputed from './computed' +import getWatch from './watch' +import getLifeCycle from './lifeCycle' +import getMethods from './methods' + +import handleUsedVarAndFunc from './handle-var-and-func' + +/** + * @desc 返回vue2的script, 根据vue2源码的结构组成,返回script + * @param { CodeModel } code + * @returns { string } 源码中的script内容 + */ +export default function (code) { + // 处理页面用到的函数跟变量 + handleUsedVarAndFunc(code) + + const importContent = getImportContent(code) + const componentsStr = getComponents(code) + const mixinsStr = getMixins(code) + const dataStr = getData(code) + const computedStr = getComputed(code) + const watchStr = getWatch(code) + const lifeCycleStr = getLifeCycle(code) + const methodsStr = getMethods(code) + + let scriptContent = ` + ${componentsStr} + ${mixinsStr} + ${dataStr} + ${computedStr} + ${watchStr} + ${lifeCycleStr} + ${methodsStr}` + + if (scriptContent.endsWith(',')) { + scriptContent = scriptContent.substr(0, scriptContent.length - 1) + } + return ` + + diff --git a/lib/client/src/views/system/components/project-form.vue b/lib/client/src/views/system/components/project-form.vue index 3216b0103..5213803ed 100644 --- a/lib/client/src/views/system/components/project-form.vue +++ b/lib/client/src/views/system/components/project-form.vue @@ -1,10 +1,23 @@ @@ -140,7 +141,10 @@ theme: 'error', message: '请上传符合规范的应用json' }) + return } + const projectData = this.importProjectData?.project || {} + Object.assign(this.formData, projectData) }, handleUploadReset () { this.importProjectData = {} diff --git a/lib/client/src/views/system/project-manage/index.vue b/lib/client/src/views/system/project-manage/index.vue index 73038ad3b..da0fd59dc 100644 --- a/lib/client/src/views/system/project-manage/index.vue +++ b/lib/client/src/views/system/project-manage/index.vue @@ -181,6 +181,8 @@ + + @@ -190,6 +192,7 @@ import dayjs from 'dayjs' import ProjectForm from '../components/project-form' import PagePreviewThumb from '@/components/project/page-preview-thumb.vue' + import ExportDialog from '../components/export-dialog' import DownloadDialog from '../components/download-dialog' import TemplateDialog from '../components/template-dialog' import IconButtonToggle from '@/components/ui/icon-button-toggle.vue' @@ -207,6 +210,7 @@ components: { ProjectForm, PagePreviewThumb, + ExportDialog, DownloadDialog, TemplateDialog, SetTemplateDialog, @@ -562,10 +566,10 @@ this.dialog.create.visible = true }, async handleExport (project) { - window.open(`/api/project/export?projectId=${project.id}`, '_self') - setTimeout(() => { - this.isShow = false - }, 500) + this.$refs.exportDialog.isShow = true + this.$refs.exportDialog.projectId = project.id + this.$refs.exportDialog.projectCode = project.projectCode + this.$refs.exportDialog.projectName = project.projectName }, handleDownloadSource (project) { this.$refs.downloadDialog.isShow = true diff --git a/lib/server/controller/component.js b/lib/server/controller/component.js index 872c13e95..27d7f2431 100644 --- a/lib/server/controller/component.js +++ b/lib/server/controller/component.js @@ -14,6 +14,7 @@ import { RequestContext } from '../middleware/request-context' import OperationLogger from '../service/common/operation-logger' import { POST_COMPONENT_CREATE, POST_COMPONENT_UPDATE } from '../system-conf/operate-log' import { whereVersionLiteral } from '../model/common' +import project from '../model/project' // 所有组件 export const list = async (ctx) => { @@ -120,8 +121,8 @@ export const useing = async (ctx) => { if (useingCompList.length > 0) { const compIds = useingCompList.map(comp => comp.compId) - const componentList = await ComponentModel.getDataByCompIds([...new Set(compIds)]) - + const compTypes = useingCompList.map(comp => comp.compType).filter(type => type) + const componentList = await ComponentModel.getDataByCompIds([...new Set(compIds)], compTypes) const sourceProjectList = await ProjectModel.getDataByIds(componentList.map(_ => _.belongProjectId)) const sourceProjectMap = sourceProjectList.reduce((result, item) => { result[item.id] = item @@ -132,6 +133,20 @@ export const useing = async (ctx) => { projectId: belongProjectId, projectVersionId: whereVersionLiteral(projectVersionId) }) + // 如果存在compId为0且compType不为空的数据,则为导入的数据,需要特殊处理 + let importCompPageData = compPageData.filter(item => !item.compId && item.compType) + if (importCompPageData.length) { + const allComp = await ComponentModel.all() + const typeIdMap = allComp.reduce((result, item) => { + result[item.type] = item.id + return result + }, {}) + importCompPageData = importCompPageData.map(comp => { + comp.compId = typeIdMap[comp.compType] || 0 + comp.version = comp.compVersion || comp.version + }) + } + const compRelatePageMap = compPageData.reduce((result, item) => { const compId = item.compId if (!result[compId]) { @@ -140,11 +155,10 @@ export const useing = async (ctx) => { result[compId].push(item) return result }, {}) - list = componentList.map(item => { let useingVersion = {} // 有使用记录默认取第一个作为使用版本信息 - if (compRelatePageMap[item.id].length > 0) { + if (compRelatePageMap[item.id]?.length > 0) { const currentPageUseing = compRelatePageMap[item.id][0] useingVersion = { versionId: currentPageUseing.versionId, @@ -177,6 +191,65 @@ export const useing = async (ctx) => { } } +export const exportComps = async (ctx) => { + const { belongProjectId, compType } = ctx.request.query + const query = { + belongProjectId, + deleteFlag: 0 + } + if (compType) { + Object.assign(query, { compType }) + } + const projectComponentList = await ComponentModel.all(query) + + const compressing = require('compressing') + const send = require('koa-send') + const path = require('path') + const fse = require('fs-extra') + + const STATIC_URL = './lib/server/temp/' + const componentDirName = `bklesscode-custom-component-${belongProjectId}` + + // 下载每个组件的源码文件并压缩成zip包 + const downloadCompItem = async (comp) => { + return new Promise(async (resolve, reject) => { + try { + const tempDir = path.resolve(__dirname, `../temp/${componentDirName}/${comp.type}`) + const zipPath = path.resolve(__dirname, `../temp/${componentDirName}/${comp.type}.zip`) + + await fileService.downloadFile(`${comp.dest}/config.json`, path.resolve(tempDir, 'config.json')) + await fileService.downloadFile(`${comp.dest}/index.iife.min.js`, path.resolve(tempDir, 'index.iife.min.js')) + await fileService.downloadFile(`${comp.dest}/index.umd.min.js`, path.resolve(tempDir, 'index.umd.min.js')) + await compressing.zip.compressDir(tempDir, zipPath).then(async () => { + // fse.remove(tempDir) + resolve() + }) + } catch (err) { + reject(err) + } + }) + } + + await Promise.all(projectComponentList.map(async comp => { + // 把每一个组件都压缩成zip包 + await downloadCompItem(comp) + })) + + const sourcePath = path.join(STATIC_URL, componentDirName) + const targetPath = path.join(STATIC_URL, `${componentDirName}.zip`) + + await fse.ensureDir(sourcePath) + await compressing.zip.compressDir(sourcePath, targetPath, { ignoreBase: true }) + .then(async () => { + ctx.attachment(targetPath) + await send(ctx, targetPath) + fse.remove(sourcePath) + fse.remove(targetPath) + }).catch((err) => { + console.log(err) + }) +} + export const updatePageComp = async (ctx) => { const operationLogger = new OperationLogger(ctx) const { projectId, projectVersionId, compId, versionId } = ctx.request.body @@ -244,13 +317,13 @@ export const create = async (ctx) => { const s3ComponentPath = `component/${componentDirName}` await fileService.uploadFolder(tempDir, s3ComponentPath) // 推送tnpm - await npmPublish({ - sourceDir: path.resolve(__dirname, tempDir), - componentDirName: componentDirName, - name: type, - version, - description - }) + // await npmPublish({ + // sourceDir: path.resolve(__dirname, tempDir), + // componentDirName: componentDirName, + // name: type, + // version, + // description + // }) const currentUser = RequestContext.getCurrentUser() const nowDate = new Date() @@ -861,6 +934,9 @@ export const register = async (ctx) => { }) if (useingVersionList.length > 0) { const allUseingVersion = useingVersionList.map(_ => _.versionId) + + // 根据type跟version找的组件,为导入的组件,如果已重新上传,则项目中的组件已在上面获取 + const useingComponentList = await ComponentModel.getDataByVersion(allUseingVersion) useingComponentList.forEach(item => { registeromponentMap[item.id] = item diff --git a/lib/server/controller/file.js b/lib/server/controller/file.js index c5142ce95..c5f4e696b 100644 --- a/lib/server/controller/file.js +++ b/lib/server/controller/file.js @@ -24,12 +24,14 @@ import { SessionParams, ProjectAuthorization, OutputJson, - Ctx + Ctx, + OutputZip } from '../decorator' import { getAll } from '../service/business/file' import { IAM_ACTION } from '../../shared/constant.js' +import { reject } from 'lodash' @Controller('/api/file') export default class FileController { @@ -148,4 +150,57 @@ export default class FileController { const result = await LCDataService.softDelete(TABLE_FILE_NAME.FILE, fileId) return result?.id } + + @ProjectAuthorization({ getId: ctx => ctx.request.query.projectId, needAuthActions: [IAM_ACTION.develop_app[0]] }) + @Get('/export') + async export ( + @Ctx() ctx, + @QueryParams({ name: 'projectId', require: true }) projectId + ) { + const { list } = await getAll(projectId) + const compressing = require('compressing') + const send = require('koa-send') + const path = require('path') + const fse = require('fs-extra') + + const STATIC_URL = './lib/server/temp/' + const pathName = `bk-lesscode-files-${projectId}` + + const sourcePath = path.join(STATIC_URL, pathName) + const targetPath = path.join(STATIC_URL, `${pathName}.zip`) + fse.ensureDirSync(sourcePath) + + const curlContent = async (item) => { + return new Promise((resolve, reject) => { + const childProcess = require('child_process') + + const curl = `curl -o ${path.resolve(sourcePath, item.name)} ${item.url}` + console.log(curl) + + childProcess.exec(curl, function (err, stdout, stderr) { + if (err) { + reject(new Error(`${item.name}下载失败`)) + } + resolve( + resolve() + ) + }) + }) + } + + await Promise.all(list.map(async item => { + await curlContent(item) + })) + + await compressing.zip.compressDir(sourcePath, targetPath) + .then(async () => { + ctx.attachment(targetPath) + await send(ctx, targetPath) + fse.remove(sourcePath) + fse.remove(targetPath) + }).catch((err) => { + console.log('zip err') + console.log(err) + }) + } } diff --git a/lib/server/model/component.js b/lib/server/model/component.js index c10b4c9c6..e7422b970 100644 --- a/lib/server/model/component.js +++ b/lib/server/model/component.js @@ -64,7 +64,19 @@ export const getDataByVersion = async function (params) { return res } -export const getDataByCompIds = async function (params) { +export const getDataByCompIds = async function (ids, compTypes = []) { + ids = ids.filter(item => item > 0) + let whereCon = '' + if (ids?.length && compTypes?.length) { + whereCon = '(comp.id IN (:...ids) OR comp.type IN (:...compTypes))' + } else if (ids.length && !compTypes?.length) { + whereCon = 'comp.id IN (:...ids)' + } else if (!ids.length && compTypes?.length) { + whereCon = 'comp.type IN (:...compTypes)' + } else { + return [] + } + console.log(whereCon, 'where') const res = await getRepository(Comp) .createQueryBuilder('comp') .leftJoin(CompCategory, 'compCategory', 'comp.categoryId = compCategory.id') @@ -78,7 +90,7 @@ export const getDataByCompIds = async function (params) { .addSelect('version.version', 'version') .addSelect('version.versionLog', 'versionLog') .addSelect('version.description', 'description') - .where('comp.id IN (:...ids)', { ids: params }) + .where(whereCon, { ids, compTypes }) .andWhere('comp.deleteFlag = 0') .orderBy('comp.createTime') .andWhere('version.isLast = 1') diff --git a/lib/server/model/entities/page-comp.js b/lib/server/model/entities/page-comp.js index 97b314795..73bbdf157 100644 --- a/lib/server/model/entities/page-comp.js +++ b/lib/server/model/entities/page-comp.js @@ -23,6 +23,12 @@ export default class extends Base { @Column({ type: 'int', comment: 'version 表主键' }) versionId + @Column({ type: 'varchar', comment: '组件ID' }) + compType + + @Column({ type: 'varchar', comment: 'version' }) + compVersion + @Column({ type: 'int', comment: 'project 表主键' }) projectId diff --git a/lib/server/model/migrations/20230106064941-update-sql.js b/lib/server/model/migrations/20230106064941-update-sql.js new file mode 100644 index 000000000..bd02785aa --- /dev/null +++ b/lib/server/model/migrations/20230106064941-update-sql.js @@ -0,0 +1,53 @@ +'use strict'; + +var dbm; +var type; +var seed; +var fs = require('fs'); +var path = require('path'); +var Promise; + +/** + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function(options, seedLink) { + dbm = options.dbmigrate; + type = dbm.dataType; + seed = seedLink; + Promise = options.Promise; +}; + +exports.up = function(db) { + var filePath = path.join(__dirname, 'sqls', '20230106064941-update-sql-up.sql'); + return new Promise( function( resolve, reject ) { + fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ + if (err) return reject(err); + console.log('received data: ' + data); + + resolve(data); + }); + }) + .then(function(data) { + return db.runSql(data); + }); +}; + +exports.down = function(db) { + var filePath = path.join(__dirname, 'sqls', '20230106064941-update-sql-down.sql'); + return new Promise( function( resolve, reject ) { + fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ + if (err) return reject(err); + console.log('received data: ' + data); + + resolve(data); + }); + }) + .then(function(data) { + return db.runSql(data); + }); +}; + +exports._meta = { + "version": 1 +}; diff --git a/lib/server/model/migrations/sqls/20230106064941-update-sql-down.sql b/lib/server/model/migrations/sqls/20230106064941-update-sql-down.sql new file mode 100644 index 000000000..44f074ea8 --- /dev/null +++ b/lib/server/model/migrations/sqls/20230106064941-update-sql-down.sql @@ -0,0 +1 @@ +/* Replace with your SQL commands */ \ No newline at end of file diff --git a/lib/server/model/migrations/sqls/20230106064941-update-sql-up.sql b/lib/server/model/migrations/sqls/20230106064941-update-sql-up.sql new file mode 100644 index 000000000..2f20bcf0f --- /dev/null +++ b/lib/server/model/migrations/sqls/20230106064941-update-sql-up.sql @@ -0,0 +1,5 @@ +ALTER TABLE `r_page_comp` +ADD COLUMN `compVersion` varchar(50) NULL COMMENT '组件版本号' AFTER `versionId`; + +ALTER TABLE `r_page_comp` +ADD COLUMN `compType` varchar(50) NULL COMMENT '组件ID' AFTER `versionId`; \ No newline at end of file diff --git a/lib/server/model/page-comp.js b/lib/server/model/page-comp.js index 3b530cc21..3ea5daaeb 100644 --- a/lib/server/model/page-comp.js +++ b/lib/server/model/page-comp.js @@ -33,12 +33,32 @@ export const getAll = async function (params) { return res } +export const getImportPageComp = async function () { + const res = await getRepository(PageComp) + .createQueryBuilder('pageComp') + .leftJoin(Page, 'page', 'page.id = pageComp.pageId') + .leftJoin(Version, 'version', 'version.id = pageComp.versionId') + // .select('pageComp.compId', 'compId') + // .select('pageComp.compType', 'compType') + // .select('pageComp.compVersion', 'compVersion') + .select('pageComp.*') + .addSelect('page.pageName', 'pageName') + .addSelect('version.id', 'versionId') + .addSelect('version.version', 'version') + .addSelect('version.versionLog', 'versionLog') + .addSelect('version.isLast', 'isLast') + .where(params) + .andWhere('page.deleteFlag = 0') + .getRawMany() + return res +} + export const getPageAndVersion = async function (params) { const res = await getRepository(PageComp) .createQueryBuilder('pageComp') .leftJoin(Page, 'page', 'page.id = pageComp.pageId') .leftJoin(Version, 'version', 'version.id = pageComp.versionId') - .select('pageComp.compId', 'compId') + .select('pageComp.*') .addSelect('page.pageName', 'pageName') .addSelect('version.id', 'versionId') .addSelect('version.version', 'version') @@ -50,6 +70,26 @@ export const getPageAndVersion = async function (params) { return res } +export const getTypeAndVersion = async function (pageIds) { + let res = await getRepository(PageComp) + .createQueryBuilder('pageComp') + .leftJoin(Comp, 'comp', 'comp.id = pageComp.compId') + .leftJoin(Version, 'version', 'version.id = pageComp.versionId') + .select('pageComp.*') + .addSelect('comp.type', 'realType') + .addSelect('version.version', 'realVersion') + .where('pageComp.pageId IN (:...pageIds)', { pageIds }) + .andWhere('pageComp.deleteFlag = 0') + .getRawMany() + res = (res || []).map(item => { + const { realType, realVersion, ...others } = item + others.compType = realType || item.compType + others.compVersion = realVersion || item.compVersion + return others + }) + return res +} + export const update = async function (params, data) { return getRepository(PageComp).update(params, data) } @@ -62,20 +102,34 @@ export const getProjectComp = async function (projectId, versionId) { .where('pageComp.projectId = :projectId', { projectId }) .andWhere(whereVersion(versionId, 'pageComp', 'projectVersionId')) .andWhere('c.deleteFlag = 0') - .select(['v.version as version', 'c.type as type']) - .distinct('pageComp.compId') + .select(['pageComp.compVersion as compVersion', 'pageComp.compType as compType']) + .addSelect(['v.version as version', 'c.type as type']) + .orderBy('pageComp.id', 'DESC') + // .distinct('pageComp.compId, pageComp.compType') .getRawMany() + + // 确保同一个type只写一条记录 + const typeArr = [] let data = [] if (res.length) { const prefix = process.env.BKPAAS_ENVIRONMENT === 'prod' ? '' : 'test-' - data = res.map(item => { - let version = item.version - version = version.substring(0, 1) === 'v' ? version.substring(1) : version - return { - name: `${npmConf.scopename}/${prefix}${item.type}`, - version: version + data = res.reduce((result, item) => { + // 如果是导入的compType跟compVersion,需要先校验compType跟compVersion是否存在 + item.version = item.version || item.compVersion + item.type = item.type || item.compType + + if (typeArr.indexOf(item.type) === -1 && item.version) { + typeArr.push(item.type) + let version = item.version + version = version?.substring(0, 1) === 'v' ? version?.substring(1) : version + result.push({ + name: `${npmConf.scopename}/${prefix}${item.type}`, + version: version + }) } - }) + return result + }, []) } + return data } diff --git a/lib/server/router/component.js b/lib/server/router/component.js index 8376678b9..cb41d23ac 100644 --- a/lib/server/router/component.js +++ b/lib/server/router/component.js @@ -21,6 +21,7 @@ const { versionDetail, create, update, + exportComps, off, online, compDelete, @@ -35,7 +36,7 @@ const router = new Router({ prefix: '/api/component' }) -router.use(['/category-count', '/list'], async (ctx, next) => { +router.use(['/category-count', '/list', 'export'], async (ctx, next) => { const projectId = ctx.request.query.belongProjectId const project = await iamModel.queryProjectByCreatorAndProjectId(ctx.session.userInfo.username, projectId) if (!project) { @@ -70,6 +71,7 @@ router.get('/list', list) router.get('/useing', useing) router.get('/detail', detail) router.get('/version-detail', versionDetail) +router.get('/export', exportComps) router.post('/create', create) router.post('/update', update) router.post('/off', off) diff --git a/lib/server/service/business/page.js b/lib/server/service/business/page.js index 7fe41251b..873d190e8 100644 --- a/lib/server/service/business/page.js +++ b/lib/server/service/business/page.js @@ -99,7 +99,7 @@ export const versionTask = async (ctx, next) => { export const importTask = async (ctx, next) => { const { projectId, idMap, importData } = ctx - const { page = [], projectPage = [], pageFunc = [] } = importData + const { page = [], projectPage = [], pageFunc = [], pageComp = [] } = importData const { formIdMap = {}, funcIdMap = {} } = idMap console.log(idMap, 'map') @@ -144,7 +144,7 @@ export const importTask = async (ctx, next) => { if (pageFunc.length) { await transactionalEntityHelper.add( TABLE_FILE_NAME.PAGE_FUNC, - projectPage.map(item => { + pageFunc.map(item => { const { id, ...others } = item return { ...others, @@ -157,6 +157,19 @@ export const importTask = async (ctx, next) => { } // 页面组件关联记录 + if (pageComp.length) { + await transactionalEntityHelper.add( + TABLE_FILE_NAME.PAGE_COMP, + pageComp.map(item => { + const { id, ...others } = item + return { + ...others, + projectId, + pageId: pageIdMap[others.pageId] + } + }) + ) + } }) ctx.idMap.pageIdMap = pageIdMap ctx.flowPageIdList = flowPageIdList diff --git a/lib/server/service/business/project.js b/lib/server/service/business/project.js index 66575d151..cf39a7de7 100644 --- a/lib/server/service/business/project.js +++ b/lib/server/service/business/project.js @@ -8,6 +8,7 @@ import { getConnection, getRepository } from 'typeorm' import Project from '../../model/entities/project' import userProjectRole from '../../model/entities/user-project-role' import CompCategory from '../../model/entities/comp-category' +import { getTypeAndVersion } from '../../model/page-comp' import Layout from '../../model/layout' import { createV3App } from './v3-service' import lcCompose from '../../utils/lc-compose' @@ -40,6 +41,7 @@ export const getExportProjectData = async (projectId, versionId = null) => { const queryWithVersion = Object.assign({}, query, { versionId }) const [ layoutInstData, + projectData, { list: comp }, { list: funcGroup }, { list: funcFunc }, @@ -63,6 +65,9 @@ export const getExportProjectData = async (projectId, versionId = null) => { // [{ tableName: 'layout', on: 'layout.id = layout_inst.routeId' }], // { express: 'layout_inst.projectId = :projectId AND layout_inst.versionId = :versionId', data: { projectId, versionId: null } } // ), + LCDataService.findOne(TABLE_FILE_NAME.PROJECT, { + id: projectId + }), LCDataService.get({ tableFileName: TABLE_FILE_NAME.COMP, query: { @@ -134,6 +139,8 @@ export const getExportProjectData = async (projectId, versionId = null) => { query: queryWithVersion }) ]) + // 解析出projectCode、projectName、projectDesc + const project = { projectCode: projectData.projectCode, projectName: projectData.projectName, projectDesc: projectData.projectDesc } // 导出函数 if (funcGroup.length) { const funcGroupIds = funcGroup.map(item => item.id) @@ -184,14 +191,16 @@ export const getExportProjectData = async (projectId, versionId = null) => { }) pageFunc = pageFuncData || [] - // pageComp - const { list: pageCompData } = await LCDataService.get({ - tableFileName: TABLE_FILE_NAME.PAGE_COMP, - query: { - pageId: pageIds - } + // pageComp, 由于新环境没有本项目的自定义组件,因此需要把组件type跟版本号记录下来,同时把compId跟versionId置为0 + const pageCompData = await getTypeAndVersion(pageIds) + pageComp = (pageCompData || []).map(item => { + const { realType, realVersion, ...others } = item + others.compType = realType || item.compType + others.compVersion = realVersion || item.compVersion + others.versionId = 0 + others.compId = 0 + return others }) - pageComp = pageCompData || [] } // pageroute @@ -216,6 +225,7 @@ export const getExportProjectData = async (projectId, versionId = null) => { api = apiData || [] } const exportData = { + project, projectPage, page, pageFunc, @@ -243,10 +253,12 @@ export const getExportProjectData = async (projectId, versionId = null) => { } // 把每一项里面的createUser, updateUser, createTime, updateTime字段去掉 Object.keys(exportData).forEach((key) => { - exportData[key] = (exportData[key] || []).map(item => { - const { createUser, updateUser, createTime, updateTime, ...others } = item - return others - }) + if (key !== 'project') { + exportData[key] = (exportData[key] || []).map(item => { + const { createUser, updateUser, createTime, updateTime, ...others } = item + return others + }) + } }) return exportData } diff --git a/lib/server/utils/file-service/index.js b/lib/server/utils/file-service/index.js index fe051119e..d7f112dff 100644 --- a/lib/server/utils/file-service/index.js +++ b/lib/server/utils/file-service/index.js @@ -84,7 +84,7 @@ const downloadFile = (targetFile, dest) => { request.get(options, function (err, res, body) { if (err || res.statusCode !== 200) { console.log(err, res.statusCode, body) - reject(new Error(`请检测自定义组件是否上传成功: ${err || res.statusCode}`)) + reject(new Error(`文件下载失败: ${err || res.statusCode}`)) } else { if (dest) { fse.outputFileSync(dest, body) diff --git a/package.json b/package.json index 3dc69172a..752ef63aa 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "change-case": "~4.1.1", "cheerio": "~1.0.0-rc.3", "co-views": "~2.1.0", - "compressing": "^1.5.1", + "compressing": "^1.6.3", "continuation-local-storage": "~3.2.1", "cookie": "~0.4.0", "core-js": "^3.25.5", From ca191741cfc75318ec13dee520c43bac0743b665 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Thu, 12 Jan 2023 11:37:37 +0800 Subject: [PATCH 28/80] =?UTF-8?q?feat:=20=E5=AF=BC=E5=85=A5=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=85=BC=E5=AE=B9=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=20&&=20=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E3=80=81=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/controller/component.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/server/controller/component.js b/lib/server/controller/component.js index 27d7f2431..dbad7fbf3 100644 --- a/lib/server/controller/component.js +++ b/lib/server/controller/component.js @@ -317,13 +317,13 @@ export const create = async (ctx) => { const s3ComponentPath = `component/${componentDirName}` await fileService.uploadFolder(tempDir, s3ComponentPath) // 推送tnpm - // await npmPublish({ - // sourceDir: path.resolve(__dirname, tempDir), - // componentDirName: componentDirName, - // name: type, - // version, - // description - // }) + await npmPublish({ + sourceDir: path.resolve(__dirname, tempDir), + componentDirName: componentDirName, + name: type, + version, + description + }) const currentUser = RequestContext.getCurrentUser() const nowDate = new Date() From 4995e6528657d1da5d05165d8e40080c177c7ebd Mon Sep 17 00:00:00 2001 From: terlinhe Date: Fri, 13 Jan 2023 10:12:09 +0800 Subject: [PATCH 29/80] =?UTF-8?q?fix:=20=E6=9F=A5=E7=9C=8B=E4=B8=8D?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E7=9A=84=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/controller/component.js | 9 ++++++++- lib/server/model/project.js | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/server/controller/component.js b/lib/server/controller/component.js index dbad7fbf3..56bf252a7 100644 --- a/lib/server/controller/component.js +++ b/lib/server/controller/component.js @@ -14,7 +14,6 @@ import { RequestContext } from '../middleware/request-context' import OperationLogger from '../service/common/operation-logger' import { POST_COMPONENT_CREATE, POST_COMPONENT_UPDATE } from '../system-conf/operate-log' import { whereVersionLiteral } from '../model/common' -import project from '../model/project' // 所有组件 export const list = async (ctx) => { @@ -123,6 +122,14 @@ export const useing = async (ctx) => { const compIds = useingCompList.map(comp => comp.compId) const compTypes = useingCompList.map(comp => comp.compType).filter(type => type) const componentList = await ComponentModel.getDataByCompIds([...new Set(compIds)], compTypes) + if (!componentList.length) { + ctx.send({ + code: 0, + message: 'success', + data: list + }) + return + } const sourceProjectList = await ProjectModel.getDataByIds(componentList.map(_ => _.belongProjectId)) const sourceProjectMap = sourceProjectList.reduce((result, item) => { result[item.id] = item diff --git a/lib/server/model/project.js b/lib/server/model/project.js index 7cc44eade..ea5c72478 100644 --- a/lib/server/model/project.js +++ b/lib/server/model/project.js @@ -91,9 +91,9 @@ const getDefaultFunc = function (options) { export default { getDataByIds: async function (params = []) { const res = await getRepository(Project) - .createQueryBuilder('prroject') - .select('prroject.*') - .where('prroject.id IN (:...ids)', { ids: params }) + .createQueryBuilder('project') + .select('project.*') + .where('project.id IN (:...ids)', { ids: params }) .getRawMany() return res }, From 120b31c7ded4dd9a748625f6fe93627a50de5a06 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Fri, 13 Jan 2023 14:40:45 +0800 Subject: [PATCH 30/80] =?UTF-8?q?feat:=20=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=88=86=E7=B1=BB=E6=94=AF=E6=8C=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../all/components/render-category.vue | 2 -- .../all/components/render-list.vue | 5 ----- lib/server/controller/component.js | 19 +++++++++++++++++++ lib/server/router/component.js | 2 ++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/client/src/views/project/component-manage/all/components/render-category.vue b/lib/client/src/views/project/component-manage/all/components/render-category.vue index dce8a38ab..b683cb80f 100644 --- a/lib/client/src/views/project/component-manage/all/components/render-category.vue +++ b/lib/client/src/views/project/component-manage/all/components/render-category.vue @@ -52,7 +52,6 @@ diff --git a/lib/server/controller/project.js b/lib/server/controller/project.js index db512eea8..b42e87876 100644 --- a/lib/server/controller/project.js +++ b/lib/server/controller/project.js @@ -153,7 +153,7 @@ module.exports = { ctx.send(res) } catch (e) { console.log('导入应用失败', e) - ctx.throw('导入应用失败:' + e) + ctx.throw('导入应用失败:' + e.message || e) } }, diff --git a/lib/server/service/business/project.js b/lib/server/service/business/project.js index cf39a7de7..cbd45ad8d 100644 --- a/lib/server/service/business/project.js +++ b/lib/server/service/business/project.js @@ -318,7 +318,7 @@ export const importProject = async (projectData, userProjectRoleData, importData // 有错误则回滚 await queryRunner.rollbackTransaction() console.log(err, 'import error') - return Promise.reject(new Error('导入应用失败')) + return Promise.reject(new Error('导入失败:' + err.message || err)) } } diff --git a/lib/shared/page-code/style/index.js b/lib/shared/page-code/style/index.js index 56f12ea65..4ce38a19f 100644 --- a/lib/shared/page-code/style/index.js +++ b/lib/shared/page-code/style/index.js @@ -29,5 +29,5 @@ export default function (code) { ${pageStyle} }` - return head + layoutStyle + settingStyle + code.cssStr + defaultStyle + '\n' + return head + layoutStyle + defaultStyle + settingStyle + code.cssStr + '\n' } From cc1322ae96b709f4a77da2143f50fad5753f4a0f Mon Sep 17 00:00:00 2001 From: terlinhe Date: Tue, 7 Feb 2023 00:20:12 +0800 Subject: [PATCH 35/80] fix --- lib/server/controller/project.js | 2 +- lib/server/service/business/project.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/server/controller/project.js b/lib/server/controller/project.js index b42e87876..0ad6d5453 100644 --- a/lib/server/controller/project.js +++ b/lib/server/controller/project.js @@ -153,7 +153,7 @@ module.exports = { ctx.send(res) } catch (e) { console.log('导入应用失败', e) - ctx.throw('导入应用失败:' + e.message || e) + ctx.throw('导入应用失败:' + (e.message || e)) } }, diff --git a/lib/server/service/business/project.js b/lib/server/service/business/project.js index cbd45ad8d..831d5a04b 100644 --- a/lib/server/service/business/project.js +++ b/lib/server/service/business/project.js @@ -318,7 +318,7 @@ export const importProject = async (projectData, userProjectRoleData, importData // 有错误则回滚 await queryRunner.rollbackTransaction() console.log(err, 'import error') - return Promise.reject(new Error('导入失败:' + err.message || err)) + return Promise.reject(new Error('导入失败:' + (err.message || err))) } } From b6f22de6123b1a9ca2b3827936ed9fe99213ecb9 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Tue, 7 Feb 2023 00:33:45 +0800 Subject: [PATCH 36/80] =?UTF-8?q?fix=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/service/business/project.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server/service/business/project.js b/lib/server/service/business/project.js index 831d5a04b..25a583eb7 100644 --- a/lib/server/service/business/project.js +++ b/lib/server/service/business/project.js @@ -318,7 +318,7 @@ export const importProject = async (projectData, userProjectRoleData, importData // 有错误则回滚 await queryRunner.rollbackTransaction() console.log(err, 'import error') - return Promise.reject(new Error('导入失败:' + (err.message || err))) + return Promise.reject(new Error(err.message || err)) } } From 405eff57eb9fa754d7579e8841b6616fb3468c50 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Tue, 7 Feb 2023 11:24:56 +0800 Subject: [PATCH 37/80] =?UTF-8?q?fix:=20=E5=AF=BC=E5=87=BA=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=A1=A8=E5=8F=98=E6=9B=B4=E8=AE=B0=E5=BD=95=E6=97=B6?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E5=8D=87=E5=BA=8F=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/server/service/business/project.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/server/service/business/project.js b/lib/server/service/business/project.js index 25a583eb7..33d0932b5 100644 --- a/lib/server/service/business/project.js +++ b/lib/server/service/business/project.js @@ -108,7 +108,10 @@ export const getExportProjectData = async (projectId, versionId = null) => { }), LCDataService.get({ tableFileName: TABLE_FILE_NAME.DATA_TABLE_MODIFY_RECORD, - query + query, + order: { + id: 'ASC' + } }), LCDataService.get({ tableFileName: TABLE_FILE_NAME.API_CATEGORY, From e73bfc917cfcfd74c98cc27efa61b25c7e8bd278 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Tue, 7 Feb 2023 12:38:04 +0800 Subject: [PATCH 38/80] =?UTF-8?q?fix:=20=E5=BC=80=E6=BA=90=E7=89=88run=20b?= =?UTF-8?q?uild=E5=89=8D=E6=89=A7=E8=A1=8Cmigration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84ba2f7a8..371db187a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "betterScripts": { "build": { - "command": "bk-cli-service-webpack build", + "command": "node node_modules/db-migrate/bin/db-migrate up --config ./lib/server/conf/db-migrate.json --migrations-dir ./lib/server/model/migrations -e prod && bk-cli-service-webpack build", "env": { "NODE_ENV": "production", "APP_CODE": "", From f68e0cd7deab24878d99e608d88673fb473a0bd4 Mon Sep 17 00:00:00 2001 From: vincenttgao Date: Tue, 7 Feb 2023 15:49:37 +0800 Subject: [PATCH 39/80] =?UTF-8?q?feature:=20=E6=8B=96=E6=8B=BD=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=97=B6=E8=87=AA=E9=80=82=E5=BA=94=E5=8D=95=E4=BD=8D?= =?UTF-8?q?=20&=20=E4=BC=98=E5=8C=96=E7=A7=BB=E5=8A=A8=E7=AB=AF=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/common/util.js | 29 +++++++++++++++++++ .../tools/lesscode-resize/hooks/use-resize.js | 9 +++--- .../render/pc/widget/free-layout.vue | 13 +++++---- lib/shared/util.js | 3 +- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/lib/client/src/common/util.js b/lib/client/src/common/util.js index 14f505886..659799b1f 100644 --- a/lib/client/src/common/util.js +++ b/lib/client/src/common/util.js @@ -12,6 +12,9 @@ import { messageSuccess } from '@/common/bkmagic' import domToImage from './dom-to-image' import Vue from 'vue' +import { useStore } from '@/store' + +const store = useStore() /** * 将html转换为Vnode @@ -867,3 +870,29 @@ export const isValEmpty = (val) => { } return false } + +/** + * px单位转为rpx单位 + * @param {*} + * value: number + */ +export function pxToRpx (value) { + const { width } = store.getters['page/pageSize'] + const fullWidthRpx = 750 + return (fullWidthRpx / width) * value +} + +export function autoStyle (node, styleName, pxNumber, isGet = false) { + // 保持元素最初的单位状态,如果最初没设置,PC平台默认px,移动端默认rpx + const platform = store.getters['page/platform'] + const defaultUnit = platform === 'PC' ? 'px' : 'rpx' + const hasOriginValue = node.renderStyles && /^\d+\D+$/.test(node.renderStyles[styleName]) + const currentUnit = hasOriginValue ? /\D+$/.exec(node.renderStyles[styleName])[0] : defaultUnit + + if (currentUnit === 'px') { + return isGet ? pxNumber + 'px' : node.setStyle(styleName, pxNumber + 'px') + } else if (currentUnit === 'rpx') { + return isGet ? pxToRpx(pxNumber) + 'rpx' : node.setStyle(styleName, pxToRpx(pxNumber) + 'rpx') + } + throw Error('传入错误参数,无法进行单位自动转换') +} diff --git a/lib/client/src/components/render/pc/tools/lesscode-resize/hooks/use-resize.js b/lib/client/src/components/render/pc/tools/lesscode-resize/hooks/use-resize.js index 237527802..dbc50caba 100644 --- a/lib/client/src/components/render/pc/tools/lesscode-resize/hooks/use-resize.js +++ b/lib/client/src/components/render/pc/tools/lesscode-resize/hooks/use-resize.js @@ -9,6 +9,7 @@ import { } from '@vue/composition-api' import _ from 'lodash' import DragLine from '../../../../common/drag-line' +import { autoStyle } from '@/common/util.js' // const halfDotSize = 8 @@ -42,7 +43,7 @@ export default function () { const { clientX } = event if (isWidthResizeable) { newWidth = Math.max(parseInt(clientX - startScreenX + moveStartWidth, 10), 0) - proxy.activeComponentData.setStyle('width', `${newWidth}px`) + autoStyle(proxy.activeComponentData, 'width', newWidth) // 指定了width,right不生效 if (componentDataParentNode.value.type === 'free-layout') { proxy.activeComponentData.setStyle('right', '') @@ -51,7 +52,7 @@ export default function () { const { clientY } = event if (isHeightResizeable) { newHeight = Math.max(parseInt(clientY - startScreenY + moveStartHeight, 10), 0) - proxy.activeComponentData.setStyle('height', `${newHeight}px`) + autoStyle(proxy.activeComponentData, 'height', newHeight) // 指定了height, bottom不生效 if (componentDataParentNode.value.type === 'free-layout') { proxy.activeComponentData.setStyle('bottom', '') @@ -195,12 +196,12 @@ export default function () { const freeLayoutHeightBoundary = Math.floor(activeNodeTop + activeNodeHeight) if (freeLayoutHeightBoundary < maxHeightAndTop) { - proxy.activeComponentData.setStyle('height', `${maxHeightAndTop - activeNodeTop + 1}px`) + autoStyle(proxy.activeComponentData, 'height', maxHeightAndTop - activeNodeTop + 1) } const freeLayoutWidthBoundary = Math.floor(activeNodeLeft + activeNodeWidth) if (freeLayoutWidthBoundary < maxWidthAndLeft) { - proxy.activeComponentData.setStyle('width', `${maxWidthAndLeft - activeNodeLeft + 1}px`) + autoStyle(proxy.activeComponentData, 'width', maxWidthAndLeft - activeNodeLeft + 1) } } diff --git a/lib/client/src/components/render/pc/widget/free-layout.vue b/lib/client/src/components/render/pc/widget/free-layout.vue index c7dc7e909..336e826c9 100644 --- a/lib/client/src/components/render/pc/widget/free-layout.vue +++ b/lib/client/src/components/render/pc/widget/free-layout.vue @@ -52,6 +52,7 @@ import Draggable from '../components/draggable' import ResolveComponent from '../resolve-component' import { unitFilter } from 'shared/util.js' + import { autoStyle } from '@/common/util.js' export default { name: 'free-layout', @@ -109,12 +110,12 @@ this.dragLine.check(this.drag.$elem, '[role="component-root"]') }).on('end', () => { this.dragLine.uncheck() - const left = parseFloat(this.drag.$elem.style.left) - const top = parseFloat(this.drag.$elem.style.top) + const left = autoStyle(childNode, 'left', parseFloat(this.drag.$elem.style.left), true) + const top = autoStyle(childNode, 'top', parseFloat(this.drag.$elem.style.top), true) childNode.setStyle({ - left: left + 'px', - top: top + 'px' + left: left, + top: top }) childNode.active() isMoveing = false @@ -180,7 +181,7 @@ } else { top = originalTop - containerTop - 15 } - top = Math.max(top, 10) + 'px' + top = autoStyle(childNode, 'top', Math.max(top, 10), true) } // left 位置计算 if (childNode.style.left) { @@ -191,7 +192,7 @@ } else { left = originalLeft - containerLeft - 15 } - left = Math.max(left, 10) + 'px' + left = autoStyle(childNode, 'left', Math.max(left, 10), true) } childNode.setStyle({ diff --git a/lib/shared/util.js b/lib/shared/util.js index df28560ef..c9433ae87 100644 --- a/lib/shared/util.js +++ b/lib/shared/util.js @@ -103,7 +103,8 @@ export function throttle (fn, delay = 200) { } /** - * 单位过滤,如果是rpx转为rem,否则直接输出 + * 用于代码生成的单位过滤与转换, + * 如果是rpx转为rem,否则直接输出 * 此处将屏幕分为20份, 即20rem = 750rpx = 100%屏幕宽度 * @param {String} value 需要过滤的值 * @returns From 82fe8ab4aa6a616a74c7e8a0dccfca507309b83e Mon Sep 17 00:00:00 2001 From: vincenttgao Date: Tue, 7 Feb 2023 16:48:44 +0800 Subject: [PATCH 40/80] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0margin=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E7=9A=84=E8=87=AA=E5=8A=A8=E5=8D=95=E4=BD=8D=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=20&=20=E6=89=93=E5=BC=80input=E7=9A=84=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/client/src/common/util.js | 22 +++++++++++++++++++ .../tools/lesscode-margin/hooks/use-margin.js | 9 ++++---- .../materials/vant/field/index.js | 2 +- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/lib/client/src/common/util.js b/lib/client/src/common/util.js index 659799b1f..0f858f497 100644 --- a/lib/client/src/common/util.js +++ b/lib/client/src/common/util.js @@ -896,3 +896,25 @@ export function autoStyle (node, styleName, pxNumber, isGet = false) { } throw Error('传入错误参数,无法进行单位自动转换') } + +/** rem转为px + * 项目rem 基准 + * fullWidth: 750 / 20rem + * 详见 lib/shared + */ +export function remToPx (value) { + const { width } = store.getters['page/pageSize'] + const fullWidthRem = 20 + + const originValue = parseInt(value, 10) + return ((originValue / fullWidthRem) * width) + 'px' +} + +/** 自动转为px */ +export function autoPxTransform (value) { + if (value.includes('px')) { + return value + } else if (value.includes('rem')) { + return remToPx(value) + } +} diff --git a/lib/client/src/components/render/pc/tools/lesscode-margin/hooks/use-margin.js b/lib/client/src/components/render/pc/tools/lesscode-margin/hooks/use-margin.js index 08c3846ca..e1f26a5a5 100644 --- a/lib/client/src/components/render/pc/tools/lesscode-margin/hooks/use-margin.js +++ b/lib/client/src/components/render/pc/tools/lesscode-margin/hooks/use-margin.js @@ -7,6 +7,7 @@ import { } from '@vue/composition-api' import _ from 'lodash' import DragLine from '../../../../common/drag-line' +import { autoStyle, autoPxTransform } from '@/common/util.js' const hideStyles = { display: 'none' @@ -40,7 +41,7 @@ export default function () { if (isMarginTopResizeable) { newMarginTop = parseInt(clientY - startClientY + moveStartMarginTop, 10) - proxy.activeComponentData.setStyle('marginTop', `${newMarginTop}px`) + autoStyle(proxy.activeComponentData, 'marginTop', newMarginTop) state.tipTopStyles = { height: `${newMarginTop}px`, zIndex: 99 @@ -48,7 +49,7 @@ export default function () { } if (isMarginLeftResizeable) { newMarginLeft = parseInt(clientX - startClientX + moveStartMarginLeft, 10) - proxy.activeComponentData.setStyle('marginLeft', `${newMarginLeft}px`) + autoStyle(proxy.activeComponentData, 'marginLeft', newMarginLeft) state.tipLeftStyles = { width: `${newMarginLeft}px`, zIndex: 99 @@ -70,7 +71,7 @@ export default function () { startClientY = event.clientY document.body.style.userSelect = 'none' - moveStartMarginTop = parseInt(proxy.activeComponentData.style['margin-top'] || 0, 10) + moveStartMarginTop = parseInt(autoPxTransform(proxy.activeComponentData.style['margin-top']), 10) || 0 if (!dragLine) { dragLine = new DragLine({ container: proxy.activeComponentData.parentNode.$elm @@ -86,7 +87,7 @@ export default function () { isMarginLeftResizeable = true startClientX = event.clientX document.body.style.userSelect = 'none' - moveStartMarginLeft = moveStartMarginTop = parseInt(proxy.activeComponentData.style['margin-left'] || 0, 10) + moveStartMarginLeft = moveStartMarginTop = parseInt(autoPxTransform(proxy.activeComponentData.style['margin-left']), 10) || 0 if (!dragLine) { dragLine = new DragLine({ diff --git a/lib/client/src/element-materials/materials/vant/field/index.js b/lib/client/src/element-materials/materials/vant/field/index.js index d7e0632a4..d20afd1ad 100644 --- a/lib/client/src/element-materials/materials/vant/field/index.js +++ b/lib/client/src/element-materials/materials/vant/field/index.js @@ -55,7 +55,7 @@ export default { 'position', { name: 'size', - include: ['display'] + include: ['display', 'width', 'min-width', 'max-width'] }, 'margin', 'pointer', From 99ae68ca63e915ee4aa3a0570bda733a63c936e4 Mon Sep 17 00:00:00 2001 From: terlinhe Date: Wed, 8 Feb 2023 15:54:06 +0800 Subject: [PATCH 41/80] =?UTF-8?q?fix=EF=BC=9A=E5=AD=98=E4=B8=BA=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E6=97=B6=E7=BB=84=E4=BB=B6id=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E9=A2=84=E8=A7=88=E6=A8=A1=E6=9D=BF=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/element-materials/core/extends/clone-node.js | 7 +++++++ .../src/element-materials/core/static/parse-template.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/client/src/element-materials/core/extends/clone-node.js b/lib/client/src/element-materials/core/extends/clone-node.js index fe3d00ed8..a4e5599eb 100644 --- a/lib/client/src/element-materials/core/extends/clone-node.js +++ b/lib/client/src/element-materials/core/extends/clone-node.js @@ -1,6 +1,7 @@ import _ from 'lodash' import isNode from '../static/is-node' import { createNode } from '../static/create-node' +import { updateFormItemVMode } from '../static/parse-template' import { camelCase, camelCaseTransformMerge } from 'change-case' const mergeData = (newNode, oldNode, deep) => { @@ -78,6 +79,12 @@ const syncVForVModel = (targetNode, originNode, replaceFunction) => { renderSlot.code = replaceFun(renderSlot.code) } }) + + // 表单容器componentId需要单独处理 + if (targetNode.type === 'widget-form') { + updateFormItemVMode(targetNode) + } + // 更新子元素 targetNode.children.forEach((childNode, index) => { syncVForVModel(childNode, originNode.children[index], replaceFun) diff --git a/lib/client/src/element-materials/core/static/parse-template.js b/lib/client/src/element-materials/core/static/parse-template.js index 2b67be364..fc3f92eff 100644 --- a/lib/client/src/element-materials/core/static/parse-template.js +++ b/lib/client/src/element-materials/core/static/parse-template.js @@ -1,7 +1,7 @@ import { camelCase, camelCaseTransformMerge } from 'change-case' import { parseTemplate } from './parse-data' -const updateFormItemVMode = (formNode) => { +export const updateFormItemVMode = (formNode) => { let formModelKey = `${camelCase(formNode.componentId, { transform: camelCaseTransformMerge })}model` const modelProps = formNode.renderProps?.model || {} if (modelProps?.buildInVariableType === 'CUSTOM' && modelProps.payload?.customVariableCode) { From 6e9f7f4c0ac12ebf3cdbeba12afcf8e3460be178 Mon Sep 17 00:00:00 2001 From: hmohuang <13407923955@163.com> Date: Wed, 8 Feb 2023 16:00:50 +0800 Subject: [PATCH 42/80] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=A1=86=E5=9F=BA=E7=A1=80=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/static/get-material.js | 4 + .../element-materials/materials/bk/index.js | 2 + .../materials/bk/input-textarea/index.js | 127 ++++++++++++++++++ .../materials/bk/input/index.js | 2 +- .../props/components/render-prop.vue | 1 + .../components/common/group-box/hacker.js | 4 +- 6 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 lib/client/src/element-materials/materials/bk/input-textarea/index.js diff --git a/lib/client/src/element-materials/core/static/get-material.js b/lib/client/src/element-materials/core/static/get-material.js index 31bd0d954..77cbb583d 100644 --- a/lib/client/src/element-materials/core/static/get-material.js +++ b/lib/client/src/element-materials/core/static/get-material.js @@ -53,6 +53,10 @@ export default function (elementType, name) { const material = bk.find(item => item.name === name) return _.cloneDeep(material) } + if (elementType.startsWith('bk-input') && name) { + const material = bk.find(item => item.name === name) + return _.cloneDeep(material) + } if (elementType === 'p') { const store = useStore() diff --git a/lib/client/src/element-materials/materials/bk/index.js b/lib/client/src/element-materials/materials/bk/index.js index fc5f97452..6c9440320 100644 --- a/lib/client/src/element-materials/materials/bk/index.js +++ b/lib/client/src/element-materials/materials/bk/index.js @@ -14,6 +14,7 @@ import grid from './grid' import column from './column' import input from './input' +import textarea from './input-textarea' import button from './button' import steps from './steps' import switcher from './switcher' @@ -91,6 +92,7 @@ const bkComponents = Object.seal([ image, link, input, + textarea, breadCrumb, steps, switcher, diff --git a/lib/client/src/element-materials/materials/bk/input-textarea/index.js b/lib/client/src/element-materials/materials/bk/input-textarea/index.js new file mode 100644 index 000000000..fd856ad1b --- /dev/null +++ b/lib/client/src/element-materials/materials/bk/input-textarea/index.js @@ -0,0 +1,127 @@ +export default { + name: 'textarea', + type: 'bk-input', + displayName: '文本框', + icon: 'bk-drag-input', + group: '表单', + order: 2, + document: 'https://magicbox.bk.tencent.com/static_api/v3/components_vue/2.0/example/index.html#/input', + events: [ + { + name: 'change', + tips: '文本框内容变化时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'input', + tips: '文本框输入时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'focus', + tips: '文本框获取焦点时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'blur', + tips: '文本框失去焦点时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'keypress', + tips: '文本框输入按下键盘时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'keydown', + tips: '文本框输入按下键盘时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'keyup', + tips: '文本框输入按下键盘按键松开时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'enter', + tips: '文本框获取焦点时,按下回车时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'paste', + tips: '文本框粘贴内容时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'clear', + tips: '点击文本框的清除图标时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'left-icon-click', + tips: '点击配置的左图标时调用该事件函数,事件回调参数 (value: String, event: Event)' + }, + { + name: 'right-icon-click', + tips: '点击配置的右图标时调用该事件函数,事件回调参数 (value: String, event: Event)' + } + ], + styles: [ + 'position', + { + name: 'size', + exclude: ['height', 'maxHeight', 'minHeight'] + }, + 'margin', + 'pointer', + 'opacity' + ], + renderStyles: { + display: 'inline-block', + verticalAlign: 'middle', + width: '300px' + }, + directives: [ + { + type: 'v-model', + prop: 'value' + } + ], + props: { + value: { + type: 'areatext', + val: '' + }, + type: { + type: 'hidden', + val: 'textarea', + tips: '输入框样式' + }, + 'font-size': { + type: 'string', + options: ['normal', 'medium', 'large'], + val: 'normal', + tips: '设置输入框内容字体大小:normal--12px;medium--14px;large--16px' + }, + placeholder: { + type: 'areatext', + tips: '空白提示' + }, + disabled: { + type: 'boolean', + val: false + }, + readonly: { + type: 'boolean', + val: false + }, + maxlength: { + type: 'number', + tips: '最大输入长度' + }, + minlength: { + type: 'number', + tips: '最小输入长度' + }, + name: { + type: 'string', + tips: 'html 原生属性 name' + }, + 'left-icon': { + type: 'icon' + }, + 'right-icon': { + type: 'icon' + } + } +} diff --git a/lib/client/src/element-materials/materials/bk/input/index.js b/lib/client/src/element-materials/materials/bk/input/index.js index 79e78f4f9..2afeb9799 100644 --- a/lib/client/src/element-materials/materials/bk/input/index.js +++ b/lib/client/src/element-materials/materials/bk/input/index.js @@ -95,7 +95,7 @@ export default { }, type: { type: 'string', - options: ['text', 'textarea', 'password', 'number', 'email', 'url', 'date'], + options: ['text', 'password', 'number', 'email', 'url', 'date', 'textarea'], val: 'text', tips: '输入框样式' }, diff --git a/lib/client/src/element-materials/modifier/component/props/components/render-prop.vue b/lib/client/src/element-materials/modifier/component/props/components/render-prop.vue index 888df3bdd..0e174444c 100644 --- a/lib/client/src/element-materials/modifier/component/props/components/render-prop.vue +++ b/lib/client/src/element-materials/modifier/component/props/components/render-prop.vue @@ -282,6 +282,7 @@ const typeMap = { 'array': 'json', + 'areatext': 'areatext', 'boolean': 'boolean', 'column': 'column', 'size': 'size', diff --git a/lib/client/src/views/index/components/material-panel/components/common/group-box/hacker.js b/lib/client/src/views/index/components/material-panel/components/common/group-box/hacker.js index 454ecd6f0..04bc61496 100644 --- a/lib/client/src/views/index/components/material-panel/components/common/group-box/hacker.js +++ b/lib/client/src/views/index/components/material-panel/components/common/group-box/hacker.js @@ -22,8 +22,8 @@ export const createElIcon = (node, config) => { } } -export const createCharts = (node, config) => { - if (node.type === 'chart' || node.type.startsWith('bk-charts')) { +export const createSameTypeComp = (node, config) => { + if (node.type === 'chart' || node.type.startsWith('bk-charts') || node.type === 'bk-input') { Object.assign(node, new Node(config)) } } From f7ce4cf7e2ecb3edec450f1c598be247f52aea97 Mon Sep 17 00:00:00 2001 From: hmohuang <13407923955@163.com> Date: Wed, 8 Feb 2023 17:09:18 +0800 Subject: [PATCH 43/80] =?UTF-8?q?perf:=E4=BC=98=E5=8C=96=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=A1=86=E5=9F=BA=E7=A1=80=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../materials/bk/input-textarea/index.js | 5 +++-- .../props/components/strategy/text.vue | 21 +------------------ 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/lib/client/src/element-materials/materials/bk/input-textarea/index.js b/lib/client/src/element-materials/materials/bk/input-textarea/index.js index fd856ad1b..441533fac 100644 --- a/lib/client/src/element-materials/materials/bk/input-textarea/index.js +++ b/lib/client/src/element-materials/materials/bk/input-textarea/index.js @@ -79,7 +79,7 @@ export default { ], props: { value: { - type: 'areatext', + type: 'text', val: '' }, type: { @@ -94,7 +94,7 @@ export default { tips: '设置输入框内容字体大小:normal--12px;medium--14px;large--16px' }, placeholder: { - type: 'areatext', + type: 'text', tips: '空白提示' }, disabled: { @@ -124,4 +124,5 @@ export default { type: 'icon' } } + } diff --git a/lib/client/src/element-materials/modifier/component/props/components/strategy/text.vue b/lib/client/src/element-materials/modifier/component/props/components/strategy/text.vue index 526716388..69f488782 100644 --- a/lib/client/src/element-materials/modifier/component/props/components/strategy/text.vue +++ b/lib/client/src/element-materials/modifier/component/props/components/strategy/text.vue @@ -9,30 +9,16 @@ specific language governing permissions and limitations under the License. --> -