style: use united style

This commit is contained in:
2026-01-03 16:09:22 +08:00
parent 955b09436e
commit c98aa73364
20 changed files with 2099 additions and 1319 deletions
+43 -8
View File
@@ -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>
+10 -10
View File
@@ -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>
+46 -46
View File
@@ -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 -->
+45 -44
View File
@@ -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>
+31 -30
View File
@@ -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>