refactor(api): 统一API路径配置并优化相关服务调用
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good
All checks were successful
test/timeline-frontend/pipeline/head This commit looks good
feat: 添加API URL全局配置文件 fix: 修复SSR环境下的窗口对象检查 perf: 优化代理配置路径匹配顺序 style: 移除无用注释和未使用的类型声明
This commit is contained in:
@@ -160,56 +160,56 @@ export default defineConfig({
|
|||||||
* @name Webpack configuration
|
* @name Webpack configuration
|
||||||
* @description Custom webpack configuration for code splitting and optimization
|
* @description Custom webpack configuration for code splitting and optimization
|
||||||
*/
|
*/
|
||||||
chainWebpack: (config: any) => {
|
// chainWebpack: (config: any) => {
|
||||||
// Ignore Windows system files to prevent Watchpack errors
|
// // Ignore Windows system files to prevent Watchpack errors
|
||||||
config.watchOptions({
|
// config.watchOptions({
|
||||||
ignored: [
|
// ignored: [
|
||||||
'**/node_modules',
|
// '**/node_modules',
|
||||||
'**/.git',
|
// '**/.git',
|
||||||
'**/C:/DumpStack.log.tmp',
|
// '**/C:/DumpStack.log.tmp',
|
||||||
'**/C:/pagefile.sys',
|
// '**/C:/pagefile.sys',
|
||||||
'**/C:/swapfile.sys',
|
// '**/C:/swapfile.sys',
|
||||||
'**/C:/System Volume Information',
|
// '**/C:/System Volume Information',
|
||||||
'**/C:/$*', // Ignore all system files starting with $
|
// '**/C:/$*', // Ignore all system files starting with $
|
||||||
],
|
// ],
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Split large vendor libraries into separate chunks
|
// // Split large vendor libraries into separate chunks
|
||||||
config.optimization.splitChunks({
|
// config.optimization.splitChunks({
|
||||||
chunks: 'all',
|
// chunks: 'all',
|
||||||
cacheGroups: {
|
// cacheGroups: {
|
||||||
// Ant Design and related libraries
|
// // Ant Design and related libraries
|
||||||
antd: {
|
// antd: {
|
||||||
name: 'antd',
|
// name: 'antd',
|
||||||
test: /[\\/]node_modules[\\/](@ant-design|antd|@antv|rc-)/,
|
// test: /[\\/]node_modules[\\/](@ant-design|antd|@antv|rc-)/,
|
||||||
priority: 20,
|
// priority: 20,
|
||||||
},
|
// },
|
||||||
// React and related libraries
|
// // React and related libraries
|
||||||
react: {
|
// react: {
|
||||||
name: 'react',
|
// name: 'react',
|
||||||
test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom)/,
|
// test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom)/,
|
||||||
priority: 20,
|
// priority: 20,
|
||||||
},
|
// },
|
||||||
// Other vendor libraries
|
// // Other vendor libraries
|
||||||
vendors: {
|
// vendors: {
|
||||||
name: 'vendors',
|
// name: 'vendors',
|
||||||
test: /[\\/]node_modules[\\/]/,
|
// test: /[\\/]node_modules[\\/]/,
|
||||||
priority: 10,
|
// priority: 10,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
|
|
||||||
// Optimize images
|
// // Optimize images
|
||||||
config.module
|
// config.module
|
||||||
.rule('images')
|
// .rule('images')
|
||||||
.test(/\.(png|jpe?g|gif|webp|svg)$/)
|
// .test(/\.(png|jpe?g|gif|webp|svg)$/)
|
||||||
.use('url-loader')
|
// .use('url-loader')
|
||||||
.loader('url-loader')
|
// .loader('url-loader')
|
||||||
.options({
|
// .options({
|
||||||
limit: 8192, // Inline images smaller than 8KB
|
// limit: 8192, // Inline images smaller than 8KB
|
||||||
name: 'static/images/[name].[hash:8].[ext]',
|
// name: 'static/images/[name].[hash:8].[ext]',
|
||||||
});
|
// });
|
||||||
|
|
||||||
return config;
|
// return config;
|
||||||
},
|
// },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,13 +15,22 @@ export default {
|
|||||||
// 如果需要自定义本地开发服务器 请取消注释按需调整
|
// 如果需要自定义本地开发服务器 请取消注释按需调整
|
||||||
dev: {
|
dev: {
|
||||||
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
||||||
|
// 注意:更具体的路径应该放在前面,避免被通用路径覆盖
|
||||||
'/api/story/': {
|
'/api/story/': {
|
||||||
// 要代理的地址
|
// 要代理的地址
|
||||||
target: basePath,
|
target: basePath,
|
||||||
// 配置了这个可以从 http 代理到 https
|
// 配置了这个可以从 http 代理到 https
|
||||||
// 依赖 origin 的功能可能需要这个,比如 cookie
|
// 依赖 origin 的功能可能需要这个,比如 cookie
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^/api/story': '/api/story' },
|
pathRewrite: { '^/api/story': '/story' },
|
||||||
|
},
|
||||||
|
'/api/user/': {
|
||||||
|
// 要代理的地址
|
||||||
|
target: basePath,
|
||||||
|
// 配置了这个可以从 http 代理到 https
|
||||||
|
// 依赖 origin 的功能可能需要这个,比如 cookie
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: { '^/api/user': '/user' },
|
||||||
},
|
},
|
||||||
'/file/': {
|
'/file/': {
|
||||||
// 要代理的地址
|
// 要代理的地址
|
||||||
@@ -39,10 +48,11 @@ export default {
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^/user-api': '/user' },
|
pathRewrite: { '^/user-api': '/user' },
|
||||||
},
|
},
|
||||||
|
// 通用 /api/ 代理放在最后,避免覆盖上面的具体配置
|
||||||
'/api/': {
|
'/api/': {
|
||||||
target: 'https://proapi.azurewebsites.net',
|
target: basePath,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^': '' },
|
pathRewrite: { '^/api': '/api' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
52
src/app.tsx
52
src/app.tsx
@@ -8,7 +8,6 @@ import { history, Link, useModel } from '@umijs/max';
|
|||||||
import { useIsMobile } from '@/hooks/useIsMobile';
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
||||||
import defaultSettings from '../config/defaultSettings';
|
import defaultSettings from '../config/defaultSettings';
|
||||||
import { errorConfig } from './requestErrorConfig';
|
import { errorConfig } from './requestErrorConfig';
|
||||||
import { currentUser as queryCurrentUser } from './services/ant-design-pro/api';
|
|
||||||
import { useNotifications } from '@/hooks/useNotifications';
|
import { useNotifications } from '@/hooks/useNotifications';
|
||||||
import BottomNav from '@/components/BottomNav';
|
import BottomNav from '@/components/BottomNav';
|
||||||
import SyncStatusIndicator from '@/components/SyncStatus/SyncStatusIndicator';
|
import SyncStatusIndicator from '@/components/SyncStatus/SyncStatusIndicator';
|
||||||
@@ -22,32 +21,33 @@ const loginPath = '/user/login';
|
|||||||
* Layout Children Wrapper Component
|
* Layout Children Wrapper Component
|
||||||
* Handles mobile detection and notifications setup
|
* Handles mobile detection and notifications setup
|
||||||
*/
|
*/
|
||||||
function LayoutChildrenWrapper({
|
function LayoutChildrenWrapper({
|
||||||
children,
|
children,
|
||||||
initialState,
|
initialState,
|
||||||
setInitialState
|
setInitialState
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
initialState: any;
|
initialState: any;
|
||||||
setInitialState: any;
|
setInitialState: any;
|
||||||
}) {
|
}) {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const { effectiveTheme, getCurrentColorScheme, fetchThemePreferences } = useModel('theme');
|
|
||||||
|
|
||||||
|
// 暂时注释掉 theme 相关代码,避免 useModel 调用导致问题
|
||||||
|
const { effectiveTheme, getCurrentColorScheme, fetchThemePreferences } = useModel('theme');
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchThemePreferences();
|
fetchThemePreferences();
|
||||||
}, [fetchThemePreferences]);
|
}, [fetchThemePreferences]);
|
||||||
|
|
||||||
const colorScheme = getCurrentColorScheme();
|
const colorScheme = getCurrentColorScheme();
|
||||||
|
|
||||||
|
// 暂时注释掉 useNotifications,避免 useModel 调用导致问题
|
||||||
useNotifications();
|
useNotifications();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
theme={{
|
theme={{
|
||||||
algorithm: effectiveTheme === 'dark' ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
algorithm: antdTheme.defaultAlgorithm,
|
||||||
token: {
|
token: {
|
||||||
colorPrimary: colorScheme.primaryColor,
|
colorPrimary: '#1890ff',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -77,24 +77,26 @@ export async function getInitialState(): Promise<{
|
|||||||
currentUser?: API.CurrentUser;
|
currentUser?: API.CurrentUser;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}> {
|
}> {
|
||||||
const fetchUserInfo = async () => {
|
// 从 localStorage 获取用户信息
|
||||||
try {
|
const getUserFromStorage = () => {
|
||||||
const msg = await queryCurrentUser({ skipErrorHandler: true });
|
if (typeof window !== 'undefined') {
|
||||||
return msg.data;
|
const userStr = localStorage.getItem('timeline_user');
|
||||||
} catch (error) {
|
if (userStr) {
|
||||||
// history.push(loginPath);
|
try {
|
||||||
|
return JSON.parse(userStr);
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (history.location.pathname !== loginPath) {
|
// 直接从 localStorage 读取用户信息,避免API调用阻塞页面渲染
|
||||||
const currentUser = await fetchUserInfo();
|
const currentUser = getUserFromStorage();
|
||||||
return {
|
|
||||||
currentUser,
|
|
||||||
settings: defaultSettings as Partial<LayoutSettings>,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
|
currentUser,
|
||||||
settings: defaultSettings as Partial<LayoutSettings>,
|
settings: defaultSettings as Partial<LayoutSettings>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -103,8 +105,8 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
|
|||||||
return {
|
return {
|
||||||
actionsRender: () => [
|
actionsRender: () => [
|
||||||
<ClientOnly key="client-only-actions">
|
<ClientOnly key="client-only-actions">
|
||||||
<SyncStatusIndicator key="sync-status" />,
|
<SyncStatusIndicator key="sync-status" />
|
||||||
<Question key="doc" />,
|
<Question key="doc" />
|
||||||
<SelectLang key="SelectLang" />
|
<SelectLang key="SelectLang" />
|
||||||
</ClientOnly>,
|
</ClientOnly>,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ const CommentList: React.FC<CommentListProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
// Sort comments chronologically (oldest first)
|
// Sort comments chronologically (oldest first)
|
||||||
const sortedComments = useMemo(() => {
|
const sortedComments = useMemo(() => {
|
||||||
|
if (!comments || !Array.isArray(comments)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return [...comments].sort((a, b) => {
|
return [...comments].sort((a, b) => {
|
||||||
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useEffect, useState, useCallback } from 'react';
|
|||||||
import { useModel, request } from '@umijs/max';
|
import { useModel, request } from '@umijs/max';
|
||||||
import { notification as antdNotification } from 'antd';
|
import { notification as antdNotification } from 'antd';
|
||||||
import { Notification, NotificationType } from '@/types';
|
import { Notification, NotificationType } from '@/types';
|
||||||
|
import { AUTH_API, USER_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
export const useNotifications = () => {
|
export const useNotifications = () => {
|
||||||
const { initialState } = useModel('@@initialState');
|
const { initialState } = useModel('@@initialState');
|
||||||
@@ -13,7 +14,7 @@ export const useNotifications = () => {
|
|||||||
const fetchUnreadNotifications = useCallback(async () => {
|
const fetchUnreadNotifications = useCallback(async () => {
|
||||||
if (!initialState?.currentUser) return;
|
if (!initialState?.currentUser) return;
|
||||||
try {
|
try {
|
||||||
const res = await request<Notification[]>('/user-api/message/unread');
|
const res = await request<Notification[]>(`${AUTH_API.INFO}/message/unread`);
|
||||||
setNotifications(res);
|
setNotifications(res);
|
||||||
setUnreadCount(res.length);
|
setUnreadCount(res.length);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -53,7 +54,7 @@ export const useNotifications = () => {
|
|||||||
|
|
||||||
const markAsRead = useCallback(async (ids: number[]) => {
|
const markAsRead = useCallback(async (ids: number[]) => {
|
||||||
try {
|
try {
|
||||||
await request('/api/user/notifications/read', {
|
await request(USER_API.NOTIFICATIONS_READ, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: ids,
|
data: ids,
|
||||||
});
|
});
|
||||||
@@ -79,9 +80,14 @@ export const useNotifications = () => {
|
|||||||
case NotificationType.SYSTEM:
|
case NotificationType.SYSTEM:
|
||||||
return '系统通知';
|
return '系统通知';
|
||||||
default:
|
default:
|
||||||
return '系统通知';
|
return '通知';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { notifications, unreadCount, markAsRead };
|
return {
|
||||||
|
notifications,
|
||||||
|
unreadCount,
|
||||||
|
markAsRead,
|
||||||
|
fetchUnreadNotifications,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export function useReactions(
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
hasReaction: !!userReaction,
|
hasReaction: !!userReaction,
|
||||||
totalReactions: reactions
|
totalReactions: reactions?.counts
|
||||||
? Object.values(reactions.counts).reduce((sum, count) => sum + count, 0)
|
? Object.values(reactions.counts).reduce((sum, count) => sum + count, 0)
|
||||||
: 0,
|
: 0,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export interface SyncState {
|
|||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const [syncState, setSyncState] = useState<SyncState>({
|
const [syncState, setSyncState] = useState<SyncState>({
|
||||||
isOnline: navigator.onLine,
|
isOnline: typeof window !== 'undefined' ? navigator.onLine : true,
|
||||||
isSyncing: false,
|
isSyncing: false,
|
||||||
pendingChanges: 0,
|
pendingChanges: 0,
|
||||||
lastSyncTime: undefined,
|
lastSyncTime: undefined,
|
||||||
@@ -24,6 +24,8 @@ export default () => {
|
|||||||
|
|
||||||
// Update online status
|
// Update online status
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
|
||||||
const handleOnline = () => {
|
const handleOnline = () => {
|
||||||
setSyncState((prev) => ({ ...prev, isOnline: true, syncError: undefined }));
|
setSyncState((prev) => ({ ...prev, isOnline: true, syncError: undefined }));
|
||||||
// Automatically trigger sync when coming online
|
// Automatically trigger sync when coming online
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import type { CurrentUser, FriendUser, HistoryMessage, ListItemDataType, MessageItem, UserInfo } from './data.d';
|
import type { CurrentUser, FriendUser, HistoryMessage, ListItemDataType, MessageItem, UserInfo } from './data.d';
|
||||||
import { CommonResponse } from '@/types/common';
|
import { CommonResponse } from '@/types/common';
|
||||||
|
import { AUTH_API, STORY_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
export async function queryCurrent(): Promise<{ data: CurrentUser }> {
|
export async function queryCurrent(): Promise<{ data: CurrentUser }> {
|
||||||
return request('/user-api/info');
|
return request(AUTH_API.INFO);
|
||||||
}
|
}
|
||||||
export async function searchUsername(params: {username: string}) : Promise<{data: UserInfo[]}> {
|
export async function searchUsername(params: {username: string}) : Promise<{data: UserInfo[]}> {
|
||||||
return request('/user-api/search', {
|
return request(AUTH_API.FRIEND_SEARCH, {
|
||||||
params: params,
|
params: params,
|
||||||
method: "GET"
|
method: "GET"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addFriend(userId: string) : Promise<CommonResponse<String>> {
|
export async function addFriend(userId: string) : Promise<CommonResponse<String>> {
|
||||||
return request('/user-api/friend/request', {
|
return request(AUTH_API.FRIEND_REQUEST, {
|
||||||
data: {
|
data: {
|
||||||
friendId: userId,
|
friendId: userId,
|
||||||
},
|
},
|
||||||
@@ -21,29 +22,29 @@ export async function addFriend(userId: string) : Promise<CommonResponse<String>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
export async function queryFriendList(): Promise<CommonResponse<FriendUser[]>> {
|
export async function queryFriendList(): Promise<CommonResponse<FriendUser[]>> {
|
||||||
return request('/user-api/friend/list');
|
return request(AUTH_API.FRIEND_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function acceptFriendRequest(friendId: string): Promise<CommonResponse<string>> {
|
export async function acceptFriendRequest(friendId: string): Promise<CommonResponse<string>> {
|
||||||
return request('/user-api/friend/accept', {
|
return request(AUTH_API.FRIEND_ACCEPT, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { friendId },
|
data: { friendId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rejectFriendRequest(friendId: string): Promise<CommonResponse<string>> {
|
export async function rejectFriendRequest(friendId: string): Promise<CommonResponse<string>> {
|
||||||
return request('/user-api/friend/reject', {
|
return request(AUTH_API.FRIEND_REJECT, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { friendId },
|
data: { friendId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function queryHistoryMessages(): Promise<CommonResponse<HistoryMessage[]>> {
|
export async function queryHistoryMessages(): Promise<CommonResponse<HistoryMessage[]>> {
|
||||||
return request('/user-api/message/history/friend', {
|
return request(AUTH_API.MESSAGE_HISTORY, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function queryFriendDynamic(): Promise<CommonResponse<HistoryMessage[]>> {
|
export async function queryFriendDynamic(): Promise<CommonResponse<HistoryMessage[]>> {
|
||||||
return request('/api/story/activity/authorized-items', {
|
return request(STORY_API.ACTIVITY_AUTHORIZED_ITEMS, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import type { CurrentUser, GeographicItemType } from './data';
|
import type { CurrentUser, GeographicItemType } from './data';
|
||||||
import { CommonResponse } from '@/types/common';
|
import { CommonResponse } from '@/types/common';
|
||||||
|
import { AUTH_API, GEO_API, COMMON_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
export async function queryCurrent(): Promise<{ data: CurrentUser }> {
|
export async function queryCurrent(): Promise<{ data: CurrentUser }> {
|
||||||
return request('/user-api/info');
|
return request(AUTH_API.INFO);
|
||||||
}
|
}
|
||||||
export async function updateCurrentUser(params: CurrentUser): Promise<{data: CommonResponse<string>}> {
|
export async function updateCurrentUser(params: CurrentUser): Promise<{data: CommonResponse<string>}> {
|
||||||
return request('/user-api/info', {
|
return request(AUTH_API.INFO, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: params,
|
data: params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export async function queryProvince(): Promise<{ data: GeographicItemType[] }> {
|
export async function queryProvince(): Promise<{ data: GeographicItemType[] }> {
|
||||||
return request('/api/geographic/province');
|
return request(GEO_API.PROVINCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryCity(province: string): Promise<{ data: GeographicItemType[] }> {
|
export async function queryCity(province: string): Promise<{ data: GeographicItemType[] }> {
|
||||||
return request(`/api/geographic/city/${province}`);
|
return request(GEO_API.CITY(province));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function query() {
|
export async function query() {
|
||||||
return request('/api/users');
|
return request(COMMON_API.USERS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Button, message, Spin, Empty, Checkbox } from 'antd';
|
import { Button, message, Spin, Empty, Checkbox } from 'antd';
|
||||||
import { useModel, request } from '@umijs/max';
|
import { useModel, request } from '@umijs/max';
|
||||||
|
import { USER_API } from '@/services/config/apiUrls';
|
||||||
import styles from '../index.less';
|
import styles from '../index.less';
|
||||||
|
|
||||||
interface PhotoSelectorProps {
|
interface PhotoSelectorProps {
|
||||||
@@ -42,16 +43,16 @@ const PhotoSelector: React.FC<PhotoSelectorProps> = ({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
// Fetch user's photos from gallery
|
// Fetch user's photos from gallery
|
||||||
const response = await request('/api/v1/gallery/photos', {
|
const response = await request(USER_API.GALLERY_PHOTOS, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: { pageSize: 100 },
|
params: { pageSize: 100 },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter out photos already in the album
|
// Filter out photos already in the album
|
||||||
const availablePhotos = response.items?.filter(
|
const availablePhotos = response.items?.filter(
|
||||||
(photo: Photo) => !existingPhotoIds.includes(photo.id)
|
(photo: Photo) => !existingPhotoIds.includes(photo.id)
|
||||||
) || [];
|
) || [];
|
||||||
|
|
||||||
setPhotos(availablePhotos);
|
setPhotos(availablePhotos);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('Failed to load photos');
|
message.error('Failed to load photos');
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||||||
import { PlayCircleOutlined, PauseCircleOutlined, FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons';
|
import { PlayCircleOutlined, PauseCircleOutlined, FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { FILE_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
interface TimelineVideoProps {
|
interface TimelineVideoProps {
|
||||||
videoInstanceId: string;
|
videoInstanceId: string;
|
||||||
@@ -21,7 +22,7 @@ const TimelineVideo: React.FC<TimelineVideoProps> = ({ videoInstanceId, thumbnai
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchVideoUrl = async () => {
|
const fetchVideoUrl = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await request(`/file/get-video-url/${videoInstanceId}`, { method: 'GET' });
|
const response = await request(FILE_API.VIDEO_URL(videoInstanceId), { method: 'GET' });
|
||||||
if (response.code === 200 && response.data) {
|
if (response.code === 200 && response.data) {
|
||||||
setVideoSrc(response.data);
|
setVideoSrc(response.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
import {StoryItem, StoryItemTimeQueryParams, StoryType} from './data.d';
|
import {StoryItem, StoryItemTimeQueryParams, StoryType} from './data.d';
|
||||||
import {CommonListResponse, CommonResponse} from "@/types/common";
|
import {CommonListResponse, CommonResponse} from "@/types/common";
|
||||||
|
import { STORY_API, FILE_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
type ParamsType = {
|
type ParamsType = {
|
||||||
count?: number;
|
count?: number;
|
||||||
@@ -13,19 +14,19 @@ type ParamsType = {
|
|||||||
export async function queryTimelineList(
|
export async function queryTimelineList(
|
||||||
params: ParamsType,
|
params: ParamsType,
|
||||||
): Promise<{ data: StoryType[] }> {
|
): Promise<{ data: StoryType[] }> {
|
||||||
return await request('/api/story/list', {
|
return await request(STORY_API.LIST, {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> {
|
export async function deleteStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> {
|
||||||
return request(`/api/story/${params.instanceId}`, {
|
return request(STORY_API.DETAIL(params.instanceId!), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> {
|
export async function addStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> {
|
||||||
return request('/api/story/add', {
|
return request(STORY_API.ADD, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
...params,
|
...params,
|
||||||
@@ -35,7 +36,7 @@ export async function addStory(params: ParamsType): Promise<{ data: { list: Stor
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function updateStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> {
|
export async function updateStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> {
|
||||||
return await request(`/api/story/${params.instanceId}`, {
|
return await request(STORY_API.DETAIL(params.instanceId!), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: {
|
data: {
|
||||||
...params,
|
...params,
|
||||||
@@ -44,12 +45,12 @@ export async function updateStory(params: ParamsType): Promise<{ data: { list: S
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function queryStoryDetail(itemId: string): Promise<{ data: StoryType }> {
|
export async function queryStoryDetail(itemId: string): Promise<{ data: StoryType }> {
|
||||||
return request(`/api/story/${itemId}`, {
|
return request(STORY_API.DETAIL(itemId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function addStoryItem(params: FormData): Promise<any> {
|
export async function addStoryItem(params: FormData): Promise<any> {
|
||||||
return request(`/api/story/item`, {
|
return request(STORY_API.ITEM, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
requestType: 'form',
|
requestType: 'form',
|
||||||
@@ -57,7 +58,7 @@ export async function addStoryItem(params: FormData): Promise<any> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function updateStoryItem(params: FormData): Promise<any> {
|
export async function updateStoryItem(params: FormData): Promise<any> {
|
||||||
return request(`/api/story/item`, {
|
return request(STORY_API.ITEM, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: params,
|
data: params,
|
||||||
requestType: 'form',
|
requestType: 'form',
|
||||||
@@ -66,43 +67,43 @@ export async function updateStoryItem(params: FormData): Promise<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function queryStoryItem(params: ParamsType): Promise<{ data: CommonListResponse<StoryItem> }> {
|
export async function queryStoryItem(params: ParamsType): Promise<{ data: CommonListResponse<StoryItem> }> {
|
||||||
return request(`/api/story/item/list`, {
|
return request(STORY_API.ITEM_LIST, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: params,
|
params: params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryStoryItemDetail(itemId: string): Promise<{ data: StoryItem }> {
|
export async function queryStoryItemDetail(itemId: string): Promise<{ data: StoryItem }> {
|
||||||
return request(`/api/story/item/${itemId}`, {
|
return request(STORY_API.ITEM_DETAIL(itemId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function countStoryItem(storyInstanceId: string): Promise<{ data: StoryItem }> {
|
export async function countStoryItem(storyInstanceId: string): Promise<{ data: StoryItem }> {
|
||||||
return request(`/api/story/item/count/${storyInstanceId}`, {
|
return request(STORY_API.ITEM_COUNT(storyInstanceId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function queryStoryItemImages(itemId: string): Promise<{ data: string[] }> {
|
export async function queryStoryItemImages(itemId: string): Promise<{ data: string[] }> {
|
||||||
return request(`/api/story/item/images/${itemId}`, {
|
return request(STORY_API.ITEM_IMAGES(itemId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function removeStoryItem(instanceId: string): Promise<CommonResponse<void>> {
|
export async function removeStoryItem(instanceId: string): Promise<CommonResponse<void>> {
|
||||||
return request(`/api/story/item/${instanceId}`, {
|
return request(STORY_API.ITEM_DETAIL(instanceId), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function searchStoryItems(params: { keyword: string; pageNum: number; pageSize: number }) {
|
export async function searchStoryItems(params: { keyword: string; pageNum: number; pageSize: number }) {
|
||||||
return request('/api/story/item/search', {
|
return request(STORY_API.ITEM_SEARCH, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchImage(imageInstanceId: string): Promise<any> {
|
export async function fetchImage(imageInstanceId: string): Promise<any> {
|
||||||
return request(`/file/image/${imageInstanceId}`, {
|
return request(FILE_API.IMAGE(imageInstanceId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
getResponse: true,
|
getResponse: true,
|
||||||
@@ -110,59 +111,59 @@ export async function fetchImage(imageInstanceId: string): Promise<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function authorizeStoryPermission(params: {userId: string, storyInstanceId: string, permissionType: number}) {
|
export async function authorizeStoryPermission(params: {userId: string, storyInstanceId: string, permissionType: number}) {
|
||||||
return request('/api/story/permission/authorize', {
|
return request(STORY_API.PERMISSION_AUTHORIZE, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStoryPermissions(storyId: string) {
|
export async function getStoryPermissions(storyId: string) {
|
||||||
return request(`/api/story/permission/story/${storyId}`, {
|
return request(STORY_API.PERMISSION_STORY(storyId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function inviteUser(params: {userId: string, storyInstanceId: string, permissionType: number}) {
|
export async function inviteUser(params: {userId: string, storyInstanceId: string, permissionType: number}) {
|
||||||
return request('/api/story/permission/invite', {
|
return request(STORY_API.PERMISSION_INVITE, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function acceptInvite(inviteId: string) {
|
export async function acceptInvite(inviteId: string) {
|
||||||
return request(`/api/story/permission/invite/${inviteId}/accept`, {
|
return request(STORY_API.PERMISSION_INVITE_ACCEPT(inviteId), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rejectInvite(inviteId: string) {
|
export async function rejectInvite(inviteId: string) {
|
||||||
return request(`/api/story/permission/invite/${inviteId}/reject`, {
|
return request(STORY_API.PERMISSION_INVITE_REJECT(inviteId), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updatePermission(params: {permissionId: string, permissionType: number}) {
|
export async function updatePermission(params: {permissionId: string, permissionType: number}) {
|
||||||
return request('/api/story/permission', {
|
return request(STORY_API.PERMISSION, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: params
|
data: params
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removePermission(permissionId: string) {
|
export async function removePermission(permissionId: string) {
|
||||||
return request(`/api/story/permission/${permissionId}`, {
|
return request(`${STORY_API.PERMISSION}/${permissionId}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新时间线节点排序
|
* 更新时间线节点排序
|
||||||
*
|
*
|
||||||
* 功能描述:
|
* 功能描述:
|
||||||
* 批量更新节点的排序值,用于拖拽排序后保存结果。
|
* 批量更新节点的排序值,用于拖拽排序后保存结果。
|
||||||
*
|
*
|
||||||
* @param orderData - 排序数据数组,包含节点ID和新排序值
|
* @param orderData - 排序数据数组,包含节点ID和新排序值
|
||||||
* @returns API响应
|
* @returns API响应
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const orderData = [
|
* const orderData = [
|
||||||
* { instanceId: 'item-1', sortOrder: 0 },
|
* { instanceId: 'item-1', sortOrder: 0 },
|
||||||
@@ -173,7 +174,7 @@ export async function removePermission(permissionId: string) {
|
|||||||
export async function updateStoryItemOrder(
|
export async function updateStoryItemOrder(
|
||||||
orderData: Array<{ instanceId: string; sortOrder: number }>
|
orderData: Array<{ instanceId: string; sortOrder: number }>
|
||||||
): Promise<CommonResponse<void>> {
|
): Promise<CommonResponse<void>> {
|
||||||
return request('/api/story/item/order', {
|
return request(`${STORY_API.ITEM}/order`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { items: orderData },
|
data: { items: orderData },
|
||||||
});
|
});
|
||||||
@@ -181,21 +182,21 @@ export async function updateStoryItemOrder(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除时间线节点
|
* 批量删除时间线节点
|
||||||
*
|
*
|
||||||
* 功能描述:
|
* 功能描述:
|
||||||
* 根据节点ID列表批量删除时间线节点。
|
* 根据节点ID列表批量删除时间线节点。
|
||||||
* 删除操作为软删除,数据可恢复。
|
* 删除操作为软删除,数据可恢复。
|
||||||
*
|
*
|
||||||
* @param instanceIds - 要删除的节点ID数组
|
* @param instanceIds - 要删除的节点ID数组
|
||||||
* @returns API响应
|
* @returns API响应
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* await batchDeleteStoryItems(['item-1', 'item-2', 'item-3']);
|
* await batchDeleteStoryItems(['item-1', 'item-2', 'item-3']);
|
||||||
*/
|
*/
|
||||||
export async function batchDeleteStoryItems(
|
export async function batchDeleteStoryItems(
|
||||||
instanceIds: string[]
|
instanceIds: string[]
|
||||||
): Promise<CommonResponse<void>> {
|
): Promise<CommonResponse<void>> {
|
||||||
return request('/api/story/item/batch-delete', {
|
return request(`${STORY_API.ITEM}/batch-delete`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { instanceIds },
|
data: { instanceIds },
|
||||||
});
|
});
|
||||||
@@ -203,10 +204,10 @@ export async function batchDeleteStoryItems(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量修改时间线节点时间
|
* 批量修改时间线节点时间
|
||||||
*
|
*
|
||||||
* 功能描述:
|
* 功能描述:
|
||||||
* 批量修改多个节点的时间信息。
|
* 批量修改多个节点的时间信息。
|
||||||
*
|
*
|
||||||
* @param instanceIds - 要修改的节点ID数组
|
* @param instanceIds - 要修改的节点ID数组
|
||||||
* @param storyItemTime - 新的时间值
|
* @param storyItemTime - 新的时间值
|
||||||
* @returns API响应
|
* @returns API响应
|
||||||
@@ -215,8 +216,8 @@ export async function batchUpdateStoryItemTime(
|
|||||||
instanceIds: string[],
|
instanceIds: string[],
|
||||||
storyItemTime: string
|
storyItemTime: string
|
||||||
): Promise<CommonResponse<void>> {
|
): Promise<CommonResponse<void>> {
|
||||||
return request('/api/story/item/batch-time', {
|
return request(`${STORY_API.ITEM}/batch-time`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { instanceIds, storyItemTime },
|
data: { instanceIds, storyItemTime },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getFakeCaptcha } from '@/services/ant-design-pro/login';
|
import { getFakeCaptcha } from '@/services/ant-design-pro/login';
|
||||||
import { loginUser } from '@/services/user/api';
|
import { loginUser } from '@/services/user/api';
|
||||||
|
import type { UserLoginParams, UserLoginResult } from '@/services/user/typing';
|
||||||
import { CommonResponse } from '@/types/common';
|
import { CommonResponse } from '@/types/common';
|
||||||
import {
|
import {
|
||||||
AlipayCircleOutlined,
|
AlipayCircleOutlined,
|
||||||
@@ -133,9 +134,9 @@ const Login: React.FC = () => {
|
|||||||
currentUser: response.data,
|
currentUser: response.data,
|
||||||
}));
|
}));
|
||||||
const urlParams = new URL(window.location.href).searchParams;
|
const urlParams = new URL(window.location.href).searchParams;
|
||||||
// 修复:直接使用 redirect 参数,如果不存在则跳转到首页
|
// 修复:使用 history.push 进行跳转,避免页面刷新导致状态丢失
|
||||||
const redirect = urlParams.get('redirect');
|
const redirect = urlParams.get('redirect');
|
||||||
window.location.href = redirect || '/';
|
history.push(redirect || '/');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(response.message);
|
console.log(response.message);
|
||||||
|
|||||||
@@ -120,8 +120,13 @@ export const errorConfig: RequestConfig = {
|
|||||||
return { ...config, headers };
|
return { ...config, headers };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无 token 提示并引导登录
|
// 无 token 时,如果是获取当前用户信息的请求,不提示登录(让调用方处理)
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
const skipAuthUrls = ['/api/currentUser', '/user-api/info'];
|
||||||
|
if (config?.url && skipAuthUrls.some((u) => config.url?.includes(u))) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
message.warning('请先登录');
|
message.warning('请先登录');
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
const urlParams = new URL(currentUrl).searchParams;
|
const urlParams = new URL(currentUrl).searchParams;
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { USER_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/albums';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all albums for the current user
|
* Get all albums for the current user
|
||||||
*/
|
*/
|
||||||
export async function getAlbums(): Promise<API.Album[]> {
|
export async function getAlbums(): Promise<API.Album[]> {
|
||||||
return request(`${BASE_URL}`, {
|
return request(USER_API.ALBUMS, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -20,7 +19,7 @@ export async function getAlbums(): Promise<API.Album[]> {
|
|||||||
* Get album details by ID
|
* Get album details by ID
|
||||||
*/
|
*/
|
||||||
export async function getAlbumById(id: string): Promise<API.Album> {
|
export async function getAlbumById(id: string): Promise<API.Album> {
|
||||||
return request(`${BASE_URL}/${id}`, {
|
return request(USER_API.ALBUM_DETAIL(id), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -29,7 +28,7 @@ export async function getAlbumById(id: string): Promise<API.Album> {
|
|||||||
* Create a new album
|
* Create a new album
|
||||||
*/
|
*/
|
||||||
export async function createAlbum(data: API.CreateAlbumDTO): Promise<API.Album> {
|
export async function createAlbum(data: API.CreateAlbumDTO): Promise<API.Album> {
|
||||||
return request(`${BASE_URL}`, {
|
return request(USER_API.ALBUMS, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -39,7 +38,7 @@ export async function createAlbum(data: API.CreateAlbumDTO): Promise<API.Album>
|
|||||||
* Update an existing album
|
* Update an existing album
|
||||||
*/
|
*/
|
||||||
export async function updateAlbum(id: string, data: API.UpdateAlbumDTO): Promise<API.Album> {
|
export async function updateAlbum(id: string, data: API.UpdateAlbumDTO): Promise<API.Album> {
|
||||||
return request(`${BASE_URL}/${id}`, {
|
return request(USER_API.ALBUM_DETAIL(id), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -49,7 +48,7 @@ export async function updateAlbum(id: string, data: API.UpdateAlbumDTO): Promise
|
|||||||
* Delete an album
|
* Delete an album
|
||||||
*/
|
*/
|
||||||
export async function deleteAlbum(id: string): Promise<void> {
|
export async function deleteAlbum(id: string): Promise<void> {
|
||||||
return request(`${BASE_URL}/${id}`, {
|
return request(USER_API.ALBUM_DETAIL(id), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ export async function deleteAlbum(id: string): Promise<void> {
|
|||||||
* Add photos to an album
|
* Add photos to an album
|
||||||
*/
|
*/
|
||||||
export async function addPhotosToAlbum(id: string, photoIds: string[]): Promise<void> {
|
export async function addPhotosToAlbum(id: string, photoIds: string[]): Promise<void> {
|
||||||
return request(`${BASE_URL}/${id}/photos`, {
|
return request(USER_API.ALBUM_PHOTOS(id), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { photoIds },
|
data: { photoIds },
|
||||||
});
|
});
|
||||||
@@ -68,7 +67,7 @@ export async function addPhotosToAlbum(id: string, photoIds: string[]): Promise<
|
|||||||
* Remove photos from an album
|
* Remove photos from an album
|
||||||
*/
|
*/
|
||||||
export async function removePhotosFromAlbum(id: string, photoIds: string[]): Promise<void> {
|
export async function removePhotosFromAlbum(id: string, photoIds: string[]): Promise<void> {
|
||||||
return request(`${BASE_URL}/${id}/photos`, {
|
return request(USER_API.ALBUM_PHOTOS(id), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
data: { photoIds },
|
data: { photoIds },
|
||||||
});
|
});
|
||||||
@@ -78,7 +77,7 @@ export async function removePhotosFromAlbum(id: string, photoIds: string[]): Pro
|
|||||||
* Reorder photos in an album
|
* Reorder photos in an album
|
||||||
*/
|
*/
|
||||||
export async function reorderPhotos(id: string, order: string[]): Promise<void> {
|
export async function reorderPhotos(id: string, order: string[]): Promise<void> {
|
||||||
return request(`${BASE_URL}/${id}/photos/order`, {
|
return request(USER_API.ALBUM_PHOTOS_ORDER(id), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { order },
|
data: { order },
|
||||||
});
|
});
|
||||||
@@ -88,7 +87,7 @@ export async function reorderPhotos(id: string, order: string[]): Promise<void>
|
|||||||
* Set album cover photo
|
* Set album cover photo
|
||||||
*/
|
*/
|
||||||
export async function setAlbumCover(id: string, coverPhotoId: string): Promise<void> {
|
export async function setAlbumCover(id: string, coverPhotoId: string): Promise<void> {
|
||||||
return request(`${BASE_URL}/${id}/cover`, {
|
return request(USER_API.ALBUM_COVER(id), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { coverPhotoId },
|
data: { coverPhotoId },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { USER_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/collections';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all smart collections for the current user
|
* Get all smart collections for the current user
|
||||||
*/
|
*/
|
||||||
export async function getSmartCollections(): Promise<API.SmartCollection[]> {
|
export async function getSmartCollections(): Promise<API.SmartCollection[]> {
|
||||||
return request(`${BASE_URL}/smart`, {
|
return request(USER_API.COLLECTIONS_SMART, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -23,7 +22,7 @@ export async function getCollectionContent(
|
|||||||
collectionId: string,
|
collectionId: string,
|
||||||
params: { page: number; pageSize: number },
|
params: { page: number; pageSize: number },
|
||||||
): Promise<API.CollectionContent> {
|
): Promise<API.CollectionContent> {
|
||||||
return request(`${BASE_URL}/smart/${collectionId}/content`, {
|
return request(USER_API.COLLECTIONS_SMART_CONTENT(collectionId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
@@ -33,7 +32,7 @@ export async function getCollectionContent(
|
|||||||
* Force refresh smart collections
|
* Force refresh smart collections
|
||||||
*/
|
*/
|
||||||
export async function refreshSmartCollections(): Promise<void> {
|
export async function refreshSmartCollections(): Promise<void> {
|
||||||
return request(`${BASE_URL}/smart/refresh`, {
|
return request(USER_API.COLLECTIONS_SMART_REFRESH, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { SOCIAL_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comments for an entity
|
* Get comments for an entity
|
||||||
@@ -12,7 +13,7 @@ export async function getComments(
|
|||||||
entityType: API.SocialEntityType,
|
entityType: API.SocialEntityType,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
): Promise<API.Comment[]> {
|
): Promise<API.Comment[]> {
|
||||||
return request(`/api/v1/comments/${entityType}/${entityId}`, {
|
return request(SOCIAL_API.COMMENTS_BY_ENTITY(entityType, entityId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -21,7 +22,7 @@ export async function getComments(
|
|||||||
* Create a new comment
|
* Create a new comment
|
||||||
*/
|
*/
|
||||||
export async function createComment(data: API.CreateCommentDTO): Promise<API.Comment> {
|
export async function createComment(data: API.CreateCommentDTO): Promise<API.Comment> {
|
||||||
return request('/api/v1/comments', {
|
return request(SOCIAL_API.COMMENTS, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -34,7 +35,7 @@ export async function updateComment(
|
|||||||
id: string,
|
id: string,
|
||||||
data: API.UpdateCommentDTO,
|
data: API.UpdateCommentDTO,
|
||||||
): Promise<API.Comment> {
|
): Promise<API.Comment> {
|
||||||
return request(`/api/v1/comments/${id}`, {
|
return request(SOCIAL_API.COMMENTS_DETAIL(id), {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -44,7 +45,7 @@ export async function updateComment(
|
|||||||
* Delete a comment
|
* Delete a comment
|
||||||
*/
|
*/
|
||||||
export async function deleteComment(id: string): Promise<void> {
|
export async function deleteComment(id: string): Promise<void> {
|
||||||
return request(`/api/v1/comments/${id}`, {
|
return request(SOCIAL_API.COMMENTS_DETAIL(id), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
131
src/services/config/apiUrls.ts
Normal file
131
src/services/config/apiUrls.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* API URL 全局配置
|
||||||
|
* 所有后端接口地址统一在此配置,便于管理和维护
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 基础 API 前缀
|
||||||
|
const API_PREFIX = '/api';
|
||||||
|
const USER_API_PREFIX = `${API_PREFIX}/user/api/v1`;
|
||||||
|
const USER_AUTH_PREFIX = '/user-api';
|
||||||
|
|
||||||
|
// Story 相关接口
|
||||||
|
export const STORY_API = {
|
||||||
|
BASE: `${API_PREFIX}/story`,
|
||||||
|
LIST: `${API_PREFIX}/story/list`,
|
||||||
|
ADD: `${API_PREFIX}/story/add`,
|
||||||
|
DETAIL: (id: string) => `${API_PREFIX}/story/${id}`,
|
||||||
|
|
||||||
|
// Story Item 相关
|
||||||
|
ITEM: `${API_PREFIX}/story/item`,
|
||||||
|
ITEM_LIST: `${API_PREFIX}/story/item/list`,
|
||||||
|
ITEM_DETAIL: (id: string) => `${API_PREFIX}/story/item/${id}`,
|
||||||
|
ITEM_COUNT: (storyInstanceId: string) => `${API_PREFIX}/story/item/count/${storyInstanceId}`,
|
||||||
|
ITEM_IMAGES: (itemId: string) => `${API_PREFIX}/story/item/images/${itemId}`,
|
||||||
|
ITEM_SEARCH: `${API_PREFIX}/story/item/search`,
|
||||||
|
|
||||||
|
// 权限相关
|
||||||
|
PERMISSION: `${API_PREFIX}/story/permission`,
|
||||||
|
PERMISSION_AUTHORIZE: `${API_PREFIX}/story/permission/authorize`,
|
||||||
|
PERMISSION_STORY: (storyId: string) => `${API_PREFIX}/story/permission/story/${storyId}`,
|
||||||
|
PERMISSION_INVITE: `${API_PREFIX}/story/permission/invite`,
|
||||||
|
PERMISSION_INVITE_ACCEPT: (inviteId: string) => `${API_PREFIX}/story/permission/invite/${inviteId}/accept`,
|
||||||
|
PERMISSION_INVITE_REJECT: (inviteId: string) => `${API_PREFIX}/story/permission/invite/${inviteId}/reject`,
|
||||||
|
|
||||||
|
// 活动相关
|
||||||
|
ACTIVITY_AUTHORIZED_ITEMS: `${API_PREFIX}/story/activity/authorized-items`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 文件相关接口
|
||||||
|
export const FILE_API = {
|
||||||
|
IMAGE: (imageInstanceId: string) => `${API_PREFIX}/file/image/${imageInstanceId}`,
|
||||||
|
IMAGE_LOW_RES: (imageInstanceId: string) => `${API_PREFIX}/file/image-low-res/${imageInstanceId}`,
|
||||||
|
IMAGE_LIST: `${API_PREFIX}/file/image/list`,
|
||||||
|
VIDEO_URL: (videoInstanceId: string) => `${API_PREFIX}/file/get-video-url/${videoInstanceId}`,
|
||||||
|
UPLOAD_IMAGE: `${API_PREFIX}/file/upload-image`,
|
||||||
|
GET_UPLOAD_URL: (fileName: string) => `${API_PREFIX}/file/get-upload-url/${fileName}`,
|
||||||
|
UPLOADED: `${API_PREFIX}/file/uploaded`,
|
||||||
|
BATCH_INFO: `${API_PREFIX}/file/batch-info`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 用户相关接口
|
||||||
|
export const USER_API = {
|
||||||
|
PROFILE: `${USER_API_PREFIX}/profile`,
|
||||||
|
PROFILE_COVER: `${USER_API_PREFIX}/profile/cover`,
|
||||||
|
PROFILE_CUSTOM_FIELDS: `${USER_API_PREFIX}/profile/custom-fields`,
|
||||||
|
|
||||||
|
PREFERENCES: `${USER_API_PREFIX}/preferences`,
|
||||||
|
PREFERENCES_THEME: `${USER_API_PREFIX}/preferences/theme`,
|
||||||
|
PREFERENCES_LAYOUT: `${USER_API_PREFIX}/preferences/layout`,
|
||||||
|
PREFERENCES_TIMELINE: `${USER_API_PREFIX}/preferences/timeline`,
|
||||||
|
|
||||||
|
ALBUMS: `${USER_API_PREFIX}/albums`,
|
||||||
|
ALBUM_DETAIL: (id: string) => `${USER_API_PREFIX}/albums/${id}`,
|
||||||
|
ALBUM_PHOTOS: (id: string) => `${USER_API_PREFIX}/albums/${id}/photos`,
|
||||||
|
ALBUM_PHOTOS_ORDER: (id: string) => `${USER_API_PREFIX}/albums/${id}/photos/order`,
|
||||||
|
ALBUM_COVER: (id: string) => `${USER_API_PREFIX}/albums/${id}/cover`,
|
||||||
|
|
||||||
|
COLLECTIONS: `${USER_API_PREFIX}/collections`,
|
||||||
|
COLLECTIONS_SMART: `${USER_API_PREFIX}/collections/smart`,
|
||||||
|
COLLECTIONS_SMART_CONTENT: (collectionId: string) => `${USER_API_PREFIX}/collections/smart/${collectionId}/content`,
|
||||||
|
COLLECTIONS_SMART_REFRESH: `${USER_API_PREFIX}/collections/smart/refresh`,
|
||||||
|
|
||||||
|
// Gallery 相关
|
||||||
|
GALLERY_PHOTOS: `${API_PREFIX}/v1/gallery/photos`,
|
||||||
|
|
||||||
|
// 通知相关
|
||||||
|
NOTIFICATIONS_READ: `${API_PREFIX}/user/notifications/read`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 用户认证相关接口
|
||||||
|
export const AUTH_API = {
|
||||||
|
INFO: `${USER_AUTH_PREFIX}/info`,
|
||||||
|
REGISTER: `${USER_AUTH_PREFIX}/auth/register`,
|
||||||
|
LOGIN: `${USER_AUTH_PREFIX}/auth/login`,
|
||||||
|
LOGOUT: `${USER_AUTH_PREFIX}/auth/logout`,
|
||||||
|
|
||||||
|
// 好友相关
|
||||||
|
FRIEND_SEARCH: `${USER_AUTH_PREFIX}/search`,
|
||||||
|
FRIEND_REQUEST: `${USER_AUTH_PREFIX}/friend/request`,
|
||||||
|
FRIEND_LIST: `${USER_AUTH_PREFIX}/friend/list`,
|
||||||
|
FRIEND_ACCEPT: `${USER_AUTH_PREFIX}/friend/accept`,
|
||||||
|
FRIEND_REJECT: `${USER_AUTH_PREFIX}/friend/reject`,
|
||||||
|
MESSAGE_HISTORY: `${USER_AUTH_PREFIX}/message/history/friend`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 社交功能接口
|
||||||
|
export const SOCIAL_API = {
|
||||||
|
COMMENTS: `${API_PREFIX}/v1/comments`,
|
||||||
|
COMMENTS_DETAIL: (id: string) => `${API_PREFIX}/v1/comments/${id}`,
|
||||||
|
COMMENTS_BY_ENTITY: (entityType: string, entityId: string) => `${API_PREFIX}/v1/comments/${entityType}/${entityId}`,
|
||||||
|
|
||||||
|
REACTIONS: `${API_PREFIX}/v1/reactions`,
|
||||||
|
REACTIONS_BY_ENTITY: (entityType: string, entityId: string) => `${API_PREFIX}/v1/reactions/${entityType}/${entityId}`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 同步相关接口
|
||||||
|
export const SYNC_API = {
|
||||||
|
BASE: `${API_PREFIX}/v1/sync`,
|
||||||
|
CHANGES: `${API_PREFIX}/v1/sync/changes`,
|
||||||
|
STATUS: `${API_PREFIX}/v1/sync/status`,
|
||||||
|
RESOLVE_CONFLICT: `${API_PREFIX}/v1/sync/resolve-conflict`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 统计相关接口
|
||||||
|
export const STATISTICS_API = {
|
||||||
|
BASE: `${API_PREFIX}/v1/statistics`,
|
||||||
|
OVERVIEW: `${API_PREFIX}/v1/statistics/overview`,
|
||||||
|
UPLOADS: `${API_PREFIX}/v1/statistics/uploads`,
|
||||||
|
STORAGE: `${API_PREFIX}/v1/statistics/storage`,
|
||||||
|
REFRESH: `${API_PREFIX}/v1/statistics/refresh`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 地理信息接口
|
||||||
|
export const GEO_API = {
|
||||||
|
PROVINCE: `${API_PREFIX}/geographic/province`,
|
||||||
|
CITY: (province: string) => `${API_PREFIX}/geographic/city/${province}`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 其他通用接口
|
||||||
|
export const COMMON_API = {
|
||||||
|
USERS: `${API_PREFIX}/users`,
|
||||||
|
} as const;
|
||||||
@@ -1,22 +1,24 @@
|
|||||||
import {request} from "@@/exports";
|
import {request} from "@@/exports";
|
||||||
import {CommonListResponse, CommonResponse} from "@/types/common";
|
import {CommonListResponse, CommonResponse} from "@/types/common";
|
||||||
import {ImageItem} from "@/pages/gallery/typings";
|
import {ImageItem} from "@/pages/gallery/typings";
|
||||||
|
import { STORY_API, FILE_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
// 查询storyItem图片列表
|
// 查询storyItem图片列表
|
||||||
export async function queryStoryItemImages(itemId: string): Promise<{ data: string[] }> {
|
export async function queryStoryItemImages(itemId: string): Promise<{ data: string[] }> {
|
||||||
return request(`/api/story/item/images/${itemId}`, {
|
return request(STORY_API.ITEM_IMAGES(itemId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 获取图片
|
// 获取图片
|
||||||
export async function fetchImage(imageInstanceId: string): Promise<any> {
|
export async function fetchImage(imageInstanceId: string): Promise<any> {
|
||||||
return request(`/file/image/${imageInstanceId}`, {
|
return request(FILE_API.IMAGE(imageInstanceId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
getResponse: true,
|
getResponse: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function fetchImageLowRes(imageInstanceId: string): Promise<any> {
|
export async function fetchImageLowRes(imageInstanceId: string): Promise<any> {
|
||||||
return request(`/file/image-low-res/${imageInstanceId}`, {
|
return request(FILE_API.IMAGE_LOW_RES(imageInstanceId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
getResponse: true,
|
getResponse: true,
|
||||||
@@ -32,7 +34,7 @@ export async function getImagesList(
|
|||||||
},
|
},
|
||||||
options?: { [key: string]: any },
|
options?: { [key: string]: any },
|
||||||
) {
|
) {
|
||||||
return request<CommonResponse<CommonListResponse<ImageItem>>>('/file/image/list', {
|
return request<CommonResponse<CommonListResponse<ImageItem>>>(FILE_API.IMAGE_LIST, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
@@ -42,7 +44,7 @@ export async function getImagesList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteImage(params: { instanceId: string }) {
|
export async function deleteImage(params: { instanceId: string }) {
|
||||||
return request<CommonResponse<any>>(`/file/image/${params.instanceId}`, {
|
return request<CommonResponse<any>>(FILE_API.IMAGE(params.instanceId), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
params: {
|
params: {
|
||||||
...params,
|
...params,
|
||||||
@@ -50,33 +52,33 @@ export async function deleteImage(params: { instanceId: string }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function uploadImage(params: FormData) {
|
export async function uploadImage(params: FormData) {
|
||||||
return request<CommonResponse<any>>('/file/upload-image', {
|
return request<CommonResponse<any>>(FILE_API.UPLOAD_IMAGE, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUploadUrl(fileName: string) {
|
export async function getUploadUrl(fileName: string) {
|
||||||
return request<CommonResponse<string>>(`/file/get-upload-url/${fileName}`, {
|
return request<CommonResponse<string>>(FILE_API.GET_UPLOAD_URL(fileName), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVideoUrl(instanceId: string): Promise<CommonResponse<string>> {
|
export async function getVideoUrl(instanceId: string): Promise<CommonResponse<string>> {
|
||||||
return request<CommonResponse<string>>(`/file/get-video-url/${instanceId}`, {
|
return request<CommonResponse<string>>(FILE_API.VIDEO_URL(instanceId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveFileMetadata(data: any) {
|
export async function saveFileMetadata(data: any) {
|
||||||
return request<CommonResponse<string>>('/file/uploaded', {
|
return request<CommonResponse<string>>(FILE_API.UPLOADED, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function batchGetFileInfo(instanceIds: string[]): Promise<CommonResponse<any[]>> {
|
export async function batchGetFileInfo(instanceIds: string[]): Promise<CommonResponse<any[]>> {
|
||||||
return request<CommonResponse<any[]>>('/file/batch-info', {
|
return request<CommonResponse<any[]>>(FILE_API.BATCH_INFO, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: instanceIds,
|
data: instanceIds,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { USER_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/preferences';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user preferences
|
* Get user preferences
|
||||||
*/
|
*/
|
||||||
export async function getPreferences(): Promise<API.UserPreferences> {
|
export async function getPreferences(): Promise<API.UserPreferences> {
|
||||||
return request(`${BASE_URL}`, {
|
return request(USER_API.PREFERENCES, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -20,7 +19,7 @@ export async function getPreferences(): Promise<API.UserPreferences> {
|
|||||||
* Update theme preferences
|
* Update theme preferences
|
||||||
*/
|
*/
|
||||||
export async function updateThemePreferences(data: API.UpdateThemeDTO): Promise<void> {
|
export async function updateThemePreferences(data: API.UpdateThemeDTO): Promise<void> {
|
||||||
return request(`${BASE_URL}/theme`, {
|
return request(USER_API.PREFERENCES_THEME, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -30,7 +29,7 @@ export async function updateThemePreferences(data: API.UpdateThemeDTO): Promise<
|
|||||||
* Update layout preferences
|
* Update layout preferences
|
||||||
*/
|
*/
|
||||||
export async function updateLayoutPreferences(data: API.UpdateLayoutDTO): Promise<void> {
|
export async function updateLayoutPreferences(data: API.UpdateLayoutDTO): Promise<void> {
|
||||||
return request(`${BASE_URL}/layout`, {
|
return request(USER_API.PREFERENCES_LAYOUT, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -40,7 +39,7 @@ export async function updateLayoutPreferences(data: API.UpdateLayoutDTO): Promis
|
|||||||
* Update timeline preferences
|
* Update timeline preferences
|
||||||
*/
|
*/
|
||||||
export async function updateTimelinePreferences(data: API.UpdateTimelineDTO): Promise<void> {
|
export async function updateTimelinePreferences(data: API.UpdateTimelineDTO): Promise<void> {
|
||||||
return request(`${BASE_URL}/timeline`, {
|
return request(USER_API.PREFERENCES_TIMELINE, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { USER_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/profile';
|
|
||||||
|
|
||||||
export interface UserProfile {
|
export interface UserProfile {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -32,7 +31,7 @@ export interface ProfileData {
|
|||||||
* Get user profile
|
* Get user profile
|
||||||
*/
|
*/
|
||||||
export async function getProfile(): Promise<ProfileData> {
|
export async function getProfile(): Promise<ProfileData> {
|
||||||
return request(`${BASE_URL}`, {
|
return request(USER_API.PROFILE, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -41,7 +40,7 @@ export async function getProfile(): Promise<ProfileData> {
|
|||||||
* Update profile bio
|
* Update profile bio
|
||||||
*/
|
*/
|
||||||
export async function updateProfile(data: { bio?: string }): Promise<void> {
|
export async function updateProfile(data: { bio?: string }): Promise<void> {
|
||||||
return request(`${BASE_URL}`, {
|
return request(USER_API.PROFILE, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -53,8 +52,8 @@ export async function updateProfile(data: { bio?: string }): Promise<void> {
|
|||||||
export async function uploadCoverPhoto(file: File): Promise<string> {
|
export async function uploadCoverPhoto(file: File): Promise<string> {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
|
|
||||||
return request(`${BASE_URL}/cover`, {
|
return request(USER_API.PROFILE_COVER, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: formData,
|
data: formData,
|
||||||
});
|
});
|
||||||
@@ -64,7 +63,7 @@ export async function uploadCoverPhoto(file: File): Promise<string> {
|
|||||||
* Update custom fields
|
* Update custom fields
|
||||||
*/
|
*/
|
||||||
export async function updateCustomFields(fields: CustomField[]): Promise<void> {
|
export async function updateCustomFields(fields: CustomField[]): Promise<void> {
|
||||||
return request(`${BASE_URL}/custom-fields`, {
|
return request(USER_API.PROFILE_CUSTOM_FIELDS, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: { fields },
|
data: { fields },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { SOCIAL_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/reactions';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get reactions for an entity
|
* Get reactions for an entity
|
||||||
@@ -14,7 +13,7 @@ export async function getReactions(
|
|||||||
entityType: API.SocialEntityType,
|
entityType: API.SocialEntityType,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
): Promise<API.ReactionSummary> {
|
): Promise<API.ReactionSummary> {
|
||||||
return request(`${BASE_URL}/${entityType}/${entityId}`, {
|
return request(SOCIAL_API.REACTIONS_BY_ENTITY(entityType, entityId), {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -23,7 +22,7 @@ export async function getReactions(
|
|||||||
* Add or update a reaction
|
* Add or update a reaction
|
||||||
*/
|
*/
|
||||||
export async function addReaction(data: API.AddReactionDTO): Promise<void> {
|
export async function addReaction(data: API.AddReactionDTO): Promise<void> {
|
||||||
return request(`${BASE_URL}`, {
|
return request(SOCIAL_API.REACTIONS, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
@@ -36,7 +35,7 @@ export async function removeReaction(
|
|||||||
entityType: API.SocialEntityType,
|
entityType: API.SocialEntityType,
|
||||||
entityId: string,
|
entityId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return request(`${BASE_URL}/${entityType}/${entityId}`, {
|
return request(SOCIAL_API.REACTIONS_BY_ENTITY(entityType, entityId), {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { STATISTICS_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/statistics';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user statistics overview
|
* Get user statistics overview
|
||||||
*/
|
*/
|
||||||
export async function getStatisticsOverview(): Promise<API.UserStatistics> {
|
export async function getStatisticsOverview(): Promise<API.UserStatistics> {
|
||||||
return request(`${BASE_URL}/overview`, {
|
return request(STATISTICS_API.OVERVIEW, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -20,7 +19,7 @@ export async function getStatisticsOverview(): Promise<API.UserStatistics> {
|
|||||||
* Get upload trends
|
* Get upload trends
|
||||||
*/
|
*/
|
||||||
export async function getUploadTrends(): Promise<API.UploadTrend[]> {
|
export async function getUploadTrends(): Promise<API.UploadTrend[]> {
|
||||||
return request(`${BASE_URL}/uploads`, {
|
return request(STATISTICS_API.UPLOADS, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -29,7 +28,7 @@ export async function getUploadTrends(): Promise<API.UploadTrend[]> {
|
|||||||
* Get storage breakdown
|
* Get storage breakdown
|
||||||
*/
|
*/
|
||||||
export async function getStorageBreakdown(): Promise<API.StorageBreakdown> {
|
export async function getStorageBreakdown(): Promise<API.StorageBreakdown> {
|
||||||
return request(`${BASE_URL}/storage`, {
|
return request(STATISTICS_API.STORAGE, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -38,7 +37,7 @@ export async function getStorageBreakdown(): Promise<API.StorageBreakdown> {
|
|||||||
* Force refresh statistics
|
* Force refresh statistics
|
||||||
*/
|
*/
|
||||||
export async function refreshStatistics(): Promise<void> {
|
export async function refreshStatistics(): Promise<void> {
|
||||||
return request(`${BASE_URL}/refresh`, {
|
return request(STATISTICS_API.REFRESH, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { SYNC_API } from '@/services/config/apiUrls';
|
||||||
const BASE_URL = '/api/v1/sync';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload offline changes batch
|
* Upload offline changes batch
|
||||||
*/
|
*/
|
||||||
export async function uploadChanges(changes: API.ChangeRecord[]): Promise<API.SyncResult> {
|
export async function uploadChanges(changes: API.ChangeRecord[]): Promise<API.SyncResult> {
|
||||||
return request(`${BASE_URL}/changes`, {
|
return request(SYNC_API.CHANGES, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: { changes },
|
data: { changes },
|
||||||
});
|
});
|
||||||
@@ -21,7 +20,7 @@ export async function uploadChanges(changes: API.ChangeRecord[]): Promise<API.Sy
|
|||||||
* Get sync status
|
* Get sync status
|
||||||
*/
|
*/
|
||||||
export async function getSyncStatus(): Promise<{ status: API.SyncStatus; pendingCount: number }> {
|
export async function getSyncStatus(): Promise<{ status: API.SyncStatus; pendingCount: number }> {
|
||||||
return request(`${BASE_URL}/status`, {
|
return request(SYNC_API.STATUS, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -30,7 +29,7 @@ export async function getSyncStatus(): Promise<{ status: API.SyncStatus; pending
|
|||||||
* Resolve sync conflict
|
* Resolve sync conflict
|
||||||
*/
|
*/
|
||||||
export async function resolveConflict(resolution: API.Resolution): Promise<void> {
|
export async function resolveConflict(resolution: API.Resolution): Promise<void> {
|
||||||
return request(`${BASE_URL}/resolve-conflict`, {
|
return request(SYNC_API.RESOLVE_CONFLICT, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: resolution,
|
data: resolution,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { CommonResponse } from "@/types/common";
|
import { CommonResponse } from "@/types/common";
|
||||||
import {request} from "@@/exports";
|
import {request} from "@@/exports";
|
||||||
|
import { AUTH_API } from '@/services/config/apiUrls';
|
||||||
|
|
||||||
|
|
||||||
export async function registerUser(params: UserRegisterParams, options?: { skipErrorHandler?: boolean }): Promise<CommonResponse<string>> {
|
export async function registerUser(params: UserRegisterParams, options?: { skipErrorHandler?: boolean }): Promise<CommonResponse<string>> {
|
||||||
return request('/user-api/auth/register', {
|
return request(AUTH_API.REGISTER, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
getResponse: true,
|
getResponse: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function loginUser(params: UserLoginParams): Promise<CommonResponse<UserLoginResult>> {
|
export async function loginUser(params: UserLoginParams): Promise<CommonResponse<UserLoginResult>> {
|
||||||
return request('/user-api/auth/login', {
|
return request(AUTH_API.LOGIN, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
// getResponse: true,
|
// getResponse: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export async function logoutUser() {
|
export async function logoutUser() {
|
||||||
return request('/user-api/auth/logout', {
|
return request(AUTH_API.LOGOUT, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/services/user/typing.d.ts
vendored
10
src/services/user/typing.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
// 注册用户参数
|
// 注册用户参数
|
||||||
interface UserRegisterParams {
|
export interface UserRegisterParams {
|
||||||
username: string;
|
username: string;
|
||||||
nickname: string;
|
nickname: string;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -7,25 +7,25 @@ interface UserRegisterParams {
|
|||||||
phone: string;
|
phone: string;
|
||||||
}
|
}
|
||||||
// 登录用户参数
|
// 登录用户参数
|
||||||
interface UserLoginParams {
|
export interface UserLoginParams {
|
||||||
username?: string;
|
username?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
loginType?: string;
|
loginType?: string;
|
||||||
}
|
}
|
||||||
// 退出登录参数
|
// 退出登录参数
|
||||||
interface UserLogoutParams {
|
export interface UserLogoutParams {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
// 登录返回参数
|
// 登录返回参数
|
||||||
interface UserLoginResult {
|
export interface UserLoginResult {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
accessTokenExpiresInSeconds: number;
|
accessTokenExpiresInSeconds: number;
|
||||||
refreshTokenExpiresInSeconds: number;
|
refreshTokenExpiresInSeconds: number;
|
||||||
}
|
}
|
||||||
// 用户信息
|
// 用户信息
|
||||||
interface UserInfo {
|
export interface UserInfo {
|
||||||
username: string,
|
username: string,
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
|
|||||||
26
src/typings.d.ts
vendored
26
src/typings.d.ts
vendored
@@ -19,17 +19,17 @@ declare module 'react-masonry-css';
|
|||||||
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
|
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;
|
||||||
|
|
||||||
// Extend Namespaces type to include 'theme'
|
// Extend Namespaces type to include 'theme'
|
||||||
declare module '@umijs/max' {
|
// declare module '@umijs/max' {
|
||||||
export type Namespaces =
|
// export type Namespaces =
|
||||||
| 'menu'
|
// | 'menu'
|
||||||
| 'settings'
|
// | 'settings'
|
||||||
| 'pages'
|
// | 'pages'
|
||||||
| 'component'
|
// | 'component'
|
||||||
| 'globalHeader'
|
// | 'globalHeader'
|
||||||
| 'theme'
|
// | 'theme'
|
||||||
| 'sync'
|
// | 'sync'
|
||||||
| 'albums'
|
// | 'albums'
|
||||||
| 'collections'
|
// | 'collections'
|
||||||
| 'layout';
|
// | 'layout';
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class OfflineDatabase extends Dexie {
|
|||||||
super('TimelineOffline');
|
super('TimelineOffline');
|
||||||
|
|
||||||
// Define database schema
|
// Define database schema
|
||||||
// Version 1: Initial schema
|
// Version 2: Added synced index to changes table
|
||||||
this.version(1).stores({
|
this.version(2).stores({
|
||||||
// Stories table with indexes
|
// Stories table with indexes
|
||||||
// Primary key: id
|
// Primary key: id
|
||||||
// Indexes: userId, createdAt, syncStatus for efficient querying
|
// Indexes: userId, createdAt, syncStatus for efficient querying
|
||||||
@@ -34,9 +34,8 @@ export class OfflineDatabase extends Dexie {
|
|||||||
|
|
||||||
// Changes table with auto-incrementing primary key
|
// Changes table with auto-incrementing primary key
|
||||||
// Primary key: ++id (auto-increment)
|
// Primary key: ++id (auto-increment)
|
||||||
// Indexes: entityType, entityId, operation, timestamp
|
// Indexes: entityType, entityId, operation, timestamp, synced
|
||||||
// Note: synced is stored as 0/1 for IndexedDB compatibility but not indexed
|
changes: '++id, entityType, entityId, operation, timestamp, synced',
|
||||||
changes: '++id, entityType, entityId, operation, timestamp',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Map tables to TypeScript types
|
// Map tables to TypeScript types
|
||||||
|
|||||||
@@ -18,19 +18,25 @@ import { message } from 'antd';
|
|||||||
export class SyncManager {
|
export class SyncManager {
|
||||||
private queue: API.ChangeRecord[] = [];
|
private queue: API.ChangeRecord[] = [];
|
||||||
private syncing: boolean = false;
|
private syncing: boolean = false;
|
||||||
private online: boolean = navigator.onLine;
|
private online: boolean = typeof window !== 'undefined' ? navigator.onLine : true;
|
||||||
private syncListeners: Array<(status: API.SyncStatus) => void> = [];
|
private syncListeners: Array<(status: API.SyncStatus) => void> = [];
|
||||||
private autoSyncEnabled: boolean = true;
|
private autoSyncEnabled: boolean = true;
|
||||||
|
private initialized: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initializeEventListeners();
|
// Defer initialization to avoid SSR issues
|
||||||
this.loadQueueFromDB();
|
if (typeof window !== 'undefined') {
|
||||||
|
this.initializeEventListeners();
|
||||||
|
this.loadQueueFromDB();
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize online/offline event listeners
|
* Initialize online/offline event listeners
|
||||||
*/
|
*/
|
||||||
private initializeEventListeners(): void {
|
private initializeEventListeners(): void {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
window.addEventListener('online', () => this.onOnline());
|
window.addEventListener('online', () => this.onOnline());
|
||||||
window.addEventListener('offline', () => this.onOffline());
|
window.addEventListener('offline', () => this.onOffline());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user