blob: 21e5533d69774df3bf6623af937ac4f5723f5220 [file] [log] [blame]
<template>
<div id="queryHistoryTable">
<div class="clearfix ksd-mt-32 ksd-mb-16">
<div class="ksd-fleft">
<div class="ksd-title-page">{{$t('kylinLang.menu.queryhistory')}}</div>
</div>
<div class="ksd-fright">
<div class="btn-group export-btn">
<el-dropdown
split-button
class="ksd-fleft"
:class="{'is-disabled': !queryHistoryTotalSize}"
type="primary"
size="medium"
id="exportSql"
btn-icon="el-ksd-icon-export_22"
placement="bottom-start"
@click="exportHistory(false)">{{$t('kylinLang.query.export')}}
<el-dropdown-menu slot="dropdown" class="model-actions-dropdown">
<el-dropdown-item
:disabled="!queryHistoryTotalSize"
@click="exportHistory(true)">
{{$t('kylinLang.query.exportSql')}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
<div class="clearfix ksd-mb-16">
<div class="table-filters ksd-fleft">
<DropdownFilter
type="checkbox"
trigger="click"
:value="realizationFilters"
hideArrow
@input="v => filterContent(v, 'realization')"
@handleChangeAll="handleChangeAll"
@filterFilters="(v) => fiterList('loadFilterHitModelsList', v)"
@filter-scroll-bottom="scrollBottom"
:totalSizeLabel="$t('totalSizeLabel', {num: searchCount})"
isShowSearchInput
:is-loading-data="isShowLoading && (isFilterItemLoading || paginationRealFilteArr.length === maxFilterAndFilterValues)"
:is-loading="isFilterItemLoading"
:loading-tips="paginationRealFilteArr.length === maxFilterAndFilterValues ? $t('loadingTips') : ''"
:filterPlaceholder="$t('searchAnsweredBy')"
:optionsTitle="$t('model')"
:isSelectAllOption="allHitModel"
:options="paginationRealFilteArr.map(item => ({label: item, value: item}))"
:options2="pushdownFilteArr.map(item => ({label: item, value: item}))">
<el-button text type="primary" iconr="el-ksd-icon-arrow_down_22">
{{$t('kylinLang.query.realization_th')}} {{ realizationLabel }}
</el-button>
</DropdownFilter>
<DropdownFilter
type="checkbox"
trigger="click"
:value="filterData.query_status"
hideArrow
@input="v => filterContent(v, 'query_status')"
:options="[
{ renderLabel: renderStatusLabel, value: 'SUCCEEDED' },
{ renderLabel: renderStatusLabel, value: 'FAILED' }
]">
<el-button text type="primary" iconr="el-ksd-icon-arrow_down_22">{{$t('kylinLang.query.query_status')}} {{filterData.query_status.length > 1 ? `${$t(filterData.query_status[0])} +${filterData.query_status.length - 1}` : filterData.query_status.join('')}}</el-button>
</DropdownFilter>
<DropdownFilter
type="datetimerange"
trigger="click"
:value="datetimerange"
hideArrow
:shortcuts="['lastDay', 'lastWeek', 'lastMonth']"
@input="v => handleInputDateRange(v)">
<el-button text type="primary" iconr="el-ksd-icon-arrow_down_22">{{$t('kylinLang.query.startTime_th')}} {{selectedRange}}</el-button>
</DropdownFilter>
<DropdownFilter
type="inputNumber"
trigger="click"
:value="[filterData.latencyFrom, filterData.latencyTo]"
hideArrow
:isShowDropDownImme="plusFilter.includes('latency_th')"
v-if="plusFilter.includes('latency_th')"
:isShowfooter="false"
@input="v => filterContent(v, 'latency')"
:options="queryNodes.map(item => ({label: item, value: item}))">
<el-button text type="primary" iconr="el-ksd-icon-arrow_down_22">{{$t('kylinLang.query.latency_th')}} <span v-if="filterData.latencyFrom!==null&&filterData.latencyTo!==null">{{filterData.latencyFrom}}s To {{ filterData.latencyTo }}s</span></el-button>
</DropdownFilter>
<DropdownFilter
type="checkbox"
trigger="click"
:value="filterData.server"
hideArrow
:isShowDropDownImme="plusFilter.includes('queryNode')"
v-if="plusFilter.includes('queryNode')"
@input="v => filterContent(v, 'server')"
:options="queryNodes.map(item => ({label: item, value: item}))">
<el-button text type="primary" iconr="el-ksd-icon-arrow_down_22">{{$t('kylinLang.query.queryNode')}} {{filterData.server.length > 1 ? `${$t(filterData.server[0])} +${filterData.server.length - 1}` : filterData.server.join('')}}</el-button>
</DropdownFilter>
<DropdownFilter
type="checkbox"
trigger="click"
:value="filterData.submitter"
hideArrow
:isShowDropDownImme="plusFilter.includes('submitter')"
isShowSearchInput
:filterPlaceholder="$t('searchSubmitter')"
v-if="queryHistoryFilter.includes('filterActions')&&plusFilter.includes('submitter')"
@input="v => filterContent(v, 'submitter')"
@filterFilters="(v) => fiterList('loadFilterSubmitterList', v)"
:options="submitterFilter.map(item => ({label: item, value: item}))">
<el-button text type="primary" iconr="el-ksd-icon-arrow_down_22">{{$t('kylinLang.query.submitter')}} {{filterData.submitter.length > 1 ? `${$t(filterData.submitter[0])} +${filterData.submitter.length - 1}` : filterData.submitter.join('')}}</el-button>
</DropdownFilter>
<el-dropdown @command="handleCommand" placement="bottom-start" trigger="click" v-if="plusFilter.length < 3 ">
<i class="el-dropdown-link el-ksd-n-icon-plus-outlined"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="latency_th" v-if="!plusFilter.includes('latency_th')">{{$t('kylinLang.query.latency_th')}}</el-dropdown-item>
<el-dropdown-item command="queryNode" v-if="!plusFilter.includes('queryNode')">{{$t('kylinLang.query.queryNode')}}</el-dropdown-item>
<el-dropdown-item command="submitter" v-if="!plusFilter.includes('submitter')">{{$t('kylinLang.query.submitter')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="actions">
<el-button
nobg-text
class="reset-filters-btn"
:disabled="!isHasFilterValue"
@click="clearAllTags">{{$t('clearAll')}}</el-button>
</div>
</div>
<div class="ksd-fright ksd-ml-10">
<el-input v-model="filterData.sql" class="searchInput" v-global-key-event.enter.debounce="onSqlFilterChange" @clear="onSqlFilterChange()" prefix-icon="el-ksd-icon-search_22" :placeholder="$t('searchSQL')" size="medium"></el-input>
</div>
</div>
<el-table
:data="queryHistoryData"
v-scroll-shadow
class="history-table"
v-loading="isLoadingHistory"
:empty-text="emptyText"
:expand-row-keys="toggleExpandId"
@expand-change="expandChange"
:row-key="val => val.query_id"
ref="queryHistoryTable"
style="width: 100%">
<el-table-column type="expand" width="34">
<template slot-scope="props">
<!-- <div class="detail-title">
<span class="ksd-fleft ksd-fs-14">{{$t('queryDetails')}}</span>
</div> -->
<div class="detail-content">
<el-row :gutter="16" type="flex">
<el-col :span="15" :style="{height: props.row.flexHeight + 'px'}">
<div class="loading" v-if="currentExpandId === props.row.query_id"><i class="el-icon-loading"></i></div>
<kylin-editor
width="100%"
:height="props.row.editorH"
lang="sql"
theme="chrome"
v-if="props.row.editorH"
:ref="'historySqlEditor' + props.row.query_id"
:key="props.row.query_id"
:readOnly="true"
:isFormatSwitch="true"
:dragable="false"
:isAbridge="true"
:value="props.row.sql_text">
</kylin-editor>
</el-col>
<el-col :span="9">
<div class="ksd-list history_detail_table" :id="'detailTable_' + props.row.query_id">
<p class="list">
<span class="label">{{$t('kylinLang.query.query_id')}}:</span>
<span class="text">{{props.row.query_id}}</span>
</p>
<p class="list">
<span class="label">{{$t('kylinLang.query.latency_th')}}:</span>
<span class="text">
<el-popover
placement="bottom"
:width="$lang === 'en' ? 400 : 320"
v-if="props.row.query_steps.length&&props.row.query_status==='SUCCEEDED'"
popper-class="duration-popover"
trigger="hover">
<el-row v-for="(step, index) in props.row.query_steps" :key="step.name" v-show="!step.group || (step.group === 'PREPARATION' && isShowDetail_PREPARATION) || (step.group === 'JOB_EXECUTION' && isShowDetail_JOB_EXECUTION)">
<el-col :span="14">
<span class="step-name"
:class="{'font-medium': index === 0, 'sub-step': ['PREPARATION', 'JOB_EXECUTION'].includes(step.group)}">{{$t(step.name)}}</span>
<i class="el-icon-ksd-more_01"
:class="{'up': (step.name === 'PREPARATION' && isShowDetail_PREPARATION) || (step.group === 'JOB_EXECUTION' && isShowDetail_JOB_EXECUTION)}"
v-if="['PREPARATION', 'JOB_EXECUTION'].includes(step.name)"
@click.stop="toggleDetail(step.name)"></i>
</el-col>
<el-col :span="4">
<span class="step-duration ksd-fright" :class="{'font-medium': index === 0, 'sub-step': ['PREPARATION', 'JOB_EXECUTION'].includes(step.group)}">{{step.duration / 1000 | fixed(2)}}s</span>
</el-col>
<el-col :span="6" v-if="props.row.query_steps&&props.row.query_steps[0].duration>0">
<el-progress :stroke-width="6" :percentage="getProgress(step.duration, props.row.query_steps[0].duration)" color="#A6D6F6" :show-text="false"></el-progress>
</el-col>
</el-row>
<span slot="reference" class="duration">{{props.row.duration / 1000 | fixed(2)}}s</span>
</el-popover>
<span v-else>{{props.row.duration / 1000 | fixed(2)}}s</span>
</span>
</p>
<template v-if="props.row.query_status === 'SUCCEEDED'">
<p class="list" :class="{'active': props.row.hightlight_realizations}" v-if="!(props.row.engine_type === 'NATIVE'&&!(props.row.realizations && getRealizations2(props.row.realizations).length))">
<span class="label">{{$t('kylinLang.query.answered_by')}}:</span>
<span class="text">
<span class="realization-tags" v-if="props.row.realizations && getRealizations2(props.row.realizations).length">
<span v-for="(item, index) in getRealizations2(props.row.realizations)" :key="item.modelId">
<template v-if="'visible' in item && !item.visible">
<span @click="openAuthorityDialog(item)" class="no-authority-model"><i class="el-icon-ksd-lock"></i>{{item.modelAlias}}</span><span>{{`${index !== getRealizations2(props.row.realizations).length - 1 ? $t('kylinLang.common.comma') : ''}`}}</span>
</template>
<template v-else>
<span @click="openIndexDialog(item, getRealizations2(props.row.realizations))" :class="{'model-tag': item.valid, 'disable': !item.valid || item.indexType === 'Table Snapshot'}">{{item.modelAlias}}</span><span>{{`${index !== getRealizations2(props.row.realizations).length - 1 ? $t('kylinLang.common.comma') : ''}`}}</span>
</template>
</span>
</span>
<span v-else class="realization-tags"><el-tag type="warning" size="small" v-if="props.row.engine_type">{{props.row.engine_type}}</el-tag></span>
</span>
</p>
<p class="list" v-if="props.row.realizations && getRealizations(props.row.realizations).length && getLayoutIds(props.row.realizations)">
<span class="label">{{$t('kylinLang.query.index_id')}}:</span>
<span class="text">
<span :class="['realizations-layout-id', {'is-disabled': !item.layoutExist}]" v-for="(item, index) in getRealizations(props.row.realizations)" :key="item.layoutId">
<el-tooltip placement="top" :content="$t('unExistLayoutTip')" :disabled="item.layoutExist">
<span @click="openLayoutDetails(item)">{{item.layoutId}}</span>
</el-tooltip>
<el-tag size="mini" v-if="item.streamingLayout" class="ksd-ml-2" style="cursor:default">{{$t('streamingTag')}}</el-tag>
<el-tooltip placement="top" :content="$t('secStorage')">
<el-icon v-if="item.secondStorage" class="ksd-fs-22" name="el-ksd-icon-tieredstorage_22" type="mult"></el-icon>
</el-tooltip><span>{{`${index !== getRealizations(props.row.realizations).length - 1 ? $t('kylinLang.common.comma') : ''}`}}</span>
</span>
</span>
</p>
<p class="list" v-if="props.row.realizations && props.row.realizations.length && getSnapshots(props.row.realizations)">
<span class="label">{{$t('kylinLang.query.snapshot')}}:</span>
<span class="text">{{getSnapshots(props.row.realizations)}}</span>
</p>
<p class="list">
<span class="label">{{$t('kylinLang.query.total_scan_count')}}:</span>
<span class="text">{{props.row.total_scan_count | filterNumbers}}</span>
</p>
<p class="list">
<span class="label">{{$t('kylinLang.query.total_scan_bytes')}}:</span>
<span class="text">{{props.row.total_scan_bytes | filterNumbers}}</span>
</p>
<p class="list">
<span class="label">{{$t('kylinLang.query.result_row_count')}}:</span>
<span class="text">{{props.row.result_row_count | filterNumbers}}</span>
</p>
<p class="list">
<span class="label">{{$t('kylinLang.query.cache_hit')}}:</span>
<span class="text">{{props.row.cache_hit}}</span>
</p>
<p class="list" v-if="props.row.query_history_info && !!props.row.query_history_info.cache_type">
<span class="label">{{$t('kylinLang.query.cache_type')}}:</span>
<span class="text">{{props.row.query_history_info.cache_type}}</span>
</p>
</template>
<el-alert
v-else
type="error"
:closable="false"
class="ksd-mt-8"
show-icon>
<span slot="title">{{$t('queryError')}}<span class="ky-a-like" @click="openErrorDialog(props.row.query_history_info.query_msg)">{{$t('viewDetails')}}</span></span>
</el-alert>
</div>
</el-col>
</el-row>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('kylinLang.query.startTime_th')" prop="query_time" width="218">
<template slot-scope="props">
{{transToGmtTime(props.row.query_time)}}
</template>
</el-table-column>
<el-table-column :label="$t('kylinLang.query.latency_th')" prop="duration" align="right" width="120">
<template slot-scope="props">
<span v-if="props.row.duration < 1000">&lt; 1s</span>
<span v-if="props.row.duration >= 1000">{{props.row.duration / 1000 | fixed(2)}}s</span>
<!-- <span v-if="props.row.query_status === 'FAILED'">Failed</span> -->
</template>
</el-table-column>
<el-table-column :label="$t('kylinLang.query.query_id')" prop="query_id" width="120" show-overflow-tooltip>
</el-table-column>
<el-table-column :label="$t('kylinLang.query.sqlContent_th')" prop="sql_limit" min-width="125">
<template slot-scope="props">
<el-popover
ref="sql-popover"
placement="top"
trigger="hover"
popper-class="col-sql-popover">
<div class="sql-column" slot="reference" @click="handleExpandType(props)">{{props.row.sql_limit}}</div>
<template>
<div class="sql-container">
<p class="popover-sql-content">{{props.row.sql_limit}}</p>
<div class="sql-tip" v-if="sqlOverLimit(props.row.sql_limit)">{{$t('sqlDetailTip')}}</div>
</div>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column
:label="$t('kylinLang.query.realization_th')"
prop="realizations"
width="250">
<template slot-scope="props">
<div class="tag-ellipsis" :class="{'hasMore': checkIsShowMore(props.row.realizations)}">
<template v-if="props.row.realizations && getRealizations2(props.row.realizations).length">
<span v-for="(item, index) in getRealizations2(props.row.realizations)" :key="item.modelId">
<el-tag :class="{'disabled': 'visible' in item && !item.visible}" v-if="index < checkShowCount(getRealizations2(props.row.realizations))" :type="'visible' in item && !item.visible ? 'info' : item.valid ? 'success' : 'info'" size="small">
<i class="el-icon-ksd-lock" v-if="'visible' in item && !item.visible"></i>{{item.modelAlias}}
</el-tag>
</span>>
<a v-if="checkIsShowMore(getRealizations2(props.row.realizations))" href="javascript:;" @click="handleExpandType(props, true)" class="showMore el-tag el-tag--small">{{$t('showDetail', {count: getRealizations2(props.row.realizations).length - checkShowCount(getRealizations2(props.row.realizations))})}}</a>
</template>
<template v-else>
<el-tag type="warning" size="small" v-if="props.row.engine_type&&props.row.engine_type!=='NATIVE'">{{props.row.engine_type}}</el-tag>
</template>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('kylinLang.query.query_status')" show-overflow-tooltip prop="query_status" width="130">
<template slot-scope="scope">
{{$t('kylinLang.query.' + scope.row.query_status)}}
</template>
</el-table-column>
<el-table-column :label="$t('kylinLang.query.queryNode')" show-overflow-tooltip prop="server" width="145">
</el-table-column>
<el-table-column
:label="$t('kylinLang.query.submitter')"
prop="submitter"
v-if="queryHistoryFilter.includes('filterActions')"
show-overflow-tooltip
width="110">
</el-table-column>
<el-table-column
:label="$t('kylinLang.query.submitter')"
prop="submitter"
v-if="!queryHistoryFilter.includes('filterActions')"
show-overflow-tooltip
width="110">
</el-table-column>
<el-table-column
v-if="(queryHistoryFilter.includes('filterActions') || isNonAdminGenQueryDiagPackage)"
:label="$t('kylinLang.common.action')"
width='70'>
<template slot-scope="scope">
<common-tip :content="$t('downloadQueryDiagnosticPackage')" >
<i class="el-icon-ksd-ostin_diagnose ksd-fs-14 ksd-ml-4" @click.stop="openQueryDialog(scope.row)"></i>
</common-tip>
</template>
</el-table-column>
</el-table>
<index-details :index-detail-title="indexDetailTitle" :detail-type="detailType" :cuboid-data="cuboidData" @close="closeDetailDialog" v-if="indexDetailShow" />
<diagnostic
v-if="showDiagnostic"
@close="showDiagnostic = false"
:jobId='queryId'
:queryServer='queryServer'
:jobStatus='jobStatus'
/>
<el-dialog
:visible.sync="queryErrorVisible"
width="600px"
class="query-error-dialog"
status-icon="el-ksd-icon-error_24"
:close-on-click-modal="false">
<span slot="title">{{$t('errorTitle')}}</span>
<pre class="error-block">{{errinfo}}</pre>
</el-dialog>
</div>
</template>
<script>
import { transToGmtTime, getStringLength, handleError } from '../../util/business'
import { handleSuccessAsync, isIE } from '../../util'
import Vue from 'vue'
import { mapActions, mapGetters } from 'vuex'
import { Component, Watch } from 'vue-property-decorator'
// import $ from 'jquery'
import { sqlRowsLimit, sqlStrLenLimit, formatSQLConfig, filterPagesize, maxFilterAndFilterValues } from '../../config/index'
// import { format } from 'sql-formatter'
import IndexDetails from '../studio/StudioModel/ModelList/ModelAggregate/indexDetails'
import Diagnostic from 'components/admin/Diagnostic/index'
import DropdownFilter from '../common/DropdownFilter/DropdownFilter.vue'
@Component({
name: 'QueryHistoryTable',
props: ['queryHistoryData', 'queryHistoryTotalSize', 'queryNodes', 'filterDirectData', 'isLoadingHistory'],
methods: {
transToGmtTime: transToGmtTime,
...mapActions({
markFav: 'MARK_FAV',
fetchHitModelsList: 'FETCH_HIT_MODELS_LIST',
fetchSubmitterList: 'FETCH_SUBMITTER_LIST',
loadAllIndex: 'LOAD_ALL_INDEX'
}),
...mapActions('DetailDialogModal', {
callGlobalDetailDialog: 'CALL_MODAL'
})
},
computed: {
...mapGetters([
'currentSelectedProject',
'briefMenuGet',
'queryHistoryFilter',
'isNonAdminGenQueryDiagPackage'
])
},
components: {
IndexDetails,
Diagnostic,
DropdownFilter
},
locales: {
'en': {
queryDetails: 'Query Details',
ruleDesc: 'Favorite Condition:<br/>Query Frequency (default by daily);<br/>Query Duration;<br/>From user/ user group;<br/>Pushdown Query.',
searchSQL: 'Search Query ID or SQL Statement',
noSpaceTips: 'Invalide entering: cannot search space',
sqlDetailTip: 'Please click sql to get more informations',
taskStatus: 'Task Status',
realization: 'Query',
clearAll: 'Clear All',
showDetail: 'More {count}',
SUCCEEDED: 'SUCCEEDED',
FAILED: 'FAILED',
pushdown: 'Pushdown',
model: 'Model',
modelName: 'All Models',
totalDuration: 'Total Duration',
PREPARATION: 'Preparation',
SQL_TRANSFORMATION: 'SQL transformation',
SQL_PARSE_AND_OPTIMIZE: 'SQL parser optimization',
HTTP_RECEPTION: 'Reception',
GET_ACL_INFO: 'ACL Checking',
MODEL_MATCHING: 'Model matching',
PREPARE_AND_SUBMIT_JOB: 'Creating and Submitting Spark job',
WAIT_FOR_EXECUTION: 'Waiting for resources',
EXECUTION: 'Executing',
FETCH_RESULT: 'Receiving result',
SPARK_JOB_EXECUTION: 'Spark Job Execution',
JOB_EXECUTION: 'Spark Job Execution',
SQL_PUSHDOWN_TRANSFORMATION: 'SQL pushdown transformation',
CONSTANT_QUERY: 'Constant query',
HIT_CACHE: 'Cache hit',
allModels: 'All',
searchAnsweredBy: 'Search by model name',
searchSubmitter: 'Search by submitter',
aggDetailTitle: 'Aggregate Detail',
tabelDetailTitle: 'Table Index Detail',
unExistLayoutTip: 'This index has been deleted',
filteredTotalSize: '{totalSize} result(s)',
secStorage: 'Tiered Storage',
streamingTag: 'streaming',
downloadQueryDiagnosticPackage: 'Download Query Diagnostic Package',
queryError: 'Query error.',
viewDetails: 'View Details',
errorTitle: 'Error Details',
fetchError: 'Can\'t get the result as the record is missing',
loadingTips: 'Up to 100 items',
totalSizeLabel: '{num} search results',
realizationFilterLengthTips: 'Exceed the selection limit. Please clear and reselect'
}
},
filters: {
filterNumbers (num) {
if (num >= 0) return num
}
}
})
export default class QueryHistoryTable extends Vue {
maxFilterAndFilterValues = maxFilterAndFilterValues
datetimerange = ''
startSec = 0
endSec = 10
latencyFilterPopoverVisible = false
realFilteArr = []
pushdownFilteArr = []
filterHandleChangeAll = false
filtePageOffset = 0
isShowLoading = false
submitterFilter = []
realizationFilters = []
plusFilter = []
searchCount = 0
modelCount = 0
filterData = {
startTimeFrom: null,
startTimeTo: null,
latencyFrom: null,
latencyTo: null,
realization: [],
exclude_realization: [],
submitter: [],
server: [],
sql: '',
query_status: []
}
timer = null
showCopyStatus = false
currentExpandId = ''
toggleExpandId = []
sqlLimitRows = 20 * 10
statusList = ['SUCCEEDED', 'FAILED']
filterTags = []
isShowDetail_PREPARATION = false // 展开查询步骤详情
isShowDetail_JOB_EXECUTION = false // 展开spark任务执行步骤详情
cuboidData = {}
indexDetailTitle = ''
indexDetailShow = false
detailType = ''
showDiagnostic = false
queryId = ''
queryServer = ''
jobStatus = ''
queryErrorVisible = false
errinfo = ''
openErrorDialog (errinfo) {
this.errinfo = errinfo
this.queryErrorVisible = true
}
renderStatusLabel (h, option) {
const { value } = option
return [
<span>{this.$t(value)}</span>
]
}
@Watch('queryHistoryData')
onQueryHistoryDataChange (val) {
val.forEach(element => {
const sql = element.sql_text
const sql_limit = this.sqlOverLimit(sql) ? `${sql.slice(0, this.sqlLimitRows)}...` : sql
const sqlTextArr = sql.split('\n') // 换行符超过一个,说明用户查询行自定义过format格式,则保留
element['sql_limit'] = (sqlTextArr.length > 1 || isIE()) ? sql_limit : this.handleFormatSql(sql_limit, formatSQLConfig)
element['server'] = [element['server']]
element['flexHeight'] = 0
element['editorH'] = 0
element['query_steps'] = element.query_history_info && this.getStepData(element.query_history_info.traces) || []
})
this.toggleExpandId = []
}
@Watch('filterDirectData.startTimeFrom')
onInitFilterData (v) {
this.initFilterData()
}
get isHasFilterValue () {
return this.filterData.sql || this.filterData.startTimeFrom || this.filterData.startTimeTo || this.filterData.latencyFrom || this.filterData.latencyTo || this.filterData.realization.length || this.filterData.query_status.length || this.filterData.server.length || this.filterData.submitter.length
}
get emptyText () {
return this.isHasFilterValue ? this.$t('kylinLang.common.noResults') : this.$t('kylinLang.common.noData')
}
get allHitModel () {
return {label: this.$t('allModels'), value: 'modelName', indeterminate: this.filterData.exclude_realization.length > 0, totalSize: this.modelCount, selectedSize: this.filterData.realization.includes('modelName') ? this.modelCount - this.filterData.exclude_realization.length : this.filterData.realization.filter(i => !this.pushdownFilteArr.includes(i)).length}
}
get realizationLabel () {
if (this.filterData.realization.length) {
if (this.filterData.realization.includes('modelName')) {
if (this.filterData.realization[0] === 'modelName') { // 过滤器第一个选择全部模型, 显示模型第一个名称+数字
return this.realizationFilters[0] !== 'modelName' ? `${this.realizationFilters[0]} +${this.modelCount - 1 - this.filterData.exclude_realization.length + this.filterData.realization.length - 1}` : `+${this.modelCount - this.filterData.exclude_realization.length + this.filterData.realization.length - 1}`
} else {
return `${this.filterData.realization[0]} +${this.filterData.realization.length - 1 - 1 + this.modelCount - this.filterData.exclude_realization.length}`
}
} else {
return this.filterData.realization.length > 1 ? `${this.filterData.realization[0]} +${this.filterData.realization.length - 1}` : this.filterData.realization[0]
}
} else {
return ''
}
}
// 排除击中 snapshot 的查询对象
getRealizations (row) {
return row.filter(item => item.indexType !== 'Table Snapshot' && item.layoutId !== 0 && item.layoutId !== -1)
}
getRealizations2 (row) {
return row.filter(item => !((item.layoutId === 0 || item.layoutId === -1 || item.layoutId === null) && item.indexType !== null))
}
handleFormatSql (sql_limit) {
try {
const sql = this._formatSql(sql_limit, formatSQLConfig)
return sql ?? sql_limit
} catch (e) {
return sql_limit
}
}
dateRangeChange () {
if (this.datetimerange.length) {
this.filterData.startTimeFrom = new Date(this.datetimerange[0]).getTime()
this.filterData.startTimeTo = new Date(this.datetimerange[1]).getTime()
this.clearDatetimeRange()
this.filterTags.push({label: `${this.transToGmtTime(this.filterData.startTimeFrom)} To ${this.transToGmtTime(this.filterData.startTimeTo)}`, source: 'kylinLang.query.startTime_th', key: 'datetimerange'})
} else {
this.filterData.startTimeFrom = null
this.filterData.startTimeTo = null
this.clearDatetimeRange()
}
}
get selectedRange () {
if (this.datetimerange && this.datetimerange[0] && this.datetimerange[1]) {
return `${this.transToGmtTime(this.filterData.startTimeFrom)} To ${this.transToGmtTime(this.filterData.startTimeTo)}`
}
return ''
}
handleCommand (command) {
this.plusFilter.push(command)
}
initFilterData () {
const { startTimeFrom, startTimeTo } = JSON.parse(JSON.stringify(this.filterDirectData))
if (!startTimeFrom || !startTimeTo) return
this.datetimerange = [startTimeFrom, startTimeTo]
this.dateRangeChange()
this.filterList()
}
fiterList (type, filterValue) {
this.timer = setTimeout(() => {
this[type](filterValue)
}, 200)
}
async loadFilterHitModelsList (filterValue) {
try {
const res = await this.fetchHitModelsList({ project: this.currentSelectedProject, model_name: filterValue, page_size: maxFilterAndFilterValues })
const data = await handleSuccessAsync(res)
this.pushdownFilteArr = data.engines
this.realFilteArr = data.models
if (this.filterData.realization.includes('modelName')) {
data.models.forEach(m => {
if (!this.filterData.exclude_realization.includes(m)) {
this.realizationFilters.push(m)
}
})
}
this.searchCount = data.search_count
this.modelCount = data.total_model_count
} catch (e) {
handleError(e)
}
}
get isFilterItemLoading () {
return this.paginationRealFilteArr.length < this.realFilteArr.length
}
get paginationRealFilteArr () {
return this.realFilteArr.slice(0, (this.filtePageOffset + 1) * filterPagesize)
}
scrollBottom () {
this.isShowLoading = true
setTimeout(() => {
this.isFilterItemLoading && this.filtePageOffset++
}, 200)
}
async loadFilterSubmitterList (filterValue) {
try {
const res = await this.fetchSubmitterList({ project: this.currentSelectedProject, submitter: filterValue, page_size: maxFilterAndFilterValues })
this.submitterFilter = await handleSuccessAsync(res)
} catch (e) {
handleError(e)
}
}
created () {
if (this.currentSelectedProject) {
if (this.queryHistoryFilter.includes('filterActions')) {
this.loadFilterSubmitterList()
}
this.loadFilterHitModelsList() // 普通用户也支持筛选查询对象
}
}
// 清除查询开始事件筛选项
clearDatetimeRange () {
if (this.filterTags.filter(item => item.key === 'datetimerange').length) {
let idx = null
for (let index in this.filterTags) {
if (this.filterTags[index].key === 'datetimerange') {
idx = index
break
}
}
this.filterTags.splice(idx, 1)
}
}
getProgress (duration, totalDuration) {
const popoverWidth = this.$lang === 'en' ? 340 : 320
const progressWidth = (popoverWidth - 30) * 0.25 // 减去padding宽度, span为6
const miniWidthRat = 0.5 / progressWidth
const dur = Math.round(duration / 1000 * 100) / 100 // 根据精确度保留两位来计算比例
let stepRat = Math.round(dur / (totalDuration / 1000) * 100) / 100
stepRat = stepRat > 1 ? 1 : stepRat // 精度问题导致有大于1的情况
if (stepRat < miniWidthRat) {
return miniWidthRat * 100
} else {
return stepRat * 100
}
}
getStepData (steps) {
if (steps && steps.length) {
let renderSteps = [
{name: 'totalDuration', duration: 0},
{name: 'PREPARATION', duration: 0}
]
let preStepNum = 0
let jobExecutionIndex = -1
steps.forEach((s) => {
renderSteps[0].duration = renderSteps[0].duration + s.duration
if (s.group === 'PREPARATION') {
preStepNum++
let preparationIndex = renderSteps.findIndex(item => item.name === 'PREPARATION')
renderSteps[preparationIndex].duration = renderSteps[preparationIndex].duration + s.duration
renderSteps.push(s)
} else if (s.group === 'JOB_EXECUTION') {
jobExecutionIndex = renderSteps.findIndex(item => item.name === 'JOB_EXECUTION')
if (jobExecutionIndex === -1) {
renderSteps.push({name: 'JOB_EXECUTION', duration: 0})
jobExecutionIndex = renderSteps.length - 1
}
renderSteps[jobExecutionIndex].duration = renderSteps[jobExecutionIndex].duration + s.duration
renderSteps.push(s)
} else if (s.name === 'HTTP_RECEPTION') {
renderSteps.splice(1, 0, {name: 'HTTP_RECEPTION', duration: s.duration})
} else {
renderSteps.push(s)
}
})
if (preStepNum === 0) {
renderSteps.splice(1, 1) // 击中缓存没有查询前置步骤
}
return renderSteps
} else {
return []
}
}
// 清除查询耗时筛选项
clearLatencyRange () {
if (this.filterTags.filter(item => item.key === 'latency').length) {
let idx = null
for (let index in this.filterTags) {
if (this.filterTags[index].key === 'latency') {
idx = index
break
}
}
idx !== null && this.filterTags.splice(idx, 1)
}
}
// 控制是否显示查看更多
checkIsShowMore (arr) {
let str = ''
if (arr && arr.length) {
// 如果将全部的模型合并后,超过21个字符,就肯定要显示更多
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
str = str + item.modelAlias
}
return arr.length > 1 && getStringLength(str) >= 21
} else {
return false
}
}
checkShowCount (arr) {
let str = ''
let idx = 1
if (arr && arr.length) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
str = str + item.modelAlias
// 如果第一个就超21个字符了,就直接返回下标1
if (i === 0 && getStringLength(str) >= 21) {
idx = 1
break
} else {
if (arr.length > 1 && getStringLength(str) >= 21) {
idx = i
break
} else { // 如果每次加上名字后,还是可以放得下,就让下标+1,保证都正常渲染
idx = i + 1
}
}
}
}
return idx
}
sqlOverLimit (sql) {
return sql && sql.length > this.sqlLimitRows
}
toggleDetail (group) {
this[`isShowDetail_${group}`] = !this[`isShowDetail_${group}`]
}
expandChange (e) {
this.isShowDetail_PREPARATION = false // 每次展开重置查询详情为隐藏
this.isShowDetail_JOB_EXECUTION = false
if (this.toggleExpandId.includes(e.query_id)) {
const index = this.toggleExpandId.indexOf(e.query_id)
this.toggleExpandId.splice(index, 1)
e.hightlight_realizations = false
return
}
this.currentExpandId = e.query_id
e.flexHeight = 0
e.editorH = 0
this.toggleExpandId.push(e.query_id)
this.$nextTick(() => {
// const tableHeigth = $('#detailTable_' + e.query_id) && $('#detailTable_' + e.query_id).height()
const tableHeigth = document.getElementById(`detailTable_${e.query_id}`) && document.getElementById(`detailTable_${e.query_id}`).offsetHeight
if (tableHeigth) {
e.flexHeight = e.flexHeight + tableHeigth
let showLimitTip = false
let sqlTextArr = e.sql_text.split('\n')
// 要手动传入高度
if ((sqlTextArr.length > 0 && sqlTextArr.length > sqlRowsLimit) || (sqlTextArr.length === 0 && e.sql_text.length > sqlStrLenLimit)) {
showLimitTip = true
}
e.editorH = showLimitTip ? (e.flexHeight - 32) : (e.flexHeight - 2)
}
this.currentExpandId = ''
})
}
handleExpandType (props, realizations) {
// 点击的时候根据当前是展开还是收起,进行展开收起
let flag = this.toggleExpandId.indexOf(props.row.query_id) === -1
if (realizations) {
props.row.hightlight_realizations = flag
}
this.$refs.queryHistoryTable.toggleRowExpansion(props.row, flag)
}
onCopy () {
this.showCopyStatus = true
setTimeout(() => {
this.showCopyStatus = false
}, 3000)
}
onError () {
this.$message(this.$t('kylinLang.common.copyfail'))
}
getLayoutIds (realizations) {
if (realizations && realizations.length) {
let filterIds = []
for (let i of realizations) {
if (i.layoutId !== -1 && i.layoutId !== null && i.layoutId !== 0) {
filterIds.push({layoutId: i.layoutId, streamingLayout: i.streamingLayout, secondStorage: i.secondStorage})
}
}
return filterIds.join(', ')
} else {
return ''
}
}
getSnapshots (realizations) {
if (realizations && realizations.length) {
let filterSnapshot = []
for (let i of realizations) {
if (i.snapshots && i.snapshots.length) {
filterSnapshot = [...filterSnapshot, ...i.snapshots]
}
}
filterSnapshot = [...new Set(filterSnapshot)]
return filterSnapshot.join(', ')
} else {
return ''
}
}
onSqlFilterChange () {
if (this.filterData.sql.trim().match(/\s/)) {
this.$message({
message: this.$t('noSpaceTips'),
type: 'warning',
duration: 10000,
showClose: true
})
}
this.filterList()
}
exportHistory (isExportSqlOnly) {
if (!this.queryHistoryTotalSize) return
this.$emit('exportHistory', isExportSqlOnly)
}
filterList () {
this.toggleExpandId = []
this.$emit('loadFilterList', {...this.filterData, server: this.filterData.server.join('')})
}
openIndexDialog (realization, rows) {
if (!realization.valid || realization.indexType === 'Table Snapshot') return
this.$emit('openIndexDialog', realization, rows)
}
handleInputDateRange (val) {
this.datetimerange = val
this.dateRangeChange()
this.filterList()
}
handleChangeAll () {
this.filterHandleChangeAll = true
}
// 查询状态过滤回调函数
filterContent (val, type) {
if (type === 'latency') {
this.filterData['latencyFrom'] = val[0]
this.filterData['latencyTo'] = val[1]
this.filterList()
} else if (type === 'realization') {
setTimeout(() => {
if (val.includes('modelName') && this.filterHandleChangeAll) { // 选择全部模型
this.realizationFilters = [...this.realFilteArr, ...val]
this.filterData.realization = [...val]
} else if (!val.includes('modelName') && this.filterHandleChangeAll) { // 取消全部模型
this.realizationFilters = [...this.filterData.realization].filter(i => i !== 'modelName')
this.filterData.realization = [...this.filterData.realization].filter(i => i !== 'modelName')
this.filterData.exclude_realization = []
} else {
if (val.includes('modelName')) { // 操作其他选项时,有全选模型,模型名称的进入反选数组
this.realizationFilters = [...val]
this.filterData.realization = [...val].filter(i => this.pushdownFilteArr.includes(i) || i === 'modelName')
this.filterData.exclude_realization = this.filterData.exclude_realization.filter(i => !this.realizationFilters.includes(i)) // 全选情况下,手动勾选的模型是取消反选的操作
this.filterData.exclude_realization = Array.from(new Set([...this.filterData.exclude_realization, ...this.realFilteArr.filter(i => !val.includes(i))]))
} else {
this.realizationFilters = [...val]
this.filterData.realization = [...val]
this.filterData.exclude_realization = []
}
}
if (this.filterData.realization.length > maxFilterAndFilterValues || this.filterData.exclude_realization.length > maxFilterAndFilterValues) {
this.$message({
message: this.$t('realizationFilterLengthTips'),
type: 'warning',
duration: 10000,
showClose: true
})
if (val.length > maxFilterAndFilterValues) {
this.realizationFilters = val.slice(0, val.length - 1)
this.filterData.realization = val.slice(0, val.length - 1)
} else {
this.realizationFilters.push(this.realFilteArr.filter(i => !val.includes(i))[0])
this.filterData.exclude_realization = this.filterData.exclude_realization.slice(0, this.filterData.exclude_realization.length - 1)
}
return
}
this.filterList()
})
} else {
this.filterData[type] = val
this.filterList()
}
this.filterHandleChangeAll = false
}
// 删除单个筛选条件
handleClose (tag) {
if (tag.key === 'datetimerange') {
this.datetimerange = ''
this.dateRangeChange()
} else if (tag.key === 'latency') {
this.filterData.latencyFrom = null
this.filterData.latencyTo = null
this.clearLatencyRange()
} else if (tag.key === 'server') {
this.filterData.server.splice(0, 1)
const index = this.filterTags.map(item => item.key).indexOf('server')
this.filterTags.splice(index, 1)
} else {
let delLabel = tag.label
if (tag.key === 'realization' && tag.label === 'allModels') {
delLabel = 'modelName'
}
const index = this.filterData[tag.key].indexOf(delLabel)
index > -1 && this.filterData[tag.key].splice(index, 1)
this.filterTags = this.filterTags.filter(item => item.key !== tag.key || item.key === tag.key && tag.label !== item.label)
}
this.filterList()
}
// 清除所有筛选条件
clearAllTags () {
this.filterData.query_status.splice(0, this.filterData.query_status.length)
this.filterData.realization.splice(0, this.filterData.realization.length)
this.filterData.exclude_realization.splice(0, this.filterData.exclude_realization.length)
this.filterData.server.splice(0, this.filterData.server.length)
this.filterData.submitter.splice(0, this.filterData.submitter.length)
this.filterData.latencyFrom = null
this.filterData.latencyTo = null
this.datetimerange = ''
this.filterTags = []
this.realizationFilters = []
this.dateRangeChange()
this.filterList()
}
openAuthorityDialog (item) {
const { unauthorized_tables, unauthorized_columns, modelAlias } = item
let details = []
if (unauthorized_tables && unauthorized_tables.length) {
details.push({title: `Table (${unauthorized_tables.length})`, list: unauthorized_tables})
}
if (unauthorized_columns && unauthorized_columns.length) {
details.push({title: `Columns (${unauthorized_columns.length})`, list: unauthorized_columns})
}
this.callGlobalDetailDialog({
theme: 'plain-mult',
title: this.$t('kylinLang.model.authorityDetail'),
msg: this.$t('kylinLang.model.authorityMsg', {modelName: modelAlias}),
showCopyBtn: true,
showIcon: false,
showDetailDirect: true,
details,
showDetailBtn: false,
dialogType: 'error',
customClass: 'no-acl-model',
showCopyTextLeftBtn: true
})
}
// 展示 layout 详情
async openLayoutDetails (item) {
if (!item.layoutExist) return
const {modelId, layoutId} = item
try {
const res = await this.loadAllIndex({
project: this.currentSelectedProject,
model: modelId,
key: layoutId,
page_offset: 0,
page_size: 10,
sort_by: '',
reverse: '',
sources: [],
status: []
})
const data = await handleSuccessAsync(res)
let [row = {}] = data.value.filter(it => it.id === layoutId)
this.cuboidData = row
let idStr = (row.id !== undefined) && (row.id !== null) && (row.id !== '') ? ' [' + row.id + ']' : ''
this.detailType = row.source.indexOf('AGG') >= 0 ? 'aggDetail' : 'tabelIndexDetail'
this.indexDetailTitle = row.source.indexOf('AGG') >= 0 ? this.$t('aggDetailTitle') + idStr : this.$t('tabelDetailTitle') + idStr
this.indexDetailShow = true
// this.indexLoading = false
} catch (e) {
handleError(e)
}
}
// 关闭 layout 详情
closeDetailDialog () {
this.indexDetailShow = false
}
openQueryDialog (row) {
this.queryId = row.query_id
this.queryServer = row.server[0]
this.jobStatus = row.job_status
this.showDiagnostic = true
}
}
</script>
<style lang="less">
@import '../../assets/styles/variables.less';
#queryHistoryTable {
margin-top: 32px;
/* table.ksd-table{
tr:nth-child(odd){
background: @table-stripe-color;
}
} */
.actions {
line-height: 22px;
// border-right: 1px solid @ke-border-divider-color;
margin: 6px 8px 0 0;
padding-right: 4px;
height: 22px;
display: inline-block;
.reset-filters-btn.is-disabled {
i {
cursor: not-allowed;
}
}
}
.table-filters {
>.dropdown-filter {
margin-left: -8px;
}
}
.el-table__expanded-cell {
padding: 24px;
.copy-btn {
margin-right: 9%;
.copyStatusMsg {
display: inline-block;
color: @text-normal-color;
.el-icon-circle-check {
color: @normal-color-1;
}
}
}
.detail-title {
/* border-bottom: 1px solid @line-border-color; */
overflow: hidden;
padding-bottom: 10px;
span:first-child {
line-height: 18px;
font-weight: @font-medium;
}
span:last-child {
color: @text-normal-color;
}
}
.detail-content {
line-height: 1.8;
.el-col {
position: relative;
.history_detail_table{
.duration {
color: @base-color;
cursor: pointer;
}
tr.active{
background-color: @base-color-9;
}
}
}
}
}
.searchInput {
width: 260px;
}
.export-btn .is-disabled .el-button-group .el-button{
color: @text-disabled-color;
cursor: not-allowed;
background-image: none;
background-color: @line-border-color4;
border-color: @line-border-color3;
}
.history-table {
th .el-dropdown {
padding: 0;
line-height: 0;
position: relative;
left: 5px;
top: 2px;
.el-ksd-icon-filter_22 {
float: none;
position: relative;
left: 0px;
}
}
.sql-column {
cursor: pointer;
width: calc(100%);
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
/*! autoprefixer: off */
-webkit-box-orient: vertical;
/* autoprefixer: on */
white-space: nowrap\0;
&:hover {
color: @base-color;
}
}
.ksd-table th {
width: 140px;
color:@text-normal-color;
}
.tag-ellipsis {
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
font-size: 0;
line-height: 1;
display:flex;
align-items: center;
/* position:relative; */
&.hasMore{
.el-tag{
max-width: calc(~"100% - 85px");
&.showMore{
padding-left: 7px;
width:auto;
max-width: 80px;
min-width: 60px;
}
}
}
.el-tag:not(:last-child) {
margin-right: 5px;
}
.el-tag{
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
&.disabled {
border: solid 1px @text-placeholder-color;
background-color: @background-disabled-color;
color: @text-disabled-color;
.el-icon-ksd-lock {
margin-right: 3px;
}
}
}
.showMore{
width:auto;
max-width: 80px;
min-width: 60px;
padding-left: 7px;
text-overflow: ellipsis;
font-size:12px;
line-height: 20px;
height:20px;
border-radius: 10px;
box-sizing: border-box;
display:inline-block;
border: none;
&:hover{
text-decoration: none;
cursor:pointer;
}
}
/* .el-tag.realizationsNumTag{
&:hover{
cursor:pointer;
}
} */
}
.realization-tags {
// display: flex;
// flex-wrap: wrap;
/* .el-tag {
margin: 2.5px 10px 2.5px 0;
border: none;
background: none;
padding: 0;
height: 16px;
line-height: 16px;
&.model-tag {
cursor: pointer;
}
} */
.model-tag {
color: @base-color;
cursor: pointer;
}
.disable{
color: @text-disabled-color;
cursor: default;
}
.split{
margin-right:10px;
}
.no-authority-model {
color: @text-disabled-color;
cursor: pointer;
&:hover {
color: @base-color;
}
.el-icon-ksd-lock {
margin-right: 3px;
}
}
}
.realizations-layout-id {
align-items: center;
display: inline-flex;
line-height: 16px;
.el-tag__text {
cursor: default !important;
}
span:first-child {
color: @base-color;
cursor: pointer;
}
&.is-disabled {
color: @text-normal-color;
cursor: default;
}
}
.el-date-editor {
line-height: 1;
padding: 0;
position: relative;
top: 3px;
left: 5px;
}
.el-ksd-icon-data_range_old {
position: relative;
top: 1px;
&.isFilter,
&:hover {
color: @base-color;
}
}
.el-icon-ksd-negative {
color: @text-normal-color;
font-size: 20px;
&:hover {
color: @base-color;
}
}
.status-icon {
font-size: 20px;
&.el-icon-ksd-acclerate_all,
&.el-icon-ksd-acclerate_portion {
color: @normal-color-1;
}
}
.el-ksd-icon-filter_22 {
// position: relative;
// font-size: 17px;
// top: 1px;
// left: 5px;
&:hover,
&.filter-open {
color: @base-color;
}
}
}
}
.query-error-dialog {
.error-block {
height: 200px;
overflow-y: auto;
background-color: @ke-background-color-secondary;
border: 1px solid @ke-border-secondary;
padding: 10px;
border-radius: 4px;
}
.el-dialog__body {
padding-bottom: 32px !important;
}
}
.duration-popover {
line-height: 1.5 !important;
.step-name {
color: @text-normal-color;
&.sub-step {
color: @color-text-placeholder;
}
}
.step-duration {
color: @color-text-primary;
&.sub-step {
color: @color-text-placeholder;
}
}
.el-icon-ksd-more_01 {
transform: scale(0.6);
&.up {
-webkit-transform: rotate(180deg) scale(0.6);
-moz-transform: rotate(180deg) scale(0.6);
-o-transform: rotate(180deg) scale(0.6);
-ms-transform: rotate(180deg) scale(0.6);
transform: rotate(180deg) scale(0.6);
}
}
.el-progress {
top: 3px;
}
.el-progress-bar__outer {
border-radius: 0;
background-color: transparent;
position: relative;
top: 5px;
width: 90%;
margin-left: 10px;
}
.el-progress-bar__inner {
border-radius: 0;
}
}
.el-popover.col-sql-popover {
max-width: 400px;
box-sizing: border-box;
padding: 0;
.sql-container {
max-height: 300px;
overflow-y: auto;
padding: 16px;
}
.popover-sql-content {
white-space: pre-wrap;
word-break: break-all;
}
}
.sql-tip {
text-align: center;
margin-top: 5px;
}
&.el-icon-ksd-acclerate_all,
&.el-icon-ksd-acclerate_portion {
color: @normal-color-1;
}
.to_acce {
font-size: 12px;
line-height: 1.5;
color: @text-title-color;
}
.latency-filter-pop {
display: inline-flex;
align-items: center;
.el-input-number--medium {
width: 120px;
margin-left: 10px;
&:first-child {
margin-left: 0;
}
}
}
.latency-filter-footer {
border-top: 1px solid @ke-border-divider-color;
padding: 10px 10px 0;
margin: 10px -10px 0;
text-align: right;
}
.filter-groups .el-checkbox {
display: block;
margin-bottom: 8px;
margin-left: 0px !important;
&:last-child {
margin-bottom: 0;
}
}
.el-popover.history-filter {
min-width: 130px;
box-sizing: border-box;
}
.loading {
width: calc(~'100% - 13px');
height: 254px;
position: absolute;
z-index: 10;
background: @fff;
.el-icon-loading {
font-size: 20px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%)
}
}
.filter-tags {
margin-bottom: 10px;
box-sizing: border-box;
position: relative;
.filter-tags-layout {
display: inline-block;
width: calc(~'100% - 80px');
.clear-all-filters {
color: @base-color;
margin-left: 4px;
position: relative;
top: 0px;
display: inline-block;
cursor: pointer;
font-size: 12px;
}
}
.el-tag {
margin-right: 4px;
margin-top: 6px;
}
.filter-queries-size {
position: absolute;
top: 8px;
right: 8px;
font-size: 12px;
color: @text-title-color;
}
}
.filter-submitter {
.el-checkbox-group {
max-height: 205px;
overflow: auto;
}
i {
margin-right: 4px;
color: @text-normal-color;
}
.el-checkbox__input.is-checked+.el-checkbox__label i {
color: @base-color;
}
}
.filter-realization i {
margin-right: 4px;
}
</style>