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/37] =?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/37] =?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/37] 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/37] =?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/37] 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/37] =?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 @@
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/37] =?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/37] 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/37] =?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, ' ')
+
+ // 初始化时是通过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, ' ')
+
+ // 初始化时是通过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/37] 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/37] =?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/37] =?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, ' ')
+ // 用户手动输入的空格编码渲染时需要切开展示
+ 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, ' ')
+ // 用户手动输入的空格编码渲染时需要切开展示
+ 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 @@
From 05617ff53ff3fc1db1e410707637180e95ccdf9e Mon Sep 17 00:00:00 2001
From: waylon <1158341873@qq.com>
Date: Fri, 10 Nov 2023 18:14:23 +0800
Subject: [PATCH 13/37] minor: release_V3.31.5
---
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 8cc76856f6..361c116175 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.4
+version: 3.31.5
category: 运维工具
language_support: 中文
desktop:
diff --git a/app_desc.yaml b/app_desc.yaml
index 46e91713af..c7baab7d7e 100644
--- a/app_desc.yaml
+++ b/app_desc.yaml
@@ -1,5 +1,5 @@
spec_version: 2
-app_version: "3.31.4"
+app_version: "3.31.5"
app:
region: default
bk_app_code: bk_sops
diff --git a/config/default.py b/config/default.py
index d18b514725..d2079d85c7 100644
--- a/config/default.py
+++ b/config/default.py
@@ -211,7 +211,7 @@
# mako模板中:
# 如果静态资源修改了以后,上线前改这个版本号即可
-STATIC_VERSION = "3.31.4"
+STATIC_VERSION = "3.31.5"
DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
From 4755f12a56d8878e7e8e3e14a1bdc31ff17ccadb Mon Sep 17 00:00:00 2001
From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com>
Date: Mon, 13 Nov 2023 19:31:18 +0800
Subject: [PATCH 14/37] =?UTF-8?q?bugfix:=20=E9=81=BF=E5=85=8Dxss=20(#7166)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* bugfix: 避免xss
* optimization: 实体名称切开展示
* optimization: 脚本种没有全局变量时提示自动关闭
---
.../common/RenderForm/tags/TagCodeEditor.vue | 1 +
.../common/RenderForm/tags/TagInput.vue | 16 ++++++++++------
.../common/RenderForm/tags/TagTextarea.vue | 16 ++++++++++++----
3 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagCodeEditor.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagCodeEditor.vue
index e438f40b05..b9804d79e5 100644
--- a/frontend/desktop/src/components/common/RenderForm/tags/TagCodeEditor.vue
+++ b/frontend/desktop/src/components/common/RenderForm/tags/TagCodeEditor.vue
@@ -223,6 +223,7 @@
})
})
this.decorationsMap = {}
+ this.globalVarLength = 0
}
},
onLanguageChange () {
diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
index 0bcf4d691f..acd8f81606 100644
--- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
+++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
@@ -417,13 +417,13 @@
return item.type === 'button' ? item.value : item.textContent
}).join('')
}
- // 用户手动输入的空格编码渲染时需要切开展示
- domValue = domValue.replace(/&(nbsp|ensp|emsp|thinsp|zwnj|zwj);/g, ($0, $1) => {
+ // 将html标签拆成文本形式
+ domValue = domValue.replace(/(<|>)/g, ($0, $1) => `${$1}`)
+ // 用户手动输入的实体字符渲染时需要切开展示
+ domValue = domValue.replace(/&(nbsp|ensp|emsp|thinsp|zwnj|zwj|quot|apos|lt|gt|amp|cent|pound|yen|euro|sect|copy|reg|trade|times|divide);/g, ($0, $1) => {
return `&${$1};`
})
- // 初始化时是通过innerText进行复制的,如果有多个连续空格则只会显示一个,所以需手动将转为
- domValue = domValue.replace(/( )/g, ' ')
const innerHtml = domValue.replace(varRegexp, (match, $0) => {
let isExistVar = false
if ($0) {
@@ -437,7 +437,11 @@
}
if (isExistVar) {
const randomId = Math.random().toString().slice(-6)
- return `` // 两边留空格保持间距
+ // 将装转的尖括号恢复原样
+ let value = match.replace(/(<|>)<\/span>/g, ($0, $1) => $1)
+ // 将双引号转为实体字符
+ value = value.replace(/"/g, '"')
+ return ``
}
return match
})
@@ -593,7 +597,7 @@
line-height: 18px;
padding: 7px 0;
color: #63656e;
- white-space: nowrap;
+ white-space: pre;
overflow: hidden;
/deep/.var-tag {
margin-right: 1px;
diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue
index 7df465f3c9..cc8e5f8f45 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(/&(nbsp|ensp|emsp|thinsp|zwnj|zwj);/g, ($0, $1) => {
+ // 将html标签拆成文本形式
+ domValue = domValue.replace(/(<|>)/g, ($0, $1) => `${$1}`)
+ // 用户手动输入的实体字符渲染时需要切开展示
+ domValue = domValue.replace(/&(nbsp|ensp|emsp|thinsp|zwnj|zwj|quot|apos|lt|gt|amp|cent|pound|yen|euro|sect|copy|reg|trade|times|divide);/g, ($0, $1) => {
return `&${$1};`
})
@@ -406,9 +408,12 @@
})
}
if (isExistVar) {
- // 两边留空格保持间距
const randomId = Math.random().toString().slice(-6)
- return ``
+ // 将装转的尖括号恢复原样
+ let value = match.replace(/(<|>)<\/span>/g, ($0, $1) => $1)
+ // 将双引号转为实体字符
+ value = value.replace(/"/g, '"')
+ return ``
}
return match
})
@@ -602,6 +607,9 @@
background: #eaebf0;
}
}
+ /deep/div {
+ word-break: break-all;
+ }
&.input-before::before {
content: attr(data-placeholder);
color: #c4c6cc;
From 0700401a1a405b7eb44abe320a0ed9d37defd4d1 Mon Sep 17 00:00:00 2001
From: v_xugzhou <941071842@qq.com>
Date: Tue, 14 Nov 2023 14:17:49 +0800
Subject: [PATCH 15/37] =?UTF-8?q?optimization:=20inputTag=E7=B2=98?=
=?UTF-8?q?=E8=B4=B4=E6=97=B6=E6=8D=A2=E8=A1=8C=E7=AC=A6=E6=9B=BF=E6=8D=A2?=
=?UTF-8?q?=E4=B8=BA=E7=A9=BA=E6=A0=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/components/common/RenderForm/tags/TagInput.vue | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
index acd8f81606..99204d62fc 100644
--- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
+++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
@@ -500,7 +500,7 @@
const clp = (e.originalEvent || e).clipboardData
if (clp === undefined || clp === null) {
text = window.clipboardData.getData('text') || ''
- text = text.replace(/(\n|\r|\r\n)/g, '')
+ text = text.replace(/(\n|\r|\r\n)/g, ' ')
if (text !== '') {
if (window.getSelection) {
const newNode = document.createElement('span')
@@ -512,7 +512,7 @@
}
} else {
text = clp.getData('text/plain') || ''
- text = text.replace(/(\n|\r|\r\n)/g, '')
+ text = text.replace(/(\n|\r|\r\n)/g, ' ')
text && document.execCommand('insertText', false, text)
}
}
From f56a4eca904d0f969d2d442c09e755b03004ac4a Mon Sep 17 00:00:00 2001
From: hanshuaikang <1758504262@qq.com>
Date: Wed, 1 Nov 2023 20:45:46 +0800
Subject: [PATCH 16/37] =?UTF-8?q?minor:=20get=5Ftask=5Flist=20=E6=96=B0?=
=?UTF-8?q?=E5=A2=9Ecreate=5Fmethod=E8=BF=87=E6=BB=A4=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gcloud/apigw/forms/get_task_list.py | 1 +
gcloud/apigw/views/get_task_list.py | 1 +
2 files changed, 2 insertions(+)
diff --git a/gcloud/apigw/forms/get_task_list.py b/gcloud/apigw/forms/get_task_list.py
index 65175e3a13..f7e54ae959 100644
--- a/gcloud/apigw/forms/get_task_list.py
+++ b/gcloud/apigw/forms/get_task_list.py
@@ -19,3 +19,4 @@ class GetTaskListForm(forms.Form):
is_finished = forms.BooleanField(required=False)
keyword = forms.CharField(required=False)
executor = forms.CharField(required=False)
+ create_method = forms.CharField(required=False)
diff --git a/gcloud/apigw/views/get_task_list.py b/gcloud/apigw/views/get_task_list.py
index 5115d842e1..f6df51ebd4 100644
--- a/gcloud/apigw/views/get_task_list.py
+++ b/gcloud/apigw/views/get_task_list.py
@@ -44,6 +44,7 @@ def get_task_list(request, project_id):
"is_started": "pipeline_instance__is_started",
"is_finished": "pipeline_instance__is_finished",
"executor": "pipeline_instance__executor",
+ "create_method": "create_method",
}
filter_kwargs = dict(is_deleted=Value(0), project_id=project.id)
for param, filter_key in param_mappings.items():
From ab867d3897943f667c0ec1131f27663f34de9961 Mon Sep 17 00:00:00 2001
From: v_xugzhou <941071842@qq.com>
Date: Wed, 15 Nov 2023 11:00:16 +0800
Subject: [PATCH 17/37] =?UTF-8?q?bugfix:=20=E5=88=86=E6=94=AF=E8=8A=82?=
=?UTF-8?q?=E7=82=B9=E5=AF=B9=E5=BA=94=E7=9A=84=E6=B1=87=E8=81=9A=E8=8A=82?=
=?UTF-8?q?=E7=82=B9=E7=AE=97=E6=B3=95=E9=80=92=E5=BD=92=E9=97=AE=E9=A2=98?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/pages/task/TaskExecute/NodeTree.vue | 2 +-
.../pages/task/TaskExecute/TaskOperation.vue | 71 ++++++++++++++-----
2 files changed, 53 insertions(+), 20 deletions(-)
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
index ae87860a84..2f373b45d2 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
@@ -84,7 +84,7 @@
@import '@/scss/mixins/scrollbar.scss';
.node-tree-wrapper {
width: 100%;
- padding: 16px 16px 0 8px;
+ padding: 16px 16px 16px 8px;
height: 100%;
overflow-x: auto;
@include scrollbar;
diff --git a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
index 5e89cb23fc..de01f93756 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
@@ -1787,6 +1787,18 @@
}, {})
Object.assign(this.nodeTargetMaps, targetMap)
},
+ /**
+ * id: 当前查找的id
+ * parentId: 最外层的网关id
+ * convergeInfo: { 汇聚详情
+ * id: '', 网关节点
+ * checkedNodes: [], 已经查找过的节点
+ * convergeNode: '', 最终汇聚的节点
+ * branchCount: 1 总共有多少条分支
+ * }
+ * index: 当前节点属于哪条分支下的
+ * isDeep: 是否递归
+ */
getGatewayConvergeNodes (id, parentId, convergeInfo = {}, index, isDeep) {
if (!id) return
if (!convergeInfo[parentId]) {
@@ -1794,54 +1806,80 @@
id: parentId,
checkedNodes: [],
convergeNode: '',
- branchCount: 1
+ branchCount: 1 // 默认是一条分支
}
}
const targetNodes = this.nodeTargetMaps[id] || []
+ // 多条输出分支
if (targetNodes.length > 1) {
+ // 非递归时,将节点分支添加到总分支,删除旧分支。
if (!isDeep) {
convergeInfo[parentId].branchCount += targetNodes.length - 1
}
targetNodes.forEach((targetId, branchIndex) => {
let newIndex = branchIndex
+ // 当前节点属于存在分支下时,向下查找时采用新的分支数
if (index !== 0) {
const branches = Object.keys(convergeInfo[parentId]).filter(item => /^branch[0-9]*$/.test(item))
newIndex = branches.length
}
+ // 非递归时使用传入的分支数
newIndex = isDeep ? index : newIndex
this.getGatewayConvergeNodes(targetId, parentId, convergeInfo, newIndex, isDeep)
})
} else {
+ // 单条输出分支
const { checkedNodes, branchCount = 0 } = convergeInfo[parentId]
const countArr = [...Array(branchCount).keys()]
const { end_event } = this.pipelineData
+ // 已查找过的节点、结束节点、汇聚节点
if ([...checkedNodes, end_event.id].includes(id) || this.nodeSourceMaps[id].length > 1) {
+ // 记录分支下的汇聚节点
const branchConvergeNode = convergeInfo[parentId][`branch${index}`]
if (!branchConvergeNode) {
convergeInfo[parentId][`branch${index}`] = [id]
} else if (!branchConvergeNode.includes(id)) {
branchConvergeNode.push(id)
}
+ // 记录查找过的节点
if (!checkedNodes.includes(id)) {
checkedNodes.push(id)
}
+ // 所有分支下的汇聚节点
const convergeNodes = countArr.map(item => {
const data = convergeInfo[parentId][`branch${item}`] || []
return [...new Set(data)]
}).flat()
- if (this.findMost(convergeNodes) === branchCount) {
- convergeInfo[parentId].convergeNode = id
- } else if (index === branchCount - 1) {
- countArr.forEach(item => {
- if (!convergeInfo[parentId].convergeNode) {
- const data = convergeInfo[parentId][`branch${item}`] || []
+ // 如果重复出现汇聚节点的最大次数等于分支数则表示已经找到最终的汇聚节点了
+ const countMap = this.getCountMap(convergeNodes)
+ const matchNode = Object.keys(countMap).filter(key => countMap[key] === branchCount)
+ if (matchNode[0]) {
+ convergeInfo[parentId].convergeNode = matchNode[0]
+ } else if (index === branchCount - 1) { // 最后一条分支
+ // 没找到汇聚节点则继续向下递归
+ if (!convergeInfo[parentId].convergeNode) {
+ const executedNodes = []
+ countArr.forEach(idx => {
+ // 根据各条分支最后的汇聚节点继续查找
+ const data = convergeInfo[parentId][`branch${idx}`] || []
const [lastId] = data.slice(-1)
+ // 合并有相同汇聚节点的分支
+ const existed = executedNodes.find(item => item.id === lastId)
+ if (existed && lastId !== end_event.id) {
+ const samePreBranch = convergeInfo[parentId][`branch${existed.index}`]
+ convergeInfo[parentId][`branch${idx}`] = [...samePreBranch]
+ const [preLastId] = samePreBranch.slice(-1)
+ this.getGatewayConvergeNodes(preLastId, parentId, convergeInfo, idx, true)
+ return
+ }
+ executedNodes.push({ id: lastId, index: idx })
const targetIds = this.nodeTargetMaps[lastId] || [lastId]
+ // 向下递归
targetIds.forEach(targetId => {
- this.getGatewayConvergeNodes(targetId, parentId, convergeInfo, item, true)
+ this.getGatewayConvergeNodes(targetId, parentId, convergeInfo, idx, true)
})
- }
- })
+ })
+ }
}
} else {
checkedNodes.push(id)
@@ -1850,18 +1888,13 @@
}
}
},
- findMost (arr) {
- if (!arr.length) return
- if (arr.length === 1) return 1
- let maxNum = 0
- arr.reduce((acc, cur) => {
+ getCountMap (arr) {
+ if (!arr.length) return {}
+ const countMap = arr.reduce((acc, cur) => {
acc[cur] ? acc[cur] += 1 : acc[cur] = 1
- if (acc[cur] > maxNum) {
- maxNum = acc[cur]
- }
return acc
}, {})
- return maxNum
+ return countMap
},
judgeNodeBack (id, backId, checked) {
if (checked.includes(id)) return id === backId
From 35726c098d82f881cdde68b613ef196c9bb013ee Mon Sep 17 00:00:00 2001
From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com>
Date: Thu, 16 Nov 2023 20:28:23 +0800
Subject: [PATCH 18/37] =?UTF-8?q?divInput=E8=BE=93=E5=85=A5=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=E4=BC=98=E5=8C=96&&=E5=85=A8=E5=B1=80=E5=8F=98?=
=?UTF-8?q?=E9=87=8F=E9=85=8D=E7=BD=AE=E6=9C=AA=E6=9B=B4=E6=96=B0=E9=97=AE?=
=?UTF-8?q?=E9=A2=98=E4=BF=AE=E5=A4=8D=20(#7171)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* optimization: 节点树空分支样式调整
* optimization: divInput输入逻辑优化
* bugfix: 批量更新子流程未更新被勾选到全局变量的表单配置问题修复
---
.../common/RenderForm/tags/TagInput.vue | 24 ++++++++++++++---
.../common/RenderForm/tags/TagTextarea.vue | 16 ++++++++----
frontend/desktop/src/config/i18n/cn.js | 3 ++-
frontend/desktop/src/config/i18n/en.js | 3 ++-
.../pages/task/TaskExecute/NodeTreeItem.vue | 26 ++++++++++++++++---
.../TemplateEdit/BatchUpdateDialog.vue | 21 ++++++++++++++-
6 files changed, 79 insertions(+), 14 deletions(-)
diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
index 99204d62fc..39ad5725bd 100644
--- a/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
+++ b/frontend/desktop/src/components/common/RenderForm/tags/TagInput.vue
@@ -315,6 +315,7 @@
// 文本框输入
handleInputChange (e, selection) {
if (!selection) {
+ // 实时更新
this.updateInputValue()
}
let matchResult = []
@@ -323,10 +324,22 @@
this.isListOpen = false
return
}
+ // 获取文本
this.lastEditRange = window.getSelection().getRangeAt(0)
const offsetText = focusNode.data.substring(0, anchorOffset)
+ let matchText = offsetText
+
+ // 如果不包含$则不进行后续计算
+ if (matchText.indexOf('$') === -1) {
+ this.isListOpen = false
+ return
+ }
+
+ // 过滤掉完整的变量格式文本
const varRegexp = /\s?\${[a-zA-Z_][\w|.]*}\s?/g
- let matchText = offsetText.split(varRegexp).pop()
+ if (varRegexp.test(matchText)) {
+ matchText = offsetText.split(varRegexp).pop()
+ }
// 拿到字段最后以$开头的部分
matchText = matchText.replace(/(.*)(\$[^\}]*)/, ($0, $1, $2) => $2)
// 判断是否为变量格式
@@ -399,12 +412,18 @@
}
inputValue = inputValue.replace(/\u00A0/g, ' ')
this.input.value = inputValue
- this.updateForm(inputValue)
},
// 文本框失焦
handleInputBlur (e) {
this.$emit('blur')
this.input.focus = false
+ // 更新文本框结构,生成tag标签
+ this.updateInputHtml()
+ // 向上更新表单
+ this.updateForm(this.input.value)
+ },
+ // 更新文本框结构,生成tag标签
+ updateInputHtml () {
// 如果表单项开启了变量免渲染,不以tag展示
if (!this.render) return
// 支持所有变量(系统变量,内置变量,自定义变量)
@@ -446,7 +465,6 @@
return match
})
divInputDom.innerHTML = innerHtml
- this.updateInputValue()
},
// 文本框按键事件
handleInputKeyDown (e) {
diff --git a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue
index cc8e5f8f45..0513a7e852 100644
--- a/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue
+++ b/frontend/desktop/src/components/common/RenderForm/tags/TagTextarea.vue
@@ -259,6 +259,7 @@
// 文本框输入
handleInputChange (e, updateForm = true) {
if (updateForm) {
+ // 实时更新
this.updateInputValue()
}
const range = window.getSelection().getRangeAt(0)
@@ -274,12 +275,12 @@
const lastNode = textNode.childNodes[startOffset - 1]
previousText = lastNode.textContent
}
- const matchText = previousText.replace(/(.*)(\$[^\}]*)/, ($0, $1, $2) => $2)
- // 如果是完整全局变量则不进行后续操作
- if (/^\$\{\w+\}$/.test(matchText)) {
+ // 如果不包含$则不进行后续计算、 如果是完整全局变量则不进行后续操作
+ if (previousText.indexOf('$') === -1 || /\${[a-zA-Z_][\w|.]*}/.test(previousText)) {
this.isListOpen = false
return
}
+ const matchText = previousText.replace(/(.*)(\$[^\}]*)/, ($0, $1, $2) => $2)
// 判断是否为变量格式
if (matchText === '$' || /^\${[a-zA-Z_]*[\w|.]*/.test(matchText)) {
this.varList = this.constantArr.filter(item => item.key.indexOf(matchText) > -1)
@@ -360,12 +361,18 @@
return domValue.replace(/\u00A0/g, ' ')
}).join('\n')
this.input.value = inputValue
- this.updateForm(inputValue)
},
// 文本框失焦
handleInputBlur (e) {
this.$emit('blur')
this.input.focus = false
+ // 更新文本框结构,生成tag标签
+ this.updateInputHtml()
+ // 向上更新表单
+ this.updateForm(this.input.value)
+ },
+ // 更新文本框结构,生成tag标签
+ updateInputHtml () {
// 如果表单项开启了变量免渲染,不以tag展示
if (!this.render) return
// 支持所有变量(系统变量,内置变量,自定义变量)
@@ -440,7 +447,6 @@
divInputDom.replaceChild(newDom, dom)
}
})
- this.updateInputValue()
},
// 文本框按键事件
handleInputKeyDown (e) {
diff --git a/frontend/desktop/src/config/i18n/cn.js b/frontend/desktop/src/config/i18n/cn.js
index 4c470c114e..d462ed049d 100644
--- a/frontend/desktop/src/config/i18n/cn.js
+++ b/frontend/desktop/src/config/i18n/cn.js
@@ -1792,7 +1792,8 @@ const cn = {
'刷新': '刷新',
'exFailedText': '节点执行失败,请前往{0}查看错误原因',
'exFailedText_调用日志': '调用日志',
- '任务还未执行,暂无执行历史': '任务还未执行,暂无执行历史'
+ '任务还未执行,暂无执行历史': '任务还未执行,暂无执行历史',
+ '空分支': '空分支'
}
export default cn
diff --git a/frontend/desktop/src/config/i18n/en.js b/frontend/desktop/src/config/i18n/en.js
index 74d5d64406..ecee08b4c1 100644
--- a/frontend/desktop/src/config/i18n/en.js
+++ b/frontend/desktop/src/config/i18n/en.js
@@ -1826,7 +1826,8 @@ const en = {
'刷新': 'Refresh',
'exFailedText': 'Node execution failed. Please go to the {0} to check the error reason.',
'exFailedText_调用日志': 'call log',
- '任务还未执行,暂无执行历史': 'Task not executed, no history available.'
+ '任务还未执行,暂无执行历史': 'Task not executed, no history available.',
+ '空分支': 'Empty branch'
}
export default en
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
index b6dd415732..810e6ee58c 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
@@ -38,9 +38,12 @@
-
+
@@ -62,7 +65,12 @@
- {{ node.title }}
+
+ {{ node.title }}
+
+ {{ $t('(') + $t('空分支') + $t(')') }}
+
+
{
+ this.subflowForms.forEach((subflow, index) => {
if (source_info[subflow.id]) { // 该节点最新版本输入输出参数有勾选
source_info[subflow.id].slice(0).forEach(nodeFormItem => {
// 注释 1.a 场景
@@ -623,6 +624,24 @@
})
}
}
+
+ const { form, inputsConfig } = subflow.latestForm
+ const formValue = form[key]
+ const inputRef = this.$refs.inputParams[index]
+ let hook = false
+ // 获取输入参数的勾选状态
+ if (inputRef && inputRef.hooked) {
+ hook = inputRef.hooked[key] || false
+ }
+ if (varItem.is_meta && formValue && hook) {
+ const schema = formSchema.getSchema(formValue.key, inputsConfig)
+ varItem['form_schema'] = schema
+ varItem.meta = formValue.meta
+ // 如果之前选中的下拉项被删除了,则删除对应的值
+ const curVal = varItem.value
+ const isMatch = curVal ? schema.attrs.items.find(item => item.value === curVal) : true
+ varItem.value = isMatch ? curVal : ''
+ }
})
if (Object.keys(source_info).length > 0) {
constants[key] = varItem
From 52b7e8d0ca3e176b9a24a91c85ae078c89dcd17c Mon Sep 17 00:00:00 2001
From: waylon <1158341873@qq.com>
Date: Fri, 17 Nov 2023 10:49:27 +0800
Subject: [PATCH 19/37] minor: release V3.31.6
---
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 361c116175..54a30493ac 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.5
+version: 3.31.6
category: 运维工具
language_support: 中文
desktop:
diff --git a/app_desc.yaml b/app_desc.yaml
index c7baab7d7e..04d56391f5 100644
--- a/app_desc.yaml
+++ b/app_desc.yaml
@@ -1,5 +1,5 @@
spec_version: 2
-app_version: "3.31.5"
+app_version: "3.31.6"
app:
region: default
bk_app_code: bk_sops
diff --git a/config/default.py b/config/default.py
index d2079d85c7..936352aea0 100644
--- a/config/default.py
+++ b/config/default.py
@@ -211,7 +211,7 @@
# mako模板中:
# 如果静态资源修改了以后,上线前改这个版本号即可
-STATIC_VERSION = "3.31.5"
+STATIC_VERSION = "3.31.6"
DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
From 96a59b147b8638738b2fdd0752969686ce06bef5 Mon Sep 17 00:00:00 2001
From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com>
Date: Tue, 21 Nov 2023 10:52:09 +0800
Subject: [PATCH 20/37] =?UTF-8?q?=E5=AD=90=E6=B5=81=E7=A8=8B=E4=BA=A4?=
=?UTF-8?q?=E4=BA=92=E4=BC=98=E5=8C=96=20(#7177)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* optimization: 子流程交互优化
* minor: 设置画布缩放最大最小值
---
.../common/TemplateCanvas/ToolPanel/index.vue | 6 ++
.../common/TemplateCanvas/index.vue | 20 +++--
.../pages/task/TaskExecute/ExecuteInfo.vue | 81 +++++++++----------
.../pages/task/TaskExecute/TaskOperation.vue | 16 +++-
4 files changed, 69 insertions(+), 54 deletions(-)
diff --git a/frontend/desktop/src/components/common/TemplateCanvas/ToolPanel/index.vue b/frontend/desktop/src/components/common/TemplateCanvas/ToolPanel/index.vue
index b4299a7edc..1efc7007c1 100644
--- a/frontend/desktop/src/components/common/TemplateCanvas/ToolPanel/index.vue
+++ b/frontend/desktop/src/components/common/TemplateCanvas/ToolPanel/index.vue
@@ -27,6 +27,7 @@
{{ zoomRatio + '%' }}
@@ -341,6 +344,8 @@
subprocessTasks: [],
subprocessNodeStatus: {},
subNodesExpanded: [], // 节点树展开的独立子流程节点
+ subProcessHeight: 160,
+ zoom: 0.75,
notPerformedSubNode: false // 是否为未执行的独立子流程节点
}
},
@@ -973,20 +978,14 @@
}
},
onZoomOut () {
- let jsFlowInstance = this.$refs.subProcessCanvas
- jsFlowInstance = jsFlowInstance.$refs.jsFlow
- jsFlowInstance.zoomOut(0.8)
- this.$nextTick(() => {
- this.setCanvasZoomPosition(true)
- })
+ const jsFlowInstance = this.$refs.subProcessCanvas
+ jsFlowInstance.onZoomOut()
+ this.zoom = jsFlowInstance.zoomRatio / 100
},
onZoomIn () {
- let jsFlowInstance = this.$refs.subProcessCanvas
- jsFlowInstance = jsFlowInstance.$refs.jsFlow
- jsFlowInstance.zoomIn(1.2)
- this.$nextTick(() => {
- this.setCanvasZoomPosition(true)
- })
+ const jsFlowInstance = this.$refs.subProcessCanvas
+ jsFlowInstance.onZoomIn()
+ this.zoom = jsFlowInstance.zoomRatio / 100
},
onTabChange (name) {
this.curActiveTab = name
@@ -1061,39 +1060,29 @@
return top > canvasTop && top < canvasTop + height && left > canvasLeft && left < canvasLeft + width
},
// 画布初始化时缩放比偏移
- setCanvasZoomPosition (zoom) {
+ setCanvasZoomPosition () {
if (!this.canvasData.locations) return
- // 获取画布上下左右最大坐标
- const xList = this.canvasData.locations.map(node => node.x)
- const yList = this.canvasData.locations.map(node => node.y)
- const minX = Math.min(...xList)
- const maxX = Math.max(...xList)
- const minY = Math.min(...yList)
- const maxY = Math.max(...yList)
- const maxXNodeId = this.canvasData.locations.find(node => node.x === maxX).id
- const maxYNodeId = this.canvasData.locations.find(node => node.y === maxY).id
- const { width } = this.$el.querySelector(`#${maxXNodeId}`).getBoundingClientRect()
- const { height } = this.$el.querySelector(`#${maxYNodeId}`).getBoundingClientRect()
- const netHeight = maxY - minY + height + 60
- const netWidth = maxX - minX + width + 80
+ // 设置默认高度
const subprocessDom = this.$el.querySelector('.sub-process')
- const { height: canvasHeight, width: canvasWidth } = subprocessDom.getBoundingClientRect()
- // 画布实例
+ const { top } = subprocessDom.getBoundingClientRect()
+ this.subProcessHeight = window.innerHeight - top - 320
+ // 设置缩放比例
let jsFlowInstance = this.$refs.subProcessCanvas
- jsFlowInstance = jsFlowInstance.$refs.jsFlow
- let ratio
- if (zoom) {
- ratio = jsFlowInstance.zoom
- } else {
- // 最大比例0.75
- ratio = Math.min(canvasHeight / netHeight, canvasWidth / netWidth)
- ratio = ratio > 0.75 ? 0.75 : ratio
- jsFlowInstance && jsFlowInstance.zoomOut(ratio, 0, 0)
- }
+ jsFlowInstance = jsFlowInstance && jsFlowInstance.$refs.jsFlow
+ jsFlowInstance && jsFlowInstance.setZoom(this.zoom, 0, 0)
// 设置偏移量
- const offsetX = canvasWidth / 2 - (minX - 30 + netWidth / 2) * ratio
- const offsetY = canvasHeight / 2 - (minY + netHeight / 2) * ratio
- jsFlowInstance.setCanvasPosition(offsetX, offsetY, true)
+ const startNode = this.canvasData.locations.find(item => item.type === 'startpoint')
+ // 判断dom是否存在当前视图中
+ const nodeEl = document.querySelector(`#${startNode.id} .canvas-node-item`)
+ if (!nodeEl) return
+ const isInViewPort = this.judgeInViewPort(nodeEl)
+ if (!isInViewPort) {
+ let jsFlowInstance = this.$refs.subProcessCanvas
+ jsFlowInstance = jsFlowInstance.$refs.jsFlow
+ const offsetX = (20 - startNode.x) * this.zoom
+ const offsetY = (160 - startNode.y) * this.zoom
+ jsFlowInstance && jsFlowInstance.setCanvasPosition(offsetX, offsetY, true)
+ }
},
toggleNodeActive (id, isActive) {
@@ -1473,8 +1462,7 @@
const resizeProxy = this.$refs.resizeProxy
resizeProxy.style.visibility = 'hidden'
resizeMask.style.display = 'none'
- const subProcessDom = document.querySelector('.sub-process')
- subProcessDom.style.height = resizeProxy.style.top
+ this.subProcessHeight = resizeProxy.style.top.slice(0, -2)
document.removeEventListener('mousemove', this.handleMouseMove)
document.removeEventListener('mouseup', this.handleMouseUp)
}
@@ -1618,7 +1606,6 @@
display: none;
}
.task-node {
- box-shadow: none !important;
&.actived {
.node-name {
border-color: #b4becd !important;
@@ -1654,6 +1641,10 @@
&:hover {
color: #3a84ff;
}
+ &.disabled {
+ color: #ccc;
+ cursor: not-allowed;
+ }
}
}
}
diff --git a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
index de01f93756..84024b69fe 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
@@ -68,7 +68,7 @@
:width="sidebarWidth"
:quick-close="true"
:before-close="onBeforeClose"
- @hidden="onHiddenSideslider">
+ @hidden="onHiddenSideSlider">
-
+
import i18n from '@/config/i18n/index.js'
import TemplateCanvas from '@/components/common/TemplateCanvas/index.vue'
- import { mapState, mapMutations, mapActions } from 'vuex'
+ import { mapState, mapActions } from 'vuex'
import axios from 'axios'
import tools from '@/utils/tools.js'
import atomFilter from '@/utils/atomFilter.js'
@@ -332,8 +332,6 @@
loop: 1,
theExecuteTime: undefined,
isReadyStatus: true,
- isShowSkipBtn: false,
- isShowRetryBtn: false,
curActiveTab: 'record',
theExecuteRecord: 0,
executeRecord: {},
@@ -350,9 +348,6 @@
}
},
computed: {
- ...mapMutations('template/', [
- 'setLine'
- ]),
...mapState({
'atomFormConfig': state => state.atomForm.config,
'atomOutputConfig': state => state.atomForm.outputConfig,
@@ -441,6 +436,22 @@
isShowContinueBtn () {
return this.isLegacySubProcess && this.executeInfo.state === 'SUSPENDED'
},
+ isShowSkipBtn () {
+ let isShow = false
+ if (this.realTimeState.state === 'FAILED') {
+ const activity = this.pipelineData.activities[this.nodeDetailConfig.node_id]
+ isShow = this.location.type === 'tasknode' && activity.skippable
+ }
+ return isShow
+ },
+ isShowRetryBtn () {
+ let isShow = false
+ if (this.realTimeState.state === 'FAILED') {
+ const activity = this.pipelineData.activities[this.nodeDetailConfig.node_id]
+ isShow = this.location.type === 'tasknode' ? activity.retryable : false
+ }
+ return isShow
+ },
isShowActionWrap () {
// 任务终止时禁止节点操作
if (this.state === 'REVOKED') return false
@@ -508,7 +519,10 @@
this.loading = false
this.subprocessLoading = false
this.randomKey = new Date().getTime()
+ const nodeInfo = this.getNodeInfo(this.nodeData, val.root_node, val.node_id)
+ nodeInfo.dynamicLoad = false
} else {
+ this.executeInfo.state = ''
this.loadNodeInfo()
}
}
@@ -625,15 +639,6 @@
const pluginInfo = this.atomFormInfo[componentCode][version]
this.executeInfo.plugin_name = `${pluginInfo.group_name}-${pluginInfo.name}`
}
- // 获取执行失败节点是否允许跳过,重试状态
- if (this.realTimeState.state === 'FAILED') {
- const activity = this.pipelineData.activities[this.nodeDetailConfig.node_id]
- this.isShowSkipBtn = this.location.type === 'tasknode' && activity.skippable
- this.isShowRetryBtn = this.location.type === 'tasknode' ? activity.retryable : false
- } else {
- this.isShowSkipBtn = false
- this.isShowRetryBtn = false
- }
} catch (e) {
this.theExecuteTime = undefined
this.executeInfo = {}
@@ -970,7 +975,14 @@
this.randomKey = new Date().getTime()
},
onNodeClick (node) {
- const nodeInfo = this.getNodeInfo(this.nodeData, '', node)
+ let parentId = ''
+ const { node_id: nodeId, root_node: rootNode } = this.nodeDetailConfig
+ if (nodeId === this.subProcessPipeline.id) {
+ parentId = rootNode ? `${rootNode}-${nodeId}` : nodeId
+ } else {
+ parentId = rootNode
+ }
+ const nodeInfo = this.getNodeInfo(this.nodeData, parentId, node)
if (nodeInfo) {
nodeInfo && this.onSelectNode(nodeInfo)
const parentInstance = this.$parent.$parent
@@ -1500,6 +1512,9 @@
}
.bk-resize-layout-border {
border: none;
+ .bk-resize-layout-aside {
+ overflow: hidden;
+ }
}
.action-wrapper {
width: 100%;
diff --git a/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo/ExecuteRecord.vue b/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo/ExecuteRecord.vue
index f64e4a0635..f9bf8a19e8 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo/ExecuteRecord.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo/ExecuteRecord.vue
@@ -113,7 +113,7 @@
if (state === 'CREATED') return this.$t('未执行')
// 如果整体任务执行完毕但有的节点没执行的话不展示描述
if (['FAILED', 'FINISHED'].includes(state) && state === 'READY') return this.$t('未执行')
- return skip || error_ignored ? this.$t.t('失败后跳过') : state && TASK_STATE_DICT[state]
+ return skip || error_ignored ? this.$t('失败后跳过') : state && TASK_STATE_DICT[state]
}
},
mounted () {
diff --git a/frontend/desktop/src/pages/task/TaskExecute/ModifyParams.vue b/frontend/desktop/src/pages/task/TaskExecute/ModifyParams.vue
index f990ad8553..0631bc4f36 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/ModifyParams.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/ModifyParams.vue
@@ -44,7 +44,7 @@
{{ confirmBtnText }}
- {{ $t('取消') }}
+ {{ $t('取消') }}
{{ $t('关闭') }}
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
index 2f373b45d2..08f9e27e5b 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
@@ -55,7 +55,20 @@
nodeId = val.split('-').slice(0, -1).join('-')
this.activeId = nodeId
}
- this.setDefaultActiveId(this.treeData, nodeId)
+ // 根据父节点过滤节点树
+ let nodes = this.treeData
+ const parentId = val.split('-').slice(1)
+ if (parentId.length) {
+ parentId.forEach(id => {
+ nodes.some(item => {
+ if (item.id === id) {
+ nodes = item.children
+ return true
+ }
+ })
+ })
+ }
+ this.setDefaultActiveId(nodes, nodeId)
},
deep: true,
immediate: true
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
index 810e6ee58c..c41cc5f308 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
@@ -65,8 +65,8 @@
-
- {{ node.title }}
+
+ {{ node.title }}
{{ $t('(') + $t('空分支') + $t(')') }}
@@ -283,9 +283,13 @@
display: flex;
align-items: center;
overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ .name {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
.empty-branch {
+ flex-shrink: 0;
font-size: 12px;
color: #979ba5;
}
diff --git a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
index 84024b69fe..c16267ed86 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
@@ -79,7 +79,7 @@
-
+
@@ -98,7 +98,8 @@
@packUp="packUp">
{
- this.setTaskStatusTimer()
- }, 1000)
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
}
} catch (e) {
console.log(e)
@@ -881,11 +882,8 @@
message: i18n.t('强制终止执行成功'),
theme: 'success'
})
- this.isNodeInfoPanelShow = false
- this.nodeInfoType = ''
- setTimeout(() => {
- this.setTaskStatusTimer()
- }, 1000)
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
}
} catch (e) {
console.log(e)
@@ -934,11 +932,8 @@
message: i18n.t('继续成功'),
theme: 'success'
})
- this.isNodeInfoPanelShow = false
- this.nodeInfoType = ''
- setTimeout(() => {
- this.setTaskStatusTimer()
- }, 1000)
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
}
} catch (e) {
console.log(e)
@@ -1075,6 +1070,8 @@
this.openNodeInfoPanel('modifyParams', isSubProcessNode ? i18n.t('重试子流程') : i18n.t('重试节点'))
this.retryNodeId = id
}
+ // 记录是否由【节点详情侧栏】打开的【重试侧栏】
+ this.isSourceDetailSideBar = !!info
} catch (error) {
console.warn(error)
}
@@ -1235,11 +1232,21 @@
data.node_id = node_id
}
await this.onRetryTask(data)
- this.isNodeInfoPanelShow = false
- this.retryNodeId = undefined
- // 重新轮询任务状态
- this.setTaskStatusTimer()
this.updateNodeActived(this.nodeDetailConfig.id, false)
+ // 重新打开详情面板
+ if (this.isSourceDetailSideBar) {
+ setTimeout(() => {
+ // 重新轮询任务状态
+ Promise.resolve(this.loadTaskStatus()).then(() => {
+ this.onNodeClick(data.node_id)
+ this.retryNodeId = undefined
+ })
+ }, 1000)
+ } else {
+ this.retryNodeId = undefined
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
+ }
} catch (error) {
console.warn(error)
} finally {
@@ -1323,9 +1330,9 @@
return
}
+ this.approval.pending = true
this.$refs.approvalForm.validate().then(async () => {
try {
- this.approval.pending = true
const { id, is_passed, message } = this.approval
const params = {
is_passed,
@@ -1347,11 +1354,19 @@
this.approval.is_passed = true
this.approval.message = ''
this.approval.dialogShow = false
+ this.$bkMessage({
+ message: i18n.t('节点审批成功'),
+ theme: 'success'
+ })
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
} catch (e) {
console.error(e)
} finally {
this.approval.pending = false
}
+ }, () => {
+ this.approval.pending = false
})
},
onApprovalCancel () {
@@ -1360,21 +1375,23 @@
this.approval.message = ''
this.approval.dialogShow = false
},
- onPauseClick (id, taskId) {
- this.taskPause(true, id, taskId)
- this.isNodeInfoPanelShow = false
- this.nodeInfoType = ''
- setTimeout(() => {
- this.setTaskStatusTimer()
- }, 1000)
+ async onPauseClick (id, taskId) {
+ try {
+ await this.taskPause(true, id, taskId)
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
+ } catch (error) {
+ console.warn(error)
+ }
},
- onContinueClick (id, taskId) {
- this.taskResume(true, id, taskId)
- this.isNodeInfoPanelShow = false
- this.nodeInfoType = ''
- setTimeout(() => {
- this.setTaskStatusTimer()
- }, 1000)
+ async onContinueClick (id, taskId) {
+ try {
+ await this.taskResume(true, id, taskId)
+ // 更新节点执行信息
+ this.updateNodeExecuteInfo()
+ } catch (error) {
+ console.warn(error)
+ }
},
onCloseConfigPanel () {
this.isShowConditionEdit = false
@@ -2329,9 +2346,20 @@
try {
this.pending.retry = true
await this.onRetryTask(data)
- this.isNodeInfoPanelShow = false
- this.setTaskStatusTimer()
- this.updateNodeActived(this.nodeDetailConfig.id, false)
+ // 重新打开详情面板
+ if (this.isSourceDetailSideBar) {
+ setTimeout(() => {
+ // 重新轮询任务状态
+ Promise.resolve(this.loadTaskStatus()).then(() => {
+ this.onNodeClick(data.node_id)
+ this.retryNodeId = undefined
+ })
+ }, 1000)
+ } else {
+ this.isNodeInfoPanelShow = false
+ this.setTaskStatusTimer()
+ this.updateNodeActived(this.nodeDetailConfig.id, false)
+ }
} catch (error) {
console.warn(error)
} finally {
@@ -2342,6 +2370,10 @@
this.isNodeInfoPanelShow = false
this.retryNodeId = undefined
this.updateNodeActived(id, false)
+ // 重新打开详情面板
+ if (this.isSourceDetailSideBar) {
+ this.onNodeClick(id)
+ }
},
onModifyTimeSuccess (id) {
this.isNodeInfoPanelShow = false
@@ -2399,6 +2431,10 @@
},
packUp () {
this.isNodeInfoPanelShow = false
+ // 重新打开详情面板
+ if (this.isSourceDetailSideBar) {
+ this.onNodeClick(this.retryNodeId)
+ }
this.retryNodeId = undefined
},
onshutDown () {
@@ -2482,6 +2518,35 @@
}
document.removeEventListener('mousemove', this.handleMouseMove)
document.removeEventListener('mouseup', this.handleMouseUp)
+ },
+ onWindowResize () {
+ const maxWidth = window.innerWidth - 400
+ let width = this.sidebarWidth
+ width = width > maxWidth ? maxWidth : width
+ width = width < 960 ? 960 : width
+ this.sidebarWidth = width
+ },
+ updateNodeExecuteInfo () {
+ if (this.isNodeInfoPanelShow && this.nodeInfoType === 'executeInfo') {
+ this.updateNodeActived(this.nodeDetailConfig.id, true)
+ const execInfoInstance = this.$refs.executeInfo
+ execInfoInstance.loading = true
+ execInfoInstance.subprocessLoading = !!execInfoInstance.subProcessPipeline
+ setTimeout(() => {
+ Promise.resolve(this.loadTaskStatus()).then(() => {
+ execInfoInstance.loadNodeInfo()
+ }).catch(() => {
+ execInfoInstance.loading = false
+ execInfoInstance.subprocessLoading = false
+ })
+ }, 1000)
+ } else {
+ this.isNodeInfoPanelShow = false
+ this.nodeInfoType = ''
+ setTimeout(() => {
+ this.setTaskStatusTimer()
+ }, 1000)
+ }
}
}
}
@@ -2549,8 +2614,11 @@
}
}
}
-/deep/.bk-sideslider-content {
- height: calc(100% - 60px);
+/deep/.bk-sideslider {
+ min-width: 1360px;
+ .bk-sideslider-content {
+ height: calc(100% - 60px);
+ }
}
.header {
display: flex;
From ac000a423b1525cf6f08ccbeee5e2550a842820e Mon Sep 17 00:00:00 2001
From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com>
Date: Thu, 23 Nov 2023 16:47:49 +0800
Subject: [PATCH 22/37] =?UTF-8?q?V3.31=E7=BA=BF=E4=B8=8A=E6=B5=8B=E8=AF=95?=
=?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D=20(#7184)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* optimization: 公共流程创建弹框统一title&&任务下公共流程创建任务不需要弹框
* optimization: 任务详情小画布分支标签支持选中
* bugfix: 节点树-节点未携带对应的任务id导致查看详情报错问题修复
---
.../src/pages/commonManage/CommonTplList.vue | 2 +-
.../src/pages/home/SelectCreateTaskDialog.vue | 2 +-
.../src/pages/task/TaskExecute/ExecuteInfo.vue | 10 +++++++++-
.../src/pages/task/TaskExecute/NodeTree.vue | 17 ++++++++++++-----
.../pages/task/TaskExecute/TaskOperation.vue | 3 ++-
.../template/TemplateEdit/TemplateHeader.vue | 9 ++++++++-
6 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/frontend/desktop/src/pages/commonManage/CommonTplList.vue b/frontend/desktop/src/pages/commonManage/CommonTplList.vue
index 6e4746d2d3..bd8a2a2573 100644
--- a/frontend/desktop/src/pages/commonManage/CommonTplList.vue
+++ b/frontend/desktop/src/pages/commonManage/CommonTplList.vue
@@ -251,7 +251,7 @@
:type="exportType">
@@ -986,9 +987,16 @@
if (nodeInfo) {
nodeInfo && this.onSelectNode(nodeInfo)
const parentInstance = this.$parent.$parent
- parentInstance.defaultActiveId = node + '-' + nodeInfo.parentId
+ if (nodeInfo.conditionType) {
+ parentInstance.defaultActiveId = node + '-' + nodeInfo.parentId + '-condition'
+ } else {
+ parentInstance.defaultActiveId = node + '-' + nodeInfo.parentId
+ }
}
},
+ onOpenConditionEdit (data) {
+ this.onNodeClick(`${data.nodeId}-${data.id}`)
+ },
onZoomOut () {
const jsFlowInstance = this.$refs.subProcessCanvas
jsFlowInstance.onZoomOut()
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
index 08f9e27e5b..59253fbb90 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
@@ -50,14 +50,18 @@
handler (val) {
this.activeId = val
let nodeId = val.split('-')[0]
+ let parentId = []
// 分支条件默认选中特殊处理
- if (val.split('-').pop() === 'condition') {
- nodeId = val.split('-').slice(0, -1).join('-')
- this.activeId = nodeId
+ if (val.match('condition')) {
+ // 小画布默认id会携带parentId
+ const nodes = val.split('-').slice(0, -1)
+ nodeId = nodes.slice(0, 2).join('-')
+ this.activeId = nodes.join('-')
+ parentId = nodes.slice(2)
}
// 根据父节点过滤节点树
let nodes = this.treeData
- const parentId = val.split('-').slice(1)
+ parentId = parentId.length ? parentId : val.split('-').slice(1)
if (parentId.length) {
parentId.forEach(id => {
nodes.some(item => {
@@ -82,9 +86,12 @@
setDefaultActiveId (treeData = [], id) {
return treeData.some(item => {
if (item.id === id) {
- item.expanded = !!item.isSubProcess
+ item.expanded = !!item.isSubProcess || !!item.conditionType
return true
} else if (item.children?.length) {
+ if (item.expanded) {
+ return this.setDefaultActiveId(item.children, id)
+ }
item.expanded = this.setDefaultActiveId(item.children, id)
return item.expanded
}
diff --git a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
index c16267ed86..f3d361c7ae 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
@@ -1748,7 +1748,8 @@
parentId,
independentId,
gatewayId: id,
- lastId: item.id
+ lastId: item.id,
+ taskId
},
ordered,
item.children
diff --git a/frontend/desktop/src/pages/template/TemplateEdit/TemplateHeader.vue b/frontend/desktop/src/pages/template/TemplateEdit/TemplateHeader.vue
index 62f2d0acee..2a334efc90 100644
--- a/frontend/desktop/src/pages/template/TemplateEdit/TemplateHeader.vue
+++ b/frontend/desktop/src/pages/template/TemplateEdit/TemplateHeader.vue
@@ -87,7 +87,7 @@
Date: Thu, 23 Nov 2023 19:10:53 +0800
Subject: [PATCH 23/37] minor: release V3.31.7
---
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 54a30493ac..e8eebb76bf 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.6
+version: 3.31.7
category: 运维工具
language_support: 中文
desktop:
diff --git a/app_desc.yaml b/app_desc.yaml
index 04d56391f5..71681f5083 100644
--- a/app_desc.yaml
+++ b/app_desc.yaml
@@ -1,5 +1,5 @@
spec_version: 2
-app_version: "3.31.6"
+app_version: "3.31.7"
app:
region: default
bk_app_code: bk_sops
diff --git a/config/default.py b/config/default.py
index 936352aea0..4eec6769bd 100644
--- a/config/default.py
+++ b/config/default.py
@@ -211,7 +211,7 @@
# mako模板中:
# 如果静态资源修改了以后,上线前改这个版本号即可
-STATIC_VERSION = "3.31.6"
+STATIC_VERSION = "3.31.7"
DEPLOY_DATETIME = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
From 58bcb7396ea0e4349d7f97f922394fb73977ace8 Mon Sep 17 00:00:00 2001
From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com>
Date: Mon, 27 Nov 2023 11:09:19 +0800
Subject: [PATCH 24/37] =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E8=AF=A6=E6=83=85?=
=?UTF-8?q?=E5=B0=8F=E7=94=BB=E5=B8=83=E7=BD=91=E5=85=B3=E9=80=89=E4=B8=AD?=
=?UTF-8?q?=E5=92=8C=E8=8A=82=E7=82=B9=E6=A0=91=E8=81=94=E5=8A=A8=20(#7186?=
=?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* optimization: 任务详情小画布网关选中和节点树联动
* bugfix: 子流程操作bug修复
---
.../pages/task/TaskExecute/ExecuteInfo.vue | 21 +++++++----
.../src/pages/task/TaskExecute/NodeTree.vue | 2 +-
.../pages/task/TaskExecute/NodeTreeItem.vue | 7 +++-
.../pages/task/TaskExecute/TaskOperation.vue | 37 +++++++++++++++----
4 files changed, 49 insertions(+), 18 deletions(-)
diff --git a/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue b/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue
index 6d48dfde7d..75b52bb83f 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue
@@ -340,7 +340,7 @@
subCanvasData: {},
timer: null,
subprocessLoading: true,
- subprocessTasks: [],
+ subprocessTasks: {},
subprocessNodeStatus: {},
subNodesExpanded: [], // 节点树展开的独立子流程节点
subProcessHeight: 160,
@@ -363,9 +363,9 @@
const { root_node, node_id, taskId } = this.nodeDetailConfig
let nodes = this.nodeDisplayStatus.children || {}
// 独立子流程节点状态特殊处理
- if (taskId && Object.keys(this.subprocessNodeStatus).length) {
+ if (taskId) {
nodes = this.subprocessNodeStatus[taskId]
- nodes = nodes && nodes.data.children
+ nodes = nodes ? nodes.data.children : {}
}
if (this.subProcessPipeline) {
const parentId = root_node?.split('-') || []
@@ -622,7 +622,7 @@
} else if (this.subProcessPipeline) {
this.subprocessLoading = false
}
- this.historyInfo = respData.skip ? [] : [respData]
+ this.historyInfo = [respData]
if (respData.histories) {
this.historyInfo.unshift(...respData.histories)
}
@@ -1029,8 +1029,15 @@
this.toggleNodeActive(this.nodeDetailConfig.node_id, false)
}
// 如果点击的是子流程节点或者是不属于当前所选中节点树的节点,需要重新刷新子流程画布
- const updateCanvas = node.parentId && (!this.subProcessPipeline || !this.subProcessPipeline.location.find(item => item.id === node.id))
- if (node.isSubProcess || updateCanvas) {
+ let updateCanvas = false
+ if (node.isSubProcess) {
+ updateCanvas = node.id !== this.nodeDetailConfig.node_id
+ }
+ if (!updateCanvas && node.parentId) {
+ const nodeId = node.conditionType ? node.id.split('-')[0] : node.id
+ updateCanvas = !this.subProcessPipeline?.location.find(item => item.id === nodeId)
+ }
+ if (updateCanvas) {
this.canvasRandomKey = new Date().getTime()
this.$nextTick(() => {
this.setCanvasZoomPosition()
@@ -1384,7 +1391,7 @@
}
const resp = await this.getBatchInstanceStatus(data)
if (!resp.result) return
- Object.assign(this.subprocessNodeStatus, resp.data)
+ this.subprocessNodeStatus = resp.data
for (const [key, value] of Object.entries(resp.data)) {
const { root_node, node_id } = this.subprocessTasks[key]
const nodeInfo = this.getNodeInfo(this.nodeData, root_node, node_id)
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
index 59253fbb90..75d76ac704 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTree.vue
@@ -86,7 +86,7 @@
setDefaultActiveId (treeData = [], id) {
return treeData.some(item => {
if (item.id === id) {
- item.expanded = !!item.isSubProcess || !!item.conditionType
+ item.expanded = !!item.isSubProcess || !!item.conditionType || item.isGateway
return true
} else if (item.children?.length) {
if (item.expanded) {
diff --git a/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue b/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
index c41cc5f308..55a703c8a0 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/NodeTreeItem.vue
@@ -83,7 +83,7 @@
:active-id="activeId"
:node-list="node.children"
@dynamicLoad="$emit('dynamicLoad', $event)"
- @click="handleClickNode">
+ @click="$emit('click', $event)">
@@ -142,7 +142,10 @@
node.expanded = true
return
}
- if (!node.expanded) {
+ if (node.expanded) {
+ const activeId = node.parentId ? node.id + '-' + node.parentId : node.id
+ node.expanded = activeId !== this.activeId
+ } else {
node.expanded = true
}
this.$emit('click', node)
diff --git a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
index f3d361c7ae..fcb6c82a2b 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/TaskOperation.vue
@@ -1240,6 +1240,7 @@
Promise.resolve(this.loadTaskStatus()).then(() => {
this.onNodeClick(data.node_id)
this.retryNodeId = undefined
+ this.isSourceDetailSideBar = false
})
}, 1000)
} else {
@@ -1596,7 +1597,8 @@
expanded: false,
target: flows[key].target,
gatewayId: id,
- children: []
+ children: [],
+ taskId
}
})
// 添加条件分支默认节点
@@ -1614,7 +1616,8 @@
expanded: false,
target: flows[flow_id].target,
gatewayId: id,
- children: []
+ children: [],
+ taskId
})
}
} else if (nodeConfig.type === 'ParallelGateway') {
@@ -1633,7 +1636,8 @@
conditionType: 'parallel',
target: flows[key].target,
gatewayId: id,
- children: []
+ children: [],
+ taskId
}
})
if (this.nodeIds[flowId]) {
@@ -2354,6 +2358,7 @@
Promise.resolve(this.loadTaskStatus()).then(() => {
this.onNodeClick(data.node_id)
this.retryNodeId = undefined
+ this.isSourceDetailSideBar = false
})
}, 1000)
} else {
@@ -2374,6 +2379,7 @@
// 重新打开详情面板
if (this.isSourceDetailSideBar) {
this.onNodeClick(id)
+ this.isSourceDetailSideBar = false
}
},
onModifyTimeSuccess (id) {
@@ -2435,6 +2441,7 @@
// 重新打开详情面板
if (this.isSourceDetailSideBar) {
this.onNodeClick(this.retryNodeId)
+ this.isSourceDetailSideBar = false
}
this.retryNodeId = undefined
},
@@ -2533,13 +2540,27 @@
const execInfoInstance = this.$refs.executeInfo
execInfoInstance.loading = true
execInfoInstance.subprocessLoading = !!execInfoInstance.subProcessPipeline
- setTimeout(() => {
- Promise.resolve(this.loadTaskStatus()).then(() => {
- execInfoInstance.loadNodeInfo()
- }).catch(() => {
+ setTimeout(async () => {
+ try {
+ const { root_node, component_code, taskId } = this.nodeDetailConfig
+ // 重新拉取父流程状态
+ await this.loadTaskStatus()
+ // 更新节点详情
+ await execInfoInstance.loadNodeInfo()
+ // 拉取独立子流程状态
+ if (taskId && component_code !== 'subprocess_plugin') {
+ const nodes = root_node.split('-')
+ execInfoInstance.subprocessTasks[taskId] = {
+ root_node: nodes.slice(0, -1).join('-'),
+ node_id: nodes.slice(-1)[0]
+ }
+ // 获取独立子流程任务状态
+ execInfoInstance.loadSubprocessStatus()
+ }
+ } catch (error) {
execInfoInstance.loading = false
execInfoInstance.subprocessLoading = false
- })
+ }
}, 1000)
} else {
this.isNodeInfoPanelShow = false
From 98ff1b8452504f277f290162190555f72271bea1 Mon Sep 17 00:00:00 2001
From: yiwenZhou <67539158+ywywZhou@users.noreply.github.com>
Date: Mon, 27 Nov 2023 18:21:02 +0800
Subject: [PATCH 25/37] =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E5=AD=90=E6=B5=81?=
=?UTF-8?q?=E7=A8=8B=E8=8A=82=E7=82=B9=E5=A4=B1=E8=B4=A5=E5=90=8E=E8=B7=B3?=
=?UTF-8?q?=E8=BF=87=E5=90=8E=EF=BC=8C=E5=85=B6=E4=B8=8B=E9=9D=A2=E7=9A=84?=
=?UTF-8?q?=E5=AD=90=E8=8A=82=E7=82=B9=E7=A6=81=E6=AD=A2=E6=93=8D=E4=BD=9C?=
=?UTF-8?q?=20(#7192)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* minor: 项目变量查看页添加默认值
* optimization: 独立子流程节点失败后跳过后,其下面的子节点禁止操作
---
.../pages/task/TaskExecute/ExecuteInfo.vue | 26 ++++++++++++++++++-
.../TabGlobalVariables/VariableEdit.vue | 11 ++++++--
2 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue b/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue
index 75b52bb83f..c2c383da79 100644
--- a/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue
+++ b/frontend/desktop/src/pages/task/TaskExecute/ExecuteInfo.vue
@@ -456,6 +456,28 @@
isShowActionWrap () {
// 任务终止时禁止节点操作
if (this.state === 'REVOKED') return false
+ // 判断父级节点是否存在失败后跳过
+ if (this.nodeDetailConfig.taskId) {
+ const allNodeStatus = {
+ ...this.nodeDisplayStatus.children,
+ ...Object.values(this.subprocessNodeStatus).reduce((acc, item) => {
+ return {
+ ...acc,
+ ...item.data.children
+ }
+ }, {})
+ }
+
+ const parentIds = this.nodeDetailConfig.root_node.split('-')
+ const isFailedSkip = parentIds.some(id => {
+ const { state, skip } = allNodeStatus[id] || {}
+ return state === 'FINISHED' && skip
+ })
+
+ if (isFailedSkip) {
+ return false
+ }
+ }
return (this.realTimeState.state === 'RUNNING' && !this.isSubProcessNode)
|| this.isShowRetryBtn
|| this.isShowSkipBtn
@@ -1391,7 +1413,9 @@
}
const resp = await this.getBatchInstanceStatus(data)
if (!resp.result) return
- this.subprocessNodeStatus = resp.data
+ Object.keys(resp.data).forEach(key => {
+ this.$set(this.subprocessNodeStatus, key, resp.data[key])
+ })
for (const [key, value] of Object.entries(resp.data)) {
const { root_node, node_id } = this.subprocessTasks[key]
const nodeInfo = this.getNodeInfo(this.nodeData, root_node, node_id)
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 4e3d8d413a..e93c8370b3 100644
--- a/frontend/desktop/src/pages/template/TemplateEdit/TemplateSetting/TabGlobalVariables/VariableEdit.vue
+++ b/frontend/desktop/src/pages/template/TemplateEdit/TemplateSetting/TabGlobalVariables/VariableEdit.vue
@@ -194,10 +194,17 @@
-