Commit c5b55285 by 周田

Merge branch 'yzh' into 'main'

修改cron组件

See merge request !8
parents b904cf02 5b14a4c2
// API路径配置文件 // API路径配置文件
// 这个文件确保所有API路径在一处统一管理,方便修改 // 这个文件确保所有API路径在一处统一管理,方便修改
import { pauseJob } from "./schedule"
export const authApi = { export const authApi = {
login: '/auth/login', login: '/auth/login',
logout: '/auth/logout' logout: '/auth/logout'
...@@ -53,8 +55,8 @@ export const scrapydServerApi = { ...@@ -53,8 +55,8 @@ export const scrapydServerApi = {
deleteScrapydServer: '/scrapydServer/deleteScrapydServer' deleteScrapydServer: '/scrapydServer/deleteScrapydServer'
} as const } as const
export const systemApi = {
// 系统相关 // 系统相关
export const systemApi = {
systemInfo: '/system/systemInfo', systemInfo: '/system/systemInfo',
systemData: '/system/systemData', systemData: '/system/systemData',
systemConfig: '/system/systemConfig', systemConfig: '/system/systemConfig',
...@@ -62,16 +64,16 @@ export const systemApi = { ...@@ -62,16 +64,16 @@ export const systemApi = {
scrapydServerList: '/system/scrapydServerList' // 没用 scrapydServerList: '/system/scrapydServerList' // 没用
} as const } as const
export const statsApi = {
// 统计相关 // 统计相关
export const statsApi = {
statsList: '/statsCollection/listItem', statsList: '/statsCollection/listItem',
statsDetail: '/statsCollection/detail', statsDetail: '/statsCollection/detail',
removeStats: '/statsCollection/delete', removeStats: '/statsCollection/delete',
clearAllStats: '/statsCollection/clearAll' // TODO 未实现 clearAllStats: '/statsCollection/clearAll' // TODO 未实现
} as const } as const
export const userApi = {
// 系统用户相关接口 // 系统用户相关接口
export const userApi = {
userList: '/user/list', userList: '/user/list',
addUser: '/user/insert', addUser: '/user/insert',
updateUser: '/user/update', updateUser: '/user/update',
...@@ -79,13 +81,28 @@ export const userApi = { ...@@ -79,13 +81,28 @@ export const userApi = {
batchDelete: '/user/batchDelete', batchDelete: '/user/batchDelete',
} as const } as const
export const spiderApi = {
// 系统用户相关接口 // 系统用户相关接口
export const spiderApi = {
spiderList: '/scrapyd/listSpiders', spiderList: '/scrapyd/listSpiders',
} as const } as const
export const spiderTaskApi = {
// 爬虫任务相关接口 // 爬虫任务相关接口
export const spiderTaskApi = {
taskList: '/schedule/getJobs', taskList: '/schedule/getJobs',
taskRecord: '/schedule/scheduleLogs', taskRecord: '/schedule/scheduleLogs',
addTask: '/schedule/addJob',
deleteTask: '/schedule/removeJob',
pauseJob: '/schedule/pauseJob',
resumeJob: '/schedule/resumeJob',
jobDetail: '/schedule/jobDetail',
} as const
// 爬虫数据相关接口
export const spiderDataApi = {
dsnList: '/dsn/list',
dsnDetail: '/dsn/detail',
dsnDataDelete: '/dsn/delete',
ituList: '/itu/list',
ituDetail: '/itu/detail',
ituDataDelete: '/itu/delete',
} as const } as const
\ No newline at end of file
import { request, POST } from '@/utils/request'
import type { ApiResponse, QueryParams, UserQueryParams } from '@/utils/request'
import { spiderDataApi } from './apiPaths'
// 获取dsn数据列表
export function getDsnlist(data: UserQueryParams) {
return request({
url: spiderDataApi.dsnList,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 获取dsn数据详情
export function getDsnDetail(data: UserQueryParams) {
return request({
url: spiderDataApi.dsnDetail,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 删除dsn数据
export function deleteDsnData(data: UserQueryParams) {
return request({
url: spiderDataApi.dsnDataDelete,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 获取itu数据列表
export function getItulist(data: UserQueryParams) {
return request({
url: spiderDataApi.ituList,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 获取itu数据详情
export function getItuDetail(data: UserQueryParams) {
return request({
url: spiderDataApi.ituDetail,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 删除itu数据
export function deleteItuData(data: UserQueryParams) {
return request({
url: spiderDataApi.ituDataDelete,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
\ No newline at end of file
...@@ -19,3 +19,48 @@ export function getSpiderTaskList(data: UserQueryParams) { ...@@ -19,3 +19,48 @@ export function getSpiderTaskList(data: UserQueryParams) {
data data
}) as unknown as Promise<ApiResponse> }) as unknown as Promise<ApiResponse>
} }
// 新增/更新爬虫任务
export function addSpiderTask(data: UserQueryParams) {
return request({
url: spiderTaskApi.addTask,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 删除爬虫任务
export function deleteSpiderTask(data: UserQueryParams) {
return request({
url: spiderTaskApi.deleteTask,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 暂停爬虫任务
export function pauseSpiderTask(data: UserQueryParams) {
return request({
url: spiderTaskApi.pauseJob,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 继续爬虫任务
export function resumeSpiderTask(data: UserQueryParams) {
return request({
url: spiderTaskApi.resumeJob,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
// 获取爬虫任务数据
export function getTaskData(data: UserQueryParams) {
return request({
url: spiderTaskApi.jobDetail,
method: POST,
data
}) as unknown as Promise<ApiResponse>
}
\ No newline at end of file
import Crontab from './src/Crontab.vue'
export { Crontab }
<script lang="ts" setup>
import { ElMessage } from 'element-plus'
import type { PropType } from 'vue'
import { ref, reactive, computed, watch, onMounted } from 'vue'
defineOptions({ name: 'Crontab' })
interface shortcutsType {
text: string
value: string
}
const props = defineProps({
modelValue: {
type: String,
default: '* * * * * ?'
},
shortcuts: { type: Array as PropType<shortcutsType[]>, default: () => [] }
})
const defaultValue = ref('')
const dialogVisible = ref(false)
const getYear = () => {
let v: number[] = []
let y = new Date().getFullYear()
for (let i = 0; i < 11; i++) {
v.push(y + i)
}
return v
}
const cronValue = reactive({
second: {
type: '0',
range: {
start: 1,
end: 2
},
loop: {
start: 0,
end: 1
},
appoint: [] as string[]
},
minute: {
type: '0',
range: {
start: 1,
end: 2
},
loop: {
start: 0,
end: 1
},
appoint: [] as string[]
},
hour: {
type: '0',
range: {
start: 1,
end: 2
},
loop: {
start: 0,
end: 1
},
appoint: [] as string[]
},
day: {
type: '0',
range: {
start: 1,
end: 2
},
loop: {
start: 1,
end: 1
},
appoint: [] as string[]
},
month: {
type: '0',
range: {
start: 1,
end: 2
},
loop: {
start: 1,
end: 1
},
appoint: [] as string[]
},
week: {
type: '5',
range: {
start: '2',
end: '3'
},
loop: {
start: 0,
end: '2'
},
last: '2',
appoint: [] as string[]
},
year: {
type: '-1',
range: {
start: getYear()[0],
end: getYear()[1]
},
loop: {
start: getYear()[0],
end: 1
},
appoint: [] as string[]
}
})
const data = reactive({
second: ['0', '5', '15', '20', '25', '30', '35', '40', '45', '50', '55', '59'],
minute: ['0', '5', '15', '20', '25', '30', '35', '40', '45', '50', '55', '59'],
hour: [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23'
],
day: [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31'
],
month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
week: [
{
value: '1',
label: '周日'
},
{
value: '2',
label: '周一'
},
{
value: '3',
label: '周二'
},
{
value: '4',
label: '周三'
},
{
value: '5',
label: '周四'
},
{
value: '6',
label: '周五'
},
{
value: '7',
label: '周六'
}
],
year: getYear()
})
const value_second = computed(() => {
let v = cronValue.second
if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
})
const value_minute = computed(() => {
let v = cronValue.minute
if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
})
const value_hour = computed(() => {
let v = cronValue.hour
if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
})
const value_day = computed(() => {
let v = cronValue.day
if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else if (v.type == '4') {
return 'L'
} else {
return '*'
}
})
const value_month = computed(() => {
let v = cronValue.month
if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
})
const value_week = computed(() => {
let v = cronValue.week
if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.end + '#' + v.loop.start
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else if (v.type == '4') {
return v.last + 'L'
} else {
return '*'
}
})
const value_year = computed(() => {
let v = cronValue.year
if (v.type == '-1') {
return ''
} else if (v.type == '0') {
return '*'
} else if (v.type == '1') {
return v.range.start + '-' + v.range.end
} else if (v.type == '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type == '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : ''
} else {
return ''
}
})
watch(
() => cronValue.week.type,
(val) => {
if (val != '5') {
cronValue.day.type = '5'
}
}
)
watch(
() => cronValue.day.type,
(val) => {
if (val != '5') {
cronValue.week.type = '5'
}
}
)
watch(
() => props.modelValue,
() => {
defaultValue.value = props.modelValue
}
)
onMounted(() => {
defaultValue.value = props.modelValue
})
const emit = defineEmits(['update:modelValue'])
const select = ref()
watch(
() => select.value,
() => {
if (select.value == 'custom') {
open()
} else {
defaultValue.value = select.value
emit('update:modelValue', defaultValue.value)
}
}
)
const open = () => {
set()
dialogVisible.value = true
}
const set = () => {
defaultValue.value = props.modelValue
let arr = (props.modelValue || '* * * * * ?').split(' ')
//简单检查
if (arr.length < 6) {
ElMessage.warning('cron表达式错误,已转换为默认表达式')
arr = '* * * * * ?'.split(' ')
}
//秒
if (arr[0] == '*') {
cronValue.second.type = '0'
} else if (arr[0].includes('-')) {
cronValue.second.type = '1'
cronValue.second.range.start = Number(arr[0].split('-')[0])
cronValue.second.range.end = Number(arr[0].split('-')[1])
} else if (arr[0].includes('/')) {
cronValue.second.type = '2'
cronValue.second.loop.start = Number(arr[0].split('/')[0])
cronValue.second.loop.end = Number(arr[0].split('/')[1])
} else {
cronValue.second.type = '3'
cronValue.second.appoint = arr[0].split(',')
}
//分
if (arr[1] == '*') {
cronValue.minute.type = '0'
} else if (arr[1].includes('-')) {
cronValue.minute.type = '1'
cronValue.minute.range.start = Number(arr[1].split('-')[0])
cronValue.minute.range.end = Number(arr[1].split('-')[1])
} else if (arr[1].includes('/')) {
cronValue.minute.type = '2'
cronValue.minute.loop.start = Number(arr[1].split('/')[0])
cronValue.minute.loop.end = Number(arr[1].split('/')[1])
} else {
cronValue.minute.type = '3'
cronValue.minute.appoint = arr[1].split(',')
}
//小时
if (arr[2] == '*') {
cronValue.hour.type = '0'
} else if (arr[2].includes('-')) {
cronValue.hour.type = '1'
cronValue.hour.range.start = Number(arr[2].split('-')[0])
cronValue.hour.range.end = Number(arr[2].split('-')[1])
} else if (arr[2].includes('/')) {
cronValue.hour.type = '2'
cronValue.hour.loop.start = Number(arr[2].split('/')[0])
cronValue.hour.loop.end = Number(arr[2].split('/')[1])
} else {
cronValue.hour.type = '3'
cronValue.hour.appoint = arr[2].split(',')
}
//日
if (arr[3] == '*') {
cronValue.day.type = '0'
} else if (arr[3] == 'L') {
cronValue.day.type = '4'
} else if (arr[3] == '?') {
cronValue.day.type = '5'
} else if (arr[3].includes('-')) {
cronValue.day.type = '1'
cronValue.day.range.start = Number(arr[3].split('-')[0])
cronValue.day.range.end = Number(arr[3].split('-')[1])
} else if (arr[3].includes('/')) {
cronValue.day.type = '2'
cronValue.day.loop.start = Number(arr[3].split('/')[0])
cronValue.day.loop.end = Number(arr[3].split('/')[1])
} else {
cronValue.day.type = '3'
cronValue.day.appoint = arr[3].split(',')
}
//月
if (arr[4] == '*') {
cronValue.month.type = '0'
} else if (arr[4].includes('-')) {
cronValue.month.type = '1'
cronValue.month.range.start = Number(arr[4].split('-')[0])
cronValue.month.range.end = Number(arr[4].split('-')[1])
} else if (arr[4].includes('/')) {
cronValue.month.type = '2'
cronValue.month.loop.start = Number(arr[4].split('/')[0])
cronValue.month.loop.end = Number(arr[4].split('/')[1])
} else {
cronValue.month.type = '3'
cronValue.month.appoint = arr[4].split(',')
}
//周
if (arr[5] == '*') {
cronValue.week.type = '0'
} else if (arr[5] == '?') {
cronValue.week.type = '5'
} else if (arr[5].includes('-')) {
cronValue.week.type = '1'
cronValue.week.range.start = arr[5].split('-')[0]
cronValue.week.range.end = arr[5].split('-')[1]
} else if (arr[5].includes('#')) {
cronValue.week.type = '2'
cronValue.week.loop.start = Number(arr[5].split('#')[1])
cronValue.week.loop.end = arr[5].split('#')[0]
} else if (arr[5].includes('L')) {
cronValue.week.type = '4'
cronValue.week.last = arr[5].split('L')[0]
} else {
cronValue.week.type = '3'
cronValue.week.appoint = arr[5].split(',')
}
//年
if (!arr[6]) {
cronValue.year.type = '-1'
} else if (arr[6] == '*') {
cronValue.year.type = '0'
} else if (arr[6].includes('-')) {
cronValue.year.type = '1'
cronValue.year.range.start = Number(arr[6].split('-')[0])
cronValue.year.range.end = Number(arr[6].split('-')[1])
} else if (arr[6].includes('/')) {
cronValue.year.type = '2'
cronValue.year.loop.start = Number(arr[6].split('/')[1])
cronValue.year.loop.end = Number(arr[6].split('/')[0])
} else {
cronValue.year.type = '3'
cronValue.year.appoint = arr[6].split(',')
}
}
const submit = () => {
let year = value_year.value ? ' ' + value_year.value : ''
defaultValue.value =
value_second.value +
' ' +
value_minute.value +
' ' +
value_hour.value +
' ' +
value_day.value +
' ' +
value_month.value +
' ' +
value_week.value +
year
emit('update:modelValue', defaultValue.value)
dialogVisible.value = false
}
const inputChange = () => {
emit('update:modelValue', defaultValue.value)
}
</script>
<template>
<div>
<el-input v-model="defaultValue" disabled class="input-with-select" v-bind="$attrs" @input="inputChange">
</el-input>
<div class="sc-cron">
<el-tabs>
<el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2></h2>
<h4>{{ value_second }}</h4>
</div>
</template>
<el-form>
<el-form-item label="类型" >
<el-radio-group v-model="cronValue.second.type">
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.second.type == '1'" label="范围" class="m-t-4">
<el-input-number v-model="cronValue.second.range.start" :max="59" :min="0" controls-position="right" />
<span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.second.range.end" :max="59" :min="0" controls-position="right" />
</el-form-item>
<el-form-item v-if="cronValue.second.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.second.loop.start" :max="59" :min="0" controls-position="right" />
秒开始,每
<el-input-number v-model="cronValue.second.loop.end" :max="59" :min="0" controls-position="right" />
秒执行一次
</el-form-item>
<el-form-item v-if="cronValue.second.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.second.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.second" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2>分钟</h2>
<h4>{{ value_minute }}</h4>
</div>
</template>
<el-form>
<el-form-item label="类型">
<el-radio-group v-model="cronValue.minute.type">
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.minute.type == '1'" label="范围" class="m-t-4">
<el-input-number v-model="cronValue.minute.range.start" :max="59" :min="0" controls-position="right" />
<span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.minute.range.end" :max="59" :min="0" controls-position="right" />
</el-form-item>
<el-form-item v-if="cronValue.minute.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.minute.loop.start" :max="59" :min="0" controls-position="right" />
分钟开始,每
<el-input-number v-model="cronValue.minute.loop.end" :max="59" :min="0" controls-position="right" />
分钟执行一次
</el-form-item>
<el-form-item v-if="cronValue.minute.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.minute.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.minute" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2>小时</h2>
<h4>{{ value_hour }}</h4>
</div>
</template>
<el-form>
<el-form-item label="类型">
<el-radio-group v-model="cronValue.hour.type">
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.hour.type == '1'" label="范围" class="m-t-4">
<el-input-number v-model="cronValue.hour.range.start" :max="23" :min="0" controls-position="right" />
<span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.hour.range.end" :max="23" :min="0" controls-position="right" />
</el-form-item>
<el-form-item v-if="cronValue.hour.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.hour.loop.start" :max="23" :min="0" controls-position="right" />
小时开始,每
<el-input-number v-model="cronValue.hour.loop.end" :max="23" :min="0" controls-position="right" />
小时执行一次
</el-form-item>
<el-form-item v-if="cronValue.hour.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.hour.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.hour" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2></h2>
<h4>{{ value_day }}</h4>
</div>
</template>
<el-form>
<el-form-item label="类型">
<el-radio-group v-model="cronValue.day.type">
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
<!-- <el-radio-button value="4">本月最后一天</el-radio-button>
<el-radio-button value="5">不指定</el-radio-button> -->
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.day.type == '1'" label="范围" class="m-t-4">
<el-input-number v-model="cronValue.day.range.start" :max="31" :min="1" controls-position="right" />
<span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.day.range.end" :max="31" :min="1" controls-position="right" />
</el-form-item>
<el-form-item v-if="cronValue.day.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.day.loop.start" :max="31" :min="1" controls-position="right" />
号开始,每
<el-input-number v-model="cronValue.day.loop.end" :max="31" :min="1" controls-position="right" />
天执行一次
</el-form-item>
<el-form-item v-if="cronValue.day.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.day.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.day" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2></h2>
<h4>{{ value_month }}</h4>
</div>
</template>
<el-form>
<el-form-item label="类型">
<el-radio-group v-model="cronValue.month.type">
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.month.type == '1'" label="范围" class="m-t-4">
<el-input-number v-model="cronValue.month.range.start" :max="12" :min="1" controls-position="right" />
<span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.month.range.end" :max="12" :min="1" controls-position="right" />
</el-form-item>
<el-form-item v-if="cronValue.month.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.month.loop.start" :max="12" :min="1" controls-position="right" />
月开始,每
<el-input-number v-model="cronValue.month.loop.end" :max="12" :min="1" controls-position="right" />
月执行一次
</el-form-item>
<el-form-item v-if="cronValue.month.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.month.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.month" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2></h2>
<h4>{{ value_week }}</h4>
</div>
</template>
<el-form>
<el-form>
<el-form-item label="类型">
<el-radio-group v-model="cronValue.week.type">
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
<!-- <el-radio-button value="4">本月最后一周</el-radio-button>
<el-radio-button value="5">不指定</el-radio-button> -->
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.week.type == '1'" label="范围" class="m-t-4">
<el-select v-model="cronValue.week.range.start">
<el-option v-for="(item, index) in data.week" :key="index" :label="item.label" :value="item.value" />
</el-select>
<span style="padding: 0 15px">-</span>
<el-select v-model="cronValue.week.range.end">
<el-option v-for="(item, index) in data.week" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item v-if="cronValue.week.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.week.loop.start" :max="4" :min="1" controls-position="right" />
周的
<el-select v-model="cronValue.week.loop.end">
<el-option v-for="(item, index) in data.week" :key="index" :label="item.label" :value="item.value" />
</el-select>
执行一次
</el-form-item>
<el-form-item v-if="cronValue.week.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.week.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.week" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item v-if="cronValue.week.type == '4'" label="最后一周" class="m-t-4">
<el-select v-model="cronValue.week.last">
<el-option v-for="(item, index) in data.week" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
</el-form>
</el-tab-pane>
<!-- <el-tab-pane>
<template #label>
<div class="sc-cron-num">
<h2>年</h2>
<h4>{{ value_year }}</h4>
</div>
</template>
<el-form >
<el-form-item label="类型">
<el-radio-group v-model="cronValue.year.type">
<el-radio-button value="-1">忽略</el-radio-button>
<el-radio-button value="0">任意值</el-radio-button>
<el-radio-button value="1">范围</el-radio-button>
<el-radio-button value="2">间隔</el-radio-button>
<el-radio-button value="3">指定</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="cronValue.year.type == '1'" label="范围" class="m-t-4">
<el-input-number v-model="cronValue.year.range.start" controls-position="right" />
<span style="padding: 0 15px">-</span>
<el-input-number v-model="cronValue.year.range.end" controls-position="right" />
</el-form-item>
<el-form-item v-if="cronValue.year.type == '2'" label="间隔" class="m-t-4">
<el-input-number v-model="cronValue.year.loop.start" controls-position="right" />
年开始,每
<el-input-number v-model="cronValue.year.loop.end" :min="1" controls-position="right" />
年执行一次
</el-form-item>
<el-form-item v-if="cronValue.year.type == '3'" label="指定" class="m-t-4">
<el-select v-model="cronValue.year.appoint" multiple style="width: 100%">
<el-option v-for="(item, index) in data.year" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-form>
</el-tab-pane> -->
</el-tabs>
<div class="m-t-6" />
<!-- <el-button @click="dialogVisible = false">取 消</el-button> -->
<el-button type="primary" @click="submit()">生成表达式</el-button>
</div>
</div>
</template>
<style scoped>
.sc-cron:deep(.el-tabs__item) {
height: auto;
padding: 0 10px;
line-height: 1;
vertical-align: bottom;
}
.sc-cron-num {
width: 100%;
margin-bottom: 15px;
text-align: center;
}
.sc-cron-num h2 {
color: #fff;
margin-bottom: 15px;
font-size: 12px;
font-weight: normal;
}
.sc-cron-num h4 {
display: block;
width: 100%;
height: 32px;
padding: 0 15px;
font-size: 12px;
line-height: 30px;
background: #1d5484;
border-radius: 4px;
color: #fff;
}
.sc-cron:deep(.el-tabs__item.is-active) .sc-cron-num h4 {
color: #fff;
background: var(--el-color-primary);
}
[data-theme='dark'] .sc-cron-num h4 {
background: var(--el-color-white);
}
.input-with-select .el-input-group__prepend {
background-color: var(--el-fill-color-blank);
}
.el-radio-button__inner{
background: #1d5484;
}
</style>
<style>
.el-input.is-disabled .el-input__wrapper{
background: #1d5484;
box-shadow: 0,0,0,0px;
}
.el-radio-button__inner{
background: #1d5484;
color: #fff;
border: none;
}
</style>
\ No newline at end of file
...@@ -3,5 +3,6 @@ ...@@ -3,5 +3,6 @@
export enum DeleteMode { export enum DeleteMode {
BATCH_DELETE = 0, BATCH_DELETE = 0,
SINGLE_DELETE = 1, SINGLE_DELETE = 1,
JOB_DELETE = 2,
} }
...@@ -23,7 +23,7 @@ interface deleteDialogPropType { ...@@ -23,7 +23,7 @@ interface deleteDialogPropType {
dialogVisible: boolean, dialogVisible: boolean,
deleteMode: DeleteMode, deleteMode: DeleteMode,
id: number, id: number,
ids: number[] ids: number[],
} }
const props = defineProps<deleteDialogPropType>() const props = defineProps<deleteDialogPropType>()
const emit = defineEmits(['update:dialogVisible', 'confirm', 'getUserList', 'update:deleteMode']) const emit = defineEmits(['update:dialogVisible', 'confirm', 'getUserList', 'update:deleteMode'])
...@@ -35,7 +35,6 @@ const handleDelelte = async () => { ...@@ -35,7 +35,6 @@ const handleDelelte = async () => {
await deleteUser({ id: props.id }) await deleteUser({ id: props.id })
emit('getUserList') emit('getUserList')
deleteDialogVisible.value = false deleteDialogVisible.value = false
} }
else if (props.deleteMode == DeleteMode.BATCH_DELETE) { else if (props.deleteMode == DeleteMode.BATCH_DELETE) {
console.log(props.ids); console.log(props.ids);
......
...@@ -111,6 +111,12 @@ export interface UserQueryParams { ...@@ -111,6 +111,12 @@ export interface UserQueryParams {
ids?: any ids?: any
scrapydServerId?: string scrapydServerId?: string
project?: string project?: string
spiders?:any spiders?: any
times?:any times?: any
job_id?: string
scrapyd_server_id?: string
schedule_type?: string
spider?: string
cron?: string
options?: string
} }
\ No newline at end of file
...@@ -200,9 +200,10 @@ ...@@ -200,9 +200,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { onMounted, ref } from 'vue'
import Pagination from '@/components/pagination/index.vue' import Pagination from '@/components/pagination/index.vue'
import exportDialog from '@/components/Export/index.vue' import exportDialog from '@/components/Export/index.vue'
import { getDsnlist, getDsnDetail, deleteDsnData } from '@/api/spiderData'
const showDeleteDialog = ref(false) const showDeleteDialog = ref(false)
const timeValue = ref('') const timeValue = ref('')
...@@ -243,51 +244,7 @@ const recordTime = ref('') ...@@ -243,51 +244,7 @@ const recordTime = ref('')
const isSuspended = ref('') const isSuspended = ref('')
const tableData = ref([ const tableData = ref([
{
number: '1',
targetName: 'maven',
distance: '2000',
roundTripTime: '12',
stationName: 'CANBERRA',
targetAzimuth: '11',
targetElevation: '24',
targetDistance: '2000',
windSpeed: '10',
upSignalSource: 'maven',
upSignalFrequencyBand: 'X',
upSignalLaunchPower: '123',
downSignalSignalSource: 'maven',
downSignalFrequencyBand: 'S',
downSignalReceptionPower: '23',
dataTime: '2025-06-21 12:00:00',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
]) ])
const pageObj = ref({ const pageObj = ref({
total: 10, total: 10,
...@@ -299,8 +256,11 @@ const handleDetails = (row: any) => { ...@@ -299,8 +256,11 @@ const handleDetails = (row: any) => {
detailVisibleValue.value = true detailVisibleValue.value = true
} }
const getData = () => { const getData = async () => {
console.log('getData'); console.log('getData');
const res = await getDsnlist({page:pageObj.value.pageNo,size:pageObj.value.pageSize})
tableData.value = res.data.list
pageObj.value.total = res.data.total
} }
const handleClose = () => { const handleClose = () => {
...@@ -312,6 +272,9 @@ const handleExportConfirm = () => { ...@@ -312,6 +272,9 @@ const handleExportConfirm = () => {
const handleExport = () => { const handleExport = () => {
showDeleteDialog.value = true showDeleteDialog.value = true
} }
onMounted(() => {
getData()
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
......
...@@ -242,9 +242,10 @@ ...@@ -242,9 +242,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { onMounted, ref } from 'vue'
import Pagination from '@/components/pagination/index.vue' import Pagination from '@/components/pagination/index.vue'
import exportDialog from '@/components/Export/index.vue' import exportDialog from '@/components/Export/index.vue'
import { getItulist, getItuDetail, deleteItuData } from '@/api/spiderData'
const showDeleteDialog = ref(false) const showDeleteDialog = ref(false)
const timeValue = ref('') const timeValue = ref('')
...@@ -295,55 +296,7 @@ const isRestoreUsed = ref('') ...@@ -295,55 +296,7 @@ const isRestoreUsed = ref('')
const validityPeriodSatelliteNetworkOldName = ref('') const validityPeriodSatelliteNetworkOldName = ref('')
// 最新相关 BR IFIC 发布日期 // 最新相关 BR IFIC 发布日期
const BFIFICdate = ref('') const BFIFICdate = ref('')
const tableData = ref([ const tableData = ref([])
{
number: '1',
SNSNoticeID: '86550007',
department: 'ARG',
targetName: 'ARGOS',
planType: '计划',
syncType: '否',
syncPosition: '-',
perigee: '2000',
apogee: '2500',
lowestAltitude: '165',
referenceSubject: '地球',
lowestFrequency: 'None',
highestFrequency: 'None',
status: 'None',
validityPeriod: '-',
earliestUsageDate: 'None',
isUsed: 'No',
isPauseUsed: 'No',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
{
name: '1',
},
])
const pageObj = ref({ const pageObj = ref({
total: 10, total: 10,
pageSize: 10, pageSize: 10,
...@@ -353,8 +306,11 @@ const handleDetails = (row: any) => { ...@@ -353,8 +306,11 @@ const handleDetails = (row: any) => {
console.log(row); console.log(row);
detailVisibleValue.value = true detailVisibleValue.value = true
} }
const getData = () => { const getData = async () => {
console.log('getData'); const res = await getItulist({page:pageObj.value.pageNo,size:pageObj.value.pageSize})
pageObj.value.total = res.data.length
tableData.value = res.data
} }
const handleClose = () => { const handleClose = () => {
detailVisibleValue.value = false detailVisibleValue.value = false
...@@ -365,6 +321,10 @@ const handleExportConfirm = () => { ...@@ -365,6 +321,10 @@ const handleExportConfirm = () => {
const handleExport = () => { const handleExport = () => {
showDeleteDialog.value = true showDeleteDialog.value = true
} }
onMounted(() => {
getData()
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
......
...@@ -157,12 +157,11 @@ const handleConfirm = async () => { ...@@ -157,12 +157,11 @@ const handleConfirm = async () => {
if (!firstRuleFormRef.value) { if (!firstRuleFormRef.value) {
return; return;
} }
// 3. 使用Promise形式的validate(不传入回调),确保await生效 // 使用Promise形式的validate(不传入回调),确保await生效
const valid = await firstRuleFormRef.value.validate(); const valid = await firstRuleFormRef.value.validate();
if (valid) { if (valid) {
// 进入这里即197行附近逻辑
await addUser({ await addUser({
nickname: firstForm.value.userName, // 注意:根据实际接口字段调整 nickname: firstForm.value.userName,
username: firstForm.value.userAccount, username: firstForm.value.userAccount,
password: firstForm.value.userPassword, password: firstForm.value.userPassword,
role: userRoleValue.value, role: userRoleValue.value,
......
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
<deleteDialog v-model:dialogVisible="showDeleteDialog" @get-user-list="getUserListData" <deleteDialog v-model:dialogVisible="showDeleteDialog" @get-user-list="getUserListData"
:ids="userIds" :deleteMode="deleteMode" :id="userId" /> :ids="userIds" :deleteMode="deleteMode" :id="userId" />
<!-- 创建用户弹窗组件 --> <!-- 创建用户弹窗组件 -->
<!-- todo -->
<addUserDialog v-model:dialogVisible="dialogVisible" :mode="mode" :nickName="nickName" :userName="userName" <addUserDialog v-model:dialogVisible="dialogVisible" :mode="mode" :nickName="nickName" :userName="userName"
:id="userId" :userRole="userRole" :userStatus="userStatus" :userPassword="userPassword" :id="userId" :userRole="userRole" :userStatus="userStatus" :userPassword="userPassword"
@get-user-list="getUserListData" /> @get-user-list="getUserListData" />
......
<template> <template>
<el-dialog v-model="currentVisible" :title="currentMode === '1' ? '新增' : '修改'" width="610" center align-center <el-dialog v-model="currentVisible" :title="currentMode === AddMode.ADD_TASK ? '新增' : '修改'" width="710" center
@close="handleClose" draggable> align-center @close="handleClose" draggable>
<el-form :inline="true" v-if="currentMode === '1'"> <el-form :model="ruleForm" :rules="rules" ref="ruleFormRef" label-width="100px">
<el-form-item label="任务名称:"> <el-form-item label="任务名称:" v-if="currentMode === AddMode.ADD_TASK" prop="taskName">
<el-input v-model="taskName" placeholder="请输入任务名称" /> <el-input v-model="ruleForm.taskName" placeholder="请输入任务名称"style="width: 90%" />
</el-form-item> </el-form-item>
<el-form-item label="执行时间:"> <el-form-item label="所属爬虫:" v-if="currentMode === AddMode.ADD_TASK" prop="spiderTypeValue">
<el-input v-model="taskFrequency" placeholder="请输入执行时间" /> <el-select v-model="ruleForm.spiderTypeValue" placeholder="请选择所属爬虫" style="width: 90%">
</el-form-item>
</el-form>
<div class="m-t-4" />
<el-form :inline="true">
<el-form-item label="所属爬虫:">
<el-select v-model="spiderTypeValue" placeholder="请选择爬虫类型" style="width: 181px">
<el-option v-for="item in spiderTypeOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in spiderTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="执行频率:"> <el-form-item label="执行频率:" prop="cronExpression">
<el-input v-model="taskFrequency" placeholder="请输入执行频率" /> <Crontab v-model="ruleForm.cronExpression" style="width: 90%" />
</el-form-item>
</el-form>
<div class="m-t-4" />
<el-form :inline="true">
<el-form-item label="启用/停止:">
<el-switch v-model="taskSwitchValue" class="ml-2"
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
...@@ -39,65 +26,125 @@ ...@@ -39,65 +26,125 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { onMounted, ref } from 'vue'
import { defineProps } from 'vue'; import { defineProps } from 'vue';
import { watch } from 'vue' import { watch } from 'vue'
import { addSpiderTask } from '@/api/spiderTask'
import { AddMode } from './enum'
import { Crontab } from '@/components/Crontab/index'
import type { FormInstance, FormRules } from 'element-plus'
const props = defineProps({ interface RuleForm {
dialogVisible: { taskName: string
type: Boolean, spiderTypeValue: string
default: false cronExpression: string
}, }
mode: { const ruleFormRef = ref<FormInstance>()
type: String, const ruleForm = ref<RuleForm>({
default: '1' taskName: '',
}, spiderTypeValue: '',
spiderType: { cronExpression: '',
type: String, })
default: '' interface addDialogPropType {
dialogVisible: boolean,
mode: AddMode,
jobId: string,
cron: string,
spiderType: string,
options: string,
}
const props = defineProps<addDialogPropType>()
const rules = ref<FormRules<RuleForm>>({
taskName: [
{ required: true, message: '请输入任务名称', trigger: 'blur' },
{ min: 1, max: 50, message: '任务名称应在1-50个字符之间', trigger: 'blur' },
],
spiderTypeValue: [
{
required: true,
message: '请选择所属爬虫',
trigger: 'change',
}, },
frequency: { ],
type: String, cronExpression: [
default: '' {
required: true,
message: '请输入执行频率',
trigger: 'change',
}, },
taskStatus: { { min: 1, message: '执行频率应为cron表达式', trigger: 'blur' },
type: Boolean, ]
default: false
}
}) })
// 向父组件传递dialog值 // 向父组件传递dialog值
const emit = defineEmits(['update:dialogVisible', 'confirm']) const emit = defineEmits(['update:dialogVisible', 'confirm', 'update:mode', 'getTaskList'])
// 组件的状态 // 组件的状态
const currentVisible = ref(props.dialogVisible) const currentVisible = ref(props.dialogVisible)
const currentMode = ref(props.mode) const currentMode = ref(props.mode)
const taskName = ref('') const taskName = ref('')
const taskFrequency = ref('')
const taskSwitchValue = ref(false)
const spiderTypeValue = ref('') const spiderTypeValue = ref('')
const spiderTypeOptions = ref([ const spiderTypeOptions = ref([
{ {
value: '选项1', value: 'api_spider',
label: 'DSN爬虫' label: 'api_spider'
}, },
{ {
value: '选项2', value: 'dsn_now',
label: 'ITU爬虫' label: 'dsn_now'
}, },
{ {
value: '选项3', value: 'itu_space_explorer',
label: 'ST爬虫' label: 'itu_space_explorer'
}, },
]) ])
const taskParams = ref({
scrapyd_server_id: '1',
schedule_type: '0',
project: 'spiders'
})
const cronExpression = ref('')
// 关闭的方法 // 关闭的方法
const handleClose = () => { const handleClose = () => {
ruleForm.value.taskName = ''
ruleForm.value.spiderTypeValue
ruleForm.value.cronExpression = ''
currentVisible.value = false currentVisible.value = false
} }
// 确定的方法 // 确定的方法
const handleConfirm = () => { const handleConfirm = async () => {
emit('confirm') if (!ruleFormRef.value) return
await ruleFormRef.value.validate(async (valid, fields) => {
console.log('开始校验');
//验证表单内容是否通过,通过继续执行
if (valid) {
if (currentMode.value === AddMode.ADD_TASK) {
await addSpiderTask({
scrapyd_server_id: taskParams.value.scrapyd_server_id,
schedule_type: taskParams.value.schedule_type,
project: taskParams.value.project,
spider: ruleForm.value.spiderTypeValue,
cron: ruleForm.value.cronExpression,
options: JSON.stringify({ jobName: ruleForm.value.taskName })
})
currentVisible.value = false
emit('getTaskList')
} else if (currentMode.value === AddMode.UPDATE_TASK) {
await addSpiderTask({
scrapyd_server_id: taskParams.value.scrapyd_server_id,
schedule_type: taskParams.value.schedule_type,
project: taskParams.value.project,
spider: props.spiderType,
job_id: props.jobId,
cron: ruleForm.value.cronExpression,
options: props.options
})
currentVisible.value = false currentVisible.value = false
emit('getTaskList')
}
} else {
console.log('校验不通过');
}
})
} }
// 监听props变化,同步给组件内部 // 监听props变化,同步给组件内部
...@@ -119,6 +166,28 @@ watch(() => props.mode, ...@@ -119,6 +166,28 @@ watch(() => props.mode,
currentMode.value = newVal currentMode.value = newVal
} }
) )
watch(() => currentMode.value,
(newVal) => {
emit('update:mode', newVal)
}
)
watch(
[() => props.dialogVisible, () => props.mode, () => props.cron],
([newVisible, newMode, newCron]) => {
if (newVisible && newMode === AddMode.UPDATE_TASK) {
ruleForm.value.cronExpression = newCron;
}
if (!newVisible) {
// 清空表单
cronExpression.value = ''
}
},
{ immediate: true }
);
onMounted(() => {
})
</script> </script>
<style> <style>
...@@ -133,6 +202,10 @@ watch(() => props.mode, ...@@ -133,6 +202,10 @@ watch(() => props.mode,
.el-dialog__title { .el-dialog__title {
color: #ffffff; color: #ffffff;
} }
.el-input__inner {
color: #ffffff;
}
} }
/* 修改表单样式 */ /* 修改表单样式 */
......
<template>
<el-dialog v-model="deleteDialogVisible" title="删除" width="250" center align-center @close="close" draggable>
<div class="text-center">
<span style="color: #fff;font-size: 15px;">确定删除吗?</span>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="handleDelelte">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { defineProps } from 'vue';
import { deleteSpiderTask } from '@/api/spiderTask.ts'
interface deleteDialogPropType {
dialogVisible: boolean,
jobId: string
}
const props = defineProps<deleteDialogPropType>()
const emit = defineEmits(['update:dialogVisible', 'confirm', 'getUserList', 'update:deleteMode'])
const deleteDialogVisible = ref(props.dialogVisible)
// 删除方法
const handleDelelte = async () => {
await deleteSpiderTask({job_id: props.jobId})
emit('getUserList')
deleteDialogVisible.value = false
}
// 关闭弹窗的方法
const close = () => {
deleteDialogVisible.value = false
}
// 监听父组件传过来的值
watch(() => props.dialogVisible,
(newVal) => {
deleteDialogVisible.value = newVal
}
)
// 监听组件内的值并向父组件更新
watch(() => deleteDialogVisible.value,
(newVal) => {
emit('update:dialogVisible', newVal)
}
)
</script>
\ No newline at end of file
/// @ts-ignore
export enum AddMode {
ADD_TASK = 0,
UPDATE_TASK = 1,
}
\ No newline at end of file
<!-- 任务执行统计卡片组件 --> <!-- 任务执行统计卡片组件 -->
<template> <template>
<div class="taskCard p-6" v-for="task in taskList" :key="task.taskId"> <div class="text-left p-4 toolbarStyle ">
<div class="flex justify-center"> <div class="formStyle">
<el-form inline>
<el-form-item> <el-form-item>
<span class="titleStyle">任务名称</span> <el-text class="mx-1" style="color: #fff;">所属爬虫:</el-text>
</el-form-item> </el-form-item>
<el-form-item>
<div>
<el-select v-model="searchCondition.spiders" placeholder="请选择" style="width: 220px">
<el-option v-for="item in taskSelectOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</el-form-item>
<el-form-item>
<el-text class="mx-1" style="color: #fff;">任务名称:</el-text>
</el-form-item>
<el-form-item>
<el-input v-model="taskName" placeholder="请输入" style="width: 220px" />
</el-form-item>
<el-form-item>
<el-space>
<el-button type="primary" @Click="getData">查询</el-button>
</el-space>
</el-form-item>
<el-form-item>
<el-space>
<el-button type="primary" @click="openTaskDialog">新建任务</el-button>
</el-space>
</el-form-item>
</el-form>
</div> </div>
<div class="flex justify-center "> </div>
<div class="cardStyle">
<div class="taskCard p-6 " v-for="task in taskList" :key="task.taskId">
<div class="taskContent">
<div>
<el-form-item>
<span class="titleStyle">{{ task.kwargs.options === '' ? '无名称' : JSON.parse(task.kwargs.options).jobName
}}</span>
</el-form-item>
</div>
<div>
<el-form-item> <el-form-item>
<el-button type="primary" @click="editTask" class="editButton">编辑</el-button> <el-button type="primary" @click="editTask(task)" class="editButton">编辑</el-button>
<el-button type="success" @click="goToTaskRecord" class="recordButton">执行记录 </el-button> <el-button type="success" @click="goToTaskRecord" class="recordButton">执行记录 </el-button>
<el-button type="danger" @click="deleteTask" class="deleteButton">删除</el-button> <el-button type="danger" @click="deleteTask(task)" class="deleteButton">删除</el-button>
</el-form-item> </el-form-item>
</div> </div>
<div class="wordStyle flex justify-center"> <div class="wordStyle">
<el-form-item> <el-form-item>
<el-space> <el-space>
<span class="wordStyle">启用/停止: </span> <span class="wordStyle">启用/停止: </span>
<el-switch v-model="task.status" :active-value="1" :inactive-value="0" @change="changeStatus(task)" /> <el-switch v-model="task.status" :active-value="'running'" :inactive-value="'paused'"
@change="(newStatus:any) => changeStatus(task, newStatus)" />
<span class="wordStyle">执行频率: {{ task.frequency }} </span> <span class="wordStyle">执行频率: {{ task.frequency }} </span>
</el-space> </el-space>
</el-form-item> </el-form-item>
</div> </div>
<div class="wordStyle flex justify-center"> <div class="wordStyle">
<el-form-item> <el-form-item>
<el-space> <el-space>
<span class="wordStyle">执行次数: {{ task.executeCount }} 个; </span> <span class="wordStyle">执行次数: 个; </span>
<span class="wordStyle">成功次数: {{ task.successCount }}</span> <span class="wordStyle">成功次数:</span>
</el-space> </el-space>
</el-form-item> </el-form-item>
</div> </div>
<div class="wordStyle flex justify-center"> <div class="wordStyle ">
<el-form-item> <el-form-item>
<el-space> <el-space>
<span class="wordStyle">失败次数: {{ task.failCount }} 个; </span> <span class="wordStyle">失败次数: 个; </span>
<span class="wordStyle">异常记录: {{ task.unusualCount }}</span> <span class="wordStyle">异常记录:</span>
</el-space> </el-space>
</el-form-item> </el-form-item>
</div> </div>
</div> </div>
</div>
</div>
<deleteDialog v-model:dialogVisible="showDeleteDialog" @confirm="handleDeleteConfirm" @mode="deleteMode" /> <addTaskDialog v-model:dialogVisible="showTaskDialog" @confirm="handleEdit" :mode="editMode" @getTaskList="getData"
<addTaskDialog v-model:dialogVisible="showTaskDialog" @confirm="handleEdit" :mode="editMode" /> :jobId="jobId" :cron="frequency" :spiderType="spiderType" :options="taskOptions" />
<deleteDialog v-model:dialogVisible="showDeleteDialog" @get-user-list="getData" :jobId="jobId" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import deleteDialog from '@/components/Delete/index.vue'
import addTaskDialog from './addTaskDialog.vue' import addTaskDialog from './addTaskDialog.vue'
import { getSpiderTaskList } from '@/api/spiderTask' import { getSpiderTaskList, resumeSpiderTask, pauseSpiderTask } from '@/api/spiderTask'
import { DeleteMode } from '@/components/Delete/enum.ts'
import { AddMode } from './enum'
import deleteDialog from './deleteDialog.vue'
defineProps({ defineProps({
successTask: { successTask: {
type: String, type: String,
...@@ -65,82 +107,53 @@ defineProps({ ...@@ -65,82 +107,53 @@ defineProps({
default: '' default: ''
} }
}) })
const router = useRouter() const taskSelectOptions = [
const editMode = ref('')
const deleteMode = ref('1')
const showDeleteDialog = ref(false)
const showTaskDialog = ref(false)
const taskSwitch = ref(false)
const taskList = ref([
{
taskId: '1',
taskName: '任务1',
status: 1,
frequency: '1',
executeCount: '2',
successCount: '3',
failCount: '4',
unusualCount: '5'
},
{
taskId: '2',
taskName: '任务2',
status: 0,
frequency: '6',
executeCount: '7',
successCount: '8',
failCount: '9',
unusualCount: '10'
},
{ {
taskId: '3', value: 'api_spider',
taskName: '任务3', label: 'api_spider',
status: 1,
frequency: '11',
executeCount: '12',
successCount: '13',
failCount: '14',
unusualCount: '15'
}, },
{ {
taskId: '4', value: 'dsn_now',
taskName: '任务4', label: 'dsn_now',
status: 0,
frequency: '16',
executeCount: '17',
successCount: '18',
failCount: '19',
unusualCount: '20'
}, },
{ {
taskId: '5', value: 'itu_space_explorer',
taskName: '任务5', label: 'itu_space_explorer',
status: 1,
frequency: '21',
executeCount: '22',
successCount: '23',
failCount: '24',
unusualCount: '25'
}, },
{ ]
taskId: '6', const searchCondition = ref({
taskName: '任务6', spiders: '',
status: 0, options: ''
frequency: '26', })
executeCount: '27', const taskName = ref('')
successCount: '28', const taskValue = ref('')
failCount: '29', const spiderType = ref('')
unusualCount: '30' const router = useRouter()
}, const frequency = ref('')
]) const editMode = ref(AddMode.UPDATE_TASK)
const deleteMode = ref(DeleteMode.SINGLE_DELETE)
const showDeleteDialog = ref(false)
const showTaskDialog = ref(false)
const taskSwitch = ref(false)
const taskOptions = ref('')
const jobId = ref('')
const taskList = ref<any[]>([])
const deleteTask = () => { const deleteTask = (task: any) => {
deleteMode.value = '1' console.log(task);
jobId.value = task.id
deleteMode.value = DeleteMode.SINGLE_DELETE
showDeleteDialog.value = true showDeleteDialog.value = true
} }
const editTask = () => { const editTask = (task: any) => {
editMode.value = '2' jobId.value = task.id
taskOptions.value = JSON.stringify(JSON.parse(task.kwargs.options))
spiderType.value = task.kwargs.spider
frequency.value = task.kwargs.cron
editMode.value = AddMode.UPDATE_TASK
showTaskDialog.value = true showTaskDialog.value = true
// console.log(JSON.stringify(JSON.parse(task.kwargs.options)));
} }
const goToTaskRecord = () => { const goToTaskRecord = () => {
router.push({ router.push({
...@@ -151,29 +164,43 @@ const goToTaskRecord = () => { ...@@ -151,29 +164,43 @@ const goToTaskRecord = () => {
} }
}) })
} }
const changeStatus = (task: any) => { const changeStatus = async (task: any, newStatus: string) => {
if (newStatus === "running") {
} await resumeSpiderTask({ job_id: task.id });
} else if (newStatus === "paused") {
await pauseSpiderTask({ job_id: task.id });
}
await getData();
};
const handleEdit = () => { const handleEdit = () => {
} }
const handleDeleteConfirm = () => { const openTaskDialog = () => {
editMode.value = AddMode.ADD_TASK
showTaskDialog.value = true
} }
const getData = async () => { const getData = async () => {
console.log('getData'); const res = await getSpiderTaskList({ spiders: searchCondition.value.spiders ? [searchCondition.value.spiders] : [], options: JSON.stringify({ jobName: taskName.value }) })
const res = await getSpiderTaskList({spiders:['api_spider']}) taskList.value = res.data
// taskList.value = res.data
console.log(res);
} }
onMounted(() => { onMounted(() => {
getData() getData()
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.taskContent {
display: flex;
flex-direction: column; // 垂直排列
align-items: center; // 水平居中
justify-content: center; // 垂直居中
height: 100%;
gap: -15px;
padding: 10px 0;
}
// .editButton{ // .editButton{
// background: none; // background: none;
// background-image: url("@/assets/picture/button1.png"); // background-image: url("@/assets/picture/button1.png");
...@@ -192,6 +219,15 @@ onMounted(() => { ...@@ -192,6 +219,15 @@ onMounted(() => {
// background-size: 100% 100%; // background-size: 100% 100%;
// background-repeat: no-repeat; // background-repeat: no-repeat;
// } // }
.cardStyle {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
margin-top: 1.5%;
padding: 8px;
height: 26vh;
}
// 任务卡片样式 // 任务卡片样式
.taskCard { .taskCard {
background-image: url("@/assets/picture/box2.png"); background-image: url("@/assets/picture/box2.png");
...@@ -202,6 +238,8 @@ onMounted(() => { ...@@ -202,6 +238,8 @@ onMounted(() => {
font-size: 20px; font-size: 20px;
height: 100%; height: 100%;
border-radius: 7px; border-radius: 7px;
display: flex;
flex-direction: column;
.titleStyle { .titleStyle {
font-size: 22px; font-size: 22px;
...@@ -216,6 +254,21 @@ onMounted(() => { ...@@ -216,6 +254,21 @@ onMounted(() => {
} }
} }
/* 工具栏样式 */
.toolbarStyle {
background-image: url("@/assets/picture/box3.png");
background-size: 100% 100%;
background-repeat: no-repeat;
}
/* 表单样式 */
.formStyle {
display: flex;
justify-content: space-around;
margin-top: 0.5%;
padding: 2px;
}
// 去除按钮边框 // 去除按钮边框
.el-button:focus { .el-button:focus {
outline: none; outline: none;
...@@ -229,4 +282,8 @@ onMounted(() => { ...@@ -229,4 +282,8 @@ onMounted(() => {
color: #ffffff; color: #ffffff;
border: none; border: none;
} */ } */
.el-input {
color: #FFFFFF;
}
</style> </style>
\ No newline at end of file
...@@ -2,63 +2,27 @@ ...@@ -2,63 +2,27 @@
<div> <div>
<div class="backStyle" v-if="route.query.jump === 'yes'" @click="goToTaskInformation" /> <div class="backStyle" v-if="route.query.jump === 'yes'" @click="goToTaskInformation" />
<div class="m-t-10" /> <div class="m-t-10" />
<div class="text-left p-4 toolbarStyle"> <div >
<div class="formStyle">
<el-form inline>
<el-form-item>
<el-text class="mx-1">所属爬虫:</el-text>
</el-form-item>
<el-form-item>
<div>
<el-select v-model="taskValue" placeholder="请选择" style="width: 220px">
<el-option v-for="item in taskOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</el-form-item>
<el-form-item>
<el-text class="mx-1">任务名称:</el-text>
</el-form-item>
<el-form-item>
<el-input v-model="taskValue" placeholder="请输入" style="width: 220px" />
</el-form-item>
<el-form-item>
<el-space>
<el-button type="primary">查询</el-button>
</el-space>
</el-form-item>
<el-form-item>
<el-space>
<el-button type="primary" @click="openTaskDialog">新建任务</el-button>
</el-space>
</el-form-item>
</el-form>
</div>
</div>
<div class="cardStyle">
<taskCard successTask="100" failTask="10" unusualTask="1" /> <taskCard successTask="100" failTask="10" unusualTask="1" />
<!-- <div class="pagination w-full flex flex-row-reverse pr-18 m-t-0"> <!-- <div class="pagination w-full flex flex-row-reverse pr-18 m-t-0">
<Pagination :total="pageObj.total" v-model:page="pageObj.pageNo" v-model:limit="pageObj.pageSize" <Pagination :total="pageObj.total" v-model:page="pageObj.pageNo" v-model:limit="pageObj.pageSize"
@pagination="getData" /> @pagination="getData" />
</div> --> </div> -->
</div> </div>
<!-- 添加任务对话框组件 -->
<addTaskDialog v-model:dialogVisible="showDialog" @confirm="handleDeleteConfirm" :mode="editMode" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import Pagination from '@/components/pagination/index.vue' import Pagination from '@/components/pagination/index.vue'
import taskCard from './components/taskCard.vue' import taskCard from './components/taskCard.vue'
import addTaskDialog from './components/addTaskDialog.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { getSpiderTaskList } from '@/api/spiderTask' import { getSpiderTaskList,getTaskData } from '@/api/spiderTask'
import { AddMode } from './components/enum'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const editMode = ref('1')
const taskValue = ref('') const taskValue = ref('')
const taskList = ref([]) const taskList = ref([])
const taskOptions = [ const taskOptions = [
...@@ -81,23 +45,9 @@ const pageObj = ref({ ...@@ -81,23 +45,9 @@ const pageObj = ref({
pageNo: 1 pageNo: 1
}) })
// 控制对话框显示/隐藏的状态变量
const showDialog = ref(false)
// 处理删除确认
const handleDeleteConfirm = () => {
console.log('用户确认删除')
}
const openTaskDialog = () => {
showDialog.value = true
}
const goToTaskInformation = () => { const goToTaskInformation = () => {
router.push({ path: '/osSpiderManager/list' }) router.push({ path: '/osSpiderManager/list' })
} }
onMounted(() => {
})
</script> </script>
<style scoped> <style scoped>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment