1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat(task): Add Display for Number of Running Tasks (#7564)

This commit is contained in:
zhengkunwang 2024-12-26 10:15:57 +08:00 committed by GitHub
parent e11a0d5766
commit 4a4e568d4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 106 additions and 36 deletions

View File

@ -29,3 +29,17 @@ func (b *BaseApi) PageTasks(c *gin.Context) {
Total: total,
})
}
// @Tags TaskLog
// @Summary Get the number of executing tasks
// @Success 200 {object} int64
// @Security ApiKeyAuth
// @Router /logs/tasks/executing/count [get]
func (b *BaseApi) CountExecutingTasks(c *gin.Context) {
count, err := taskService.CountExecutingTask()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, count)
}

View File

@ -19,6 +19,7 @@ type ITaskRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.Task, error)
Update(ctx context.Context, task *model.Task) error
UpdateRunningTaskToFailed() error
CountExecutingTask() (int64, error)
WithByID(id string) DBOption
WithResourceID(id uint) DBOption
@ -95,3 +96,9 @@ func (t TaskRepo) Update(ctx context.Context, task *model.Task) error {
func (t TaskRepo) UpdateRunningTaskToFailed() error {
return getTaskDb(commonRepo.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Updates(map[string]interface{}{"status": constant.StatusFailed, "error_msg": "1Panel restart causes failure"}).Error
}
func (t TaskRepo) CountExecutingTask() (int64, error) {
var count int64
err := getTaskDb(commonRepo.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Count(&count).Error
return count, err
}

View File

@ -10,6 +10,7 @@ type TaskLogService struct{}
type ITaskLogService interface {
Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error)
SyncForRestart() error
CountExecutingTask() (int64, error)
}
func NewITaskService() ITaskLogService {
@ -45,3 +46,7 @@ func (u *TaskLogService) Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, e
func (u *TaskLogService) SyncForRestart() error {
return taskRepo.UpdateRunningTaskToFailed()
}
func (u *TaskLogService) CountExecutingTask() (int64, error) {
return taskRepo.CountExecutingTask()
}

View File

@ -14,5 +14,6 @@ func (s *LogRouter) InitRouter(Router *gin.RouterGroup) {
operationRouter.GET("/system/files", baseApi.GetSystemFiles)
operationRouter.POST("/system", baseApi.GetSystemLogs)
operationRouter.POST("/tasks/search", baseApi.PageTasks)
operationRouter.GET("/tasks/executing/count", baseApi.CountExecutingTasks)
}
}

View File

@ -24,3 +24,7 @@ export const cleanLogs = (param: Log.CleanLog) => {
export const searchTasks = (req: Log.SearchTaskReq) => {
return http.post<ResPage<Log.Task>>(`/logs/tasks/search`, req);
};
export const countExecutingTask = () => {
return http.get<number>(`/tasks/executing/count`);
};

View File

@ -1,7 +1,7 @@
<template>
<el-tooltip v-if="msg" effect="dark" placement="bottom">
<template #content>
<div style="width: 300px; word-break: break-all">{{ msg }}</div>
<div class="content">{{ msg }}</div>
</template>
<el-tag size="small" :type="getType(statusItem)" round effect="light">
<span class="flx-align-center">
@ -19,6 +19,10 @@
<el-icon v-if="loadingIcon(statusItem)" class="is-loading">
<Loading />
</el-icon>
<el-icon size="15" v-if="operate">
<CaretRight v-if="statusItem == 'running'" />
<CaretBottom v-if="statusItem == 'stopped'" />
</el-icon>
</span>
</el-tag>
</template>
@ -29,6 +33,11 @@ import { computed } from 'vue';
const props = defineProps({
status: String,
msg: String,
operate: {
type: Boolean,
default: false,
required: false,
},
});
const statusItem = computed(() => {
@ -88,3 +97,10 @@ const loadingIcon = (status: string): boolean => {
return loadingStatus.indexOf(status) > -1;
};
</script>
<style lang="scss" scoped>
.content {
width: 300px;
word-break: break-all;
}
</style>

View File

@ -19,11 +19,9 @@ const props = defineProps({
default: '#005eeb',
},
});
// iconfont
const iconClassName = computed(() => {
return `#${props.iconName}`;
});
//
const svgClass = computed(() => {
if (props.className) {
return `svg-icon ${props.className}`;

View File

@ -39,5 +39,5 @@ defineProps<{ menuList: RouteRecordRaw[] }>();
</script>
<style scoped lang="scss">
@import '../index.scss';
@use '../index.scss';
</style>

View File

@ -1,3 +1,4 @@
@use '@/styles/var.scss' as *;
.el-menu {
user-select: none;
background: none;

View File

@ -7,24 +7,37 @@
element-loading-background="rgba(122, 122, 122, 0.01)"
>
<Logo :isCollapse="isCollapse" />
<span v-if="nodes.length !== 0" class="el-dropdown-link">
<div class="el-dropdown-link flex justify-between items-center">
<el-button type="text" @click="openChangeNode" @mouseenter="openChangeNode">
<span>
{{ loadCurrentName() }}
</span>
<el-dropdown v-if="nodes.length !== 0" placement="right-start" @command="changeNode">
<el-icon class="ico"><Switch /></el-icon>
</el-button>
<div>
<el-dropdown
ref="nodeChangeRef"
trigger="contextmenu"
v-if="nodes.length > 0"
placement="right-start"
@command="changeNode"
>
<span></span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="local">
<SvgIcon class="ico" iconName="p-host" />
{{ $t('terminal.local') }}
</el-dropdown-item>
<el-dropdown-item v-for="item in nodes" :key="item.name" :command="item.name">
<SvgIcon class="ico" iconName="p-host" />
{{ item.name }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<el-tag type="danger" size="small" effect="light" class="mr-2">{{ taskCount }}</el-tag>
</div>
<el-scrollbar>
<el-menu
:default-active="activeMenu"
@ -59,16 +72,18 @@ import SubItem from './components/SubItem.vue';
import router, { menuList } from '@/routers/router';
import { logOutApi } from '@/api/modules/auth';
import i18n from '@/lang';
import { ElMessageBox } from 'element-plus';
import { DropdownInstance, ElMessageBox } from 'element-plus';
import { GlobalStore, MenuStore } from '@/store';
import { MsgSuccess } from '@/utils/message';
import { isString } from '@vueuse/core';
import { getSettingInfo, listNodeOptions } from '@/api/modules/setting';
import { countExecutingTask } from '@/api/modules/log';
const route = useRoute();
const menuStore = MenuStore();
const globalStore = GlobalStore();
const nodes = ref([]);
const nodeChangeRef = ref<DropdownInstance>();
defineProps({
menuRouter: {
type: Boolean,
@ -86,6 +101,10 @@ let routerMenus = computed((): RouteRecordRaw[] => {
return menuStore.menuList.filter((route) => route.meta && !route.meta.hideInSidebar);
});
const openChangeNode = () => {
nodeChangeRef.value?.handleOpen();
};
const loadCurrentName = () => {
if (globalStore.currentNode) {
return globalStore.currentNode === 'local' ? i18n.global.t('terminal.local') : globalStore.currentNode;
@ -223,15 +242,26 @@ const search = async () => {
menuStore.menuList = rstMenuList;
};
const taskCount = ref(0);
const checkTask = async () => {
try {
const res = await countExecutingTask();
taskCount.value = res.data;
} catch (error) {
console.error(error);
}
};
onMounted(() => {
menuStore.setMenuList(menuList);
search();
loadNodes();
checkTask();
});
</script>
<style lang="scss">
@import './index.scss';
@use './index.scss';
.sidebar-container {
position: relative;
@ -252,20 +282,13 @@ onMounted(() => {
.el-dropdown-link {
margin-top: -5px;
margin-left: 30px;
margin-left: 15px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
height: 28px;
height: 38px;
}
.ico {
margin-top: -20px;
display: flex;
float: left;
position: absolute;
right: 25px;
height: 20px !important;
}
</style>

View File

@ -732,7 +732,7 @@ onUnmounted(() => {
</script>
<style scoped lang="scss">
@import '../index.scss';
@use '../index.scss';
@media only screen and (max-width: 1300px) {
.install-card-col-12 {
max-width: 100%;

View File

@ -138,10 +138,11 @@
<template #default="{ row }">
<Status
v-if="row.status === 'Running'"
:operate="true"
:status="row.status"
@click="opWebsite('stop', row.id)"
/>
<Status v-else :status="row.status" @click="opWebsite('start', row.id)" />
<Status v-else :status="row.status" :operate="true" @click="opWebsite('start', row.id)" />
</template>
</el-table-column>
<el-table-column