Merge branch 'pr-140'

This commit is contained in:
fawney19
2026-02-04 12:17:46 +08:00
5 changed files with 600 additions and 23 deletions

View File

@@ -490,12 +490,13 @@
<template #actions>
<Button
size="sm"
:disabled="checkinTimeLoading || !hasCheckinTimeChanged"
@click="handleCheckinTimeSave"
:disabled="schedulerConfigLoading || !hasSchedulerConfigChanges"
@click="handleSchedulerConfigSave"
>
{{ checkinTimeLoading ? '保存中...' : '保存' }}
{{ schedulerConfigLoading ? '保存中...' : '保存' }}
</Button>
</template>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="flex items-center space-x-2">
<Switch
@@ -564,6 +565,100 @@
</p>
</div>
</div>
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="flex items-center space-x-2">
<Switch
id="enable-user-quota-reset"
:model-value="systemConfig.enable_user_quota_reset"
@update:model-value="handleUserQuotaResetToggle"
/>
<div>
<Label
for="enable-user-quota-reset"
class="cursor-pointer"
>
启用用户配额自动重置
</Label>
<p class="text-xs text-muted-foreground">
每天定时触发,按周期执行
</p>
</div>
</div>
<div
v-if="systemConfig.enable_user_quota_reset"
class="grid grid-cols-1 md:grid-cols-2 gap-6"
>
<div>
<Label class="block text-sm font-medium">
执行时间
</Label>
<div class="mt-1 flex items-center gap-2">
<Select
v-model:open="userQuotaResetHourSelectOpen"
:model-value="userQuotaResetHour"
@update:model-value="(val: string) => updateUserQuotaResetTime(val, userQuotaResetMinute)"
>
<SelectTrigger class="w-20">
<SelectValue placeholder="" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="h in 24"
:key="h - 1"
:value="String(h - 1).padStart(2, '0')"
>
{{ String(h - 1).padStart(2, '0') }}
</SelectItem>
</SelectContent>
</Select>
<span class="text-muted-foreground">:</span>
<Select
v-model:open="userQuotaResetMinuteSelectOpen"
:model-value="userQuotaResetMinute"
@update:model-value="(val: string) => updateUserQuotaResetTime(userQuotaResetHour, val)"
>
<SelectTrigger class="w-20">
<SelectValue placeholder="" />
</SelectTrigger>
<SelectContent>
<SelectItem
v-for="m in 60"
:key="m - 1"
:value="String(m - 1).padStart(2, '0')"
>
{{ String(m - 1).padStart(2, '0') }}
</SelectItem>
</SelectContent>
</Select>
</div>
<p class="mt-1 text-xs text-muted-foreground">
每天定时执行24小时制
</p>
</div>
<div>
<Label class="block text-sm font-medium">
重置周期
</Label>
<div class="mt-1 flex items-center gap-2 flex-wrap">
<span class="text-xs text-muted-foreground">每</span>
<Input
v-model.number="systemConfig.user_quota_reset_interval_days"
type="number"
min="1"
step="1"
class="w-24"
/>
<span class="text-xs text-muted-foreground">天</span>
</div>
<p class="mt-1 text-xs text-muted-foreground">
滚动计算:距离上次成功执行满 N 天后才会再次执行
</p>
</div>
</div>
</div>
</CardSection>
<!-- 系统版本信息 -->
@@ -982,6 +1077,9 @@ interface SystemConfig {
// 定时任务
enable_provider_checkin: boolean
provider_checkin_time: string
enable_user_quota_reset: boolean
user_quota_reset_time: string
user_quota_reset_interval_days: number
}
const basicConfigLoading = ref(false)
@@ -1039,6 +1137,9 @@ const systemConfig = ref<SystemConfig>({
// 定时任务
enable_provider_checkin: true,
provider_checkin_time: '01:05',
enable_user_quota_reset: false,
user_quota_reset_time: '05:00',
user_quota_reset_interval_days: 1,
})
// 原始配置值(用于检测变动)
@@ -1147,6 +1248,9 @@ async function loadSystemConfig() {
// 定时任务
'enable_provider_checkin',
'provider_checkin_time',
'enable_user_quota_reset',
'user_quota_reset_time',
'user_quota_reset_interval_days',
]
for (const key of configs) {
@@ -1163,6 +1267,9 @@ async function loadSystemConfig() {
originalConfig.value = JSON.parse(JSON.stringify(systemConfig.value))
// 初始化签到时间的原始值(用于回滚)
previousCheckinTime.value = systemConfig.value.provider_checkin_time
// 初始化配额重置时间的原始值(用于回滚)
previousUserQuotaResetTime.value = systemConfig.value.user_quota_reset_time
previousUserQuotaResetIntervalDays.value = systemConfig.value.user_quota_reset_interval_days
} catch (err) {
error('加载系统配置失败')
log.error('加载系统配置失败:', err)
@@ -1305,9 +1412,8 @@ async function handleProviderCheckinToggle(enabled: boolean) {
}
}
// 签到时间相关
const previousCheckinTime = ref('')
const checkinTimeLoading = ref(false)
const schedulerConfigLoading = ref(false)
const checkinHourSelectOpen = ref(false)
const checkinMinuteSelectOpen = ref(false)
@@ -1334,29 +1440,138 @@ const hasCheckinTimeChanged = computed(() => {
return systemConfig.value.provider_checkin_time !== previousCheckinTime.value
})
async function handleCheckinTimeSave() {
const newTime = systemConfig.value.provider_checkin_time
// 验证时间格式
if (!newTime || !/^\d{2}:\d{2}$/.test(newTime)) {
error('请输入有效的时间格式 (HH:MM)')
return
}
checkinTimeLoading.value = true
async function handleUserQuotaResetToggle(enabled: boolean) {
const previousValue = systemConfig.value.enable_user_quota_reset
systemConfig.value.enable_user_quota_reset = enabled
try {
await adminApi.updateSystemConfig(
'provider_checkin_time',
newTime,
'Provider 自动签到执行时间HH:MM 格式)'
'enable_user_quota_reset',
enabled,
'是否启用用户配额自动重置任务'
)
previousCheckinTime.value = newTime
success(`签到时间已设置为 ${newTime}`)
success(enabled ? '已启用用户配额自动重置' : '已禁用用户配额自动重置')
} catch (err) {
error('保存签到时间失败')
log.error('保存签到时间失败:', err)
error('保存配置失败')
log.error('保存用户配额自动重置配置失败:', err)
// 回滚状态
systemConfig.value.enable_user_quota_reset = previousValue
}
}
// 用户配额重置时间相关
const previousUserQuotaResetTime = ref('')
const userQuotaResetHourSelectOpen = ref(false)
const userQuotaResetMinuteSelectOpen = ref(false)
const previousUserQuotaResetIntervalDays = ref(1)
const userQuotaResetHour = computed(() => {
const time = systemConfig.value.user_quota_reset_time
if (!time || !time.includes(':')) return '05'
return time.split(':')[0]
})
const userQuotaResetMinute = computed(() => {
const time = systemConfig.value.user_quota_reset_time
if (!time || !time.includes(':')) return '00'
return time.split(':')[1]
})
function updateUserQuotaResetTime(hour: string, minute: string) {
systemConfig.value.user_quota_reset_time = `${hour}:${minute}`
}
const hasUserQuotaResetTimeChanged = computed(() => {
return systemConfig.value.user_quota_reset_time !== previousUserQuotaResetTime.value
})
const hasUserQuotaResetIntervalChanged = computed(() => {
return systemConfig.value.user_quota_reset_interval_days !== previousUserQuotaResetIntervalDays.value
})
const hasSchedulerConfigChanges = computed(() => {
return (
hasCheckinTimeChanged.value ||
hasUserQuotaResetTimeChanged.value ||
hasUserQuotaResetIntervalChanged.value
)
})
async function handleSchedulerConfigSave() {
const configItems: Array<{ key: string, value: any, description: string, onSuccess: () => void }> = []
if (hasCheckinTimeChanged.value) {
const newTime = systemConfig.value.provider_checkin_time
if (!newTime || !/^\d{2}:\d{2}$/.test(newTime)) {
error('请输入有效的时间格式 (HH:MM)')
return
}
configItems.push({
key: 'provider_checkin_time',
value: newTime,
description: 'Provider 自动签到执行时间HH:MM 格式)',
onSuccess: () => {
previousCheckinTime.value = newTime
}
})
}
if (hasUserQuotaResetTimeChanged.value) {
const newTime = systemConfig.value.user_quota_reset_time
if (!newTime || !/^\d{2}:\d{2}$/.test(newTime)) {
error('请输入有效的时间格式 (HH:MM)')
return
}
configItems.push({
key: 'user_quota_reset_time',
value: newTime,
description: '用户配额自动重置执行时间HH:MM 格式)',
onSuccess: () => {
previousUserQuotaResetTime.value = newTime
}
})
}
if (hasUserQuotaResetIntervalChanged.value) {
let intervalDays = Number(systemConfig.value.user_quota_reset_interval_days)
if (!Number.isFinite(intervalDays) || intervalDays < 1) intervalDays = 1
intervalDays = Math.trunc(intervalDays)
systemConfig.value.user_quota_reset_interval_days = intervalDays
configItems.push({
key: 'user_quota_reset_interval_days',
value: intervalDays,
description: '用户配额重置周期(天数),滚动计算',
onSuccess: () => {
previousUserQuotaResetIntervalDays.value = intervalDays
}
})
}
if (configItems.length === 0) return
schedulerConfigLoading.value = true
const failedKeys: string[] = []
try {
for (const item of configItems) {
try {
await adminApi.updateSystemConfig(item.key, item.value, item.description)
item.onSuccess()
} catch (err) {
failedKeys.push(item.key)
log.error(`保存定时任务配置失败: ${item.key}`, err)
}
}
if (failedKeys.length > 0) {
error(`部分配置保存失败: ${failedKeys.join(', ')}`)
return
}
success('定时任务配置已保存')
} finally {
checkinTimeLoading.value = false
schedulerConfigLoading.value = false
}
}