mirror of
https://github.com/jumpserver/lina.git
synced 2026-01-29 21:28:52 +00:00
perf: Create a job that supports adding node parameters (#4412)
* perf: Create a job that supports adding node parameters * feat: add variables component * perf: Update file tip position * feat: Add variable support to job from template * feat: Parameters can be set when running job * feat: Supports setting variable type * feat: Supports running adhoc with parameters * feat: Supports running playbook with parameters * feat: Support setting variables for scheduled tasks * perf: Translate --------- Co-authored-by: wangruidong <940853815@qq.com>
This commit is contained in:
93
src/components/Apps/VariableCreateUpdateForm/index.vue
Normal file
93
src/components/Apps/VariableCreateUpdateForm/index.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<AutoDataForm
|
||||
ref="AutoDataForm"
|
||||
class="variable-add"
|
||||
:submit-btn-text="submitBtnText"
|
||||
v-bind="$data"
|
||||
@submit="confirm"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'VariableCreateForm',
|
||||
components: {
|
||||
AutoDataForm
|
||||
},
|
||||
props: {
|
||||
variable: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
submitBtnText: this.$t('Confirm'),
|
||||
url: '/api/v1/ops/variable/',
|
||||
form: Object.assign({ 'on_invalid': 'error' }, this.variable || {}),
|
||||
fields: [
|
||||
['', ['name', 'var_name', 'type', 'text_default_value', 'select_default_value', 'extra_args', 'tips', 'required']]
|
||||
],
|
||||
fieldsMeta: {
|
||||
text_default_value: {
|
||||
label: this.$t('DefaultValue'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.type !== 'text'
|
||||
},
|
||||
el: {
|
||||
type: 'input'
|
||||
}
|
||||
},
|
||||
select_default_value: {
|
||||
label: this.$t('DefaultValue'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.type !== 'select'
|
||||
},
|
||||
el: { type: 'input' }
|
||||
},
|
||||
extra_args: {
|
||||
hidden: (formValue) => {
|
||||
return formValue.type !== 'select'
|
||||
},
|
||||
el: { type: 'textarea', rows: 4, placeholder: this.$t('ExtraArgsPlaceholder') }
|
||||
}
|
||||
},
|
||||
hasSaveContinue: false,
|
||||
method: 'get'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm(form) {
|
||||
if (this.variable?.name) {
|
||||
this.$emit('edit', form)
|
||||
} else {
|
||||
this.$emit('add', form)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.variable-add {
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.help-block {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .form-group-header {
|
||||
.hr-line-dashed {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
74
src/components/Apps/VariableSetForm/index.vue
Normal file
74
src/components/Apps/VariableSetForm/index.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<AutoDataForm
|
||||
ref="AutoDataForm"
|
||||
class="variable-set"
|
||||
:submit-btn-text="submitBtnText"
|
||||
v-bind="$data"
|
||||
:fields="fields"
|
||||
@submit="confirm"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AutoDataForm from '@/components/Form/AutoDataForm/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'VariableSetForm',
|
||||
components: {
|
||||
AutoDataForm
|
||||
},
|
||||
props: {
|
||||
formData: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
queryParam: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
submitBtnText: this.$t('Confirm'),
|
||||
// 防止缓存form remoteMeta
|
||||
url: `/api/v1/ops/variable/form_data/?t=${new Date().getTime()}&` + this.queryParam,
|
||||
form: {},
|
||||
hasSaveContinue: false,
|
||||
method: 'get',
|
||||
hasReset: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return [['', this.formData.map(item => item.var_name)]]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm(form) {
|
||||
this.$emit('confirm', form)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.variable-set {
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.help-block {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .form-group-header {
|
||||
.hr-line-dashed {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -52,6 +52,7 @@ export default {
|
||||
title: this.$tc('Select'),
|
||||
name: 'select',
|
||||
can: true,
|
||||
type: 'primary',
|
||||
callback: ({ row }) => {
|
||||
this.$emit('select', row)
|
||||
this.iVisible = false
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
:visible.sync="showOpenAdhocSaveDialog"
|
||||
/>
|
||||
<VariableHelpDialog :visible.sync="showHelpDialog" />
|
||||
<setVariableDialog
|
||||
:form-data="variableFormData"
|
||||
:query-param="variableQueryParam"
|
||||
:visible.sync="showSetVariableDialog"
|
||||
@submit="onSubmitVariable"
|
||||
/>
|
||||
<AssetTreeTable ref="TreeTable" :tree-setting="treeSetting">
|
||||
<template slot="table">
|
||||
<div class="transition-box" style="width: calc(100% - 17px);">
|
||||
@@ -43,6 +49,7 @@ import Page from '@/layout/components/Page'
|
||||
import AdhocOpenDialog from './AdhocOpenDialog.vue'
|
||||
import AdhocSaveDialog from './AdhocSaveDialog.vue'
|
||||
import VariableHelpDialog from './VariableHelpDialog.vue'
|
||||
import setVariableDialog from '@/views/ops/Template/components/setVariableDialog'
|
||||
import { createJob, getJob, getTaskDetail, StopJob } from '@/api/ops'
|
||||
|
||||
export default {
|
||||
@@ -51,6 +58,7 @@ export default {
|
||||
VariableHelpDialog,
|
||||
AdhocSaveDialog,
|
||||
AdhocOpenDialog,
|
||||
setVariableDialog,
|
||||
AssetTreeTable,
|
||||
Page,
|
||||
QuickJobTerm,
|
||||
@@ -70,6 +78,7 @@ export default {
|
||||
showHelpDialog: false,
|
||||
showOpenAdhocDialog: false,
|
||||
showOpenAdhocSaveDialog: false,
|
||||
showSetVariableDialog: false,
|
||||
DataZTree: 0,
|
||||
runas: '',
|
||||
runasPolicy: 'skip',
|
||||
@@ -93,6 +102,10 @@ export default {
|
||||
type: 'primary'
|
||||
},
|
||||
callback: () => {
|
||||
if (this.variableFormData.length !== 0) {
|
||||
this.showSetVariableDialog = true
|
||||
return
|
||||
}
|
||||
this.execute()
|
||||
}
|
||||
},
|
||||
@@ -297,7 +310,9 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
iShowTree: true
|
||||
iShowTree: true,
|
||||
variableFormData: [],
|
||||
variableQueryParam: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -339,6 +354,10 @@ export default {
|
||||
}
|
||||
},
|
||||
onSelectAdhoc(adhoc) {
|
||||
this.variableFormData = adhoc?.variable.map((data) => {
|
||||
return data.form_data
|
||||
})
|
||||
this.variableQueryParam = 'adhoc=' + adhoc.id
|
||||
this.command = adhoc.args
|
||||
},
|
||||
enableWS() {
|
||||
@@ -432,7 +451,6 @@ export default {
|
||||
this.$message.error(this.$tc('RequiredRunas'))
|
||||
return
|
||||
}
|
||||
|
||||
const data = {
|
||||
assets: hosts,
|
||||
nodes: nodes,
|
||||
@@ -447,6 +465,9 @@ export default {
|
||||
if (this.chdir) {
|
||||
data.chdir = this.chdir
|
||||
}
|
||||
if (this.parameters) {
|
||||
data.parameters = this.parameters
|
||||
}
|
||||
createJob(data).then(res => {
|
||||
this.executionInfo.timeCost = 0
|
||||
this.executionInfo.status = { value: 'running', label: this.$t('Running') }
|
||||
@@ -460,7 +481,7 @@ export default {
|
||||
stop() {
|
||||
StopJob({ task_id: this.currentTaskId }).then(() => {
|
||||
this.xterm.write('\x1b[31m' +
|
||||
this.$tc('StopLogOutput').replace('currentTaskId', this.currentTaskId) + '\x1b[0m')
|
||||
this.$tc('StopLogOutput').replace('currentTaskId', this.currentTaskId) + '\x1b[0m')
|
||||
this.xterm.write(this.wrapperError(''))
|
||||
this.getTaskStatus()
|
||||
}).catch((e) => {
|
||||
@@ -476,6 +497,11 @@ export default {
|
||||
}
|
||||
this.toolbar.left.run.isVisible = this.executionInfo.status.value === 'running'
|
||||
this.toolbar.left.stop.isVisible = this.executionInfo.status.value !== 'running'
|
||||
},
|
||||
onSubmitVariable(parameters) {
|
||||
this.parameters = parameters
|
||||
this.showSetVariableDialog = false
|
||||
this.execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ export default {
|
||||
|
||||
.empty-file-tip {
|
||||
position: absolute;
|
||||
right: 20%;
|
||||
right: calc(50% - 230px);
|
||||
top: 50%;
|
||||
font-size: 18px;
|
||||
color: #c5c9cc;
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
<div v-if="ready">
|
||||
<VariableHelpDialog :visible.sync="showHelpDialog" />
|
||||
<GenericCreateUpdatePage ref="form" v-bind="$data" />
|
||||
<setVariableDialog
|
||||
v-if="showVariableDialog"
|
||||
:form-data="formData"
|
||||
:query-param="queryParam"
|
||||
:visible.sync="showVariableDialog"
|
||||
@submit="setPeriodicParams"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -13,9 +20,13 @@ import i18n from '@/i18n/i18n'
|
||||
import VariableHelpDialog from '@/views/ops/Adhoc/VariableHelpDialog.vue'
|
||||
import { Required } from '@/components/Form/DataForm/rules'
|
||||
import { crontab, interval } from '@/views/accounts/const'
|
||||
import LoadTemplateLink from '@/views/ops/Job/components/loadTemplateLink'
|
||||
import Variable from '@/views/ops/Template/components/Variable'
|
||||
import setVariableDialog from '@/views/ops/Template/components/setVariableDialog.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
setVariableDialog,
|
||||
GenericCreateUpdatePage,
|
||||
VariableHelpDialog
|
||||
},
|
||||
@@ -27,9 +38,9 @@ export default {
|
||||
url: '/api/v1/ops/jobs/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'type', 'instant']],
|
||||
[this.$t('Task'), ['module', 'args', 'playbook', 'chdir', 'timeout']],
|
||||
[this.$t('Asset'), ['assets', 'runas', 'runas_policy']],
|
||||
[this.$t('Plan'), ['run_after_save', 'is_periodic', 'interval', 'crontab']],
|
||||
[this.$t('Asset'), ['assets', 'nodes', 'runas', 'runas_policy']],
|
||||
[this.$t('Task'), ['module', 'argsLoadFromTemplate', 'args', 'playbook', 'variable', 'chdir', 'timeout']],
|
||||
[this.$t('Plan'), ['is_periodic', 'interval', 'crontab', 'periodic_variable']],
|
||||
[this.$t('Other'), ['comment']]
|
||||
],
|
||||
initial: {
|
||||
@@ -88,13 +99,26 @@ export default {
|
||||
return { label: item.name, value: item.id }
|
||||
}
|
||||
}
|
||||
},
|
||||
on: {
|
||||
change: ([event], updateForm) => {
|
||||
this.queryParam = `playbook=${event.pk}`
|
||||
this.$axios.get(`/api/v1/ops/playbooks/${event.pk}/`,
|
||||
).then(data => {
|
||||
data?.variable.map(item => {
|
||||
delete item.job
|
||||
delete item.playbook
|
||||
return item
|
||||
})
|
||||
updateForm({ variable: data.variable })
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
assets: {
|
||||
type: 'assetSelect',
|
||||
component: AssetSelect,
|
||||
label: this.$t('Asset'),
|
||||
rules: [Required],
|
||||
el: {
|
||||
baseUrl: '/api/v1/perms/users/self/assets/',
|
||||
baseNodeUrl: '/api/v1/perms/users/self/nodes/',
|
||||
@@ -102,6 +126,38 @@ export default {
|
||||
value: []
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
el: {
|
||||
value: [],
|
||||
ajax: {
|
||||
url: '/api/v1/perms/users/self/nodes/',
|
||||
filterOption: (item) => {
|
||||
if (item.value !== 'favorite') {
|
||||
return item
|
||||
}
|
||||
},
|
||||
transformOption: (item) => {
|
||||
return { label: item.full_value || item.name, value: item.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
argsLoadFromTemplate: {
|
||||
label: this.$t('Templates'),
|
||||
hidden: (formValue) => {
|
||||
return formValue.type !== 'adhoc'
|
||||
},
|
||||
component: LoadTemplateLink,
|
||||
on: {
|
||||
change: ([event], updateForm) => {
|
||||
updateForm({
|
||||
args: event.args,
|
||||
module: event.module.value,
|
||||
variable: event.variable
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
args: {
|
||||
rules: [Required],
|
||||
hidden: (formValue) => {
|
||||
@@ -125,6 +181,25 @@ export default {
|
||||
]
|
||||
}
|
||||
},
|
||||
variable: {
|
||||
component: Variable,
|
||||
on: {
|
||||
input: ([event], updateForm) => {
|
||||
this.formData = event.map(item => {
|
||||
return item.form_data
|
||||
})
|
||||
if (event.length > 0) {
|
||||
if (event[0].job) {
|
||||
this.queryParam = `job=${event[0].job}`
|
||||
} else if (event[0].adhoc) {
|
||||
this.queryParam = `adhoc=${event[0].adhoc}`
|
||||
} else if (event[0].playbook) {
|
||||
this.queryParam = `playbook=${event[0].playbook}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
timeout: {
|
||||
helpText: i18n.t('TimeoutHelpText')
|
||||
},
|
||||
@@ -149,13 +224,42 @@ export default {
|
||||
type: 'switch',
|
||||
hidden: () => {
|
||||
return this.instantTask
|
||||
},
|
||||
on: {
|
||||
change: ([event], updateForm) => {
|
||||
if (this.formData.length > 0) {
|
||||
this.showVariableDialog = event
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
periodic_variable: {
|
||||
hidden: () => {
|
||||
return true
|
||||
}
|
||||
},
|
||||
interval,
|
||||
crontab
|
||||
},
|
||||
createSuccessNextRoute: { name: 'JobManagement' },
|
||||
updateSuccessNextRoute: { name: 'JobManagement' }
|
||||
updateSuccessNextRoute: { name: 'JobManagement' },
|
||||
cleanFormValue: (data) => {
|
||||
Object.assign(data, { periodic_variable: this.periodicVariableValue })
|
||||
return data
|
||||
},
|
||||
moreButtons: [
|
||||
{
|
||||
title: this.$t('ExecuteAfterSaving'),
|
||||
callback: (value, form, btn) => {
|
||||
form.value.instant = true
|
||||
this.submitForm(form, btn)
|
||||
}
|
||||
}
|
||||
],
|
||||
formData: [],
|
||||
queryParam: '',
|
||||
showVariableDialog: false,
|
||||
periodicVariableValue: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -192,7 +296,20 @@ export default {
|
||||
this.ready = true
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
methods: {
|
||||
submitForm(form, btn) {
|
||||
form.validate((valid) => {
|
||||
if (valid) {
|
||||
btn.loading = true
|
||||
}
|
||||
})
|
||||
this.$refs.form.$refs.createUpdateForm.$refs.form.$refs.dataForm.submitForm('form', false)
|
||||
},
|
||||
setPeriodicParams(data) {
|
||||
this.showVariableDialog = false
|
||||
this.periodicVariableValue = data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -1,129 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<JobRunDialog v-if="showJobRunDialog" :item="item" :visible.sync="showJobRunDialog" @submit="runJob" />
|
||||
<BaseJob />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JobRunDialog from '@/views/ops/Job/JobRunDialog'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { ActionsFormatter, DateFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import BaseJob from './BaseJob'
|
||||
|
||||
export default {
|
||||
name: 'Adhoc',
|
||||
components: {
|
||||
GenericListTable,
|
||||
JobRunDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {},
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/jobs/?type=adhoc',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'type', 'asset_amount', 'average_time_cost',
|
||||
'summary', 'date_last_run', 'actions'
|
||||
]
|
||||
},
|
||||
columns: [
|
||||
'name', 'type', 'summary', 'average_time_cost', 'asset_amount',
|
||||
'date_last_run', 'comment', 'date_updated', 'date_created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '140px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'JobDetail',
|
||||
params: { id: row.id }
|
||||
})
|
||||
}
|
||||
},
|
||||
type: {
|
||||
width: '96px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('Summary'),
|
||||
formatter: (row) => {
|
||||
return row.summary['success'] + '/' + row.summary['total']
|
||||
}
|
||||
},
|
||||
average_time_cost: {
|
||||
formatter: (row) => {
|
||||
return row.average_time_cost.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
asset_amount: {
|
||||
label: this.$t('AssetsOfNumber'),
|
||||
formatter: (row) => {
|
||||
return row.assets.length
|
||||
}
|
||||
},
|
||||
date_last_run: {
|
||||
width: '140px',
|
||||
formatter: DateFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
|
||||
updateRoute: 'JobUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_job'),
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
title: this.$t('Run'),
|
||||
name: 'run',
|
||||
can: this.$hasPerm('ops.add_jobexecution') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
if (row?.use_parameter_define && row?.parameters_define) {
|
||||
const params = JSON.parse(row.parameters_define)
|
||||
if (Object.keys(params).length > 0) {
|
||||
this.item = row
|
||||
this.showJobRunDialog = true
|
||||
}
|
||||
} else {
|
||||
this.runJob(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: 'JobCreate',
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false
|
||||
},
|
||||
showJobRunDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
runJob(row, parameters) {
|
||||
this.$axios.post('/api/v1/ops/job-executions/', {
|
||||
job: row.id,
|
||||
parameters: parameters
|
||||
}).then((resp) => {
|
||||
openTaskPage(resp.task_id)
|
||||
})
|
||||
}
|
||||
BaseJob
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
166
src/views/ops/Job/components/BaseJob.vue
Normal file
166
src/views/ops/Job/components/BaseJob.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<JobRunDialog v-if="showJobRunDialog" :item="item" :visible.sync="showJobRunDialog" @submit="runJob" />
|
||||
<setVariableDialog
|
||||
v-if="showVariableDialog"
|
||||
:form-data="formData"
|
||||
:query-param="'job=' + item.id"
|
||||
:visible.sync="showVariableDialog"
|
||||
@submit="runJobWithParams"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JobRunDialog from '@/views/ops/Job/JobRunDialog'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
import setVariableDialog from '@/views/ops/Template/components/setVariableDialog'
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { ActionsFormatter, DateFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GenericListTable,
|
||||
JobRunDialog,
|
||||
setVariableDialog
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'adhoc'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {},
|
||||
tableConfig: {
|
||||
url: `/api/v1/ops/jobs/?type=${this.type}`,
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'type', 'asset_amount', 'average_time_cost',
|
||||
'summary', 'date_last_run', 'actions'
|
||||
]
|
||||
},
|
||||
columns: [
|
||||
'name', 'type', 'summary', 'average_time_cost', 'asset_amount',
|
||||
'date_last_run', 'comment', 'date_updated', 'date_created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '140px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'JobDetail',
|
||||
params: { id: row.id }
|
||||
})
|
||||
}
|
||||
},
|
||||
type: {
|
||||
width: '96px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('Summary'),
|
||||
formatter: (row) => {
|
||||
return row.summary['success'] + '/' + row.summary['total']
|
||||
}
|
||||
},
|
||||
average_time_cost: {
|
||||
formatter: (row) => {
|
||||
return row.average_time_cost.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
asset_amount: {
|
||||
label: this.$t('AssetsOfNumber'),
|
||||
formatter: (row) => {
|
||||
return row.assets.length
|
||||
}
|
||||
},
|
||||
date_last_run: {
|
||||
width: '140px',
|
||||
formatter: DateFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
|
||||
updateRoute: 'JobUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_job'),
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
title: this.$t('Run'),
|
||||
name: 'run',
|
||||
order: 5,
|
||||
type: 'primary',
|
||||
can: this.$hasPerm('ops.add_jobexecution') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
this.item = row
|
||||
if (row?.use_parameter_define && row?.parameters_define) {
|
||||
const params = JSON.parse(row.parameters_define)
|
||||
if (Object.keys(params).length > 0) {
|
||||
this.showJobRunDialog = true
|
||||
}
|
||||
} else if (row?.variable?.length) {
|
||||
this.showVariableDialog = true
|
||||
} else {
|
||||
this.runJob(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'JobCreate',
|
||||
query: {
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
},
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false
|
||||
},
|
||||
showJobRunDialog: false,
|
||||
showVariableDialog: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formData() {
|
||||
return this.item.variable.map((data) => {
|
||||
return data.form_data
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
methods: {
|
||||
runJob(row, parameters) {
|
||||
this.$axios.post('/api/v1/ops/job-executions/', {
|
||||
job: row.id,
|
||||
parameters: parameters
|
||||
}).then((resp) => {
|
||||
openTaskPage(resp.task_id)
|
||||
})
|
||||
},
|
||||
runJobWithParams(parameters) {
|
||||
this.runJob(this.item, parameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,136 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
|
||||
<JobRunDialog v-if="showJobRunDialog" :item="item" :visible.sync="showJobRunDialog" @submit="runJob" />
|
||||
<BaseJob :type="'playbook'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JobRunDialog from '@/views/ops/Job/JobRunDialog'
|
||||
import GenericListTable from '@/layout/components/GenericListTable'
|
||||
|
||||
import { openTaskPage } from '@/utils/jms'
|
||||
import { ActionsFormatter, DateFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
|
||||
import BaseJob from './BaseJob'
|
||||
|
||||
export default {
|
||||
name: 'PlayBook',
|
||||
components: {
|
||||
GenericListTable,
|
||||
JobRunDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
item: {},
|
||||
tableConfig: {
|
||||
url: '/api/v1/ops/jobs/?type=playbook',
|
||||
columnsShow: {
|
||||
min: ['name', 'actions'],
|
||||
default: [
|
||||
'name', 'type', 'asset_amount', 'average_time_cost',
|
||||
'summary', 'date_last_run', 'actions'
|
||||
]
|
||||
},
|
||||
columns: [
|
||||
'name', 'type', 'summary', 'average_time_cost', 'asset_amount',
|
||||
'date_last_run', 'comment', 'date_updated', 'date_created', 'actions'
|
||||
],
|
||||
columnsMeta: {
|
||||
name: {
|
||||
width: '140px',
|
||||
formatter: DetailFormatter,
|
||||
formatterArgs: {
|
||||
can: true,
|
||||
getRoute: ({ row }) => ({
|
||||
name: 'JobDetail',
|
||||
params: { id: row.id }
|
||||
})
|
||||
}
|
||||
},
|
||||
type: {
|
||||
width: '96px',
|
||||
formatter: (row) => {
|
||||
return row.type.label
|
||||
}
|
||||
},
|
||||
comment: {
|
||||
width: '240px'
|
||||
},
|
||||
summary: {
|
||||
label: this.$t('Summary'),
|
||||
formatter: (row) => {
|
||||
return row.summary['success'] + '/' + row.summary['total']
|
||||
}
|
||||
},
|
||||
average_time_cost: {
|
||||
formatter: (row) => {
|
||||
return row.average_time_cost.toFixed(2) + 's'
|
||||
}
|
||||
},
|
||||
asset_amount: {
|
||||
label: this.$t('AssetsOfNumber'),
|
||||
formatter: (row) => {
|
||||
return row.assets.length
|
||||
}
|
||||
},
|
||||
date_last_run: {
|
||||
width: '140px',
|
||||
formatter: DateFormatter
|
||||
},
|
||||
actions: {
|
||||
formatter: ActionsFormatter,
|
||||
formatterArgs: {
|
||||
hasUpdate: true,
|
||||
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
|
||||
updateRoute: 'JobUpdate',
|
||||
hasDelete: true,
|
||||
canDelete: this.$hasPerm('ops.delete_job'),
|
||||
hasClone: false,
|
||||
extraActions: [
|
||||
{
|
||||
title: this.$t('Run'),
|
||||
name: 'run',
|
||||
can: this.$hasPerm('ops.add_jobexecution') && !this.$store.getters.currentOrgIsRoot,
|
||||
callback: ({ row }) => {
|
||||
if (row?.use_parameter_define && row?.parameters_define) {
|
||||
const params = JSON.parse(row.parameters_define)
|
||||
if (Object.keys(params).length > 0) {
|
||||
this.item = row
|
||||
this.showJobRunDialog = true
|
||||
}
|
||||
} else {
|
||||
this.runJob(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
headerActions: {
|
||||
hasRefresh: true,
|
||||
hasExport: false,
|
||||
hasImport: false,
|
||||
createRoute: () => {
|
||||
return {
|
||||
name: 'JobCreate',
|
||||
query: {
|
||||
type: 'playbook'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
showJobRunDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
runJob(row, parameters) {
|
||||
this.$axios.post('/api/v1/ops/job-executions/', {
|
||||
job: row.id,
|
||||
parameters: parameters
|
||||
}).then((resp) => {
|
||||
openTaskPage(resp.task_id)
|
||||
})
|
||||
}
|
||||
BaseJob
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
41
src/views/ops/Job/components/loadTemplateLink.vue
Normal file
41
src/views/ops/Job/components/loadTemplateLink.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<AdhocOpenDialog v-if="showOpenAdhocDialog" :visible.sync="showOpenAdhocDialog" @select="onSelectAdhoc" />
|
||||
<el-link :underline="false" type="default" @click="onClick()">
|
||||
{{ $t('LoadTemplate') }}
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AdhocOpenDialog from '@/views/ops/Adhoc/AdhocOpenDialog'
|
||||
|
||||
export default {
|
||||
name: 'LoadTemplateLink',
|
||||
components: {
|
||||
AdhocOpenDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showOpenAdhocDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.showOpenAdhocDialog = true
|
||||
},
|
||||
onSelectAdhoc(adhoc) {
|
||||
adhoc?.variable.map(item => {
|
||||
delete item.id
|
||||
delete item.job
|
||||
delete item.adhoc
|
||||
return item
|
||||
})
|
||||
this.$emit('change', adhoc)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -5,6 +5,7 @@
|
||||
<script>
|
||||
import { GenericCreateUpdatePage } from '@/layout/components'
|
||||
import CodeEditor from '@/components/Form/FormFields/CodeEditor'
|
||||
import Variable from '@/views/ops/Template/components/Variable'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -14,7 +15,7 @@ export default {
|
||||
return {
|
||||
url: '/api/v1/ops/adhocs/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'module', 'args', 'comment', 'scope']]
|
||||
[this.$t('Basic'), ['name', 'scope', 'module', 'args', 'variable', 'comment']]
|
||||
],
|
||||
initial: {
|
||||
module: 'shell',
|
||||
@@ -23,6 +24,9 @@ export default {
|
||||
fieldsMeta: {
|
||||
args: {
|
||||
component: CodeEditor
|
||||
},
|
||||
variable: {
|
||||
component: Variable
|
||||
}
|
||||
},
|
||||
createSuccessNextRoute: {
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
return {
|
||||
url: '/api/v1/ops/playbooks/',
|
||||
fields: [
|
||||
[this.$t('Basic'), ['name', 'comment', 'scope']]
|
||||
[this.$t('Basic'), ['name', 'scope', 'comment']]
|
||||
],
|
||||
createSuccessNextRoute: {
|
||||
name: 'Template'
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:title="$tc('NewFile')"
|
||||
:visible.sync="iVisible"
|
||||
top="1vh"
|
||||
width="20%"
|
||||
width="40%"
|
||||
@confirm="onConfirm"
|
||||
>
|
||||
<el-form>
|
||||
@@ -45,7 +45,8 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
onConfirm() {
|
||||
this.$emit('confirm', this.name)
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div style="display: flex;margin-top:10px;justify-content: space-between" />
|
||||
<el-form ref="form" label-position="left" label-width="30px">
|
||||
<div class="form-content">
|
||||
<el-form-item label="" prop="variable">
|
||||
<Variable :value.sync="variables" @input="setVariable" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
</TreeTable>
|
||||
@@ -46,13 +53,15 @@ import CodeEditor from '@/components/Form/FormFields/CodeEditor'
|
||||
import item from '@/layout/components/NavLeft/Item'
|
||||
import NewNodeDialog from '@/views/ops/Template/Playbook/PlaybookDetail/Editor/NewNodeDialog.vue'
|
||||
import { renameFile } from '@/api/ops'
|
||||
import Variable from '@/views/ops/Template/components/Variable'
|
||||
|
||||
export default {
|
||||
name: 'CommandExecution',
|
||||
components: {
|
||||
NewNodeDialog,
|
||||
TreeTable,
|
||||
CodeEditor
|
||||
CodeEditor,
|
||||
Variable
|
||||
},
|
||||
props: {
|
||||
object: {
|
||||
@@ -146,7 +155,8 @@ export default {
|
||||
},
|
||||
iShowTree: true,
|
||||
activeEditorId: '',
|
||||
openedEditor: {}
|
||||
openedEditor: {},
|
||||
variables: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -167,6 +177,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.variables = this.object?.variable
|
||||
this.onOpenEditor({ id: 'main.yml', name: 'main.yml' })
|
||||
},
|
||||
methods: {
|
||||
@@ -286,6 +297,12 @@ export default {
|
||||
},
|
||||
hasChange(editor) {
|
||||
return editor.value !== editor.originValue
|
||||
},
|
||||
setVariable(variables) {
|
||||
this.$axios.patch(`/api/v1/ops/playbooks/${this.object.id}/`,
|
||||
{ variable: variables }).catch(err => {
|
||||
this.$message.error(this.$tc('UpdateErrorMsg') + ' ' + err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
src/views/ops/Template/components/AddVariableDialog.vue
Normal file
73
src/views/ops/Template/components/AddVariableDialog.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="iVisible"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="$tc('AddVariable')"
|
||||
:visible.sync="iVisible"
|
||||
width="800px"
|
||||
>
|
||||
<VariableCreateForm
|
||||
:variable="variable"
|
||||
@add="addVariable"
|
||||
@edit="editVariable"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import VariableCreateForm from '@/components/Apps/VariableCreateUpdateForm'
|
||||
|
||||
export default {
|
||||
name: 'AddVariableDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
VariableCreateForm
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
variable: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
variables: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addVariable(variable) {
|
||||
const i = this.variables.findIndex(item => item.var_name === variable.var_name)
|
||||
if (i !== -1) {
|
||||
this.variables.splice(i, 1)
|
||||
}
|
||||
this.variables.push(variable)
|
||||
this.iVisible = false
|
||||
},
|
||||
editVariable(form) {
|
||||
const i = this.variables.findIndex(item => item.var_name === this.variable.var_name)
|
||||
this.variables.splice(i, 1, form)
|
||||
this.iVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
167
src/views/ops/Template/components/Variable.vue
Normal file
167
src/views/ops/Template/components/Variable.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="variables el-data-table">
|
||||
<el-table :data="variables" class="el-table--fit el-table--border">
|
||||
<el-table-column :label="$tc('Name')" prop="name" />
|
||||
<el-table-column :label="$tc('VariableName')" prop="var_name" />
|
||||
<el-table-column :label="$tc('Actions')" align="center" class-name="buttons" fixed="right" width="135">
|
||||
<template v-slot="scope">
|
||||
<el-button icon="el-icon-minus" size="mini" type="danger" @click="removeVariable(scope.row)" />
|
||||
<el-button
|
||||
:disabled="!!scope.row.template"
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="onEditClick(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="actions">
|
||||
<el-button size="mini" type="primary" @click="onAddClick">
|
||||
{{ $t('Add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<AddVariableDialog
|
||||
:variable="variable"
|
||||
:variables="variables"
|
||||
:visible.sync="addVariableDialogVisible"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AddVariableDialog from './AddVariableDialog'
|
||||
|
||||
export default {
|
||||
name: 'Variable',
|
||||
components: {
|
||||
AddVariableDialog
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [Array],
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
variable: {},
|
||||
initial: false,
|
||||
addVariableDialogVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
variables: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:value', val)
|
||||
this.$emit('change', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
variables: {
|
||||
handler(value) {
|
||||
if (value.length > 0 || this.initial) {
|
||||
this.$emit('input', value)
|
||||
}
|
||||
if (value) {
|
||||
this.initial = true
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
removeVariable(variable) {
|
||||
this.variables = this.variables.filter((item) => {
|
||||
if (variable.id && item.id) {
|
||||
return item.id !== variable.id
|
||||
} else if (variable.var_name && item.var_name) {
|
||||
return item.var_name !== variable.var_name
|
||||
} else {
|
||||
return variable.name !== item.name
|
||||
}
|
||||
})
|
||||
},
|
||||
onEditClick(variable) {
|
||||
this.variable = variable
|
||||
setTimeout(() => {
|
||||
this.addVariableDialogVisible = true
|
||||
})
|
||||
},
|
||||
onAddClick() {
|
||||
this.variable = null
|
||||
setTimeout(() => {
|
||||
this.addVariableDialogVisible = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-data-table ::v-deep .el-table {
|
||||
.table {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.el-table__row {
|
||||
&.selected-row {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
& > td {
|
||||
line-height: 1.5;
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
border-right: none;
|
||||
|
||||
* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
& > div > span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__header > thead > tr > th {
|
||||
padding: 6px 0;
|
||||
background-color: #ffffff;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
border-right: none;
|
||||
|
||||
.cell {
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:hover {
|
||||
border-right: 2px solid #EBEEF5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-data-table ::v-deep .el-table .el-table__header > thead > tr .is-sortable {
|
||||
padding: 5px 0;
|
||||
|
||||
.cell {
|
||||
padding-top: 3px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
66
src/views/ops/Template/components/setVariableDialog.vue
Normal file
66
src/views/ops/Template/components/setVariableDialog.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-if="iVisible"
|
||||
:destroy-on-close="true"
|
||||
:show-cancel="false"
|
||||
:show-confirm="false"
|
||||
:title="$tc('setVariable')"
|
||||
:visible.sync="iVisible"
|
||||
width="800px"
|
||||
>
|
||||
<VariableSetForm
|
||||
:form-data="formData"
|
||||
:query-param="queryParam"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dialog from '@/components/Dialog'
|
||||
import VariableSetForm from '@/components/Apps/VariableSetForm'
|
||||
|
||||
export default {
|
||||
name: 'SetVariableDialog',
|
||||
components: {
|
||||
Dialog,
|
||||
VariableSetForm
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formData: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
queryParam: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
iVisible: {
|
||||
get() {
|
||||
return this.visible
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('update:visible', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleConfirm(variable) {
|
||||
this.$emit('submit', variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user