2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 12:40:25 +08:00
Files
panel/web/src/views/cron/IndexView.vue
2024-10-19 20:47:33 +08:00

476 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
defineOptions({
name: 'cron-index'
})
import Editor from '@guolao/vue-monaco-editor'
import { NButton, NDataTable, NInput, NPopconfirm, NSwitch } from 'naive-ui'
import cron from '@/api/panel/cron'
import dashboard from '@/api/panel/dashboard'
import file from '@/api/panel/file'
import website from '@/api/panel/website'
import { formatDateTime, renderIcon } from '@/utils'
import type { CronTask } from '@/views/cron/types'
const addModel = ref({
name: '',
type: 'shell',
target: '',
save: 1,
backup_type: 'website',
backup_path: '',
script: '# 在此输入您要执行的脚本内容',
time: '* * * * *'
})
const cronSelectModal = ref(false)
const taskLogModal = ref(false)
const editTaskModal = ref(false)
const installedDbAndPhp = ref({
php: [
{
label: '',
value: ''
}
],
db: [
{
label: '',
value: ''
}
]
})
const mySQLInstalled = computed(() => {
return installedDbAndPhp.value.db.find((item) => item.value === 'mysql')
})
const postgreSQLInstalled = computed(() => {
return installedDbAndPhp.value.db.find((item) => item.value === 'postgresql')
})
const websites = ref<any>([])
const columns: any = [
{ type: 'selection', fixed: 'left' },
{
title: '任务名',
key: 'name',
minWidth: 150,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '任务类型',
key: 'type',
width: 100,
resizable: true,
render(row: any) {
return row.type === 'shell' ? '运行脚本' : row.type === 'backup' ? '备份数据' : '切割日志'
}
},
{
title: '启用',
key: 'status',
width: 100,
align: 'center',
resizable: true,
render(row: any) {
return h(NSwitch, {
size: 'small',
rubberBand: false,
value: row.status,
onUpdateValue: () => handleStatusChange(row)
})
}
},
{
title: '任务周期',
key: 'time',
width: 100,
resizable: true,
ellipsis: { tooltip: true }
},
{
title: '创建时间',
key: 'created_at',
width: 200,
resizable: true,
ellipsis: { tooltip: true },
render(row: any): string {
return formatDateTime(row.created_at)
}
},
{
title: '最后更新时间',
key: 'updated_at',
width: 200,
ellipsis: { tooltip: true },
render(row: any): string {
return formatDateTime(row.updated_at)
}
},
{
title: '操作',
key: 'actions',
width: 280,
align: 'center',
hideInExcel: true,
render(row: any) {
return [
h(
NButton,
{
size: 'small',
type: 'warning',
secondary: true,
onClick: () => handleShowLog(row)
},
{
default: () => '日志',
icon: renderIcon('majesticons:eye-line', { size: 14 })
}
),
h(
NButton,
{
size: 'small',
type: 'primary',
style: 'margin-left: 15px;',
onClick: () => handleEdit(row)
},
{
default: () => '修改',
icon: renderIcon('material-symbols:edit-outline', { size: 14 })
}
),
h(
NPopconfirm,
{
onPositiveClick: () => handleDelete(row.id)
},
{
default: () => {
return '确定删除任务吗?'
},
trigger: () => {
return h(
NButton,
{
size: 'small',
type: 'error',
style: 'margin-left: 15px;'
},
{
default: () => '删除',
icon: renderIcon('material-symbols:delete-outline', { size: 14 })
}
)
}
}
)
]
}
}
]
const pagination = reactive({
page: 1,
pageCount: 1,
pageSize: 20,
itemCount: 0,
showQuickJumper: true,
showSizePicker: true,
pageSizes: [20, 50, 100, 200]
})
const data = ref<CronTask[]>([] as CronTask[])
const taskLog = ref('')
const editTask = ref({
id: 0,
name: '',
time: '',
script: ''
})
const getTaskList = async (page: number, limit: number) => {
const { data } = await cron.list(page, limit)
return data
}
const getWebsiteList = async (page: number, limit: number) => {
const { data } = await website.list(page, limit)
for (const item of data.items) {
websites.value.push({
label: item.name,
value: item.name
})
}
addModel.value.target = websites.value[0]?.value
}
const getPhpAndDb = async () => {
const { data } = await dashboard.installedDbAndPhp()
installedDbAndPhp.value = data
}
const onPageChange = (page: number) => {
pagination.page = page
getTaskList(page, pagination.pageSize).then((res) => {
data.value = res.items
pagination.itemCount = res.total
pagination.pageCount = res.total / pagination.pageSize + 1
})
}
const onPageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize
onPageChange(1)
}
const handleStatusChange = async (row: any) => {
cron.status(row.id, !row.status).then(() => {
row.status = !row.status
window.$message.success('修改成功')
})
}
const handleShowLog = async (row: any) => {
cron.log(row.id).then((res) => {
taskLog.value = res.data
taskLogModal.value = true
})
}
const handleCreate = async () => {
await cron.create(addModel.value).then(() => {
window.$message.success('创建成功')
})
onPageChange(pagination.page)
}
const handleEdit = async (row: any) => {
await cron.get(row.id).then(async (res) => {
await file.content(res.data.shell).then((res) => {
editTask.value.id = row.id
editTask.value.name = row.name
editTask.value.time = row.time
editTask.value.script = res.data
editTaskModal.value = true
})
})
}
const handleDelete = async (id: number) => {
await cron.delete(id).then(() => {
window.$message.success('删除成功')
})
onPageChange(pagination.page)
}
const saveTaskEdit = async () => {
cron
.update(editTask.value.id, editTask.value.name, editTask.value.time, editTask.value.script)
.then(() => {
window.$message.success('修改成功')
})
}
const handleCronSelectUpdate = (value: string) => {
if (editTaskModal.value) {
editTask.value.time = value
return
}
addModel.value.time = value
}
watch(addModel, (value) => {
if (value.backup_type === 'website') {
addModel.value.target = websites.value[0]?.value
} else {
addModel.value.target = ''
}
})
onMounted(() => {
getPhpAndDb()
getWebsiteList(1, 10000)
onPageChange(pagination.page)
})
</script>
<template>
<common-page show-footer>
<n-space vertical>
<n-card flex-1 rounded-10 title="创建计划任务">
<n-space vertical>
<n-alert type="info">
面板的计划任务均基于脚本运行若任务类型满足不了需求可自行修改对应的脚本
</n-alert>
<n-form>
<n-form-item label="任务类型">
<n-select
v-model:value="addModel.type"
:options="[
{ label: '运行脚本', value: 'shell' },
{ label: '备份数据', value: 'backup' },
{ label: '切割日志', value: 'cutoff' }
]"
>
</n-select>
</n-form-item>
<n-form-item label="任务名称">
<n-input v-model:value="addModel.name" placeholder="任务名称" />
</n-form-item>
<n-form-item label="任务周期">
<n-input
v-model:value="addModel.time"
placeholder="* * * * *"
@click="cronSelectModal = true"
/>
</n-form-item>
<div v-if="addModel.type === 'shell'">
<n-text>脚本内容</n-text>
<Editor
v-model:value="addModel.script"
language="shell"
theme="vs-dark"
height="40vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</div>
<n-form-item v-if="addModel.type === 'backup'" label="备份类型">
<n-radio-group v-model:value="addModel.backup_type">
<n-radio value="website">网站目录</n-radio>
<n-radio value="mysql" :disabled="!mySQLInstalled"> MySQL 数据库</n-radio>
<n-radio value="postgres" :disabled="!postgreSQLInstalled">
PostgreSQL 数据库
</n-radio>
</n-radio-group>
</n-form-item>
<n-form-item
v-if="
(addModel.backup_type === 'website' && addModel.type === 'backup') ||
addModel.type === 'cutoff'
"
label="选择网站"
>
<n-select
v-model:value="addModel.target"
:options="websites"
placeholder="选择网站"
/>
</n-form-item>
<n-form-item
v-if="addModel.backup_type !== 'website' && addModel.type === 'backup'"
label="数据库名"
>
<n-input v-model:value="addModel.target" placeholder="数据库名" />
</n-form-item>
<n-form-item v-if="addModel.type === 'backup'" label="保存目录">
<n-input v-model:value="addModel.backup_path" placeholder="保存目录" />
</n-form-item>
<n-form-item v-if="addModel.type !== 'shell'" label="保留份数">
<n-input-number v-model:value="addModel.save" />
</n-form-item>
</n-form>
<n-button type="primary" @click="handleCreate">创建</n-button>
</n-space>
</n-card>
<n-card flex-1 rounded-10 title="计划任务列表">
<n-data-table
striped
remote
:scroll-x="1000"
:data="data"
:columns="columns"
:row-key="(row: any) => row.id"
:pagination="pagination"
:bordered="false"
:loading="false"
@update:page="onPageChange"
@update:page-size="onPageSizeChange"
/>
</n-card>
</n-space>
</common-page>
<n-modal
v-model:show="cronSelectModal"
preset="card"
title="Cron 表达式生成"
style="width: 60vw"
size="huge"
:bordered="false"
:segmented="false"
>
<cron-select @update="handleCronSelectUpdate" />
</n-modal>
<n-modal
v-model:show="taskLogModal"
preset="card"
title="任务日志"
style="width: 80vw"
size="huge"
:bordered="false"
:segmented="false"
>
<Editor
v-model:value="taskLog"
language="ini"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true,
readOnly: true
}"
/>
</n-modal>
<n-modal
v-model:show="editTaskModal"
preset="card"
title="编辑任务"
style="width: 80vw"
size="huge"
:bordered="false"
:segmented="false"
@close="saveTaskEdit"
>
<n-form inline>
<n-form-item label="任务名称">
<n-input v-model:value="editTask.name" placeholder="任务名称" />
</n-form-item>
<n-form-item label="任务周期">
<n-input
v-model:value="editTask.time"
placeholder="* * * * *"
@click="cronSelectModal = true"
/>
</n-form-item>
</n-form>
<Editor
v-model:value="editTask.script"
language="shell"
theme="vs-dark"
height="60vh"
mt-8
:options="{
automaticLayout: true,
formatOnType: true,
formatOnPaste: true
}"
/>
</n-modal>
</template>