mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-27 17:50:53 +01:00
633 lines
22 KiB
Vue
633 lines
22 KiB
Vue
<template>
|
|
<div class="app-container schedule-list">
|
|
<parameters-dialog
|
|
:visible="isParametersVisible"
|
|
:param="scheduleForm.param"
|
|
@confirm="onParametersConfirm"
|
|
@close="isParametersVisible = false"
|
|
/>
|
|
|
|
<!--tour-->
|
|
<v-tour
|
|
name="schedule-list"
|
|
:steps="tourSteps"
|
|
:callbacks="tourCallbacks"
|
|
:options="$utils.tour.getOptions(true)"
|
|
/>
|
|
<v-tour
|
|
name="schedule-list-add"
|
|
:steps="tourAddSteps"
|
|
:callbacks="tourAddCallbacks"
|
|
:options="$utils.tour.getOptions(true)"
|
|
/>
|
|
<!--./tour-->
|
|
|
|
<!--add popup-->
|
|
<el-dialog
|
|
:title="$t(dialogTitle)"
|
|
:visible.sync="dialogVisible"
|
|
width="640px"
|
|
:before-close="onDialogClose">
|
|
<el-form label-width="180px"
|
|
class="add-form"
|
|
:model="scheduleForm"
|
|
:inline-message="true"
|
|
ref="scheduleForm"
|
|
label-position="right">
|
|
<el-form-item :label="$t('Schedule Name')" prop="name" required>
|
|
<el-input id="schedule-name" v-model="scheduleForm.name" :placeholder="$t('Schedule Name')"></el-input>
|
|
</el-form-item>
|
|
<el-form-item :label="$t('Run Type')" prop="run_type" required>
|
|
<el-select id="run-type" v-model="scheduleForm.run_type" :placeholder="$t('Run Type')">
|
|
<el-option value="all-nodes" :label="$t('All Nodes')"/>
|
|
<el-option value="selected-nodes" :label="$t('Selected Nodes')"/>
|
|
<el-option value="random" :label="$t('Random')"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item v-if="scheduleForm.run_type === 'selected-nodes'" :label="$t('Nodes')" prop="node_ids" required>
|
|
<el-select id="node-ids" v-model="scheduleForm.node_ids" :placeholder="$t('Nodes')" multiple filterable>
|
|
<el-option
|
|
v-for="op in nodeList"
|
|
:key="op._id"
|
|
:value="op._id"
|
|
:label="op.name"
|
|
:disabled="op.status === 'offline'"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item v-if="!isDisabledSpiderSchedule" :label="$t('Spider')" prop="spider_id" required>
|
|
<el-select
|
|
id="spider-id"
|
|
v-model="scheduleForm.spider_id"
|
|
:placeholder="$t('Spider')"
|
|
filterable
|
|
:disabled="isDisabledSpiderSchedule"
|
|
>
|
|
<el-option
|
|
v-for="op in spiderList"
|
|
:key="op._id"
|
|
:value="op._id"
|
|
:label="`${op.display_name} (${op.name})`"
|
|
:disabled="isDisabledSpider(op)"
|
|
>
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item v-else :label="$t('Spider')" required>
|
|
<el-select
|
|
:value="spiderId"
|
|
:placeholder="$t('Spider')"
|
|
filterable
|
|
:disabled="isDisabledSpiderSchedule"
|
|
>
|
|
<el-option
|
|
v-for="op in spiderList"
|
|
:key="op._id"
|
|
:value="op._id"
|
|
:label="`${op.display_name} (${op.name})`"
|
|
:disabled="isDisabledSpider(op)"
|
|
>
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item v-if="spiderForm.is_scrapy" :label="$t('Scrapy Spider')" prop="scrapy_spider" required
|
|
inline-message>
|
|
<el-select v-model="scheduleForm.scrapy_spider" :placeholder="$t('Scrapy Spider')" :disabled="isLoading">
|
|
<el-option
|
|
v-for="s in spiderForm.spider_names"
|
|
:key="s"
|
|
:label="s"
|
|
:value="s"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item v-if="spiderForm.is_scrapy" :label="$t('Scrapy Log Level')" prop="scrapy_spider" required
|
|
inline-message>
|
|
<el-select v-model="scheduleForm.scrapy_log_level" :placeholder="$t('Scrapy Log Level')">
|
|
<el-option value="INFO" label="INFO"/>
|
|
<el-option value="DEBUG" label="DEBUG"/>
|
|
<el-option value="WARN" label="WARN"/>
|
|
<el-option value="ERROR" label="ERROR"/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item :label="$t('Cron')" prop="cron" required>
|
|
<el-popover v-model="isShowCron" trigger="focus">
|
|
<template>
|
|
<vue-cron-linux :data="scheduleForm.cron" :i18n="lang" @change="onCronChange"/>
|
|
</template>
|
|
<template slot="reference">
|
|
<el-input
|
|
id="cron"
|
|
ref="cron"
|
|
v-model="scheduleForm.cron"
|
|
:placeholder="`${$t('[minute] [hour] [day] [month] [day of week]')}`"
|
|
>
|
|
</el-input>
|
|
</template>
|
|
</el-popover>
|
|
<!--<el-button size="small" style="width:100px" type="primary" @click="onShowCronDialog">{{$t('schedules.add_cron')}}</el-button>-->
|
|
</el-form-item>
|
|
<el-form-item :label="$t('Execute Command')" prop="cmd">
|
|
<el-input
|
|
id="cmd"
|
|
v-model="spiderForm.cmd"
|
|
:placeholder="$t('Execute Command')"
|
|
disabled
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item v-if="spiderForm.type === 'customized'" :label="$t('Parameters')" prop="param">
|
|
<template v-if="spiderForm.is_scrapy">
|
|
<el-input v-model="scheduleForm.param" :placeholder="$t('Parameters')" class="param-input"/>
|
|
<el-button type="primary" icon="el-icon-edit" class="param-btn" @click="onOpenParameters"/>
|
|
</template>
|
|
<template v-else>
|
|
<el-input v-model="scheduleForm.param" :placeholder="$t('Parameters')"></el-input>
|
|
</template>
|
|
</el-form-item>
|
|
<el-form-item :label="$t('Schedule Description')" prop="description">
|
|
<el-input id="schedule-description" v-model="scheduleForm.description" type="textarea"
|
|
:placeholder="$t('Schedule Description')"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<!--取消、保存-->
|
|
<span slot="footer" class="dialog-footer">
|
|
<el-button size="small" @click="onCancel">{{$t('Cancel')}}</el-button>
|
|
<el-button id="btn-submit" size="small" type="primary" @click="onAddSubmit" :disabled="isLoading">{{$t('Submit')}}</el-button>
|
|
</span>
|
|
</el-dialog>
|
|
|
|
<!--cron generation popup-->
|
|
<!--<el-dialog title="生成 Cron" :visible.sync="showCron">-->
|
|
<!--<vcrontab @hide="showCron=false" @fill="onCrontabFill" :expression="expression"></vcrontab>-->
|
|
<!--</el-dialog>-->
|
|
|
|
<el-card style="border-radius: 0" class="schedule-list">
|
|
<!--filter-->
|
|
<div class="filter">
|
|
<div class="right">
|
|
<el-button size="small" type="primary"
|
|
icon="el-icon-plus"
|
|
class="btn-add"
|
|
@click="onAdd">
|
|
{{$t('Add Schedule')}}
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
<!--./filter-->
|
|
|
|
<!--table list-->
|
|
<el-table :data="filteredTableData"
|
|
class="table" height="500"
|
|
:header-cell-style="{background:'rgb(48, 65, 86)',color:'white'}"
|
|
border>
|
|
<template v-for="col in columns">
|
|
<el-table-column v-if="col.name === 'status'"
|
|
:key="col.name"
|
|
:property="col.name"
|
|
:label="$t(col.label)"
|
|
:sortable="col.sortable"
|
|
:align="col.align"
|
|
:width="col.width">
|
|
<template slot-scope="scope">
|
|
<el-tooltip v-if="scope.row[col.name] === 'error'" :content="$t(scope.row['message'])" placement="top">
|
|
<el-tag class="status-tag" type="danger">
|
|
{{scope.row[col.name] ? $t(scope.row[col.name]) : $t('NA')}}
|
|
</el-tag>
|
|
</el-tooltip>
|
|
<el-tag class="status-tag" v-else>
|
|
{{scope.row[col.name] ? $t(scope.row[col.name]) : $t('NA')}}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-else-if="col.name === 'run_type'" :key="col.name" :label="$t(col.label)"
|
|
:width="col.width">
|
|
<template slot-scope="scope">
|
|
<template v-if="scope.row.run_type === 'all-nodes'">{{$t('All Nodes')}}</template>
|
|
<template v-else-if="scope.row.run_type === 'selected-nodes'">{{$t('Selected Nodes')}}</template>
|
|
<template v-else-if="scope.row.run_type === 'random'">{{$t('Random')}}</template>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-else-if="col.name === 'node_names'" :key="col.name" :label="$t(col.label)"
|
|
:width="col.width">
|
|
<template slot-scope="scope">
|
|
{{scope.row.nodes.map(d => d.name).join(', ')}}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-else-if="col.name === 'enable'" :key="col.name" :label="$t(col.label)" :width="col.width">
|
|
<template slot-scope="scope">
|
|
<el-switch
|
|
v-model="scope.row.enabled"
|
|
active-color="#13ce66"
|
|
inactive-color="#ff4949"
|
|
@change="onEnabledChange(scope.row)"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-else :key="col.name"
|
|
:property="col.name"
|
|
:label="$t(col.label)"
|
|
:sortable="col.sortable"
|
|
:align="col.align"
|
|
:width="col.width">
|
|
<template slot-scope="scope">
|
|
{{scope.row[col.name]}}
|
|
</template>
|
|
</el-table-column>
|
|
</template>
|
|
<el-table-column :label="$t('Action')" align="left" width="130" fixed="right">
|
|
<template slot-scope="scope">
|
|
<!-- 编辑 -->
|
|
<el-tooltip :content="$t('Edit')" placement="top">
|
|
<el-button type="warning" icon="el-icon-edit" size="mini" @click="onEdit(scope.row)"></el-button>
|
|
</el-tooltip>
|
|
<!-- 删除 -->
|
|
<el-tooltip :content="$t('Remove')" placement="top">
|
|
<el-button type="danger" icon="el-icon-delete" size="mini" @click="onRemove(scope.row)"></el-button>
|
|
</el-tooltip>
|
|
<!--<el-tooltip :content="$t(getStatusTooltip(scope.row))" placement="top">-->
|
|
<!--<el-button type="success" icon="fa fa-bug" size="mini" @click="onCrawl(scope.row)"></el-button>-->
|
|
<!--</el-tooltip>-->
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<!--./table list-->
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import request from '../../api/request'
|
|
import VueCronLinux from '../../components/Cron'
|
|
import {
|
|
mapState
|
|
} from 'vuex'
|
|
import ParametersDialog from '../../components/Common/ParametersDialog'
|
|
|
|
export default {
|
|
name: 'ScheduleList',
|
|
components: {
|
|
VueCronLinux,
|
|
ParametersDialog
|
|
},
|
|
data () {
|
|
return {
|
|
columns: [
|
|
{ name: 'name', label: 'Name', width: '150px' },
|
|
{ name: 'cron', label: 'Cron', width: '120px' },
|
|
{ name: 'run_type', label: 'Run Type', width: '120px' },
|
|
{ name: 'node_names', label: 'Node', width: '150px' },
|
|
{ name: 'spider_name', label: 'Spider', width: '150px' },
|
|
{ name: 'scrapy_spider', label: 'Scrapy Spider', width: '150px' },
|
|
{ name: 'param', label: 'Parameters', width: '150px' },
|
|
{ name: 'description', label: 'Description', width: '200px' },
|
|
{ name: 'enable', label: 'Enable/Disable', width: '120px' }
|
|
// { name: 'status', label: 'Status', width: '100px' }
|
|
],
|
|
isEdit: false,
|
|
dialogTitle: '',
|
|
dialogVisible: false,
|
|
showCron: false,
|
|
expression: '',
|
|
spiderList: [],
|
|
nodeList: [],
|
|
isShowCron: false,
|
|
isLoading: false,
|
|
isParametersVisible: false,
|
|
|
|
// tutorial
|
|
tourSteps: [
|
|
{
|
|
target: '.table',
|
|
content: this.$t('This is a list of schedules (cron jobs) to periodically run spider tasks. You can add/modify/edit your schedules here.<br><br>For more information, please refer to the <a href="https://docs.crawlab.cn/Usage/Schedule/" target="_blank" style="color: #409EFF">Documentation (Chinese)</a> for detail.')
|
|
},
|
|
{
|
|
target: '.btn-add',
|
|
content: this.$t('You can add a new schedule by clicking this button.')
|
|
}
|
|
],
|
|
tourCallbacks: {
|
|
onStop: () => {
|
|
this.$utils.tour.finishTour('schedule-list')
|
|
},
|
|
onPreviousStep: (currentStep) => {
|
|
if (currentStep === 2) {
|
|
this.dialogVisible = false
|
|
}
|
|
this.$utils.tour.prevStep('schedule-list', currentStep)
|
|
},
|
|
onNextStep: (currentStep) => {
|
|
if (currentStep === 1) {
|
|
this.isEdit = false
|
|
this.dialogVisible = true
|
|
this.$store.commit('schedule/SET_SCHEDULE_FORM', { node_ids: [] })
|
|
}
|
|
this.$utils.tour.nextStep('schedule-list', currentStep)
|
|
}
|
|
},
|
|
tourAddSteps: [
|
|
{
|
|
target: '.add-form',
|
|
content: this.$t('You should fill the form before adding the new schedule.'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#schedule-name',
|
|
content: this.$t('The name of the schedule'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#run-type',
|
|
content: this.$t('The type of how to run the task.<br><br>Please refer to the <a href="https://docs.crawlab.cn/Usage/Spider/Run.html" target="_blank" style="color: #409EFF">Documentation (Chinese)</a> for detailed explanation for the options.<br><br>Let\'s select <strong>Selected Nodes</strong> for example.'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#spider-id',
|
|
content: this.$t('The spider to run'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#cron',
|
|
content: this.$t('<strong>Cron</strong> expression for the schedule.<br><br>If you are not sure what a cron expression is, please refer to this <a href="https://baike.baidu.com/item/crontab/8819388" target="_blank" style="color: #409EFF">Article</a>.'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#change-crontab',
|
|
content: this.$t('You can select the correct options in the cron config box to configure the cron expression.'),
|
|
params: {
|
|
placement: 'top'
|
|
}
|
|
},
|
|
{
|
|
target: '#param',
|
|
content: this.$t('The parameters which will be passed into the spider program.'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#schedule-description',
|
|
content: this.$t('The description for the schedule'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
},
|
|
{
|
|
target: '#btn-submit',
|
|
content: this.$t('Once you have filled all fields, click this button to submit.'),
|
|
params: {
|
|
placement: 'right'
|
|
}
|
|
}
|
|
],
|
|
tourAddCallbacks: {
|
|
onStop: () => {
|
|
this.$utils.tour.finishTour('schedule-list-add')
|
|
},
|
|
onPreviousStep: (currentStep) => {
|
|
if (currentStep === 4) {
|
|
this.isShowCron = false
|
|
} else if (currentStep === 6) {
|
|
this.isShowCron = true
|
|
}
|
|
this.$utils.tour.prevStep('schedule-list-add', currentStep)
|
|
},
|
|
onNextStep: (currentStep) => {
|
|
if (currentStep === 3) {
|
|
this.isShowCron = true
|
|
} else if (currentStep === 5) {
|
|
this.isShowCron = false
|
|
}
|
|
this.$utils.tour.nextStep('schedule-list-add', currentStep)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
...mapState('spider', [
|
|
'spiderForm'
|
|
]),
|
|
...mapState('schedule', [
|
|
'scheduleList',
|
|
'scheduleForm'
|
|
]),
|
|
lang () {
|
|
const lang = this.$store.state.lang.lang || window.localStorage.getItem('lang')
|
|
if (!lang) return 'cn'
|
|
if (lang === 'zh') return 'cn'
|
|
return 'en'
|
|
},
|
|
filteredTableData () {
|
|
return this.scheduleList
|
|
},
|
|
spider () {
|
|
for (let i = 0; i < this.spiderList.length; i++) {
|
|
if (this.spiderList[i]._id === this.scheduleForm.spider_id) {
|
|
return this.spiderList[i]
|
|
}
|
|
}
|
|
return {}
|
|
},
|
|
isDisabledSpiderSchedule () {
|
|
return false
|
|
}
|
|
},
|
|
methods: {
|
|
onDialogClose () {
|
|
this.dialogVisible = false
|
|
},
|
|
onCancel () {
|
|
this.dialogVisible = false
|
|
},
|
|
onAdd () {
|
|
this.isEdit = false
|
|
this.dialogVisible = true
|
|
this.$store.commit('schedule/SET_SCHEDULE_FORM', { node_ids: [] })
|
|
this.$st.sendEv('定时任务', '添加定时任务')
|
|
|
|
if (!this.$utils.tour.isFinishedTour('schedule-list-add')) {
|
|
setTimeout(() => {
|
|
this.$tours['schedule-list-add'].start()
|
|
this.$st.sendEv('教程', '开始', 'schedule-list-add')
|
|
}, 500)
|
|
}
|
|
},
|
|
onAddSubmit () {
|
|
this.$refs.scheduleForm.validate(res => {
|
|
if (res) {
|
|
const form = JSON.parse(JSON.stringify(this.scheduleForm))
|
|
form.cron = '0 ' + this.scheduleForm.cron
|
|
if (this.spiderForm.is_scrapy) {
|
|
form.param += form.param + ' --loglevel=' + form.scrapy_log_level
|
|
}
|
|
if (this.isEdit) {
|
|
request.post(`/schedules/${this.scheduleForm._id}`, form).then(response => {
|
|
if (response.data.error) {
|
|
this.$message.error(response.data.error)
|
|
return
|
|
}
|
|
this.dialogVisible = false
|
|
this.$store.dispatch('schedule/getScheduleList')
|
|
this.$message.success(this.$t('The schedule has been saved'))
|
|
})
|
|
} else {
|
|
request.put('/schedules', form).then(response => {
|
|
if (response.data.error) {
|
|
this.$message.error(response.data.error)
|
|
return
|
|
}
|
|
this.dialogVisible = false
|
|
this.$store.dispatch('schedule/getScheduleList')
|
|
this.$message.success(this.$t('The schedule has been added'))
|
|
})
|
|
}
|
|
}
|
|
})
|
|
this.$st.sendEv('定时任务', '提交定时任务')
|
|
},
|
|
isShowRun (row) {
|
|
},
|
|
async onEdit (row) {
|
|
this.$store.commit('schedule/SET_SCHEDULE_FORM', row)
|
|
this.dialogVisible = true
|
|
this.isEdit = true
|
|
this.$st.sendEv('定时任务', '修改定时任务')
|
|
|
|
this.isLoading = true
|
|
if (!this.scheduleForm.scrapy_log_level) {
|
|
this.$set(this.scheduleForm, 'scrapy_log_level', 'INFO')
|
|
}
|
|
await this.$store.dispatch('spider/getSpiderData', row.spider_id)
|
|
if (this.spiderForm.is_scrapy) {
|
|
await this.$store.dispatch('spider/getSpiderScrapySpiders', row.spider_id)
|
|
if (!this.scheduleForm.scrapy_spider) {
|
|
if (this.spiderForm.spider_names && this.spiderForm.spider_names.length > 0) {
|
|
this.$set(this.scheduleForm, 'scrapy_spider', this.spiderForm.spider_names[0])
|
|
}
|
|
}
|
|
}
|
|
this.isLoading = false
|
|
},
|
|
onRemove (row) {
|
|
this.$confirm(this.$t('Are you sure to delete the schedule task?'), this.$t('Notification'), {
|
|
confirmButtonText: this.$t('Confirm'),
|
|
cancelButtonText: this.$t('Cancel'),
|
|
type: 'warning'
|
|
}).then(() => {
|
|
this.$store.dispatch('schedule/removeSchedule', row._id)
|
|
.then(() => {
|
|
setTimeout(() => {
|
|
this.$store.dispatch('schedule/getScheduleList')
|
|
this.$message.success(this.$t('The schedule has been removed'))
|
|
}, 100)
|
|
})
|
|
}).catch(() => {
|
|
})
|
|
this.$st.sendEv('定时任务', '删除定时任务')
|
|
},
|
|
isDisabledSpider (spider) {
|
|
if (spider.type === 'customized') {
|
|
return !spider.cmd
|
|
} else {
|
|
return false
|
|
}
|
|
},
|
|
async onEnabledChange (row) {
|
|
let res
|
|
if (row.enabled) {
|
|
res = await this.$store.dispatch('schedule/enableSchedule', row._id)
|
|
} else {
|
|
res = await this.$store.dispatch('schedule/disableSchedule', row._id)
|
|
}
|
|
if (!res || res.data.error) {
|
|
this.$message.error(this.$t(`${row.enabled ? 'Enabling' : 'Disabling'} the schedule unsuccessful`))
|
|
} else {
|
|
this.$message.success(this.$t(`${row.enabled ? 'Enabling' : 'Disabling'} the schedule successful`))
|
|
}
|
|
this.$st.sendEv('定时任务', '启用/禁用')
|
|
},
|
|
onCronChange (value) {
|
|
this.$set(this.scheduleForm, 'cron', value)
|
|
this.$st.sendEv('定时任务', '配置Cron')
|
|
},
|
|
onOpenParameters () {
|
|
this.isParametersVisible = true
|
|
},
|
|
onParametersConfirm (value) {
|
|
this.scheduleForm.param = value
|
|
this.isParametersVisible = false
|
|
}
|
|
},
|
|
created () {
|
|
this.$store.dispatch('schedule/getScheduleList')
|
|
|
|
// 节点列表
|
|
request.get('/nodes', {}).then(response => {
|
|
this.nodeList = response.data.data.map(d => {
|
|
d.systemInfo = {
|
|
os: '',
|
|
arch: '',
|
|
num_cpu: '',
|
|
executables: []
|
|
}
|
|
return d
|
|
})
|
|
})
|
|
|
|
// 爬虫列表
|
|
request.get('/spiders', {})
|
|
.then(response => {
|
|
this.spiderList = response.data.data.list || []
|
|
})
|
|
},
|
|
mounted () {
|
|
if (!this.isDisabledSpiderSchedule) {
|
|
if (!this.$utils.tour.isFinishedTour('schedule-list')) {
|
|
this.$tours['schedule-list'].start()
|
|
this.$st.sendEv('教程', '开始', 'schedule-list')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.filter .right {
|
|
text-align: right;
|
|
}
|
|
|
|
.table {
|
|
min-height: 360px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.status-tag {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.schedule-list >>> .param-input {
|
|
width: calc(100% - 56px);
|
|
}
|
|
|
|
.schedule-list >>> .param-input .el-input__inner {
|
|
border-top-right-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
border-right: none;
|
|
}
|
|
|
|
.schedule-list >>> .param-btn {
|
|
width: 56px;
|
|
border-top-left-radius: 0;
|
|
border-bottom-left-radius: 0;
|
|
}
|
|
</style>
|