Skip to content

Commit

Permalink
Feature/func bar (#183)
Browse files Browse the repository at this point in the history
* feat: search and copy

* feat: func bar

1. search
2. copy
3. download

* feat: func bar in logs

* fix: some details

* feat: add icons

* fix: hide bar

* refactor: template
  • Loading branch information
Feudalman authored Jan 10, 2024
1 parent 54c77c5 commit 27674c6
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 33 deletions.
1 change: 1 addition & 0 deletions swanlab/server/api/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ async def get_experiment(experiment_id: int):
path = os.path.join(SWANLOG_DIR, experiment["name"], "logs")
experiment["tags"] = __list_subdirectories(path)
experiment["default_color"] = DEFAULT_COLOR
# COMPAT
return SUCCESS_200(experiment)


Expand Down
2 changes: 1 addition & 1 deletion vue/src/assets/iconfont.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions vue/src/components/SLButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ button {
}
// ----------------------- hollow 样式 -----------------------
.sa-button-default-hollow {
@apply text-default border-default bg-default transition-all;
&:hover {
@apply border-primary-dimmer;
}
&:active {
@apply border-primary-default;
}
}
.sa-button-negative-hollow {
@apply text-negative-default border-negative-default bg-default transition-all;
Expand Down
5 changes: 4 additions & 1 deletion vue/src/components/SLCopy.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<template>
<Tippy trigger="click">
<template v-slot="{ hide }">
<SLIcon icon="copy" :class="iconClass" @click="copy(hide)" />
<div class="flex gap-2 items-center">
<SLIcon icon="copy" :class="iconClass" @click="copy(hide)" />
<slot></slot>
</div>
</template>
<template #content> {{ $t('common.copy') }}</template>
</Tippy>
Expand Down
2 changes: 1 addition & 1 deletion vue/src/components/SLSearch.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="w-full border flex gap-2 justify-between items-center py-2 px-2 rounded-lg">
<SLIcon class="w-4 h-4" icon="search"></SLIcon>
<SLIcon class="w-4 h-4 shrink-0" icon="search"></SLIcon>
<input
type="text"
v-model="value"
Expand Down
5 changes: 5 additions & 0 deletions vue/src/i18n/en-US/experiment.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,10 @@
"empty": {
"requirements": "There are no dependencies"
}
},
"func-bar": {
"copy": "Copy",
"download": "Download",
"copy-success": "Copied"
}
}
5 changes: 5 additions & 0 deletions vue/src/i18n/zh-CN/experiment.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,10 @@
"empty": {
"requirements": "There are no dependencies"
}
},
"func-bar": {
"copy": "Copy",
"download": "Download",
"copy-success": "Copied"
}
}
18 changes: 18 additions & 0 deletions vue/src/utils/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,21 @@ export const addTaskToBrowserMainThread = (task) => {
export const uuid = (now = Math.round(new Date() / 1000)) => {
return now.toString(36) + Math.random().toString(36).slice(-4)
}

/**
* 将目标字符串下载成文件
* @param {string} content 下载的内容
* @param {string} fileName 文件名
*/
export const downloadStringAsFile = (content, fileName) => {
// 创建一个包含字符串内容的 Blob 对象
const blob = new Blob([content], { type: 'text/plain' })

// 创建一个下载链接
const downloadLink = document.createElement('a')

// 设置链接的属性,包括下载文件的名称
downloadLink.href = window.URL.createObjectURL(blob)
downloadLink.download = fileName
downloadLink.click()
}
69 changes: 69 additions & 0 deletions vue/src/views/experiment/components/FuncBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div class="w-full flex gap-5 justify-between">
<SLSearch @input="input" class="max-w-[400px]" />
<div class="flex gap-3">
<SLButton hollow @click="copy">
<SLIcon icon="copy" class="icon"></SLIcon>
{{ $t('experiment.func-bar.copy') }}
</SLButton>
<SLButton hollow @click="download">
<SLIcon icon="download" class="icon"></SLIcon>
{{ $t('experiment.func-bar.download') }}
</SLButton>
</div>
</div>
</template>

<script setup>
/**
* @description: 功能条:搜索、复制、下载
* @file: FuncBar.vue
* @since: 2024-01-10 12:54:19
**/
import SLSearch from '@swanlab-vue/components/SLSearch.vue'
import { message } from '@swanlab-vue/components/message'
import { t } from '@swanlab-vue/i18n'
import { copyTextToClipboard, downloadStringAsFile } from '@swanlab-vue/utils/browser'
const props = defineProps({
// 复制、下载的内容
content: {
type: String,
default: ''
},
// 下载时使用的文件名
filename: {
type: String,
default: 'swanlab.file'
}
})
const emits = defineEmits(['input', 'download'])
// 输入触发
const input = (value) => {
emits('input', value)
}
// 复制
const copy = () => {
copyTextToClipboard(props.content)
message.success(t('experiment.func-bar.copy-success'))
}
// 下载
const download = () => {
downloadStringAsFile(props.content, props.filename)
}
</script>

<style lang="scss" scoped>
button {
@apply rounded-lg px-3 py-1 flex items-center gap-2;
}
.icon {
@apply w-4 h-4;
}
</style>
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
<template>
<div class="w-full">
<div class="px-6 py-4 bg-higher rounded" v-if="dependencies && dependencies.length !== 0">
<p v-for="line in dependencies" :key="line">{{ line }}</p>
</div>
<template v-if="dependencies && dependencies.length !== 0">
<!-- 搜索、复制、下载 -->
<FuncBar class="pb-6 py-4" @input="search" :content="dependencies?.join('\n')" :filename="filename" />
<!-- 如果有依赖项 -->
<div class="px-6 py-4 bg-higher rounded">
<p v-for="line in lines" :key="line">
<span v-show="!line.isTarget">{{ line.value }}</span>
<span v-show="line.isTarget">
<span
v-for="substring in line.value"
:key="substring"
:class="substring.toLowerCase() === searchValue ? ' bg-warning-dimmest' : ''"
>
{{ substring }}
</span>
</span>
</p>
</div>
</template>
<!-- 没有依赖项时占位 -->
<div class="w-full text-center pt-10" v-else>
{{ $t('experiment.env.empty.requirements') }}
</div>
Expand All @@ -16,10 +33,50 @@
* @since: 2024-01-09 16:01:55
**/
import { ref, computed } from 'vue'
import { useExperimentStroe } from '@swanlab-vue/store'
import FuncBar from '@swanlab-vue/views/experiment/components/FuncBar.vue'
const experimentStore = useExperimentStroe()
const dependencies = experimentStore.experiment.system?.dependencies
// ---------------------------------- 搜索 ----------------------------------
// 查找字符
const searchValue = ref('')
const search = (value) => {
searchValue.value = value.toLowerCase().trim()
}
// 对依赖的每一行进行一些特殊处理
const lines = computed(() => {
return dependencies.map((line) => {
// 查找内容不为空,并且该行含有查找内容,说明是目标行
const isTarget = searchValue.value !== '' && line.toLowerCase().includes(searchValue.value)
return {
isTarget,
value: isTarget ? splitStringBySearch(line, searchValue.value) : line
}
})
})
// 以查找字符串作为分割点,将一行字符串分割成数组,且忽略大小写
function splitStringBySearch(target, substring) {
// 使用正则表达式进行大小写不敏感的分割,并保留分割点
let resultArray = target.split(new RegExp(`(${substring})`, 'i'))
// 去除数组中的空字符串
resultArray = resultArray.filter((item) => item !== '')
// 返回包含分割点的数组
return resultArray
}
// ---------------------------------- 下载成文件 ----------------------------------
const filename = 'requirements.txt'
</script>
<style lang="scss" scoped></style>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<ExtendBlock class="pt-2 pr-5" icon="config" :title="$t('experiment.index.config.title')" :retract="false">
<div class="pr-5" icon="config" :title="$t('experiment.index.config.title')" :retract="false">
<div class="pl-6 w-full grid lg:grid-cols-2 lg:gap-10">
<div>
<div class="pt-4">
<div class="flex items-center pb-4">
<p class="font-semibold pr-2">{{ $t('experiment.index.config.detail') }}</p>
<SLHelp document="https://geektechstudio.feishu.cn/wiki/EFi3wuACGiEWlLki5aDcQiSpngg">{{
Expand All @@ -10,7 +10,7 @@
</div>
<SLTable :column="column" :data="configs" flexable />
</div>
<div v-if="summaries?.length !== 0">
<div class="pt-4" v-if="summaries?.length !== 0">
<div class="flex items-center pb-4" v-if="summaries?.length !== 0">
<p class="font-semibold pr-2">{{ $t('experiment.index.config.summarize') }}</p>
<SLHelp document="https://geektechstudio.feishu.cn/wiki/TudNwOSMyihFetky7l5cTI8UnJf"
Expand All @@ -21,7 +21,7 @@
</div>
</div>
<div class="w-full h-6"></div>
</ExtendBlock>
</div>
</template>

<script setup>
Expand All @@ -30,7 +30,7 @@
* @file: ExperimentConfig.vue
* @since: 2023-12-11 17:07:31
**/
import ExtendBlock from '@swanlab-vue/views/experiment/components/ExtendBlock.vue'
import SLTable from '@swanlab-vue/components/table'
import SLHelp from '@swanlab-vue/components/SLHelp.vue'
import http from '@swanlab-vue/api/http'
Expand Down
58 changes: 36 additions & 22 deletions vue/src/views/experiment/pages/log/LogPage.vue
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
<template>
<div class="w-full h-full px-7 py-6 relative overflow-hidden">
<SLSearch class="max-w-[400px] mb-5" @input="search" dealy="300" />
<FuncBar class="pb-6" @input="search" :content="logs?.join('\n')" :filename="filename" v-if="logs" />
<section class="log-container">
<div class="log-area" ref="logAreaRef" v-if="logs">
<!-- 运行日志 -->
<div class="log-line" v-for="line in filteredLogs" :key="line">
<div class="log-line" v-for="line in lines" :key="line">
<!-- 行号 -->
<span class="w-8 text-right flex-shrink-0 text-dimmest select-none">{{ line.index }}</span>
<!-- 日志内容 -->
<!-- 如果没有搜索内容/不含搜索内容 -->
<span v-show="!searchValue || !line.lower.includes(searchValue)">{{ line.value }} </span>
<span v-show="!line.isTarget">{{ line.value }} </span>
<!-- 如果有搜索内容且含有搜索内容 -->
<p class="flex" v-show="searchValue && line.lower.includes(searchValue)">
<span>{{ line.value.substring(0, line.lower.indexOf(searchValue)) }}</span>
<!-- 高亮展示 -->
<span class="bg-warning-dimmest">{{
line.value.substring(
line.lower.indexOf(searchValue),
line.lower.indexOf(searchValue) + searchValue.length
)
}}</span>
<span>{{ line.value.substring(line.lower.indexOf(searchValue) + searchValue.length) }}</span>
</p>
<span v-show="line.isTarget">
<span
v-for="substring in line.value"
:key="substring"
:class="substring.toLowerCase() === searchValue ? ' bg-warning-dimmest' : ''"
>
{{ substring }}
</span>
</span>
</div>
<!-- 错误日志 -->
<div class="log-line text-negative-default" v-for="(line, index) in errorLogs" :key="line">
Expand Down Expand Up @@ -50,7 +48,7 @@ import http from '@swanlab-vue/api/http'
import { useExperimentStroe } from '@swanlab-vue/store'
import { addTaskToBrowserMainThread } from '@swanlab-vue/utils/browser'
import SLLoding from '@swanlab-vue/components/SLLoading.vue'
import SLSearch from '@swanlab-vue/components/SLSearch.vue'
import FuncBar from '../../components/FuncBar.vue'
import { computed } from 'vue'
const logAreaRef = ref()
Expand All @@ -66,16 +64,31 @@ const id = experimentStore.id
const logs = ref()
// 分开行号和内容之后的日志
const filteredLogs = computed(() => {
const lines = computed(() => {
return logs.value.map((line) => {
const index = line.substring(0, line.indexOf(' '))
const content = line.substring(line.indexOf(' '))
const isTarget = searchValue.value !== '' && content.toLowerCase().includes(searchValue.value)
return {
index: line.substring(0, line.indexOf(' ')),
value: line.substring(line.indexOf(' ')),
lower: line.substring(line.indexOf(' ')).toLowerCase()
isTarget,
index,
value: isTarget ? splitStringBySearch(content, searchValue.value) : content
}
})
})
function splitStringBySearch(target, substring) {
// 使用正则表达式进行大小写不敏感的分割,并保留分割点
let resultArray = target.split(new RegExp(`(${substring})`, 'i'))
// 去除数组中的空字符串
resultArray = resultArray.filter((item) => item !== '')
// 返回包含分割点的数组
return resultArray
}
// 错误日志
const errorLogs = ref([])
;(async function () {
Expand All @@ -97,6 +110,10 @@ const searchValue = ref('')
const search = (value) => {
searchValue.value = value.toLowerCase()
}
// ---------------------------------- 下载 ----------------------------------
const filename = 'print.log'
</script>

<style lang="scss" scoped>
Expand All @@ -117,9 +134,6 @@ const search = (value) => {
.log-line {
@apply flex gap-2 whitespace-pre-wrap;
span {
@apply block;
}
}
}
</style>

0 comments on commit 27674c6

Please sign in to comment.