mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
chore: format code style and update linting configuration
This commit is contained in:
@@ -1,3 +1,425 @@
|
||||
<script lang="ts" setup>
|
||||
import type { DataTableColumns } from 'naive-ui'
|
||||
import type { CheckInRankingInfo, CheckInResult } from '@/api/api-models'
|
||||
import { Info24Filled } from '@vicons/fluent'
|
||||
import { NAlert, NButton, NCard, NDataTable, NDivider, NForm, NFormItem, NIcon, NInput, NInputGroup, NInputNumber, NPopconfirm, NSelect, NSpace, NSpin, NSwitch, NTabPane, NTabs, NText, NTime, NTooltip } from 'naive-ui'
|
||||
import { computed, h, onMounted, ref } from 'vue'
|
||||
import { SaveEnableFunctions, SaveSetting, useAccount } from '@/api/account'
|
||||
import { FunctionTypes } from '@/api/api-models'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import { useAutoAction } from '@/client/store/useAutoAction'
|
||||
import { CHECKIN_API_URL } from '@/data/constants'
|
||||
import AutoActionEditor from '../AutoActionEditor.vue'
|
||||
import TemplateHelper from '../TemplateHelper.vue'
|
||||
|
||||
interface LiveInfo {
|
||||
roomId?: number
|
||||
}
|
||||
|
||||
const autoActionStore = useAutoAction()
|
||||
const config = autoActionStore.checkInModule.checkInConfig
|
||||
const accountInfo = useAccount()
|
||||
const isLoading = ref(false)
|
||||
|
||||
const customTestContext = ref({
|
||||
checkin: {
|
||||
points: 0,
|
||||
consecutiveDays: 0,
|
||||
todayRank: 0,
|
||||
time: new Date(),
|
||||
},
|
||||
})
|
||||
|
||||
// 签到模板的特定占位符
|
||||
const checkInPlaceholders = [
|
||||
{ name: '{{checkin.points}}', description: '获得的总积分' },
|
||||
{ name: '{{checkin.consecutiveDays}}', description: '连续签到天数' },
|
||||
{ name: '{{checkin.todayRank}}', description: '今日签到排名' },
|
||||
{ name: '{{checkin.time}}', description: '签到时间对象' },
|
||||
]
|
||||
|
||||
// 服务端签到设置
|
||||
const serverSetting = computed(() => {
|
||||
return accountInfo.value?.settings?.point || {}
|
||||
})
|
||||
|
||||
// 是否可以编辑设置
|
||||
const canEdit = computed(() => {
|
||||
return accountInfo.value && accountInfo.value.settings && accountInfo.value.settings.point
|
||||
})
|
||||
|
||||
// 更新所有设置
|
||||
async function updateSettings() {
|
||||
// 先保存服务端设置
|
||||
const serverSaved = await updateServerSettings()
|
||||
|
||||
if (serverSaved) {
|
||||
window.$notification.success({
|
||||
title: '设置已保存',
|
||||
duration: 3000,
|
||||
})
|
||||
}
|
||||
|
||||
return serverSaved
|
||||
}
|
||||
|
||||
// 更新服务端签到设置
|
||||
async function updateServerSettings() {
|
||||
if (!canEdit.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const msg = await SaveSetting('Point', accountInfo.value.settings.point)
|
||||
if (msg) {
|
||||
return true
|
||||
} else {
|
||||
window.$notification.error({
|
||||
title: '保存失败',
|
||||
content: msg,
|
||||
duration: 5000,
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
window.$notification.error({
|
||||
title: '保存失败',
|
||||
content: String(err),
|
||||
duration: 5000,
|
||||
})
|
||||
console.error('保存签到设置失败:', err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 排行榜数据
|
||||
const rankingData = ref<CheckInRankingInfo[]>([])
|
||||
const isLoadingRanking = ref(false)
|
||||
const timeRange = ref<string>('all')
|
||||
const userFilter = ref<string>('')
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
|
||||
// 时间段选项
|
||||
const timeRangeOptions = [
|
||||
{ label: '全部时间', value: 'all' },
|
||||
{ label: '今日', value: 'today' },
|
||||
{ label: '本周', value: 'week' },
|
||||
{ label: '本月', value: 'month' },
|
||||
{ label: '上个月', value: 'lastMonth' },
|
||||
]
|
||||
|
||||
// 过滤后的排行榜数据
|
||||
const filteredRankingData = computed(() => {
|
||||
let filtered = rankingData.value
|
||||
|
||||
// 按时间范围筛选
|
||||
if (timeRange.value !== 'all') {
|
||||
const now = new Date()
|
||||
let startTime: Date
|
||||
|
||||
if (timeRange.value === 'today') {
|
||||
// 今天凌晨
|
||||
startTime = new Date(now)
|
||||
startTime.setHours(0, 0, 0, 0)
|
||||
} else if (timeRange.value === 'week') {
|
||||
// 本周一
|
||||
const dayOfWeek = now.getDay() || 7 // 把周日作为7处理
|
||||
startTime = new Date(now)
|
||||
startTime.setDate(now.getDate() - (dayOfWeek - 1))
|
||||
startTime.setHours(0, 0, 0, 0)
|
||||
} else if (timeRange.value === 'month') {
|
||||
// 本月1号
|
||||
startTime = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
} else if (timeRange.value === 'lastMonth') {
|
||||
// 上月1号
|
||||
startTime = new Date(now.getFullYear(), now.getMonth() - 1, 1)
|
||||
// 本月1号作为结束时间
|
||||
const endTime = new Date(now.getFullYear(), now.getMonth(), 1)
|
||||
filtered = filtered.filter((user) => {
|
||||
const checkInTime = new Date(user.lastCheckInTime)
|
||||
return checkInTime >= startTime && checkInTime < endTime
|
||||
})
|
||||
// 已经筛选完成,不需要再次筛选
|
||||
startTime = new Date(0)
|
||||
}
|
||||
|
||||
// 如果不是上个月,用通用筛选逻辑
|
||||
if (timeRange.value !== 'lastMonth') {
|
||||
filtered = filtered.filter((user) => {
|
||||
const checkInTime = new Date(user.lastCheckInTime)
|
||||
return checkInTime >= startTime
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 按用户名筛选
|
||||
if (userFilter.value) {
|
||||
const keyword = userFilter.value.toLowerCase()
|
||||
filtered = filtered.filter(user =>
|
||||
user.name.toLowerCase().includes(keyword),
|
||||
)
|
||||
}
|
||||
|
||||
return filtered
|
||||
})
|
||||
|
||||
// 排行榜列定义
|
||||
const rankingColumns: DataTableColumns<CheckInRankingInfo> = [
|
||||
{
|
||||
title: '排名',
|
||||
key: 'rank',
|
||||
render: (row: CheckInRankingInfo, index: number) => h('span', {}, index + 1),
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '连续签到天数',
|
||||
key: 'consecutiveDays',
|
||||
sorter: 'default',
|
||||
},
|
||||
{
|
||||
title: '积分',
|
||||
key: 'points',
|
||||
sorter: 'default',
|
||||
},
|
||||
{
|
||||
title: '最近签到时间',
|
||||
key: 'lastCheckInTime',
|
||||
render(row: CheckInRankingInfo) {
|
||||
return h(NTooltip, {
|
||||
}, {
|
||||
trigger: () => h(NTime, {
|
||||
time: row.lastCheckInTime,
|
||||
type: 'relative',
|
||||
}),
|
||||
default: () => new Date(row.lastCheckInTime).toLocaleString(),
|
||||
})
|
||||
},
|
||||
sorter: 'default',
|
||||
},
|
||||
{
|
||||
title: '已认证',
|
||||
key: 'isAuthed',
|
||||
render(row: CheckInRankingInfo) {
|
||||
return h('span', {}, row.isAuthed ? '是' : '否')
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
render(row: CheckInRankingInfo) {
|
||||
return h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => resetUserCheckInByGuid(row.ouId),
|
||||
},
|
||||
{
|
||||
trigger: () => h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
disabled: isResetting.value,
|
||||
loading: isResetting.value && resetTargetId.value === row.ouId,
|
||||
onClick: e => e.stopPropagation(),
|
||||
},
|
||||
{ default: () => '重置签到' },
|
||||
),
|
||||
default: () => '确定要重置该用户的所有签到数据吗?此操作不可撤销。',
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 加载签到排行榜数据
|
||||
async function loadCheckInRanking() {
|
||||
if (isLoadingRanking.value) return
|
||||
|
||||
isLoadingRanking.value = true
|
||||
try {
|
||||
// 获取所有用户数据,不再根据时间范围过滤
|
||||
const response = await QueryGetAPI<CheckInRankingInfo[]>(`${CHECKIN_API_URL}admin/users`)
|
||||
|
||||
if (response.code == 200) {
|
||||
rankingData.value = response.data
|
||||
pagination.value.page = 1 // 重置为第一页
|
||||
} else {
|
||||
rankingData.value = []
|
||||
window.$message.error(`获取签到排行榜失败: ${response.message}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载签到排行榜失败:', error)
|
||||
window.$notification.error({
|
||||
title: '加载失败',
|
||||
content: '无法加载签到排行榜数据',
|
||||
duration: 5000,
|
||||
})
|
||||
rankingData.value = []
|
||||
} finally {
|
||||
isLoadingRanking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置签到数据相关
|
||||
const isResetting = ref(false)
|
||||
const resetTargetId = ref<string>()
|
||||
|
||||
// 重置单个用户签到数据
|
||||
async function resetUserCheckInByGuid(ouId: string) {
|
||||
if (!ouId || isResetting.value) return
|
||||
|
||||
isResetting.value = true
|
||||
resetTargetId.value = ouId
|
||||
|
||||
try {
|
||||
const response = await QueryGetAPI(`${CHECKIN_API_URL}admin/reset`, {
|
||||
ouId,
|
||||
})
|
||||
|
||||
if (response && response.code === 200) {
|
||||
window.$notification.success({
|
||||
title: '重置成功',
|
||||
content: '用户签到数据已重置',
|
||||
duration: 3000,
|
||||
})
|
||||
|
||||
// 重置成功后重新加载排行榜
|
||||
await loadCheckInRanking()
|
||||
} else {
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: response?.message || '无法重置用户签到数据',
|
||||
duration: 5000,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重置用户签到数据失败:', error)
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: '重置用户签到数据时发生错误',
|
||||
duration: 5000,
|
||||
})
|
||||
} finally {
|
||||
isResetting.value = false
|
||||
resetTargetId.value = undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 重置所有用户签到数据
|
||||
async function resetAllCheckIn() {
|
||||
if (isResetting.value) return
|
||||
|
||||
isResetting.value = true
|
||||
try {
|
||||
const response = await QueryGetAPI(`${CHECKIN_API_URL}admin/reset`, {})
|
||||
|
||||
if (response && response.code === 200) {
|
||||
window.$notification.success({
|
||||
title: '重置成功',
|
||||
content: '所有用户的签到数据已重置',
|
||||
duration: 3000,
|
||||
})
|
||||
|
||||
// 重置成功后重新加载排行榜
|
||||
await loadCheckInRanking()
|
||||
} else {
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: response?.message || '无法重置所有用户签到数据',
|
||||
duration: 5000,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重置所有用户签到数据失败:', error)
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: '重置所有用户签到数据时发生错误',
|
||||
duration: 5000,
|
||||
})
|
||||
} finally {
|
||||
isResetting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 测试签到功能
|
||||
const testUid = ref<number>()
|
||||
const testUsername = ref<string>('测试用户')
|
||||
const testResult = ref<{ success: boolean, message: string }>()
|
||||
|
||||
// 处理测试签到
|
||||
async function handleTestCheckIn() {
|
||||
if (!testUid.value || !serverSetting.value.enableCheckIn) {
|
||||
testResult.value = {
|
||||
success: false,
|
||||
message: '请输入有效的UID或确保签到功能已启用',
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 直接调用服务端签到API
|
||||
const response = await QueryGetAPI<CheckInResult>(`${CHECKIN_API_URL}check-in-for`, {
|
||||
uId: testUid.value,
|
||||
name: testUsername.value || '测试用户',
|
||||
})
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const result = response.data
|
||||
|
||||
testResult.value = {
|
||||
success: result.success,
|
||||
message: result.success
|
||||
? `签到成功!用户 ${testUsername.value || '测试用户'} 获得 ${result.points} 积分,连续签到 ${result.consecutiveDays} 天`
|
||||
: result.message || '签到失败,可能今天已经签到过了',
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
window.$notification[result.success ? 'success' : 'info']({
|
||||
title: result.success ? '测试签到成功' : '测试签到失败',
|
||||
content: testResult.value.message,
|
||||
duration: 3000,
|
||||
})
|
||||
} else {
|
||||
testResult.value = {
|
||||
success: false,
|
||||
message: `API返回错误: ${response.message || '未知错误'}`,
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
testResult.value = {
|
||||
success: false,
|
||||
message: `签到操作失败: ${error instanceof Error ? error.message : String(error)}`,
|
||||
}
|
||||
|
||||
// 显示错误通知
|
||||
window.$notification.error({
|
||||
title: '测试签到失败',
|
||||
content: testResult.value.message,
|
||||
duration: 5000,
|
||||
})
|
||||
}
|
||||
}
|
||||
function updateCheckInRanking(value: boolean) {
|
||||
accountInfo.value.settings.enableFunctions = value ? [...accountInfo.value.settings.enableFunctions, FunctionTypes.CheckInRanking] : accountInfo.value.settings.enableFunctions.filter(f => f !== FunctionTypes.CheckInRanking)
|
||||
SaveEnableFunctions(accountInfo.value.settings.enableFunctions)
|
||||
}
|
||||
|
||||
// 组件挂载时加载排行榜
|
||||
onMounted(() => {
|
||||
loadCheckInRanking()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard
|
||||
v-if="config"
|
||||
@@ -24,7 +446,7 @@
|
||||
label-placement="left"
|
||||
:label-width="120"
|
||||
:style="{
|
||||
maxWidth: '650px'
|
||||
maxWidth: '650px',
|
||||
}"
|
||||
>
|
||||
<!-- 服务端签到设置 -->
|
||||
@@ -272,7 +694,7 @@
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
onChange: (page: number) => pagination.page = page,
|
||||
onUpdatePageSize: (pageSize: number) => pagination.pageSize = pageSize
|
||||
onUpdatePageSize: (pageSize: number) => pagination.pageSize = pageSize,
|
||||
}"
|
||||
:bordered="false"
|
||||
:loading="isLoadingRanking"
|
||||
@@ -380,428 +802,6 @@
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { SaveEnableFunctions, SaveSetting, useAccount } from '@/api/account';
|
||||
import { CheckInRankingInfo, CheckInResult, FunctionTypes } from '@/api/api-models';
|
||||
import { QueryGetAPI } from '@/api/query';
|
||||
import { useAutoAction } from '@/client/store/useAutoAction';
|
||||
import { CHECKIN_API_URL } from '@/data/constants';
|
||||
import { GuidUtils } from '@/Utils';
|
||||
import { Info24Filled } from '@vicons/fluent';
|
||||
import type { DataTableColumns } from 'naive-ui';
|
||||
import { NAlert, NButton, NCard, NDataTable, NDivider, NEmpty, NForm, NFormItem, NIcon, NInput, NInputGroup, NInputNumber, NPopconfirm, NSelect, NSpace, NSpin, NSwitch, NTabPane, NTabs, NText, NTime, NTooltip } from 'naive-ui';
|
||||
import { computed, h, onMounted, ref, watch } from 'vue';
|
||||
import AutoActionEditor from '../AutoActionEditor.vue';
|
||||
import TemplateHelper from '../TemplateHelper.vue';
|
||||
|
||||
interface LiveInfo {
|
||||
roomId?: number;
|
||||
}
|
||||
|
||||
const autoActionStore = useAutoAction();
|
||||
const config = autoActionStore.checkInModule.checkInConfig;
|
||||
const accountInfo = useAccount();
|
||||
const isLoading = ref(false);
|
||||
|
||||
const customTestContext = ref({
|
||||
checkin: {
|
||||
points: 0,
|
||||
consecutiveDays: 0,
|
||||
todayRank: 0,
|
||||
time: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
// 签到模板的特定占位符
|
||||
const checkInPlaceholders = [
|
||||
{ name: '{{checkin.points}}', description: '获得的总积分' },
|
||||
{ name: '{{checkin.consecutiveDays}}', description: '连续签到天数' },
|
||||
{ name: '{{checkin.todayRank}}', description: '今日签到排名' },
|
||||
{ name: '{{checkin.time}}', description: '签到时间对象' }
|
||||
];
|
||||
|
||||
// 服务端签到设置
|
||||
const serverSetting = computed(() => {
|
||||
return accountInfo.value?.settings?.point || {};
|
||||
});
|
||||
|
||||
// 是否可以编辑设置
|
||||
const canEdit = computed(() => {
|
||||
return accountInfo.value && accountInfo.value.settings && accountInfo.value.settings.point;
|
||||
});
|
||||
|
||||
// 更新所有设置
|
||||
async function updateSettings() {
|
||||
// 先保存服务端设置
|
||||
const serverSaved = await updateServerSettings();
|
||||
|
||||
if (serverSaved) {
|
||||
window.$notification.success({
|
||||
title: '设置已保存',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
|
||||
return serverSaved;
|
||||
}
|
||||
|
||||
// 更新服务端签到设置
|
||||
async function updateServerSettings() {
|
||||
if (!canEdit.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
const msg = await SaveSetting('Point', accountInfo.value.settings.point);
|
||||
if (msg) {
|
||||
return true;
|
||||
} else {
|
||||
window.$notification.error({
|
||||
title: '保存失败',
|
||||
content: msg,
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
window.$notification.error({
|
||||
title: '保存失败',
|
||||
content: String(err),
|
||||
duration: 5000
|
||||
});
|
||||
console.error('保存签到设置失败:', err);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 排行榜数据
|
||||
const rankingData = ref<CheckInRankingInfo[]>([]);
|
||||
const isLoadingRanking = ref(false);
|
||||
const timeRange = ref<string>('all');
|
||||
const userFilter = ref<string>('');
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
});
|
||||
|
||||
// 时间段选项
|
||||
const timeRangeOptions = [
|
||||
{ label: '全部时间', value: 'all' },
|
||||
{ label: '今日', value: 'today' },
|
||||
{ label: '本周', value: 'week' },
|
||||
{ label: '本月', value: 'month' },
|
||||
{ label: '上个月', value: 'lastMonth' },
|
||||
];
|
||||
|
||||
// 过滤后的排行榜数据
|
||||
const filteredRankingData = computed(() => {
|
||||
let filtered = rankingData.value;
|
||||
|
||||
// 按时间范围筛选
|
||||
if (timeRange.value !== 'all') {
|
||||
const now = new Date();
|
||||
let startTime: Date;
|
||||
|
||||
if (timeRange.value === 'today') {
|
||||
// 今天凌晨
|
||||
startTime = new Date(now);
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
} else if (timeRange.value === 'week') {
|
||||
// 本周一
|
||||
const dayOfWeek = now.getDay() || 7; // 把周日作为7处理
|
||||
startTime = new Date(now);
|
||||
startTime.setDate(now.getDate() - (dayOfWeek - 1));
|
||||
startTime.setHours(0, 0, 0, 0);
|
||||
} else if (timeRange.value === 'month') {
|
||||
// 本月1号
|
||||
startTime = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
} else if (timeRange.value === 'lastMonth') {
|
||||
// 上月1号
|
||||
startTime = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||
// 本月1号作为结束时间
|
||||
const endTime = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
filtered = filtered.filter(user => {
|
||||
const checkInTime = new Date(user.lastCheckInTime);
|
||||
return checkInTime >= startTime && checkInTime < endTime;
|
||||
});
|
||||
// 已经筛选完成,不需要再次筛选
|
||||
startTime = new Date(0);
|
||||
}
|
||||
|
||||
// 如果不是上个月,用通用筛选逻辑
|
||||
if (timeRange.value !== 'lastMonth') {
|
||||
filtered = filtered.filter(user => {
|
||||
const checkInTime = new Date(user.lastCheckInTime);
|
||||
return checkInTime >= startTime;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 按用户名筛选
|
||||
if (userFilter.value) {
|
||||
const keyword = userFilter.value.toLowerCase();
|
||||
filtered = filtered.filter(user =>
|
||||
user.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
|
||||
// 排行榜列定义
|
||||
const rankingColumns: DataTableColumns<CheckInRankingInfo> = [
|
||||
{
|
||||
title: '排名',
|
||||
key: 'rank',
|
||||
render: (row: CheckInRankingInfo, index: number) => h('span', {}, index + 1)
|
||||
},
|
||||
{
|
||||
title: '用户名',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: '连续签到天数',
|
||||
key: 'consecutiveDays',
|
||||
sorter: 'default'
|
||||
},
|
||||
{
|
||||
title: '积分',
|
||||
key: 'points',
|
||||
sorter: 'default'
|
||||
},
|
||||
{
|
||||
title: '最近签到时间',
|
||||
key: 'lastCheckInTime',
|
||||
render(row: CheckInRankingInfo) {
|
||||
return h(NTooltip, {
|
||||
}, {
|
||||
trigger: () => h(NTime, {
|
||||
time: row.lastCheckInTime,
|
||||
type: 'relative'
|
||||
}),
|
||||
default: () => new Date(row.lastCheckInTime).toLocaleString()
|
||||
});
|
||||
},
|
||||
sorter: 'default'
|
||||
},
|
||||
{
|
||||
title: '已认证',
|
||||
key: 'isAuthed',
|
||||
render(row: CheckInRankingInfo) {
|
||||
return h('span', {}, row.isAuthed ? '是' : '否');
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
render(row: CheckInRankingInfo) {
|
||||
return h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => resetUserCheckInByGuid(row.ouId)
|
||||
},
|
||||
{
|
||||
trigger: () => h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'warning',
|
||||
disabled: isResetting.value,
|
||||
loading: isResetting.value && resetTargetId.value === row.ouId,
|
||||
onClick: (e) => e.stopPropagation()
|
||||
},
|
||||
{ default: () => '重置签到' }
|
||||
),
|
||||
default: () => '确定要重置该用户的所有签到数据吗?此操作不可撤销。'
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 加载签到排行榜数据
|
||||
async function loadCheckInRanking() {
|
||||
if (isLoadingRanking.value) return;
|
||||
|
||||
isLoadingRanking.value = true;
|
||||
try {
|
||||
// 获取所有用户数据,不再根据时间范围过滤
|
||||
const response = await QueryGetAPI<CheckInRankingInfo[]>(`${CHECKIN_API_URL}admin/users`);
|
||||
|
||||
if (response.code == 200) {
|
||||
rankingData.value = response.data;
|
||||
pagination.value.page = 1; // 重置为第一页
|
||||
} else {
|
||||
rankingData.value = [];
|
||||
window.$message.error(`获取签到排行榜失败: ${response.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载签到排行榜失败:', error);
|
||||
window.$notification.error({
|
||||
title: '加载失败',
|
||||
content: '无法加载签到排行榜数据',
|
||||
duration: 5000
|
||||
});
|
||||
rankingData.value = [];
|
||||
} finally {
|
||||
isLoadingRanking.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 重置签到数据相关
|
||||
const isResetting = ref(false);
|
||||
const resetTargetId = ref<string>();
|
||||
|
||||
// 重置单个用户签到数据
|
||||
async function resetUserCheckInByGuid(ouId: string) {
|
||||
if (!ouId || isResetting.value) return;
|
||||
|
||||
isResetting.value = true;
|
||||
resetTargetId.value = ouId;
|
||||
|
||||
try {
|
||||
const response = await QueryGetAPI(`${CHECKIN_API_URL}admin/reset`, {
|
||||
ouId: ouId
|
||||
});
|
||||
|
||||
if (response && response.code === 200) {
|
||||
window.$notification.success({
|
||||
title: '重置成功',
|
||||
content: '用户签到数据已重置',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
// 重置成功后重新加载排行榜
|
||||
await loadCheckInRanking();
|
||||
} else {
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: response?.message || '无法重置用户签到数据',
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重置用户签到数据失败:', error);
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: '重置用户签到数据时发生错误',
|
||||
duration: 5000
|
||||
});
|
||||
} finally {
|
||||
isResetting.value = false;
|
||||
resetTargetId.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 重置所有用户签到数据
|
||||
async function resetAllCheckIn() {
|
||||
if (isResetting.value) return;
|
||||
|
||||
isResetting.value = true;
|
||||
try {
|
||||
const response = await QueryGetAPI(`${CHECKIN_API_URL}admin/reset`, {});
|
||||
|
||||
if (response && response.code === 200) {
|
||||
window.$notification.success({
|
||||
title: '重置成功',
|
||||
content: '所有用户的签到数据已重置',
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
// 重置成功后重新加载排行榜
|
||||
await loadCheckInRanking();
|
||||
} else {
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: response?.message || '无法重置所有用户签到数据',
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重置所有用户签到数据失败:', error);
|
||||
window.$notification.error({
|
||||
title: '重置失败',
|
||||
content: '重置所有用户签到数据时发生错误',
|
||||
duration: 5000
|
||||
});
|
||||
} finally {
|
||||
isResetting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试签到功能
|
||||
const testUid = ref<number>();
|
||||
const testUsername = ref<string>('测试用户');
|
||||
const testResult = ref<{ success: boolean; message: string }>();
|
||||
|
||||
// 处理测试签到
|
||||
async function handleTestCheckIn() {
|
||||
if (!testUid.value || !serverSetting.value.enableCheckIn) {
|
||||
testResult.value = {
|
||||
success: false,
|
||||
message: '请输入有效的UID或确保签到功能已启用'
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 直接调用服务端签到API
|
||||
const response = await QueryGetAPI<CheckInResult>(`${CHECKIN_API_URL}check-in-for`, {
|
||||
uId: testUid.value,
|
||||
name: testUsername.value || '测试用户'
|
||||
});
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
const result = response.data;
|
||||
|
||||
testResult.value = {
|
||||
success: result.success,
|
||||
message: result.success
|
||||
? `签到成功!用户 ${testUsername.value || '测试用户'} 获得 ${result.points} 积分,连续签到 ${result.consecutiveDays} 天`
|
||||
: result.message || '签到失败,可能今天已经签到过了'
|
||||
};
|
||||
|
||||
// 显示通知
|
||||
window.$notification[result.success ? 'success' : 'info']({
|
||||
title: result.success ? '测试签到成功' : '测试签到失败',
|
||||
content: testResult.value.message,
|
||||
duration: 3000
|
||||
});
|
||||
} else {
|
||||
testResult.value = {
|
||||
success: false,
|
||||
message: `API返回错误: ${response.message || '未知错误'}`
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
testResult.value = {
|
||||
success: false,
|
||||
message: `签到操作失败: ${error instanceof Error ? error.message : String(error)}`
|
||||
};
|
||||
|
||||
// 显示错误通知
|
||||
window.$notification.error({
|
||||
title: '测试签到失败',
|
||||
content: testResult.value.message,
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
}
|
||||
function updateCheckInRanking(value: boolean) {
|
||||
accountInfo.value.settings.enableFunctions = value ? [...accountInfo.value.settings.enableFunctions, FunctionTypes.CheckInRanking] : accountInfo.value.settings.enableFunctions.filter(f => f !== FunctionTypes.CheckInRanking);
|
||||
SaveEnableFunctions(accountInfo.value.settings.enableFunctions);
|
||||
}
|
||||
|
||||
// 组件挂载时加载排行榜
|
||||
onMounted(() => {
|
||||
loadCheckInRanking();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.settings-section {
|
||||
margin: 8px 0;
|
||||
@@ -810,4 +810,4 @@ onMounted(() => {
|
||||
.ranking-filter {
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user