From 138be4bdc1f6e1e7238fe7616092d386711e04be Mon Sep 17 00:00:00 2001 From: v_xugzhou <941071842@qq.com> Date: Tue, 31 Oct 2023 09:17:41 +0800 Subject: [PATCH 01/62] =?UTF-8?q?bugfix:=20=E5=AE=A1=E6=89=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E4=BC=A0=E5=8F=82=E9=94=99=E8=AF=AF=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue index 9020878e05..5e89cb23fc 100644 --- a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue +++ b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue @@ -1328,8 +1328,8 @@ task_id: this.subProcessTaskId || this.instance_id, node_id: id } - // 如果存在子流程任务节点时则不需要传subprocess_id - if (!this.subProcessTaskId) { + // 如果存在子流程任务节点时则需要传subprocess_id + if (this.subProcessTaskId) { let { subprocess_stack: stack } = this.nodeDetailConfig if (stack) { stack = JSON.parse(stack) From 1d50f1e82ad65402932cf6e04504764efc655695 Mon Sep 17 00:00:00 2001 From: hanshuaikang <1758504262@qq.com> Date: Fri, 14 Jul 2023 12:11:41 +0800 Subject: [PATCH 02/62] =?UTF-8?q?bugfix:=20=E8=A6=86=E7=9B=96IamPermission?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=8C=85=E6=9B=B4=E6=96=B0=E6=97=A0=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/apis/drf/viewsets/package_source.py | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/gcloud/core/apis/drf/viewsets/package_source.py b/gcloud/core/apis/drf/viewsets/package_source.py index 6a6b1b3e19..dbaf74ced0 100644 --- a/gcloud/core/apis/drf/viewsets/package_source.py +++ b/gcloud/core/apis/drf/viewsets/package_source.py @@ -11,26 +11,23 @@ specific language governing permissions and limitations under the License. """ import logging +from itertools import chain + import jsonschema import ujson as json -from itertools import chain from django.db import transaction - -from rest_framework import status - -from rest_framework import permissions -from rest_framework.response import Response +from rest_framework import permissions, status from rest_framework.exceptions import NotAcceptable -from rest_framework.generics import UpdateAPIView, ListCreateAPIView, DestroyAPIView +from rest_framework.generics import DestroyAPIView, ListCreateAPIView, UpdateAPIView +from rest_framework.response import Response -from gcloud.iam_auth import IAMMeta +from gcloud.core.apis.drf.permission import IamPermission, IamPermissionInfo +from gcloud.core.apis.drf.serilaziers import PackageSourceSerializer +from gcloud.core.apis.drf.viewsets.base import GcloudCommonMixin from gcloud.external_plugins import exceptions -from gcloud.external_plugins.models import source_cls_factory, CachePackageSource +from gcloud.external_plugins.models import CachePackageSource, source_cls_factory from gcloud.external_plugins.schemas import ADD_SOURCE_SCHEMA, UPDATE_SOURCE_SCHEMA - -from gcloud.core.apis.drf.viewsets.base import GcloudCommonMixin -from gcloud.core.apis.drf.permission import IamPermissionInfo, IamPermission -from gcloud.core.apis.drf.serilaziers import PackageSourceSerializer +from gcloud.iam_auth import IAMMeta logger = logging.getLogger("root") @@ -43,6 +40,17 @@ class PackageSourcePermission(IamPermission): "destroy": IamPermissionInfo(IAMMeta.ADMIN_EDIT_ACTION), } + def check_permission(self, request, view, resource_param=None, check_hook=None): + + permission_info = self.actions.get(view.action, IamPermissionInfo(IAMMeta.ADMIN_EDIT_ACTION)) + + # 不匹配权限不做校验 + if permission_info.check_hook != check_hook: + return True + + self.iam_auth_check(request, action=permission_info.iam_action, resources=[]) + return True + def get_source_models(): origin_models = list(source_cls_factory.values()) From 3e8c751ce9921f59e5a4ed49b9e864b8b4f50a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E6=95=B0?= <33194175+hanshuaikang@users.noreply.github.com> Date: Tue, 31 Oct 2023 15:56:27 +0800 Subject: [PATCH 03/62] minor: release 3.31.1 (#7150) * minor: release 3.31.1 --- app.yml | 2 +- app_desc.yaml | 2 +- config/default.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app.yml b/app.yml index 9f37147d67..11f39d8dd6 100644 --- a/app.yml +++ b/app.yml @@ -6,7 +6,7 @@ is_use_celery: True author: 蓝鲸智云 introduction: 标准运维是通过一套成熟稳定的任务调度引擎,把在多系统间的工作整合到一个流程,助力运维实现跨系统调度自动化的SaaS应用。 introduction_en: SOPS is a SaaS application that utilizes a set of mature and stable task scheduling engines to help realize cross-system scheduling automation, and integrates the work among multiple systems into a single process. -version: 3.31.0 +version: 3.31.1 category: 运维工具 language_support: 中文 desktop: diff --git a/app_desc.yaml b/app_desc.yaml index c45cea3485..165778650e 100644 --- a/app_desc.yaml +++ b/app_desc.yaml @@ -1,5 +1,5 @@ spec_version: 2 -app_version: "3.31.0" +app_version: "3.31.1" app: region: default bk_app_code: bk_sops diff --git a/config/default.py b/config/default.py index e797d76236..ff4b0425fc 100644 --- a/config/default.py +++ b/config/default.py @@ -211,7 +211,7 @@ # mako模板中: # 如果静态资源修改了以后,上线前改这个版本号即可 -STATIC_VERSION = "3.31.0" +STATIC_VERSION = "3.31.1" DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S") STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] @@ -676,6 +676,7 @@ def monitor_report_config(): from bk_monitor_report import MonitorReporter # noqa from bk_monitor_report.contrib.celery import MonitorReportStep # noqa + from blueapps.core.celery import celery_app # noqa reporter = MonitorReporter( From f867d9064155b4bd264c7a6ca641dc58702d0161 Mon Sep 17 00:00:00 2001 From: waylon <1158341873@qq.com> Date: Wed, 8 Nov 2023 11:08:50 +0800 Subject: [PATCH 04/62] =?UTF-8?q?bugfix:=20=E4=BF=AE=E5=A4=8D=E6=9A=82?= =?UTF-8?q?=E5=81=9C=E8=8A=82=E7=82=B9=E5=81=B6=E7=8E=B0=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gcloud/taskflow3/models.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gcloud/taskflow3/models.py b/gcloud/taskflow3/models.py index f62c64a0f7..20fb804e8e 100644 --- a/gcloud/taskflow3/models.py +++ b/gcloud/taskflow3/models.py @@ -1082,13 +1082,12 @@ def nodes_action(self, action, node_id, username, **kwargs): return {"result": False, "message": message, "code": err_code.REQUEST_PARAM_INVALID.code} try: - with transaction.atomic(): - # 如果当前节点属于独立子任务,则先唤醒父任务 - if self.is_child_taskflow and action in ["skip", "retry"]: - self.change_parent_task_node_state_to_running() + # 如果当前节点属于独立子任务,则先唤醒父任务 + if self.is_child_taskflow and action in ["skip", "retry"]: + self.change_parent_task_node_state_to_running() - dispatcher = NodeCommandDispatcher(engine_ver=self.engine_ver, node_id=node_id, taskflow_id=self.id) - return dispatcher.dispatch(action, username, **kwargs) + dispatcher = NodeCommandDispatcher(engine_ver=self.engine_ver, node_id=node_id, taskflow_id=self.id) + return dispatcher.dispatch(action, username, **kwargs) except Exception as e: logger.exception(traceback.format_exc()) message = _(f"节点操作失败: 节点[ID: {node_id}], 任务[ID: {self.id}]操作失败: {e}, 请重试. 如持续失败可联系管理员处理 | nodes_action") From d0e20ccd9c7753323bf4be9d9539b2a3e7926ab8 Mon Sep 17 00:00:00 2001 From: waylon <1158341873@qq.com> Date: Wed, 8 Nov 2023 11:10:07 +0800 Subject: [PATCH 05/62] minor: release V3.31.2 --- app.yml | 2 +- app_desc.yaml | 2 +- config/default.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app.yml b/app.yml index 11f39d8dd6..f87a711639 100644 --- a/app.yml +++ b/app.yml @@ -6,7 +6,7 @@ is_use_celery: True author: 蓝鲸智云 introduction: 标准运维是通过一套成熟稳定的任务调度引擎,把在多系统间的工作整合到一个流程,助力运维实现跨系统调度自动化的SaaS应用。 introduction_en: SOPS is a SaaS application that utilizes a set of mature and stable task scheduling engines to help realize cross-system scheduling automation, and integrates the work among multiple systems into a single process. -version: 3.31.1 +version: 3.31.2 category: 运维工具 language_support: 中文 desktop: diff --git a/app_desc.yaml b/app_desc.yaml index 165778650e..06a8ee7c3f 100644 --- a/app_desc.yaml +++ b/app_desc.yaml @@ -1,5 +1,5 @@ spec_version: 2 -app_version: "3.31.1" +app_version: "3.31.2" app: region: default bk_app_code: bk_sops diff --git a/config/default.py b/config/default.py index ff4b0425fc..59dde69e3c 100644 --- a/config/default.py +++ b/config/default.py @@ -211,7 +211,7 @@ # mako模板中: # 如果静态资源修改了以后,上线前改这个版本号即可 -STATIC_VERSION = "3.31.1" +STATIC_VERSION = "3.31.2" DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S") STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] @@ -676,7 +676,6 @@ def monitor_report_config(): from bk_monitor_report import MonitorReporter # noqa from bk_monitor_report.contrib.celery import MonitorReportStep # noqa - from blueapps.core.celery import celery_app # noqa reporter = MonitorReporter( From c333784db7fa097f22f2190fd4562b9db5ae37b9 Mon Sep 17 00:00:00 2001 From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:39:45 +0800 Subject: [PATCH 06/62] =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91div=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E7=B2=98=E8=B4=B4=E9=80=BB=E8=BE=91&&=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E4=BB=BB=E5=8A=A1=E6=B2=A1=E6=9C=89=E3=80=90=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=8E=86=E5=8F=B2=E3=80=91=E6=97=B6=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E8=B0=83=E6=95=B4=20(#7156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * optimization: 可编辑div禁止粘贴图片&&计划任务没有【执行历史】时展示调整 * minor: input粘贴禁止换行&&添加【执行历史】说明 --- .../common/RenderForm/tags/TagInput.vue | 23 +++++++++++++++++++ .../common/RenderForm/tags/TagTextarea.vue | 21 +++++++++++++++++ .../TemplateCanvas/PalettePanel/index.vue | 6 ++--- frontend/desktop/src/config/i18n/cn.js | 3 ++- frontend/desktop/src/config/i18n/en.js | 3 ++- .../src/pages/task/ClockedList/index.vue | 4 +++- 6 files changed, 53 insertions(+), 7 deletions(-) diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue index bc2ef4a6fc..633be6ec53 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue @@ -208,6 +208,7 @@ this.handleInputBlur() } } + divInputDom.addEventListener('paste', this.handlePaste) }, beforeDestroy () { window.removeEventListener('click', this.handleListShow, false) @@ -476,6 +477,28 @@ handleBlur () { this.emit_event(this.tagCode, 'blur', this.value) this.$emit('blur', this.value) + }, + handlePaste (e) { + event.preventDefault() + let text = '' + const clp = (e.originalEvent || e).clipboardData + if (clp === undefined || clp === null) { + text = window.clipboardData.getData('text') || '' + text = text.split('\n').join('') + if (text !== '') { + if (window.getSelection) { + const newNode = document.createElement('span') + newNode.innerHTML = text + window.getSelection().getRangeAt(0).insertNode(newNode) + } else { + document.selection.createRange().pasteHTML(text) + } + } + } else { + text = clp.getData('text/plain') || '' + text = text.split('\n').join('') + text && document.execCommand('insertText', false, text) + } } } } diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue index f3588658aa..f29bbb0683 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue @@ -167,6 +167,7 @@ divInputDom.innerHTML = divInputDom.innerHTML.replace(/
/g, '

