This commit is contained in:
2025-05-23 18:05:45 +08:00
parent e9f040c3e6
commit a190900181
7 changed files with 171 additions and 27 deletions

View File

@@ -7,6 +7,9 @@ import {
Button, Card, Col, DatePicker, Form, Input, InputNumber, message,
Row, Select, Space, Table, Tabs, Typography, Modal
} from 'antd';
import type { TableProps, TablePaginationConfig } from 'antd';
import type { FilterValue, SorterResult } from 'antd/es/table/interface';
import dayjs, { Dayjs } from 'dayjs';
import { api } from '@/services/api';
import { BaseResponse } from '@/types/api';
import LoadingSpinner from '@/components/LoadingSpinner';
@@ -38,8 +41,8 @@ interface GenerateCodesRequest {
}
interface ActivationCodeQueryFormData extends ActivationCodeQueryRequest {
expireTimeRange?: [any, any]; // Using 'any' for now, ideally Dayjs or Moment tuple
createTimeRange?: [any, any];
expireTimeRange?: [Dayjs | null, Dayjs | null]; // Changed from any
createTimeRange?: [Dayjs | null, Dayjs | null]; // Changed from any
}
interface ActivationCodeQueryRequest {
@@ -107,7 +110,7 @@ const AdminActivationCodesPage = () => {
try {
const requestPayload = {
...values,
expireTime: values.expireTime ? (values.expireTime as any).toISOString() : null,
expireTime: values.expireTime ? dayjs(values.expireTime).toISOString() : null,
};
const response = await api.post<BaseResponse<string[]>>('/activation-code/admin/generate', requestPayload);
if (response.data.code === 0 && response.data.data) {
@@ -118,8 +121,13 @@ const AdminActivationCodesPage = () => {
} else {
message.error(response.data.message || '生成激活码失败');
}
} catch (error: any) {
message.error(error.response?.data?.message || error.message || '生成激活码时发生错误');
} catch (error: unknown) {
let errorMessage = '生成激活码时发生错误';
if (typeof error === 'object' && error !== null) {
const potentialError = error as { response?: { data?: { message?: string } }, message?: string };
errorMessage = potentialError.response?.data?.message || potentialError.message || errorMessage;
}
message.error(errorMessage);
}
setIsGenerating(false);
};
@@ -136,15 +144,15 @@ const AdminActivationCodesPage = () => {
...params,
};
if (formValues.expireTimeRange && formValues.expireTimeRange.length === 2) {
queryParams.expireTimeStart = (formValues.expireTimeRange[0] as any).toISOString();
queryParams.expireTimeEnd = (formValues.expireTimeRange[1] as any).toISOString();
if (formValues.expireTimeRange && formValues.expireTimeRange[0] && formValues.expireTimeRange[1]) {
queryParams.expireTimeStart = formValues.expireTimeRange[0].toISOString();
queryParams.expireTimeEnd = formValues.expireTimeRange[1].toISOString();
}
delete (queryParams as ActivationCodeQueryFormData).expireTimeRange;
if (formValues.createTimeRange && formValues.createTimeRange.length === 2) {
queryParams.createTimeStart = (formValues.createTimeRange[0] as any).toISOString();
queryParams.createTimeEnd = (formValues.createTimeRange[1] as any).toISOString();
if (formValues.createTimeRange && formValues.createTimeRange[0] && formValues.createTimeRange[1]) {
queryParams.createTimeStart = formValues.createTimeRange[0].toISOString();
queryParams.createTimeEnd = formValues.createTimeRange[1].toISOString();
}
delete (queryParams as ActivationCodeQueryFormData).createTimeRange;
@@ -157,8 +165,13 @@ const AdminActivationCodesPage = () => {
setActivationCodes([]);
setTotalCodes(0);
}
} catch (error: any) {
message.error(error.response?.data?.message || error.message || '获取激活码列表时发生错误');
} catch (error: unknown) {
let errorMessage = '获取激活码列表时发生错误';
if (typeof error === 'object' && error !== null) {
const potentialError = error as { response?: { data?: { message?: string } }, message?: string };
errorMessage = potentialError.response?.data?.message || potentialError.message || errorMessage;
}
message.error(errorMessage);
setActivationCodes([]);
setTotalCodes(0);
}
@@ -171,15 +184,19 @@ const AdminActivationCodesPage = () => {
}
}, [fetchActivationCodes, isAuthenticated, user]);
const handleTableChange = (newPagination: any, filters: any, sorter: any) => {
const handleTableChange = (newPagination: TablePaginationConfig, filters: Record<string, FilterValue | null>, sorter: SorterResult<ActivationCodeVO> | SorterResult<ActivationCodeVO>[]) => {
const sortParams: Partial<ActivationCodeQueryRequest> = {};
if (sorter.field && sorter.order) {
sortParams.sortField = sorter.field as string;
sortParams.sortOrder = sorter.order;
const currentSorter = Array.isArray(sorter) ? sorter[0] : sorter;
if (currentSorter && currentSorter.field && currentSorter.order) {
sortParams.sortField = String(currentSorter.field);
sortParams.sortOrder = currentSorter.order === 'ascend' ? 'asc' : 'desc';
}
const newPager = {
current: newPagination.current,
pageSize: newPagination.pageSize,
current: newPagination.current || 1,
pageSize: newPagination.pageSize || pagination.pageSize,
};
setPagination(newPager);
fetchActivationCodes({
@@ -188,7 +205,7 @@ const AdminActivationCodesPage = () => {
});
};
const onQueryFinish = (values: ActivationCodeQueryFormData) => {
const onQueryFinish = () => {
const newPager = {...pagination, current: 1};
setPagination(newPager);
fetchActivationCodes({current: 1});
@@ -231,7 +248,7 @@ const AdminActivationCodesPage = () => {
};
// --- Columns for Activation Codes Table ---
const columns: any[] = [
const columns: TableProps<ActivationCodeVO>['columns'] = [
{ title: 'ID', dataIndex: 'id', key: 'id', sorter: true },
{ title: '激活码', dataIndex: 'code', key: 'code' },
{ title: '面值', dataIndex: 'value', key: 'value', sorter: true, render: (val: number) => `¥${val.toFixed(2)}` },
@@ -252,7 +269,7 @@ const AdminActivationCodesPage = () => {
{
title: '操作',
key: 'action',
render: (text: any, record: ActivationCodeVO) => (
render: (_: unknown, record: ActivationCodeVO) => (
<Space size="middle">
<Button type="link" danger onClick={() => handleDeleteCode(record.id)}>
@@ -340,7 +357,7 @@ const AdminActivationCodesPage = () => {
</Row>
<Form.Item>
<Button type="primary" htmlType="submit" loading={codesLoading}></Button>
<Button style={{ marginLeft: 8 }} onClick={() => { queryForm.resetFields(); onQueryFinish({}); }}></Button>
<Button style={{ marginLeft: 8 }} onClick={() => { queryForm.resetFields(); onQueryFinish(); }}></Button>
</Form.Item>
</Form>
</Card>

View File

@@ -70,7 +70,7 @@ const MqttCommunicationLogPage: React.FC = () => {
} finally {
setLoading(false);
}
}, [form, pagination.current, pagination.pageSize]); // Dependencies for useCallback
}, [form, pagination.current, pagination.pageSize]); // 恢复原来的依赖,以避免循环依赖
useEffect(() => {
if (user?.role === 'admin') {

View File

@@ -1,3 +1,4 @@
'use client';
import React, { useState, ChangeEvent, FormEvent } from 'react';
import { Input, Button, Card, Typography, message } from 'antd';
import { useAuth } from '@/contexts/AuthContext';
@@ -17,7 +18,7 @@ const RedeemActivationCodePage = () => {
const [code, setCode] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const { isAuthenticated, user } = useAuth();
const { isAuthenticated } = useAuth();
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setCode(e.target.value.trim());
@@ -53,9 +54,20 @@ const RedeemActivationCodePage = () => {
setError(errorMessage);
message.error(errorMessage);
}
} catch (err: any) {
} catch (err: unknown) {
console.error('Redeem code error:', err);
const errorMessage = err.response?.data?.message || err.message || '兑换过程中发生错误,请检查网络或联系管理员。';
let apiErrorMessage: string | undefined;
if (typeof err === 'object' && err !== null) {
// Attempt to access properties in a type-safe manner
const potentialError = err as {
response?: { data?: { message?: string } },
message?: string
};
apiErrorMessage = potentialError.response?.data?.message || potentialError.message;
}
const errorMessage = apiErrorMessage || '兑换过程中发生错误,请检查网络或联系管理员。';
setError(errorMessage);
message.error(errorMessage);
} finally {