From 4ff041854cb721087d2a0d3c882247cfd677ded1 Mon Sep 17 00:00:00 2001 From: AoXuan Date: Wed, 6 Aug 2025 15:07:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(plans):=20=E5=AE=9E=E7=8E=B0=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加计划列表、创建计划、删除计划等功能 - 实现计划数据的加载和保存 - 优化空状态和面包屑样式 - 新增 usePlanApi 和 useUserApi 组合式函数 --- frontend/src/composables/usePlanApi.ts | 101 +++ frontend/src/composables/useTheme.ts | 2 +- frontend/src/composables/useUserApi.ts | 115 ++++ frontend/src/style.css | 2 +- frontend/src/views/Plans.vue | 880 ++++++++++++++++++++++++- frontend/src/views/ScriptEdit.vue | 41 -- 6 files changed, 1093 insertions(+), 48 deletions(-) create mode 100644 frontend/src/composables/usePlanApi.ts create mode 100644 frontend/src/composables/useUserApi.ts diff --git a/frontend/src/composables/usePlanApi.ts b/frontend/src/composables/usePlanApi.ts new file mode 100644 index 0000000..7bac76f --- /dev/null +++ b/frontend/src/composables/usePlanApi.ts @@ -0,0 +1,101 @@ +import { ref } from 'vue' +import { message } from 'ant-design-vue' +import { Service } from '../api' +import type { PlanCreateIn, PlanGetIn, PlanUpdateIn, PlanDeleteIn, PlanReorderIn } from '../api' + +export function usePlanApi() { + const loading = ref(false) + + // 获取所有计划 + const getPlans = async (planId?: string) => { + loading.value = true + try { + const params: PlanGetIn = planId ? { planId } : {} + const response = await Service.getPlanApiPlanGetPost(params) + return response + } catch (error) { + console.error('获取计划失败:', error) + message.error('获取计划失败') + throw error + } finally { + loading.value = false + } + } + + // 创建计划 + const createPlan = async (type: string) => { + loading.value = true + try { + const params: PlanCreateIn = { type } + const response = await Service.addPlanApiPlanAddPost(params) + message.success('创建计划成功') + return response + } catch (error) { + console.error('创建计划失败:', error) + message.error('创建计划失败') + throw error + } finally { + loading.value = false + } + } + + // 更新计划 + const updatePlan = async (planId: string, data: Record>) => { + loading.value = true + try { + const params: PlanUpdateIn = { planId, data } + const response = await Service.updatePlanApiPlanUpdatePost(params) + // message.success('更新计划成功') + return response + } catch (error) { + console.error('更新计划失败:', error) + message.error('更新计划失败') + throw error + } finally { + loading.value = false + } + } + + // 删除计划 + const deletePlan = async (planId: string) => { + loading.value = true + try { + const params: PlanDeleteIn = { planId } + const response = await Service.deletePlanApiPlanDeletePost(params) + message.success('删除计划成功') + return response + } catch (error) { + console.error('删除计划失败:', error) + message.error('删除计划失败') + throw error + } finally { + loading.value = false + } + } + + // 重新排序计划 + const reorderPlans = async (indexList: string[]) => { + loading.value = true + try { + const params: PlanReorderIn = { indexList } + const response = await Service.reorderPlanApiPlanOrderPost(params) + message.success('重新排序成功') + return response + } catch (error) { + console.error('重新排序失败:', error) + message.error('重新排序失败') + throw error + } finally { + loading.value = false + } + } + + return { + loading, + getPlans, + createPlan, + updatePlan, + deletePlan, + reorderPlans + } +} \ No newline at end of file diff --git a/frontend/src/composables/useTheme.ts b/frontend/src/composables/useTheme.ts index 7801c8a..89c1b2b 100644 --- a/frontend/src/composables/useTheme.ts +++ b/frontend/src/composables/useTheme.ts @@ -99,7 +99,7 @@ const updateCSSVariables = () => { root.style.setProperty('--ant-color-bg-layout', '#f5f5f5') root.style.setProperty('--ant-color-bg-elevated', '#ffffff') root.style.setProperty('--ant-color-border', '#d9d9d9') - root.style.setProperty('--ant-color-border-secondary', '#f0f0f0') + root.style.setProperty('--ant-color-border-secondary', '#d9d9d9') root.style.setProperty('--ant-color-error', '#ff4d4f') root.style.setProperty('--ant-color-success', '#52c41a') root.style.setProperty('--ant-color-warning', '#faad14') diff --git a/frontend/src/composables/useUserApi.ts b/frontend/src/composables/useUserApi.ts new file mode 100644 index 0000000..a7fe0aa --- /dev/null +++ b/frontend/src/composables/useUserApi.ts @@ -0,0 +1,115 @@ +import { ref } from 'vue' +import { message } from 'ant-design-vue' +import { Service } from '@/api' +import type { UserInBase, UserCreateOut, UserUpdateIn, UserDeleteIn } from '@/api' + +export function useUserApi() { + const loading = ref(false) + const error = ref(null) + + // 添加用户 + const addUser = async (scriptId: string): Promise => { + loading.value = true + error.value = null + + try { + const requestData: UserInBase = { + scriptId + } + + const response = await Service.addUserApiScriptsUserAddPost(requestData) + + if (response.code !== 200) { + const errorMsg = response.message || '添加用户失败' + message.error(errorMsg) + throw new Error(errorMsg) + } + + return response + } catch (err) { + const errorMsg = err instanceof Error ? err.message : '添加用户失败' + error.value = errorMsg + if (!err.message?.includes('HTTP error')) { + message.error(errorMsg) + } + return null + } finally { + loading.value = false + } + } + + // 更新用户 + const updateUser = async (scriptId: string, userId: string, userData: Record>): Promise => { + loading.value = true + error.value = null + + try { + const requestData: UserUpdateIn = { + scriptId, + userId, + data: userData + } + + const response = await Service.updateUserApiScriptsUserUpdatePost(requestData) + + if (response.code !== 200) { + const errorMsg = response.message || '更新用户失败' + message.error(errorMsg) + throw new Error(errorMsg) + } + + message.success(response.message || '用户更新成功') + return true + } catch (err) { + const errorMsg = err instanceof Error ? err.message : '更新用户失败' + error.value = errorMsg + if (!err.message?.includes('HTTP error')) { + message.error(errorMsg) + } + return false + } finally { + loading.value = false + } + } + + // 删除用户 + const deleteUser = async (scriptId: string, userId: string): Promise => { + loading.value = true + error.value = null + + try { + const requestData: UserDeleteIn = { + scriptId, + userId + } + + const response = await Service.deleteUserApiScriptsUserDeletePost(requestData) + + if (response.code !== 200) { + const errorMsg = response.message || '删除用户失败' + message.error(errorMsg) + throw new Error(errorMsg) + } + + message.success(response.message || '用户删除成功') + return true + } catch (err) { + const errorMsg = err instanceof Error ? err.message : '删除用户失败' + error.value = errorMsg + if (!err.message?.includes('HTTP error')) { + message.error(errorMsg) + } + return false + } finally { + loading.value = false + } + } + + return { + loading, + error, + addUser, + updateUser, + deleteUser, + } +} \ No newline at end of file diff --git a/frontend/src/style.css b/frontend/src/style.css index e3edf68..c84fa94 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -21,7 +21,7 @@ --ant-color-bg-layout: #f5f5f5; --ant-color-bg-elevated: #ffffff; --ant-color-border: #d9d9d9; - --ant-color-border-secondary: #f0f0f0; + --ant-color-border-secondary: #303030; --ant-color-error: #ff4d4f; --ant-color-success: #52c41a; --ant-color-warning: #faad14; diff --git a/frontend/src/views/Plans.vue b/frontend/src/views/Plans.vue index 4f9eaac..f888fa2 100644 --- a/frontend/src/views/Plans.vue +++ b/frontend/src/views/Plans.vue @@ -1,12 +1,882 @@ + + \ No newline at end of file diff --git a/frontend/src/views/ScriptEdit.vue b/frontend/src/views/ScriptEdit.vue index 0f9f094..5845ecc 100644 --- a/frontend/src/views/ScriptEdit.vue +++ b/frontend/src/views/ScriptEdit.vue @@ -1169,10 +1169,6 @@ const getCardTitle = () => { transition: color 0.3s ease; } -.breadcrumb-link:hover { - color: var(--ant-color-primary); -} - .breadcrumb-current { display: flex; align-items: center; @@ -1188,39 +1184,6 @@ const getCardTitle = () => { transition: all 0.3s ease; } -/* 按钮样式 */ -.cancel-button { - padding: 0 12px; - font-size: 16px; - font-weight: 500; - border-radius: 12px; - border: 2px solid var(--ant-color-border); - background: var(--ant-color-bg-container); - color: var(--ant-color-text); - transition: all 0.3s ease; -} - -.cancel-button:hover { - border-color: var(--ant-color-error); - color: var(--ant-color-error); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(255, 77, 79, 0.2); -} - -.save-button { - padding: 0 12px; - font-size: 16px; - font-weight: 600; - border-radius: 12px; - box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); - transition: all 0.3s ease; -} - -.save-button:hover { - transform: translateY(-2px); - box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4); -} - /* 内容区域 */ .script-edit-content { flex: 1; @@ -1459,10 +1422,6 @@ const getCardTitle = () => { box-shadow: 0 6px 16px rgba(24, 144, 255, 0.5); } - .cancel-button:hover { - box-shadow: 0 4px 12px rgba(255, 77, 79, 0.3); - } - .path-input-group:focus-within { box-shadow: 0 0 0 4px rgba(24, 144, 255, 0.2); }