227 lines
6.6 KiB
Vue
227 lines
6.6 KiB
Vue
<script setup>
|
|
import { computed, h, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
|
import Refresh from '@/components/icons/Refresh.vue'
|
|
import { debounce, isEmpty, map, size, split } from 'lodash'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { NIcon, useThemeVars } from 'naive-ui'
|
|
import dayjs from 'dayjs'
|
|
import useBrowserStore from 'stores/browser.js'
|
|
import { timeout } from '@/utils/promise.js'
|
|
import AutoRefreshForm from '@/components/common/AutoRefreshForm.vue'
|
|
|
|
const themeVars = useThemeVars()
|
|
|
|
const browserStore = useBrowserStore()
|
|
const i18n = useI18n()
|
|
const props = defineProps({
|
|
server: {
|
|
type: String,
|
|
},
|
|
})
|
|
|
|
const autoRefresh = reactive({
|
|
on: false,
|
|
interval: 5,
|
|
})
|
|
|
|
const data = reactive({
|
|
list: [],
|
|
sortOrder: 'descend',
|
|
listLimit: 20,
|
|
loading: false,
|
|
client: '',
|
|
keyword: '',
|
|
})
|
|
|
|
const tableRef = ref(null)
|
|
|
|
const columns = computed(() => [
|
|
{
|
|
title: () => i18n.t('slog.exec_time'),
|
|
key: 'timestamp',
|
|
sortOrder: data.sortOrder,
|
|
sorter: 'default',
|
|
width: 180,
|
|
align: 'center',
|
|
titleAlign: 'center',
|
|
render: ({ timestamp }, index) => {
|
|
return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
|
|
},
|
|
},
|
|
{
|
|
title: () => i18n.t('slog.client'),
|
|
key: 'client',
|
|
filterOptionValue: data.client,
|
|
resizable: true,
|
|
filter: (value, row) => {
|
|
return value === '' || row.client === value.toString() || row.addr === value.toString()
|
|
},
|
|
width: 200,
|
|
align: 'center',
|
|
titleAlign: 'center',
|
|
ellipsis: {
|
|
tooltip: {
|
|
style: {
|
|
maxWidth: '50vw',
|
|
maxHeight: '50vh',
|
|
},
|
|
scrollable: true,
|
|
},
|
|
},
|
|
render: ({ client, addr }, index) => {
|
|
let content = ''
|
|
if (!isEmpty(client)) {
|
|
content += client
|
|
}
|
|
if (!isEmpty(addr)) {
|
|
if (!isEmpty(content)) {
|
|
content += ' - '
|
|
}
|
|
content += addr
|
|
}
|
|
return content
|
|
},
|
|
},
|
|
{
|
|
title: () => i18n.t('slog.cmd'),
|
|
key: 'cmd',
|
|
titleAlign: 'center',
|
|
filterOptionValue: data.keyword,
|
|
resizable: true,
|
|
filter: (value, row) => {
|
|
return value === '' || !!~row.cmd.indexOf(value.toString())
|
|
},
|
|
render: ({ cmd }, index) => {
|
|
const cmdList = split(cmd, '\n')
|
|
if (size(cmdList) > 1) {
|
|
return h(
|
|
'div',
|
|
null,
|
|
map(cmdList, (c) => h('div', { class: 'cmd-line' }, c)),
|
|
)
|
|
}
|
|
return h('div', { class: 'cmd-line' }, cmd)
|
|
},
|
|
},
|
|
{
|
|
title: () => i18n.t('slog.cost_time'),
|
|
key: 'cost',
|
|
width: 100,
|
|
align: 'center',
|
|
titleAlign: 'center',
|
|
render: ({ cost }, index) => {
|
|
const ms = dayjs.duration(cost).asMilliseconds()
|
|
if (ms < 1000) {
|
|
return `${ms} ms`
|
|
} else {
|
|
return `${Math.floor(ms / 1000)} s`
|
|
}
|
|
},
|
|
},
|
|
])
|
|
|
|
const _loadSlowLog = () => {
|
|
data.loading = true
|
|
browserStore
|
|
.getSlowLog(props.server, data.listLimit)
|
|
.then((list) => {
|
|
data.list = list || []
|
|
})
|
|
.finally(async () => {
|
|
data.loading = false
|
|
await nextTick()
|
|
tableRef.value?.scrollTo({ position: data.sortOrder === 'ascend' ? 'bottom' : 'top' })
|
|
})
|
|
}
|
|
const loadSlowLog = debounce(_loadSlowLog, 1000, { leading: true, trailing: true })
|
|
|
|
const startAutoRefresh = async () => {
|
|
let lastExec = Date.now()
|
|
do {
|
|
if (!autoRefresh.on) {
|
|
break
|
|
}
|
|
await timeout(100)
|
|
if (data.loading || Date.now() - lastExec < autoRefresh.interval * 1000) {
|
|
continue
|
|
}
|
|
lastExec = Date.now()
|
|
loadSlowLog()
|
|
} while (true)
|
|
stopAutoRefresh()
|
|
}
|
|
|
|
const stopAutoRefresh = () => {
|
|
autoRefresh.on = false
|
|
}
|
|
|
|
onMounted(() => loadSlowLog())
|
|
|
|
onUnmounted(() => stopAutoRefresh())
|
|
|
|
const onToggleRefresh = (on) => {
|
|
if (on) {
|
|
startAutoRefresh()
|
|
} else {
|
|
stopAutoRefresh()
|
|
}
|
|
}
|
|
|
|
const onListLimitChanged = (limit) => {
|
|
loadSlowLog()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="content-log content-container content-value fill-height flex-box-v">
|
|
<n-form :disabled="data.loading" class="flex-item" inline>
|
|
<n-form-item :label="$t('slog.limit')">
|
|
<n-input-number
|
|
v-model:value="data.listLimit"
|
|
:min="1"
|
|
:max="9999"
|
|
style="width: 120px"
|
|
@update:value="onListLimitChanged" />
|
|
</n-form-item>
|
|
<n-form-item :label="$t('slog.filter')">
|
|
<n-input v-model:value="data.keyword" clearable placeholder="" />
|
|
</n-form-item>
|
|
<n-form-item label=" ">
|
|
<n-popover :delay="500" keep-alive-on-hover placement="bottom" trigger="hover">
|
|
<template #trigger>
|
|
<n-button :loading="data.loading" circle size="small" tertiary @click="_loadSlowLog">
|
|
<template #icon>
|
|
<n-icon :size="props.size">
|
|
<refresh
|
|
:class="{ 'auto-rotate': autoRefresh.on }"
|
|
:color="autoRefresh.on ? themeVars.primaryColor : undefined"
|
|
:stroke-width="autoRefresh.on ? 6 : 3" />
|
|
</n-icon>
|
|
</template>
|
|
</n-button>
|
|
</template>
|
|
<auto-refresh-form
|
|
v-model:interval="autoRefresh.interval"
|
|
v-model:on="autoRefresh.on"
|
|
:default-value="5"
|
|
:loading="data.loading"
|
|
@toggle="onToggleRefresh" />
|
|
</n-popover>
|
|
</n-form-item>
|
|
</n-form>
|
|
<n-data-table
|
|
ref="tableRef"
|
|
:columns="columns"
|
|
:data="data.list"
|
|
:loading="data.loading"
|
|
class="flex-item-expand"
|
|
flex-height
|
|
striped
|
|
@update:sorter="({ order }) => (data.sortOrder = order)" />
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/styles/content';
|
|
</style>
|