feat: 搜索框(部分内容)
This commit is contained in:
@@ -13,8 +13,10 @@
|
||||
"@amcharts/amcharts4-geodata": "^4.1.20",
|
||||
"axios": "^0.21.1",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"codemirror": "^5.65.1",
|
||||
"core-js": "^3.6.5",
|
||||
"dayjs": "^1.10.5",
|
||||
"dt-sql-parser": "^4.0.0-beta.2.2",
|
||||
"echarts": "^5.1.1",
|
||||
"element-plus": "^1.0.2-beta.44",
|
||||
"leaflet": "^1.7.1",
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
.advanced-search {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
.search__suffixes {
|
||||
&.search__suffixes--text-mode, &.search__suffixes--tag-mode {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
|
||||
.search__suffix {
|
||||
margin-left: 8px;
|
||||
|
||||
.cn-icon-search-advance {
|
||||
color: #A6AAAE;
|
||||
font-size: 18px;
|
||||
}
|
||||
.el-icon-search {
|
||||
color: #3976CB;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.advanced-search--show-list .CodeMirror, .advanced-search--show-list .tag-search {
|
||||
border: none;
|
||||
}
|
||||
.tag-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
border: 1px solid #CECECE;
|
||||
border-radius: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 80px;
|
||||
background-color: white;
|
||||
|
||||
.tag-search__meta, .tag-search__add {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tag-search__add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 4px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
background-color: #F5F6F7;
|
||||
border: 1px solid #D7D7D7;
|
||||
border-radius: 1px;
|
||||
cursor: pointer;
|
||||
transition: all linear .1s;
|
||||
|
||||
&:hover {
|
||||
background-color: white;
|
||||
color: #111;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-search__meta {
|
||||
display: flex;
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
background-color: #F5F6F7;
|
||||
|
||||
&>div:not(:last-of-type) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
&.tag-search__meta--condition {
|
||||
.condition__delete {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #bcbdbd;
|
||||
font-size: 16px;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
transition: all linear .1s;
|
||||
|
||||
&:hover {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.condition__column, .condition__operator, .condition__value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
cursor: text;
|
||||
}
|
||||
.condition__operator {
|
||||
color: #3976CB;
|
||||
}
|
||||
.condition__operation-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.condition__operation {
|
||||
margin: 0 5px;
|
||||
color: #3976CB;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tag-search__meta--connection {
|
||||
.connection__value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #3976CB;
|
||||
font-weight: bold;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/* BASICS */
|
||||
|
||||
.CodeMirror {
|
||||
/* Set height, width, borders, and global font properties here */
|
||||
font-family: monospace;
|
||||
height: 40px;
|
||||
color: black;
|
||||
direction: ltr;
|
||||
border: 1px solid #CECECE;
|
||||
border-radius: 2px;
|
||||
padding-right: 80px;
|
||||
}
|
||||
|
||||
/* PADDING */
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding: 11px 5px; /* Vertical padding around content */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
padding: 0 4px; /* Horizontal padding of content */
|
||||
}
|
||||
|
||||
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
background-color: white; /* The little square between H and V scrollbars */
|
||||
}
|
||||
|
||||
/* GUTTER */
|
||||
|
||||
.CodeMirror-gutters {
|
||||
border-right: 1px solid #ddd;
|
||||
background-color: #f7f7f7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.CodeMirror-linenumbers {}
|
||||
.CodeMirror-linenumber {
|
||||
padding: 0 3px 0 5px;
|
||||
min-width: 20px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.CodeMirror-guttermarker { color: black; }
|
||||
.CodeMirror-guttermarker-subtle { color: #999; }
|
||||
|
||||
/* CURSOR */
|
||||
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid black;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
/* Shown when moving in bi-directional text */
|
||||
.CodeMirror div.CodeMirror-secondarycursor {
|
||||
border-left: 1px solid silver;
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0 !important;
|
||||
background: #7e7;
|
||||
}
|
||||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
z-index: 1;
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-line::selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span::selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; }
|
||||
.cm-fat-cursor .CodeMirror-line::-moz-selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span::-moz-selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; }
|
||||
.cm-fat-cursor { caret-color: transparent; }
|
||||
@-moz-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
|
||||
/* Can style cursor different in overwrite (non-insert) mode */
|
||||
.CodeMirror-overwrite .CodeMirror-cursor {}
|
||||
|
||||
.cm-tab { display: inline-block; text-decoration: inherit; }
|
||||
|
||||
.CodeMirror-rulers {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: -50px; bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.CodeMirror-ruler {
|
||||
border-left: 1px solid #ccc;
|
||||
top: 0; bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* DEFAULT THEME */
|
||||
|
||||
.cm-s-default .cm-header {color: blue;}
|
||||
.cm-s-default .cm-quote {color: #090;}
|
||||
.cm-negative {color: #d44;}
|
||||
.cm-positive {color: #292;}
|
||||
.cm-header, .cm-strong {font-weight: bold;}
|
||||
.cm-em {font-style: italic;}
|
||||
.cm-link {text-decoration: underline;}
|
||||
.cm-strikethrough {text-decoration: line-through;}
|
||||
|
||||
.cm-s-default .cm-keyword {color: #708;}
|
||||
.cm-s-default .cm-atom {color: #219;}
|
||||
.cm-s-default .cm-number {color: #164;}
|
||||
.cm-s-default .cm-def {color: #00f;}
|
||||
.cm-s-default .cm-variable,
|
||||
.cm-s-default .cm-punctuation,
|
||||
.cm-s-default .cm-property,
|
||||
.cm-s-default .cm-operator {}
|
||||
.cm-s-default .cm-variable-2 {color: #05a;}
|
||||
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
|
||||
.cm-s-default .cm-comment {color: #a50;}
|
||||
.cm-s-default .cm-string {color: #a11;}
|
||||
.cm-s-default .cm-string-2 {color: #f50;}
|
||||
.cm-s-default .cm-meta {color: #555;}
|
||||
.cm-s-default .cm-qualifier {color: #555;}
|
||||
.cm-s-default .cm-builtin {color: #30a;}
|
||||
.cm-s-default .cm-bracket {color: #997;}
|
||||
.cm-s-default .cm-tag {color: #170;}
|
||||
.cm-s-default .cm-attribute {color: #00c;}
|
||||
.cm-s-default .cm-hr {color: #999;}
|
||||
.cm-s-default .cm-link {color: #00c;}
|
||||
|
||||
.cm-s-default .cm-error {color: #f00;}
|
||||
.cm-invalidchar {color: #f00;}
|
||||
|
||||
.CodeMirror-composing { border-bottom: 2px solid; }
|
||||
|
||||
/* Default styles for common addons */
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
|
||||
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
||||
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||
|
||||
/* STOP */
|
||||
|
||||
/* The rest of this file contains styles related to the mechanics of
|
||||
the editor. You probably shouldn't touch them. */
|
||||
|
||||
.CodeMirror {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
// overflow: scroll !important; /* Things will break if this is overridden */
|
||||
/* 50px is the magic margin used to hide the element's real scrollbars */
|
||||
/* See overflow: hidden in .CodeMirror */
|
||||
margin-bottom: -50px; margin-right: -50px;
|
||||
padding-bottom: 50px;
|
||||
height: 100%;
|
||||
outline: none; /* Prevent dragging from highlighting the element */
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.CodeMirror-sizer {
|
||||
position: relative;
|
||||
border-right: 50px solid transparent;
|
||||
}
|
||||
|
||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||
before actual scrolling happens, thus preventing shaking and
|
||||
flickering artifacts. */
|
||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
display: none !important;
|
||||
outline: none;
|
||||
}
|
||||
.CodeMirror-vscrollbar {
|
||||
right: 0; top: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.CodeMirror-hscrollbar {
|
||||
bottom: 0; left: 0;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.CodeMirror-scrollbar-filler {
|
||||
right: 0; bottom: 0;
|
||||
}
|
||||
.CodeMirror-gutter-filler {
|
||||
left: 0; bottom: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute; left: 0; top: 0;
|
||||
min-height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
.CodeMirror-gutter {
|
||||
white-space: normal;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-bottom: -50px;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.CodeMirror-gutter-background {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-elt {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
|
||||
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
|
||||
|
||||
.CodeMirror-lines {
|
||||
cursor: text;
|
||||
min-height: 1px; /* prevents collapsing before first draw */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
/* Reset some styles that the rest of the page might have set */
|
||||
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
||||
border-width: 0;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-font-variant-ligatures: contextual;
|
||||
font-variant-ligatures: contextual;
|
||||
}
|
||||
.CodeMirror-wrap pre.CodeMirror-line,
|
||||
.CodeMirror-wrap pre.CodeMirror-line-like {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.CodeMirror-linebackground {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: 0; bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-linewidget {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0.1px; /* Force widget margins to stay inside of the container */
|
||||
}
|
||||
|
||||
.CodeMirror-widget {}
|
||||
|
||||
.CodeMirror-rtl pre { direction: rtl; }
|
||||
|
||||
.CodeMirror-code {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Force content-box sizing for the elements where we expect it */
|
||||
.CodeMirror-scroll,
|
||||
.CodeMirror-sizer,
|
||||
.CodeMirror-gutter,
|
||||
.CodeMirror-gutters,
|
||||
.CodeMirror-linenumber {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.CodeMirror-measure {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-cursor {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.CodeMirror-measure pre { position: static; }
|
||||
|
||||
div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
div.CodeMirror-dragcursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-focused div.CodeMirror-cursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-selected { background: #d9d9d9; }
|
||||
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
||||
.CodeMirror-crosshair { cursor: crosshair; }
|
||||
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
|
||||
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
|
||||
|
||||
.cm-searching {
|
||||
background-color: #ffa;
|
||||
background-color: rgba(255, 255, 0, .4);
|
||||
}
|
||||
|
||||
/* Used to force a border model for a node */
|
||||
.cm-force-border { padding-right: .1px; }
|
||||
|
||||
@media print {
|
||||
/* Hide the cursor when printing */
|
||||
.CodeMirror div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* See issue #2901 */
|
||||
.cm-tab-wrap-hack:after { content: ''; }
|
||||
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext { background: none; }
|
||||
@@ -1,7 +1,8 @@
|
||||
@import 'components/advancedSearch/advanced-search';
|
||||
@import './components/advancedSearch/advanced-search2';
|
||||
@import './components/advancedSearch/codemirror';
|
||||
// @import './components/charts/panel';
|
||||
@import 'components/common/TimeRange/date-time-range';
|
||||
@import 'components/common/TimeRange/time-refresh';
|
||||
@import './components/common/TimeRange/date-time-range';
|
||||
@import './components/common/TimeRange/time-refresh';
|
||||
@import './components/common/pagination';
|
||||
// @import './components/entities/entities';
|
||||
@import './components/layout/layout';
|
||||
|
||||
@@ -45,14 +45,14 @@
|
||||
}
|
||||
.explorer-search__input {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
height: 66px;
|
||||
max-width: 1000px;
|
||||
height: 40px;
|
||||
}
|
||||
.explorer-search__foot {
|
||||
display: flex;
|
||||
padding-top: 18px;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
max-width: 1000px;
|
||||
justify-content: space-between;
|
||||
font-weight: bold;
|
||||
|
||||
|
||||
80
src/components/advancedSearch/Index.vue
Normal file
80
src/components/advancedSearch/Index.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div
|
||||
class="advanced-search"
|
||||
>
|
||||
<text-mode
|
||||
v-if="searchMode === 'text'"
|
||||
@changeMode="changeMode"
|
||||
@search="search"
|
||||
></text-mode>
|
||||
<tag-mode
|
||||
v-if="searchMode === 'tag'"
|
||||
:column-list="columnList"
|
||||
:operator-list="showOperatorList"
|
||||
:connection-list="showConnectionList"
|
||||
@changeMode="changeMode"
|
||||
@search="search"
|
||||
></tag-mode>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TagMode from '@/components/advancedSearch/TagMode'
|
||||
import TextMode from '@/components/advancedSearch/TextMode'
|
||||
import { defaultOperatorList, defaultConnectionList } from '@/components/advancedSearch/meta/meta'
|
||||
import _ from 'lodash'
|
||||
import { ref } from 'vue'
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
TagMode,
|
||||
TextMode
|
||||
},
|
||||
props: {
|
||||
// 默认模式,tag | text
|
||||
defaultMode: String,
|
||||
// 使用全文检索
|
||||
fullText: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 条件字段列表
|
||||
columnList: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 操作符列表
|
||||
operatorList: Array,
|
||||
// 连接符列表
|
||||
connectionList: Array
|
||||
},
|
||||
methods: {
|
||||
search () {
|
||||
|
||||
},
|
||||
changeMode (mode) {
|
||||
this.searchMode = mode
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
// 默认为文本模式 TODO 改回text
|
||||
let searchMode = ref('tag')
|
||||
if (props.defaultMode) {
|
||||
switch (props.defaultMode) {
|
||||
case 'tag': {
|
||||
searchMode = 'tag'
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果参数中的operatorList、connectionList为空,使用默认值
|
||||
const showOperatorList = _.isEmpty(props.operatorList) ? defaultOperatorList : [...props.operatorList]
|
||||
const showConnectionList = _.isEmpty(props.connectionList) ? defaultConnectionList : [...props.connectionList]
|
||||
return {
|
||||
searchMode,
|
||||
showOperatorList,
|
||||
showConnectionList
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
226
src/components/advancedSearch/TagMode.vue
Normal file
226
src/components/advancedSearch/TagMode.vue
Normal file
@@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="tag-search">
|
||||
<div class="tag-search__meta"
|
||||
v-for="(meta, index) in metaList"
|
||||
:key="index"
|
||||
:class="`tag-search__meta--${meta.meta}`"
|
||||
>
|
||||
<template v-if="meta.meta === condition">
|
||||
<!-- 删除按钮 -->
|
||||
<div class="condition__delete" @click="removeCondition(index)"><i class="el-icon-error"></i></div>
|
||||
<!-- 字段选择 -->
|
||||
<div class="condition__column">
|
||||
<div v-show="meta.column.isEditing">
|
||||
<el-select
|
||||
allow-create
|
||||
filterable
|
||||
size="mini"
|
||||
v-model="meta.column.name"
|
||||
ref="columnSelect"
|
||||
:placeholder="meta.column.name || ''"
|
||||
@blur="columnBlur(meta, index)"
|
||||
@change="(value) => selectColumn(value, meta)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(column, index) in columnList"
|
||||
:key="index"
|
||||
:label="column.label"
|
||||
:value="column.name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<span v-show="!meta.column.isEditing" @click="columnClick(meta)">{{meta.column.label}}</span>
|
||||
</div>
|
||||
<!-- 已选操作符显示 -->
|
||||
<div class="condition__operator" @click="operatorClick(meta)" v-if="meta.operator.show">{{meta.operator.value}}</div>
|
||||
<!-- 值 -->
|
||||
<div class="condition__value">
|
||||
<div v-if="meta.value.isEditing">
|
||||
<el-input v-model="meta.value.value" size="mini" @blur="valueBlur(meta)" ref="valueInput"></el-input>
|
||||
</div>
|
||||
<span v-else @click="valueClick(meta)">{{meta.value.label}}</span>
|
||||
</div>
|
||||
<!-- 操作符选择器 -->
|
||||
<div class="condition__operation-select" v-if="meta.operator.show && meta.operator.isEditing">
|
||||
<div
|
||||
class="condition__operation"
|
||||
v-for="(operator, index) in operatorList"
|
||||
:key="index"
|
||||
@click.stop="selectOperator(operator, meta)"
|
||||
>{{operator}}</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 条件连接符 -->
|
||||
<template v-else-if="meta.meta === connection">
|
||||
<div class="connection__value">
|
||||
<div v-show="meta.isEditing">
|
||||
<el-select
|
||||
:placeholder="meta.value"
|
||||
v-model="meta.value"
|
||||
size="mini"
|
||||
@change="(value) => selectConnection(value, meta)"
|
||||
>
|
||||
<el-option
|
||||
v-for="(connection, index) in connectionList"
|
||||
:key="index"
|
||||
:label="connection.label"
|
||||
:value="connection.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<span v-show="!meta.isEditing" @click="connectionClick(meta)">{{meta.value}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="tag-search__add" @click="addCondition">{{$t('entities.advancedSearch.add')}}</div>
|
||||
<div class="search__suffixes search__suffixes--tag-mode">
|
||||
<div class="search__suffix" @click="changeMode">
|
||||
<i class="cn-icon cn-icon-search-advance"></i>
|
||||
</div>
|
||||
<div class="search__suffix" @click="search">
|
||||
<i class="el-icon-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Meta, { connection, condition, columnType } from './meta/meta'
|
||||
import _ from 'lodash'
|
||||
export default {
|
||||
name: 'TagMode',
|
||||
props: {
|
||||
columnList: Array,
|
||||
operatorList: Array,
|
||||
connectionList: Array
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
condition,
|
||||
connection,
|
||||
metaList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 新增条件
|
||||
addCondition () {
|
||||
this.metaList.forEach(meta => {
|
||||
meta.cancelEditing()
|
||||
})
|
||||
// 先判断上一个condition是否已填写完整,没有则删除;之后将当前所有meta的内容的isEditing置为false
|
||||
if (this.metaList.length > 0) {
|
||||
const prevMeta = this.metaList[this.metaList.length - 1]
|
||||
if (!prevMeta.isCompleteCondition()) {
|
||||
this.metaList.splice(this.metaList.length - (this.metaList.length === 1 ? 1 : 2), this.metaList.length === 1 ? 1 : 2)
|
||||
}
|
||||
}
|
||||
// 不是首个condition时,先增加一个connection,再增加一个新condition
|
||||
if (!_.isEmpty(this.metaList)) {
|
||||
this.addConnection()
|
||||
}
|
||||
const newCondition = new Meta(condition)
|
||||
newCondition.isEditing = true
|
||||
newCondition.column.isEditing = true
|
||||
this.metaList.push(newCondition)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.columnSelect.focus()
|
||||
})
|
||||
},
|
||||
addConnection () {
|
||||
this.metaList.push(new Meta(connection))
|
||||
},
|
||||
removeCondition (index) {
|
||||
// 删除首个条件时,连带删除之后的第一个连接符;否则删除之前的第一个连接符
|
||||
if (index === 0) {
|
||||
if (this.metaList.length > 2) {
|
||||
this.metaList = this.metaList.slice(0, 2)
|
||||
} else {
|
||||
this.metaList = []
|
||||
}
|
||||
} else {
|
||||
this.metaList.splice(index - 1, 2)
|
||||
}
|
||||
},
|
||||
// 选择搜索条件的事件
|
||||
selectColumn (value, meta) {
|
||||
// 处理column。如果是用户自己新增的column,视为全文搜索
|
||||
if (this.isCustomized(value)) {
|
||||
meta.column.type = columnType.fullText
|
||||
meta.column.label = value
|
||||
} else {
|
||||
const selectedColumn = this.columnList.find(column => {
|
||||
return column.name === value
|
||||
})
|
||||
meta.column.label = selectedColumn.label
|
||||
meta.column.type = selectedColumn.type
|
||||
}
|
||||
setTimeout(() => {
|
||||
meta.column.isEditing = false
|
||||
|
||||
// 处理操作符
|
||||
if (!meta.operator.value) {
|
||||
meta.operator.isEditing = true
|
||||
meta.operator.show = true
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
selectConnection (value, meta) {
|
||||
meta.isEditing = false
|
||||
},
|
||||
columnClick (meta) {
|
||||
meta.column.isEditing = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.columnSelect.focus()
|
||||
})
|
||||
},
|
||||
columnBlur (meta, index) {
|
||||
setTimeout(() => {
|
||||
meta.column.isEditing = false
|
||||
if (meta.isEmpty()) {
|
||||
this.metaList.splice(index, 1)
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
connectionClick (meta) {
|
||||
meta.isEditing = true
|
||||
},
|
||||
// 选择操作符的事件
|
||||
selectOperator (operator, meta) {
|
||||
meta.operator.value = operator
|
||||
meta.operator.isEditing = false
|
||||
// 处理搜索值
|
||||
meta.value.isEditing = true
|
||||
meta.value.show = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.valueInput.focus()
|
||||
})
|
||||
},
|
||||
operatorClick (meta) {
|
||||
meta.operator.isEditing = true
|
||||
},
|
||||
valueBlur (meta) {
|
||||
meta.value.label = meta.value.value // label是显示,value是实际值;目前的需求label和value是相等的,
|
||||
meta.value.isEditing = !meta.isCompleteCondition()
|
||||
},
|
||||
valueClick (meta) {
|
||||
meta.value.isEditing = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.valueInput.focus()
|
||||
})
|
||||
},
|
||||
// 判断是否是用户自己添加的内容,用于判断是否是全局搜索
|
||||
isCustomized (value) {
|
||||
return !this.columnList.some(meta => {
|
||||
return meta.name === value
|
||||
})
|
||||
},
|
||||
search () {
|
||||
this.$emit('search', this.metaList)
|
||||
},
|
||||
changeMode () {
|
||||
this.$emit('changeMode', 'text')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
43
src/components/advancedSearch/TextMode.vue
Normal file
43
src/components/advancedSearch/TextMode.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<textarea
|
||||
ref="textSearch"
|
||||
></textarea>
|
||||
<div class="search__suffixes search__suffixes--text-mode">
|
||||
<div class="search__suffix" @click="changeMode">
|
||||
<i class="cn-icon cn-icon-search-advance"></i>
|
||||
</div>
|
||||
<div class="search__suffix">
|
||||
<i class="el-icon-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'codemirror/theme/ambiance.css'
|
||||
import 'codemirror/addon/hint/show-hint'
|
||||
import 'codemirror/addon/hint/show-hint.css'
|
||||
import 'codemirror/mode/sql/sql'
|
||||
const CodeMirror = require('codemirror')
|
||||
export default {
|
||||
name: 'TextMode',
|
||||
methods: {
|
||||
initCodeMirror () {
|
||||
CodeMirror.fromTextArea(this.$refs.textSearch, {
|
||||
mode: {
|
||||
name: 'sql'
|
||||
},
|
||||
lineNumbers: false
|
||||
})
|
||||
},
|
||||
search () {
|
||||
this.$emit('search', this.metaList)
|
||||
},
|
||||
changeMode () {
|
||||
this.$emit('changeMode', 'tag')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initCodeMirror()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
88
src/components/advancedSearch/meta/meta.js
Normal file
88
src/components/advancedSearch/meta/meta.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import _ from 'lodash'
|
||||
export const connection = 'connection'
|
||||
export const condition = 'condition'
|
||||
export const columnType = {
|
||||
fullText: 'fullText',
|
||||
string: 'string',
|
||||
long: 'long'
|
||||
}
|
||||
export const defaultOperatorList = ['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE']
|
||||
export const defaultConnectionList = [
|
||||
{
|
||||
value: 'AND',
|
||||
label: 'AND'
|
||||
},
|
||||
{
|
||||
value: 'OR',
|
||||
label: 'OR'
|
||||
}
|
||||
]
|
||||
export default class Meta {
|
||||
// meta元数据有两种,一是condition,表示字段、操作符、值的组合,二是connection,是condition之间的连接符,AND | OR
|
||||
constructor (type) {
|
||||
switch (type) {
|
||||
case connection: {
|
||||
this.newConnection()
|
||||
break
|
||||
}
|
||||
default: {
|
||||
this.newCondition()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newConnection () {
|
||||
this.meta = connection
|
||||
this.value = 'AND'
|
||||
this.isEditing = false
|
||||
}
|
||||
|
||||
newCondition () {
|
||||
this.meta = condition
|
||||
this.column = {
|
||||
name: '',
|
||||
type: '', // fullText | string | long ...
|
||||
label: '',
|
||||
isEditing: false
|
||||
}
|
||||
this.operator = {
|
||||
value: '',
|
||||
isEditing: false,
|
||||
show: false
|
||||
}
|
||||
this.value = {
|
||||
value: '',
|
||||
label: '',
|
||||
isEditing: false,
|
||||
show: false
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty () {
|
||||
if (this.meta === condition) {
|
||||
return _.isEmpty(this.column.name)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 是否是完整的condition
|
||||
isCompleteCondition () {
|
||||
return (this.column.type === columnType.fullText)
|
||||
? !_.isEmpty(this.column.name)
|
||||
: !_.isEmpty(this.column.name) && !_.isEmpty(this.operator.value) && !_.isEmpty(this.value.value)
|
||||
}
|
||||
|
||||
// 取消editing状态
|
||||
cancelEditing () {
|
||||
if (this.meta === condition) {
|
||||
this.column.isEditing = false
|
||||
this.operator.isEditing = false
|
||||
this.value.isEditing = false
|
||||
}
|
||||
if (this.meta === connection) {
|
||||
this.isEditing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,11 @@
|
||||
<div class="explorer-search__input">
|
||||
<advanced-search
|
||||
ref="search"
|
||||
:show-list="showList"
|
||||
:column-list="columnList"
|
||||
:operator-list="operatorList"
|
||||
:connection-list="connectionList"
|
||||
:full-text="true"
|
||||
:class="{'advanced-search--show-list': showList}"
|
||||
@search="search"
|
||||
></advanced-search>
|
||||
</div>
|
||||
@@ -28,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdvancedSearch from '@/components/advancedSearch/AdvancedSearch'
|
||||
import AdvancedSearch from '@/components/advancedSearch/Index'
|
||||
export default {
|
||||
name: 'CnSearch',
|
||||
components: {
|
||||
@@ -40,6 +44,138 @@ export default {
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
columnList: [
|
||||
{
|
||||
name: 'entity_type',
|
||||
type: 'string',
|
||||
label: this.$t('overall.type')
|
||||
},
|
||||
{
|
||||
name: 'ip_addr',
|
||||
type: 'string',
|
||||
label: 'IP'
|
||||
},
|
||||
{
|
||||
name: 'ip_location_country',
|
||||
type: 'string',
|
||||
label: this.$t('overall.country')
|
||||
},
|
||||
{
|
||||
name: 'ip_location_province',
|
||||
type: 'string',
|
||||
label: this.$t('overall.province')
|
||||
},
|
||||
{
|
||||
name: 'ip_location_city',
|
||||
type: 'string',
|
||||
label: this.$t('overall.city')
|
||||
},
|
||||
{
|
||||
name: 'ip_asn',
|
||||
type: 'string',
|
||||
label: this.$t('entities.asn')
|
||||
},
|
||||
{
|
||||
name: 'ip_os',
|
||||
type: 'string',
|
||||
label: 'OS'
|
||||
},
|
||||
{
|
||||
name: 'domain_name',
|
||||
type: 'string',
|
||||
label: 'Domain'
|
||||
},
|
||||
{
|
||||
name: 'domain_category',
|
||||
type: 'string',
|
||||
label: this.$t('entities.domainCategory')
|
||||
},
|
||||
{
|
||||
name: 'domain_category_group',
|
||||
type: 'string',
|
||||
label: this.$t('entities.domainDetail.categoryGroup')
|
||||
},
|
||||
{
|
||||
name: 'domain_reputation_score',
|
||||
type: 'long',
|
||||
label: this.$t('entities.domainDetail.reputationValue')
|
||||
},
|
||||
{
|
||||
name: 'domain_reputation_level',
|
||||
type: 'string',
|
||||
label: this.$t('entities.reputationLevel')
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_email',
|
||||
type: 'string',
|
||||
label: 'Domain whois email'
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_name_servers',
|
||||
type: 'string',
|
||||
label: 'Domain whois ns'
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_registrar',
|
||||
type: 'string',
|
||||
label: 'Domain whois registrar'
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_org',
|
||||
type: 'string',
|
||||
label: 'Domain whois organization'
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_city',
|
||||
type: 'string',
|
||||
label: 'Domain whois city'
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_state',
|
||||
type: 'string',
|
||||
label: 'Domain whois state'
|
||||
},
|
||||
{
|
||||
name: 'domain_whois_country',
|
||||
type: 'string',
|
||||
label: 'Domain whois country'
|
||||
},
|
||||
{
|
||||
name: 'app_name',
|
||||
type: 'string',
|
||||
label: 'App'
|
||||
},
|
||||
{
|
||||
name: 'app_category',
|
||||
type: 'string',
|
||||
label: this.$t('trafficSummary.appCategory')
|
||||
},
|
||||
{
|
||||
name: 'app_subcategory',
|
||||
type: 'string',
|
||||
label: this.$t('entities.domainDetail.appSubcategory')
|
||||
},
|
||||
{
|
||||
name: 'app_risk',
|
||||
type: 'long',
|
||||
label: this.$t('trafficSummary.appRisk')
|
||||
}
|
||||
],
|
||||
operatorList: ['=', '!=', '>', '<', '>=', '<='/* , 'IN', 'NOT IN', 'LIKE', 'NOT LIKE' */],
|
||||
connectionList: [
|
||||
{
|
||||
value: 'AND',
|
||||
label: 'AND'
|
||||
},
|
||||
{
|
||||
value: 'OR',
|
||||
label: 'OR'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
search (params = {}) {
|
||||
this.$emit('search', params)
|
||||
|
||||
Reference in New Issue
Block a user