From bd72f60db8ec7c91b2d3287d03b42a1234208d90 Mon Sep 17 00:00:00 2001 From: yourname Date: Tue, 13 May 2025 04:21:50 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E5=B0=86=E6=B5=8B=E8=AF=95=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=BB=9F=E4=B8=80=E5=88=B0=20test/=20=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/admin/pages_know_info.test.tsx | 543 ---------------------- client/admin/pages_theme_setting.test.tsx | 115 ----- 2 files changed, 658 deletions(-) delete mode 100644 client/admin/pages_know_info.test.tsx delete mode 100644 client/admin/pages_theme_setting.test.tsx diff --git a/client/admin/pages_know_info.test.tsx b/client/admin/pages_know_info.test.tsx deleted file mode 100644 index 9f76896..0000000 --- a/client/admin/pages_know_info.test.tsx +++ /dev/null @@ -1,543 +0,0 @@ -import { JSDOM } from 'jsdom' -import React from 'react' -import {render, waitFor, within, fireEvent} from '@testing-library/react' -import {userEvent} from '@testing-library/user-event' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { createBrowserRouter, RouterProvider } from 'react-router' -import { - assertEquals, - assertExists, - assertNotEquals, - assertRejects, - assert, -} from "https://deno.land/std@0.217.0/assert/mod.ts"; -import axios from 'axios'; -import { KnowInfoPage } from "./pages_know_info.tsx" -import { AuthProvider } from './hooks_sys.tsx' -import { ProtectedRoute } from './components_protected_route.tsx' - -// 拦截React DOM中的attachEvent和detachEvent错误 -const originalError = console.error; -console.error = (...args) => { - // 过滤掉attachEvent和detachEvent相关的错误 - if (args[0] instanceof Error) { - if (args[0].message?.includes('attachEvent is not a function') || - args[0].message?.includes('detachEvent is not a function')) { - return; // 不输出这些错误 - } - } else if (typeof args[0] === 'string') { - if (args[0].includes('attachEvent is not a function') || - args[0].includes('detachEvent is not a function')) { - return; // 不输出这些错误 - } - } - originalError(...args); -}; - -// 应用入口组件 -const App = () => { - // 路由配置 - const router = createBrowserRouter([ - { - path: '/', - element: ( - - - - ) - }, - ]); - return -}; -// setup function -function setup() { - - const dom = new JSDOM(``, { - runScripts: "dangerously", - pretendToBeVisual: true, - url: "http://localhost", - }); - - // 模拟浏览器环境 - globalThis.window = dom.window; - globalThis.document = dom.window.document; - - // 添加必要的 DOM 配置 - globalThis.Node = dom.window.Node; - globalThis.Document = dom.window.Document; - globalThis.HTMLInputElement = dom.window.HTMLInputElement; - globalThis.HTMLButtonElement = dom.window.HTMLButtonElement; - - // 定义浏览器环境所需的类 - globalThis.Element = dom.window.Element; - globalThis.HTMLElement = dom.window.HTMLElement; - globalThis.ShadowRoot = dom.window.ShadowRoot; - globalThis.SVGElement = dom.window.SVGElement; - - - - // 模拟 getComputedStyle - globalThis.getComputedStyle = (elt) => { - const style = new dom.window.CSSStyleDeclaration(); - style.getPropertyValue = () => ''; - return style; - }; - - // 模拟matchMedia函数 - globalThis.matchMedia = (query) => ({ - matches: query.includes('max-width'), - media: query, - onchange: null, - addListener: () => {}, - removeListener: () => {}, - addEventListener: () => {}, - removeEventListener: () => {}, - dispatchEvent: () => false, - }); - - // 模拟动画相关API - globalThis.AnimationEvent = globalThis.AnimationEvent || dom.window.Event; - globalThis.TransitionEvent = globalThis.TransitionEvent || dom.window.Event; - - // 模拟requestAnimationFrame - globalThis.requestAnimationFrame = globalThis.requestAnimationFrame || ((cb) => setTimeout(cb, 0)); - globalThis.cancelAnimationFrame = globalThis.cancelAnimationFrame || clearTimeout; - - // 设置浏览器尺寸相关方法 - window.resizeTo = (width, height) => { - window.innerWidth = width || window.innerWidth; - window.innerHeight = height || window.innerHeight; - window.dispatchEvent(new Event('resize')); - }; - window.scrollTo = () => {}; - - - const customScreen = within(document.body); - - const user = userEvent.setup({ - document: dom.window.document, - delay: 10, - skipAutoClose: true, - }); - - localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwidXNlcm5hbWUiOiJhZG1pbiIsInNlc3Npb25JZCI6Ijk4T2lzTW5SMm0zQ0dtNmo4SVZrNyIsInJvbGVJbmZvIjpudWxsLCJpYXQiOjE3NDQzNjIzNTUsImV4cCI6MTc0NDQ0ODc1NX0.k1Ld7qWAZmdzsbjmrl_0ec1FqF_GimaOuQIic4znRtc'); - - axios.defaults.baseURL = 'https://23957.dev.d8dcloud.com' - - const queryClient = new QueryClient() - - return { - user, - // Import `render` from the framework library of your choice. - // See https://testing-library.com/docs/dom-testing-library/install#wrappers - ...render( - - - - - - ), - } -} - -// // 使用异步测试处理组件渲染 -// Deno.test({ -// name: '知识库管理页面基础测试', -// fn: async (t) => { -// // 存储所有需要清理的定时器 -// const timers: number[] = []; -// const originalSetTimeout = globalThis.setTimeout; -// const originalSetInterval = globalThis.setInterval; - -// // 重写定时器方法以跟踪所有创建的定时器 -// globalThis.setTimeout = ((callback, delay, ...args) => { -// const id = originalSetTimeout(callback, delay, ...args); -// timers.push(id); -// return id; -// }) as typeof setTimeout; - -// globalThis.setInterval = ((callback, delay, ...args) => { -// const id = originalSetInterval(callback, delay, ...args); -// timers.push(id); -// return id; -// }) as typeof setInterval; - -// // 清理函数 -// const cleanup = () => { -// for (const id of timers) { -// clearTimeout(id); -// clearInterval(id); -// } -// // 恢复原始定时器方法 -// globalThis.setTimeout = originalSetTimeout; -// globalThis.setInterval = originalSetInterval; -// }; - - - -// try { - - -// // // 渲染组件 -// // const { -// // findByText, findByPlaceholderText, queryByText, -// // findByRole, findAllByRole, findByLabelText, findAllByText, debug, -// // queryByRole - -// // } = render( -// // -// // -// // -// // -// // -// // ); - -// // 测试1: 基本渲染 -// await t.step('应正确渲染页面元素', async () => { -// const { findByText } = setup() -// await waitFor(async () => { -// const title = await findByText(/知识库管理/i); -// assertExists(title, '未找到知识库管理标题'); -// }, { -// timeout: 1000 * 5, -// }); -// }); - -// // 初始加载表格数据 -// await t.step('初始加载表格数据', async () => { -// const { findByRole } = setup() -// await waitFor(async () => { -// const table = await findByRole('table'); -// const rows = await within(table).findAllByRole('row'); - -// // 应该大于2行 -// assert(rows.length > 2, '表格没有数据'); // 1是表头行 2是数据行 - -// }, { -// timeout: 1000 * 5, -// }); -// }); - -// // 测试2: 搜索表单功能 -// await t.step('搜索表单应正常工作', async () => { -// const {findByPlaceholderText, findByText, findByRole, user} = setup() - -// // 等待知识库管理标题出现 -// await waitFor(async () => { -// const title = await findByText(/知识库管理/i); -// assertExists(title, '未找到知识库管理标题'); -// }, { -// timeout: 1000 * 5, -// }); - -// // 直接查找标题搜索输入框和搜索按钮 -// const searchInput = await findByPlaceholderText(/请输入文章标题/i) as HTMLInputElement; -// const searchButton = await findByText(/搜 索/i); - -// assertExists(searchInput, '未找到搜索输入框'); -// assertExists(searchButton, '未找到搜索按钮'); - -// // 输入搜索内容 -// await user.type(searchInput, '数据分析') -// assertEquals(searchInput.value, '数据分析', '搜索输入框值未更新'); - -// // 提交搜索 -// await user.click(searchButton); - - -// let rows: HTMLElement[] = []; - - -// const table = await findByRole('table'); -// assertExists(table, '未找到数据表格'); - -// // 等待表格刷新并验证 -// await waitFor(async () => { -// rows = await within(table).findAllByRole('row'); -// assert(rows.length === 2, '表格未刷新'); -// }, { -// timeout: 1000 * 5, -// onTimeout: () => new Error('等待表格刷新超时') -// }); - -// // 等待搜索结果并验证 -// await waitFor(async () => { -// rows = await within(table).findAllByRole('row'); -// assert(rows.length > 2, '表格没有数据'); -// }, { -// timeout: 1000 * 5, -// onTimeout: () => new Error('等待搜索结果超时') -// }); - - - -// // 检查至少有一行包含"数据分析" -// const matchResults = await Promise.all(rows.map(async row => { -// try{ -// const cells = await within(row).findAllByRole('cell'); -// return cells.some(cell => { -// return cell.textContent?.includes('数据分析') -// }); -// } catch (error: unknown) { -// // console.error('搜索结果获取失败', error) -// return false -// } -// })) -// // console.log('matchResults', matchResults) -// const hasMatch = matchResults.some(result => result); - -// assert(hasMatch, '搜索结果中没有找到包含"数据分析"的文章'); -// }); - -// // 测试3: 表格数据加载 -// await t.step('表格应加载并显示数据', async () => { -// const {findByRole, queryByText} = setup() -// // 等待数据加载完成或表格出现,最多等待5秒 -// await waitFor(async () => { -// // 检查加载状态是否消失 -// const loading = queryByText(/正在加载数据/i); -// if (loading) { -// throw new Error('数据仍在加载中'); -// } - -// // 检查表格是否出现 -// const table = await findByRole('table'); -// assertExists(table, '未找到数据表格'); - -// // 检查表格是否有数据行 -// const rows = await within(table).findAllByRole('row'); -// assertNotEquals(rows.length, 1, '表格没有数据行'); // 1是表头行 -// }, { -// timeout: 5000, // 5秒超时 -// onTimeout: (error) => { -// return new Error(`数据加载超时: ${error.message}`); -// } -// }); -// }); - -// // 测试4: 添加文章功能 -// await t.step('应能打开添加文章模态框', async () => { -// const {findByText, findByRole, user} = setup() -// // 等待知识库管理标题出现 -// await waitFor(async () => { -// const title = await findByText(/知识库管理/i); -// assertExists(title, '未找到知识库管理标题'); -// }, { -// timeout: 1000 * 5, -// }); - -// const addButton = await findByText(/添加文章/i); -// assertExists(addButton, '未找到添加文章按钮'); - -// await user.click(addButton); - -// // 找到模态框 -// const modal = await findByRole('dialog'); -// assertExists(modal, '未找到模态框'); - -// const modalTitle = await within(modal).findByText(/添加知识库文章/i); -// assertExists(modalTitle, '未找到添加文章模态框'); - -// // 验证表单字段 -// const titleInput = await within(modal).findByPlaceholderText(/请输入文章标题/i); -// assertExists(titleInput, '未找到标题输入框'); - -// const contentInput = await within(modal).findByPlaceholderText(/请输入文章内容/i); -// assertExists(contentInput, '未找到文章内容输入框'); - -// // 取消 -// const cancelButton = await within(modal).findByText(/取 消/i); -// assertExists(cancelButton, '未找到取消按钮'); -// await user.click(cancelButton); - -// // 验证模态框是否关闭 -// await waitFor(async () => { - -// const modalTitle = await within(modal).findByText(/添加知识库文章/i); -// assertExists(!modalTitle, '模态框未关闭'); -// }, { -// timeout: 1000 * 5, -// onTimeout: () => new Error('等待模态框关闭超时') -// }); -// }); - -// } finally { -// // 确保清理所有定时器 -// cleanup(); -// } -// }, -// sanitizeOps: false, // 禁用操作清理检查 -// sanitizeResources: false, // 禁用资源清理检查 -// }); - -Deno.test({ - name: '知识库管理页面新增测试', - fn: async (t) => { - // 存储所有需要清理的定时器 - const timers: number[] = []; - const originalSetTimeout = globalThis.setTimeout; - const originalSetInterval = globalThis.setInterval; - - // 重写定时器方法以跟踪所有创建的定时器 - globalThis.setTimeout = ((callback, delay, ...args) => { - const id = originalSetTimeout(callback, delay, ...args); - timers.push(id); - return id; - }) as typeof setTimeout; - - globalThis.setInterval = ((callback, delay, ...args) => { - const id = originalSetInterval(callback, delay, ...args); - timers.push(id); - return id; - }) as typeof setInterval; - - // 清理函数 - const cleanup = () => { - for (const id of timers) { - clearTimeout(id); - clearInterval(id); - } - // 恢复原始定时器方法 - globalThis.setTimeout = originalSetTimeout; - globalThis.setInterval = originalSetInterval; - }; - - - - try { - - - // // 渲染组件 - // const { - // findByText, findByPlaceholderText, queryByText, - // findByRole, findAllByRole, findByLabelText, findAllByText, debug, - // queryByRole - - // } = render( - // - // - // - // - // - // ); - - - // 测试5: 完整添加文章流程 - await t.step('应能完整添加一篇文章', async () => { - const {findByText, findByRole, debug, user} = setup() - // 等待知识库管理标题出现 - await waitFor(async () => { - const title = await findByText(/知识库管理/i); - assertExists(title, '未找到知识库管理标题'); - }, { - timeout: 1000 * 5, - }); - // 打开添加模态框 - const addButton = await findByText(/添加文章/i); - assertExists(addButton, '未找到添加文章按钮'); - - await user.click(addButton); - - // 找到模态框 - const modal = await findByRole('dialog'); - assertExists(modal, '未找到模态框'); - - const modalTitle = await within(modal).findByText(/添加知识库文章/i); - assertExists(modalTitle, '未找到添加文章模态框'); - - // 确保上一个测试的输入框值不会影响这个测试 - // 在模态框内查找标题和内容输入框 - const titleInput = await within(modal).findByPlaceholderText(/请输入文章标题/i) as HTMLInputElement; - const contentInput = await within(modal).findByPlaceholderText(/请输入文章内容/i) as HTMLTextAreaElement; - const submitButton = await within(modal).findByText(/确 定/i); - - assertExists(titleInput, '未找到模态框中的标题输入框'); - assertExists(contentInput, '未找到文章内容输入框'); - assertExists(submitButton, '未找到提交按钮'); - - // 先清空输入框,以防止之前的测试影响 - // await user.clear(titleInput); - // await user.clear(contentInput); - - // 输入新值 - await user.type(titleInput, '测试文章标题'); - await user.type(contentInput, '这是测试文章内容'); - - debug(titleInput); - debug(contentInput); - debug(submitButton); - - // 提交表单 - await user.click(submitButton); - - // 验证表单字段 - assertEquals(titleInput.value, '测试文章标题', '模态框中标题输入框值未更新'); - assertEquals(contentInput.value, '这是测试文章内容', '内容输入框值未更新'); - - let rows: HTMLElement[] = []; - - - const table = await findByRole('table'); - assertExists(table, '未找到数据表格'); - - // 等待表格刷新并验证 - await waitFor(async () => { - rows = await within(table).findAllByRole('row'); - assert(rows.length === 2, '表格未刷新'); - }, { - timeout: 1000 * 5, - onTimeout: () => new Error('等待表格刷新超时') - }); - - // 等待搜索结果并验证 - await waitFor(async () => { - rows = await within(table).findAllByRole('row'); - assert(rows.length > 2, '表格没有数据'); - }, { - timeout: 1000 * 5, - onTimeout: () => new Error('等待搜索结果超时') - }); - - // 检查至少有一行包含"测试文章标题" - const matchResults = await Promise.all(rows.map(async row => { - try{ - const cells = await within(row).findAllByRole('cell'); - return cells.some(cell => { - return cell.textContent?.includes('测试文章标题') - }); - } catch (error: unknown) { - // console.error('搜索结果获取失败', error) - return false - } - })) - // console.log('matchResults', matchResults) - const hasMatch = matchResults.some(result => result); - - assert(hasMatch, '搜索结果中没有找到包含"测试文章标题"的文章'); - }); - - // // 测试5: 分页功能 - // await t.step('应显示分页控件', async () => { - // const pagination = await findByRole('navigation'); - // assertExists(pagination, '未找到分页控件'); - - // const pageItems = await findAllByRole('button', { name: /1|2|3|下一页|上一页/i }); - // assertNotEquals(pageItems.length, 0, '未找到分页按钮'); - // }); - - // // 测试6: 操作按钮 - // await t.step('应显示操作按钮', async () => { - // const editButtons = await findAllByText(/编辑/i); - // assertNotEquals(editButtons.length, 0, '未找到编辑按钮'); - - // const deleteButtons = await findAllByText(/删除/i); - // assertNotEquals(deleteButtons.length, 0, '未找到删除按钮'); - // }); - } finally { - // 确保清理所有定时器 - cleanup(); - } - }, - sanitizeOps: false, // 禁用操作清理检查 - sanitizeResources: false, // 禁用资源清理检查 -}); \ No newline at end of file diff --git a/client/admin/pages_theme_setting.test.tsx b/client/admin/pages_theme_setting.test.tsx deleted file mode 100644 index 3ed0d48..0000000 --- a/client/admin/pages_theme_setting.test.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { JSDOM } from 'jsdom' -import React from 'react' -import {render, fireEvent, within, screen} from '@testing-library/react' -import { ThemeSettingsPage } from "./pages_theme_settings.tsx" -import { ThemeProvider } from "./hooks_sys.tsx" -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { - assertEquals, - assertExists, - assertNotEquals, - assertRejects, -} from "https://deno.land/std@0.217.0/assert/mod.ts"; - -const queryClient = new QueryClient() - - -const dom = new JSDOM(``, { - runScripts: "dangerously", - pretendToBeVisual: true, - url: "http://localhost" -}); - -// 模拟浏览器环境 -globalThis.window = dom.window; -globalThis.document = dom.window.document; - -// 定义浏览器环境所需的类 -globalThis.Element = dom.window.Element; -globalThis.HTMLElement = dom.window.HTMLElement; -globalThis.ShadowRoot = dom.window.ShadowRoot; -globalThis.SVGElement = dom.window.SVGElement; - -// 模拟 getComputedStyle -globalThis.getComputedStyle = (elt) => { - const style = new dom.window.CSSStyleDeclaration(); - style.getPropertyValue = () => ''; - return style; -}; - -// 模拟matchMedia函数 -globalThis.matchMedia = (query) => ({ - matches: query.includes('max-width'), - media: query, - onchange: null, - addListener: () => {}, - removeListener: () => {}, - addEventListener: () => {}, - removeEventListener: () => {}, - dispatchEvent: () => false, -}); - -// 模拟动画相关API -globalThis.AnimationEvent = globalThis.AnimationEvent || dom.window.Event; -globalThis.TransitionEvent = globalThis.TransitionEvent || dom.window.Event; - -// 模拟requestAnimationFrame -globalThis.requestAnimationFrame = globalThis.requestAnimationFrame || ((cb) => setTimeout(cb, 0)); -globalThis.cancelAnimationFrame = globalThis.cancelAnimationFrame || clearTimeout; - -// 设置浏览器尺寸相关方法 -window.resizeTo = (width, height) => { - window.innerWidth = width || window.innerWidth; - window.innerHeight = height || window.innerHeight; - window.dispatchEvent(new Event('resize')); -}; -window.scrollTo = () => {}; - -const customScreen = within(document.body); - -// 使用异步测试处理真实API调用 -Deno.test('主题设置页面测试', async (t) => { - // 渲染组件 - const {findByRole, debug} = render( - - - - - - ) - - // debug(await findByRole('radio', { name: /浅色模式/i })) - - // 测试1: 渲染基本元素 - await t.step('应渲染主题设置标题', async () => { - const title = await customScreen.findByText(/主题设置/i) - assertExists(title, '未找到主题设置标题') - }) - - // 测试2: 表单初始化状态 - await t.step('表单应正确初始化', async () => { - // 检查主题模式选择 - const lightRadio = await customScreen.findByRole('radio', { name: /浅色模式/i }) - assertExists(lightRadio, '未找到浅色模式单选按钮') - - // 检查主题模式标签 - const themeModeLabel = await customScreen.findByText(/主题模式/i) - assertExists(themeModeLabel, '未找到主题模式标签') - - // // 检查主题模式选择器 - Ant Design 使用 div 包裹 radio 而不是 radiogroup - // const themeModeField = await customScreen.findByTestId('theme-mode-selector') - // assertExists(themeModeField, '未找到主题模式选择器') - }) - - // 测试3: 配色方案选择 - await t.step('应显示配色方案选项', async () => { - // 查找预设配色方案标签 - const colorSchemeLabel = await customScreen.findByText('预设配色方案') - assertExists(colorSchemeLabel, '未找到预设配色方案标签') - - // 查找配色方案按钮 - const colorSchemeButtons = await customScreen.findAllByRole('button') - assertNotEquals(colorSchemeButtons.length, 0, '未找到配色方案按钮') - }) -}) - From 08ae3b85df20f3abf70ca2f08d1d8a4f9a233a33 Mon Sep 17 00:00:00 2001 From: yourname Date: Tue, 13 May 2025 08:06:34 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=E5=AE=8C=E6=95=B4API=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=9A=20client/admin/api/=20=E2=94=9C?= =?UTF-8?q?=E2=94=80=E2=94=80=20auth.ts=20(=E8=AE=A4=E8=AF=81API)=20?= =?UTF-8?q?=E2=94=9C=E2=94=80=E2=94=80=20users.ts=20(=E7=94=A8=E6=88=B7API?= =?UTF-8?q?)=20=E2=94=9C=E2=94=80=E2=94=80=20files.ts=20(=E6=96=87?= =?UTF-8?q?=E4=BB=B6API)=20=E2=94=9C=E2=94=80=E2=94=80=20theme.ts=20(?= =?UTF-8?q?=E4=B8=BB=E9=A2=98API)=20=E2=94=9C=E2=94=80=E2=94=80=20charts.t?= =?UTF-8?q?s=20(=E5=9B=BE=E8=A1=A8API)=20=E2=94=9C=E2=94=80=E2=94=80=20mes?= =?UTF-8?q?sages.ts=20(=E6=B6=88=E6=81=AFAPI)=20=E2=94=9C=E2=94=80?= =?UTF-8?q?=E2=94=80=20sys.ts=20(=E7=B3=BB=E7=BB=9FAPI)=20=E2=94=9C?= =?UTF-8?q?=E2=94=80=E2=94=80=20know=5Finfo.ts=20(=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93API)=20=E2=94=9C=E2=94=80=E2=94=80=20maps.ts=20(?= =?UTF-8?q?=E5=9C=B0=E5=9B=BEAPI)=20=E2=94=94=E2=94=80=E2=94=80=20index.ts?= =?UTF-8?q?=20(=E7=BB=9F=E4=B8=80=E5=85=A5=E5=8F=A3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY.md | 5 +- client/admin/api.ts | 782 -------------------------- client/admin/api/auth.ts | 106 ++++ client/admin/api/charts.ts | 68 +++ client/admin/api/files.ts | 161 ++++++ client/admin/api/index.ts | 9 + client/admin/api/know_info.ts | 95 ++++ client/admin/api/maps.ts | 64 +++ client/admin/api/messages.ts | 81 +++ client/admin/api/sys.ts | 49 ++ client/admin/api/theme.ts | 38 ++ client/admin/api/users.ts | 81 +++ client/admin/components_uploader.tsx | 2 +- client/admin/hooks_sys.tsx | 2 +- client/admin/pages_chart.tsx | 28 +- client/admin/pages_know_info.tsx | 8 +- client/admin/pages_map.tsx | 19 +- client/admin/pages_messages.tsx | 6 +- client/admin/pages_settings.tsx | 79 ++- client/admin/pages_sys.tsx | 9 +- client/admin/pages_theme_settings.tsx | 33 +- 21 files changed, 818 insertions(+), 907 deletions(-) delete mode 100644 client/admin/api.ts create mode 100644 client/admin/api/auth.ts create mode 100644 client/admin/api/charts.ts create mode 100644 client/admin/api/files.ts create mode 100644 client/admin/api/index.ts create mode 100644 client/admin/api/know_info.ts create mode 100644 client/admin/api/maps.ts create mode 100644 client/admin/api/messages.ts create mode 100644 client/admin/api/sys.ts create mode 100644 client/admin/api/theme.ts create mode 100644 client/admin/api/users.ts diff --git a/HISTORY.md b/HISTORY.md index d16fc32..692f97c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,5 +3,6 @@ 迁移管理页面,在正式环境中,需要验证env中配置的密码参数才能打开 2025.05.13 0.1.0 -首页添加了迁移管理入口按钮, 无需登录即可访问 -打开迁移管理页面时,将迁移历史读取出来 \ No newline at end of file +将admin api.ts 拆开 +打开迁移管理页面时,将迁移历史读取出来 +首页添加了迁移管理入口按钮, 无需登录即可访问 \ No newline at end of file diff --git a/client/admin/api.ts b/client/admin/api.ts deleted file mode 100644 index 852a365..0000000 --- a/client/admin/api.ts +++ /dev/null @@ -1,782 +0,0 @@ -import axios from 'axios'; -import { getGlobalConfig } from './utils.ts'; -import type { MinioUploadPolicy, OSSUploadPolicy } from '@d8d-appcontainer/types'; -import 'dayjs/locale/zh-cn'; -import type { - User, FileLibrary, FileCategory, ThemeSettings, - SystemSetting, SystemSettingGroupData, - LoginLocation, LoginLocationDetail, - Message, UserMessage, KnowInfo -} from '../share/types.ts'; - - - -// 定义API基础URL -const API_BASE_URL = '/api'; - -// 获取OSS完整URL -export const getOssUrl = (path: string): string => { - // 获取全局配置中的OSS_HOST,如果不存在使用默认值 - const ossHost = getGlobalConfig('OSS_BASE_URL') || ''; - // 确保path不以/开头 - const ossPath = path.startsWith('/') ? path.substring(1) : path; - return `${ossHost}/${ossPath}`; -}; - -// =================== -// Auth API 定义部分 -// =================== - -// 定义API返回数据类型 -interface AuthLoginResponse { - message: string; - token: string; - refreshToken?: string; - user: User; -} - -interface AuthResponse { - message: string; - [key: string]: any; -} - -// 定义Auth API接口类型 -interface AuthAPIType { - login: (username: string, password: string, latitude?: number, longitude?: number) => Promise; - register: (username: string, email: string, password: string) => Promise; - logout: () => Promise; - getCurrentUser: () => Promise; - updateUser: (userId: number, userData: Partial) => Promise; - changePassword: (oldPassword: string, newPassword: string) => Promise; - requestPasswordReset: (email: string) => Promise; - resetPassword: (token: string, newPassword: string) => Promise; -} - - -// Auth相关API -export const AuthAPI: AuthAPIType = { - // 登录API - login: async (username: string, password: string, latitude?: number, longitude?: number) => { - try { - const response = await axios.post(`${API_BASE_URL}/auth/login`, { - username, - password, - latitude, - longitude - }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 注册API - register: async (username: string, email: string, password: string) => { - try { - const response = await axios.post(`${API_BASE_URL}/auth/register`, { username, email, password }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 登出API - logout: async () => { - try { - const response = await axios.post(`${API_BASE_URL}/auth/logout`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取当前用户信息 - getCurrentUser: async () => { - try { - const response = await axios.get(`${API_BASE_URL}/auth/me`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 更新用户信息 - updateUser: async (userId: number, userData: Partial) => { - try { - const response = await axios.put(`${API_BASE_URL}/auth/users/${userId}`, userData); - return response.data; - } catch (error) { - throw error; - } - }, - - // 修改密码 - changePassword: async (oldPassword: string, newPassword: string) => { - try { - const response = await axios.post(`${API_BASE_URL}/auth/change-password`, { oldPassword, newPassword }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 请求重置密码 - requestPasswordReset: async (email: string) => { - try { - const response = await axios.post(`${API_BASE_URL}/auth/request-password-reset`, { email }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 重置密码 - resetPassword: async (token: string, newPassword: string) => { - try { - const response = await axios.post(`${API_BASE_URL}/auth/reset-password`, { token, newPassword }); - return response.data; - } catch (error) { - throw error; - } - } -}; - -// 为UserAPI添加的接口响应类型 -interface UsersResponse { - data: User[]; - pagination: { - total: number; - current: number; - pageSize: number; - totalPages: number; - }; -} - -interface UserResponse { - data: User; - message?: string; -} - -interface UserCreateResponse { - message: string; - data: User; -} - -interface UserUpdateResponse { - message: string; - data: User; -} - -interface UserDeleteResponse { - message: string; - id: number; -} - -// 用户管理API -export const UserAPI = { - // 获取用户列表 - getUsers: async (params?: { page?: number, limit?: number, search?: string }): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/users`, { params }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取单个用户详情 - getUser: async (userId: number): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/users/${userId}`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 创建用户 - createUser: async (userData: Partial): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/users`, userData); - return response.data; - } catch (error) { - throw error; - } - }, - - // 更新用户信息 - updateUser: async (userId: number, userData: Partial): Promise => { - try { - const response = await axios.put(`${API_BASE_URL}/users/${userId}`, userData); - return response.data; - } catch (error) { - throw error; - } - }, - - // 删除用户 - deleteUser: async (userId: number): Promise => { - try { - const response = await axios.delete(`${API_BASE_URL}/users/${userId}`); - return response.data; - } catch (error) { - throw error; - } - } -}; - - -// 定义文件相关接口类型 -interface FileUploadPolicyResponse { - message: string; - data: MinioUploadPolicy | OSSUploadPolicy; -} - -interface FileListResponse { - message: string; - data: { - list: FileLibrary[]; - pagination: { - current: number; - pageSize: number; - total: number; - }; - }; -} - -interface FileSaveResponse { - message: string; - data: FileLibrary; -} - -interface FileInfoResponse { - message: string; - data: FileLibrary; -} - -interface FileDeleteResponse { - message: string; -} - - -interface FileCategoryListResponse { - data: FileCategory[]; - total: number; - page: number; - pageSize: number; -} - -interface FileCategoryCreateResponse { - message: string; - data: FileCategory; -} - -interface FileCategoryUpdateResponse { - message: string; - data: FileCategory; -} - -interface FileCategoryDeleteResponse { - message: string; -} - -// 文件API接口定义 -export const FileAPI = { - // 获取文件上传策略 - getUploadPolicy: async (filename: string, prefix: string = 'uploads/', maxSize: number = 10 * 1024 * 1024): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/upload/policy`, { - params: { filename, prefix, maxSize } - }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 保存文件信息 - saveFileInfo: async (fileData: Partial): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/upload/save`, fileData); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取文件列表 - getFileList: async (params?: { - page?: number, - pageSize?: number, - category_id?: number, - fileType?: string, - keyword?: string - }): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/upload/list`, { params }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取单个文件信息 - getFileInfo: async (id: number): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/upload/${id}`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 更新文件下载计数 - updateDownloadCount: async (id: number): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/upload/${id}/download`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 删除文件 - deleteFile: async (id: number): Promise => { - try { - const response = await axios.delete(`${API_BASE_URL}/upload/${id}`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取文件分类列表 - getCategories: async (params?: { - page?: number, - pageSize?: number, - search?: string - }): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/file-categories`, { params }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 创建文件分类 - createCategory: async (data: Partial): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/file-categories`, data); - return response.data; - } catch (error) { - throw error; - } - }, - - // 更新文件分类 - updateCategory: async (id: number, data: Partial): Promise => { - try { - const response = await axios.put(`${API_BASE_URL}/file-categories/${id}`, data); - return response.data; - } catch (error) { - throw error; - } - }, - - // 删除文件分类 - deleteCategory: async (id: number): Promise => { - try { - const response = await axios.delete(`${API_BASE_URL}/file-categories/${id}`); - return response.data; - } catch (error) { - throw error; - } - } -}; - -// Theme API 响应类型 -export interface ThemeSettingsResponse { - message: string; - data: ThemeSettings; -} - -// Theme API 定义 -export const ThemeAPI = { - // 获取主题设置 - getThemeSettings: async (): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/theme`); - return response.data.data; - } catch (error) { - throw error; - } - }, - - // 更新主题设置 - updateThemeSettings: async (themeData: Partial): Promise => { - try { - const response = await axios.put(`${API_BASE_URL}/theme`, themeData); - return response.data.data; - } catch (error) { - throw error; - } - }, - - // 重置主题设置 - resetThemeSettings: async (): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/theme/reset`); - return response.data.data; - } catch (error) { - throw error; - } - } -}; - -// 图表数据API接口类型 -interface ChartDataResponse { - message: string; - data: T; -} - -interface UserActivityData { - date: string; - count: number; -} - -interface FileUploadsData { - month: string; - count: number; -} - -interface FileTypesData { - type: string; - value: number; -} - -interface DashboardOverviewData { - userCount: number; - fileCount: number; - articleCount: number; - todayLoginCount: number; -} - -// 图表数据API -export const ChartAPI = { - // 获取用户活跃度数据 - getUserActivity: async (): Promise> => { - try { - const response = await axios.get(`${API_BASE_URL}/charts/user-activity`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取文件上传统计数据 - getFileUploads: async (): Promise> => { - try { - const response = await axios.get(`${API_BASE_URL}/charts/file-uploads`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取文件类型分布数据 - getFileTypes: async (): Promise> => { - try { - const response = await axios.get(`${API_BASE_URL}/charts/file-types`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取仪表盘概览数据 - getDashboardOverview: async (): Promise> => { - try { - const response = await axios.get(`${API_BASE_URL}/charts/dashboard-overview`); - return response.data; - } catch (error) { - throw error; - } - } -}; - -// 消息API接口类型 -interface MessagesResponse { - data: UserMessage[]; - pagination: { - total: number; - current: number; - pageSize: number; - totalPages: number; - }; -} - -interface MessageResponse { - data: Message; - message?: string; -} - -interface MessageCountResponse { - count: number; -} - -// 消息API -export const MessageAPI = { - // 获取消息列表 - getMessages: async (params?: { - page?: number, - pageSize?: number, - type?: string, - status?: string, - search?: string - }): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/messages`, { params }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 发送消息 - sendMessage: async (data: { - title: string, - content: string, - type: string, - receiver_ids: number[] - }): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/messages`, data); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取未读消息数 - getUnreadCount: async (): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/messages/count/unread`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 标记消息为已读 - markAsRead: async (id: number): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/messages/${id}/read`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 删除消息 - deleteMessage: async (id: number): Promise => { - try { - const response = await axios.delete(`${API_BASE_URL}/messages/${id}`); - return response.data; - } catch (error) { - throw error; - } - } -}; - -// 地图相关API的接口类型定义 -export interface LoginLocationResponse { - message: string; - data: LoginLocation[]; -} - -export interface LoginLocationDetailResponse { - message: string; - data: LoginLocationDetail; -} - -export interface LoginLocationUpdateResponse { - message: string; - data: LoginLocationDetail; -} - -// 知识库相关接口类型定义 -export interface KnowInfoListResponse { - data: KnowInfo[]; - pagination: { - total: number; - current: number; - pageSize: number; - totalPages: number; - }; -} - -interface KnowInfoResponse { - data: KnowInfo; - message?: string; -} - -interface KnowInfoCreateResponse { - message: string; - data: KnowInfo; -} - -interface KnowInfoUpdateResponse { - message: string; - data: KnowInfo; -} - -interface KnowInfoDeleteResponse { - message: string; - id: number; -} - - -// 地图相关API -export const MapAPI = { - // 获取地图标记点数据 - getMarkers: async (params?: { - startTime?: string; - endTime?: string; - userId?: number - }): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/map/markers`, { params }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取登录位置详情 - getLocationDetail: async (locationId: number): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/map/location/${locationId}`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 更新登录位置信息 - updateLocation: async (locationId: number, data: { - longitude: number; - latitude: number; - location_name?: string; - }): Promise => { - try { - const response = await axios.put(`${API_BASE_URL}/map/location/${locationId}`, data); - return response.data; - } catch (error) { - throw error; - } - } -}; - -// 系统设置API -// 知识库API -export const KnowInfoAPI = { - // 获取知识库列表 - getKnowInfos: async (params?: { - page?: number; - pageSize?: number; - title?: string; - category?: string; - tags?: string; - }): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/know-infos`, { params }); - return response.data; - } catch (error) { - throw error; - } - }, - - // 获取单个知识详情 - getKnowInfo: async (id: number): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/know-infos/${id}`); - return response.data; - } catch (error) { - throw error; - } - }, - - // 创建知识 - createKnowInfo: async (data: Partial): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/know-infos`, data); - return response.data; - } catch (error) { - throw error; - } - }, - - // 更新知识 - updateKnowInfo: async (id: number, data: Partial): Promise => { - try { - const response = await axios.put(`${API_BASE_URL}/know-infos/${id}`, data); - return response.data; - } catch (error) { - throw error; - } - }, - - // 删除知识 - deleteKnowInfo: async (id: number): Promise => { - try { - const response = await axios.delete(`${API_BASE_URL}/know-infos/${id}`); - return response.data; - } catch (error) { - throw error; - } - } -}; - -export const SystemAPI = { - // 获取所有系统设置 - getSettings: async (): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/settings`); - return response.data.data; - } catch (error) { - throw error; - } - }, - - // 获取指定分组的系统设置 - getSettingsByGroup: async (group: string): Promise => { - try { - const response = await axios.get(`${API_BASE_URL}/settings/group/${group}`); - return response.data.data; - } catch (error) { - throw error; - } - }, - - // 更新系统设置 - updateSettings: async (settings: Partial[]): Promise => { - try { - const response = await axios.put(`${API_BASE_URL}/settings`, settings); - return response.data.data; - } catch (error) { - throw error; - } - }, - - // 重置系统设置 - resetSettings: async (): Promise => { - try { - const response = await axios.post(`${API_BASE_URL}/settings/reset`); - return response.data.data; - } catch (error) { - throw error; - } - } -}; - diff --git a/client/admin/api/auth.ts b/client/admin/api/auth.ts new file mode 100644 index 0000000..999fce3 --- /dev/null +++ b/client/admin/api/auth.ts @@ -0,0 +1,106 @@ +import axios from 'axios'; +import type { User } from '../../share/types.ts'; + +const API_BASE_URL = '/api'; + +interface AuthLoginResponse { + message: string; + token: string; + refreshToken?: string; + user: User; +} + +interface AuthResponse { + message: string; + [key: string]: any; +} + +interface AuthAPIType { + login: (username: string, password: string, latitude?: number, longitude?: number) => Promise; + register: (username: string, email: string, password: string) => Promise; + logout: () => Promise; + getCurrentUser: () => Promise; + updateUser: (userId: number, userData: Partial) => Promise; + changePassword: (oldPassword: string, newPassword: string) => Promise; + requestPasswordReset: (email: string) => Promise; + resetPassword: (token: string, newPassword: string) => Promise; +} + +export const AuthAPI: AuthAPIType = { + login: async (username: string, password: string, latitude?: number, longitude?: number) => { + try { + const response = await axios.post(`${API_BASE_URL}/auth/login`, { + username, + password, + latitude, + longitude + }); + return response.data; + } catch (error) { + throw error; + } + }, + + register: async (username: string, email: string, password: string) => { + try { + const response = await axios.post(`${API_BASE_URL}/auth/register`, { username, email, password }); + return response.data; + } catch (error) { + throw error; + } + }, + + logout: async () => { + try { + const response = await axios.post(`${API_BASE_URL}/auth/logout`); + return response.data; + } catch (error) { + throw error; + } + }, + + getCurrentUser: async () => { + try { + const response = await axios.get(`${API_BASE_URL}/auth/me`); + return response.data; + } catch (error) { + throw error; + } + }, + + updateUser: async (userId: number, userData: Partial) => { + try { + const response = await axios.put(`${API_BASE_URL}/auth/users/${userId}`, userData); + return response.data; + } catch (error) { + throw error; + } + }, + + changePassword: async (oldPassword: string, newPassword: string) => { + try { + const response = await axios.post(`${API_BASE_URL}/auth/change-password`, { oldPassword, newPassword }); + return response.data; + } catch (error) { + throw error; + } + }, + + requestPasswordReset: async (email: string) => { + try { + const response = await axios.post(`${API_BASE_URL}/auth/request-password-reset`, { email }); + return response.data; + } catch (error) { + throw error; + } + }, + + resetPassword: async (token: string, newPassword: string) => { + try { + const response = await axios.post(`${API_BASE_URL}/auth/reset-password`, { token, newPassword }); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/charts.ts b/client/admin/api/charts.ts new file mode 100644 index 0000000..0fa23ce --- /dev/null +++ b/client/admin/api/charts.ts @@ -0,0 +1,68 @@ +import axios from 'axios'; + +const API_BASE_URL = '/api'; + +interface ChartDataResponse { + message: string; + data: T; +} + +interface UserActivityData { + date: string; + count: number; +} + +interface FileUploadsData { + month: string; + count: number; +} + +interface FileTypesData { + type: string; + value: number; +} + +interface DashboardOverviewData { + userCount: number; + fileCount: number; + articleCount: number; + todayLoginCount: number; +} + +export const ChartAPI = { + getUserActivity: async (): Promise> => { + try { + const response = await axios.get(`${API_BASE_URL}/charts/user-activity`); + return response.data; + } catch (error) { + throw error; + } + }, + + getFileUploads: async (): Promise> => { + try { + const response = await axios.get(`${API_BASE_URL}/charts/file-uploads`); + return response.data; + } catch (error) { + throw error; + } + }, + + getFileTypes: async (): Promise> => { + try { + const response = await axios.get(`${API_BASE_URL}/charts/file-types`); + return response.data; + } catch (error) { + throw error; + } + }, + + getDashboardOverview: async (): Promise> => { + try { + const response = await axios.get(`${API_BASE_URL}/charts/dashboard-overview`); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/files.ts b/client/admin/api/files.ts new file mode 100644 index 0000000..3ab56bd --- /dev/null +++ b/client/admin/api/files.ts @@ -0,0 +1,161 @@ +import axios from 'axios'; +import type { FileLibrary, FileCategory } from '../../share/types.ts'; +import type { MinioUploadPolicy, OSSUploadPolicy } from '@d8d-appcontainer/types'; + +const API_BASE_URL = '/api'; + +interface FileUploadPolicyResponse { + message: string; + data: MinioUploadPolicy | OSSUploadPolicy; +} + +interface FileListResponse { + message: string; + data: { + list: FileLibrary[]; + pagination: { + current: number; + pageSize: number; + total: number; + }; + }; +} + +interface FileSaveResponse { + message: string; + data: FileLibrary; +} + +interface FileInfoResponse { + message: string; + data: FileLibrary; +} + +interface FileDeleteResponse { + message: string; +} + +interface FileCategoryListResponse { + data: FileCategory[]; + total: number; + page: number; + pageSize: number; +} + +interface FileCategoryCreateResponse { + message: string; + data: FileCategory; +} + +interface FileCategoryUpdateResponse { + message: string; + data: FileCategory; +} + +interface FileCategoryDeleteResponse { + message: string; +} + +export const FileAPI = { + getUploadPolicy: async (filename: string, prefix: string = 'uploads/', maxSize: number = 10 * 1024 * 1024): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/upload/policy`, { + params: { filename, prefix, maxSize } + }); + return response.data; + } catch (error) { + throw error; + } + }, + + saveFileInfo: async (fileData: Partial): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/upload/save`, fileData); + return response.data; + } catch (error) { + throw error; + } + }, + + getFileList: async (params?: { + page?: number, + pageSize?: number, + category_id?: number, + fileType?: string, + keyword?: string + }): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/upload/list`, { params }); + return response.data; + } catch (error) { + throw error; + } + }, + + getFileInfo: async (id: number): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/upload/${id}`); + return response.data; + } catch (error) { + throw error; + } + }, + + updateDownloadCount: async (id: number): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/upload/${id}/download`); + return response.data; + } catch (error) { + throw error; + } + }, + + deleteFile: async (id: number): Promise => { + try { + const response = await axios.delete(`${API_BASE_URL}/upload/${id}`); + return response.data; + } catch (error) { + throw error; + } + }, + + getCategories: async (params?: { + page?: number, + pageSize?: number, + search?: string + }): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/file-categories`, { params }); + return response.data; + } catch (error) { + throw error; + } + }, + + createCategory: async (data: Partial): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/file-categories`, data); + return response.data; + } catch (error) { + throw error; + } + }, + + updateCategory: async (id: number, data: Partial): Promise => { + try { + const response = await axios.put(`${API_BASE_URL}/file-categories/${id}`, data); + return response.data; + } catch (error) { + throw error; + } + }, + + deleteCategory: async (id: number): Promise => { + try { + const response = await axios.delete(`${API_BASE_URL}/file-categories/${id}`); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/index.ts b/client/admin/api/index.ts new file mode 100644 index 0000000..bf427ab --- /dev/null +++ b/client/admin/api/index.ts @@ -0,0 +1,9 @@ +export * from './auth.ts'; +export * from './users.ts'; +export * from './files.ts'; +export * from './theme.ts'; +export * from './charts.ts'; +export * from './messages.ts'; +export * from './sys.ts'; +export * from './know_info.ts'; +export * from './maps.ts'; \ No newline at end of file diff --git a/client/admin/api/know_info.ts b/client/admin/api/know_info.ts new file mode 100644 index 0000000..f199261 --- /dev/null +++ b/client/admin/api/know_info.ts @@ -0,0 +1,95 @@ +import axios from 'axios'; +import type { KnowInfo } from '../../share/types.ts'; + +export interface KnowInfoListResponse { + data: KnowInfo[]; + pagination: { + current: number; + pageSize: number; + total: number; + totalPages: number; + }; +} + +interface KnowInfoResponse { + data: KnowInfo; + message?: string; +} + +interface KnowInfoCreateResponse { + message: string; + data: KnowInfo; +} + +interface KnowInfoUpdateResponse { + message: string; + data: KnowInfo; +} + +interface KnowInfoDeleteResponse { + message: string; + id: number; +} + + +const API_BASE_URL = '/api'; + + +// 知识库API +export const KnowInfoAPI = { + // 获取知识库列表 + getKnowInfos: async (params?: { + page?: number; + pageSize?: number; + title?: string; + category?: string; + tags?: string; + }): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/know-infos`, { params }); + return response.data; + } catch (error) { + throw error; + } + }, + + // 获取单个知识详情 + getKnowInfo: async (id: number): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/know-infos/${id}`); + return response.data; + } catch (error) { + throw error; + } + }, + + // 创建知识 + createKnowInfo: async (data: Partial): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/know-infos`, data); + return response.data; + } catch (error) { + throw error; + } + }, + + // 更新知识 + updateKnowInfo: async (id: number, data: Partial): Promise => { + try { + const response = await axios.put(`${API_BASE_URL}/know-infos/${id}`, data); + return response.data; + } catch (error) { + throw error; + } + }, + + // 删除知识 + deleteKnowInfo: async (id: number): Promise => { + try { + const response = await axios.delete(`${API_BASE_URL}/know-infos/${id}`); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/maps.ts b/client/admin/api/maps.ts new file mode 100644 index 0000000..540345c --- /dev/null +++ b/client/admin/api/maps.ts @@ -0,0 +1,64 @@ +import axios from 'axios'; +import type { + LoginLocation, LoginLocationDetail, +} from '../../share/types.ts'; + +const API_BASE_URL = '/api'; + + +// 地图相关API的接口类型定义 +export interface LoginLocationResponse { + message: string; + data: LoginLocation[]; +} + +export interface LoginLocationDetailResponse { + message: string; + data: LoginLocationDetail; +} + +export interface LoginLocationUpdateResponse { + message: string; + data: LoginLocationDetail; +} + +// 地图相关API +export const MapAPI = { + // 获取地图标记点数据 + getMarkers: async (params?: { + startTime?: string; + endTime?: string; + userId?: number + }): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/map/markers`, { params }); + return response.data; + } catch (error) { + throw error; + } + }, + + // 获取登录位置详情 + getLocationDetail: async (locationId: number): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/map/location/${locationId}`); + return response.data; + } catch (error) { + throw error; + } + }, + + // 更新登录位置信息 + updateLocation: async (locationId: number, data: { + longitude: number; + latitude: number; + location_name?: string; + }): Promise => { + try { + const response = await axios.put(`${API_BASE_URL}/map/location/${locationId}`, data); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/messages.ts b/client/admin/api/messages.ts new file mode 100644 index 0000000..a5d5fa2 --- /dev/null +++ b/client/admin/api/messages.ts @@ -0,0 +1,81 @@ +import axios from 'axios'; +import type { UserMessage, Message } from '../../share/types.ts'; + +const API_BASE_URL = '/api'; + +interface MessagesResponse { + data: UserMessage[]; + pagination: { + total: number; + current: number; + pageSize: number; + totalPages: number; + }; +} + +interface MessageResponse { + data: Message; + message?: string; +} + +interface MessageCountResponse { + count: number; +} + +export const MessageAPI = { + getMessages: async (params?: { + page?: number, + pageSize?: number, + type?: string, + status?: string, + search?: string + }): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/messages`, { params }); + return response.data; + } catch (error) { + throw error; + } + }, + + sendMessage: async (data: { + title: string, + content: string, + type: string, + receiver_ids: number[] + }): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/messages`, data); + return response.data; + } catch (error) { + throw error; + } + }, + + getUnreadCount: async (): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/messages/count/unread`); + return response.data; + } catch (error) { + throw error; + } + }, + + markAsRead: async (id: number): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/messages/${id}/read`); + return response.data; + } catch (error) { + throw error; + } + }, + + deleteMessage: async (id: number): Promise => { + try { + const response = await axios.delete(`${API_BASE_URL}/messages/${id}`); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/sys.ts b/client/admin/api/sys.ts new file mode 100644 index 0000000..f7dc7ff --- /dev/null +++ b/client/admin/api/sys.ts @@ -0,0 +1,49 @@ +import axios from 'axios'; + +import type { + SystemSetting, SystemSettingGroupData, +} from '../../share/types.ts'; + +const API_BASE_URL = '/api'; + +export const SystemAPI = { + // 获取所有系统设置 + getSettings: async (): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/settings`); + return response.data.data; + } catch (error) { + throw error; + } + }, + + // 获取指定分组的系统设置 + getSettingsByGroup: async (group: string): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/settings/group/${group}`); + return response.data.data; + } catch (error) { + throw error; + } + }, + + // 更新系统设置 + updateSettings: async (settings: Partial[]): Promise => { + try { + const response = await axios.put(`${API_BASE_URL}/settings`, settings); + return response.data.data; + } catch (error) { + throw error; + } + }, + + // 重置系统设置 + resetSettings: async (): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/settings/reset`); + return response.data.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/theme.ts b/client/admin/api/theme.ts new file mode 100644 index 0000000..863a6b2 --- /dev/null +++ b/client/admin/api/theme.ts @@ -0,0 +1,38 @@ +import axios from 'axios'; +import type { ThemeSettings } from '../../share/types.ts'; + +const API_BASE_URL = '/api'; + +export interface ThemeSettingsResponse { + message: string; + data: ThemeSettings; +} + +export const ThemeAPI = { + getThemeSettings: async (): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/theme`); + return response.data.data; + } catch (error) { + throw error; + } + }, + + updateThemeSettings: async (themeData: Partial): Promise => { + try { + const response = await axios.put(`${API_BASE_URL}/theme`, themeData); + return response.data.data; + } catch (error) { + throw error; + } + }, + + resetThemeSettings: async (): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/theme/reset`); + return response.data.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/api/users.ts b/client/admin/api/users.ts new file mode 100644 index 0000000..8e6c018 --- /dev/null +++ b/client/admin/api/users.ts @@ -0,0 +1,81 @@ +import axios from 'axios'; +import type { User } from '../../share/types.ts'; + +const API_BASE_URL = '/api'; + +interface UsersResponse { + data: User[]; + pagination: { + total: number; + current: number; + pageSize: number; + totalPages: number; + }; +} + +interface UserResponse { + data: User; + message?: string; +} + +interface UserCreateResponse { + message: string; + data: User; +} + +interface UserUpdateResponse { + message: string; + data: User; +} + +interface UserDeleteResponse { + message: string; + id: number; +} + +export const UserAPI = { + getUsers: async (params?: { page?: number, limit?: number, search?: string }): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/users`, { params }); + return response.data; + } catch (error) { + throw error; + } + }, + + getUser: async (userId: number): Promise => { + try { + const response = await axios.get(`${API_BASE_URL}/users/${userId}`); + return response.data; + } catch (error) { + throw error; + } + }, + + createUser: async (userData: Partial): Promise => { + try { + const response = await axios.post(`${API_BASE_URL}/users`, userData); + return response.data; + } catch (error) { + throw error; + } + }, + + updateUser: async (userId: number, userData: Partial): Promise => { + try { + const response = await axios.put(`${API_BASE_URL}/users/${userId}`, userData); + return response.data; + } catch (error) { + throw error; + } + }, + + deleteUser: async (userId: number): Promise => { + try { + const response = await axios.delete(`${API_BASE_URL}/users/${userId}`); + return response.data; + } catch (error) { + throw error; + } + } +}; \ No newline at end of file diff --git a/client/admin/components_uploader.tsx b/client/admin/components_uploader.tsx index bf9785e..0671b05 100644 --- a/client/admin/components_uploader.tsx +++ b/client/admin/components_uploader.tsx @@ -16,7 +16,7 @@ import type { MinioUploadPolicy, OSSUploadPolicy } from '@d8d-appcontainer/types import 'dayjs/locale/zh-cn'; import { OssType } from '../share/types.ts'; -import { FileAPI } from './api.ts'; +import { FileAPI } from './api/index.ts'; // MinIO文件上传组件 export const Uploader = ({ diff --git a/client/admin/hooks_sys.tsx b/client/admin/hooks_sys.tsx index c0245f9..a222c66 100644 --- a/client/admin/hooks_sys.tsx +++ b/client/admin/hooks_sys.tsx @@ -24,7 +24,7 @@ import { import { AuthAPI, ThemeAPI -} from './api.ts'; +} from './api/index.ts'; // 配置 dayjs 插件 diff --git a/client/admin/pages_chart.tsx b/client/admin/pages_chart.tsx index 5de33e0..ac084e2 100644 --- a/client/admin/pages_chart.tsx +++ b/client/admin/pages_chart.tsx @@ -1,11 +1,6 @@ import React from 'react'; import { - Layout, Menu, Button, Table, Space, - Form, Input, Select, message, Modal, - Card, Spin, Row, Col, Breadcrumb, Avatar, - Dropdown, ConfigProvider, theme, Typography, - Switch, Badge, Image, Upload, Divider, Descriptions, - Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer + Card, Spin, Row, Col, Statistic, } from 'antd'; import { @@ -15,13 +10,10 @@ import { Line , Pie, Column} from "@ant-design/plots"; import 'dayjs/locale/zh-cn'; -import { ChartAPI } from './api.ts'; +import { ChartAPI } from './api/index.ts'; import { useTheme } from './hooks_sys.tsx'; -interface ChartTooltipInfo { - items: Array>; - title: string; -} + // 用户活跃度图表组件 const UserActivityChart: React.FC = () => { @@ -49,7 +41,7 @@ const UserActivityChart: React.FC = () => { }; return ( - + ); @@ -92,7 +84,7 @@ const FileUploadsChart: React.FC = () => { }; return ( - + ); @@ -130,7 +122,7 @@ const FileTypesChart: React.FC = () => { }; return ( - + ); @@ -151,7 +143,7 @@ const DashboardOverview: React.FC = () => { return ( - + { - + { - + { - + { const queryClient = useQueryClient(); const [form] = Form.useForm(); - const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [isModalVisible, setIsModalVisible] = useState(false); const [searchParams, setSearchParams] = useState({ page: 1, diff --git a/client/admin/pages_settings.tsx b/client/admin/pages_settings.tsx index dfa3061..d86a4b4 100644 --- a/client/admin/pages_settings.tsx +++ b/client/admin/pages_settings.tsx @@ -1,20 +1,14 @@ -import React, { useState, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { - Layout, Menu, Button, Table, Space, + Button,Space, Form, Input, Select, message, Modal, - Card, Spin, Row, Col, Breadcrumb, Avatar, - Dropdown, ConfigProvider, theme, Typography, - Switch, Badge, Image, Upload, Divider, Descriptions, - Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer, InputNumber,ColorPicker, - Popover + Card, Spin, Typography, + Switch, Tabs, Alert, InputNumber } from 'antd'; import { - UploadOutlined, ReloadOutlined, SaveOutlined, - BgColorsOutlined } from '@ant-design/icons'; -import { debounce } from 'lodash'; import { useQuery, useMutation, @@ -25,25 +19,20 @@ import weekday from 'dayjs/plugin/weekday'; import localeData from 'dayjs/plugin/localeData'; import 'dayjs/locale/zh-cn'; import type { - FileLibrary, FileCategory, KnowInfo, SystemSetting, SystemSettingValue, - ColorScheme + SystemSetting, SystemSettingValue } from '../share/types.ts'; -import { ThemeMode } from '../share/types.ts'; import { SystemSettingGroup, SystemSettingKey, - FontSize, - CompactMode, AllowedFileType } from '../share/types.ts'; -import { getEnumOptions } from './utils.ts'; import { - SystemAPI, -} from './api.ts'; + SystemAPI +} from './api/index.ts'; import { useTheme } from './hooks_sys.tsx'; @@ -241,44 +230,44 @@ export const SettingsPage = () => { items={Object.values(SystemSettingGroup).map(group => ({ key: group, label: String(GROUP_TITLES[group]), - children: ( -
- + -
+ type="info" + showIcon + style={{ marginBottom: 24 }} + /> + {settingsData ?.find(g => g.name === group) ?.settings.map(setting => ( - {renderSettingInput(setting)} - + ))} - - - -
-
- ) + + + + + + ) }))} /> diff --git a/client/admin/pages_sys.tsx b/client/admin/pages_sys.tsx index 64cad76..dacae58 100644 --- a/client/admin/pages_sys.tsx +++ b/client/admin/pages_sys.tsx @@ -25,20 +25,15 @@ import { uploadMinIOWithPolicy,uploadOSSWithPolicy } from '@d8d-appcontainer/api import type { MinioUploadPolicy, OSSUploadPolicy } from '@d8d-appcontainer/types'; import 'dayjs/locale/zh-cn'; import type { - FileLibrary, FileCategory, KnowInfo + FileLibrary, FileCategory } from '../share/types.ts'; import { - AuditStatus,AuditStatusNameMap, OssType, } from '../share/types.ts'; -import { getEnumOptions } from './utils.ts'; -import { - FileAPI, - UserAPI, -} from './api.ts'; +import { FileAPI , UserAPI } from './api/index.ts'; // 配置 dayjs 插件 diff --git a/client/admin/pages_theme_settings.tsx b/client/admin/pages_theme_settings.tsx index 613d10d..a6f6e12 100644 --- a/client/admin/pages_theme_settings.tsx +++ b/client/admin/pages_theme_settings.tsx @@ -1,53 +1,30 @@ import React, { useState, useEffect } from 'react'; import { - Layout, Menu, Button, Table, Space, - Form, Input, Select, message, Modal, - Card, Spin, Row, Col, Breadcrumb, Avatar, - Dropdown, ConfigProvider, theme, Typography, - Switch, Badge, Image, Upload, Divider, Descriptions, - Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer, InputNumber,ColorPicker, - Popover + Button, Space, + Form, message, + Card, Spin, Typography, + Switch, + Popconfirm, Radio, InputNumber,ColorPicker, } from 'antd'; -import { - UploadOutlined, - ReloadOutlined, - SaveOutlined, - BgColorsOutlined -} from '@ant-design/icons'; -import { debounce } from 'lodash'; -import { - useQuery, - useMutation, - useQueryClient, -} from '@tanstack/react-query'; import dayjs from 'dayjs'; import weekday from 'dayjs/plugin/weekday'; import localeData from 'dayjs/plugin/localeData'; import 'dayjs/locale/zh-cn'; import type { - FileLibrary, FileCategory, KnowInfo, SystemSetting, SystemSettingValue, ColorScheme } from '../share/types.ts'; import { ThemeMode } from '../share/types.ts'; import { - SystemSettingGroup, - SystemSettingKey, FontSize, CompactMode, - AllowedFileType } from '../share/types.ts'; -import { getEnumOptions } from './utils.ts'; -import { - SystemAPI, -} from './api.ts'; import { useTheme } from './hooks_sys.tsx'; -import { Uploader } from './components_uploader.tsx'; // 配置 dayjs 插件 dayjs.extend(weekday); From d0d88ab950a1502a9e9940bc04ef4ec5954dd803 Mon Sep 17 00:00:00 2001 From: yourname Date: Tue, 13 May 2025 09:17:50 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E4=BA=863=E4=B8=AA?= =?UTF-8?q?=E6=96=B0=E6=96=87=E4=BB=B6=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pages_dashboard.tsx (系统仪表盘功能) pages_users.tsx (用户管理功能) pages_file_library.tsx (文件库管理功能) 更新了所有引用: 修改了web_app.tsx中的导入语句 确保路由配置正确指向新文件 原pages_sys.tsx文件已不再使用,可以安全删除 --- client/admin/pages_dashboard.tsx | 44 +++ .../{pages_sys.tsx => pages_file_library.tsx} | 340 +----------------- client/admin/pages_users.tsx | 270 ++++++++++++++ client/admin/web_app.tsx | 10 +- 4 files changed, 330 insertions(+), 334 deletions(-) create mode 100644 client/admin/pages_dashboard.tsx rename client/admin/{pages_sys.tsx => pages_file_library.tsx} (70%) create mode 100644 client/admin/pages_users.tsx diff --git a/client/admin/pages_dashboard.tsx b/client/admin/pages_dashboard.tsx new file mode 100644 index 0000000..47cf2fb --- /dev/null +++ b/client/admin/pages_dashboard.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { + Card, Row, Col, Typography, Statistic +} from 'antd'; + +const { Title } = Typography; + +// 仪表盘页面 +export const DashboardPage = () => { + return ( +
+ 仪表盘 + + + + + + + + + + + + + + + + + +
+ ); +}; \ No newline at end of file diff --git a/client/admin/pages_sys.tsx b/client/admin/pages_file_library.tsx similarity index 70% rename from client/admin/pages_sys.tsx rename to client/admin/pages_file_library.tsx index dacae58..81fe93f 100644 --- a/client/admin/pages_sys.tsx +++ b/client/admin/pages_file_library.tsx @@ -1,11 +1,8 @@ import React, { useState, useEffect } from 'react'; import { - Layout, Menu, Button, Table, Space, - Form, Input, Select, message, Modal, - Card, Spin, Row, Col, Breadcrumb, Avatar, - Dropdown, ConfigProvider, theme, Typography, - Switch, Badge, Image, Upload, Divider, Descriptions, - Popconfirm, Tag, Statistic, DatePicker, Radio, Progress, Tabs, List, Alert, Collapse, Empty, Drawer + Button, Table, Space, Form, Input, Select, + message, Modal, Card, Typography, Tag, Popconfirm, + Tabs, Image, Upload, Descriptions } from 'antd'; import { UploadOutlined, @@ -14,336 +11,17 @@ import { FileWordOutlined, FilePdfOutlined, FileOutlined, -} from '@ant-design/icons'; -import { - useQuery, -} from '@tanstack/react-query'; +} from '@ant-design/icons'; +import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; -import weekday from 'dayjs/plugin/weekday'; -import localeData from 'dayjs/plugin/localeData'; -import { uploadMinIOWithPolicy,uploadOSSWithPolicy } from '@d8d-appcontainer/api'; +import { uploadMinIOWithPolicy, uploadOSSWithPolicy } from '@d8d-appcontainer/api'; import type { MinioUploadPolicy, OSSUploadPolicy } from '@d8d-appcontainer/types'; -import 'dayjs/locale/zh-cn'; -import type { - FileLibrary, FileCategory -} from '../share/types.ts'; - -import { - OssType, -} from '../share/types.ts'; - - -import { FileAPI , UserAPI } from './api/index.ts'; - - -// 配置 dayjs 插件 -dayjs.extend(weekday); -dayjs.extend(localeData); - -// 设置 dayjs 语言 -dayjs.locale('zh-cn'); +import { FileAPI } from './api/index.ts'; +import type { FileLibrary, FileCategory } from '../share/types.ts'; +import { OssType } from '../share/types.ts'; const { Title } = Typography; - -// 仪表盘页面 -export const DashboardPage = () => { - return ( -
- 仪表盘 - - - - - - - - - - - - - - - - - -
- ); -}; - -// 用户管理页面 -export const UsersPage = () => { - const [searchParams, setSearchParams] = useState({ - page: 1, - limit: 10, - search: '' - }); - const [modalVisible, setModalVisible] = useState(false); - const [modalTitle, setModalTitle] = useState(''); - const [editingUser, setEditingUser] = useState(null); - const [form] = Form.useForm(); - - const { data: usersData, isLoading, refetch } = useQuery({ - queryKey: ['users', searchParams], - queryFn: async () => { - return await UserAPI.getUsers(searchParams); - } - }); - - const users = usersData?.data || []; - const pagination = { - current: searchParams.page, - pageSize: searchParams.limit, - total: usersData?.pagination?.total || 0 - }; - - // 处理搜索 - const handleSearch = (values: any) => { - setSearchParams(prev => ({ - ...prev, - search: values.search || '', - page: 1 - })); - }; - - // 处理分页变化 - const handleTableChange = (newPagination: any) => { - setSearchParams(prev => ({ - ...prev, - page: newPagination.current, - limit: newPagination.pageSize - })); - }; - - // 打开创建用户模态框 - const showCreateModal = () => { - setModalTitle('创建用户'); - setEditingUser(null); - form.resetFields(); - setModalVisible(true); - }; - - // 打开编辑用户模态框 - const showEditModal = (user: any) => { - setModalTitle('编辑用户'); - setEditingUser(user); - form.setFieldsValue(user); - setModalVisible(true); - }; - - // 处理模态框确认 - const handleModalOk = async () => { - try { - const values = await form.validateFields(); - - if (editingUser) { - // 编辑用户 - await UserAPI.updateUser(editingUser.id, values); - message.success('用户更新成功'); - } else { - // 创建用户 - await UserAPI.createUser(values); - message.success('用户创建成功'); - } - - setModalVisible(false); - form.resetFields(); - refetch(); // 刷新用户列表 - } catch (error) { - console.error('表单提交失败:', error); - message.error('操作失败,请重试'); - } - }; - - // 处理删除用户 - const handleDelete = async (id: number) => { - try { - await UserAPI.deleteUser(id); - message.success('用户删除成功'); - refetch(); // 刷新用户列表 - } catch (error) { - console.error('删除用户失败:', error); - message.error('删除失败,请重试'); - } - }; - - const columns = [ - { - title: '用户名', - dataIndex: 'username', - key: 'username', - }, - { - title: '昵称', - dataIndex: 'nickname', - key: 'nickname', - }, - { - title: '邮箱', - dataIndex: 'email', - key: 'email', - }, - { - title: '角色', - dataIndex: 'role', - key: 'role', - render: (role: string) => ( - - {role === 'admin' ? '管理员' : '普通用户'} - - ), - }, - { - title: '创建时间', - dataIndex: 'created_at', - key: 'created_at', - render: (date: string) => dayjs(date).format('YYYY-MM-DD HH:mm:ss'), - }, - { - title: '操作', - key: 'action', - render: (_: any, record: any) => ( - - - handleDelete(record.id)} - okText="确定" - cancelText="取消" - > - - - - ), - }, - ]; - - return ( -
- 用户管理 - -
- - - - - - - - - -
- - `共 ${total} 条记录` - }} - onChange={handleTableChange} - /> - - - {/* 创建/编辑用户模态框 */} - { - setModalVisible(false); - form.resetFields(); - }} - width={600} - > -
- - - - - - - - - - - - - {!editingUser && ( - - - - )} - - - - - -
- - ); -}; - // 文件库管理页面 export const FileLibraryPage = () => { const [loading, setLoading] = useState(false); diff --git a/client/admin/pages_users.tsx b/client/admin/pages_users.tsx new file mode 100644 index 0000000..4040f81 --- /dev/null +++ b/client/admin/pages_users.tsx @@ -0,0 +1,270 @@ +import React, { useState } from 'react'; +import { + Button, Table, Space, Form, Input, Select, + message, Modal, Card, Typography, Tag, Popconfirm +} from 'antd'; +import { useQuery } from '@tanstack/react-query'; +import dayjs from 'dayjs'; +import { UserAPI } from './api/index.ts'; + +const { Title } = Typography; + +// 用户管理页面 +export const UsersPage = () => { + const [searchParams, setSearchParams] = useState({ + page: 1, + limit: 10, + search: '' + }); + const [modalVisible, setModalVisible] = useState(false); + const [modalTitle, setModalTitle] = useState(''); + const [editingUser, setEditingUser] = useState(null); + const [form] = Form.useForm(); + + const { data: usersData, isLoading, refetch } = useQuery({ + queryKey: ['users', searchParams], + queryFn: async () => { + return await UserAPI.getUsers(searchParams); + } + }); + + const users = usersData?.data || []; + const pagination = { + current: searchParams.page, + pageSize: searchParams.limit, + total: usersData?.pagination?.total || 0 + }; + + // 处理搜索 + const handleSearch = (values: any) => { + setSearchParams(prev => ({ + ...prev, + search: values.search || '', + page: 1 + })); + }; + + // 处理分页变化 + const handleTableChange = (newPagination: any) => { + setSearchParams(prev => ({ + ...prev, + page: newPagination.current, + limit: newPagination.pageSize + })); + }; + + // 打开创建用户模态框 + const showCreateModal = () => { + setModalTitle('创建用户'); + setEditingUser(null); + form.resetFields(); + setModalVisible(true); + }; + + // 打开编辑用户模态框 + const showEditModal = (user: any) => { + setModalTitle('编辑用户'); + setEditingUser(user); + form.setFieldsValue(user); + setModalVisible(true); + }; + + // 处理模态框确认 + const handleModalOk = async () => { + try { + const values = await form.validateFields(); + + if (editingUser) { + // 编辑用户 + await UserAPI.updateUser(editingUser.id, values); + message.success('用户更新成功'); + } else { + // 创建用户 + await UserAPI.createUser(values); + message.success('用户创建成功'); + } + + setModalVisible(false); + form.resetFields(); + refetch(); // 刷新用户列表 + } catch (error) { + console.error('表单提交失败:', error); + message.error('操作失败,请重试'); + } + }; + + // 处理删除用户 + const handleDelete = async (id: number) => { + try { + await UserAPI.deleteUser(id); + message.success('用户删除成功'); + refetch(); // 刷新用户列表 + } catch (error) { + console.error('删除用户失败:', error); + message.error('删除失败,请重试'); + } + }; + + const columns = [ + { + title: '用户名', + dataIndex: 'username', + key: 'username', + }, + { + title: '昵称', + dataIndex: 'nickname', + key: 'nickname', + }, + { + title: '邮箱', + dataIndex: 'email', + key: 'email', + }, + { + title: '角色', + dataIndex: 'role', + key: 'role', + render: (role: string) => ( + + {role === 'admin' ? '管理员' : '普通用户'} + + ), + }, + { + title: '创建时间', + dataIndex: 'created_at', + key: 'created_at', + render: (date: string) => dayjs(date).format('YYYY-MM-DD HH:mm:ss'), + }, + { + title: '操作', + key: 'action', + render: (_: any, record: any) => ( + + + handleDelete(record.id)} + okText="确定" + cancelText="取消" + > + + + + ), + }, + ]; + + return ( +
+ 用户管理 + +
+ + + + + + + + + + + +
`共 ${total} 条记录` + }} + onChange={handleTableChange} + /> + + + {/* 创建/编辑用户模态框 */} + { + setModalVisible(false); + form.resetFields(); + }} + width={600} + > +
+ + + + + + + + + + + + + {!editingUser && ( + + + + )} + + + + + +
+ + ); +}; \ No newline at end of file diff --git a/client/admin/web_app.tsx b/client/admin/web_app.tsx index 1726e7b..f89e5d5 100644 --- a/client/admin/web_app.tsx +++ b/client/admin/web_app.tsx @@ -55,10 +55,14 @@ import { } from './hooks_sys.tsx'; import { - DashboardPage, - UsersPage, + DashboardPage +} from './pages_dashboard.tsx'; +import { + UsersPage +} from './pages_users.tsx'; +import { FileLibraryPage -} from './pages_sys.tsx'; +} from './pages_file_library.tsx'; import { KnowInfoPage } from './pages_know_info.tsx'; import { MessagesPage } from './pages_messages.tsx'; import {SettingsPage } from './pages_settings.tsx'; From e4f45ed9523e87e28170cfa5b164034271bc394a Mon Sep 17 00:00:00 2001 From: yourname Date: Tue, 13 May 2025 11:44:28 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=E5=B7=B2=E4=BB=8E10=E4=B8=AAAPI=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E6=96=87=E4=BB=B6=E4=B8=AD=E7=A7=BB=E9=99=A4=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E7=9A=84API=5FBASE=5FURL=E5=AE=9A=E4=B9=89=20?= =?UTF-8?q?=E6=89=80=E6=9C=89API=E8=B0=83=E7=94=A8=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BD=BF=E7=94=A8client/admin/api/index.ts?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=85=A8=E5=B1=80axios=E9=85=8D=E7=BD=AE=20?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E5=8E=9F=E6=9C=89=E5=8A=9F=E8=83=BD=E4=B8=8D?= =?UTF-8?q?=E5=8F=98=E7=9A=84=E5=90=8C=E6=97=B6=E7=AE=80=E5=8C=96=E4=BA=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/admin/api/auth.ts | 18 ++++++++---------- client/admin/api/charts.ts | 10 ++++------ client/admin/api/files.ts | 22 ++++++++++------------ client/admin/api/index.ts | 16 ++++++++++++++++ client/admin/api/know_info.ts | 13 +++++-------- client/admin/api/maps.ts | 5 ++--- client/admin/api/messages.ts | 12 +++++------- client/admin/api/sys.ts | 14 ++++++-------- client/admin/api/theme.ts | 8 +++----- client/admin/api/users.ts | 12 +++++------- 10 files changed, 64 insertions(+), 66 deletions(-) diff --git a/client/admin/api/auth.ts b/client/admin/api/auth.ts index 999fce3..cc46b82 100644 --- a/client/admin/api/auth.ts +++ b/client/admin/api/auth.ts @@ -1,8 +1,6 @@ import axios from 'axios'; import type { User } from '../../share/types.ts'; -const API_BASE_URL = '/api'; - interface AuthLoginResponse { message: string; token: string; @@ -29,7 +27,7 @@ interface AuthAPIType { export const AuthAPI: AuthAPIType = { login: async (username: string, password: string, latitude?: number, longitude?: number) => { try { - const response = await axios.post(`${API_BASE_URL}/auth/login`, { + const response = await axios.post('/auth/login', { username, password, latitude, @@ -43,7 +41,7 @@ export const AuthAPI: AuthAPIType = { register: async (username: string, email: string, password: string) => { try { - const response = await axios.post(`${API_BASE_URL}/auth/register`, { username, email, password }); + const response = await axios.post('/auth/register', { username, email, password }); return response.data; } catch (error) { throw error; @@ -52,7 +50,7 @@ export const AuthAPI: AuthAPIType = { logout: async () => { try { - const response = await axios.post(`${API_BASE_URL}/auth/logout`); + const response = await axios.post('/auth/logout'); return response.data; } catch (error) { throw error; @@ -61,7 +59,7 @@ export const AuthAPI: AuthAPIType = { getCurrentUser: async () => { try { - const response = await axios.get(`${API_BASE_URL}/auth/me`); + const response = await axios.get('/auth/me'); return response.data; } catch (error) { throw error; @@ -70,7 +68,7 @@ export const AuthAPI: AuthAPIType = { updateUser: async (userId: number, userData: Partial) => { try { - const response = await axios.put(`${API_BASE_URL}/auth/users/${userId}`, userData); + const response = await axios.put(`/auth/users/${userId}`, userData); return response.data; } catch (error) { throw error; @@ -79,7 +77,7 @@ export const AuthAPI: AuthAPIType = { changePassword: async (oldPassword: string, newPassword: string) => { try { - const response = await axios.post(`${API_BASE_URL}/auth/change-password`, { oldPassword, newPassword }); + const response = await axios.post('/auth/change-password', { oldPassword, newPassword }); return response.data; } catch (error) { throw error; @@ -88,7 +86,7 @@ export const AuthAPI: AuthAPIType = { requestPasswordReset: async (email: string) => { try { - const response = await axios.post(`${API_BASE_URL}/auth/request-password-reset`, { email }); + const response = await axios.post('/auth/request-password-reset', { email }); return response.data; } catch (error) { throw error; @@ -97,7 +95,7 @@ export const AuthAPI: AuthAPIType = { resetPassword: async (token: string, newPassword: string) => { try { - const response = await axios.post(`${API_BASE_URL}/auth/reset-password`, { token, newPassword }); + const response = await axios.post('/auth/reset-password', { token, newPassword }); return response.data; } catch (error) { throw error; diff --git a/client/admin/api/charts.ts b/client/admin/api/charts.ts index 0fa23ce..64c185e 100644 --- a/client/admin/api/charts.ts +++ b/client/admin/api/charts.ts @@ -1,7 +1,5 @@ import axios from 'axios'; -const API_BASE_URL = '/api'; - interface ChartDataResponse { message: string; data: T; @@ -32,7 +30,7 @@ interface DashboardOverviewData { export const ChartAPI = { getUserActivity: async (): Promise> => { try { - const response = await axios.get(`${API_BASE_URL}/charts/user-activity`); + const response = await axios.get('/charts/user-activity'); return response.data; } catch (error) { throw error; @@ -41,7 +39,7 @@ export const ChartAPI = { getFileUploads: async (): Promise> => { try { - const response = await axios.get(`${API_BASE_URL}/charts/file-uploads`); + const response = await axios.get('/charts/file-uploads'); return response.data; } catch (error) { throw error; @@ -50,7 +48,7 @@ export const ChartAPI = { getFileTypes: async (): Promise> => { try { - const response = await axios.get(`${API_BASE_URL}/charts/file-types`); + const response = await axios.get('/charts/file-types'); return response.data; } catch (error) { throw error; @@ -59,7 +57,7 @@ export const ChartAPI = { getDashboardOverview: async (): Promise> => { try { - const response = await axios.get(`${API_BASE_URL}/charts/dashboard-overview`); + const response = await axios.get('/charts/dashboard-overview'); return response.data; } catch (error) { throw error; diff --git a/client/admin/api/files.ts b/client/admin/api/files.ts index 3ab56bd..a0ccb2a 100644 --- a/client/admin/api/files.ts +++ b/client/admin/api/files.ts @@ -2,8 +2,6 @@ import axios from 'axios'; import type { FileLibrary, FileCategory } from '../../share/types.ts'; import type { MinioUploadPolicy, OSSUploadPolicy } from '@d8d-appcontainer/types'; -const API_BASE_URL = '/api'; - interface FileUploadPolicyResponse { message: string; data: MinioUploadPolicy | OSSUploadPolicy; @@ -59,7 +57,7 @@ interface FileCategoryDeleteResponse { export const FileAPI = { getUploadPolicy: async (filename: string, prefix: string = 'uploads/', maxSize: number = 10 * 1024 * 1024): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/upload/policy`, { + const response = await axios.get('/upload/policy', { params: { filename, prefix, maxSize } }); return response.data; @@ -70,7 +68,7 @@ export const FileAPI = { saveFileInfo: async (fileData: Partial): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/upload/save`, fileData); + const response = await axios.post('/upload/save', fileData); return response.data; } catch (error) { throw error; @@ -85,7 +83,7 @@ export const FileAPI = { keyword?: string }): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/upload/list`, { params }); + const response = await axios.get('/upload/list', { params }); return response.data; } catch (error) { throw error; @@ -94,7 +92,7 @@ export const FileAPI = { getFileInfo: async (id: number): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/upload/${id}`); + const response = await axios.get(`/upload/${id}`); return response.data; } catch (error) { throw error; @@ -103,7 +101,7 @@ export const FileAPI = { updateDownloadCount: async (id: number): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/upload/${id}/download`); + const response = await axios.post(`/upload/${id}/download`); return response.data; } catch (error) { throw error; @@ -112,7 +110,7 @@ export const FileAPI = { deleteFile: async (id: number): Promise => { try { - const response = await axios.delete(`${API_BASE_URL}/upload/${id}`); + const response = await axios.delete(`/upload/${id}`); return response.data; } catch (error) { throw error; @@ -125,7 +123,7 @@ export const FileAPI = { search?: string }): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/file-categories`, { params }); + const response = await axios.get('/file-categories', { params }); return response.data; } catch (error) { throw error; @@ -134,7 +132,7 @@ export const FileAPI = { createCategory: async (data: Partial): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/file-categories`, data); + const response = await axios.post('/file-categories', data); return response.data; } catch (error) { throw error; @@ -143,7 +141,7 @@ export const FileAPI = { updateCategory: async (id: number, data: Partial): Promise => { try { - const response = await axios.put(`${API_BASE_URL}/file-categories/${id}`, data); + const response = await axios.put(`/file-categories/${id}`, data); return response.data; } catch (error) { throw error; @@ -152,7 +150,7 @@ export const FileAPI = { deleteCategory: async (id: number): Promise => { try { - const response = await axios.delete(`${API_BASE_URL}/file-categories/${id}`); + const response = await axios.delete(`/file-categories/${id}`); return response.data; } catch (error) { throw error; diff --git a/client/admin/api/index.ts b/client/admin/api/index.ts index bf427ab..2ac6ce7 100644 --- a/client/admin/api/index.ts +++ b/client/admin/api/index.ts @@ -1,3 +1,19 @@ +import axios from 'axios'; + +// 基础配置 +export const API_BASE_URL = '/api'; +// 全局axios配置 +axios.defaults.baseURL = API_BASE_URL; + +// 获取OSS完整URL +export const getOssUrl = (path: string): string => { + // 获取全局配置中的OSS_HOST,如果不存在使用默认值 + const ossHost = (window.CONFIG?.OSS_BASE_URL) || ''; + // 确保path不以/开头 + const ossPath = path.startsWith('/') ? path.substring(1) : path; + return `${ossHost}/${ossPath}`; +}; + export * from './auth.ts'; export * from './users.ts'; export * from './files.ts'; diff --git a/client/admin/api/know_info.ts b/client/admin/api/know_info.ts index f199261..50ae3fd 100644 --- a/client/admin/api/know_info.ts +++ b/client/admin/api/know_info.ts @@ -32,9 +32,6 @@ interface KnowInfoDeleteResponse { } -const API_BASE_URL = '/api'; - - // 知识库API export const KnowInfoAPI = { // 获取知识库列表 @@ -46,7 +43,7 @@ export const KnowInfoAPI = { tags?: string; }): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/know-infos`, { params }); + const response = await axios.get('/know-infos', { params }); return response.data; } catch (error) { throw error; @@ -56,7 +53,7 @@ export const KnowInfoAPI = { // 获取单个知识详情 getKnowInfo: async (id: number): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/know-infos/${id}`); + const response = await axios.get(`/know-infos/${id}`); return response.data; } catch (error) { throw error; @@ -66,7 +63,7 @@ export const KnowInfoAPI = { // 创建知识 createKnowInfo: async (data: Partial): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/know-infos`, data); + const response = await axios.post('/know-infos', data); return response.data; } catch (error) { throw error; @@ -76,7 +73,7 @@ export const KnowInfoAPI = { // 更新知识 updateKnowInfo: async (id: number, data: Partial): Promise => { try { - const response = await axios.put(`${API_BASE_URL}/know-infos/${id}`, data); + const response = await axios.put(`/know-infos/${id}`, data); return response.data; } catch (error) { throw error; @@ -86,7 +83,7 @@ export const KnowInfoAPI = { // 删除知识 deleteKnowInfo: async (id: number): Promise => { try { - const response = await axios.delete(`${API_BASE_URL}/know-infos/${id}`); + const response = await axios.delete(`/know-infos/${id}`); return response.data; } catch (error) { throw error; diff --git a/client/admin/api/maps.ts b/client/admin/api/maps.ts index 540345c..36e6e3a 100644 --- a/client/admin/api/maps.ts +++ b/client/admin/api/maps.ts @@ -1,10 +1,9 @@ import axios from 'axios'; -import type { +import { API_BASE_URL } from './index.ts'; +import type { LoginLocation, LoginLocationDetail, } from '../../share/types.ts'; -const API_BASE_URL = '/api'; - // 地图相关API的接口类型定义 export interface LoginLocationResponse { diff --git a/client/admin/api/messages.ts b/client/admin/api/messages.ts index a5d5fa2..bc633cb 100644 --- a/client/admin/api/messages.ts +++ b/client/admin/api/messages.ts @@ -1,8 +1,6 @@ import axios from 'axios'; import type { UserMessage, Message } from '../../share/types.ts'; -const API_BASE_URL = '/api'; - interface MessagesResponse { data: UserMessage[]; pagination: { @@ -31,7 +29,7 @@ export const MessageAPI = { search?: string }): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/messages`, { params }); + const response = await axios.get('/messages', { params }); return response.data; } catch (error) { throw error; @@ -45,7 +43,7 @@ export const MessageAPI = { receiver_ids: number[] }): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/messages`, data); + const response = await axios.post('/messages', data); return response.data; } catch (error) { throw error; @@ -54,7 +52,7 @@ export const MessageAPI = { getUnreadCount: async (): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/messages/count/unread`); + const response = await axios.get('/messages/count/unread'); return response.data; } catch (error) { throw error; @@ -63,7 +61,7 @@ export const MessageAPI = { markAsRead: async (id: number): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/messages/${id}/read`); + const response = await axios.post(`/messages/${id}/read`); return response.data; } catch (error) { throw error; @@ -72,7 +70,7 @@ export const MessageAPI = { deleteMessage: async (id: number): Promise => { try { - const response = await axios.delete(`${API_BASE_URL}/messages/${id}`); + const response = await axios.delete(`/messages/${id}`); return response.data; } catch (error) { throw error; diff --git a/client/admin/api/sys.ts b/client/admin/api/sys.ts index f7dc7ff..6130a7e 100644 --- a/client/admin/api/sys.ts +++ b/client/admin/api/sys.ts @@ -1,16 +1,14 @@ import axios from 'axios'; -import type { - SystemSetting, SystemSettingGroupData, +import type { + SystemSetting, SystemSettingGroupData, } from '../../share/types.ts'; -const API_BASE_URL = '/api'; - export const SystemAPI = { // 获取所有系统设置 getSettings: async (): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/settings`); + const response = await axios.get('/settings'); return response.data.data; } catch (error) { throw error; @@ -20,7 +18,7 @@ export const SystemAPI = { // 获取指定分组的系统设置 getSettingsByGroup: async (group: string): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/settings/group/${group}`); + const response = await axios.get(`/settings/group/${group}`); return response.data.data; } catch (error) { throw error; @@ -30,7 +28,7 @@ export const SystemAPI = { // 更新系统设置 updateSettings: async (settings: Partial[]): Promise => { try { - const response = await axios.put(`${API_BASE_URL}/settings`, settings); + const response = await axios.put('/settings', settings); return response.data.data; } catch (error) { throw error; @@ -40,7 +38,7 @@ export const SystemAPI = { // 重置系统设置 resetSettings: async (): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/settings/reset`); + const response = await axios.post('/settings/reset'); return response.data.data; } catch (error) { throw error; diff --git a/client/admin/api/theme.ts b/client/admin/api/theme.ts index 863a6b2..ad9c189 100644 --- a/client/admin/api/theme.ts +++ b/client/admin/api/theme.ts @@ -1,8 +1,6 @@ import axios from 'axios'; import type { ThemeSettings } from '../../share/types.ts'; -const API_BASE_URL = '/api'; - export interface ThemeSettingsResponse { message: string; data: ThemeSettings; @@ -11,7 +9,7 @@ export interface ThemeSettingsResponse { export const ThemeAPI = { getThemeSettings: async (): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/theme`); + const response = await axios.get('/theme'); return response.data.data; } catch (error) { throw error; @@ -20,7 +18,7 @@ export const ThemeAPI = { updateThemeSettings: async (themeData: Partial): Promise => { try { - const response = await axios.put(`${API_BASE_URL}/theme`, themeData); + const response = await axios.put('/theme', themeData); return response.data.data; } catch (error) { throw error; @@ -29,7 +27,7 @@ export const ThemeAPI = { resetThemeSettings: async (): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/theme/reset`); + const response = await axios.post('/theme/reset'); return response.data.data; } catch (error) { throw error; diff --git a/client/admin/api/users.ts b/client/admin/api/users.ts index 8e6c018..052f796 100644 --- a/client/admin/api/users.ts +++ b/client/admin/api/users.ts @@ -1,8 +1,6 @@ import axios from 'axios'; import type { User } from '../../share/types.ts'; -const API_BASE_URL = '/api'; - interface UsersResponse { data: User[]; pagination: { @@ -36,7 +34,7 @@ interface UserDeleteResponse { export const UserAPI = { getUsers: async (params?: { page?: number, limit?: number, search?: string }): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/users`, { params }); + const response = await axios.get('/users', { params }); return response.data; } catch (error) { throw error; @@ -45,7 +43,7 @@ export const UserAPI = { getUser: async (userId: number): Promise => { try { - const response = await axios.get(`${API_BASE_URL}/users/${userId}`); + const response = await axios.get(`/users/${userId}`); return response.data; } catch (error) { throw error; @@ -54,7 +52,7 @@ export const UserAPI = { createUser: async (userData: Partial): Promise => { try { - const response = await axios.post(`${API_BASE_URL}/users`, userData); + const response = await axios.post('/users', userData); return response.data; } catch (error) { throw error; @@ -63,7 +61,7 @@ export const UserAPI = { updateUser: async (userId: number, userData: Partial): Promise => { try { - const response = await axios.put(`${API_BASE_URL}/users/${userId}`, userData); + const response = await axios.put(`/users/${userId}`, userData); return response.data; } catch (error) { throw error; @@ -72,7 +70,7 @@ export const UserAPI = { deleteUser: async (userId: number): Promise => { try { - const response = await axios.delete(`${API_BASE_URL}/users/${userId}`); + const response = await axios.delete(`/users/${userId}`); return response.data; } catch (error) { throw error;