Merge branch 'master' into fix_bug_1217

This commit is contained in:
pycook 2024-12-20 16:46:48 +08:00 committed by GitHub
commit c3ae1f3ffa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 371 additions and 100 deletions

79
cmdb-api/.ruff.toml Normal file
View File

@ -0,0 +1,79 @@
line-length = 120
cache-dir = ".ruff_cache"
target-version = "py310"
unsafe-fixes = true
show-fixes = true
[lint]
select = [
"E",
"F",
"I",
"TCH",
# W
"W505",
# PT
"PT018",
# SIM
"SIM101",
"SIM114",
# PGH
"PGH004",
# PL
"PLE1142",
# RUF
"RUF100",
# UP
"UP007"
]
preview = true
ignore = ["FURB101"]
[lint.flake8-pytest-style]
mark-parentheses = false
parametrize-names-type = "list"
parametrize-values-row-type = "list"
parametrize-values-type = "tuple"
[lint.flake8-unused-arguments]
ignore-variadic-names = true
[lint.isort]
lines-between-types = 1
order-by-type = true
[lint.per-file-ignores]
"**/api/v1/*.py" = ["TCH"]
"**/model/*.py" = ["TCH003"]
"**/models/__init__.py" = ["F401", "F403"]
"**/tests/*.py" = ["E402"]
"celery_worker.py" = ["F401"]
"api/views/entry.py" = ["I001"]
"migrations/*.py" = ["I001", "E402"]
"*.py" = ["I001"]
"api/views/common_setting/department.py" = ["F841"]
"api/lib/common_setting/upload_file.py" = ["F841"]
"api/lib/common_setting/acl.py" = ["F841"]
"**/__init__.py" = ["F822"]
"api/tasks/*.py" = ["E722"]
"api/views/cmdb/*.py" = ["E722"]
"api/views/acl/*.py" = ["E722"]
"api/lib/secrets/*.py" = ["E722", "F841"]
"api/lib/utils.py" = ["E722", "E731"]
"api/lib/perm/authentication/cas/*" = ["E113", "F841"]
"api/lib/perm/acl/*" = ["E722"]
"api/lib/*" = ["E721", "F722"]
"api/lib/cmdb/*" = ["F722", "E722"]
"api/lib/cmdb/search/ci/es/search.py" = ["F841", "SIM114"]
"api/lib/cmdb/search/ci/db/search.py" = ["F841"]
"api/lib/cmdb/value.py" = ["F841"]
"api/lib/cmdb/history.py" = ["E501"]
"api/commands/common.py" = ["E722"]
"api/commands/click_cmdb.py" = ["E722"]
"api/lib/perm/auth.py" = ["SIM114"]
[format]
preview = true
quote-style = "single"
docstring-code-format = true
skip-magic-trailing-comma = false

View File

@ -346,7 +346,7 @@ def cmdb_inner_secrets_init(address):
if valid_address(address):
token = current_app.config.get("INNER_TRIGGER_TOKEN", "") if not token else token
if not token:
token = click.prompt(f'Enter root token', hide_input=True, confirmation_prompt=False)
token = click.prompt('Enter root token', hide_input=True, confirmation_prompt=False)
assert token is not None
resp = requests.post("{}/api/v0.1/secrets/auto_seal".format(address.strip("/")),
headers={"Inner-Token": token})

View File

@ -415,7 +415,7 @@ class AttributeManager(object):
db.session.rollback()
current_app.logger.error("update attribute error, {0}".format(str(e)))
return abort(400, ErrFormat.update_attribute_failed.format(("id=".format(_id))))
return abort(400, ErrFormat.update_attribute_failed.format(("id={}".format(_id))))
new = attr.to_dict()
if not new['choice_web_hook'] and new['is_choice']:

View File

