Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion vue/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<router-view v-if="!error_code" />
<ErrorView :code="error_code" v-else />
</MainLayout>
<SLMessages ref="messages" />
</template>

<script setup>
Expand All @@ -19,6 +20,9 @@ import { ref } from 'vue'
import { provide } from 'vue'
import { useRoute } from 'vue-router'
import { watch } from 'vue'
import { installMessage, SLMessages, message } from '@swanlab-vue/components/message'
import { onMounted } from 'vue'

const projectStore = useProjectStore()
const ready = ref()

Expand Down Expand Up @@ -51,10 +55,26 @@ provide('show_error', show_error)
provide('clear_error', clear_error)

const route = useRoute()

// 监测路由修改
watch(
computed(() => route.fullPath),
() => clear_error()
() => {
// 清除错误,恢复正常页面
clear_error()
// 清除消息弹窗
message.clear()
}
)

// ---------------------------------- 项目配置 ----------------------------------

const messages = ref(null)

onMounted(() => {
// 注册全局顶部提醒
installMessage(messages)
})
</script>

<style scoped></style>
2 changes: 1 addition & 1 deletion vue/src/assets/iconfont.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions vue/src/components/HomeSiderBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
<!-- 概览区域 -->
<div class="p-4 flex flex-col border-b">
<RouterLink to="/" active-class="active-router">
<SLIcon icon="home" class="w-4 h-4 mr-2" />
<span>{{ $t('sider.nav.home') }}</span>
<SLIcon icon="dashboard" class="w-4 h-4 mr-2" />
<!-- <span>{{ $t('sider.nav.home') }}</span> -->
<span>Project Dashboard</span>
</RouterLink>
</div>
<!-- 实验路由 -->
Expand Down
12 changes: 4 additions & 8 deletions vue/src/components/config-editor/ConfigEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,11 @@ const emits = defineEmits(['modify'])
* 2. 弹窗并不立即消失
* 可以看到,触发 modify 的时候会传递两个参数,一个是修改后的值,一个是回调函数
* 只有在外部的处理函数调用回调的同时,才会结束缓冲操作
*
* 这里为什么要用promise,是因为confirm也接受了一个回调函数,这个回调函数是子组件中传递的,负责取消按钮的处理状态
*/
const confirm = async (newV) => {
new Promise((resolve) => {
emits('modify', newV, () => {
showModal.value = false
resolve()
})
const confirm = async (newV, callback) => {
emits('modify', newV, () => {
showModal.value = false
callback()
})
}
</script>
Expand Down
8 changes: 7 additions & 1 deletion vue/src/components/config-editor/EditorWrap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
import { useProjectStore, useExperimentStroe } from '@swanlab-vue/store'
import { ref } from 'vue'
import SLLoading from '../SLLoading.vue'
import { message } from '@swanlab-vue/components/message'
import { t } from '@swanlab-vue/i18n'

const projectStore = useProjectStore()
const experimentStore = useExperimentStroe()
Expand Down Expand Up @@ -116,7 +118,11 @@ const save = async () => {
if (duplicated) return

handling.value = true
emits('confirm', info.value, () => (handling.value = false))

emits('confirm', info.value, () => {
handling.value = false
message.success(t('common.config-editor.save.success'))
})
}
</script>

Expand Down
38 changes: 38 additions & 0 deletions vue/src/components/message/SLMessage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<div class="px-3 h-10 border border-gray bg-default rounded">
<div class="flex items-center h-full">
<p>{{ message.text }}</p>
</div>
</div>
</template>

<script setup>
/**
* @description: 消息提示组件,用于显示单个消息提示,由SLMessages组件调用,不应该单独使用
* @file: SLMessage.vue
* @property { string } text 消息提示的信息
* @property { string } type 消息提示的类型,可选值为error、success、processing
* @since: 2024-01-03 15:34:01
**/
import { onMounted, onUnmounted } from 'vue'

const props = defineProps({
message: {
type: Object,
required: true
}
})

let timer = null
onMounted(() => {
timer = setTimeout(() => {
props.message.close()
}, props.message.delay)
})

onUnmounted(() => {
clearTimeout(timer)
})
</script>

<style lang="scss" scoped></style>
100 changes: 100 additions & 0 deletions vue/src/components/message/SLMessages.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<template>
<TransitionGroup name="list" tag="ul" class="messages-container">
<SLMessage :message="m" v-for="m in messages" :key="m.id" />
</TransitionGroup>
</template>

<script setup>
/**
* @description: 消息提示组件容器,全局挂载,通过App.vue中的message对象调用
* 此组件暴露一些方法,用于控制消息提示的显示,虽然这样破坏了vue的单向数据流,但是这样做可以减少很多重复代码
* 并且全局理论上只会有一个SLMessages组件,所以这样做也不会有什么问题
* @file: SLMessages.vue
* @since: 2024-01-03 15:34:01
**/
import { ref } from 'vue'
import { debounce } from '@swanlab-vue/utils/common'
import { uuid } from '@swanlab-vue/utils/browser'
import SLMessage from './SLMessage.vue'

const messages = ref([])
// 消息提示的最大数量,超过这个数量,添加新消息时会删除最早的消息
const maxLength = 10

/**
* 添加一个消息提示
* @param { String } message 消息信息
* @param { Number } delay 消息显示的时间,单位为毫秒
* @param { String } type 消息类型,可选值为error、success、processing
* @param { Function } callback 消息被关闭时的回调函数,可选
*/
const add = (message, type = 'error', delay = 2000, callback) => {
const id = uuid()
// 生成一个函数callback,用于关闭该消息提示,并且将它传入BsMessage组件
const close = (id, callback) => () => {
remove(id, callback)
}
messages.value.push({
id,
text: message,
type,
delay,
close: close(id, callback)
})
limitRemoveDebounce()
}
/**
* 清空所有消息提示
*/
const clear = () => {
messages.value = []
}

/**
* 关闭一个消息提示
* @param { String } id 消息提示的id
*/
const remove = (id, callback) => {
// 找到该消息提示的索引
const index = messages.value.findIndex((item) => item.id === id)
// 如果找到了,就将该消息提示从messages中删除,没找到就不管了
if (index !== -1) {
messages.value.splice(index, 1)
// 如果有回调函数,就调用回调函数
callback && callback()
}
}
const limitRemoveDebounce = debounce(() => {
if (messages.value.length > maxLength) {
const m = messages.value[0]
m.close()
}
}, 500)

// ---------------------------------- 暴露给外部的方法 ----------------------------------

defineExpose({
add,
clear
})
</script>

<style lang="scss" scoped>
.messages-container {
@apply fixed top-10 left-1/2 -translate-x-1/2 z-full;
@apply inline-block;
@apply flex flex-col gap-2;
}

// vue动画
.list-enter-active,
.list-leave-active {
transition: all 0.3s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
height: 0;
transform: translateY(-30px);
}
</style>
41 changes: 41 additions & 0 deletions vue/src/components/message/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// ---------------------------------- Message Component,在此处写入组件的使用、注册、导出等信息 ----------------------------------

import SLMessagesVue from './SLMessages.vue'
export const message = {
// 用于存储组件实例
_r: null,
get _ref() {
if (!message._r) console.error('Message组件未注册')
return message._r.value
},
/**
* @description 显示错误消息
* @param {String} text 消息文本
* @param {Number} delay 延迟关闭时间
* @param {Function} onClose 关闭时的回调函数
*/
error: (text, delay, onClose) => {
message._ref.add(text, 'error', delay, onClose)
},
/**
* @description 显示成功消息
* @param {String} text 消息文本
* @param {Number} delay 延迟关闭时间
* @param {Function} onClose 关闭时的回调函数
*/
success: (text, delay, onClose) => {
message._ref.add(text, 'success', delay, onClose)
},
/**
* 清空所有消息
*/
clear: () => {
message._ref.clear()
}
}

export const installMessage = (ref) => {
message._r = ref
}

export const SLMessages = SLMessagesVue
6 changes: 5 additions & 1 deletion vue/src/components/table/SLTable.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="w-full relative">
<div class="relative w-full" :class="maxW">
<div class="w-full overflow-auto border">
<div class="w-full overflow-auto border" :class="data.length ? '' : 'overflow-hidden'">
<table class="w-full">
<!-- 标签用于对表格中的列进行组合,以便对其进行格式化 -->
<colgroup>
Expand Down Expand Up @@ -82,6 +82,10 @@
</tr>
</tbody>
</table>
<!-- 没有数据时,空占位 -->
<div v-if="!data.length" class="flex justify-center py-3">
<div class="data-empty">{{ $t('common.table.empty') }}</div>
</div>
</div>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion vue/src/i18n/en-US/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
},
"save": {
"default": "save changes",
"savimg": "saving"
"savimg": "saving",
"success": "Save Successfully"
}
}
}
9 changes: 9 additions & 0 deletions vue/src/utils/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,12 @@ export const copyTextToClipboard = async (text, callback) => {
export const addTaskToBrowserMainThread = (task) => {
setTimeout(task, 0)
}

/**
* 生成一个uuid,用于标识一个唯一的id
* @param { Number } now 传入一个时间戳,用于生成uuid,如果不传入,默认为当前时间戳
* @returns { String } 生成的uuid
*/
export const uuid = (now = Math.round(new Date() / 1000)) => {
return now.toString(36) + Math.random().toString(36).slice(-4)
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { formatTime } from '@swanlab-vue/utils/time'
import { t } from '@swanlab-vue/i18n'
import { useExperimentStroe, useProjectStore } from '@swanlab-vue/store'
import http from '@swanlab-vue/api/http'
import ConfigEditor from '@swanlab-vue/components/config-editor/ConfigEditor.vue'
// import StopButton from './StopButton.vue'

const experimentStore = useExperimentStroe()
Expand Down