| <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">< 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> |