@ -862,15 +862,15 @@ class CITypeRelationManager(object):
graph = nx.DiGraph()
def get_children(_id):
def get_children(_id, _graph):
children = CITypeRelation.get_by(parent_id=_id, to_dict=False)
for i in children:
if i.child_id != _id:
graph.add_edge(i.parent_id, i.child_id)
get_children(i.child_id)
_graph.add_edge(i.parent_id, i.child_id)
get_children(i.child_id, _graph)
get_children(source_type_id)
get_children(source_type_id, graph)
paths = list(nx.all_simple_paths(graph, source_type_id, target_type_ids))

View File

@ -16,8 +16,9 @@ class ErrFormat(CommonErrFormat):
argument_file_not_found = _l("The file doesn't seem to be uploaded") # 文件似乎并未上传
attribute_not_found = _l("Attribute {} does not exist!") # 属性 {} 不存在!
# 该属性是模型的唯一标识,不能被删除!
attribute_is_unique_id = _l(
"This attribute is the unique identifier of the model and cannot be deleted!") # 该属性是模型的唯一标识,不能被删除!
"This attribute is the unique identifier of the model and cannot be deleted!")
attribute_is_ref_by_type = _l(
"This attribute is referenced by model {} and cannot be deleted!") # 该属性被模型 {} 引用, 不能删除!
attribute_value_type_cannot_change = _l(
@ -129,7 +130,8 @@ class ErrFormat(CommonErrFormat):
adr_default_ref_once = _l("The default auto-discovery rule is already referenced by model {}!")
# unique_key方法必须返回非空字符串!
adr_unique_key_required = _l("The unique_key method must return a non-empty string!")
adr_plugin_attributes_list_required = _l("The attributes method must return a list") # attributes方法必须返回的是list
# attributes方法必须返回的是list
adr_plugin_attributes_list_required = _l("The attributes method must return a list")
# attributes方法返回的list不能为空!
adr_plugin_attributes_list_no_empty = _l("The list returned by the attributes method cannot be empty!")
# 只有管理员才可以定义执行机器为: 所有节点!

View File

@ -107,3 +107,12 @@ FROM
WHERE c_value_index_datetime.value LIKE "{0}") AS {1}
GROUP BY {1}.ci_id
"""
QUERY_CI_BY_NO_ATTR_IN = """
SELECT *
FROM
(SELECT c_value_index_texts.ci_id
FROM c_value_index_texts
WHERE c_value_index_texts.value in ({0})) AS {1}
GROUP BY {1}.ci_id
"""

View File

@ -27,6 +27,7 @@ from api.lib.cmdb.search.ci.db.query_sql import FACET_QUERY
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_ATTR_NAME
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_ID
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR_IN
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_TYPE
from api.lib.cmdb.search.ci.db.query_sql import QUERY_UNION_CI_ATTRIBUTE_IS_NULL
from api.lib.cmdb.utils import TableMap
@ -527,10 +528,15 @@ class Search(object):
for q in queries:
_query_sql = ""
if isinstance(q, dict):
alias, _query_sql, operator = self.__query_build_by_field(q['queries'], True, True, alias, is_sub=True)
# current_app.logger.info(_query_sql)
# current_app.logger.info((operator, is_first, alias))
operator = q['operator']
current_app.logger.debug("Dict query content: queries=%s, operator=%s", q['queries'], q['operator'])
if len(q['queries']) == 1 and ";" in q['queries'][0]:
values = q['queries'][0].split(";")
in_values = ",".join("'{0}'".format(v) for v in values)
_query_sql = QUERY_CI_BY_NO_ATTR_IN.format(in_values, alias)
operator = q['operator']
else:
alias, _query_sql, operator = self.__query_build_by_field(q['queries'], True, True, alias, is_sub=True)
operator = q['operator']
elif ":" in q and not q.startswith("*"):
alias, _query_sql, operator = self.__query_by_attr(q, queries, alias, is_sub)

View File

@ -55,7 +55,6 @@ def str2datetime(x):
return datetime.datetime.strptime(x, "%Y-%m-%d %H:%M")
class ValueTypeMap(object):
deserialize = {
ValueTypeEnum.INT: string2int,

View File

@ -1,6 +1,6 @@
import functools
from flask import abort, session
from flask import abort, session, current_app
from api.lib.common_setting.acl import ACLManager
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.perm.acl.acl import is_app_admin
@ -15,6 +15,7 @@ def perms_role_required(app_name, resource_type_name, resource_name, perm, role_
try:
has_perms = acl.role_has_perms(session["acl"]['rid'], resource_name, resource_type_name, perm)
except Exception as e:
current_app.logger.error(f"acl role_has_perms err: {e}")
# resource_type not exist, continue check role
if role_name:
if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(app_name):

View File

@ -476,7 +476,7 @@ class EditDepartmentInACL(object):
for employee in e_list:
employee_acl_rid = employee.get('e_acl_rid')
if employee_acl_rid == 0:
result.append(f"employee_acl_rid == 0")
result.append("employee_acl_rid == 0")
continue
cls.remove_single_employee_from_old_department(acl, employee, result)
@ -501,8 +501,8 @@ class EditDepartmentInACL(object):
acl.remove_user_from_role(employee.get('e_acl_rid'), payload)
current_app.logger.info(f"remove {employee.get('e_acl_rid')} from {d_acl_rid}")
except Exception as e:
result.append(
f"remove_user_from_role employee_acl_rid: {employee.get('e_acl_rid')}, parent_id: {d_acl_rid}, err: {e}")
err = f"remove_user_from_role e_acl_rid: {employee.get('e_acl_rid')}, parent_id: {d_acl_rid}, err: {e}"
result.append(err)
return True
@ -548,7 +548,7 @@ class EditDepartmentInACL(object):
for employee in e_list:
employee_acl_rid = employee.get('e_acl_rid')
if employee_acl_rid == 0:
result.append(f"employee_acl_rid == 0")
result.append("employee_acl_rid == 0")
continue
cls.remove_single_employee_from_old_department(acl, employee, result)

View File

@ -4,7 +4,7 @@ import traceback
from datetime import datetime
import requests
from flask import abort
from flask import abort, current_app
from flask_login import current_user
from sqlalchemy import or_, literal_column, func, not_, and_
from werkzeug.datastructures import MultiDict
@ -478,7 +478,7 @@ class EmployeeCRUD(object):
Employee.deleted == 0,
Employee.block == block,
]
if type(department_id) == list:
if isinstance(department_id, list):
if len(department_id) == 0:
return []
else:
@ -702,6 +702,7 @@ class EmployeeCRUD(object):
try:
last_login = datetime.strptime(last_login, '%Y-%m-%d %H:%M:%S')
except Exception as e:
current_app.logger.error(f"strptime {last_login} err: {e}")
last_login = datetime.now()
else:
last_login = datetime.now()
@ -712,6 +713,7 @@ class EmployeeCRUD(object):
)
return last_login
except Exception as e:
current_app.logger.error(f"update last_login err: {e}")
return

View File

@ -2,7 +2,7 @@ import requests
from api.lib.common_setting.const import BotNameMap
from api.lib.common_setting.resp_format import ErrFormat
from api.models.common_setting import CompanyInfo, NoticeConfig
from api.models.common_setting import NoticeConfig
from wtforms import Form
from wtforms import StringField
from wtforms import validators

View File

@ -48,7 +48,9 @@ class CMDBApp(BaseApp):
{"page": "Model_Relationships", "page_cn": "模型关系", "perms": ["read"]},
{"page": "Operation_Audit", "page_cn": "操作审计", "perms": ["read"]},
{"page": "Relationship_Types", "page_cn": "关系类型", "perms": ["read"]},
{"page": "Auto_Discovery", "page_cn": "自动发现", "perms": ["read", "create_plugin", "update_plugin", "delete_plugin"]},
{"page": "Auto_Discovery", "page_cn": "自动发现",
"perms": ["read", "create_plugin", "update_plugin", "delete_plugin"]
},
{"page": "TopologyView", "page_cn": "拓扑视图",
"perms": ["read", "create_topology_group", "update_topology_group", "delete_topology_group",
"create_topology_view"],

View File

@ -6,7 +6,7 @@ from functools import wraps
from flask import abort
from flask import request
from api.lib.perm.acl.cache import AppCache, AppAccessTokenCache
from api.lib.perm.acl.cache import AppCache
from api.lib.perm.acl.resp_format import ErrFormat

View File

@ -1,8 +1,5 @@
# -*- coding:utf-8 -*-
import time
import redis_lock
import six
from flask import abort

View File

@ -1,7 +1,6 @@
# -*- coding:utf-8 -*-
import base64
from typing import Set
import elasticsearch
import redis

View File

@ -131,7 +131,7 @@ class EmployeeChangePasswordWithACLID(APIView):
if not password:
abort(400, ErrFormat.password_is_required)
data = EmployeeCRUD.change_password_by_uid(_uid, password)
EmployeeCRUD.change_password_by_uid(_uid, password)
return self.jsonify(200)

View File

@ -6,7 +6,7 @@ import magic
from api.lib.common_setting.const import MIMEExtMap
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name, CommonFileCRUD
from api.lib.common_setting.upload_file import generate_new_file_name, CommonFileCRUD
from api.resource import APIView
prefix = '/file'

View File

@ -58,3 +58,4 @@ python-magic==0.4.27
jsonpath==0.82.2
networkx>=3.1
ipaddress>=1.0.23
ruff==0.8.3

View File

@ -314,6 +314,9 @@ const cmdb_en = {
enum: 'Enum',
ciGrantTip: `Filter conditions can be changed dynamically using {{}} referenced variables, currently user variables are supported, such as {{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
searchInputTip: 'Please search for resource keywords',
columnSearchInputTip: '192.168.1.1\n192.168.1.2\n192.168.1.3',
rowSearchMode: 'Single Row Search',
columnSearchMode: 'Multi Row Search',
resourceSearch: 'Resource Search',
recentSearch: 'Recent Search',
myCollection: 'My Collection',

View File

@ -314,6 +314,9 @@ const cmdb_zh = {
enum: '枚举',
ciGrantTip: `筛选条件可使用{{}}引用变量实现动态变化,目前支持用户变量,如{{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
searchInputTip: '请搜索资源关键字',
columnSearchInputTip: '192.168.1.1\n192.168.1.2\n192.168.1.3',
rowSearchMode: '单行搜索',
columnSearchMode: '多行搜索',
resourceSearch: '资源搜索',
recentSearch: '最近搜索',
myCollection: '我的收藏',

View File

@ -95,7 +95,7 @@
attr.name,
{
rules: [{ required: attr.is_required, message: $t('placeholder1') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default ? attr.default.default : null,
initialValue: attr.default && attr.default.default !== undefined && attr.default.default !== null ? attr.default.default : null,
},
]"
style="width: 100%"
@ -148,6 +148,7 @@
</template>
<script>
import _ from 'lodash'
import moment from 'moment'
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
import CIReferenceAttr from '@/components/ciReferenceAttr/index.vue'
@ -210,7 +211,7 @@ export default {
},
getChoiceDefault(attr) {
if (!attr?.default?.default) {
if (_.isNil(attr?.default?.default)) {
return attr.is_list ? [] : null
}

View File

@ -659,7 +659,7 @@ export default {
} else {
this.$nextTick(() => {
this.form.setFieldsValue({
default_value: _record.default && _record.default.default ? _record.default.default : null,
default_value: _record?.default?.default ?? null,
})
})
}

View File

@ -180,7 +180,7 @@ export default {
saveCondition(isSubmit) {
this.$refs.conditionFilterRef.handleSubmit()
this.$nextTick(() => {
this.$emit('saveCondition', isSubmit)
this.$emit('saveCondition', isSubmit, this.$parent.isColumnSearch ? 'column' : 'normal')
this.visible = false
})
},

View File

@ -1,29 +1,59 @@
<template>
<div :class="['search-input', classType ? 'search-input-' + classType : '']">
<a-input
:value="searchValue"
class="search-input-component"
:placeholder="$t('cmdb.ciType.searchInputTip')"
@change="handleChangeSearchValue"
@pressEnter="saveCondition(true)"
>
<a-icon
class="search-input-component-icon"
slot="prefix"
type="search"
@click="saveCondition(true)"
/>
</a-input>
<FilterPopover
ref="filterPpoverRef"
:CITypeGroup="CITypeGroup"
:allAttributesList="allAttributesList"
:expression="expression"
:selectCITypeIds="selectCITypeIds"
@changeFilter="changeFilter"
@updateAllAttributesList="updateAllAttributesList"
@saveCondition="saveCondition"
/>
<div :class="['search-input', classType ? 'search-input-' + classType : '', { 'column-search-mode': isColumnSearch }]">
<div class="search-area">
<div v-show="!isColumnSearch" class="input-wrapper">
<a-input
:value="searchValue"
class="search-input-component"
:placeholder="$t('cmdb.ciType.searchInputTip')"
@change="handleChangeSearchValue"
@pressEnter="saveCondition(true, 'normal')"
/>
<a-icon
class="search-icon"
type="search"
@click="saveCondition(true, 'normal')"
/>
</div>
<div v-show="isColumnSearch" class="textarea-wrapper">
<div class="textarea-container">
<a-textarea
:value="searchValue"
class="column-search-component"
:rows="4"
:placeholder="$t('cmdb.ciType.columnSearchInputTip')"
@change="handleChangeColumnSearchValue"
@pressEnter="handlePressEnter"
/>
<a-icon
class="search-icon"
type="search"
@click="saveCondition(true, 'column')"
/>
</div>
</div>
<div class="operation-area">
<FilterPopover
ref="filterPpoverRef"
:CITypeGroup="CITypeGroup"
:allAttributesList="allAttributesList"
:expression="expression"
:selectCITypeIds="selectCITypeIds"
@changeFilter="changeFilter"
@updateAllAttributesList="updateAllAttributesList"
@saveCondition="saveCondition"
/>
<div class="column-search-btn" @click="toggleColumnSearch">
<a-icon class="column-search-btn-icon" type="menu" />
<span class="column-search-btn-title">
{{ isColumnSearch ? $t('cmdb.ciType.rowSearchMode') : $t('cmdb.ciType.columnSearchMode') }}
</span>
</div>
</div>
</div>
<div v-if="copyText" class="expression-display">
<span class="expression-display-text">{{ copyText }}</span>
@ -69,11 +99,12 @@ export default {
classType: {
type: String,
default: ''
},
isColumnSearch: {
type: Boolean,
default: false
}
},
data() {
return {}
},
computed: {
// 复制文字展示与实际文本复制内容区别在于未选择模型时不展示所有模型拼接数据
copyText() {
@ -88,7 +119,14 @@ export default {
textArray.push(exp)
}
if (this.searchValue) {
textArray.push(`*${this.searchValue}*`)
let processedValue = this.searchValue
if (this.isColumnSearch) {
const values = this.searchValue.split('\n').filter(v => v.trim())
if (values.length) {
processedValue = `(${values.join(';')})`
}
}
textArray.push(`${!this.isColumnSearch ? '*' : ''}${processedValue}${!this.isColumnSearch ? '*' : ''}`)
}
return textArray.length ? `q=${textArray.join(',')}` : ''
@ -98,8 +136,8 @@ export default {
updateAllAttributesList(value) {
this.$emit('updateAllAttributesList', value)
},
saveCondition(isSubmit) {
this.$emit('saveCondition', isSubmit)
saveCondition(isSubmit, searchType = 'normal') {
this.$emit('saveCondition', isSubmit, searchType)
},
handleChangeSearchValue(e) {
const value = e.target.value
@ -125,7 +163,9 @@ export default {
ciTypeIds.push(...ids)
})
}
const copyText = `${ciTypeIds?.length ? `_type:(${ciTypeIds.join(';')})` : ''}${exp ? `,${exp}` : ''}${searchValue ? `,*${searchValue}*` : ''}`
const copyText = `${ciTypeIds?.length ? `_type:(${ciTypeIds.join(';')})` : ''}${exp ? `,${exp}` : ''}${
searchValue ? `,${!this.isColumnSearch ? '*' : ''}${searchValue}${!this.isColumnSearch ? '*' : ''}` : ''
}`
this.$copyText(copyText)
.then(() => {
@ -134,6 +174,35 @@ export default {
.catch(() => {
this.$message.error(this.$t('cmdb.ci.copyFailed'))
})
},
toggleColumnSearch() {
this.$emit('toggleSearchMode', !this.isColumnSearch)
this.saveCondition(false, !this.isColumnSearch ? 'column' : 'normal')
},
handleChangeColumnSearchValue(e) {
const value = e.target.value
this.changeFilter({
name: 'searchValue',
value
})
},
handlePressEnter(e) {
if (this.isColumnSearch) {
// 列搜索模式下按下 Enter 键时阻止默认行为并插入换行符
e.preventDefault()
const value = this.searchValue || ''
const cursorPosition = e.target.selectionStart
const newValue = value.slice(0, cursorPosition) + '\n' + value.slice(cursorPosition)
this.changeFilter({
name: 'searchValue',
value: newValue
})
} else {
this.saveCondition(true, 'normal')
}
}
}
}
@ -142,43 +211,107 @@ export default {
<style lang="less" scoped>
.search-input {
width: 100%;
height: 48px;
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
&-component {
height: 100%;
.search-area {
display: flex;
align-items: flex-start;
min-height: 48px;
width: 100%;
}
.input-wrapper {
position: relative;
flex-grow: 1;
background-color: #FFFFFF;
border: none;
font-size: 14px;
border-radius: 48px;
overflow: hidden;
&-icon {
color: #2F54EB;
.search-input-component {
height: 48px;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #d9d9d9;
font-size: 14px;
border-radius: 8px;
/deep/ input {
height: 100%;
padding-right: 40px;
}
}
/deep/ & > input {
height: 100%;
margin-left: 10px;
border: solid 1px transparent;
box-shadow: none;
.search-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
color: #2F54EB;
font-size: 14px;
cursor: pointer;
}
}
&:focus {
border-color: @primary-color;
.textarea-wrapper {
flex-grow: 1;
.textarea-container {
position: relative;
width: 100%;
max-height: 200px;
.column-search-component {
width: 100%;
max-height: 200px;
background-color: #FFFFFF;
border: 1px solid #d9d9d9;
font-size: 14px;
border-radius: 8px;
padding-right: 35px;
resize: none;
transition: all 0.3s;
&:hover, &:focus {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
.search-icon {
position: absolute;
right: 12px;
top: 12px;
color: #2F54EB;
font-size: 14px;
cursor: pointer;
}
}
}
&-after {
height: 38px;
justify-content: flex-start;
.operation-area {
display: flex;
align-items: center;
height: 48px;
margin-left: 10px;
}
.search-input-component {
max-width: 524px;
.column-search-btn {
flex-shrink: 0;
display: flex;
align-items: center;
margin-left: 13px;
cursor: pointer;
&-icon {
color: #2F54EB;
font-size: 12px;
}
&-title {
font-size: 14px;
font-weight: 400;
color: #2F54EB;
margin-left: 3px;
}
}
@ -201,5 +334,18 @@ export default {
cursor: pointer;
}
}
.search-input-component,
.column-search-component {
&:hover {
border-color: #40a9ff;
}
&:focus {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
outline: none;
}
}
}
</style>

View File

@ -15,9 +15,11 @@
:searchValue="searchValue"
:selectCITypeIds="selectCITypeIds"
:expression="expression"
:isColumnSearch="currentSearchType === 'column'"
@changeFilter="changeFilter"
@updateAllAttributesList="updateAllAttributesList"
@saveCondition="saveCondition"
@toggleSearchMode="handleToggleSearchMode"
/>
<HistoryList
:recentList="recentList"
@ -46,9 +48,11 @@
:searchValue="searchValue"
:selectCITypeIds="selectCITypeIds"
:expression="expression"
:isColumnSearch="currentSearchType === 'column'"
@changeFilter="changeFilter"
@updateAllAttributesList="updateAllAttributesList"
@saveCondition="saveCondition"
@toggleSearchMode="handleToggleSearchMode"
/>
<HistoryList
:recentList="recentList"
@ -172,6 +176,7 @@ export default {
showInstanceDetail: false,
detailCIId: -1,
detailCITypeId: -1,
currentSearchType: 'normal',
}
},
computed: {
@ -240,7 +245,9 @@ export default {
}
},
async saveCondition(isSubmit) {
async saveCondition(isSubmit, searchType = 'normal') {
this.currentSearchType = searchType
if (
this.searchValue ||
this.expression ||
@ -253,7 +260,8 @@ export default {
if (
option.searchValue === this.searchValue &&
option.expression === this.expression &&
_.isEqual(option.ciTypeIds, this.selectCITypeIds)
_.isEqual(option.ciTypeIds, this.selectCITypeIds) &&
option.searchType === this.currentSearchType
) {
needDeleteList.push(item.id)
} else {
@ -279,7 +287,8 @@ export default {
searchValue: this.searchValue,
expression: this.expression,
ciTypeIds: this.selectCITypeIds,
ciTypeNames
ciTypeNames,
searchType: this.currentSearchType
},
name: '__recent__'
})
@ -290,7 +299,7 @@ export default {
this.isSearch = true
this.currentPage = 1
this.hideDetail()
this.loadInstance()
this.loadInstance(this.currentSearchType)
}
},
@ -307,11 +316,19 @@ export default {
this.getRecentList()
},
async loadInstance() {
const { selectCITypeIds, expression, searchValue } = this
async loadInstance(searchType = 'normal') {
const { selectCITypeIds, expression } = this
let { searchValue } = this
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
if (searchType === 'column' && searchValue) {
const values = searchValue.split('\n').filter(v => v.trim())
if (values.length) {
searchValue = `(${values.join(';')})`
}
}
const ciTypeIds = [...selectCITypeIds]
if (!ciTypeIds.length) {
this.CITypeGroup.forEach((item) => {
@ -322,7 +339,7 @@ export default {
const res = await searchCI({
q: `${ciTypeIds?.length ? `_type:(${ciTypeIds.join(';')})` : ''}${exp ? `,${exp}` : ''}${
searchValue ? `,*${searchValue}*` : ''
searchValue ? `,${searchType === 'normal' ? '*' : ''}${searchValue}${searchType === 'normal' ? '*' : ''}` : ''
}`,
count: this.pageSize,
page: this.currentPage,
@ -389,7 +406,6 @@ export default {
}
this.ciTabList = ciTabList
// 处理引用属性
const allAttr = []
subscribedRes.map((item) => {
allAttr.push(...item.attributes)
@ -477,20 +493,21 @@ export default {
this.searchValue = data?.searchValue || ''
this.expression = data?.expression || ''
this.selectCITypeIds = data?.ciTypeIds || []
this.currentSearchType = data?.searchType || 'normal'
this.hideDetail()
this.loadInstance()
this.loadInstance(this.currentSearchType)
},
handlePageSizeChange(_, pageSize) {
this.pageSize = pageSize
this.currentPage = 1
this.loadInstance()
this.loadInstance(this.currentSearchType)
},
changePage(page) {
this.currentPage = page
this.loadInstance()
this.loadInstance(this.currentSearchType)
},
changeFilter(data) {
@ -533,6 +550,10 @@ export default {
clickFavor(data) {
this.isSearch = true
this.showDetail(data)
},
handleToggleSearchMode(isColumn) {
this.currentSearchType = isColumn ? 'column' : 'normal'
}
}
}