') } } + divInputDom.addEventListener('paste', this.handlePaste) }, beforeDestroy () { window.removeEventListener('click', this.handleListShow, false) @@ -465,6 +466,26 @@ handleBlur () { this.emit_event(this.tagCode, 'blur', this.value) this.$emit('blur', this.value) + }, + handlePaste (e) { + event.preventDefault() + let text = '' + const clp = (e.originalEvent || e).clipboardData + if (clp === undefined || clp === null) { + text = window.clipboardData.getData('text') || '' + if (text !== '') { + if (window.getSelection) { + const newNode = document.createElement('span') + newNode.innerHTML = text + window.getSelection().getRangeAt(0).insertNode(newNode) + } else { + document.selection.createRange().pasteHTML(text) + } + } + } else { + text = clp.getData('text/plain') || '' + text && document.execCommand('insertText', false, text) + } } } } diff --git a/frontend/desktop/src/components/common/TemplateCanvas/PalettePanel/index.vue b/frontend/desktop/src/components/common/TemplateCanvas/PalettePanel/index.vue index 75cd4e75ed..1d65dc2f7b 100755 --- a/frontend/desktop/src/components/common/TemplateCanvas/PalettePanel/index.vue +++ b/frontend/desktop/src/components/common/TemplateCanvas/PalettePanel/index.vue @@ -34,14 +34,12 @@
+ data-type="tasknode">
+ data-type="subflow">
diff --git a/frontend/desktop/src/config/i18n/cn.js b/frontend/desktop/src/config/i18n/cn.js index b69269f734..4c470c114e 100644 --- a/frontend/desktop/src/config/i18n/cn.js +++ b/frontend/desktop/src/config/i18n/cn.js @@ -1791,7 +1791,8 @@ const cn = { '节点输出型变量仅支持从节点"取消接收输出"来删除': '节点输出型变量仅支持从节点"取消接收输出"来删除', '刷新': '刷新', 'exFailedText': '节点执行失败,请前往{0}查看错误原因', - 'exFailedText_调用日志': '调用日志' + 'exFailedText_调用日志': '调用日志', + '任务还未执行,暂无执行历史': '任务还未执行,暂无执行历史' } export default cn diff --git a/frontend/desktop/src/config/i18n/en.js b/frontend/desktop/src/config/i18n/en.js index ad57c28e8d..74d5d64406 100644 --- a/frontend/desktop/src/config/i18n/en.js +++ b/frontend/desktop/src/config/i18n/en.js @@ -1825,7 +1825,8 @@ const en = { '节点输出型变量仅支持从节点"取消接收输出"来删除': 'Node output variables can only be deleted by the node "Cancel Receiving Output"', '刷新': 'Refresh', 'exFailedText': 'Node execution failed. Please go to the {0} to check the error reason.', - 'exFailedText_调用日志': 'call log' + 'exFailedText_调用日志': 'call log', + '任务还未执行,暂无执行历史': 'Task not executed, no history available.' } export default en diff --git a/frontend/desktop/src/pages/task/ClockedList/index.vue b/frontend/desktop/src/pages/task/ClockedList/index.vue index 83918b9618..61d3af4cdb 100644 --- a/frontend/desktop/src/pages/task/ClockedList/index.vue +++ b/frontend/desktop/src/pages/task/ClockedList/index.vue @@ -128,7 +128,7 @@ {{ $t('执行历史') }} - {{ '--' }} + {{ $t('执行历史') }}
@@ -745,6 +745,8 @@ } .empty-text { padding: 5px; + color: #ccc; + cursor: not-allowed; } } From c0e5e64c8b3d9874c9629074efbf3a1de6820cc8 Mon Sep 17 00:00:00 2001 From: v_xugzhou <941071842@qq.com> Date: Wed, 8 Nov 2023 17:20:42 +0800 Subject: [PATCH 07/62] =?UTF-8?q?bugfix:=20textareaTag=E6=8D=A2=E8=A1=8C?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91=E9=94=99=E8=AF=AF=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/RenderForm/tags/TagTextarea.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue index f29bbb0683..d22a24ce1a 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue @@ -345,7 +345,7 @@ const childNodes = Array.from(divInputDom.childNodes).filter(item => item.nodeName !== 'TEXT') const inputValue = childNodes.map(dom => { // 获取行内纯文本 - let domValue = dom.textContent || '\n' + let domValue = dom.textContent if (dom.childNodes.length) { domValue = Array.from(dom.childNodes).map(item => { return item.type === 'button' @@ -397,8 +397,8 @@ } return match }) - // div会将\n解析成
标签,需要手动把内容标签下的
标签清理掉 - if (dom.nodeName !== 'BR' && dom.nextElementSibling?.nodeName === 'BR') { + // 初始化时文本标签为text,如果下一行标签为br时需要删除掉br,后面会将文本text标签转为div标签 + if (dom.nodeName === '#text' && dom.nextElementSibling?.nodeName === 'BR') { divInputDom.removeChild(dom.nextElementSibling) } if (dom.nodeName === '#text') { From f09da6a01c8c784a9d11e26b20317e64d21aa006 Mon Sep 17 00:00:00 2001 From: waylon <1158341873@qq.com> Date: Wed, 8 Nov 2023 17:33:37 +0800 Subject: [PATCH 08/62] minor: release_V3.31.3 --- app.yml | 2 +- app_desc.yaml | 2 +- config/default.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app.yml b/app.yml index f87a711639..3a9378363b 100644 --- a/app.yml +++ b/app.yml @@ -6,7 +6,7 @@ is_use_celery: True author: 蓝鲸智云 introduction: 标准运维是通过一套成熟稳定的任务调度引擎,把在多系统间的工作整合到一个流程,助力运维实现跨系统调度自动化的SaaS应用。 introduction_en: SOPS is a SaaS application that utilizes a set of mature and stable task scheduling engines to help realize cross-system scheduling automation, and integrates the work among multiple systems into a single process. -version: 3.31.2 +version: 3.31.3 category: 运维工具 language_support: 中文 desktop: diff --git a/app_desc.yaml b/app_desc.yaml index 06a8ee7c3f..5647cb5517 100644 --- a/app_desc.yaml +++ b/app_desc.yaml @@ -1,5 +1,5 @@ spec_version: 2 -app_version: "3.31.2" +app_version: "3.31.3" app: region: default bk_app_code: bk_sops diff --git a/config/default.py b/config/default.py index 59dde69e3c..2ace81fc3f 100644 --- a/config/default.py +++ b/config/default.py @@ -211,7 +211,7 @@ # mako模板中: # 如果静态资源修改了以后,上线前改这个版本号即可 -STATIC_VERSION = "3.31.2" +STATIC_VERSION = "3.31.3" DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S") STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] From 5ded8d8563a296480640542664b47f2fdf1542b0 Mon Sep 17 00:00:00 2001 From: v_xugzhou <941071842@qq.com> Date: Thu, 9 Nov 2023 20:34:26 +0800 Subject: [PATCH 09/62] =?UTF-8?q?bugfix:=20=E5=8F=AF=E7=BC=96=E8=BE=91div?= =?UTF-8?q?=E7=B2=98=E8=B4=B4/=E6=8D=A2=E8=A1=8C/ =E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/RenderForm/tags/TagInput.vue | 27 +++++++--- .../common/RenderForm/tags/TagTextarea.vue | 52 ++++++++++++++----- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue index 633be6ec53..1559c59214 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue @@ -170,7 +170,7 @@ this.$nextTick(() => { const divInputDom = this.$el.querySelector('.div-input') if (divInputDom) { - divInputDom.innerHTML = this.value + divInputDom.innerText = this.value this.handleInputBlur() } }) @@ -180,7 +180,7 @@ // 如果表单项开启了变量免渲染,不以tag展示 if (!val) { const divInputDom = this.$el.querySelector('.div-input') - divInputDom.innerHTML = this.value + divInputDom.innerText = this.value } else { this.handleInputBlur() } @@ -189,7 +189,7 @@ if (val) { this.$nextTick(() => { const divInputDom = this.$el.querySelector('.div-input') - divInputDom.innerHTML = this.value + divInputDom.innerText = this.value this.handleInputBlur() }) } else { @@ -203,15 +203,19 @@ mounted () { const divInputDom = this.$el.querySelector('.div-input') if (divInputDom) { - divInputDom.innerHTML = this.value + divInputDom.innerText = this.value if (this.render && this.value) { this.handleInputBlur() } + divInputDom.addEventListener('paste', this.handlePaste) } - divInputDom.addEventListener('paste', this.handlePaste) }, beforeDestroy () { window.removeEventListener('click', this.handleListShow, false) + const divInputDom = this.$el.querySelector('.div-input') + if (divInputDom) { + divInputDom.removeEventListener('paste', this.handlePaste) + } }, methods: { handleListShow (e) { @@ -390,8 +394,10 @@ ? item.value : item.textContent.trim() === '' ? ' ' - : item.textContent.replace(/ /g, ' ') + : item.textContent.replace(/\u00A0/g, ' ') }).join('') + } else { + inputValue = divInputDom.textContent.replace(/\u00A0/g, ' ') } this.input.value = inputValue this.updateForm(inputValue) @@ -412,6 +418,11 @@ return item.type === 'button' ? item.value : item.textContent }).join('') } + // 用户手动输入 渲染时需要切开展示 + domValue = domValue.replace(/ /g, '&nbsp;') + + // 初始化时是通过innerText进行复制的,如果有多个连续空格则只会显示一个,所以需手动将转为  + domValue = domValue.replace(/( )/g, ' ') const innerHtml = domValue.replace(varRegexp, (match, $0) => { let isExistVar = false if ($0) { @@ -484,7 +495,7 @@ const clp = (e.originalEvent || e).clipboardData if (clp === undefined || clp === null) { text = window.clipboardData.getData('text') || '' - text = text.split('\n').join('') + text = text.replace(/(\n|\r|\r\n)/g, '') if (text !== '') { if (window.getSelection) { const newNode = document.createElement('span') @@ -496,7 +507,7 @@ } } else { text = clp.getData('text/plain') || '' - text = text.split('\n').join('') + text = text.replace(/(\n|\r|\r\n)/g, '') text && document.execCommand('insertText', false, text) } } diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue index d22a24ce1a..9a73d3f39d 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue @@ -163,14 +163,16 @@ divInputDom.innerText = value if (this.render && value) { this.handleInputBlur() - // 把用户手动换行变成div标签 - divInputDom.innerHTML = divInputDom.innerHTML.replace(/
/g, '

') } + divInputDom.addEventListener('paste', this.handlePaste) } - divInputDom.addEventListener('paste', this.handlePaste) }, beforeDestroy () { window.removeEventListener('click', this.handleListShow, false) + const divInputDom = this.$el.querySelector('.div-input') + if (divInputDom) { + divInputDom.removeEventListener('paste', this.handlePaste) + } }, methods: { handleListShow (e) { @@ -350,10 +352,12 @@ domValue = Array.from(dom.childNodes).map(item => { return item.type === 'button' ? item.value - : item.textContent.trim() === '' - ? ' ' - : item.textContent.replace(/ /g, ' ') + : item.nodeName === 'BR' + ? '' + : item.textContent.replace(/\u00A0/g, ' ') }).join('') + } else { + domValue = dom.textContent.replace(/\u00A0/g, ' ') } return domValue }).join('\n') @@ -370,7 +374,13 @@ const varRegexp = /\${([^${}]+)}/g const divInputDom = this.$el.querySelector('.div-input') const childNodes = Array.from(divInputDom.childNodes).filter(item => item.nodeName !== 'TEXT') - childNodes.forEach(dom => { + const deleteMap = {} // 需要删除的br下标 + childNodes.forEach((dom, index) => { + // 删除多余的br标签 + if (deleteMap[index]) { + divInputDom.removeChild(dom) + return + } // 获取行内纯文本 let domValue = dom.textContent if (dom.childNodes.length) { @@ -378,6 +388,11 @@ return item.type === 'button' ? item.value : item.textContent }).join('') } + // 用户手动输入 渲染时需要切开展示 + domValue = domValue.replace(/ /g, '&nbsp;') + + // 初始化时是通过innerText进行复制的,如果有多个连续空格则只会显示一个,所以需手动将转为  + domValue = domValue.replace(/( )/g, ' ') // 支持匹配变量内运算 const innerHtml = domValue.replace(varRegexp, (match, $0) => { let isExistVar = false @@ -397,16 +412,27 @@ } return match }) - // 初始化时文本标签为text,如果下一行标签为br时需要删除掉br,后面会将文本text标签转为div标签 - if (dom.nodeName === '#text' && dom.nextElementSibling?.nodeName === 'BR') { - divInputDom.removeChild(dom.nextElementSibling) - } + // 初始化时\n会转化为【独占一行】的
标签,导致渲染异常。当我们手动把text标签转为div标签时需要删除【紧挨】着的
标签 if (dom.nodeName === '#text') { + // 记录需要被删除的br标签下标 + if (dom.nextSibling?.nodeName === 'BR') { + deleteMap[index + 1] = true + } const newDom = document.createElement('div') newDom.innerHTML = innerHtml divInputDom.replaceChild(newDom, dom) - } else if (dom.nodeName === 'DIV') { + } else if (dom.nodeName === 'DIV' && innerHtml) { dom.innerHTML = innerHtml + } else if (dom.nodeName === 'BR') { + // br标签实际上是初始化时\n转化的,\n表示当前行换行了,那么br标签必定会有下一行!!! + if (!dom.nextSibling) { + const appendDom = document.createElement('div') + appendDom.innerHTML = '
' + divInputDom.appendChild(appendDom) + } + const newDom = document.createElement('div') + newDom.innerHTML = '
' + divInputDom.replaceChild(newDom, dom) } }) this.updateInputValue() @@ -473,6 +499,7 @@ const clp = (e.originalEvent || e).clipboardData if (clp === undefined || clp === null) { text = window.clipboardData.getData('text') || '' + text = text.replace(/(\r|\r\n)/g, '') if (text !== '') { if (window.getSelection) { const newNode = document.createElement('span') @@ -484,6 +511,7 @@ } } else { text = clp.getData('text/plain') || '' + text = text.replace(/(\r|\r\n)/g, '') text && document.execCommand('insertText', false, text) } } From 00bfb9f6d1209f83c6b592776ca1b7ff10299a44 Mon Sep 17 00:00:00 2001 From: waylon <1158341873@qq.com> Date: Fri, 10 Nov 2023 08:47:31 +0800 Subject: [PATCH 10/62] minor: release_V3.31.4 --- app.yml | 2 +- app_desc.yaml | 2 +- config/default.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app.yml b/app.yml index 3a9378363b..8cc76856f6 100644 --- a/app.yml +++ b/app.yml @@ -6,7 +6,7 @@ is_use_celery: True author: 蓝鲸智云 introduction: 标准运维是通过一套成熟稳定的任务调度引擎,把在多系统间的工作整合到一个流程,助力运维实现跨系统调度自动化的SaaS应用。 introduction_en: SOPS is a SaaS application that utilizes a set of mature and stable task scheduling engines to help realize cross-system scheduling automation, and integrates the work among multiple systems into a single process. -version: 3.31.3 +version: 3.31.4 category: 运维工具 language_support: 中文 desktop: diff --git a/app_desc.yaml b/app_desc.yaml index 5647cb5517..46e91713af 100644 --- a/app_desc.yaml +++ b/app_desc.yaml @@ -1,5 +1,5 @@ spec_version: 2 -app_version: "3.31.3" +app_version: "3.31.4" app: region: default bk_app_code: bk_sops diff --git a/config/default.py b/config/default.py index 2ace81fc3f..d18b514725 100644 --- a/config/default.py +++ b/config/default.py @@ -211,7 +211,7 @@ # mako模板中: # 如果静态资源修改了以后,上线前改这个版本号即可 -STATIC_VERSION = "3.31.3" +STATIC_VERSION = "3.31.4" DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S") STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")] From c37e3aaadc92b374a07940b0af51a47965a5e49d Mon Sep 17 00:00:00 2001 From: v_xugzhou <941071842@qq.com> Date: Fri, 10 Nov 2023 14:12:50 +0800 Subject: [PATCH 11/62] =?UTF-8?q?bugfix:=20=E5=AF=B9=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E7=9A=84=E5=86=85=E5=AE=B9=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?unicode=E7=BC=96=E7=A0=81=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/RenderForm/tags/TagInput.vue | 5 ++--- .../src/components/common/RenderForm/tags/TagTextarea.vue | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue index 1559c59214..0ffe9cf059 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue @@ -394,11 +394,10 @@ ? item.value : item.textContent.trim() === '' ? ' ' - : item.textContent.replace(/\u00A0/g, ' ') + : item.textContent }).join('') - } else { - inputValue = divInputDom.textContent.replace(/\u00A0/g, ' ') } + inputValue = inputValue.replace(/\u00A0/g, ' ') this.input.value = inputValue this.updateForm(inputValue) }, diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue index 9a73d3f39d..ab7b8284b5 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue @@ -354,12 +354,10 @@ ? item.value : item.nodeName === 'BR' ? '' - : item.textContent.replace(/\u00A0/g, ' ') + : item.textContent }).join('') - } else { - domValue = dom.textContent.replace(/\u00A0/g, ' ') } - return domValue + return domValue.replace(/\u00A0/g, ' ') }).join('\n') this.input.value = inputValue this.updateForm(inputValue) From a51323d4ae002c994b96b8e6721a6448df2e2de4 Mon Sep 17 00:00:00 2001 From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:08:53 +0800 Subject: [PATCH 12/62] =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91div=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC=E7=BC=96=E7=A0=81=E7=89=B9=E6=AE=8A=E5=A4=84=E7=90=86?= =?UTF-8?q?&&=E5=8F=98=E9=87=8F=E7=BC=96=E8=BE=91=E9=A1=B5=E8=81=94?= =?UTF-8?q?=E6=83=B3=E4=B8=8D=E5=88=B0=E8=87=AA=E5=AE=9A=E4=B9=89=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D=20(#7164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * bugfix: 可编辑div空格编码特殊处理&&变量编辑页联想不到自定义节点问题修复 * minor: 正则优化 --- .../src/components/common/RenderForm/tags/TagInput.vue | 6 ++++-- .../src/components/common/RenderForm/tags/TagTextarea.vue | 6 ++++-- .../TemplateSetting/TabGlobalVariables/VariableEdit.vue | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue index 0ffe9cf059..0bcf4d691f 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue @@ -417,8 +417,10 @@ return item.type === 'button' ? item.value : item.textContent }).join('') } - // 用户手动输入 渲染时需要切开展示 - domValue = domValue.replace(/ /g, '&nbsp;') + // 用户手动输入的空格编码渲染时需要切开展示 + domValue = domValue.replace(/&(nbsp|ensp|emsp|thinsp|zwnj|zwj);/g, ($0, $1) => { + return `&${$1};` + }) // 初始化时是通过innerText进行复制的,如果有多个连续空格则只会显示一个,所以需手动将转为  domValue = domValue.replace(/( )/g, ' ') diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue index ab7b8284b5..7df465f3c9 100644 --- a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue +++ b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue @@ -386,8 +386,10 @@ return item.type === 'button' ? item.value : item.textContent }).join('') } - // 用户手动输入 渲染时需要切开展示 - domValue = domValue.replace(/ /g, '&nbsp;') + // 用户手动输入的空格编码渲染时需要切开展示 + domValue = domValue.replace(/&(nbsp|ensp|emsp|thinsp|zwnj|zwj);/g, ($0, $1) => { + return `&${$1};` + }) // 初始化时是通过innerText进行复制的,如果有多个连续空格则只会显示一个,所以需手动将转为  domValue = domValue.replace(/( )/g, ' ') diff --git a/frontend/desktop/src/pages/template/TemplateEdit/TemplateSetting/TabGlobalVariables/VariableEdit.vue b/frontend/desktop/src/pages/template/TemplateEdit/TemplateSetting/TabGlobalVariables/VariableEdit.vue index 4d16eb1482..4e3d8d413a 100644 --- a/frontend/desktop/src/pages/template/TemplateEdit/TemplateSetting/TabGlobalVariables/VariableEdit.vue +++ b/frontend/desktop/src/pages/template/TemplateEdit/TemplateSetting/TabGlobalVariables/VariableEdit.vue @@ -202,6 +202,7 @@