feat: 增加定时刷新组件

This commit is contained in:
ssongliu 2023-02-10 15:55:56 +08:00 committed by ssongliu
parent 1d3738be39
commit 3f3d24648b
26 changed files with 318 additions and 151 deletions

4
.gitignore vendored
View File

@ -5,10 +5,6 @@
*.so
*.dylib
.idea
cmd/server/web/assets/
cmd/server/web/monacoeditorwork
cmd/server/web/favicon.ico
cmd/server/web/index.html
build

View File

@ -38,12 +38,12 @@ func (b *BaseApi) CreateSnapshot(c *gin.Context) {
// @Summary Page system snapshot
// @Description 获取系统快照列表分页
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /settings/snapshot/search [post]
func (b *BaseApi) SearchSnapshot(c *gin.Context) {
var req dto.PageInfo
var req dto.SearchWithPage
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return

View File

@ -25,7 +25,7 @@ type SnapshotService struct {
}
type ISnapshotService interface {
SearchWithPage(req dto.PageInfo) (int64, interface{}, error)
SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error)
SnapshotCreate(req dto.SnapshotCreate) error
SnapshotRecover(req dto.SnapshotRecover) error
SnapshotRollback(req dto.SnapshotRecover) error
@ -38,8 +38,8 @@ func NewISnapshotService() ISnapshotService {
return &SnapshotService{}
}
func (u *SnapshotService) SearchWithPage(req dto.PageInfo) (int64, interface{}, error) {
total, systemBackups, err := snapshotRepo.Page(req.Page, req.PageSize)
func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
total, systemBackups, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info))
var dtoSnap []dto.SnapshotInfo
for _, systemBackup := range systemBackups {
var item dto.SnapshotInfo
@ -90,7 +90,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
Description: req.Description,
From: req.From,
Version: versionItem.Value,
Status: constant.StatusSuccess,
Status: constant.StatusWaiting,
}
_ = snapshotRepo.Create(&snap)
_ = settingRepo.Update("SystemStatus", "Snapshoting")
@ -134,11 +134,10 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
return
}
if err := u.handlePanelDatas(fileOp, "snapshot", global.CONF.BaseDir+"/1panel", backupPanelDir, localDir, dockerDataDir); err != nil {
if err := u.handlePanelDatas(snap.ID, fileOp, "snapshot", global.CONF.BaseDir+"/1panel", backupPanelDir, localDir, dockerDataDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusWaiting})
snapJson := SnapshotJson{DockerDataDir: dockerDataDir, BackupDataDir: localDir, PanelDataDir: global.CONF.BaseDir + "/1panel", LiveRestoreEnabled: liveRestoreStatus}
if err := u.saveJson(snapJson, rootDir); err != nil {
@ -154,6 +153,7 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
_ = settingRepo.Update("SystemStatus", "Free")
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
localPath := fmt.Sprintf("%s/system/1panel_snapshot_%s.tar.gz", localDir, timeNow)
if ok, err := backupAccont.Upload(localPath, fmt.Sprintf("system_snapshot/1panel_snapshot_%s.tar.gz", timeNow)); err != nil || !ok {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
@ -311,7 +311,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
}
if !isReTry || snap.InterruptStep == "1PanelData" {
if err := u.handlePanelDatas(fileOp, operation, rootDir, snapJson.PanelDataDir, localDir, snapJson.OldDockerDataDir); err != nil {
if err := u.handlePanelDatas(snap.ID, fileOp, operation, rootDir, snapJson.PanelDataDir, localDir, snapJson.OldDockerDataDir); err != nil {
updateRecoverStatus(snap.ID, "1PanelData", constant.StatusFailed, err.Error())
return
}
@ -344,89 +344,92 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
if _, err := os.Stat(u.OriginalPath); err != nil && os.IsNotExist(err) {
return fmt.Errorf("load original dir failed, err: %s", err)
}
_ = settingRepo.Update("SystemStatus", "Rollbacking")
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"rollback_status": constant.StatusWaiting})
snapJson, err := u.readFromJson(fmt.Sprintf("%s/snapshot.json", rootDir))
if err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("decompress file failed, err: %v", err))
return err
}
_, _ = cmd.Exec("systemctl stop docker")
if err := u.handleDockerDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldDockerDataDir); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "DockerDir" {
_, _ = cmd.Exec("systemctl restart docker")
return nil
}
if err := u.handleDaemonJson(fileOp, "rollback", u.OriginalPath+"/daemon.json", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "DaemonJson" {
_, _ = cmd.Exec("systemctl restart docker")
return nil
}
if snapJson.LiveRestoreEnabled {
if err := u.updateLiveRestore(true); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
go func() {
snapJson, err := u.readFromJson(fmt.Sprintf("%s/snapshot.json", rootDir))
if err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("decompress file failed, err: %v", err))
return
}
}
if snap.InterruptStep == "UpdateLiveRestore" {
_, _ = cmd.Exec("systemctl restart dockere")
return nil
}
if err := u.handlePanelBinary(fileOp, "rollback", u.OriginalPath+"/1panel", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "1PanelBinary" {
return nil
}
_, _ = cmd.Exec("systemctl stop docker")
if err := u.handleDockerDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldDockerDataDir); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "DockerDir" {
_, _ = cmd.Exec("systemctl restart docker")
return
}
if err := u.handlePanelctlBinary(fileOp, "rollback", u.OriginalPath+"/1pctl", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "1PctlBinary" {
return nil
}
if err := u.handleDaemonJson(fileOp, "rollback", u.OriginalPath+"/daemon.json", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "DaemonJson" {
_, _ = cmd.Exec("systemctl restart docker")
return
}
if snapJson.LiveRestoreEnabled {
if err := u.updateLiveRestore(true); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
}
if snap.InterruptStep == "UpdateLiveRestore" {
_, _ = cmd.Exec("systemctl restart dockere")
return
}
if err := u.handlePanelService(fileOp, "rollback", u.OriginalPath+"/1panel.service", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "1PanelService" {
if err := u.handlePanelBinary(fileOp, "rollback", u.OriginalPath+"/1panel", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "1PanelBinary" {
return
}
if err := u.handlePanelctlBinary(fileOp, "rollback", u.OriginalPath+"/1pctl", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "1PctlBinary" {
return
}
if err := u.handlePanelService(fileOp, "rollback", u.OriginalPath+"/1panel.service", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "1PanelService" {
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
return
}
if err := u.handleBackupDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldBackupDataDir); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "1PanelBackups" {
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
return
}
if err := u.handlePanelDatas(snap.ID, fileOp, "rollback", u.OriginalPath, snapJson.OldPanelDataDir, "", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if snap.InterruptStep == "1PanelData" {
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
return
}
_ = os.RemoveAll(rootDir)
global.LOG.Info("rollback successful")
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
return nil
}
if err := u.handleBackupDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldBackupDataDir); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "1PanelBackups" {
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
return nil
}
if err := u.handlePanelDatas(fileOp, "rollback", u.OriginalPath, snapJson.OldPanelDataDir, "", ""); err != nil {
updateRollbackStatus(snap.ID, constant.StatusFailed, err.Error())
return err
}
if snap.InterruptStep == "1PanelData" {
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
return nil
}
_ = os.RemoveAll(rootDir)
global.LOG.Info("rollback successful")
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
updateRollbackStatus(snap.ID, constant.StatusSuccess, "")
}()
return nil
}
@ -604,7 +607,7 @@ func (u *SnapshotService) handleBackupDatas(fileOp files.FileOp, operation strin
return nil
}
func (u *SnapshotService) handlePanelDatas(fileOp files.FileOp, operation string, source, target, backupDir, dockerDir string) error {
func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, operation string, source, target, backupDir, dockerDir string) error {
switch operation {
case "snapshot":
exclusionRules := "./tmp;./cache;"
@ -614,9 +617,12 @@ func (u *SnapshotService) handlePanelDatas(fileOp files.FileOp, operation string
if strings.Contains(dockerDir, source) {
exclusionRules += ("." + strings.ReplaceAll(dockerDir, source, "") + ";")
}
_ = snapshotRepo.Update(snapID, map[string]interface{}{"status": constant.StatusSuccess})
if err := u.handleTar(source, target, "1panel_data.tar.gz", exclusionRules); err != nil {
return fmt.Errorf("backup panel data failed, err: %v", err)
}
_ = snapshotRepo.Update(snapID, map[string]interface{}{"status": constant.StatusWaiting})
case "recover":
exclusionRules := "./tmp/;./cache;"
if strings.Contains(backupDir, target) {
@ -625,9 +631,12 @@ func (u *SnapshotService) handlePanelDatas(fileOp files.FileOp, operation string
if strings.Contains(dockerDir, target) {
exclusionRules += ("." + strings.ReplaceAll(dockerDir, target, "") + ";")
}
_ = snapshotRepo.Update(snapID, map[string]interface{}{"recover_status": ""})
if err := u.handleTar(target, u.OriginalPath, "1panel_data.tar.gz", exclusionRules); err != nil {
return fmt.Errorf("restore original panel data failed, err: %v", err)
}
_ = snapshotRepo.Update(snapID, map[string]interface{}{"recover_status": constant.StatusWaiting})
if err := u.handleUnTar(source+"/1panel/1panel_data.tar.gz", target); err != nil {
return fmt.Errorf("recover panel data failed, err: %v", err)
@ -701,6 +710,7 @@ func updateRecoverStatus(id uint, interruptStep, status string, message string)
_ = settingRepo.Update("SystemStatus", "Free")
}
func updateRollbackStatus(id uint, status string, message string) {
_ = settingRepo.Update("SystemStatus", "Free")
if status == constant.StatusSuccess {
if err := snapshotRepo.Update(id, map[string]interface{}{
"recover_status": "",

View File

@ -1,11 +1,12 @@
package constant
const (
StatusRunning = "Running"
StatusStoped = "Stoped"
StatusWaiting = "Waiting"
StatusSuccess = "Success"
StatusFailed = "Failed"
StatusEnable = "Enable"
StatusDisable = "Disable"
StatusRunning = "Running"
StatusStoped = "Stoped"
StatusWaiting = "Waiting"
StatusSuccess = "Success"
StatusFailed = "Failed"
StatusUploading = "Uploading"
StatusEnable = "Enable"
StatusDisable = "Disable"
)

View File

@ -28,26 +28,26 @@ var doc = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/apps/:id": {
"/apps/:key": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "通过 id 获取应用信息",
"description": "通过 key 获取应用信息",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Search app by id",
"summary": "Search app by key",
"parameters": [
{
"type": "integer",
"description": "app id",
"name": "id",
"type": "string",
"description": "app key",
"name": "key",
"in": "path",
"required": true
}
@ -6489,7 +6489,7 @@ var doc = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PageInfo"
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
@ -10668,6 +10668,9 @@ var doc = `{
"name": {
"type": "string"
},
"recommend": {
"type": "integer"
},
"required": {
"type": "string"
},
@ -11105,6 +11108,9 @@ var doc = `{
"pageSize": {
"type": "integer"
},
"recommend": {
"type": "boolean"
},
"tags": {
"type": "array",
"items": {
@ -11985,6 +11991,9 @@ var doc = `{
"name": {
"type": "string"
},
"recommend": {
"type": "integer"
},
"required": {
"type": "string"
},

View File

@ -14,26 +14,26 @@
"host": "localhost",
"basePath": "/api/v1",
"paths": {
"/apps/:id": {
"/apps/:key": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "通过 id 获取应用信息",
"description": "通过 key 获取应用信息",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Search app by id",
"summary": "Search app by key",
"parameters": [
{
"type": "integer",
"description": "app id",
"name": "id",
"type": "string",
"description": "app key",
"name": "key",
"in": "path",
"required": true
}
@ -6475,7 +6475,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.PageInfo"
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
@ -10654,6 +10654,9 @@
"name": {
"type": "string"
},
"recommend": {
"type": "integer"
},
"required": {
"type": "string"
},
@ -11091,6 +11094,9 @@
"pageSize": {
"type": "integer"
},
"recommend": {
"type": "boolean"
},
"tags": {
"type": "array",
"items": {
@ -11971,6 +11977,9 @@
"name": {
"type": "string"
},
"recommend": {
"type": "integer"
},
"required": {
"type": "string"
},

View File

@ -1496,6 +1496,8 @@ definitions:
type: integer
name:
type: string
recommend:
type: integer
required:
type: string
shortDesc:
@ -1782,6 +1784,8 @@ definitions:
type: integer
pageSize:
type: integer
recommend:
type: boolean
tags:
items:
type: string
@ -2375,6 +2379,8 @@ definitions:
type: integer
name:
type: string
recommend:
type: integer
required:
type: string
shortDesc:
@ -2629,17 +2635,17 @@ info:
title: 1Panel
version: "1.0"
paths:
/apps/:id:
/apps/:key:
get:
consumes:
- application/json
description: 通过 id 获取应用信息
description: 通过 key 获取应用信息
parameters:
- description: app id
- description: app key
in: path
name: id
name: key
required: true
type: integer
type: string
responses:
"200":
description: OK
@ -2647,7 +2653,7 @@ paths:
$ref: '#/definitions/response.AppDTO'
security:
- ApiKeyAuth: []
summary: Search app by id
summary: Search app by key
tags:
- App
/apps/detail/:appId/:version:
@ -6736,7 +6742,7 @@ paths:
name: request
required: true
schema:
$ref: '#/definitions/dto.PageInfo'
$ref: '#/definitions/dto.SearchWithPage'
responses:
"200":
description: OK

View File

@ -60,7 +60,6 @@ declare module 'vue' {
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSpan: typeof import('element-plus/es')['ElSpan']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
@ -83,6 +82,7 @@ declare module 'vue' {
Status: typeof import('./src/components/status/index.vue')['default']
SubItem: typeof import('./src/components/app-layout/menu/components/sub-item.vue')['default']
SvgIcon: typeof import('./src/components/svg-icon/svg-icon.vue')['default']
TableSetting: typeof import('./src/components/table-setting/index.vue')['default']
}
}

View File

@ -1,5 +1,5 @@
import http from '@/api';
import { ReqPage, ResPage } from '../interface';
import { ResPage, SearchWithPage } from '../interface';
import { Setting } from '../interface/setting';
export const getSettingInfo = () => {
@ -62,7 +62,7 @@ export const snapshotRecover = (param: Setting.SnapshotRecover) => {
export const snapshotRollback = (param: Setting.SnapshotRecover) => {
return http.post(`/settings/snapshot/rollback`, param);
};
export const searchSnapshotPage = (param: ReqPage) => {
export const searchSnapshotPage = (param: SearchWithPage) => {
return http.post<ResPage<Setting.SnapshotInfo>>(`/settings/snapshot/search`, param);
};

View File

@ -65,7 +65,7 @@ const loadStatus = async () => {
clearInterval(Number(timer));
timer = null;
});
}, 1000 * 5);
}, 1000 * 20);
}
};

View File

@ -0,0 +1,79 @@
<template>
<div>
<el-popover placement="bottom-start" :width="200" trigger="click">
<template #reference>
<el-button round class="timer-button">{{ $t('commons.table.tableSetting') }}</el-button>
</template>
<div style="margin-left: 15px">
<div>
<span>{{ $t('commons.table.autoRefresh') }}</span>
<el-switch style="margin-left: 5px" v-model="autoRefresh" @change="changeRefresh"></el-switch>
</div>
<div>
<span>{{ $t('commons.table.refreshRate') }}</span>
<el-select style="margin-left: 5px; width: 80px" v-model="refreshRate" @change="changeRefresh">
<el-option label="5s" :value="5"></el-option>
<el-option label="10s" :value="10"></el-option>
<el-option label="30s" :value="30"></el-option>
<el-option label="1min" :value="60"></el-option>
<el-option label="2min" :value="120"></el-option>
<el-option label="5min" :value="300"></el-option>
</el-select>
</div>
</div>
</el-popover>
</div>
</template>
<script setup lang="ts">
import { onUnmounted, ref } from 'vue';
defineOptions({ name: 'TableSetting' });
const autoRefresh = ref<boolean>(false);
const refreshRate = ref<number>(10);
const emit = defineEmits(['search']);
let timer: NodeJS.Timer | null = null;
const changeRefresh = () => {
if (autoRefresh.value) {
if (timer) {
clearInterval(Number(timer));
timer = null;
}
timer = setInterval(() => {
emit('search');
}, 1000 * refreshRate.value);
} else {
if (timer) {
clearInterval(Number(timer));
timer = null;
}
}
};
const startTimer = () => {
autoRefresh.value = true;
changeRefresh();
};
const endTimer = () => {
autoRefresh.value = false;
changeRefresh();
};
onUnmounted(() => {
clearInterval(Number(timer));
timer = null;
});
defineExpose({
startTimer,
endTimer,
});
</script>
<style lang="scss" scoped>
.timer-button {
float: right;
}
</style>

View File

@ -61,12 +61,16 @@ export default {
description: 'Description',
interval: 'Interval',
title: 'Title',
tableSetting: 'Table setting',
autoRefresh: 'Auto refresh',
refreshRate: 'Refresh rate',
},
loadingText: {
Upgrading: 'System upgrade, please wait...',
Restarting: 'System restart, please wait...',
Snapshoting: 'Making snapshots, please wait...',
Recovering: 'Recovering from snapshot, please wait...',
Rollbacking: 'Rollbacking from snapshot, please wait...',
},
msg: {
delete: 'This operation cannot be rolled back. Do you want to continue',
@ -155,6 +159,7 @@ export default {
error: 'error',
created: 'created',
restarting: 'restarting',
uploading: 'uploading',
removing: 'removing',
paused: 'paused',
exited: 'exited',

View File

@ -64,12 +64,16 @@ export default {
description: '描述信息',
interval: '耗时',
title: '标题',
tableSetting: '列表设置',
autoRefresh: '定时刷新',
refreshRate: '刷新频率',
},
loadingText: {
Upgrading: '系统升级中请稍候...',
Restarting: '系统重启中请稍候...',
Snapshoting: '制作快照中请稍候...',
Recovering: '从快照恢复中请稍候...',
Rollbacking: '快照回滚中请稍候...',
},
msg: {
delete: '删除 操作不可回滚是否继续',
@ -165,6 +169,7 @@ export default {
error: '失败',
created: '已创建',
restarting: '重启中',
uploading: '上传中',
unhealthy: '异常',
removing: '迁移中',
paused: '暂停',

View File

@ -19,12 +19,13 @@
>
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onOpenDialog()">
{{ $t('container.createCompose') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -88,6 +89,7 @@
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import { reactive, onMounted, ref } from 'vue';
import LayoutContent from '@/layout/layout-content.vue';
import EditDialog from '@/views/container/compose/edit/index.vue';

View File

@ -14,7 +14,7 @@
>
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onCreate()">
{{ $t('container.createContainer') }}
</el-button>
@ -42,7 +42,8 @@
</el-button>
</el-button-group>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -118,6 +119,7 @@
<script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import ReNameDialog from '@/views/container/container/rename/index.vue';
import CreateDialog from '@/views/container/container/create/index.vue';
import MonitorDialog from '@/views/container/container/monitor/index.vue';

View File

@ -47,6 +47,7 @@ interface DialogProps {
tags: Array<string>;
}
const acceptParams = (params: DialogProps) => {
isByID.value = false;
deleteVisiable.value = true;
deleteForm.id = params.id.replaceAll('sha256:', '').substring(0, 12);
deleteForm.deleteTags = [];

View File

@ -11,7 +11,7 @@
<LayoutContent v-loading="loading" :title="$t('container.image')" :class="{ mask: dockerStatus != 'Running' }">
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" plain @click="onOpenPull">
{{ $t('container.imagePull') }}
</el-button>
@ -22,7 +22,8 @@
{{ $t('container.build') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -75,6 +76,7 @@
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import { reactive, onMounted, ref } from 'vue';
import { dateFormatSimple } from '@/utils/util';
import { Container } from '@/api/interface/container';

View File

@ -15,7 +15,7 @@
>
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onCreate()">
{{ $t('container.createNetwork') }}
</el-button>
@ -23,7 +23,8 @@
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -99,6 +100,7 @@
<script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import CreateDialog from '@/views/container/network/create/index.vue';
import CodemirrorDialog from '@/components/codemirror-dialog/codemirror.vue';
import { reactive, onMounted, ref } from 'vue';

View File

@ -11,7 +11,7 @@
<LayoutContent v-loading="loading" :title="$t('container.repo')" :class="{ mask: dockerStatus != 'Running' }">
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('container.createRepo') }}
</el-button>
@ -19,7 +19,8 @@
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -78,6 +79,7 @@
<script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import OperatorDialog from '@/views/container/repo/operator/index.vue';
import DeleteDialog from '@/views/container/repo/delete/index.vue';
import { reactive, onMounted, ref } from 'vue';

View File

@ -15,7 +15,7 @@
>
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('container.createComposeTemplate') }}
</el-button>
@ -23,7 +23,8 @@
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -102,6 +103,7 @@
<script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';

View File

@ -11,7 +11,7 @@
<LayoutContent v-loading="loading" :title="$t('container.volume')" :class="{ mask: dockerStatus != 'Running' }">
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onCreate()">
{{ $t('container.createVolume') }}
</el-button>
@ -19,7 +19,8 @@
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -84,6 +85,7 @@
<script lang="ts" setup>
import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import CreateDialog from '@/views/container/volume/create/index.vue';
import CodemirrorDialog from '@/components/codemirror-dialog/codemirror.vue';
import { reactive, onMounted, ref } from 'vue';

View File

@ -11,7 +11,7 @@
<LayoutContent v-loading="loading" v-if="!isRecordShow" :title="$t('cronjob.cronTask')">
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('commons.button.create') }}{{ $t('cronjob.cronTask') }}
</el-button>
@ -19,7 +19,8 @@
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -124,6 +125,7 @@
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import OperatrDialog from '@/views/cronjob/operate/index.vue';
import Records from '@/views/cronjob/record/index.vue';
import LayoutContent from '@/layout/layout-content.vue';

View File

@ -3,12 +3,13 @@
<LayoutContent v-loading="loading" :title="$t('logs.login')">
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" plain @click="onClean()">
{{ $t('logs.deleteLogs') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchIP"
@ -64,6 +65,7 @@
<script setup lang="ts">
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import LayoutContent from '@/layout/layout-content.vue';
import { dateFormat } from '@/utils/util';

View File

@ -3,12 +3,13 @@
<LayoutContent v-loading="loading" :title="$t('logs.operation')">
<template #toolbar>
<el-row>
<el-col :span="20">
<el-col :span="16">
<el-button type="primary" plain @click="onClean()">
{{ $t('logs.deleteLogs') }}
</el-button>
</el-col>
<el-col :span="4">
<el-col :span="8">
<TableSetting @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
@ -91,6 +92,7 @@
<script setup lang="ts">
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { dateFormat } from '@/utils/util';
import LayoutContent from '@/layout/layout-content.vue';

View File

@ -2,12 +2,30 @@
<div>
<LayoutContent v-loading="loading" v-if="!isRecordShow" :title="$t('setting.snapshot')">
<template #toolbar>
<el-button type="primary" @click="onCreate()">
{{ $t('setting.createSnapshot') }}
</el-button>
<el-button type="primary" plain :disabled="selects.length === 0" @click="batchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
<el-row>
<el-col :span="16">
<el-button type="primary" @click="onCreate()">
{{ $t('setting.createSnapshot') }}
</el-button>
<el-button type="primary" plain :disabled="selects.length === 0" @click="batchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</el-col>
<el-col :span="8">
<TableSetting ref="timerRef" @search="search()" />
<div class="search-button">
<el-input
v-model="searchName"
clearable
@clear="search()"
suffix-icon="Search"
@keyup.enter="search()"
@blur="search()"
:placeholder="$t('commons.button.search')"
></el-input>
</div>
</el-col>
</el-row>
</template>
<template #main>
<ComplexTable
@ -40,6 +58,9 @@
<el-tag v-if="row.status === 'Waiting'" type="info">
{{ $t('commons.table.statusWaiting') }}
</el-tag>
<el-tag v-if="row.status === 'Uploading'" type="info">
{{ $t('commons.status.uploading') }}...
</el-tag>
<el-tooltip
v-if="row.status === 'Failed'"
class="box-item"
@ -67,7 +88,7 @@
</ComplexTable>
</template>
</LayoutContent>
<RecoverStatus @search="search()" ref="recoverStatusRef"></RecoverStatus>
<RecoverStatus ref="recoverStatusRef" @search="search()"></RecoverStatus>
<el-drawer v-model="drawerVisiable" size="50%">
<template #header>
<DrawerHeader :header="$t('setting.createSnapshot')" :back="handleClose" />
@ -117,6 +138,7 @@
<script setup lang="ts">
import ComplexTable from '@/components/complex-table/index.vue';
import TableSetting from '@/components/table-setting/index.vue';
import { snapshotCreate, searchSnapshotPage, snapshotDelete } from '@/api/modules/setting';
import { onMounted, reactive, ref } from 'vue';
import { dateFormat } from '@/utils/util';
@ -139,6 +161,7 @@ const paginationConfig = reactive({
pageSize: 10,
total: 0,
});
const searchName = ref();
const recoverStatusRef = ref();
const isRecordShow = ref();
@ -231,6 +254,7 @@ const buttons = [
const search = async () => {
let params = {
info: searchName.value,
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
};

View File

@ -198,7 +198,7 @@ const acceptParams = (params: DialogProps): void => {
snapInfo.value = params.snapInfo;
drawerVisiable.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();
const emit = defineEmits(['search']);
const handleClose = () => {
drawerVisiable.value = false;
@ -211,6 +211,7 @@ const doRecover = async (isNew: boolean) => {
emit('search');
loading.value = false;
dialogVisiable.value = false;
drawerVisiable.value = false;
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
@ -240,6 +241,7 @@ const rollbackSnapshot = async () => {
emit('search');
loading.value = false;
dialogVisiable.value = false;
drawerVisiable.value = false;
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {