mirror of
https://github.com/Cccc-owo/CheckInApp.git
synced 2026-06-17 14:06:28 +00:00
style: use united style
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<a-row :gutter="[20, 20]">
|
||||
<!-- Token 状态卡片 -->
|
||||
<a-col :xs="24" :sm="24" :md="24">
|
||||
<a-card class="status-card">
|
||||
<a-card class="status-card md3-card">
|
||||
<template #title>
|
||||
<div class="card-header">
|
||||
<KeyOutlined />
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
<!-- 手动打卡卡片 -->
|
||||
<a-col :xs="24" :sm="24" :md="24">
|
||||
<a-card>
|
||||
<a-card class="md3-card">
|
||||
<template #title>
|
||||
<div class="card-header">
|
||||
<CalendarOutlined />
|
||||
@@ -123,7 +123,7 @@
|
||||
}}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="打卡响应" :span="2">
|
||||
<a-descriptions-item label="打卡响应" :span="{ xs: 1, sm: 1, md: 2 }">
|
||||
{{ lastCheckIn.response_text || lastCheckIn.error_message || '-' }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
@@ -134,7 +134,7 @@
|
||||
|
||||
<!-- 用户信息卡片 -->
|
||||
<a-col :xs="24" :sm="24" :md="24">
|
||||
<a-card>
|
||||
<a-card class="md3-card">
|
||||
<template #title>
|
||||
<div class="card-header">
|
||||
<UserOutlined />
|
||||
@@ -151,10 +151,10 @@
|
||||
{{ authStore.isAdmin ? '管理员' : '普通用户' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="邮箱" :span="2">
|
||||
<a-descriptions-item label="邮箱">
|
||||
{{ authStore.user?.email || '未设置' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="注册时间" :span="2">
|
||||
<a-descriptions-item label="注册时间">
|
||||
{{ formatDateTime(authStore.user?.created_at, false) }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
@@ -335,23 +335,58 @@ onMounted(async () => {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.token-status {
|
||||
padding: 10px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.token-status .ant-descriptions {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.check-in-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
padding: 16px 20px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.check-in-container .hint {
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
font-size: 14px;
|
||||
margin: 0 0 4px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.last-check-in {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.last-check-in .label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--md-sys-color-on-surface-variant);
|
||||
margin: 12px 0 8px 0;
|
||||
}
|
||||
|
||||
.ant-alert {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.ant-select {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<Layout>
|
||||
<div class="settings-view">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<h1 class="text-3xl font-bold text-gray-800 dark:text-gray-100 mb-6">个人设置</h1>
|
||||
<h1 class="text-3xl font-bold text-on-surface mb-6">个人设置</h1>
|
||||
|
||||
<!-- 基本信息卡片 -->
|
||||
<div class="md3-card p-6 mb-6">
|
||||
<h2 class="text-xl font-bold text-gray-800 dark:text-gray-100 mb-4 flex items-center">
|
||||
<a-card class="md3-card mb-6">
|
||||
<h2 class="text-xl font-bold text-on-surface mb-4 flex items-center">
|
||||
<UserOutlined class="mr-2" />
|
||||
基本信息
|
||||
</h2>
|
||||
@@ -28,11 +28,11 @@
|
||||
{{ formatDate(user?.created_at) }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 修改邮箱 -->
|
||||
<div class="md3-card p-6 mb-6">
|
||||
<h2 class="text-xl font-bold text-gray-800 dark:text-gray-100 mb-4 flex items-center">
|
||||
<a-card class="md3-card mb-6">
|
||||
<h2 class="text-xl font-bold text-on-surface mb-4 flex items-center">
|
||||
<EditOutlined class="mr-2" />
|
||||
修改个人信息
|
||||
</h2>
|
||||
@@ -73,11 +73,11 @@
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 设置/修改密码 -->
|
||||
<div class="md3-card p-6">
|
||||
<h2 class="text-xl font-bold text-gray-800 dark:text-gray-100 mb-4 flex items-center">
|
||||
<a-card class="md3-card">
|
||||
<h2 class="text-xl font-bold text-on-surface mb-4 flex items-center">
|
||||
<KeyOutlined class="mr-2" />
|
||||
{{ hasPassword ? '修改密码' : '设置密码' }}
|
||||
</h2>
|
||||
@@ -136,7 +136,7 @@
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
返回任务列表
|
||||
</a-button>
|
||||
|
||||
<div v-if="currentTask" class="fluent-card p-6">
|
||||
<a-card v-if="currentTask" class="md3-card">
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<h1 class="text-3xl font-bold text-gradient mb-2">{{ currentTask.name || '未命名任务' }}</h1>
|
||||
<div class="flex items-center gap-4 text-sm text-gray-600">
|
||||
<div class="flex items-center gap-4 text-sm text-on-surface-variant">
|
||||
<span class="flex items-center">
|
||||
<NumberOutlined class="mr-1" />
|
||||
接龙 ID: {{ getThreadId(currentTask) }}
|
||||
@@ -35,54 +35,54 @@
|
||||
{{ checkInLoading ? '打卡中...' : '立即打卡' }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<!-- Stats Summary -->
|
||||
<a-row :gutter="[16, 16]" class="mb-6">
|
||||
<a-col :xs="12" :sm="8" :md="4">
|
||||
<div class="fluent-card p-5 animate-slide-up">
|
||||
<p class="text-sm text-gray-600 mb-1">总打卡次数</p>
|
||||
<p class="text-2xl font-bold text-gray-800">{{ recordStats.total }}</p>
|
||||
</div>
|
||||
<a-card class="md3-card animate-slide-up">
|
||||
<p class="text-sm text-on-surface-variant mb-1">总打卡次数</p>
|
||||
<p class="text-2xl font-bold text-on-surface">{{ recordStats.total }}</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="12" :sm="8" :md="4">
|
||||
<div class="fluent-card p-5 animate-slide-up" style="animation-delay: 0.05s">
|
||||
<p class="text-sm text-gray-600 mb-1">成功次数</p>
|
||||
<p class="text-2xl font-bold text-green-600">{{ recordStats.success }}</p>
|
||||
</div>
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.05s">
|
||||
<p class="text-sm text-on-surface-variant mb-1">成功次数</p>
|
||||
<p class="text-2xl font-bold text-green-600 dark:text-green-400">{{ recordStats.success }}</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="12" :sm="8" :md="4">
|
||||
<div class="fluent-card p-5 animate-slide-up" style="animation-delay: 0.1s">
|
||||
<p class="text-sm text-gray-600 mb-1">时间范围外</p>
|
||||
<p class="text-2xl font-bold text-blue-600">{{ recordStats.outOfTime }}</p>
|
||||
</div>
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.1s">
|
||||
<p class="text-sm text-on-surface-variant mb-1">时间范围外</p>
|
||||
<p class="text-2xl font-bold text-blue-600 dark:text-blue-400">{{ recordStats.outOfTime }}</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="12" :sm="8" :md="4">
|
||||
<div class="fluent-card p-5 animate-slide-up" style="animation-delay: 0.15s">
|
||||
<p class="text-sm text-gray-600 mb-1">失败次数</p>
|
||||
<p class="text-2xl font-bold text-red-600">{{ recordStats.failure }}</p>
|
||||
</div>
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.15s">
|
||||
<p class="text-sm text-on-surface-variant mb-1">失败次数</p>
|
||||
<p class="text-2xl font-bold text-red-600 dark:text-red-400">{{ recordStats.failure }}</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="12" :sm="8" :md="4">
|
||||
<div class="fluent-card p-5 animate-slide-up" style="animation-delay: 0.2s">
|
||||
<p class="text-sm text-gray-600 mb-1">异常次数</p>
|
||||
<p class="text-2xl font-bold text-orange-600">{{ recordStats.unknown }}</p>
|
||||
</div>
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.2s">
|
||||
<p class="text-sm text-on-surface-variant mb-1">异常次数</p>
|
||||
<p class="text-2xl font-bold text-orange-600 dark:text-orange-400">{{ recordStats.unknown }}</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :xs="12" :sm="8" :md="4">
|
||||
<div class="fluent-card p-5 animate-slide-up" style="animation-delay: 0.25s">
|
||||
<p class="text-sm text-gray-600 mb-1">成功率</p>
|
||||
<p class="text-2xl font-bold text-purple-600">{{ recordStats.successRate }}%</p>
|
||||
</div>
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.25s">
|
||||
<p class="text-sm text-on-surface-variant mb-1">成功率</p>
|
||||
<p class="text-2xl font-bold text-purple-600 dark:text-purple-400">{{ recordStats.successRate }}%</p>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="fluent-card p-4 mb-6">
|
||||
<a-card class="md3-card mb-6">
|
||||
<a-space wrap :size="[16, 16]">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-gray-700">状态筛选:</span>
|
||||
<span class="text-sm font-medium text-on-surface">状态筛选:</span>
|
||||
<a-radio-group v-model:value="filterStatus" button-style="solid" size="small" @change="handleFilterChange">
|
||||
<a-radio-button value="">全部</a-radio-button>
|
||||
<a-radio-button value="success">成功</a-radio-button>
|
||||
@@ -93,7 +93,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-gray-700">触发方式:</span>
|
||||
<span class="text-sm font-medium text-on-surface">触发方式:</span>
|
||||
<a-radio-group v-model:value="filterTrigger" button-style="solid" size="small" @change="handleFilterChange">
|
||||
<a-radio-button value="">全部</a-radio-button>
|
||||
<a-radio-button value="scheduler">自动</a-radio-button>
|
||||
@@ -106,7 +106,7 @@
|
||||
刷新
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- Records List -->
|
||||
<div v-if="loading" class="space-y-4">
|
||||
@@ -115,22 +115,22 @@
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<div v-else-if="records.length === 0" class="fluent-card p-12 text-center">
|
||||
<FileTextOutlined class="text-8xl text-gray-300 mb-4" />
|
||||
<h3 class="text-xl font-semibold text-gray-700 mb-2">暂无打卡记录</h3>
|
||||
<p class="text-gray-500">当前筛选条件下没有找到任何打卡记录</p>
|
||||
</div>
|
||||
<a-card v-else-if="records.length === 0" class="md3-card text-center" style="padding: 48px 20px;">
|
||||
<FileTextOutlined class="text-8xl text-on-surface-variant opacity-30 mb-4" />
|
||||
<h3 class="text-xl font-semibold text-on-surface mb-2">暂无打卡记录</h3>
|
||||
<p class="text-on-surface-variant">当前筛选条件下没有找到任何打卡记录</p>
|
||||
</a-card>
|
||||
|
||||
<div v-else class="space-y-4">
|
||||
<div
|
||||
<a-card
|
||||
v-for="record in records"
|
||||
:key="record.id"
|
||||
class="fluent-card p-6 hover:shadow-xl transition-all animate-slide-up"
|
||||
class="md3-card hover:shadow-xl transition-all animate-slide-up"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-3 mb-2 flex-wrap">
|
||||
<h3 class="text-lg font-semibold text-gray-800">
|
||||
<h3 class="text-lg font-semibold text-on-surface">
|
||||
打卡记录 #{{ record.id }}
|
||||
</h3>
|
||||
<a-tag
|
||||
@@ -153,7 +153,7 @@
|
||||
{{ record.trigger_type === 'scheduled' ? '自动触发' : '手动触发' }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<div class="flex items-center text-sm text-on-surface-variant">
|
||||
<ClockCircleOutlined class="mr-1" />
|
||||
{{ formatDateTime(record.check_in_time) }}
|
||||
</div>
|
||||
@@ -161,18 +161,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Record Details -->
|
||||
<div class="bg-gray-50 rounded-lg p-4 space-y-2">
|
||||
<div class="bg-surface-container-high dark:bg-surface-container rounded-lg p-4 space-y-2">
|
||||
<div v-if="record.response_text" class="flex items-start">
|
||||
<span class="text-sm font-medium text-gray-700 w-20">响应:</span>
|
||||
<span class="text-sm text-gray-900 flex-1">{{ record.response_text }}</span>
|
||||
<span class="text-sm font-medium text-on-surface-variant w-20">响应:</span>
|
||||
<span class="text-sm text-on-surface flex-1">{{ record.response_text }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="record.error_message" class="flex items-start">
|
||||
<span class="text-sm font-medium text-red-700 w-20">错误:</span>
|
||||
<span class="text-sm text-red-600 flex-1">{{ record.error_message }}</span>
|
||||
<span class="text-sm font-medium text-error w-20">错误:</span>
|
||||
<span class="text-sm text-error flex-1">{{ record.error_message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold text-gradient mb-2">任务管理</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400">管理您的自动打卡任务</p>
|
||||
<p class="text-on-surface-variant">管理您的自动打卡任务</p>
|
||||
</div>
|
||||
<a-button
|
||||
type="primary"
|
||||
@@ -25,45 +25,45 @@
|
||||
<!-- Stats Cards -->
|
||||
<a-row :gutter="[16, 16]" class="mb-6">
|
||||
<a-col :xs="24" :sm="8" :md="8">
|
||||
<div class="fluent-card p-6 animate-slide-up">
|
||||
<a-card class="md3-card animate-slide-up">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-1">总任务数</p>
|
||||
<p class="text-3xl font-bold text-primary-600 dark:text-primary-400">{{ taskStore.taskStats.total }}</p>
|
||||
<p class="text-sm text-on-surface-variant mb-1">总任务数</p>
|
||||
<p class="text-3xl font-bold text-primary">{{ taskStore.taskStats.total }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900/30 rounded-md3 flex items-center justify-center">
|
||||
<FileTextOutlined class="text-2xl text-primary-600 dark:text-primary-400" />
|
||||
<FileTextOutlined class="text-2xl text-primary" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :xs="24" :sm="8" :md="8">
|
||||
<div class="fluent-card p-6 animate-slide-up" style="animation-delay: 0.1s">
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.1s">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-1">启用中</p>
|
||||
<p class="text-sm text-on-surface-variant mb-1">启用中</p>
|
||||
<p class="text-3xl font-bold text-green-600 dark:text-green-400">{{ taskStore.taskStats.active }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-green-100 dark:bg-green-900/30 rounded-md3 flex items-center justify-center">
|
||||
<CheckCircleOutlined class="text-2xl text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<a-col :xs="24" :sm="8" :md="8">
|
||||
<div class="fluent-card p-6 animate-slide-up" style="animation-delay: 0.2s">
|
||||
<a-card class="md3-card animate-slide-up" style="animation-delay: 0.2s">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-1">已禁用</p>
|
||||
<p class="text-3xl font-bold text-gray-600 dark:text-gray-300">{{ taskStore.taskStats.inactive }}</p>
|
||||
<p class="text-sm text-on-surface-variant mb-1">已禁用</p>
|
||||
<p class="text-3xl font-bold text-on-surface-variant">{{ taskStore.taskStats.inactive }}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-gray-100 dark:bg-gray-700 rounded-md3 flex items-center justify-center">
|
||||
<StopOutlined class="text-2xl text-gray-600 dark:text-gray-300" />
|
||||
<div class="w-12 h-12 bg-surface-container-high rounded-md3 flex items-center justify-center">
|
||||
<StopOutlined class="text-2xl text-on-surface-variant" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
@@ -79,14 +79,14 @@
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<div v-else-if="taskStore.tasks.length === 0" class="fluent-card p-12 text-center">
|
||||
<FileTextOutlined class="text-8xl text-gray-300 mb-4" />
|
||||
<h3 class="text-xl font-semibold text-gray-700 mb-2">暂无任务</h3>
|
||||
<p class="text-gray-500 mb-6">点击右上角的"创建任务"按钮开始添加您的第一个打卡任务</p>
|
||||
<a-card v-else-if="taskStore.tasks.length === 0" class="md3-card text-center" style="padding: 48px 20px;">
|
||||
<FileTextOutlined class="text-8xl text-on-surface-variant opacity-30 mb-4" />
|
||||
<h3 class="text-xl font-semibold text-on-surface mb-2">暂无任务</h3>
|
||||
<p class="text-on-surface-variant mb-6">点击右上角的"创建任务"按钮开始添加您的第一个打卡任务</p>
|
||||
<a-button type="primary" @click="showCreateDialog = true">
|
||||
创建第一个任务
|
||||
</a-button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<a-row v-else :gutter="[16, 16]">
|
||||
<a-col
|
||||
@@ -94,15 +94,16 @@
|
||||
v-for="task in taskStore.tasks"
|
||||
:key="task.id"
|
||||
>
|
||||
<div
|
||||
class="fluent-card p-6 hover:scale-105 transform transition-all cursor-pointer animate-slide-up"
|
||||
<a-card
|
||||
class="md3-card hover:scale-105 transform transition-all cursor-pointer animate-slide-up"
|
||||
@click="viewTask(task)"
|
||||
>
|
||||
<!-- Task Header -->
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-1">{{ task.name || '未命名任务' }}</h3>
|
||||
<p class="text-sm text-gray-500">任务 ID: {{ task.id }}</p>
|
||||
<h3 class="text-lg font-semibold text-on-surface mb-1">{{ task.name || '未命名任务' }}</h3>
|
||||
<a-divider style="margin: 8px 0;" />
|
||||
<p class="text-sm text-on-surface-variant">任务 ID: {{ task.id }}</p>
|
||||
</div>
|
||||
<a-tag :color="task.is_active ? 'success' : 'default'">
|
||||
{{ task.is_active ? '启用' : '禁用' }}
|
||||
@@ -111,21 +112,21 @@
|
||||
|
||||
<!-- Task Details -->
|
||||
<div class="space-y-2 mb-4">
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<div class="flex items-center text-sm text-on-surface-variant">
|
||||
<TagOutlined class="mr-2" />
|
||||
接龙ID: {{ getThreadId(task) }}
|
||||
</div>
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<div class="flex items-center text-sm text-on-surface-variant">
|
||||
<ClockCircleOutlined class="mr-2" />
|
||||
最后打卡: {{ task.last_check_in_time ? formatDateTime(task.last_check_in_time) : '未打卡' }}
|
||||
</div>
|
||||
<div class="flex items-center text-sm">
|
||||
<CheckCircleOutlined class="mr-2 text-gray-600" />
|
||||
<CheckCircleOutlined class="mr-2 text-on-surface-variant" />
|
||||
<span v-if="task.last_check_in_status" :class="{
|
||||
'text-green-600 font-medium': task.last_check_in_status === 'success',
|
||||
'text-blue-600 font-medium': task.last_check_in_status === 'out_of_time',
|
||||
'text-red-600 font-medium': task.last_check_in_status === 'failure',
|
||||
'text-yellow-600 font-medium': task.last_check_in_status === 'unknown'
|
||||
'text-green-600 dark:text-green-400 font-medium': task.last_check_in_status === 'success',
|
||||
'text-blue-600 dark:text-blue-400 font-medium': task.last_check_in_status === 'out_of_time',
|
||||
'text-red-600 dark:text-red-400 font-medium': task.last_check_in_status === 'failure',
|
||||
'text-yellow-600 dark:text-yellow-400 font-medium': task.last_check_in_status === 'unknown'
|
||||
}">
|
||||
{{
|
||||
task.last_check_in_status === 'success' ? '✅ 打卡成功' :
|
||||
@@ -134,12 +135,12 @@
|
||||
'❗ 打卡异常'
|
||||
}}
|
||||
</span>
|
||||
<span v-else class="text-gray-500">暂无打卡记录</span>
|
||||
<span v-else class="text-on-surface-variant">暂无打卡记录</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Task Actions -->
|
||||
<div class="flex gap-2 pt-4 border-t border-gray-100">
|
||||
<div class="flex gap-2 pt-4 border-t border-outline-variant">
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@@ -174,7 +175,7 @@
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
@@ -192,12 +193,12 @@
|
||||
<div v-if="!editingTask">
|
||||
<div v-if="loadingTemplates" class="text-center py-8">
|
||||
<a-spin size="large" />
|
||||
<p class="text-gray-500 mt-2">加载模板中...</p>
|
||||
<p class="text-on-surface-variant mt-2">加载模板中...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeTemplates.length === 0" class="text-center py-8">
|
||||
<p class="text-gray-500">暂无可用模板</p>
|
||||
<p class="text-sm text-gray-400 mt-2">请联系管理员创建模板</p>
|
||||
<p class="text-on-surface-variant">暂无可用模板</p>
|
||||
<p class="text-sm text-on-surface-variant opacity-70 mt-2">请联系管理员创建模板</p>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
@@ -208,10 +209,10 @@
|
||||
v-for="template in activeTemplates"
|
||||
:key="template.id"
|
||||
@click="selectTemplate(template)"
|
||||
class="border rounded-lg p-4 cursor-pointer hover:border-primary-500 hover:bg-primary-50 transition-all"
|
||||
class="border border-outline-variant rounded-lg p-4 cursor-pointer hover:border-primary hover:bg-primary-container/10 transition-all"
|
||||
>
|
||||
<h4 class="font-semibold text-gray-800 mb-1">{{ template.name }}</h4>
|
||||
<p class="text-sm text-gray-600">{{ template.description || '无描述' }}</p>
|
||||
<h4 class="font-semibold text-on-surface mb-1">{{ template.name }}</h4>
|
||||
<p class="text-sm text-on-surface-variant">{{ template.description || '无描述' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
@@ -281,7 +282,7 @@
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<span v-if="fieldConfig.default_value" class="text-xs text-gray-500 mt-1">
|
||||
<span v-if="fieldConfig.default_value" class="text-xs text-on-surface-variant mt-1">
|
||||
默认值: {{ fieldConfig.default_value }}
|
||||
</span>
|
||||
</a-form-item>
|
||||
@@ -298,7 +299,7 @@
|
||||
|
||||
<a-form-item label="启用状态">
|
||||
<a-switch v-model:checked="taskForm.is_active" />
|
||||
<span class="ml-2 text-sm text-gray-500">
|
||||
<span class="ml-2 text-sm text-on-surface-variant">
|
||||
{{ taskForm.is_active ? '启用自动打卡' : '禁用自动打卡(仍可手动打卡)' }}
|
||||
</span>
|
||||
</a-form-item>
|
||||
@@ -312,7 +313,7 @@
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-sm text-gray-600">完整的打卡请求配置</span>
|
||||
<span class="text-sm text-on-surface-variant">完整的打卡请求配置</span>
|
||||
<a-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@@ -330,7 +331,7 @@
|
||||
class="font-mono text-xs"
|
||||
style="resize: vertical; min-height: 200px; max-height: 400px;"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
<p class="text-xs text-on-surface-variant mt-1">
|
||||
💡 此配置由模板自动生成,如需修改请删除任务后从模板重新创建
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gradient mb-2">任务模板管理</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400">JSON 映射架构 - 配置即结构,字段名保持原样</p>
|
||||
<p class="text-on-surface-variant">JSON 映射架构 - 配置即结构</p>
|
||||
</div>
|
||||
<button @click="showCreateDialog" class="md3-button-filled">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -20,37 +20,38 @@
|
||||
|
||||
<!-- Templates List -->
|
||||
<div v-if="loading && templates.length === 0" class="space-y-4">
|
||||
<div v-for="i in 3" :key="i" class="fluent-card p-6">
|
||||
<a-card v-for="i in 3" :key="i" class="md3-card">
|
||||
<a-skeleton :active="true" :paragraph="{ rows: 2 }" />
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<div v-else-if="templates.length === 0" class="fluent-card p-12 text-center">
|
||||
<svg class="w-20 h-20 mx-auto text-gray-300 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<a-card v-else-if="templates.length === 0" class="md3-card text-center" style="padding: 48px 20px;">
|
||||
<svg class="w-20 h-20 mx-auto text-on-surface-variant opacity-30 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<h3 class="text-xl font-semibold text-gray-700 dark:text-gray-200 mb-2">暂无模板</h3>
|
||||
<p class="text-gray-500 dark:text-gray-400 mb-4">创建第一个模板,让用户更轻松地创建打卡任务</p>
|
||||
<h3 class="text-xl font-semibold text-on-surface mb-2">暂无模板</h3>
|
||||
<p class="text-on-surface-variant mb-4">创建第一个模板,让用户更轻松地创建打卡任务</p>
|
||||
<button @click="showCreateDialog" class="md3-button-filled">新建模板</button>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div
|
||||
<a-card
|
||||
v-for="template in templates"
|
||||
:key="template.id"
|
||||
class="fluent-card p-7 hover:shadow-xl transition-all animate-slide-up"
|
||||
class="md3-card hover:shadow-xl transition-all animate-slide-up"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-5">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2">{{ template.name }}</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-3">{{ template.description || '无描述' }}</p>
|
||||
<span :class="template.is_active ? 'status-success' : 'status-info'">
|
||||
<h3 class="text-lg font-semibold text-on-surface mb-2">{{ template.name }}</h3>
|
||||
<a-divider style="margin: 8px 0;" />
|
||||
<p class="text-sm text-on-surface-variant mb-2">{{ template.description || '无描述' }}</p>
|
||||
<span :class="template.is_active ? 'md3-badge-success' : 'md3-badge-info'">
|
||||
{{ template.is_active ? '已启用' : '已禁用' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5 pt-4 border-t border-gray-100 space-y-2">
|
||||
<div class="mt-3 pt-3 border-t border-outline-variant space-y-2">
|
||||
<!-- 第一行:预览在左半部分居中,编辑在右半部分居中 -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="flex justify-center">
|
||||
@@ -77,7 +78,7 @@
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div></div>
|
||||
<div class="flex justify-center">
|
||||
<button @click="deleteTemplate(template)" class="md3-button-text text-sm !text-red-600 dark:!text-red-500 flex-shrink-0">
|
||||
<button @click="deleteTemplate(template)" class="md3-button-outlined text-sm !text-red-600 dark:!text-red-500 !border-red-600 dark:!border-red-500 hover:!bg-red-50 dark:hover:!bg-red-900/20 flex-shrink-0">
|
||||
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
@@ -86,7 +87,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
<!-- Create/Edit Dialog -->
|
||||
@@ -156,7 +157,7 @@
|
||||
<!-- 字段配置编辑器 -->
|
||||
<div class="field-config-editor">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-bold text-gray-800 dark:text-gray-100">字段配置</h3>
|
||||
<h3 class="text-lg font-bold text-on-surface">字段配置</h3>
|
||||
<a-dropdown>
|
||||
<a-button type="primary">
|
||||
添加字段
|
||||
@@ -188,12 +189,12 @@
|
||||
</div>
|
||||
|
||||
<!-- 递归渲染字段树 -->
|
||||
<div v-if="Object.keys(formData.field_config).length === 0" class="text-center py-12 border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800/50">
|
||||
<svg class="w-16 h-16 mx-auto text-gray-400 dark:text-gray-500 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<div v-if="Object.keys(formData.field_config).length === 0" class="text-center py-12 border-2 border-dashed border-outline-variant rounded-lg bg-surface-container">
|
||||
<svg class="w-16 h-16 mx-auto text-on-surface-variant opacity-40 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-2">暂无字段配置</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">点击上方"添加字段"开始配置模板</p>
|
||||
<h3 class="text-lg font-semibold text-on-surface mb-2">暂无字段配置</h3>
|
||||
<p class="text-sm text-on-surface-variant">点击上方"添加字段"开始配置模板</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-3">
|
||||
@@ -215,7 +216,7 @@
|
||||
<span class="text-lg font-bold">JSON 预览</span>
|
||||
</a-divider>
|
||||
|
||||
<div class="bg-gray-900 dark:bg-black text-green-400 p-4 rounded-lg font-mono text-sm overflow-auto max-h-96">
|
||||
<div class="bg-surface-container text-green-400 p-4 rounded-lg font-mono text-sm overflow-auto max-h-96">
|
||||
<pre>{{ JSON.stringify(formData.field_config, null, 2) }}</pre>
|
||||
</div>
|
||||
</a-form>
|
||||
@@ -242,7 +243,7 @@
|
||||
placeholder="例如: Id, Group1, DateTarget"
|
||||
@keyup.enter="confirmAddField"
|
||||
/>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 mt-1 block">
|
||||
<span class="text-xs text-on-surface-variant mt-1 block">
|
||||
💡 字段名将保持原样,不会进行大小写转换
|
||||
</span>
|
||||
</a-form-item>
|
||||
@@ -262,14 +263,14 @@
|
||||
:style="isMobile ? { top: 0, maxWidth: '100vw' } : {}"
|
||||
>
|
||||
<div v-if="previewData" class="space-y-4">
|
||||
<div class="bg-gray-50 dark:bg-gray-800 rounded p-4">
|
||||
<h4 class="font-semibold mb-2 text-gray-800 dark:text-gray-100">生成的 Payload(使用默认值):</h4>
|
||||
<pre class="text-xs bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200 p-3 rounded border dark:border-gray-700 overflow-auto max-h-96">{{ JSON.stringify(previewData.preview_payload, null, 2) }}</pre>
|
||||
<div class="bg-surface-container rounded p-4">
|
||||
<h4 class="font-semibold mb-2 text-on-surface">生成的 Payload(使用默认值):</h4>
|
||||
<pre class="text-xs bg-surface text-on-surface p-3 rounded border border-outline-variant overflow-auto max-h-96">{{ JSON.stringify(previewData.preview_payload, null, 2) }}</pre>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 dark:bg-gray-800 rounded p-4">
|
||||
<h4 class="font-semibold mb-2 text-gray-800 dark:text-gray-100">字段配置:</h4>
|
||||
<pre class="text-xs bg-white dark:bg-gray-900 text-gray-800 dark:text-gray-200 p-3 rounded border dark:border-gray-700 overflow-auto max-h-96">{{ JSON.stringify(previewData.field_config, null, 2) }}</pre>
|
||||
<div class="bg-surface-container rounded p-4">
|
||||
<h4 class="font-semibold mb-2 text-on-surface">字段配置:</h4>
|
||||
<pre class="text-xs bg-surface text-on-surface p-3 rounded border border-outline-variant overflow-auto max-h-96">{{ JSON.stringify(previewData.field_config, null, 2) }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user