diff --git a/app.platform/build.gradle b/app.platform/build.gradle index 7314c171..a9ecd9b0 100644 --- a/app.platform/build.gradle +++ b/app.platform/build.gradle @@ -20,21 +20,21 @@ dependencies { project(":io.sc.platform.scheduler.manager"), project(":io.sc.platform.scheduler.executor"), -// project(":io.sc.engine.mv"), -// project(":io.sc.engine.mv.frontend"), -// project(":io.sc.engine.mv.sample"), -// -// project(":io.sc.engine.rule.client"), -// project(":io.sc.engine.rule.client.spring"), -// project(":io.sc.engine.rule.core"), -// project(":io.sc.engine.rule.server"), -// project(":io.sc.engine.rule.sample"), -// + project(":io.sc.engine.mv"), + project(":io.sc.engine.mv.frontend"), + project(":io.sc.engine.mv.sample"), + + project(":io.sc.engine.rule.client"), + project(":io.sc.engine.rule.client.spring"), + project(":io.sc.engine.rule.core"), + project(":io.sc.engine.rule.server"), + project(":io.sc.engine.rule.sample"), + project(":io.sc.engine.st"), project(":io.sc.engine.st.frontend"), - project(":erm"), - project(":erm.frontend"), +// project(":erm"), +// project(":erm.frontend"), project(":io.sc.standard"), ) diff --git a/io.sc.engine.rule.core/src/main/java/io/sc/engine/rule/core/util/ExpressionReplacer.java b/io.sc.engine.rule.core/src/main/java/io/sc/engine/rule/core/util/ExpressionReplacer.java index ad4b953f..de4ec73d 100644 --- a/io.sc.engine.rule.core/src/main/java/io/sc/engine/rule/core/util/ExpressionReplacer.java +++ b/io.sc.engine.rule.core/src/main/java/io/sc/engine/rule/core/util/ExpressionReplacer.java @@ -36,9 +36,11 @@ public class ExpressionReplacer { public static String placeholder(String varName,String fieldName) { if(varName!=null) { if(fieldName!=null) { - return "${" + varName + "." + fieldName + "}"; + //return "${" + varName + "." + fieldName + "}"; + return varName + "." + fieldName; }else { - return "${" + varName + "}"; + //return "${" + varName + "}"; + return varName; } } return null; diff --git a/io.sc.engine.rule.frontend/package.json b/io.sc.engine.rule.frontend/package.json index e39133fb..9e3f7f57 100644 --- a/io.sc.engine.rule.frontend/package.json +++ b/io.sc.engine.rule.frontend/package.json @@ -92,7 +92,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.1.7", - "platform-core": "8.1.231", + "platform-core": "8.1.237", "quasar": "2.15.3", "tailwindcss": "3.4.3", "vue": "3.4.24", diff --git a/io.sc.engine.rule.frontend/src/i18n/messages.json b/io.sc.engine.rule.frontend/src/i18n/messages.json index 401c38be..daeb2de2 100644 --- a/io.sc.engine.rule.frontend/src/i18n/messages.json +++ b/io.sc.engine.rule.frontend/src/i18n/messages.json @@ -83,7 +83,7 @@ "re.resources.designer.processor.grid.entity.when": "When Expression", "re.resources.designer.processor.grid.entity.then": "Then Expression", "re.resources.designer.processor.grid.entity.isWhenThenShorted": "Shorted", - "re.resources.designer.processor.grid.entity.numberRangeVar": "Number Range", + "re.resources.designer.processor.grid.entity.numberRangeVar": "Number Range Expression", "re.resources.designer.processor.grid.entity.numberRange": "Number Range", "re.resources.designer.processor.grid.entity.conditionRangeVar": "Condition Range", "re.resources.designer.processor.grid.entity.conditionRange": "Condition Range", diff --git a/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json b/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json index 51a539ce..c8b75502 100644 --- a/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json +++ b/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json @@ -83,7 +83,7 @@ "re.resources.designer.processor.grid.entity.when": "When 表達式", "re.resources.designer.processor.grid.entity.then": "Then 表達式", "re.resources.designer.processor.grid.entity.isWhenThenShorted": "短路操作", - "re.resources.designer.processor.grid.entity.numberRangeVar": "數值分段", + "re.resources.designer.processor.grid.entity.numberRangeVar": "數值分段表達式", "re.resources.designer.processor.grid.entity.numberRange": "數值分段", "re.resources.designer.processor.grid.entity.conditionRangeVar": "條件分段", "re.resources.designer.processor.grid.entity.conditionRange": "條件分段", diff --git a/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json b/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json index f2ff25ae..2abba359 100644 --- a/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json +++ b/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json @@ -83,7 +83,7 @@ "re.resources.designer.processor.grid.entity.when": "When 表达式", "re.resources.designer.processor.grid.entity.then": "Then 表达式", "re.resources.designer.processor.grid.entity.isWhenThenShorted": "短路操作", - "re.resources.designer.processor.grid.entity.numberRangeVar": "数值分段", + "re.resources.designer.processor.grid.entity.numberRangeVar": "数值分段表达式", "re.resources.designer.processor.grid.entity.numberRange": "数值分段", "re.resources.designer.processor.grid.entity.conditionRangeVar": "条件分段", "re.resources.designer.processor.grid.entity.conditionRange": "条件分段", diff --git a/io.sc.engine.rule.frontend/src/views/resources/ImportSampleDialog.vue b/io.sc.engine.rule.frontend/src/views/resources/ImportSampleDialog.vue index d711456c..734fb6a0 100644 --- a/io.sc.engine.rule.frontend/src/views/resources/ImportSampleDialog.vue +++ b/io.sc.engine.rule.frontend/src/views/resources/ImportSampleDialog.vue @@ -25,13 +25,13 @@ click: (arg) => { if (arg.tickeds && arg.tickeds.length > 0) { const ids = Tools.extractProperties(arg.tickeds, 'id'); - console.log(ids); - DialogManager.confirm($t('re.resources.importSample.grid.toolbar.import.tip'), () => { - axios.post(Environment.apiContextPath('/api/re/resource/createExample'), ids).then(() => { - close(); - emit('afterImported'); - }); + axios.post(Environment.apiContextPath('/api/re/resource/createExample'), ids, { loading: true }).then(() => { + close(); + emit('afterImported'); }); + // DialogManager.confirm($t('re.resources.importSample.grid.toolbar.import.tip'), () => { + + // }); } }, }, @@ -64,12 +64,14 @@ +./w-code-mirror/GroovyFunctions diff --git a/io.sc.platform.core.frontend/src/platform/components/form/elements/WListGrid.vue b/io.sc.platform.core.frontend/src/platform/components/form/elements/WListGrid.vue new file mode 100644 index 00000000..1b6a3fa8 --- /dev/null +++ b/io.sc.platform.core.frontend/src/platform/components/form/elements/WListGrid.vue @@ -0,0 +1,81 @@ + + + diff --git a/io.sc.platform.core.frontend/src/platform/components/form/elements/w-code-mirror/PlaceholderPlugin.ts b/io.sc.platform.core.frontend/src/platform/components/form/elements/w-code-mirror/PlaceholderPlugin.ts new file mode 100644 index 00000000..efc9a3f3 --- /dev/null +++ b/io.sc.platform.core.frontend/src/platform/components/form/elements/w-code-mirror/PlaceholderPlugin.ts @@ -0,0 +1,84 @@ +import { EditorView, ViewPlugin, ViewUpdate, MatchDecorator, Decoration, DecorationSet, WidgetType } from '@codemirror/view'; + +class PlaceholderWidget extends WidgetType { + constructor(name) { + super(); + this.name = name; + } + + eq(other) { + return this.name == other.name; + } + + toDOM() { + const elt = document.createElement('span'); + elt.setAttribute('placeholder', true); + elt.style.cssText = ` + border: 1px solid gray; + border-radius: 4px; + padding: 2px 2px; + `; + elt.textContent = this.name; + return elt; + } + + ignoreEvent() { + return false; + } +} + +const placeholderMatcher = new MatchDecorator({ + regexp: /\$\{(.+?)\}/g, + decoration: (match) => + Decoration.replace({ + widget: new PlaceholderWidget(match[1]), + }), +}); + +const placeholderPlugin = ViewPlugin.fromClass( + class { + placeholders: DecorationSet; + constructor(view: EditorView) { + this.placeholders = placeholderMatcher.createDeco(view); + } + update(update: ViewUpdate) { + this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders); + } + }, + { + decorations: (instance) => instance.placeholders, + provide: (plugin) => + EditorView.atomicRanges.of((view) => { + return view.plugin(plugin)?.placeholders || Decoration.none; + }), + eventHandlers: { + mouseover: (e, view) => { + const target = e.target as HTMLElement; + if (target.tagName.toLowerCase() === 'span' && target.getAttribute('placeholder')) { + target.style.cssText = ` + border: 1px solid gray; + border-radius: 4px; + padding: 2px 2px; + background: orange; + `; + } + }, + mouseout: (e, view) => { + const target = e.target as HTMLElement; + if (target.tagName.toLowerCase() === 'span' && target.getAttribute('placeholder')) { + target.style.cssText = ` + border: 1px solid gray; + border-radius: 4px; + padding: 2px 2px; + `; + } + }, + contextmenu: (e, view) => { + e.preventDefault(); + console.log(view); + }, + }, + }, +); + +export default placeholderPlugin; diff --git a/io.sc.platform.core.frontend/src/platform/plugin/axios.ts b/io.sc.platform.core.frontend/src/platform/plugin/axios.ts index d8ffab41..eff20714 100644 --- a/io.sc.platform.core.frontend/src/platform/plugin/axios.ts +++ b/io.sc.platform.core.frontend/src/platform/plugin/axios.ts @@ -4,6 +4,7 @@ import { i18n } from './i18n'; import { PConst } from '@/platform/PConst'; import { Environment } from '@/platform/plugin/environment'; import { NotifyManager } from './manager'; +import { QuasarTools } from '@/platform/utils'; const ignoredUrls: string[] = [PConst.API_I18N_MESSAGES_URL, PConst.API_APP_CONFIGURE_URL]; const gc = Environment.getConfigure(); @@ -22,6 +23,14 @@ const requestInterceptor = (config: any) => { if (gc.axios?.basicAuth?.enable) { result.headers.Authorization = 'Basic ' + window.btoa(gc.axios.basicAuth.username + ':' + gc.axios.basicAuth.password); } + // 如果请求时传入 { loading: true } 属性, 则自动显示 "正在处理..., 请等待" 模态对话框 + if (config?.loading) { + QuasarTools.getQuasar()?.loading?.show({ + message: i18n.global.t('loading'), + boxClass: 'bg-grey-2 text-grey-9', + spinnerColor: 'primary', + }); + } return result; }; @@ -33,11 +42,13 @@ const requestErrorInterceptor = (error: any) => { // 响应拦截器 const responseInterceptor = (response: any) => { // 请求成功, 进入该方法说明 response 的状态码为 2xx + QuasarTools.getQuasar()?.loading?.hide(); return response.data; }; // 响应错误拦截器 const responseErrorInterceptor = (error: any) => { + QuasarTools.getQuasar()?.loading?.hide(); // 请求失败, 进入该方法说明 response 的状态码不为 2xx if (error.code === 'ECONNABORTED' || error.message.indexOf('timeout') !== -1 || error.message === 'Network Error') { // 发生网络错误 @@ -94,6 +105,7 @@ const noErrorAxios = Axios.create({ }); noErrorAxios.interceptors.request.use(requestInterceptor, requestErrorInterceptor); noErrorAxios.interceptors.response.use(responseInterceptor, (error: any) => { + QuasarTools.getQuasar()?.loading?.hide(); // 请求失败, 进入该方法说明 response 的状态码不为 2xx if (error.code === 'ECONNABORTED' || error.message.indexOf('timeout') !== -1 || error.message === 'Network Error') { // 发生网络错误 diff --git a/io.sc.platform.core.frontend/src/platform/utils/QuasarTools.ts b/io.sc.platform.core.frontend/src/platform/utils/QuasarTools.ts index 76eff2fb..d6b70b28 100644 --- a/io.sc.platform.core.frontend/src/platform/utils/QuasarTools.ts +++ b/io.sc.platform.core.frontend/src/platform/utils/QuasarTools.ts @@ -5,6 +5,24 @@ import { Environment } from '@/platform/plugin/environment'; * Quasar 工具类 */ class QuasarTools { + static #quasar: any = null; + + /** + * 获取 quasar 示例 + * @returns quasar 示例 + */ + public static getQuasar() { + return QuasarTools.#quasar; + } + + /** + * 设置 quasar 示例 + * @param quasar quasar 示例 + */ + public static setQuasar(quasar: any) { + QuasarTools.#quasar = quasar; + } + /** * 改变 quasar 的 CSS 中的变量值 * @param key 变量名 diff --git a/io.sc.platform.core.frontend/src/routes/routes.json b/io.sc.platform.core.frontend/src/routes/routes.json index 9a499a7d..c0f6d157 100644 --- a/io.sc.platform.core.frontend/src/routes/routes.json +++ b/io.sc.platform.core.frontend/src/routes/routes.json @@ -52,6 +52,32 @@ } }, + { + "name": "route.testcase.codemirror", + "path": "testcase/codemirror", + "parent": "/", + "priority": 0, + "component": "component.testcase.codemirror", + "componentPath": "@/views/testcase/code-mirror/code-mirror.vue", + "redirect": null, + "meta": { + "permissions": ["/testcase/loading/**/*"] + } + }, + + { + "name": "route.testcase.loading", + "path": "testcase/loading", + "parent": "/", + "priority": 0, + "component": "component.testcase.loading", + "componentPath": "@/views/testcase/loading/loading.vue", + "redirect": null, + "meta": { + "permissions": ["/testcase/loading/**/*"] + } + }, + { "name": "route.testcase.excel", "path": "testcase/excel", diff --git a/io.sc.platform.core.frontend/src/views/testcase/code-mirror/GroovyFunctions.ts b/io.sc.platform.core.frontend/src/views/testcase/code-mirror/GroovyFunctions.ts new file mode 100644 index 00000000..672e902a --- /dev/null +++ b/io.sc.platform.core.frontend/src/views/testcase/code-mirror/GroovyFunctions.ts @@ -0,0 +1,43 @@ +const GroovyFunctions = [ + { label: 'PI', type: 'constant', apply: 'PI', detail: '常量 π' }, + { label: 'E', type: 'constant', apply: 'E', detail: '常量 e' }, + { label: 'IEEEremainder(v1,v2)', type: 'function', apply: 'IEEEremainder(v1,v2)', detail: '根据 IEEE 754 标准返回 v1 除以 v2 的余数' }, + { label: 'abs(v)', type: 'function', apply: 'abs(v)', detail: '绝对值' }, + { label: 'acos(v)', type: 'function', apply: 'acos(v)', detail: '反余弦' }, + { label: 'asin(v)', type: 'function', apply: 'asin(v)', detail: '反正弦' }, + { label: 'atan(v)', type: 'function', apply: 'atan(v)', detail: '' }, + { label: 'atan2(v)', type: 'function', apply: 'atan2(v)', detail: '' }, + { label: 'cbrt(v)', type: 'function', apply: 'cbrt(v)', detail: '' }, + { label: 'ceil(v)', type: 'function', apply: 'ceil(v)', detail: '' }, + { label: 'cos(v)', type: 'function', apply: 'cos(v)', detail: '' }, + { label: 'cosh(v)', type: 'function', apply: 'cosh(v)', detail: '' }, + { label: 'exp(v)', type: 'function', apply: 'exp(v)', detail: '' }, + { label: 'expm1(v)', type: 'function', apply: 'expm1(v)', detail: '' }, + { label: 'floor(v)', type: 'function', apply: 'floor(v)', detail: '' }, + { label: 'inverseNormalDistributioin(x)', type: 'function', apply: 'inverseNormalDistributioin(x)', detail: '' }, + { label: 'join(split,s1,s2,s3...,sn)', type: 'function', apply: 'join(split,s1,s2,s3...,sn)', detail: '' }, + { label: 'ln(v)', type: 'function', apply: 'ln(v)', detail: '' }, + { label: 'max(v1,v2,...)', type: 'function', apply: 'max(v1,v2,...)', detail: '' }, + { label: 'min(v1,v2,...)', type: 'function', apply: 'min(v1,v2,...)', detail: '' }, + { label: 'normalDistributioin(x)', type: 'function', apply: 'normalDistributioin(x)', detail: '' }, + { label: 'pow(x,y)', type: 'function', apply: 'pow(x,y)', detail: '' }, + { label: 'random()', type: 'function', apply: 'random()', detail: '' }, + { label: 'rint(v)', type: 'function', apply: 'rint(v)', detail: '' }, + { label: 'round(v)', type: 'function', apply: 'round(v)', detail: '' }, + { label: 'sin(v)', type: 'function', apply: 'sin(v)', detail: '' }, + { label: 'sinh(v)', type: 'function', apply: 'sinh(v)', detail: '' }, + { label: 'sqrt(v)', type: 'function', apply: 'sqrt(v)', detail: '' }, + { label: 'sum(v1,v2,...)', type: 'function', apply: 'sum(v1,v2,...)', detail: '' }, + { label: 'tan(v)', type: 'function', apply: 'tan(v)', detail: '' }, + { label: 'tanh(v)', type: 'function', apply: 'tanh(v)', detail: '' }, + { label: 'toDegrees(v)', type: 'function', apply: 'toDegrees(v)', detail: '' }, + { label: 'toRadians(v)', type: 'function', apply: 'toRadians(v)', detail: '' }, + { + label: 'transformSequencing(value,sourceMin,sourceMax,targetMin,targetMax)', + type: 'function', + apply: 'transformSequencing(value,sourceMin,sourceMax,targetMin,targetMax)', + detail: '', + }, +]; + +export default GroovyFunctions; diff --git a/io.sc.platform.core.frontend/src/views/testcase/code-mirror/code-mirror.vue b/io.sc.platform.core.frontend/src/views/testcase/code-mirror/code-mirror.vue new file mode 100644 index 00000000..135cfe7d --- /dev/null +++ b/io.sc.platform.core.frontend/src/views/testcase/code-mirror/code-mirror.vue @@ -0,0 +1,525 @@ + + diff --git a/io.sc.platform.core.frontend/src/views/testcase/loading/loading.vue b/io.sc.platform.core.frontend/src/views/testcase/loading/loading.vue new file mode 100644 index 00000000..65db1949 --- /dev/null +++ b/io.sc.platform.core.frontend/src/views/testcase/loading/loading.vue @@ -0,0 +1,12 @@ + + diff --git a/io.sc.platform.core.frontend/template-project/package.json b/io.sc.platform.core.frontend/template-project/package.json index 2d18dea0..18aaba2d 100644 --- a/io.sc.platform.core.frontend/template-project/package.json +++ b/io.sc.platform.core.frontend/template-project/package.json @@ -1,6 +1,6 @@ { "name": "platform-core", - "version": "8.1.231", + "version": "8.1.237", "description": "前端核心包,用于快速构建前端的脚手架", "private": false, "keywords": [], @@ -104,7 +104,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.1.7", - "platform-core": "8.1.231", + "platform-core": "8.1.237", "quasar": "2.15.3", "tailwindcss": "3.4.3", "vue": "3.4.24", diff --git a/io.sc.platform.core.frontend/template-project/src/components/index.ts b/io.sc.platform.core.frontend/template-project/src/components/index.ts index 8dfeb7fd..39705852 100644 --- a/io.sc.platform.core.frontend/template-project/src/components/index.ts +++ b/io.sc.platform.core.frontend/template-project/src/components/index.ts @@ -6,6 +6,8 @@ import component_testcase_openNoMenuRoute from '@/views/testcase/route/OpenNoMen import component_testcase_noMenuRoute from '@/views/testcase/route/NoMenuRoute.vue'; import component_testcase_mathEditor from '@/views/testcase/math/MathEditor.vue'; import component_testcase_form from '@/views/testcase/form/form.vue'; +import component_testcase_codemirror from '@/views/testcase/code-mirror/code-mirror.vue'; +import component_testcase_loading from '@/views/testcase/loading/loading.vue'; import component_testcase_excel from '@/views/testcase/excel/Excel.vue'; import component_testcase_word from '@/views/testcase/word/Word.vue'; import component_testcase_likmDialog from '@/views/likm/Dialog.vue'; @@ -23,6 +25,8 @@ const localComponents = { 'component.testcase.noMenuRoute': component_testcase_noMenuRoute, 'component.testcase.mathEditor': component_testcase_mathEditor, 'component.testcase.form': component_testcase_form, + 'component.testcase.codemirror': component_testcase_codemirror, + 'component.testcase.loading': component_testcase_loading, 'component.testcase.excel': component_testcase_excel, 'component.testcase.word': component_testcase_word, 'component.testcase.likmDialog': component_testcase_likmDialog, diff --git a/io.sc.platform.core.frontend/template-project/src/i18n/messages.json b/io.sc.platform.core.frontend/template-project/src/i18n/messages.json index aa4092d5..a5945b76 100644 --- a/io.sc.platform.core.frontend/template-project/src/i18n/messages.json +++ b/io.sc.platform.core.frontend/template-project/src/i18n/messages.json @@ -3,6 +3,8 @@ "menu.testcase.openNoMenuRoute": "Open No Menu Route", "menu.testcase.mathEditor": "Math Formual Editor", "menu.testcase.form":"Form Element", + "menu.testcase.codemirror":"Code Mirror", + "menu.testcase.loading":"Loading", "menu.testcase.excel": "Excel", "menu.testcase.word": "Word", diff --git a/io.sc.platform.core.frontend/template-project/src/i18n/messages_tw_CN.json b/io.sc.platform.core.frontend/template-project/src/i18n/messages_tw_CN.json index 67d73c56..73b0171f 100644 --- a/io.sc.platform.core.frontend/template-project/src/i18n/messages_tw_CN.json +++ b/io.sc.platform.core.frontend/template-project/src/i18n/messages_tw_CN.json @@ -3,6 +3,8 @@ "menu.testcase.openNoMenuRoute": "打開無關聯菜單的路由", "menu.testcase.mathEditor": "數學公式編輯器", "menu.testcase.form":"表單元素", + "menu.testcase.codemirror":"代碼編輯器", + "menu.testcase.loading":"正在加載", "menu.testcase.excel": "Excel", "menu.testcase.word": "Word", diff --git a/io.sc.platform.core.frontend/template-project/src/i18n/messages_zh_CN.json b/io.sc.platform.core.frontend/template-project/src/i18n/messages_zh_CN.json index 27fc8c52..6b203fb1 100644 --- a/io.sc.platform.core.frontend/template-project/src/i18n/messages_zh_CN.json +++ b/io.sc.platform.core.frontend/template-project/src/i18n/messages_zh_CN.json @@ -3,6 +3,8 @@ "menu.testcase.openNoMenuRoute": "打开无关联菜单的路由示例", "menu.testcase.mathEditor": "数学公式编辑器", "menu.testcase.form":"表单元素", + "menu.testcase.codemirror":"代码编辑器", + "menu.testcase.loading":"正在加載", "menu.testcase.excel": "Excel", "menu.testcase.word": "Word", diff --git a/io.sc.platform.core.frontend/template-project/src/menus/menus.json b/io.sc.platform.core.frontend/template-project/src/menus/menus.json index ed7f8b9c..5dee34b0 100644 --- a/io.sc.platform.core.frontend/template-project/src/menus/menus.json +++ b/io.sc.platform.core.frontend/template-project/src/menus/menus.json @@ -45,6 +45,24 @@ "icon": "bi-palette", "routeName": "route.testcase.form" }, + { + "type": "ROUTE", + "order": 350, + "parentId": "menu.testcase", + "id": "menu.testcase.codemirror", + "titleI18nKey": "menu.testcase.codemirror", + "icon": "bi-palette", + "routeName": "route.testcase.codemirror" + }, + { + "type": "ROUTE", + "order": 350, + "parentId": "menu.testcase", + "id": "menu.testcase.loading", + "titleI18nKey": "menu.testcase.loading", + "icon": "bi-palette", + "routeName": "route.testcase.loading" + }, { "type": "ROUTE", "order": 400, diff --git a/io.sc.platform.core.frontend/template-project/src/routes/routes.json b/io.sc.platform.core.frontend/template-project/src/routes/routes.json index 9a499a7d..c0f6d157 100644 --- a/io.sc.platform.core.frontend/template-project/src/routes/routes.json +++ b/io.sc.platform.core.frontend/template-project/src/routes/routes.json @@ -52,6 +52,32 @@ } }, + { + "name": "route.testcase.codemirror", + "path": "testcase/codemirror", + "parent": "/", + "priority": 0, + "component": "component.testcase.codemirror", + "componentPath": "@/views/testcase/code-mirror/code-mirror.vue", + "redirect": null, + "meta": { + "permissions": ["/testcase/loading/**/*"] + } + }, + + { + "name": "route.testcase.loading", + "path": "testcase/loading", + "parent": "/", + "priority": 0, + "component": "component.testcase.loading", + "componentPath": "@/views/testcase/loading/loading.vue", + "redirect": null, + "meta": { + "permissions": ["/testcase/loading/**/*"] + } + }, + { "name": "route.testcase.excel", "path": "testcase/excel", diff --git a/io.sc.platform.core.frontend/template-project/src/views/testcase/code-mirror/GroovyFunctions.ts b/io.sc.platform.core.frontend/template-project/src/views/testcase/code-mirror/GroovyFunctions.ts new file mode 100644 index 00000000..672e902a --- /dev/null +++ b/io.sc.platform.core.frontend/template-project/src/views/testcase/code-mirror/GroovyFunctions.ts @@ -0,0 +1,43 @@ +const GroovyFunctions = [ + { label: 'PI', type: 'constant', apply: 'PI', detail: '常量 π' }, + { label: 'E', type: 'constant', apply: 'E', detail: '常量 e' }, + { label: 'IEEEremainder(v1,v2)', type: 'function', apply: 'IEEEremainder(v1,v2)', detail: '根据 IEEE 754 标准返回 v1 除以 v2 的余数' }, + { label: 'abs(v)', type: 'function', apply: 'abs(v)', detail: '绝对值' }, + { label: 'acos(v)', type: 'function', apply: 'acos(v)', detail: '反余弦' }, + { label: 'asin(v)', type: 'function', apply: 'asin(v)', detail: '反正弦' }, + { label: 'atan(v)', type: 'function', apply: 'atan(v)', detail: '' }, + { label: 'atan2(v)', type: 'function', apply: 'atan2(v)', detail: '' }, + { label: 'cbrt(v)', type: 'function', apply: 'cbrt(v)', detail: '' }, + { label: 'ceil(v)', type: 'function', apply: 'ceil(v)', detail: '' }, + { label: 'cos(v)', type: 'function', apply: 'cos(v)', detail: '' }, + { label: 'cosh(v)', type: 'function', apply: 'cosh(v)', detail: '' }, + { label: 'exp(v)', type: 'function', apply: 'exp(v)', detail: '' }, + { label: 'expm1(v)', type: 'function', apply: 'expm1(v)', detail: '' }, + { label: 'floor(v)', type: 'function', apply: 'floor(v)', detail: '' }, + { label: 'inverseNormalDistributioin(x)', type: 'function', apply: 'inverseNormalDistributioin(x)', detail: '' }, + { label: 'join(split,s1,s2,s3...,sn)', type: 'function', apply: 'join(split,s1,s2,s3...,sn)', detail: '' }, + { label: 'ln(v)', type: 'function', apply: 'ln(v)', detail: '' }, + { label: 'max(v1,v2,...)', type: 'function', apply: 'max(v1,v2,...)', detail: '' }, + { label: 'min(v1,v2,...)', type: 'function', apply: 'min(v1,v2,...)', detail: '' }, + { label: 'normalDistributioin(x)', type: 'function', apply: 'normalDistributioin(x)', detail: '' }, + { label: 'pow(x,y)', type: 'function', apply: 'pow(x,y)', detail: '' }, + { label: 'random()', type: 'function', apply: 'random()', detail: '' }, + { label: 'rint(v)', type: 'function', apply: 'rint(v)', detail: '' }, + { label: 'round(v)', type: 'function', apply: 'round(v)', detail: '' }, + { label: 'sin(v)', type: 'function', apply: 'sin(v)', detail: '' }, + { label: 'sinh(v)', type: 'function', apply: 'sinh(v)', detail: '' }, + { label: 'sqrt(v)', type: 'function', apply: 'sqrt(v)', detail: '' }, + { label: 'sum(v1,v2,...)', type: 'function', apply: 'sum(v1,v2,...)', detail: '' }, + { label: 'tan(v)', type: 'function', apply: 'tan(v)', detail: '' }, + { label: 'tanh(v)', type: 'function', apply: 'tanh(v)', detail: '' }, + { label: 'toDegrees(v)', type: 'function', apply: 'toDegrees(v)', detail: '' }, + { label: 'toRadians(v)', type: 'function', apply: 'toRadians(v)', detail: '' }, + { + label: 'transformSequencing(value,sourceMin,sourceMax,targetMin,targetMax)', + type: 'function', + apply: 'transformSequencing(value,sourceMin,sourceMax,targetMin,targetMax)', + detail: '', + }, +]; + +export default GroovyFunctions; diff --git a/io.sc.platform.core.frontend/template-project/src/views/testcase/code-mirror/code-mirror.vue b/io.sc.platform.core.frontend/template-project/src/views/testcase/code-mirror/code-mirror.vue new file mode 100644 index 00000000..135cfe7d --- /dev/null +++ b/io.sc.platform.core.frontend/template-project/src/views/testcase/code-mirror/code-mirror.vue @@ -0,0 +1,525 @@ + + diff --git a/io.sc.platform.core.frontend/template-project/src/views/testcase/loading/loading.vue b/io.sc.platform.core.frontend/template-project/src/views/testcase/loading/loading.vue new file mode 100644 index 00000000..65db1949 --- /dev/null +++ b/io.sc.platform.core.frontend/template-project/src/views/testcase/loading/loading.vue @@ -0,0 +1,12 @@ + + diff --git a/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties b/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties index 595244d6..3217bda5 100644 --- a/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties +++ b/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words.properties @@ -227,4 +227,5 @@ paste=Paste cut =Cut online=Online offline=Offline -finish=Finish \ No newline at end of file +finish=Finish +loading=Processing, please wait ...... \ No newline at end of file diff --git a/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties b/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties index 7cfc4571..7a7cbba8 100644 --- a/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties +++ b/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_tw_CN.properties @@ -227,4 +227,5 @@ paste=\u7C98\u8CBC cut =\u526A\u5207 online=\u5728\u7DDA offline=\u96E2\u7DDA -finish=\u5B8C\u6210 \ No newline at end of file +finish=\u5B8C\u6210 +loading=\u6B63\u5728\u8655\u7406, \u8ACB\u7B49\u5F85...... \ No newline at end of file diff --git a/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties b/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties index 860934cb..f710bf61 100644 --- a/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties +++ b/io.sc.platform.core/src/main/resources/io/sc/platform/core/i18n/words_zh_CN.properties @@ -227,4 +227,5 @@ paste=\u7C98\u8D34 cut =\u526A\u5207 online=\u5728\u7EBF offline=\u79BB\u7EBF -finish=\u5B8C\u6210 \ No newline at end of file +finish=\u5B8C\u6210 +loading=\u6B63\u5728\u5904\u7406, \u8BF7\u7B49\u5F85...... \ No newline at end of file diff --git a/io.sc.platform.developer.frontend/package.json b/io.sc.platform.developer.frontend/package.json index a59b9cd9..58d6c68d 100644 --- a/io.sc.platform.developer.frontend/package.json +++ b/io.sc.platform.developer.frontend/package.json @@ -92,7 +92,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.1.7", - "platform-core": "8.1.231", + "platform-core": "8.1.232", "quasar": "2.15.3", "tailwindcss": "3.4.3", "vue": "3.4.24", diff --git a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/support/JpaEntityPersistentEvent.java b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/support/JpaEntityPersistentEvent.java index 3fefb7ae..54e74120 100644 --- a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/support/JpaEntityPersistentEvent.java +++ b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/entity/support/JpaEntityPersistentEvent.java @@ -6,8 +6,6 @@ import org.springframework.context.ApplicationEvent; * Jpa */ public class JpaEntityPersistentEvent extends ApplicationEvent{ - private static final long serialVersionUID = -73175079790751083L; - private Class entityClass; //实体类 private JpaEntityPersistentEventType type; //类型:增删改 private Object oldEntity; //原实体对象 diff --git a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/DaoRepository.java b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/DaoRepository.java index 57192a8a..7825d2e3 100644 --- a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/DaoRepository.java +++ b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/DaoRepository.java @@ -9,6 +9,7 @@ import org.springframework.data.repository.NoRepositoryBean; import javax.persistence.EntityManager; import javax.persistence.metamodel.ManagedType; import java.io.Serializable; +import java.util.List; @NoRepositoryBean public interface DaoRepository extends JpaRepository,JpaSpecificationExecutor{ @@ -24,4 +25,10 @@ public interface DaoRepository extends JpaRepository< public String getIdName(); public ID getId(E entity); public void setId(E entity,ID id); + + public S save(S entity,boolean isSendEvent); + public List saveAll(Iterable entities, boolean isSendEvent); + public void delete(E entity,boolean isSendEvent); + public void deleteInBatch(Iterable entities,boolean isSendEvent); + public void deleteAllInBatch(boolean isSendEvent); } diff --git a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/impl/DaoRepositoryImpl.java b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/impl/DaoRepositoryImpl.java index cee6bd13..631216e0 100644 --- a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/impl/DaoRepositoryImpl.java +++ b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/repository/impl/DaoRepositoryImpl.java @@ -1,21 +1,33 @@ package io.sc.platform.orm.repository.impl; +import io.sc.platform.core.Environment; +import io.sc.platform.orm.entity.support.JpaEntityPersistentEvent; +import io.sc.platform.orm.entity.support.JpaEntityPersistentEventType; import io.sc.platform.orm.repository.DaoRepository; import io.sc.platform.orm.service.support.ManagedTypeAttributes; +import org.springframework.context.ApplicationContext; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; import javax.persistence.EntityManager; import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.SingularAttribute; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_STRING; +import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind; +import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString; public class DaoRepositoryImpl extends SimpleJpaRepository implements DaoRepository { private JpaEntityInformation jpaEntityInformation; private EntityManager entityManager; - + public DaoRepositoryImpl(Class domainClass, EntityManager entityManager) { super(domainClass, entityManager); } @@ -82,4 +94,136 @@ public class DaoRepositoryImpl extends SimpleJpaRepos wrapper.setPropertyValue(idName, id); } } + + @Override + @Transactional + public S save(S entity) { + Assert.notNull(entity, "Entity must not be null."); + if (jpaEntityInformation.isNew(entity)) { + entityManager.persist(entity); + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.AFTER_ADD,null, entity)); + return entity; + } else if("".equals(getId(entity))){//当单一主键为空字符串时,也认为需要新增 + setId(entity,null); + entityManager.persist(entity); + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.AFTER_ADD,null, entity)); + return entity; + }else { + E oldEntity = this.getReferenceById(getId(entity)); + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(), JpaEntityPersistentEventType.BEFORE_UPDATE, oldEntity, entity)); + S result = entityManager.merge(entity); + return result; + } + } + + @Override + @Transactional + public S save(S entity, boolean isSendEvent) { + if (jpaEntityInformation.isNew(entity)) { + entityManager.persist(entity); + if(isSendEvent) {applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.AFTER_ADD,null, entity));} + return entity; + } else if("".equals(getId(entity))){//当单一主键为空字符串时,也认为需要新增 + setId(entity,null); + entityManager.persist(entity); + if(isSendEvent) {applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.AFTER_ADD,null, entity));} + return entity; + }else { + E oldEntity =this.getReferenceById(getId(entity)); + if(isSendEvent) {applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.BEFORE_UPDATE,oldEntity, entity));} + S result =entityManager.merge(entity); + return result; + } + } + + @Override + @Transactional + public List saveAll(Iterable entities, boolean isSendEvent) { + Assert.notNull(entities, "The given Iterable of entities not be null!"); + List result = new ArrayList(); + if(isSendEvent) { + for (S entity : entities) { + result.add(save(entity)); + } + }else { + for (S entity : entities) { + result.add(save(entity,false)); + } + } + return result; + } + + @Override + @Transactional + public void delete(E entity) { + Assert.notNull(entity, "The entity must not be null!"); + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.BEFORE_DELETE,entity, null)); + entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity)); + } + + @Override + @Transactional + public void delete(E entity,boolean isSendEvent) { + Assert.notNull(entity, "The entity must not be null!"); + if(isSendEvent) { + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.BEFORE_DELETE,entity, null)); + } + entityManager.remove(entityManager.contains(entity) ? entity : entityManager.merge(entity)); + } + + @Override + @Transactional + public void deleteInBatch(Iterable entities) { + Assert.notNull(entities, "The given Iterable of entities not be null!"); + + if (!entities.iterator().hasNext()) { + return; + } + E entity =entities.iterator().next(); + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.BEFORE_DELETE,entities, null)); + applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, jpaEntityInformation.getEntityName()), entities, entityManager) + .executeUpdate(); + } + + @Override + @Transactional + public void deleteInBatch(Iterable entities,boolean isSendEvent) { + Assert.notNull(entities, "The given Iterable of entities not be null!"); + + if (!entities.iterator().hasNext()) { + return; + } + E entity =entities.iterator().next(); + if(isSendEvent) { + applicationContext().publishEvent(new JpaEntityPersistentEvent(entity.getClass(),JpaEntityPersistentEventType.BEFORE_DELETE,entities, null)); + } + applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, jpaEntityInformation.getEntityName()), entities, entityManager) + .executeUpdate(); + } + + @Override + @Transactional + public void deleteAllInBatch() { + String entityName =jpaEntityInformation.getEntityName(); + Assert.hasText(entityName, "Entity name must not be null or empty!"); + applicationContext().publishEvent(new JpaEntityPersistentEvent(jpaEntityInformation.getJavaType(),JpaEntityPersistentEventType.BEFORE_DELETE_ALL,null, null)); + String sql =String.format("delete from %s x", entityName); + entityManager.createQuery(sql).executeUpdate(); + } + + @Override + @Transactional + public void deleteAllInBatch(boolean isSendEvent) { + String entityName =jpaEntityInformation.getEntityName(); + Assert.hasText(entityName, "Entity name must not be null or empty!"); + if(isSendEvent) { + applicationContext().publishEvent(new JpaEntityPersistentEvent(jpaEntityInformation.getJavaType(),JpaEntityPersistentEventType.BEFORE_DELETE_ALL,null, null)); + } + String sql =String.format("delete from %s x", entityName); + entityManager.createQuery(sql).executeUpdate(); + } + + private ApplicationContext applicationContext(){ + return Environment.getInstance().getApplicationContext(); + } } diff --git a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/impl/DaoServiceImpl.java b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/impl/DaoServiceImpl.java index 685d80aa..9f04d903 100644 --- a/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/impl/DaoServiceImpl.java +++ b/io.sc.platform.orm/src/main/java/io/sc/platform/orm/service/impl/DaoServiceImpl.java @@ -2,6 +2,8 @@ package io.sc.platform.orm.service.impl; import io.sc.platform.core.Environment; import io.sc.platform.orm.entity.CorporationAuditorEntity; +import io.sc.platform.orm.entity.support.JpaEntityPersistentEvent; +import io.sc.platform.orm.entity.support.JpaEntityPersistentEventType; import io.sc.platform.orm.repository.DaoRepository; import io.sc.platform.orm.service.DaoService; import io.sc.platform.orm.service.support.*; @@ -254,10 +256,14 @@ public abstract class DaoServiceImpl