多维表格
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

420 lines
16 KiB

<template>
<div style="height:100%">
<v-tabs @change="this.loadCrons()" height="30" v-model="dbsTab">
<template v-for="db in dbAliasList">
<v-tab :key="db.meta.dbAlias" class="text-capitalize caption">{{ db.connection.database }}
({{ db.meta.dbAlias }})
</v-tab>
<v-tab-item :key="db.meta.dbAlias" style="height:100%">
<!-- <v-toolbar flat height="42" class="toolbar-border-bottom">
<v-toolbar-title>
<v-breadcrumbs :items="[ ]" divider=">" dark large light class="title">
<template v-slot:divider>
<v-icon small color="grey lighten-2">forward</v-icon>
</template>
</v-breadcrumbs>
</v-toolbar-title>
<v-spacer></v-spacer>
<x-btn outlined tooltip="Reload list" small @click="loadCrons()" color="primary" icon="refresh">Reload
</x-btn>
<x-btn outlined tooltip="Add new cron job" small @click="addNewCron()" color="primary" icon="mdi-plus">New
Cron
</x-btn>
<x-btn outlined :loading="updating" :disabled="updating || !selectedItem" tooltip="Save Changes" small
@click="saveCron()"
color="primary" icon="save">Save
</x-btn>
</v-toolbar>-->
<v-card style="height:calc(100% - 42px)">
<v-container style="height: 100%" fluid>
<!-- <div class="d-flex d-100 justify-center">-->
<v-row style="height:100%">
<v-col cols="6">
<v-card class="pt-5 h-100">
<div style="position: relative; " class="mb-4">
<h4 class="text-center" :class="{
'grey--text text--darken-2' : !$store.state.windows.darkTheme
}">Cron Job List</h4>
<div style="position: absolute; right:5px;bottom:0">
<x-btn outlined tooltip="Reload list" small @click="loadCrons()" color="primary" icon="refresh">
Reload
</x-btn>
<x-btn outlined tooltip="Add new cron job" small @click="addNewCron()" color="primary"
icon="mdi-plus">New
Cron
</x-btn>
</div>
</div>
<v-text-field v-if="dbAliasList && dbAliasList[dbsTab]" v-model="filter" dense hide-details
class="my-2 mx-auto"
:placeholder="`Filter '${dbAliasList[dbsTab].connection.database}' scheduled cron jobs`"
prepend-inner-icon="search"
style="max-width:500px"
outlined></v-text-field>
<v-simple-table dense v-slot:default style="min-width: 400px">
<thead>
<tr>
<th class="text-center">#</th>
<th>Cron Title</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-if="crons && !crons.length">
<td class="caption text-center" colspan="3">
No cron jobs are found
</td>
</tr>
<tr v-for="(cron,i) in crons" :key="`${cron.title}-${cron.id}`"
v-if="!filter || (cron.title && cron.title.toLowerCase().indexOf(filter.toLowerCase()) > -1)"
@click="editCronHandler(cron)"
class="pointer"
>
<td>
<v-radio-group dense hide-details v-model="selectedItemIndex" class="mt-n2">
<v-radio :value="i"
></v-radio>
</v-radio-group>
</td>
<td>{{ cron.title }}</td>
<td>
<!-- <v-icon small @click="editCronHandler(cron)">mdi-pencil</v-icon>-->
<x-icon tooltip="Delete cron job" class="ml-2" color="error" @click.stop="deleteCron(cron)"
small>mdi-delete
</x-icon>
</td>
</tr>
</tbody>
</v-simple-table>
</v-card>
</v-col>
<v-col cols="6" v-if="selectedItem" style="height:100%; overflow: auto">
<v-card class="px-4 py-2" style="min-height: 100%">
<v-row class="mt-3">
<v-col cols="12" class="edit-header">
<h4 class="text-center text-capitalize mt-2 d-100" :class="{
'grey--text text--darken-2' : !$store.state.windows.darkTheme
}">{{ selectedItem.title }}</h4>
<div class="save-btn">
<x-btn outlined :loading="updating" :disabled="updating || !selectedItem"
tooltip="Save Changes" small
@click="saveCron()"
color="primary" icon="save">Save
</x-btn>
</div>
<v-switch
class="enable-disable-switch"
inset
dense
hide-details
v-model="selectedItem.active"
:label="selectedItem.active ? 'Enabled' : 'Disabled' "
></v-switch>
</v-col>
<!-- <v-col cols="12">-->
<!-- <v-switch-->
<!-- inset-->
<!-- dense-->
<!-- hide-details-->
<!-- v-model="selectedItem.active"-->
<!-- :label="selectedItem.active ? 'Enabled' : 'Disabled' "-->
<!-- ></v-switch>-->
<!-- </v-col>-->
<v-col cols="12">
<v-text-field auto
dense
hide-details
v-model="selectedItem.title"
outlined label="Title"></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field
dense
hide-details
v-model="selectedItem.pattern"
outlined label="Pattern"></v-text-field>
<span class="caption grey--text">Generate pattern from <a target="_blank"
href="http://corntab.com/"
class="grey--text">http://corntab.com/</a></span>
</v-col>
<v-col cols="6">
<v-text-field
dense
hide-details
v-model="selectedItem.timezone"
outlined label="Timezone"></v-text-field>
<span class="caption grey--text" target="_blank">All timezones available at <a
href="https://momentjs.com/timezone/" class="grey--text">Moment Timezone Website</a>.</span>
</v-col>
<v-col cols="12">
<v-tabs height="30">
<v-tab><span class="caption text-capitalize">Handler</span></v-tab>
<v-tab-item class="pt-2">
<label class="caption grey--text">Cron handler</label>
<monaco-ts-editor v-model="selectedItem.cron_handler"
style="height : 400px"></monaco-ts-editor>
</v-tab-item>
<v-tab><span class="caption text-capitalize">Webhook</span></v-tab>
<v-tab-item class="pt-3">
<v-overlay absolute>
<div class="text-center">Coming Soon...</div>
</v-overlay>
<v-text-field
dense
outlined
label="Webhook url"
type="url"
v-model="selectedItem.webhook"
></v-text-field>
</v-tab-item>
</v-tabs>
</v-col>
<!-- <v-col cols="12">-->
<!-- <v-textarea-->
<!-- dense-->
<!-- hide-details-->
<!-- v-model="selectedItem.description"-->
<!-- dense outlined label="Description"></v-textarea>-->
<!-- </v-col>-->
<!--
<v-col cols="6">
<v-text-field auto
dense
hide-details
v-model="selectedItem.env"
dense outlined label="Environment"></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field auto
dense
hide-details
v-model="selectedItem"
dense outlined label="Timeout(in milliseconds)" type="number"
step="1000"></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field auto dense outlined label="Retry" type="number"
step="1"></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field auto dense outlined label="Retry interval(in milliseconds)" type="number"
step="1000"></v-text-field>
</v-col>-->
</v-row>
</v-card>
</v-col>
</v-row>
</v-container>
</v-card>
<!-- </div>-->
</v-tab-item>
</template>
</v-tabs>
</div>
</template>
<script>
import {mapGetters} from "vuex";
import MonacoJsonEditor from "@/components/monaco/MonacoJsonEditor";
import MonacoTsEditor from "@/components/monaco/MonacoTsEditor";
export default {
name: "cron-jobs",
components: {MonacoTsEditor, MonacoJsonEditor},
data: () => ({
edited: false,
crons: null,
updating: false,
dbsTab: 0,
filter: '',
selectedItem: null
}),
props: ['nodes'],
async mounted() {
await this.loadCrons()
},
methods: {
async editCronHandler(cron) {
this.selectedItem = cron;
},
async deleteCron(cron) {
if (cron.id) {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
dbAlias: this.dbAliasList[this.dbsTab].meta.dbAlias,
env: this.$store.getters['project/GtrEnv']
}, 'cronDelete', {
id: cron.id
}]);
await this.loadCrons();
} else {
this.crons.splice(this.crons.indexOf(cron), 1)
}
if (cron === this.selectedItem) {
this.selectedItem = null;
}
},
async addNewCron() {
this.crons = this.crons || [];
this.crons.push(this.selectedItem = {
title: 'cron_job' + this.crons.length,
pattern: '* * * * * *',
timezone: 'America/Los_Angeles',
active: true,
cron_handler: ''
})
},
async loadCrons() {
this.crons = await this.$store.dispatch('sqlMgr/ActSqlOp', [{
dbAlias: this.dbAliasList[this.dbsTab].meta.dbAlias,
env: this.$store.getters['project/GtrEnv']
}, 'xcCronList']);
if (this.selectedItem) {
this.selectedItem = this.crons.find(c => c.title === this.selectedItem.title);
}
this.edited = false;
}
, async saveCron() {
this.updating = true;
let errorCrons = [];
try {
const saveList = this.crons.filter(cron => {
if (cron === this.selectedItem || !cron.id) {
if (cron.cron_handler.trim() && cron.webhook) {
errorCrons.push(`'${cron.title}'`)
}
return true;
}
return false;
})
// await this.$store.dispatch('sqlMgr/ActSqlOp', [{
// dbAlias: this.dbAliasList[this.dbsTab].meta.dbAlias,
// env: this.$store.getters['project/GtrEnv']
// }, 'xcCronSave', this.selectedItem]);
if (!errorCrons.length) {
for (const cron of saveList) {
// if (cron !== this.selectedItem && !cron.id) {
await this.$store.dispatch('sqlMgr/ActSqlOp', [{
dbAlias: this.dbAliasList[this.dbsTab].meta.dbAlias,
env: this.$store.getters['project/GtrEnv']
}, 'xcCronSave', cron]);
// }
}
await this.loadCrons();
this.$toast.success('Cron job saved successfully').goAway(3000);
} else {
this.$toast.error(`${errorCrons.join(', ')} cron jobs<br> have both webhook and handler, please remove one of them.`).goAway(10000)
}
} catch (e) {
this.$toast.error('Some error occurred').goAway(3000);
console.log(e, e.message);
}
this.updating = false;
this.edited = false;
}
},
computed: {
...mapGetters({
dbAliasList: 'project/GtrDbAliasList'
}),
enableCountText() {
return ''
},
selectedItemIndex: {
get() {
return this.crons ? this.crons.indexOf(this.selectedItem) : -1;
}, set(i) {
this.selectedItem = this.crons[i];
}
}
}
}
</script>
<style scoped lang="scss">
::v-deep {
.v-tabs-bar {
border-bottom: solid 1px var(--v-primary-lighten2);
}
.v-tab {
border-right: 1px solid var(--v-primary-lighten2);
}
.v-input .v-input__slot fieldset legend {
margin-left: 8px;
}
.v-tabs {
height: 100%;
.v-tabs-items {
height: calc(100% - 30px);
.v-window__container {
height: 100%;
}
}
}
}
.edit-header {
position: relative;
width: 100%;
.enable-disable-switch {
position: absolute;
left: 12px;
top: 0;
}
}
.save-btn {
position: absolute !important;
right: 12px;
bottom: 10px;
}
</style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->