feat(plans): 实现计划管理功能

- 添加计划列表、创建计划、删除计划等功能
- 实现计划数据的加载和保存
- 优化空状态和面包屑样式
- 新增 usePlanApi 和 useUserApi 组合式函数
This commit is contained in:
2025-08-06 15:07:09 +08:00
parent be2c446906
commit 4ff041854c
6 changed files with 1093 additions and 48 deletions

View File

@@ -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<string, Record<string, any>>) => {
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
}
}

View File

@@ -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')

View File

@@ -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<string | null>(null)
// 添加用户
const addUser = async (scriptId: string): Promise<UserCreateOut | null> => {
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<string, Record<string, any>>): Promise<boolean> => {
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<boolean> => {
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,
}
}

View File

@@ -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;

View File

@@ -1,12 +1,882 @@
<template>
<div class="page-container">
<h1>计划管理</h1>
<p>这里是计划管理内容的占位符</p>
<!-- 空状态 -->
<div v-if="planList.length === 0 && !loading" class="empty-state">
<div class="empty-content">
<div class="empty-icon">
<CalendarOutlined />
</div>
<h3>暂无计划</h3>
<p>创建您的第一个计划来开始使用</p>
<a-button type="primary" size="large" @click="handleAddPlan">
<template #icon>
<PlusOutlined />
</template>
创建计划
</a-button>
</div>
</div>
<!-- 有计划时显示 -->
<div v-else-if="planList.length > 0" class="plans-content">
<!-- 计划头部 -->
<div class="plans-header">
<div class="header-title">
<h1>计划管理</h1>
</div>
<a-space size="middle">
<a-button type="primary" size="large" @click="handleAddPlan">
<template #icon>
<PlusOutlined />
</template>
新建计划
</a-button>
<a-button size="large" @click="handleRefresh">
<template #icon>
<ReloadOutlined />
</template>
刷新
</a-button>
</a-space>
</div>
<!-- 计划选择器 -->
<div class="plan-selector">
<a-tabs
v-model:activeKey="activePlanId"
type="editable-card"
@edit="onTabEdit"
@change="onPlanChange"
class="plan-tabs"
>
<a-tab-pane
v-for="plan in planList"
:key="plan.id"
:tab="plan.name"
:closable="planList.length > 1"
/>
</a-tabs>
</div>
<!-- 计划内容区域 -->
<div class="plan-content" v-if="currentPlanData">
<!-- MAA计划配置 -->
<div class="maa-config-section">
<div class="section-header">
<div class="section-title">
<div class="plan-name-editor">
<a-input
v-model:value="currentPlanName"
placeholder="请输入计划名称"
size="large"
class="plan-name-input"
@blur="onPlanNameBlur"
@pressEnter="onPlanNameBlur"
/>
</div>
</div>
<div class="section-controls">
<a-space>
<span class="mode-label">模式</span>
<a-radio-group v-model:value="currentMode" @change="onModeChange" size="default">
<a-radio-button value="ALL">全局</a-radio-button>
<a-radio-button value="Weekly">周计划</a-radio-button>
</a-radio-group>
</a-space>
</div>
</div>
<!-- 使用 Ant Design 表格组件 -->
<a-table
:columns="dynamicTableColumns"
:data-source="tableData"
:pagination="false"
:scroll="{ x: 1000 }"
class="plan-table"
size="middle"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'taskName'">
<div class="task-name">
{{ record.taskName }}
</div>
</template>
<template v-else-if="record.taskName === '吃理智药'">
<a-input-number
v-model:value="record[column.key]"
size="small"
:min="0"
:max="999"
:placeholder="getPlaceholder(column.key, record.taskName)"
class="config-input"
/>
</template>
<template v-else>
<a-select
v-model:value="record[column.key]"
size="small"
:options="getSelectOptions(column.key, record.taskName)"
:placeholder="getPlaceholder(column.key, record.taskName)"
class="config-select"
allow-clear
/>
</template>
</template>
</a-table>
</div>
</div>
</div>
<!-- 悬浮保存按钮 -->
<a-float-button
type="primary"
@click="handleSave"
class="float-button"
:style="{ right: '24px' }"
>
<template #icon>
<SaveOutlined />
</template>
</a-float-button>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { CalendarOutlined, PlusOutlined, ReloadOutlined, SaveOutlined } from '@ant-design/icons-vue'
import { usePlanApi } from '../composables/usePlanApi'
// API 相关
const { getPlans, createPlan, updatePlan, deletePlan } = usePlanApi()
// 计划列表和当前选中的计划
const planList = ref<Array<{ id: string; name: string }>>([])
const activePlanId = ref<string>('')
const currentPlanData = ref<Record<string, any> | null>(null)
// 当前计划的名称和模式
const currentPlanName = ref<string>('')
const currentMode = ref<'ALL' | 'Weekly'>('ALL')
// 表格列配置(全局和周计划模式都使用相同的表格结构)
const dynamicTableColumns = computed(() => {
return tableColumns.value
})
// 表格列配置
const tableColumns = ref([
{
title: '配置项',
dataIndex: 'taskName',
key: 'taskName',
width: 120,
fixed: 'left',
align: 'center',
},
{
title: '全局',
dataIndex: 'ALL',
key: 'ALL',
width: 120,
align: 'center',
},
{
title: '周一',
dataIndex: 'Monday',
key: 'Monday',
width: 120,
align: 'center',
},
{
title: '周二',
dataIndex: 'Tuesday',
key: 'Tuesday',
width: 120,
align: 'center',
},
{
title: '周三',
dataIndex: 'Wednesday',
key: 'Wednesday',
width: 120,
align: 'center',
},
{
title: '周四',
dataIndex: 'Thursday',
key: 'Thursday',
width: 120,
align: 'center',
},
{
title: '周五',
dataIndex: 'Friday',
key: 'Friday',
width: 120,
align: 'center',
},
{
title: '周六',
dataIndex: 'Saturday',
key: 'Saturday',
width: 120,
align: 'center',
},
{
title: '周日',
dataIndex: 'Sunday',
key: 'Sunday',
width: 120,
align: 'center',
},
])
// 表格数据
const tableData = ref([
{
key: 'MedicineNumb',
taskName: '吃理智药',
ALL: 0,
Monday: 0,
Tuesday: 0,
Wednesday: 0,
Thursday: 0,
Friday: 0,
Saturday: 0,
Sunday: 0,
},
{
key: 'SeriesNumb',
taskName: '连战次数',
ALL: '0',
Monday: '0',
Tuesday: '0',
Wednesday: '0',
Thursday: '0',
Friday: '0',
Saturday: '0',
Sunday: '0',
},
{
key: 'Stage',
taskName: '关卡选择',
ALL: '-',
Monday: '-',
Tuesday: '-',
Wednesday: '-',
Thursday: '-',
Friday: '-',
Saturday: '-',
Sunday: '-',
},
{
key: 'Stage_1',
taskName: '备选-1',
ALL: '-',
Monday: '-',
Tuesday: '-',
Wednesday: '-',
Thursday: '-',
Friday: '-',
Saturday: '-',
Sunday: '-',
},
{
key: 'Stage_2',
taskName: '备选-2',
ALL: '-',
Monday: '-',
Tuesday: '-',
Wednesday: '-',
Thursday: '-',
Friday: '-',
Saturday: '-',
Sunday: '-',
},
{
key: 'Stage_3',
taskName: '备选-3',
ALL: '-',
Monday: '-',
Tuesday: '-',
Wednesday: '-',
Thursday: '-',
Friday: '-',
Saturday: '-',
Sunday: '-',
},
{
key: 'Stage_Remain',
taskName: '剩余理智',
ALL: '-',
Monday: '-',
Tuesday: '-',
Wednesday: '-',
Thursday: '-',
Friday: '-',
Saturday: '-',
Sunday: '-',
},
])
// 关卡数据配置
const STAGE_DAILY_INFO = [
{ value: '-', text: '当前/上次', days: [1, 2, 3, 4, 5, 6, 7] },
{ value: '1-7', text: '1-7', days: [1, 2, 3, 4, 5, 6, 7] },
{ value: 'R8-11', text: 'R8-11', days: [1, 2, 3, 4, 5, 6, 7] },
{ value: '12-17-HARD', text: '12-17-HARD', days: [1, 2, 3, 4, 5, 6, 7] },
{ value: 'CE-6', text: '龙门币-6/5', days: [2, 4, 6, 7] },
{ value: 'AP-5', text: '红票-5', days: [1, 4, 6, 7] },
{ value: 'CA-5', text: '技能-5', days: [2, 3, 5, 7] },
{ value: 'LS-6', text: '经验-6/5', days: [1, 2, 3, 4, 5, 6, 7] },
{ value: 'SK-5', text: '碳-5', days: [1, 3, 5, 6] },
{ value: 'PR-A-1', text: '奶/盾芯片', days: [1, 4, 5, 7] },
{ value: 'PR-A-2', text: '奶/盾芯片组', days: [1, 4, 5, 7] },
{ value: 'PR-B-1', text: '术/狙芯片', days: [1, 2, 5, 6] },
{ value: 'PR-B-2', text: '术/狙芯片组', days: [1, 2, 5, 6] },
{ value: 'PR-C-1', text: '先/辅芯片', days: [3, 4, 6, 7] },
{ value: 'PR-C-2', text: '先/辅芯片组', days: [3, 4, 6, 7] },
{ value: 'PR-D-1', text: '近/特芯片', days: [2, 3, 6, 7] },
{ value: 'PR-D-2', text: '近/特芯片组', days: [2, 3, 6, 7] },
]
// 获取星期对应的数字
const getDayNumber = (columnKey: string) => {
const dayMap: Record<string, number> = {
ALL: 0, // 全局显示所有选项
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6,
Sunday: 7,
}
return dayMap[columnKey] || 0
}
// 获取选择器选项
const getSelectOptions = (columnKey: string, taskName: string) => {
switch (taskName) {
case '连战次数':
return [
{ label: '0', value: '0' },
{ label: '1', value: '1' },
{ label: '2', value: '2' },
{ label: '3', value: '3' },
{ label: '4', value: '4' },
{ label: '5', value: '5' },
{ label: '6', value: '6' },
{ label: 'AUTO', value: '-1' },
]
case '关卡选择':
case '备选-1':
case '备选-2':
case '备选-3':
case '剩余理智': {
const dayNumber = getDayNumber(columnKey)
// 如果是全局列,显示所有选项
if (dayNumber === 0) {
return STAGE_DAILY_INFO.map(stage => ({
label: stage.text,
value: stage.value,
}))
}
// 根据星期过滤可用的关卡
return STAGE_DAILY_INFO.filter(stage => stage.days.includes(dayNumber)).map(stage => ({
label: stage.text,
value: stage.value,
}))
}
default:
return []
}
}
// 获取占位符
const getPlaceholder = (columnKey: string, taskName: string) => {
switch (taskName) {
case '吃理智药':
return '输入数量'
case '连战次数':
return '选择次数'
case '关卡选择':
case '备选-1':
case '备选-2':
case '备选-3':
return '1-7'
case '剩余理智':
return '1-8'
default:
return '请选择'
}
}
// 模式切换处理
const onModeChange = () => {
// 模式切换时只更新本地状态,不自动保存
// 用户需要手动点击保存按钮
}
// 计划名称编辑失焦处理
const onPlanNameBlur = () => {
// 当用户编辑完计划名称后,更新标签页显示的名称
if (activePlanId.value) {
const currentPlan = planList.value.find(plan => plan.id === activePlanId.value)
if (currentPlan) {
currentPlan.name = currentPlanName.value || `计划 ${planList.value.indexOf(currentPlan) + 1}`
}
}
}
// 手动保存处理
const handleSave = async () => {
if (!activePlanId.value) {
message.warning('请先选择一个计划')
return
}
try {
await savePlanData()
message.success('保存成功')
} catch (error) {
message.error('保存失败')
}
}
// 标签页编辑处理
const onTabEdit = async (targetKey: string | MouseEvent, action: 'add' | 'remove') => {
if (action === 'add') {
await handleAddPlan()
} else if (action === 'remove' && typeof targetKey === 'string') {
await handleRemovePlan(targetKey)
}
}
// 添加计划
const handleAddPlan = async () => {
try {
const response = await createPlan('MaaPlan')
const defaultName = `计划 ${planList.value.length + 1}`
const newPlan = {
id: response.planId,
name: defaultName,
}
planList.value.push(newPlan)
activePlanId.value = newPlan.id
// 设置默认名称到输入框中
currentPlanName.value = defaultName
await loadPlanData(newPlan.id)
} catch (error) {
console.error('添加计划失败:', error)
}
}
// 删除计划
const handleRemovePlan = async (planId: string) => {
try {
await deletePlan(planId)
const index = planList.value.findIndex(plan => plan.id === planId)
if (index > -1) {
planList.value.splice(index, 1)
if (activePlanId.value === planId) {
activePlanId.value = planList.value[0]?.id || ''
if (activePlanId.value) {
await loadPlanData(activePlanId.value)
}
}
}
} catch (error) {
console.error('删除计划失败:', error)
}
}
// 计划切换
const onPlanChange = async (planId: string) => {
await loadPlanData(planId)
}
// 加载计划数据
const loadPlanData = async (planId: string) => {
try {
const response = await getPlans(planId)
currentPlanData.value = response.data
// 根据API响应数据更新表格数据
if (response.data && response.data[planId]) {
const planData = response.data[planId]
// 更新计划名称和模式
if (planData.Info) {
// 如果API返回的名称为空并且当前输入框也为空则使用默认名称
const apiName = planData.Info.Name || ''
if (!apiName && !currentPlanName.value) {
// 找到当前计划在列表中的位置,使用默认名称
const currentPlan = planList.value.find(plan => plan.id === planId)
if (currentPlan) {
currentPlanName.value = currentPlan.name
}
} else if (apiName) {
// 如果API有名称使用API的名称
currentPlanName.value = apiName
}
// 如果API名称为空但当前输入框有值保持当前值不变
currentMode.value = planData.Info.Mode || 'ALL'
}
// 更新表格数据
tableData.value.forEach(row => {
const fieldKey = row.key
// 更新每个时间段的数据
const timeKeys = [
'ALL',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
]
timeKeys.forEach(timeKey => {
if (planData[timeKey] && planData[timeKey][fieldKey] !== undefined) {
row[timeKey] = planData[timeKey][fieldKey]
}
})
})
}
} catch (error) {
console.error('加载计划数据失败:', error)
}
}
// 初始化
const initPlans = async () => {
try {
const response = await getPlans()
if (response.index && response.index.length > 0) {
planList.value = response.index.map((item: any, index: number) => {
// API响应格式: {"uid": "xxx", "type": "MaaPlanConfig"}
const planId = item.uid
const planName = response.data[planId]?.Info?.Name || `计划 ${index + 1}`
return {
id: planId,
name: planName,
}
})
activePlanId.value = planList.value[0].id
await loadPlanData(activePlanId.value)
} else {
// 如果没有计划,显示空状态而不是自动创建
currentPlanData.value = null
}
} catch (error) {
console.error('初始化计划失败:', error)
// 显示空状态
currentPlanData.value = null
}
}
// 保存计划数据
const savePlanData = async () => {
if (!activePlanId.value) return
try {
// 构建符合API要求的数据结构
const planData: Record<string, Record<string, any>> = {}
// 为每个时间段构建数据
const timeKeys = [
'ALL',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
]
timeKeys.forEach(timeKey => {
planData[timeKey] = {}
tableData.value.forEach(row => {
planData[timeKey][row.key] = row[timeKey]
})
})
// 添加Info信息
planData['Info'] = {
Mode: currentMode.value,
Name: currentPlanName.value,
}
await updatePlan(activePlanId.value, planData)
} catch (error) {
console.error('保存计划数据失败:', error)
throw error
}
}
// 刷新计划列表
const handleRefresh = async () => {
await initPlans()
// message.success('刷新成功')
}
// 移除自动保存功能,改为手动保存
// 用户需要点击悬浮按钮才能保存数据
onMounted(() => {
initPlans()
})
</script>
<style scoped>
.page-container {
padding: 20px;
.plans-container {
height: 100vh;
display: flex;
flex-direction: column;
background: var(--ant-color-bg-layout);
padding: 32px;
}
/* 空状态样式 */
.empty-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.empty-content {
text-align: center;
padding: 48px;
background: var(--ant-color-bg-container);
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid var(--ant-color-border-secondary);
}
.empty-icon {
font-size: 64px;
color: var(--ant-color-text-tertiary);
margin-bottom: 24px;
}
.empty-content h3 {
font-size: 20px;
font-weight: 600;
color: var(--ant-color-text);
margin: 0 0 8px 0;
}
.empty-content p {
font-size: 14px;
color: var(--ant-color-text-secondary);
margin: 0 0 32px 0;
}
/* 计划内容区域 */
.plans-content {
flex: 1;
display: flex;
flex-direction: column;
background: var(--ant-color-bg-container);
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid var(--ant-color-border-secondary);
overflow: hidden;
}
/* 计划头部样式 */
.plans-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24px 32px;
background: var(--ant-color-bg-container);
border-bottom: 1px solid var(--ant-color-border-secondary);
margin-bottom: 5px;
}
.header-title h1 {
margin: 0;
font-size: 32px;
font-weight: 700;
color: var(--ant-color-text);
background: linear-gradient(135deg, var(--ant-color-primary), var(--ant-color-primary-hover));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* 计划选择器 */
.plan-selector {
padding: 0 32px;
background: var(--ant-color-bg-container);
border-bottom: 1px solid var(--ant-color-border-secondary);
}
.plan-tabs :deep(.ant-tabs-content-holder) {
display: none;
}
.plan-tabs :deep(.ant-tabs-nav) {
margin: 0;
}
.plan-tabs :deep(.ant-tabs-tab) {
padding: 12px 16px;
font-weight: 500;
}
/* 计划内容 */
.plan-content {
flex: 1;
padding: 24px 32px;
overflow: auto;
background: var(--ant-color-bg-layout);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.section-title h3 {
margin: 0;
color: var(--ant-color-text);
font-size: 18px;
font-weight: 600;
}
.section-controls {
display: flex;
align-items: center;
}
.mode-label {
color: var(--ant-color-text-secondary);
font-size: 14px;
font-weight: 500;
}
/* 计划名称编辑器样式 */
.plan-name-editor {
display: flex;
align-items: center;
}
.plan-name-input {
max-width: 300px;
font-size: 18px;
font-weight: 600;
}
.plan-name-input :deep(.ant-input) {
border: 1px solid transparent;
background: transparent;
color: var(--ant-color-text);
font-size: 18px;
font-weight: 600;
padding: 4px 8px;
border-radius: 6px;
transition: all 0.3s ease;
}
.plan-name-input :deep(.ant-input:hover) {
border-color: var(--ant-color-border);
background: var(--ant-color-bg-container);
}
.plan-name-input :deep(.ant-input:focus) {
border-color: var(--ant-color-primary);
background: var(--ant-color-bg-container);
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
/* 表格样式 */
.plan-table {
background: var(--ant-color-bg-container);
}
.plan-table :deep(.ant-table-thead > tr > th) {
background: var(--ant-color-bg-layout);
border-bottom: 2px solid var(--ant-color-border);
font-weight: 600;
color: var(--ant-color-text);
text-align: center;
}
.plan-table :deep(.ant-table-tbody > tr > td) {
border-bottom: 1px solid var(--ant-color-border-secondary);
text-align: center;
padding: 12px 8px;
}
.plan-table :deep(.ant-table-tbody > tr:hover > td) {
background: var(--ant-color-bg-layout);
}
.task-name {
font-weight: 600;
color: var(--ant-color-text);
text-align: center;
}
.config-select {
width: 100%;
min-width: 100px;
}
.config-select :deep(.ant-select-selector) {
border-radius: 6px;
border: 1px solid var(--ant-color-border);
background: var(--ant-color-bg-container);
}
.config-select :deep(.ant-select-selector:hover) {
border-color: var(--ant-color-primary-hover);
}
.config-select :deep(.ant-select-focused .ant-select-selector) {
border-color: var(--ant-color-primary);
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
/* 输入框样式 */
.config-input {
width: 100%;
min-width: 100px;
}
.config-input :deep(.ant-input-number-focused) {
border-color: var(--ant-color-primary);
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
/* 深色模式适配 */
@media (prefers-color-scheme: dark) {
.maa-config-section {
border-color: var(--ant-color-border-secondary);
border-radius: 16px;
}
.section-header {
border-color: var(--ant-color-border-secondary);
border-radius: 16px;
}
}
.float-button {
width: 60px;
height: 60px;
}
</style>

View File

@@ -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);
}