chore: update cron desc mobile #20

This commit is contained in:
engigu
2026-04-08 20:31:27 +08:00
parent ad803da843
commit ba522c4f36
+111 -35
View File
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, onMounted, onUnmounted, watch } from 'vue'
import { import {
X, Trash2, Maximize2, CheckCircle2, XCircle, AlertCircle, Clock, Ban, X, Trash2, Maximize2, CheckCircle2, XCircle, AlertCircle, Clock, Ban,
Zap as ZapIcon, Search, MinusCircle Zap as ZapIcon, Search, MinusCircle
@@ -43,13 +43,56 @@ defineEmits<{
}>() }>()
const searchKeyword = ref('') const searchKeyword = ref('')
const currentDuration = ref(props.log?.duration || 0)
let timer: ReturnType<typeof setInterval> | null = null
function formatDuration(ms: number): string { function formatDuration(ms: number): string {
if (ms < 1000) return `${ms}毫秒` if (ms <= 0) return '0毫秒'
if (ms < 1000) return `${ms.toFixed(0)}毫秒`
if (ms < 60000) return `${(ms / 1000).toFixed(1)}` if (ms < 60000) return `${(ms / 1000).toFixed(1)}`
return `${(ms / 60000).toFixed(1)}分钟` return `${(ms / 60000).toFixed(1)}分钟`
} }
const startTimer = () => {
stopTimer()
if (!props.log || props.log.status !== TASK_STATUS.RUNNING || !props.log.start_time) return
const startTime = new Date(props.log.start_time).getTime()
timer = setInterval(() => {
currentDuration.value = Date.now() - startTime
}, 100)
}
const stopTimer = () => {
if (timer) {
clearInterval(timer)
timer = null
}
}
onMounted(() => {
startTimer()
})
onUnmounted(() => {
stopTimer()
})
watch(() => props.log?.status, (newStatus) => {
if (newStatus === TASK_STATUS.RUNNING) {
startTimer()
} else {
stopTimer()
if (props.log) currentDuration.value = props.log.duration
}
})
watch(() => props.log?.duration, (newVal) => {
if (props.log?.status !== TASK_STATUS.RUNNING) {
currentDuration.value = newVal || 0
}
})
function getStatusBadgeClass(status: string) { function getStatusBadgeClass(status: string) {
switch (status) { switch (status) {
case TASK_STATUS.SUCCESS: case TASK_STATUS.SUCCESS:
@@ -74,44 +117,77 @@ function getStatusBadgeClass(status: string) {
<template> <template>
<div v-if="log" class="w-full h-full flex flex-col overflow-hidden bg-card"> <div v-if="log" class="w-full h-full flex flex-col overflow-hidden bg-card">
<!-- 头部菜单 --> <!-- 头部菜单 (响应式两行布局) -->
<div class="flex items-center justify-between px-4 h-11 border-b bg-muted/20 shrink-0 gap-4"> <div class="flex flex-col border-b bg-muted/20 shrink-0">
<div class="flex items-center gap-3 min-w-0"> <!-- 第一行: 标题与核心控制按钮 -->
<span class="text-sm font-normal text-muted-foreground whitespace-nowrap">{{ title }}</span> <div class="flex items-center justify-between px-4 h-11 gap-4">
<div class="flex items-center gap-2 min-w-0">
<span class="text-sm font-medium text-foreground whitespace-nowrap truncate">{{ title }}</span>
<!-- Simple 模式下的状态显示 --> <!-- Simple 模式下的状态显示 -->
<Badge v-if="variant === 'simple'" variant="outline" :class="[ <Badge v-if="variant === 'simple'" variant="outline" :class="[
'px-2 py-0.5 font-normal rounded-full border text-[10px] hidden sm:flex', 'px-2 py-0.5 font-normal rounded-full border text-[10px] flex',
getStatusBadgeClass(log.status) getStatusBadgeClass(log.status)
]"> ]">
{{ TASK_STATUS_TEXT[log.status] || log.status }} {{ TASK_STATUS_TEXT[log.status] || log.status }}
</Badge> </Badge>
<Button v-if="log.status === TASK_STATUS.RUNNING" variant="destructive" size="sm" <!-- 详情模式下的停止按钮 (始终在第一行进入) -->
class="h-6 px-2 text-[10px]" :disabled="isStopping" @click="$emit('stop')"> <Button v-if="log.status === TASK_STATUS.RUNNING && variant === 'full'" variant="destructive" size="sm"
{{ isStopping ? '停止中...' : '停止任务' }} class="h-6 px-2 text-[10px] shrink-0" :disabled="isStopping" @click="$emit('stop')">
</Button> {{ isStopping ? '停止中' : '停止任务' }}
</div> </Button>
<div class="flex items-center gap-2">
<!-- Searchbox for Simple mode -->
<div v-if="variant === 'simple'" class="relative flex-1 sm:flex-none">
<Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
<Input v-model="searchKeyword" placeholder="搜索内容..." class="h-7 pl-8 w-full sm:w-48 text-xs bg-muted/30" />
</div> </div>
<Button v-if="variant === 'simple'" variant="ghost" size="icon" class="h-7 w-7 text-muted-foreground" <div class="flex items-center gap-1.5 shrink-0">
title="全屏切换" @click="$emit('maximize')"> <!-- 极简模式桌面端显示的停止按钮 -->
<Maximize2 class="h-3.5 w-3.5" /> <Button v-if="log.status === TASK_STATUS.RUNNING && variant === 'simple'" variant="destructive" size="sm"
class="h-7 px-2 text-[10px] hidden sm:flex" :disabled="isStopping" @click="$emit('stop')">
{{ isStopping ? '停止中' : '停止任务' }}
</Button>
<div class="flex items-center">
<Button v-if="variant === 'simple'" variant="ghost" size="icon" class="h-8 w-8 text-muted-foreground"
title="全屏切换" @click="$emit('maximize')">
<Maximize2 class="h-4 w-4" />
</Button>
<Button variant="ghost" size="icon" class="h-8 w-8 text-muted-foreground hover:text-destructive"
title="删除该日志" @click="$emit('delete', log.id)">
<Trash2 class="h-4 w-4" />
</Button>
<Button v-if="showClose" variant="ghost" size="icon" class="h-8 w-8" @click="$emit('close')" title="关闭">
<X class="h-4 w-4" />
</Button>
</div>
</div>
</div>
<!-- 第二行: 工具栏 (仅在极简模式的移动端展示) -->
<div v-if="variant === 'simple'"
class="flex items-center gap-2 px-4 pb-3 sm:hidden border-t pt-2 mt-0.5 border-dashed border-muted-foreground/10">
<!-- 极简模式移动端停止按钮 -->
<Button v-if="log.status === TASK_STATUS.RUNNING" variant="destructive" size="sm"
class="h-8 px-3 text-xs gap-1.5 flex-1 max-w-[120px]" :disabled="isStopping" @click="$emit('stop')">
<Ban class="h-3.5 w-3.5" />
{{ isStopping ? '停止中' : '停止' }}
</Button> </Button>
<Button variant="ghost" size="icon" class="h-7 w-7 text-muted-foreground hover:text-destructive" <!-- 极简模式移动端搜索框 -->
title="删除该日志" @click="$emit('delete', log.id)"> <div class="relative flex-1">
<Trash2 class="h-3.5 w-3.5" /> <Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
</Button> <Input v-model="searchKeyword" placeholder="搜索日志内容..." class="h-8 pl-8 w-full text-sm bg-background/50 border-none shadow-inner" />
<Button v-if="showClose" variant="ghost" size="icon" class="h-7 w-7" @click="$emit('close')" title="关闭"> </div>
<X class="h-3.5 w-3.5" /> </div>
</Button>
<!-- 桌面端搜索栏 (Simple 模式) -->
<div v-if="variant === 'simple'" class="hidden sm:flex items-center px-4 pb-2 -mt-1">
<div class="relative w-64">
<Search class="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
<Input v-model="searchKeyword" placeholder="搜索内容..." class="h-7 pl-8 w-full text-xs bg-background/50" />
</div>
</div> </div>
</div> </div>
@@ -145,7 +221,7 @@ function getStatusBadgeClass(status: string) {
</div> </div>
<div class="flex justify-between items-center h-6"> <div class="flex justify-between items-center h-6">
<span class="text-sm font-normal text-muted-foreground">耗时</span> <span class="text-sm font-normal text-muted-foreground">耗时</span>
<span class="text-sm font-normal text-muted-foreground">{{ formatDuration(log.duration) }}</span> <span class="text-sm font-normal text-muted-foreground">{{ formatDuration(currentDuration) }}</span>
</div> </div>
<div class="flex justify-between items-center h-6"> <div class="flex justify-between items-center h-6">
<span class="text-sm font-normal text-muted-foreground">开始时间</span> <span class="text-sm font-normal text-muted-foreground">开始时间</span>