chore: update cron desc mobile #20
This commit is contained in:
@@ -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">
|
||||||
<!-- Simple 模式下的状态显示 -->
|
<span class="text-sm font-medium text-foreground whitespace-nowrap truncate">{{ title }}</span>
|
||||||
<Badge v-if="variant === 'simple'" variant="outline" :class="[
|
|
||||||
'px-2 py-0.5 font-normal rounded-full border text-[10px] hidden sm:flex',
|
<!-- Simple 模式下的状态显示 -->
|
||||||
getStatusBadgeClass(log.status)
|
<Badge v-if="variant === 'simple'" variant="outline" :class="[
|
||||||
]">
|
'px-2 py-0.5 font-normal rounded-full border text-[10px] flex',
|
||||||
{{ TASK_STATUS_TEXT[log.status] || log.status }}
|
getStatusBadgeClass(log.status)
|
||||||
</Badge>
|
]">
|
||||||
|
{{ TASK_STATUS_TEXT[log.status] || log.status }}
|
||||||
|
</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>
|
||||||
|
|||||||
Reference in New Issue
Block a user