mirror of
https://github.com/gotoeasy/glogcenter.git
synced 2025-09-15 12:58:34 +08:00
v0.15.0
This commit is contained in:
parent
2eefa7ea8f
commit
0ed4b55c65
@ -2,6 +2,7 @@ package ldb
|
||||
|
||||
import (
|
||||
"glc/com"
|
||||
"glc/conf"
|
||||
"glc/ldb/search"
|
||||
"glc/ldb/storage"
|
||||
"glc/ldb/storage/indexword"
|
||||
@ -152,7 +153,49 @@ func (e *Engine) Search(cond *search.SearchCondition) *search.SearchResult {
|
||||
rs = search.SearchLogData(e.storeName, cond)
|
||||
} else {
|
||||
// 多关键词查询模式
|
||||
rs = search.SearchWordIndex(e.storeName, cond)
|
||||
if cond.NewNearId > 0 {
|
||||
// 相邻检索模式的判断及条件准备
|
||||
max := conf.GetPageSize()
|
||||
leftSize := 20
|
||||
cond.CurrentId = cond.NewNearId
|
||||
isForward := cond.OldNearId < 1 || cond.NewNearId < cond.OldNearId // 是否向下检索更多的旧日志
|
||||
|
||||
// 搜索更多的新数据
|
||||
// 反向检索前x条
|
||||
cond.Forward = false
|
||||
if isForward {
|
||||
cond.SearchSize = leftSize
|
||||
} else {
|
||||
cond.SearchSize = max - leftSize
|
||||
}
|
||||
rs = search.SearchWordIndex(e.storeName, cond)
|
||||
// 定位日志1条固定(不管是否满足检索条件)
|
||||
ldm := search.GetLogDataModelById(cond.NearStoreName, cond.NewNearId)
|
||||
// 正向检索剩余件数
|
||||
cond.Forward = true
|
||||
cond.SearchSize = max - len(rs.Data)
|
||||
rsForward := search.SearchWordIndex(e.storeName, cond)
|
||||
|
||||
// 正向件数不够,反向重查来凑
|
||||
if len(rsForward.Data) < cond.SearchSize && len(rs.Data) == leftSize {
|
||||
cond.Forward = false
|
||||
cond.SearchSize = max - len(rsForward.Data)
|
||||
rs = search.SearchWordIndex(e.storeName, cond)
|
||||
}
|
||||
|
||||
// 拼接检索结果
|
||||
rs.Data = append(rs.Data, ldm)
|
||||
rs.Data = append(rs.Data, rsForward.Data...)
|
||||
// 检索结果最大匹配件数修正
|
||||
if len(rs.Data)-1 < max {
|
||||
rs.Count = cmn.IntToString(len(rs.Data) - 1)
|
||||
}
|
||||
|
||||
} else {
|
||||
// 普通检索
|
||||
rs = search.SearchWordIndex(e.storeName, cond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if maxMatchCount < cmn.StringToUint32(rs.Count, 0) {
|
||||
|
||||
@ -28,6 +28,9 @@ type SearchCondition struct {
|
||||
CurrentStoreName string // 隐藏条件,当前日志文档ID所属的日志仓
|
||||
CurrentId uint32 // 隐藏条件,当前日志文档ID
|
||||
Forward bool // 隐藏条件,是否向前检索(玩下滚动查询)
|
||||
OldNearId uint32 // 隐藏条件,相邻检索时的旧ID
|
||||
NewNearId uint32 // 隐藏条件,相邻检索时的新ID
|
||||
NearStoreName string // 隐藏条件,相邻检索时新ID对应的日志仓
|
||||
Kws []string // 【内部用】解析条件所得的检索关键词,非直接输入的检索文本
|
||||
SearchSize int // 【内部用】需要查询多少件(跨仓检索时可能多次检索,中间会内部调整)
|
||||
Systems []string // 【内部用】有权限的系统名(一定有值,第一个元素是“*”时表示全部)
|
||||
@ -98,6 +101,14 @@ func SearchWordIndex(storeName string, cond *SearchCondition) *SearchResult {
|
||||
return findSame(cond, minDocumentId, maxDocumentId, storeLogData, widxs...)
|
||||
}
|
||||
|
||||
// 按ID查取日志
|
||||
func GetLogDataModelById(storeName string, id uint32) *logdata.LogDataModel {
|
||||
storeLogData := storage.NewLogDataStorageHandle(storeName) // 数据
|
||||
ldm := storeLogData.GetLogDataModel(id)
|
||||
ldm.StoreName = storeName // 日志仓名称未序列化,前端要使用,这里补足赋值
|
||||
return ldm
|
||||
}
|
||||
|
||||
// 无关键词时走全量检索
|
||||
func SearchLogData(storeName string, cond *SearchCondition) *SearchResult {
|
||||
allloglevels := cmn.Join(cond.Loglevels, ",") // 合并多选的级别条件
|
||||
@ -193,6 +204,7 @@ func SearchLogData(storeName string, cond *SearchCondition) *SearchResult {
|
||||
}
|
||||
|
||||
md := storeLogData.GetLogDataDocument(i).ToLogDataModel()
|
||||
md.StoreName = storeLogData.GetStoreName() // 日志仓名称未序列化,前端要使用,这里补足赋值
|
||||
if noLogLevels || cmn.ContainsIngoreCase(allloglevels, md.LogLevel) {
|
||||
rs.Data = append(rs.Data, md)
|
||||
rsCnt++
|
||||
@ -251,6 +263,7 @@ func SearchLogData(storeName string, cond *SearchCondition) *SearchResult {
|
||||
}
|
||||
|
||||
md := storeLogData.GetLogDataDocument(i).ToLogDataModel()
|
||||
md.StoreName = storeLogData.GetStoreName() // 日志仓名称未序列化,前端要使用,这里补足赋值
|
||||
if noLogLevels || cmn.ContainsIngoreCase(allloglevels, md.LogLevel) {
|
||||
rs.Data = append(rs.Data, md)
|
||||
rsCnt++
|
||||
@ -306,6 +319,7 @@ func SearchLogData(storeName string, cond *SearchCondition) *SearchResult {
|
||||
}
|
||||
|
||||
md := storeLogData.GetLogDataDocument(i).ToLogDataModel()
|
||||
md.StoreName = storeLogData.GetStoreName() // 日志仓名称未序列化,前端要使用,这里补足赋值
|
||||
if noLogLevels || cmn.ContainsIngoreCase(allloglevels, md.LogLevel) {
|
||||
rs.Data = append(rs.Data, md)
|
||||
rsCnt++
|
||||
@ -355,11 +369,52 @@ func findSame(cond *SearchCondition, minDocumentId uint32, maxDocumentId uint32,
|
||||
}
|
||||
|
||||
// 找匹配位置并排除没结果的情景
|
||||
pos := totalCount // 默认检索最新第一页
|
||||
if cond.CurrentId > 0 {
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, cond.CurrentId) // 有相对文档ID时找相对位置
|
||||
if pos == 0 || (pos == 1 && cond.Forward) || (pos == totalCount && !cond.Forward) {
|
||||
return rs // 找不到、或最后条还要向后、或最前条还要向前,都是找不到
|
||||
var pos uint32
|
||||
if cond.Forward {
|
||||
pos = totalCount // 向后检索更加旧的日志,游标从大开始递降
|
||||
if cond.NewNearId > 0 {
|
||||
// 相邻检索
|
||||
nearId := cond.NewNearId - 1 // 查找比定位日志小的旧日志
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, nearId) // 有相对文档ID时找相对位置
|
||||
for pos == 0 && nearId >= minDocumentId {
|
||||
nearId--
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, nearId)
|
||||
}
|
||||
if pos == 0 {
|
||||
return rs // 找不到
|
||||
}
|
||||
pos++ // 找到,后续会减1排除本条找到的日志,但相邻检索时本条日志是需要的,所以加1处理
|
||||
} else {
|
||||
// 普通检索
|
||||
if cond.CurrentId > 0 {
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, cond.CurrentId) // 有相对文档ID时找相对位置
|
||||
if pos <= 1 {
|
||||
return rs // 找不到、或最后条还要向后
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pos = 1 // 向前检索更加新的日志,游标从小开始递增
|
||||
if cond.NewNearId > 0 {
|
||||
// 相邻检索
|
||||
nearId := cond.NewNearId + 1 // 查找比定位日志大的新日志
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, nearId) // 有相对文档ID时找相对位置
|
||||
for pos == 0 && nearId <= maxDocumentId {
|
||||
nearId++
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, nearId)
|
||||
}
|
||||
if pos == 0 {
|
||||
return rs // 找不到
|
||||
}
|
||||
pos-- // 找到,后续会加1排除本条找到的日志,但相邻检索时本条日志是需要的,所以减1处理
|
||||
} else {
|
||||
// 普通检索
|
||||
if cond.CurrentId > 0 {
|
||||
pos = minIdx.idxdocStorage.GetWordDocSeq(minIdx.word, cond.CurrentId) // 有相对文档ID时找相对位置
|
||||
if pos == 0 || pos == totalCount {
|
||||
return rs // 找不到、或最前条还要向前
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,6 +484,7 @@ func findSame(cond *SearchCondition, minDocumentId uint32, maxDocumentId uint32,
|
||||
// 找到则加入结果
|
||||
if flg {
|
||||
md := storeLogData.GetLogDataModel(docId)
|
||||
md.StoreName = storeLogData.GetStoreName() // 日志仓名称未序列化,前端要使用,这里补足赋值
|
||||
if noLogLevels || cmn.ContainsIngoreCase(allloglevels, md.LogLevel) {
|
||||
rsCnt++
|
||||
rs.Data = append(rs.Data, md)
|
||||
@ -444,10 +500,11 @@ func findSame(cond *SearchCondition, minDocumentId uint32, maxDocumentId uint32,
|
||||
tmpMinPos-- // 当前最短索引可能不变,得正常减1,若变化则会被覆盖没有关系
|
||||
}
|
||||
} else {
|
||||
// 有相对文档ID且是前一页方向(注:暂未使用,未经测试)
|
||||
// 有相对文档ID且是前一页方向
|
||||
pos++
|
||||
var ary []*logdata.LogDataModel
|
||||
for i := pos; i <= totalCount; i++ {
|
||||
total := storeLogData.TotalCount() // 当前日志最大件数
|
||||
for i := pos; i <= total; i++ {
|
||||
// 取值
|
||||
docId := minIdx.idxwordStorage.GetDocId(minIdx.word, i)
|
||||
|
||||
@ -500,6 +557,7 @@ func findSame(cond *SearchCondition, minDocumentId uint32, maxDocumentId uint32,
|
||||
// 找到则加入结果
|
||||
if flg {
|
||||
md := storeLogData.GetLogDataModel(docId)
|
||||
md.StoreName = storeLogData.GetStoreName() // 日志仓名称未序列化,前端要使用,这里补足赋值
|
||||
if noLogLevels || cmn.ContainsIngoreCase(allloglevels, md.LogLevel) {
|
||||
rsCnt++
|
||||
ary = append(ary, md)
|
||||
|
||||
@ -166,7 +166,7 @@ func (s *WordIndexStorage) Add(word string, docId uint32) error {
|
||||
}
|
||||
|
||||
// 保存建好的索引数
|
||||
s.setTotalCount(word, seq)
|
||||
err = s.setTotalCount(word, seq)
|
||||
if err != nil {
|
||||
cmn.Error("保存关键词反向索引件数失败", err)
|
||||
return err // 忽略事务问题,可下回重建
|
||||
|
||||
@ -25,6 +25,7 @@ type LogDataModel struct {
|
||||
LogLevel string `json:"loglevel,omitempty"` // 日志级别(debug、info、warn、error)
|
||||
User string `json:"user,omitempty"` // 用户
|
||||
Detail string `json:"detail,omitempty"` // 【内部字段】多行时的详细日志信息,通常是包含错误堆栈等的日志内容
|
||||
StoreName string `json:"storename,omitempty"` // 日志仓名称(未存储,仅赋值给前端使用)
|
||||
}
|
||||
|
||||
func (d *LogDataModel) ToJson() string {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package ver
|
||||
|
||||
// 版本号,升级版本时修改
|
||||
const VERSION = "v0.14.2"
|
||||
const VERSION = "v0.15.0"
|
||||
|
||||
@ -44,6 +44,9 @@ func LogSearchController(req *gweb.HttpRequest) *gweb.HttpResult {
|
||||
cond.CurrentStoreName = req.GetFormParameter("currentStoreName") // 滚动查询时定位用日志仓
|
||||
cond.CurrentId = cmn.StringToUint32(req.GetFormParameter("currentId"), 0) // 滚动查询时定位用ID
|
||||
cond.Forward = cmn.StringToBool(req.GetFormParameter("forward"), true) // 是否向下滚动查询
|
||||
cond.OldNearId = cmn.StringToUint32(req.GetFormParameter("oldNearId"), 0) // 相邻检索旧ID
|
||||
cond.NewNearId = cmn.StringToUint32(req.GetFormParameter("newNearId"), 0) // 相邻检索新ID
|
||||
cond.NearStoreName = req.GetFormParameter("nearStoreName") // 相邻检索时新ID对应的日志仓
|
||||
cond.DatetimeFrom = req.GetFormParameter("datetimeFrom") // 日期范围(From)
|
||||
cond.DatetimeTo = req.GetFormParameter("datetimeTo") // 日期范围(To)
|
||||
cond.OrgSystem = cmn.Trim(req.GetFormParameter("system")) // 系统
|
||||
|
||||
6
glc/www/web/.vscode/settings.json
vendored
6
glc/www/web/.vscode/settings.json
vendored
@ -4,9 +4,9 @@
|
||||
"editor.formatOnType": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true,
|
||||
"source.fixAll.eslint": true,
|
||||
"source.fixAll.stylelint": true
|
||||
"source.fixAll": "explicit",
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
},
|
||||
"eslint.validate": ["javascript", "javascriptvue", "vue"],
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass", "html"]
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"@gotoeasy/glcui": "^0.1.15",
|
||||
"@gotoeasy/glcui": "0.2.0",
|
||||
"@imengyu/vue3-context-menu": "^1.3.0",
|
||||
"@vueuse/core": "^10.2.1",
|
||||
"axios": "^1.4.0",
|
||||
|
||||
@ -9,8 +9,8 @@ dependencies:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0(vue@3.3.4)
|
||||
'@gotoeasy/glcui':
|
||||
specifier: ^0.1.15
|
||||
version: 0.1.15
|
||||
specifier: 0.2.0
|
||||
version: 0.2.0
|
||||
'@imengyu/vue3-context-menu':
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
@ -772,8 +772,8 @@ packages:
|
||||
resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
|
||||
dev: true
|
||||
|
||||
/@gotoeasy/glcui@0.1.15:
|
||||
resolution: {integrity: sha512-Htk1iNsX46ROcCCxNZFjOY2fON33AN/H3Os5yApZA+r/2ljsXyYOCzyIFP5pGMHNkgfAH9HaEyynqnG5M+18dQ==}
|
||||
/@gotoeasy/glcui@0.2.0:
|
||||
resolution: {integrity: sha512-GERMjGBaojq8L317uxGLtM7Aghbr5Xyzc7TG47nKw8jZKFKKtzAQLPVnXGFBll5InxDiDlS/bOmUY2Cb3NFJqA==}
|
||||
dev: false
|
||||
|
||||
/@humanwhocodes/config-array@0.11.10:
|
||||
|
||||
1
glc/www/web/src/assets/icons/near-search.svg
Normal file
1
glc/www/web/src/assets/icons/near-search.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M458.678857 730.672762c150.211048 0 271.993905-121.782857 271.993905-272.018286 0-3.047619-0.048762-6.095238-0.146286-9.142857h-107.471238a10.678857 10.678857 0 0 1-7.558095-18.212571l125.123047-125.074286c4.144762-4.169143 10.898286-4.169143 15.067429 0l125.098667 125.074286a10.654476 10.654476 0 0 1-7.558096 18.212571H789.211429a329.313524 329.313524 0 0 1-76.897524 221.305905l183.100952 183.100952-41.472 41.496381-183.100952-183.100952a329.337905 329.337905 0 0 1-212.163048 77.019428c-107.398095 0-202.849524-51.2-263.241143-130.511238l41.935238-41.935238a271.652571 271.652571 0 0 0 221.305905 113.785905zM458.678857 186.660571c92.769524 0 174.689524 46.445714 223.792762 117.369905l42.032762-42.057143A330.191238 330.191238 0 0 0 458.678857 128c-182.613333 0-330.678857 148.041143-330.678857 330.678857 0 4.071619 0.073143 8.118857 0.24381 12.190476H47.104a10.678857 10.678857 0 0 0-7.558095 18.188191l125.098666 125.098666c4.169143 4.144762 10.922667 4.144762 15.09181 0l125.074286-125.098666a10.678857 10.678857 0 0 0-7.533715-18.212572H186.928762a276.601905 276.601905 0 0 1-0.24381-12.190476c0-150.186667 121.758476-271.993905 271.993905-271.993905z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -5,8 +5,8 @@
|
||||
<el-input v-model="formData.searchKeys" placeholder="请输入关键词检索,支持多关键词" input-style="width:500px;height: 30px"
|
||||
maxlength="1000" @keyup.enter="fnSearch">
|
||||
<template #append>
|
||||
<el-button type="primary" class="c-btn-search" style="height:30px;color: white; background-color:#0081dd"
|
||||
@click="fnSearch">
|
||||
<el-button type="primary" class="c-btn-search" style="height:30px;color:white"
|
||||
:style="{ backgroundColor: searchFormNormalMode ? '#0081dd' : '#e6a23c' }" @click="fnSearch">
|
||||
<el-icon>
|
||||
<Search />
|
||||
</el-icon>
|
||||
@ -14,7 +14,10 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<GxButton icon="refresh-left" style="margin-left:108px;" @click="fnReset">重 置</GxButton>
|
||||
<GxButton icon="refresh-left" style="margin-left:108px;" @click="fnReset">
|
||||
<span v-if="searchFormNormalMode">重 置</span>
|
||||
<span v-else title="取消定位相邻检索">取 消</span>
|
||||
</GxButton>
|
||||
<el-badge is-dot :hidden="noMoreSearchCondition" style="margin-left:12px;" type="primary" class="c-btn-badge">
|
||||
<el-button circle @click="() => (moreVisible = !moreVisible)">
|
||||
<el-icon>
|
||||
@ -32,7 +35,7 @@
|
||||
<el-divider style="margin: 0 0 10px" />
|
||||
|
||||
<el-row justify="center">
|
||||
<el-button type="primary" class="c-btn" @click="fnSearch">
|
||||
<el-button :type="searchFormNormalMode ? 'primary' : 'warning'" class="c-btn" @click="fnSearch">
|
||||
<el-icon size="14">
|
||||
<Search />
|
||||
</el-icon>
|
||||
@ -73,6 +76,24 @@ const moreVisible = ref(false);
|
||||
|
||||
$emitter.on("defaultStorageCondtion", v => defaultData.value.storage = v);
|
||||
|
||||
// 检索模式(普通/相邻)
|
||||
const searchFormNormalMode = ref(true);
|
||||
const tmpFormDataNormal = {};
|
||||
const setSearchNormalMode = (normal = false) => {
|
||||
if (searchFormNormalMode.value && !normal) {
|
||||
// 正切换模式,保存普通检索的条件,用于取消时恢复
|
||||
tmpFormDataNormal.storage = formData.value.storage || '';
|
||||
tmpFormDataNormal.system = formData.value.system || '';
|
||||
tmpFormDataNormal.loglevel = [...(formData.value.loglevel || [])];
|
||||
tmpFormDataNormal.datetime = [...(formData.value.datetime || [])];
|
||||
tmpFormDataNormal.user = formData.value.user || '';
|
||||
tmpFormDataNormal.searchKeys = formData.value.searchKeys || '';
|
||||
}
|
||||
searchFormNormalMode.value = normal;
|
||||
moreVisible.value = false; // 收起,避免点相邻检索时还展开着条件
|
||||
}
|
||||
$emitter.on("searchFormNormalMode", setSearchNormalMode);
|
||||
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
const fnSearch = () => {
|
||||
@ -83,19 +104,33 @@ $emitter.on("fnSearch", fnSearch);
|
||||
|
||||
const fnReset = () => {
|
||||
|
||||
const keys = Object.keys(formData.value);
|
||||
for (const key of keys) {
|
||||
if (!formData.value[key] && !defaultData.value[key]) {
|
||||
continue;
|
||||
} else if (!formData.value[key] && defaultData.value[key]) {
|
||||
formData.value[key] = defaultData.value[key];
|
||||
} else if (formData.value[key] && !defaultData.value[key]) {
|
||||
formData.value[key] = null;
|
||||
} else if (Array.isArray(defaultData.value[key])) {
|
||||
formData.value[key] = defaultData.value[key].slice(0);
|
||||
} else {
|
||||
formData.value[key] = defaultData.value[key];
|
||||
}
|
||||
if (!searchFormNormalMode.value) {
|
||||
// 【取消】相邻检索模式 --> 默认检索模式,恢复正常检索的条件
|
||||
searchFormNormalMode.value = true;
|
||||
$emitter.emit("searchNearMode", false);
|
||||
|
||||
formData.value.storage = tmpFormDataNormal.storage;
|
||||
formData.value.system = tmpFormDataNormal.system;
|
||||
!formData.value.loglevel && (formData.value.loglevel = []);
|
||||
formData.value.loglevel.splice(0, formData.value.loglevel.length);
|
||||
formData.value.loglevel.push(...(tmpFormDataNormal.loglevel || []));
|
||||
!formData.value.datetime && (formData.value.datetime = []);
|
||||
formData.value.datetime.splice(0, formData.value.datetime.length);
|
||||
formData.value.datetime.push(...(tmpFormDataNormal.datetime || []));
|
||||
formData.value.user = tmpFormDataNormal.user;
|
||||
formData.value.searchKeys = tmpFormDataNormal.searchKeys;
|
||||
} else {
|
||||
// 【重置】正常检索时的重置
|
||||
formData.value.storage = defaultData.value.storage;
|
||||
formData.value.system = defaultData.value.system;
|
||||
!formData.value.loglevel && (formData.value.loglevel = []);
|
||||
formData.value.loglevel.splice(0, formData.value.loglevel.length);
|
||||
formData.value.loglevel.push(...(defaultData.value.loglevel || []));
|
||||
!formData.value.datetime && (formData.value.datetime = []);
|
||||
formData.value.datetime.splice(0, formData.value.datetime.length);
|
||||
formData.value.datetime.push(...(defaultData.value.datetime || []));
|
||||
formData.value.user = defaultData.value.user;
|
||||
formData.value.searchKeys = '';
|
||||
}
|
||||
|
||||
moreVisible.value = false;
|
||||
@ -103,47 +138,50 @@ const fnReset = () => {
|
||||
}
|
||||
|
||||
const noMoreSearchCondition = computed(() => {
|
||||
for (const [key, value] of Object.entries(formData.value)) {
|
||||
if (key == 'searchKeys') {
|
||||
continue; // 忽略不提示小蓝点
|
||||
}
|
||||
if (key == 'storage') {
|
||||
if (!defaultData.value.storage) {
|
||||
continue; // 初始化时日志仓还没拿到,忽略不提示小蓝点
|
||||
} else if (defaultData.value.storage == value) {
|
||||
continue; // 和默认的日志仓条件一样,忽略不提示小蓝点
|
||||
}
|
||||
if (!value) return false; // 日志仓条件清空时,显示小蓝点
|
||||
}
|
||||
if (value) {
|
||||
if (Array.isArray(value)) {
|
||||
if (!defaultData.value[key]) {
|
||||
if (!value.length) {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (value.length != defaultData.value[key].length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (value[i] != defaultData.value[key][i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 相邻检索模式下,级别时间用户任一有填写即有修改
|
||||
if (!searchFormNormalMode.value) {
|
||||
if ((formData.value.loglevel || []).length) return false;
|
||||
const ary = formData.value.datetime || ['', ''];
|
||||
!ary.length && ary.push(...['', '']);
|
||||
if (ary.length != 2 || ary[0] || ary[1]) return false;
|
||||
if (formData.value.user) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (value != defaultData.value[key]) {
|
||||
return false;
|
||||
}
|
||||
} else if (defaultData.value[key]) {
|
||||
// 普通检索模式下,逐个比较展开的条件字段
|
||||
if ((formData.value.storage || '') != (defaultData.value.storage || '')) {
|
||||
return false;
|
||||
}
|
||||
if ((formData.value.system || '') != (defaultData.value.system || '')) {
|
||||
return false;
|
||||
}
|
||||
if ((formData.value.user || '') != (defaultData.value.user || '')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 日志
|
||||
const formDataLoglevel = formData.value.loglevel || [];
|
||||
const defaultDataLoglevel = defaultData.value.loglevel || [];
|
||||
if (formDataLoglevel.length != defaultDataLoglevel.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < formDataLoglevel.length; i++) {
|
||||
if (!defaultDataLoglevel.includes(formDataLoglevel[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 时间范围
|
||||
const formDataDatetime = formData.value.datetime || [];
|
||||
!formDataDatetime.length && formDataDatetime.push(...['', '']);
|
||||
const defaultDataDatetime = defaultData.value.datetime || [];
|
||||
!defaultDataDatetime.length && defaultDataDatetime.push(...['', '']);
|
||||
if (formDataDatetime.length != defaultDataDatetime.length || formDataDatetime[0] != defaultDataDatetime[0] || formDataDatetime[1] != defaultDataDatetime[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@ -3,37 +3,43 @@
|
||||
|
||||
<GxToolbar style="margin-bottom: 13px" class="c-btn">
|
||||
<template #left>
|
||||
<SearchForm :data="formData" class="c-search-form" @search="search">
|
||||
<SearchForm :data="formData" class="c-search-form" @search="() => search()">
|
||||
<el-row>
|
||||
<el-form-item label="选择日志仓">
|
||||
<el-select v-model="formData.storage" clearable filterable placeholder="请选择" style="width:420px;"
|
||||
@clear="reGetStorageOptions">
|
||||
:disabled="searchNearMode" @clear="reGetStorageOptions">
|
||||
<el-option v-for="item in storageOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="系统名">
|
||||
<el-select v-model="formData.system" :multiple="false" filterable allow-create default-first-option
|
||||
style="width:420px;" clearable :reserve-keyword="false" placeholder="请输入系统名">
|
||||
:disabled="searchNearMode" style="width:420px;" clearable :reserve-keyword="false" placeholder="请输入系统名">
|
||||
<el-option v-for="item in systemOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="日志级别">
|
||||
<el-select v-model="formData.loglevel" multiple clearable :reserve-keyword="true" style="width:420px;"
|
||||
<!-- <el-select v-model="formData.loglevel" multiple clearable :reserve-keyword="true" style="width:420px;"
|
||||
placeholder="请选择...">
|
||||
<el-option label="ERROR" value="error" />
|
||||
<el-option label="WARN" value="warn" />
|
||||
<el-option label="INFO" value="info" />
|
||||
<el-option label="DEBUG" value="debug" />
|
||||
</el-select>
|
||||
</el-select> -->
|
||||
<el-checkbox-group v-model="formData.loglevel">
|
||||
<el-checkbox label="DEBUG" value="debug" border />
|
||||
<el-checkbox label="INFO" value="info" border style="margin-left: 6px;" />
|
||||
<el-checkbox label="WARN" value="warn" border style="margin-left: 6px;" />
|
||||
<el-checkbox label="ERROR" value="error" border style="margin-left: 6px;" />
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间范围">
|
||||
<el-date-picker v-model="formData.datetime" type="datetimerange" :shortcuts="shortcuts" range-separator="~"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" start-placeholder="开始时间" end-placeholder="结束时间"
|
||||
<el-date-picker v-model="formData.datetime" type="datetimerange" :shortcuts="shortcuts"
|
||||
range-separator="~" value-format="YYYY-MM-DD HH:mm:ss" start-placeholder="开始时间" end-placeholder="结束时间"
|
||||
popper-class="c-datapicker" />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户">
|
||||
<el-input v-model="formData.user" placeholder="请输入用户" maxlength="100" style="width:420px;"
|
||||
@keyup.enter="() => $emitter.emit('fnSearch')" />
|
||||
@keyup.enter="() => { $emitter.emit('fnSearch'); }" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</SearchForm>
|
||||
@ -46,7 +52,7 @@
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="autoSearchMode ? '停止自动查询' : '开始自动查询'" placement="top">
|
||||
<el-button circle @click="switchAutoSearchMode">
|
||||
<el-button circle :disabled="searchNearMode" @click="switchAutoSearchMode">
|
||||
<SvgIcon v-if="!autoSearchMode" name="play" />
|
||||
<SvgIcon v-if="autoSearchMode" name="stop" />
|
||||
</el-button>
|
||||
@ -65,17 +71,21 @@
|
||||
</template>
|
||||
</GxToolbar>
|
||||
|
||||
<GxTable ref="table" v-loading="showTableLoadding" scrollbar-always-on :enable-header-contextmenu="false"
|
||||
:enable-first-expand="true" stripe :tid="tid" :data="tableData" :height="tableHeight" class="c-gx-table c-glc-table"
|
||||
row-key="id">
|
||||
<GxTable ref="table" v-loading="showTableLoadding" :row-class-name="tableRowClassName" scrollbar-always-on
|
||||
:enable-header-contextmenu="true" :enable-first-expand="true" stripe :tid="tid" :data="tableData"
|
||||
:height="tableHeight" class="c-gx-table c-glc-table" row-key="id">
|
||||
<template #$operation="{ row }">
|
||||
<div title="定位相邻检索">
|
||||
<SvgIcon name="near-search" class="hand" @click="fnSearchNear(row)" />
|
||||
</div>
|
||||
</template>
|
||||
</GxTable>
|
||||
|
||||
<div>
|
||||
<div style="display:flex;justify-content:space-between;">
|
||||
<div style="min-height:30px;padding-top:5px; line-height:30px; color: #909399;" v-text="info"></div>
|
||||
<div style="min-height:30px;padding-top:5px; line-height:30px; color: #909399;" v-text="info"> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -96,7 +106,7 @@ const { formData, visible, tableData, tableHeight, pageSettingStore, showTableLo
|
||||
const showTestBtn = ref(false); // 是否显示生成测试数据按钮
|
||||
const autoSearchMode = ref(false); // 自动查询
|
||||
const table = ref(); // 表格实例
|
||||
const tid = ref('searchMain231126'); // 表格ID
|
||||
const tid = ref('v001500GlcMain'); // 表格ID
|
||||
const info = ref(''); // 底部提示信息
|
||||
const storageOptions = ref([]) // 日志仓
|
||||
const systemSet = new Set();
|
||||
@ -188,6 +198,51 @@ const shortcuts = ref([
|
||||
},
|
||||
]);
|
||||
|
||||
// 相邻检索模式
|
||||
const searchNearMode = ref(false);
|
||||
const setSearchNearMode = (near = false) => {
|
||||
searchNearMode.value = near;
|
||||
}
|
||||
$emitter.on("searchNearMode", setSearchNearMode);
|
||||
|
||||
const oldNearId = ref(0);
|
||||
const newNearId = ref(0);
|
||||
const nearStoreName = ref('');
|
||||
const tableRowClassName = ({ row }) => {
|
||||
if (searchNearMode.value && row.id == newNearId.value) {
|
||||
return 'search-near-row';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// 定位相邻检索
|
||||
const fnSearchNear = (row) => {
|
||||
$emitter.emit("searchFormNormalMode", false); // 收起条件等处理
|
||||
|
||||
if (searchNearMode.value) {
|
||||
// 相邻检索模式下,继续进行相邻检索,需使用输入的条件进一步筛选
|
||||
oldNearId.value = newNearId.value;
|
||||
newNearId.value = row.id;
|
||||
} else {
|
||||
// 普通检索模式下,进行相邻检索
|
||||
oldNearId.value = 0;
|
||||
newNearId.value = row.id;
|
||||
nearStoreName.value = row.storename;
|
||||
|
||||
formData.value.storage = row.storename;
|
||||
formData.value.system = row.system;
|
||||
formData.value.loglevel = [];
|
||||
formData.value.datetime = [];
|
||||
formData.value.user = '';
|
||||
formData.value.searchKeys = '';
|
||||
|
||||
$emitter.emit("searchNearMode", true);
|
||||
}
|
||||
|
||||
// 检索
|
||||
search();
|
||||
}
|
||||
|
||||
// 初期默认检索
|
||||
onMounted(async () => {
|
||||
const configStore = $emitter.emit('$table:config', { id: tid.value });
|
||||
@ -276,7 +331,7 @@ function isAutoSearchMode() {
|
||||
}
|
||||
function switchAutoSearchMode(changMode = true) {
|
||||
changMode && (autoSearchMode.value = !autoSearchMode.value);
|
||||
if (autoSearchMode.value) {
|
||||
if (autoSearchMode.value && !searchNearMode.value) {
|
||||
search();
|
||||
setTimeout(() => {
|
||||
isAutoSearchMode() && switchAutoSearchMode(false);
|
||||
@ -297,30 +352,58 @@ function search() {
|
||||
data.datetimeFrom = (formData.value.datetime || ['', ''])[0];
|
||||
data.datetimeTo = (formData.value.datetime || ['', ''])[1];
|
||||
data.user = formData.value.user;
|
||||
if (searchNearMode.value && newNearId.value) {
|
||||
data.oldNearId = oldNearId.value;
|
||||
data.newNearId = newNearId.value;
|
||||
data.nearStoreName = newNearId.value ? nearStoreName.value : '';
|
||||
}
|
||||
|
||||
// 保存好滚动检索的输入条件,保持和检索时一致,避免修改输入再滚动查询而出现矛盾结果
|
||||
moreConditon.value = data;
|
||||
|
||||
$post(url, data, null, { 'Content-Type': 'application/x-www-form-urlencoded' }).then(rs => {
|
||||
console.log(rs)
|
||||
if (rs.success) {
|
||||
const resultData = rs.result.data || [];
|
||||
const pagesize = rs.result.pagesize - 0;
|
||||
tableData.value.splice(0, tableData.value.length); // 删除原全部元素,nextTick时再插入新查询结果
|
||||
document.querySelector('.c-glc-table .el-scrollbar__wrap').scrollTop = 0; // 滚动到顶部
|
||||
!searchNearMode.value && (document.querySelector('.c-glc-table .el-scrollbar__wrap').scrollTop = 0); // 普通检索时滚动到顶部
|
||||
rs.result.laststorename && (lastStoreName.value = rs.result.laststorename); // 查到有结果时,更新
|
||||
maxMatchCount.value = rs.result.count; // 最大匹配件数
|
||||
|
||||
nextTick(() => {
|
||||
resultData.forEach(item => {
|
||||
tableData.value.push(item);
|
||||
item.system && !systemSet.has(item.system) && systemSet.add(item.system) && systemOptions.value.push({ value: item.system, label: item.system });
|
||||
});
|
||||
if (searchNearMode.value) {
|
||||
// 相邻检索
|
||||
tableData.value.push(...resultData);
|
||||
info.value = `日志总量 ${rs.result.total} 条,当前条件最多匹配 ${maxMatchCount.value} 条,正展示相邻 ${tableData.value.length - 1} 条,查询${rs.result.timemessage}`
|
||||
|
||||
if (resultData.length < pagesize) {
|
||||
info.value = `日志总量 ${rs.result.total} 条,当前条件最多匹配 ${tableData.value.length} 条,正展示前 ${tableData.value.length} 条,查询${rs.result.timemessage}`
|
||||
nextTick(() => {
|
||||
const step = 30;
|
||||
const down = oldNearId.value == 0 || newNearId.value <= oldNearId.value; // 向下定位检索
|
||||
let scrollTop = down ? (-7 * step) : (-15 * step); // 滚动到合适位置(向下时上部7行新日志,剩余下部较大空间给旧日志展示;向上时上部固定留出15行显示新日志)
|
||||
for (let i = 0; i < tableData.value.length; i++) {
|
||||
if (newNearId.value == tableData.value[i].id) {
|
||||
break;
|
||||
}
|
||||
scrollTop += step;
|
||||
}
|
||||
// scrollTop <= (step * 15) && (scrollTop = 0);
|
||||
document.querySelector('.c-glc-table .el-scrollbar__wrap').scrollTop = scrollTop; // 滚动到焦点行
|
||||
// if (oldNearId.value > 0 && newNearId.value > oldNearId.value) {
|
||||
// document.querySelector('.c-glc-table .el-scrollbar__wrap').scrollTop = 20000; // 滚动到底部
|
||||
// }
|
||||
});
|
||||
} else {
|
||||
info.value = `日志总量 ${rs.result.total} 条,当前条件最多匹配 ${maxMatchCount.value} 条,正展示前 ${tableData.value.length} 条,查询${rs.result.timemessage}`
|
||||
// 普通检索
|
||||
resultData.forEach(item => {
|
||||
tableData.value.push(item);
|
||||
item.system && !systemSet.has(item.system) && systemSet.add(item.system) && systemOptions.value.push({ value: item.system, label: item.system });
|
||||
});
|
||||
|
||||
if (resultData.length < pagesize) {
|
||||
info.value = `日志总量 ${rs.result.total} 条,当前条件最多匹配 ${tableData.value.length} 条,正展示前 ${tableData.value.length} 条,查询${rs.result.timemessage}`
|
||||
} else {
|
||||
info.value = `日志总量 ${rs.result.total} 条,当前条件最多匹配 ${maxMatchCount.value} 条,正展示前 ${tableData.value.length} 条,查询${rs.result.timemessage}`
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -334,11 +417,15 @@ function search() {
|
||||
}
|
||||
|
||||
function searchMore() {
|
||||
if (searchNearMode.value) {
|
||||
return; // 相邻检索模式不支持滚动检索
|
||||
}
|
||||
|
||||
if (tableData.value.length >= 5000) {
|
||||
if (info.value.indexOf('请考虑') < 0) {
|
||||
info.value += ` (数据太多不再自动加载,请考虑添加条件)`
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const url = `/v1/log/search`;
|
||||
@ -399,6 +486,14 @@ function fnDownload() {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped.search-near-row td.el-table__cell {
|
||||
background: lemonchiffon;
|
||||
}
|
||||
|
||||
.el-table__body tr.el-table__row.search-near-row td.el-table__cell {
|
||||
background: lemonchiffon;
|
||||
}
|
||||
|
||||
.c-glc-table .el-popper.is-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user