2105 lines
66 KiB
Vue
2105 lines
66 KiB
Vue
<template>
|
||
<div class="user-edit-header">
|
||
<div class="header-nav">
|
||
<a-breadcrumb class="breadcrumb">
|
||
<a-breadcrumb-item>
|
||
<router-link to="/scripts">脚本管理</router-link>
|
||
</a-breadcrumb-item>
|
||
<a-breadcrumb-item>
|
||
<router-link :to="`/scripts/${scriptId}/edit`" class="breadcrumb-link">
|
||
{{ scriptName }}
|
||
</router-link>
|
||
</a-breadcrumb-item>
|
||
<a-breadcrumb-item>
|
||
{{ isEdit ? '编辑用户' : '添加用户' }}
|
||
</a-breadcrumb-item>
|
||
</a-breadcrumb>
|
||
</div>
|
||
|
||
<a-space size="middle">
|
||
<a-button
|
||
v-if="formData.Info.Mode !== '简洁'"
|
||
type="primary"
|
||
ghost
|
||
size="large"
|
||
@click="handleMAAConfig"
|
||
:loading="maaConfigLoading"
|
||
>
|
||
<template #icon>
|
||
<SettingOutlined />
|
||
</template>
|
||
MAA配置
|
||
</a-button>
|
||
<a-button size="large" @click="handleCancel" class="cancel-button">
|
||
<template #icon>
|
||
<ArrowLeftOutlined />
|
||
</template>
|
||
返回
|
||
</a-button>
|
||
<a-button
|
||
type="primary"
|
||
size="large"
|
||
@click="handleSubmit"
|
||
:loading="loading"
|
||
class="save-button"
|
||
>
|
||
<template #icon>
|
||
<SaveOutlined />
|
||
</template>
|
||
{{ isEdit ? '保存修改' : '创建用户' }}
|
||
</a-button>
|
||
</a-space>
|
||
</div>
|
||
|
||
<div class="user-edit-content">
|
||
<a-card class="config-card">
|
||
<a-form ref="formRef" :model="formData" :rules="rules" layout="vertical" class="config-form">
|
||
<!-- 基本信息 -->
|
||
<div class="form-section">
|
||
<div class="section-header">
|
||
<h3>基本信息</h3>
|
||
</div>
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item name="userName" required>
|
||
<template #label>
|
||
<a-tooltip title="用于区分用户的名称,相同名称的用户将被视为同一用户进行统计">
|
||
<span class="form-label">
|
||
用户名
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-input
|
||
v-model:value="formData.userName"
|
||
placeholder="请输入用户名"
|
||
:disabled="loading"
|
||
size="large"
|
||
class="modern-input"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item name="userId">
|
||
<template #label>
|
||
<a-tooltip title="用于切换账号,官服输入手机号,B服输入B站ID,无需切换则留空">
|
||
<span class="form-label">
|
||
账号ID
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-input
|
||
v-model:value="formData.userId"
|
||
placeholder="请输入账号ID"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item name="status">
|
||
<template #label>
|
||
<a-tooltip title="是否启用该用户">
|
||
<span class="form-label">
|
||
启用状态
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-select v-model:value="formData.Info.Status" size="large">
|
||
<a-select-option :value="true">是</a-select-option>
|
||
<a-select-option :value="false">否</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item :name="['Info', 'Password']">
|
||
<template #label>
|
||
<a-tooltip title="用户密码,仅用于存储以防遗忘,此外无任何作用">
|
||
<span class="form-label">
|
||
密码
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-input-password
|
||
v-model:value="formData.Info.Password"
|
||
placeholder="密码仅用于储存以防遗忘,此外无任何作用"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item name="server">
|
||
<template #label>
|
||
<a-tooltip title="选择用户所在的游戏服务器">
|
||
<span class="form-label">
|
||
服务器
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-select
|
||
v-model:value="formData.Info.Server"
|
||
placeholder="请选择服务器"
|
||
:disabled="loading"
|
||
:options="serverOptions"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item name="remainedDay">
|
||
<template #label>
|
||
<a-tooltip title="账号剩余的有效天数,「-1」表示无限">
|
||
<span class="form-label">
|
||
剩余天数
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-input-number
|
||
v-model:value="formData.Info.RemainedDay"
|
||
:min="-1"
|
||
:max="9999"
|
||
placeholder="0"
|
||
:disabled="loading"
|
||
size="large"
|
||
style="width: 100%"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip title="简洁模式下配置沿用脚本全局配置,详细模式下沿用用户自定义配置">
|
||
<span class="form-label">
|
||
用户配置模式
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-select
|
||
v-model:value="formData.Info.Mode"
|
||
:options="[
|
||
{ label: '简洁', value: '简洁' },
|
||
{ label: '详细', value: '详细' },
|
||
]"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip title="选择基建模式,自定义基建模式需要自行选择自定义基建配置文件">
|
||
<span class="form-label">
|
||
基建模式
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-select
|
||
v-model:value="formData.Info.InfrastMode"
|
||
:options="[
|
||
{ label: '常规模式', value: 'Normal' },
|
||
{ label: '一键轮休', value: 'Rotation' },
|
||
{ label: '自定义基建', value: 'Custom' },
|
||
]"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<!-- 自定义基建配置文件选择 -->
|
||
<a-row :gutter="24" v-if="formData.Info.InfrastMode === 'Custom'">
|
||
<a-col :span="24">
|
||
<a-form-item name="infrastructureConfigFile">
|
||
<template #label>
|
||
<a-tooltip title="选择自定义基建配置JSON文件">
|
||
<span class="form-label">
|
||
基建配置文件
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<div style="display: flex; gap: 12px; align-items: center">
|
||
<a-input
|
||
v-model:value="formData.Info.InfrastPath"
|
||
placeholder="请选择基建配置JSON文件"
|
||
readonly
|
||
size="large"
|
||
style="flex: 1"
|
||
/>
|
||
<a-button
|
||
type="primary"
|
||
ghost
|
||
@click="selectInfrastructureConfig"
|
||
:disabled="loading"
|
||
size="large"
|
||
>
|
||
选择文件
|
||
</a-button>
|
||
<a-button
|
||
type="primary"
|
||
@click="importInfrastructureConfig"
|
||
:disabled="loading || !infrastructureConfigPath || !isEdit"
|
||
:loading="infrastructureImporting"
|
||
size="large"
|
||
>
|
||
导入配置
|
||
</a-button>
|
||
</div>
|
||
<div style="color: #999; font-size: 12px; margin-top: 4px">
|
||
请选择有效的基建配置JSON文件,点击「导入配置」按钮将其应用到当前用户。如果已经导入,可以忽略此选择框。
|
||
</div>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<a-form-item name="notes">
|
||
<template #label>
|
||
<a-tooltip title="为用户添加备注信息">
|
||
<span class="form-label">
|
||
备注
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-textarea
|
||
v-model:value="formData.Info.Notes"
|
||
placeholder="请输入备注信息"
|
||
:rows="4"
|
||
:disabled="loading"
|
||
class="modern-input"
|
||
/>
|
||
</a-form-item>
|
||
</div>
|
||
|
||
<!-- 关卡配置 -->
|
||
<div class="form-section">
|
||
<div class="section-header">
|
||
<h3>关卡配置</h3>
|
||
</div>
|
||
<a-row :gutter="24">
|
||
<a-col :span="12">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip title="剿灭代理关卡选择">
|
||
<span class="form-label">
|
||
剿灭代理
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-select
|
||
v-model:value="formData.Info.Annihilation"
|
||
:options="[
|
||
{ label: '关闭', value: 'Close' },
|
||
{ label: '当期剿灭', value: 'Annihilation' },
|
||
{ label: '切尔诺伯格', value: 'Chernobog@Annihilation' },
|
||
{ label: '龙门外环', value: 'LungmenOutskirts@Annihilation' },
|
||
{ label: '龙门市区', value: 'LungmenDowntown@Annihilation' },
|
||
]"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="12">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip title="可选择「固定」或「计划表」">
|
||
<span class="form-label">
|
||
关卡配置模式
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<a-select
|
||
v-model:value="formData.Info.StageMode"
|
||
:options="stageModeOptions"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
<a-row :gutter="24">
|
||
<a-col :span="6">
|
||
<a-form-item name="medicineNumb">
|
||
<template #label>
|
||
<a-tooltip title="吃理智药数量">
|
||
<span class="form-label">
|
||
吃理智药数量
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">{{ displayMedicineNumb }}</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示输入框 -->
|
||
<a-input-number
|
||
v-else
|
||
v-model:value="displayMedicineNumb"
|
||
:min="0"
|
||
:max="9999"
|
||
placeholder="0"
|
||
:disabled="loading"
|
||
size="large"
|
||
style="width: 100%"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip
|
||
title="AUTO:自动识别关卡最大代理倍率,保持最大代理倍率且使用理智药后理智不溢出;数值(1~6):按设定倍率执行代理;不切换:不调整游戏内代理倍率设定"
|
||
>
|
||
<span class="form-label">
|
||
连战次数
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">
|
||
{{
|
||
displaySeriesNumb === '0'
|
||
? 'AUTO'
|
||
: displaySeriesNumb === '-1'
|
||
? '不切换'
|
||
: displaySeriesNumb
|
||
}}
|
||
</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示选择框 -->
|
||
<a-select
|
||
v-else
|
||
v-model:value="displaySeriesNumb"
|
||
:options="[
|
||
{ label: 'AUTO', 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: '不切换', value: '-1' },
|
||
]"
|
||
:disabled="loading"
|
||
size="large"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip title="关卡选择">
|
||
<span class="form-label">
|
||
关卡选择
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">
|
||
{{ displayStage === '-' ? '当前/上次' : displayStage || '不选择' }}
|
||
</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示选择框 -->
|
||
<a-select
|
||
v-else
|
||
v-model:value="displayStage"
|
||
:disabled="loading"
|
||
size="large"
|
||
placeholder="选择或输入自定义关卡"
|
||
>
|
||
<template #dropdownRender="{ menuNode: menu }">
|
||
<v-nodes :vnodes="menu" />
|
||
<a-divider style="margin: 4px 0" />
|
||
<a-space style="padding: 4px 8px" size="small">
|
||
<a-input
|
||
ref="stageInputRef"
|
||
v-model:value="customStageName"
|
||
placeholder="输入自定义关卡,如: 11-8"
|
||
style="flex: 1"
|
||
size="small"
|
||
@keyup.enter="addCustomStage"
|
||
/>
|
||
<a-button type="text" size="small" @click="addCustomStage">
|
||
<template #icon>
|
||
<PlusOutlined />
|
||
</template>
|
||
添加关卡
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
<a-select-option
|
||
v-for="option in stageOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
<template v-if="option.label.includes('|')">
|
||
<span>{{ option.label.split('|')[0] }}</span>
|
||
<a-tag color="green" size="small" style="margin-left: 8px">
|
||
{{ option.label.split('|')[1] }}
|
||
</a-tag>
|
||
</template>
|
||
<template v-else>
|
||
{{ option.label }}
|
||
<a-tag
|
||
v-if="isCustomStage(option.value)"
|
||
color="blue"
|
||
size="small"
|
||
style="margin-left: 8px"
|
||
>
|
||
自定义
|
||
</a-tag>
|
||
</template>
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
<a-row :gutter="24">
|
||
<a-col :span="6">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip
|
||
title="备选关卡-1,所有备选关卡均选择「当前/上次」时视为不使用备选关卡"
|
||
>
|
||
<span class="form-label">
|
||
备选关卡-1
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">
|
||
{{ displayStage1 === '-' ? '当前/上次' : displayStage1 || '不选择' }}
|
||
</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示选择框 -->
|
||
<a-select
|
||
v-else
|
||
v-model:value="displayStage1"
|
||
:disabled="loading"
|
||
size="large"
|
||
placeholder="选择或输入自定义关卡"
|
||
>
|
||
<template #dropdownRender="{ menuNode: menu }">
|
||
<v-nodes :vnodes="menu" />
|
||
<a-divider style="margin: 4px 0" />
|
||
<a-space style="padding: 4px 8px" size="small">
|
||
<a-input
|
||
ref="stage1InputRef"
|
||
v-model:value="customStage1Name"
|
||
placeholder="输入自定义关卡,如: 11-8"
|
||
style="flex: 1"
|
||
size="small"
|
||
@keyup.enter="addCustomStage1"
|
||
/>
|
||
<a-button type="text" size="small" @click="addCustomStage1">
|
||
<template #icon>
|
||
<PlusOutlined />
|
||
</template>
|
||
添加关卡
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
<a-select-option
|
||
v-for="option in stageOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
<template v-if="option.label.includes('|')">
|
||
<span>{{ option.label.split('|')[0] }}</span>
|
||
<a-tag color="green" size="small" style="margin-left: 8px">
|
||
{{ option.label.split('|')[1] }}
|
||
</a-tag>
|
||
</template>
|
||
<template v-else>
|
||
{{ option.label }}
|
||
<a-tag
|
||
v-if="isCustomStage(option.value)"
|
||
color="blue"
|
||
size="small"
|
||
style="margin-left: 8px"
|
||
>
|
||
自定义
|
||
</a-tag>
|
||
</template>
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip
|
||
title="备选关卡-2,所有备选关卡均选择「当前/上次」时视为不使用备选关卡"
|
||
>
|
||
<span class="form-label">
|
||
备选关卡-2
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">
|
||
{{ displayStage2 === '-' ? '当前/上次' : displayStage2 || '不选择' }}
|
||
</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示选择框 -->
|
||
<a-select
|
||
v-else
|
||
v-model:value="displayStage2"
|
||
:disabled="loading"
|
||
size="large"
|
||
placeholder="选择或输入自定义关卡"
|
||
>
|
||
<template #dropdownRender="{ menuNode: menu }">
|
||
<v-nodes :vnodes="menu" />
|
||
<a-divider style="margin: 4px 0" />
|
||
<a-space style="padding: 4px 8px" size="small">
|
||
<a-input
|
||
ref="stage2InputRef"
|
||
v-model:value="customStage2Name"
|
||
placeholder="输入自定义关卡,如: 11-8"
|
||
style="flex: 1"
|
||
size="small"
|
||
@keyup.enter="addCustomStage2"
|
||
/>
|
||
<a-button type="text" size="small" @click="addCustomStage2">
|
||
<template #icon>
|
||
<PlusOutlined />
|
||
</template>
|
||
添加关卡
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
<a-select-option
|
||
v-for="option in stageOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
<template v-if="option.label.includes('|')">
|
||
<span>{{ option.label.split('|')[0] }}</span>
|
||
<a-tag color="green" size="small" style="margin-left: 8px">
|
||
{{ option.label.split('|')[1] }}
|
||
</a-tag>
|
||
</template>
|
||
<template v-else>
|
||
{{ option.label }}
|
||
<a-tag
|
||
v-if="isCustomStage(option.value)"
|
||
color="blue"
|
||
size="small"
|
||
style="margin-left: 8px"
|
||
>
|
||
自定义
|
||
</a-tag>
|
||
</template>
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip
|
||
title="备选关卡-3,所有备选关卡均选择「当前/上次」时视为不使用备选关卡"
|
||
>
|
||
<span class="form-label">
|
||
备选关卡-3
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">
|
||
{{ displayStage3 === '-' ? '当前/上次' : displayStage3 || '不选择' }}
|
||
</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示选择框 -->
|
||
<a-select
|
||
v-else
|
||
v-model:value="displayStage3"
|
||
:disabled="loading"
|
||
size="large"
|
||
placeholder="选择或输入自定义关卡"
|
||
>
|
||
<template #dropdownRender="{ menuNode: menu }">
|
||
<v-nodes :vnodes="menu" />
|
||
<a-divider style="margin: 4px 0" />
|
||
<a-space style="padding: 4px 8px" size="small">
|
||
<a-input
|
||
ref="stage3InputRef"
|
||
v-model:value="customStage3Name"
|
||
placeholder="输入自定义关卡,如: 11-8"
|
||
style="flex: 1"
|
||
size="small"
|
||
@keyup.enter="addCustomStage3"
|
||
/>
|
||
<a-button type="text" size="small" @click="addCustomStage3">
|
||
<template #icon>
|
||
<PlusOutlined />
|
||
</template>
|
||
添加关卡
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
<a-select-option
|
||
v-for="option in stageOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
<template v-if="option.label.includes('|')">
|
||
<span>{{ option.label.split('|')[0] }}</span>
|
||
<a-tag color="green" size="small" style="margin-left: 8px">
|
||
{{ option.label.split('|')[1] }}
|
||
</a-tag>
|
||
</template>
|
||
<template v-else>
|
||
{{ option.label }}
|
||
<a-tag
|
||
v-if="isCustomStage(option.value)"
|
||
color="blue"
|
||
size="small"
|
||
style="margin-left: 8px"
|
||
>
|
||
自定义
|
||
</a-tag>
|
||
</template>
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="mode">
|
||
<template #label>
|
||
<a-tooltip title="剩余理智关卡,选择「不选择」时视为不使用剩余理智关卡">
|
||
<span class="form-label">
|
||
剩余理智关卡
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</span>
|
||
</a-tooltip>
|
||
</template>
|
||
<!-- 计划模式:显示只读文本 -->
|
||
<div v-if="isPlanMode" class="plan-mode-display">
|
||
<div class="plan-value">
|
||
{{ displayStageRemain === '-' ? '不选择' : displayStageRemain || '不选择' }}
|
||
</div>
|
||
<div class="plan-source">来自计划表</div>
|
||
</div>
|
||
<!-- 固定模式:显示选择框 -->
|
||
<a-select
|
||
v-else
|
||
v-model:value="displayStageRemain"
|
||
:disabled="loading"
|
||
size="large"
|
||
placeholder="选择或输入自定义关卡"
|
||
>
|
||
<template #dropdownRender="{ menuNode: menu }">
|
||
<v-nodes :vnodes="menu" />
|
||
<a-divider style="margin: 4px 0" />
|
||
<a-space style="padding: 4px 8px" size="small">
|
||
<a-input
|
||
ref="stageRemainInputRef"
|
||
v-model:value="customStageRemainName"
|
||
placeholder="输入自定义关卡,如: 11-8"
|
||
style="flex: 1"
|
||
size="small"
|
||
@keyup.enter="addCustomStageRemain"
|
||
/>
|
||
<a-button type="text" size="small" @click="addCustomStageRemain">
|
||
<template #icon>
|
||
<PlusOutlined />
|
||
</template>
|
||
添加关卡
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
<a-select-option
|
||
v-for="option in stageRemainOptions"
|
||
:key="option.value"
|
||
:value="option.value"
|
||
>
|
||
<template v-if="option.label.includes('|')">
|
||
<span>{{ option.label.split('|')[0] }}</span>
|
||
<a-tag color="green" size="small" style="margin-left: 8px">
|
||
{{ option.label.split('|')[1] }}
|
||
</a-tag>
|
||
</template>
|
||
<template v-else>
|
||
{{ option.label }}
|
||
<a-tag
|
||
v-if="isCustomStage(option.value)"
|
||
color="blue"
|
||
size="small"
|
||
style="margin-left: 8px"
|
||
>
|
||
自定义
|
||
</a-tag>
|
||
</template>
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
|
||
<!-- 任务配置 -->
|
||
<div class="form-section">
|
||
<div class="section-header">
|
||
<h3>任务配置</h3>
|
||
</div>
|
||
<a-row :gutter="24">
|
||
<a-col :span="6">
|
||
<a-form-item name="ifWakeUp" label="开始唤醒">
|
||
<a-switch v-model:checked="formData.Task.IfWakeUp" :disabled="loading" />
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="ifRecruiting" label="自动公招">
|
||
<a-switch v-model:checked="formData.Task.IfRecruiting" :disabled="loading" />
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="ifBase" label="基建换班">
|
||
<a-switch v-model:checked="formData.Task.IfBase" :disabled="loading" />
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="ifCombat" label="刷理智">
|
||
<a-switch v-model:checked="formData.Task.IfCombat" :disabled="loading" />
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
<a-row :gutter="24">
|
||
<a-col :span="6">
|
||
<a-form-item name="ifMall" label="获取信用及购物">
|
||
<a-switch v-model:checked="formData.Task.IfMall" :disabled="loading" />
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="ifMission" label="领取奖励">
|
||
<a-switch v-model:checked="formData.Task.IfMission" :disabled="loading" />
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="ifAutoRoguelike">
|
||
<template #label>
|
||
<a-tooltip title="未完全适配,请谨慎使用">
|
||
<span>自动肉鸽 </span>
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</a-tooltip>
|
||
</template>
|
||
<a-switch v-model:checked="formData.Task.IfAutoRoguelike" :disabled="true" />
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col :span="6">
|
||
<a-form-item name="ifReclamation">
|
||
<template #label>
|
||
<a-tooltip title="暂不支持,等待适配中~">
|
||
<span>生息演算 </span>
|
||
<QuestionCircleOutlined class="help-icon" />
|
||
</a-tooltip>
|
||
</template>
|
||
<a-switch v-model:checked="formData.Task.IfReclamation" :disabled="true" />
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
|
||
<!-- 森空岛配置 -->
|
||
<div class="form-section">
|
||
<div class="section-header">
|
||
<h3>森空岛配置</h3>
|
||
<a
|
||
href="https://doc.auto-mas.top/docs/advanced-features.html#%E8%8E%B7%E5%8F%96%E9%B9%B0%E8%A7%92%E7%BD%91%E7%BB%9C%E9%80%9A%E8%A1%8C%E8%AF%81%E7%99%BB%E5%BD%95%E5%87%AD%E8%AF%81"
|
||
target="_blank"
|
||
class="section-doc-link"
|
||
title="查看森空岛签到配置文档"
|
||
>
|
||
文档
|
||
</a>
|
||
</div>
|
||
<a-row :gutter="24" align="middle">
|
||
<a-col :span="6">
|
||
<span style="font-weight: 500">森空岛签到</span>
|
||
</a-col>
|
||
<a-col :span="18">
|
||
<a-switch v-model:checked="formData.Info.IfSkland" :disabled="loading" />
|
||
<span class="switch-description">开启后将启用森空岛签到功能</span>
|
||
</a-col>
|
||
</a-row>
|
||
<a-row :gutter="24" style="margin-top: 16px">
|
||
<a-col :span="24">
|
||
<span style="font-weight: 500">森空岛Token</span>
|
||
<a-input-password
|
||
v-model:value="formData.Info.SklandToken"
|
||
:disabled="loading || !formData.Info.IfSkland"
|
||
placeholder="请输入森空岛Token"
|
||
size="large"
|
||
style="margin-top: 8px; width: 100%"
|
||
allow-clear
|
||
/>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
|
||
<!-- 通知配置 -->
|
||
<div class="form-section">
|
||
<div class="section-header">
|
||
<h3>通知配置</h3>
|
||
</div>
|
||
<a-row :gutter="24" align="middle">
|
||
<a-col :span="6">
|
||
<span style="font-weight: 500">启用通知</span>
|
||
</a-col>
|
||
<a-col :span="18">
|
||
<a-switch v-model:checked="formData.Notify.Enabled" :disabled="loading" />
|
||
<span class="switch-description">启用后将发送此用户的任务通知到选中的渠道</span>
|
||
</a-col>
|
||
</a-row>
|
||
<!-- 发送统计/六星等可选通知 -->
|
||
<a-row :gutter="24" style="margin-top: 16px">
|
||
<a-col :span="6">
|
||
<span style="font-weight: 500">通知内容</span>
|
||
</a-col>
|
||
<a-col :span="18" style="display: flex; gap: 32px">
|
||
<a-checkbox
|
||
v-model:checked="formData.Notify.IfSendStatistic"
|
||
:disabled="loading || !formData.Notify.Enabled"
|
||
>统计信息
|
||
</a-checkbox>
|
||
<a-checkbox
|
||
v-model:checked="formData.Notify.IfSendSixStar"
|
||
:disabled="loading || !formData.Notify.Enabled"
|
||
>公开招募高资喜报
|
||
</a-checkbox>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<!-- 邮件通知 -->
|
||
<a-row :gutter="24" style="margin-top: 16px">
|
||
<a-col :span="6">
|
||
<a-checkbox
|
||
v-model:checked="formData.Notify.IfSendMail"
|
||
:disabled="loading || !formData.Notify.Enabled"
|
||
>邮件通知
|
||
</a-checkbox>
|
||
</a-col>
|
||
<a-col :span="18">
|
||
<a-input
|
||
v-model:value="formData.Notify.ToAddress"
|
||
placeholder="请输入收件人邮箱地址"
|
||
:disabled="loading || !formData.Notify.Enabled || !formData.Notify.IfSendMail"
|
||
size="large"
|
||
style="width: 100%"
|
||
/>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<!-- Server酱通知 -->
|
||
<a-row :gutter="24" style="margin-top: 16px">
|
||
<a-col :span="6">
|
||
<a-checkbox
|
||
v-model:checked="formData.Notify.IfServerChan"
|
||
:disabled="loading || !formData.Notify.Enabled"
|
||
>Server酱
|
||
</a-checkbox>
|
||
</a-col>
|
||
<a-col :span="18" style="display: flex; gap: 8px">
|
||
<a-input
|
||
v-model:value="formData.Notify.ServerChanKey"
|
||
placeholder="请输入SENDKEY"
|
||
:disabled="loading || !formData.Notify.Enabled || !formData.Notify.IfServerChan"
|
||
size="large"
|
||
style="flex: 2"
|
||
/>
|
||
</a-col>
|
||
</a-row>
|
||
|
||
<!-- 企业微信群机器人通知 -->
|
||
<a-row :gutter="24" style="margin-top: 16px">
|
||
<a-col :span="6">
|
||
<a-checkbox
|
||
v-model:checked="formData.Notify.IfCompanyWebHookBot"
|
||
:disabled="loading || !formData.Notify.Enabled"
|
||
>企业微信群机器人
|
||
</a-checkbox>
|
||
</a-col>
|
||
<a-col :span="18">
|
||
<a-input
|
||
v-model:value="formData.Notify.CompanyWebHookBotUrl"
|
||
placeholder="请输入机器人Webhook地址"
|
||
:disabled="
|
||
loading || !formData.Notify.Enabled || !formData.Notify.IfCompanyWebHookBot
|
||
"
|
||
size="large"
|
||
style="width: 100%"
|
||
class="modern-input"
|
||
/>
|
||
</a-col>
|
||
</a-row>
|
||
</div>
|
||
</a-form>
|
||
</a-card>
|
||
</div>
|
||
|
||
<a-float-button
|
||
type="primary"
|
||
@click="handleSubmit"
|
||
class="float-button"
|
||
:style="{
|
||
right: '24px',
|
||
}"
|
||
>
|
||
<template #icon>
|
||
<SaveOutlined />
|
||
</template>
|
||
</a-float-button>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { message } from 'ant-design-vue'
|
||
import {
|
||
ArrowLeftOutlined,
|
||
PlusOutlined,
|
||
QuestionCircleOutlined,
|
||
SaveOutlined,
|
||
SettingOutlined,
|
||
} from '@ant-design/icons-vue'
|
||
import type { FormInstance, Rule } from 'ant-design-vue/es/form'
|
||
import { useUserApi } from '@/composables/useUserApi'
|
||
import { useScriptApi } from '@/composables/useScriptApi'
|
||
import { usePlanApi } from '@/composables/usePlanApi'
|
||
import { useWebSocket } from '@/composables/useWebSocket'
|
||
import { Service } from '@/api'
|
||
import { GetStageIn } from '@/api/models/GetStageIn'
|
||
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
const { addUser, updateUser, getUsers, loading: userLoading } = useUserApi()
|
||
const { getScript } = useScriptApi()
|
||
const { getPlans } = usePlanApi()
|
||
const { subscribe, unsubscribe } = useWebSocket()
|
||
|
||
const formRef = ref<FormInstance>()
|
||
const loading = computed(() => userLoading.value)
|
||
|
||
// 路由参数
|
||
const scriptId = route.params.scriptId as string
|
||
const userId = route.params.userId as string
|
||
const isEdit = computed(() => !!userId)
|
||
|
||
// 脚本信息
|
||
const scriptName = ref('')
|
||
|
||
// MAA配置相关
|
||
const maaConfigLoading = ref(false)
|
||
const maaWebsocketId = ref<string | null>(null)
|
||
|
||
// 基建配置文件相关
|
||
const infrastructureConfigPath = ref('')
|
||
const infrastructureImporting = ref(false)
|
||
|
||
// 服务器选项
|
||
const serverOptions = [
|
||
{ label: '官服', value: 'Official' },
|
||
{ label: 'B服', value: 'Bilibili' },
|
||
{ label: '国际服(YoStarEN)', value: 'YoStarEN' },
|
||
{ label: '日服(YoStarJP)', value: 'YoStarJP' },
|
||
{ label: '韩服(YoStarKR)', value: 'YoStarKR' },
|
||
{ label: '繁中服(txwy)', value: 'txwy' },
|
||
]
|
||
|
||
// 关卡选项
|
||
const stageOptions = ref<any[]>([{ label: '不选择', value: '' }])
|
||
|
||
// 剩余理智关卡专用选项(将"当前/上次"改为"不选择")
|
||
const stageRemainOptions = computed(() => {
|
||
return stageOptions.value.map(option => {
|
||
if (option.value === '-') {
|
||
return { ...option, label: option.label.replace('当前/上次', '不选择') }
|
||
}
|
||
return option
|
||
})
|
||
})
|
||
|
||
// 判断值是否为自定义关卡
|
||
const isCustomStage = (value: string) => {
|
||
if (!value || value === '' || value === '-') return false
|
||
|
||
// 检查是否在从API加载的关卡列表中
|
||
const predefinedStage = stageOptions.value.find(
|
||
option => option.value === value && !option.isCustom
|
||
)
|
||
|
||
return !predefinedStage
|
||
}
|
||
|
||
// 关卡配置模式选项
|
||
const stageModeOptions = ref<any[]>([{ label: '固定', value: 'Fixed' }])
|
||
|
||
// 计划模式状态
|
||
const isPlanMode = computed(() => {
|
||
return formData.Info.StageMode !== 'Fixed'
|
||
})
|
||
const planModeConfig = ref<any>(null)
|
||
|
||
// 计算属性用于显示正确的值(来自计划表或用户配置)
|
||
const displayMedicineNumb = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.MedicineNumb !== undefined) {
|
||
return planModeConfig.value.MedicineNumb
|
||
}
|
||
return formData.Info.MedicineNumb
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.MedicineNumb = value
|
||
}
|
||
},
|
||
})
|
||
|
||
const displaySeriesNumb = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.SeriesNumb !== undefined) {
|
||
return planModeConfig.value.SeriesNumb
|
||
}
|
||
return formData.Info.SeriesNumb
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.SeriesNumb = value
|
||
}
|
||
},
|
||
})
|
||
|
||
const displayStage = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.Stage !== undefined) {
|
||
return planModeConfig.value.Stage
|
||
}
|
||
return formData.Info.Stage
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage = value
|
||
}
|
||
},
|
||
})
|
||
|
||
const displayStage1 = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.Stage_1 !== undefined) {
|
||
return planModeConfig.value.Stage_1
|
||
}
|
||
return formData.Info.Stage_1
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_1 = value
|
||
}
|
||
},
|
||
})
|
||
|
||
const displayStage2 = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.Stage_2 !== undefined) {
|
||
return planModeConfig.value.Stage_2
|
||
}
|
||
return formData.Info.Stage_2
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_2 = value
|
||
}
|
||
},
|
||
})
|
||
|
||
const displayStage3 = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.Stage_3 !== undefined) {
|
||
return planModeConfig.value.Stage_3
|
||
}
|
||
return formData.Info.Stage_3
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_3 = value
|
||
}
|
||
},
|
||
})
|
||
|
||
const displayStageRemain = computed({
|
||
get: () => {
|
||
if (isPlanMode.value && planModeConfig.value?.Stage_Remain !== undefined) {
|
||
return planModeConfig.value.Stage_Remain
|
||
}
|
||
return formData.Info.Stage_Remain
|
||
},
|
||
set: value => {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_Remain = value
|
||
}
|
||
},
|
||
})
|
||
|
||
// 获取计划当前配置
|
||
const getPlanCurrentConfig = (planData: any) => {
|
||
if (!planData) return null
|
||
|
||
const mode = planData.Info?.Mode || 'ALL'
|
||
|
||
if (mode === 'ALL') {
|
||
return planData.ALL || null
|
||
} else if (mode === 'Weekly') {
|
||
// 获取+12时区的当前时间
|
||
const now = new Date()
|
||
const utc12Time = new Date(now.getTime() + 12 * 60 * 60 * 1000)
|
||
|
||
// 如果是凌晨4点前,算作前一天
|
||
if (utc12Time.getHours() < 4) {
|
||
utc12Time.setDate(utc12Time.getDate() - 1)
|
||
}
|
||
|
||
const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
|
||
const today = weekdays[utc12Time.getDay()]
|
||
|
||
console.log('计划表周模式调试:', {
|
||
原始时间: now.toISOString(),
|
||
UTC12时间: utc12Time.toISOString(),
|
||
星期: today,
|
||
计划数据: planData,
|
||
})
|
||
|
||
// 优先使用今天的配置,如果没有或为空则使用ALL配置
|
||
const todayConfig = planData[today]
|
||
if (todayConfig && Object.keys(todayConfig).length > 0) {
|
||
return todayConfig
|
||
}
|
||
return planData.ALL || null
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
// MAA脚本默认用户数据
|
||
const getDefaultMAAUserData = () => ({
|
||
Info: {
|
||
Name: '',
|
||
Id: '',
|
||
Password: '',
|
||
Server: 'Official',
|
||
MedicineNumb: 0,
|
||
RemainedDay: -1,
|
||
SeriesNumb: '0',
|
||
Notes: '',
|
||
Status: true,
|
||
Mode: '简洁',
|
||
InfrastMode: 'Normal',
|
||
InfrastPath: '',
|
||
Routine: true,
|
||
Annihilation: 'Annihilation',
|
||
Stage: '1-7',
|
||
StageMode: 'Fixed',
|
||
Stage_1: '',
|
||
Stage_2: '',
|
||
Stage_3: '',
|
||
Stage_Remain: '',
|
||
IfSkland: false,
|
||
SklandToken: '',
|
||
},
|
||
Task: {
|
||
IfWakeUp: true,
|
||
IfBase: true,
|
||
IfCombat: true,
|
||
IfMall: true,
|
||
IfMission: true,
|
||
IfRecruiting: true,
|
||
IfReclamation: false,
|
||
IfAutoRoguelike: false,
|
||
},
|
||
Notify: {
|
||
Enabled: false,
|
||
ToAddress: '',
|
||
IfSendMail: false,
|
||
IfSendSixStar: false,
|
||
IfSendStatistic: false,
|
||
IfServerChan: false,
|
||
IfCompanyWebHookBot: false,
|
||
ServerChanKey: '',
|
||
ServerChanChannel: '',
|
||
ServerChanTag: '',
|
||
CompanyWebHookBotUrl: '',
|
||
},
|
||
Data: {
|
||
CustomInfrastPlanIndex: '',
|
||
IfPassCheck: false,
|
||
LastAnnihilationDate: '',
|
||
LastProxyDate: '',
|
||
LastSklandDate: '',
|
||
ProxyTimes: 0,
|
||
},
|
||
})
|
||
|
||
// 创建扁平化的表单数据,用于表单验证
|
||
const formData = reactive({
|
||
// 扁平化的验证字段
|
||
userName: '',
|
||
userId: '',
|
||
// 嵌套的实际数据
|
||
...getDefaultMAAUserData(),
|
||
})
|
||
|
||
// 表单验证规则
|
||
const rules = computed(() => {
|
||
const baseRules: Record<string, Rule[]> = {
|
||
userName: [
|
||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||
{ min: 1, max: 50, message: '用户名长度应在1-50个字符之间', trigger: 'blur' },
|
||
],
|
||
}
|
||
return baseRules
|
||
})
|
||
|
||
// 同步扁平化字段与嵌套数据
|
||
watch(
|
||
() => formData.Info.Name,
|
||
newVal => {
|
||
if (formData.userName !== newVal) {
|
||
formData.userName = newVal || ''
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
)
|
||
|
||
watch(
|
||
() => formData.Info.Id,
|
||
newVal => {
|
||
if (formData.userId !== newVal) {
|
||
formData.userId = newVal || ''
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
)
|
||
|
||
watch(
|
||
() => formData.userName,
|
||
newVal => {
|
||
if (formData.Info.Name !== newVal) {
|
||
formData.Info.Name = newVal || ''
|
||
}
|
||
}
|
||
)
|
||
|
||
watch(
|
||
() => formData.userId,
|
||
newVal => {
|
||
if (formData.Info.Id !== newVal) {
|
||
formData.Info.Id = newVal || ''
|
||
}
|
||
}
|
||
)
|
||
|
||
// 加载脚本信息
|
||
const loadScriptInfo = async () => {
|
||
try {
|
||
const script = await getScript(scriptId)
|
||
if (script) {
|
||
scriptName.value = script.name
|
||
|
||
// 如果是编辑模式,加载用户数据
|
||
if (isEdit.value) {
|
||
await loadUserData()
|
||
}
|
||
} else {
|
||
message.error('脚本不存在')
|
||
handleCancel()
|
||
}
|
||
} catch (error) {
|
||
console.error('加载脚本信息失败:', error)
|
||
message.error('加载脚本信息失败')
|
||
}
|
||
}
|
||
|
||
// 加载用户数据
|
||
const loadUserData = async () => {
|
||
try {
|
||
const userResponse = await getUsers(scriptId, userId)
|
||
|
||
if (userResponse && userResponse.code === 200) {
|
||
// 查找指定的用户数据
|
||
const userIndex = userResponse.index.find(index => index.uid === userId)
|
||
if (userIndex && userResponse.data[userId]) {
|
||
const userData = userResponse.data[userId] as any
|
||
|
||
// 填充MAA用户数据
|
||
if (userIndex.type === 'MaaUserConfig') {
|
||
Object.assign(formData, {
|
||
Info: { ...getDefaultMAAUserData().Info, ...userData.Info },
|
||
Task: { ...getDefaultMAAUserData().Task, ...userData.Task },
|
||
Notify: { ...getDefaultMAAUserData().Notify, ...userData.Notify },
|
||
Data: { ...getDefaultMAAUserData().Data, ...userData.Data },
|
||
})
|
||
}
|
||
|
||
// 同步扁平化字段 - 使用nextTick确保数据更新完成后再同步
|
||
await nextTick()
|
||
formData.userName = formData.Info.Name || ''
|
||
formData.userId = formData.Info.Id || ''
|
||
|
||
// 检查并添加自定义关卡到选项列表
|
||
const stageFields = ['Stage', 'Stage_1', 'Stage_2', 'Stage_3', 'Stage_Remain']
|
||
stageFields.forEach(field => {
|
||
const stageValue = (formData.Info as any)[field]
|
||
if (stageValue && isCustomStage(stageValue)) {
|
||
// 检查是否已存在
|
||
const exists = stageOptions.value.find((option: any) => option.value === stageValue)
|
||
if (!exists) {
|
||
stageOptions.value.push({
|
||
label: stageValue,
|
||
value: stageValue,
|
||
isCustom: true,
|
||
})
|
||
}
|
||
}
|
||
})
|
||
|
||
console.log('用户数据加载成功:', {
|
||
userName: formData.userName,
|
||
userId: formData.userId,
|
||
InfoName: formData.Info.Name,
|
||
InfoId: formData.Info.Id,
|
||
fullData: formData,
|
||
})
|
||
} else {
|
||
message.error('用户不存在')
|
||
handleCancel()
|
||
}
|
||
} else {
|
||
message.error('获取用户数据失败')
|
||
handleCancel()
|
||
}
|
||
} catch (error) {
|
||
console.error('加载用户数据失败:', error)
|
||
message.error('加载用户数据失败')
|
||
}
|
||
}
|
||
|
||
const loadStageOptions = async () => {
|
||
try {
|
||
const response = await Service.getStageComboxApiInfoComboxStagePost({
|
||
type: GetStageIn.type.TODAY,
|
||
})
|
||
if (response && response.code === 200 && response.data) {
|
||
stageOptions.value = [...response.data].map(option => ({
|
||
...option,
|
||
isCustom: false, // 明确标记从API加载的关卡为非自定义
|
||
}))
|
||
}
|
||
} catch (error) {
|
||
console.error('加载关卡选项失败:', error)
|
||
}
|
||
}
|
||
|
||
const loadStageModeOptions = async () => {
|
||
try {
|
||
const response = await Service.getPlanComboxApiInfoComboxPlanPost()
|
||
if (response && response.code === 200 && response.data) {
|
||
stageModeOptions.value = response.data
|
||
}
|
||
} catch (error) {
|
||
console.error('加载关卡配置模式选项失败:', error)
|
||
// 保持默认的固定选项
|
||
}
|
||
}
|
||
|
||
// 替换 VNodes 组件定义
|
||
const VNodes = defineComponent({
|
||
props: { vnodes: { type: Object, required: true } },
|
||
setup(props) {
|
||
return () => props.vnodes as any
|
||
},
|
||
})
|
||
|
||
// 选择基建配置文件
|
||
const selectInfrastructureConfig = async () => {
|
||
try {
|
||
const path = await (window as any).electronAPI?.selectFile([
|
||
{ name: 'JSON 文件', extensions: ['json'] },
|
||
{ name: '所有文件', extensions: ['*'] },
|
||
])
|
||
if (path && path.length > 0) {
|
||
infrastructureConfigPath.value = path
|
||
formData.Info.InfrastPath = path[0]
|
||
message.success('文件选择成功')
|
||
}
|
||
} catch (error) {
|
||
console.error('文件选择失败:', error)
|
||
message.error('文件选择失败')
|
||
}
|
||
}
|
||
|
||
// 导入基建配置
|
||
const importInfrastructureConfig = async () => {
|
||
if (!infrastructureConfigPath.value) {
|
||
message.warning('请先选择配置文件')
|
||
return
|
||
}
|
||
if (!isEdit.value) {
|
||
message.warning('请先保存用户后再导入配置')
|
||
return
|
||
}
|
||
try {
|
||
infrastructureImporting.value = true
|
||
const result = await Service.importInfrastructureApiScriptsUserInfrastructurePost({
|
||
scriptId: scriptId,
|
||
userId: userId,
|
||
jsonFile: infrastructureConfigPath.value[0],
|
||
})
|
||
if (result && result.code === 200) {
|
||
message.success('基建配置导入成功')
|
||
infrastructureConfigPath.value = ''
|
||
} else {
|
||
message.error('基建配置导入失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('基建配置导入失败:', error)
|
||
message.error('基建配置导入失败')
|
||
} finally {
|
||
infrastructureImporting.value = false
|
||
}
|
||
}
|
||
|
||
const handleSubmit = async () => {
|
||
try {
|
||
await formRef.value?.validate()
|
||
|
||
// 确保扁平化字段同步到嵌套数据
|
||
formData.Info.Name = formData.userName
|
||
formData.Info.Id = formData.userId
|
||
|
||
console.log('提交前的表单数据:', {
|
||
userName: formData.userName,
|
||
userId: formData.userId,
|
||
InfoName: formData.Info.Name,
|
||
InfoId: formData.Info.Id,
|
||
isEdit: isEdit.value,
|
||
})
|
||
|
||
// 排除 InfrastPath 字段
|
||
const { InfrastPath, ...infoWithoutInfrastPath } = formData.Info
|
||
|
||
// 构建提交数据
|
||
const userData = {
|
||
Info: { ...infoWithoutInfrastPath },
|
||
Task: { ...formData.Task },
|
||
Notify: { ...formData.Notify },
|
||
Data: { ...formData.Data },
|
||
}
|
||
|
||
if (isEdit.value) {
|
||
// 编辑模式
|
||
const result = await updateUser(scriptId, userId, userData)
|
||
if (result) {
|
||
message.success('用户更新成功')
|
||
handleCancel()
|
||
}
|
||
} else {
|
||
// 添加模式
|
||
const result = await addUser(scriptId)
|
||
if (result) {
|
||
// 创建成功后立即更新用户数据
|
||
try {
|
||
const updateResult = await updateUser(scriptId, result.userId, userData)
|
||
console.log('用户数据更新结果:', updateResult)
|
||
|
||
if (updateResult) {
|
||
message.success('用户创建成功')
|
||
handleCancel()
|
||
} else {
|
||
message.error('用户创建成功,但数据更新失败,请手动编辑用户信息')
|
||
// 不跳转,让用户可以重新保存
|
||
}
|
||
} catch (updateError) {
|
||
console.error('更新用户数据时发生错误:', updateError)
|
||
message.error('用户创建成功,但数据更新失败,请手动编辑用户信息')
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('表单验证失败:', error)
|
||
}
|
||
}
|
||
|
||
const handleMAAConfig = async () => {
|
||
if (!isEdit.value) {
|
||
message.warning('请先保存用户后再进行MAA配置')
|
||
return
|
||
}
|
||
|
||
try {
|
||
maaConfigLoading.value = true
|
||
|
||
// 如果已有连接,先断开
|
||
if (maaWebsocketId.value) {
|
||
unsubscribe(maaWebsocketId.value)
|
||
maaWebsocketId.value = null
|
||
}
|
||
|
||
// 直接订阅(旧 connect 参数移除)
|
||
const subId = userId
|
||
subscribe(subId, {
|
||
onError: error => {
|
||
console.error(`用户 ${formData.userName} MAA配置错误:`, error)
|
||
message.error(`MAA配置连接失败: ${error}`)
|
||
maaWebsocketId.value = null
|
||
},
|
||
})
|
||
|
||
maaWebsocketId.value = subId
|
||
message.success(`已开始配置用户 ${formData.userName} 的MAA设置`)
|
||
} catch (error) {
|
||
console.error('MAA配置失败:', error)
|
||
message.error('MAA配置失败')
|
||
} finally {
|
||
maaConfigLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 自定义关卡相关
|
||
const customStageName = ref('')
|
||
const customStage1Name = ref('')
|
||
const customStage2Name = ref('')
|
||
const customStage3Name = ref('')
|
||
const customStageRemainName = ref('')
|
||
|
||
// 输入框引用
|
||
const stageInputRef = ref()
|
||
const stage1InputRef = ref()
|
||
const stage2InputRef = ref()
|
||
const stage3InputRef = ref()
|
||
const stageRemainInputRef = ref()
|
||
|
||
// 验证关卡名称格式
|
||
const validateStageName = (stageName: string): boolean => {
|
||
if (!stageName || !stageName.trim()) {
|
||
return false
|
||
}
|
||
|
||
// 简单的关卡名称验证,可以根据实际需要调整
|
||
const stagePattern = /^[a-zA-Z0-9\-_\u4e00-\u9fa5]+$/
|
||
return stagePattern.test(stageName.trim())
|
||
}
|
||
|
||
// 添加自定义关卡到选项列表
|
||
const addStageToOptions = (stageName: string) => {
|
||
if (!stageName || !stageName.trim()) {
|
||
return false
|
||
}
|
||
|
||
const trimmedName = stageName.trim()
|
||
|
||
// 检查是否已存在
|
||
const exists = stageOptions.value.find((option: any) => option.value === trimmedName)
|
||
if (exists) {
|
||
message.warning(`关卡 "${trimmedName}" 已存在`)
|
||
return false
|
||
}
|
||
|
||
// 添加到选项列表
|
||
stageOptions.value.push({
|
||
label: trimmedName,
|
||
value: trimmedName,
|
||
isCustom: true,
|
||
})
|
||
|
||
message.success(`自定义关卡 "${trimmedName}" 添加成功`)
|
||
return true
|
||
}
|
||
|
||
// 添加主关卡
|
||
const addCustomStage = () => {
|
||
if (!validateStageName(customStageName.value)) {
|
||
message.error('请输入有效的关卡名称')
|
||
return
|
||
}
|
||
|
||
if (addStageToOptions(customStageName.value)) {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage = customStageName.value.trim()
|
||
}
|
||
customStageName.value = ''
|
||
nextTick(() => {
|
||
stageInputRef.value?.focus()
|
||
})
|
||
}
|
||
}
|
||
|
||
// 添加备选关卡-1
|
||
const addCustomStage1 = () => {
|
||
if (!validateStageName(customStage1Name.value)) {
|
||
message.error('请输入有效的关卡名称')
|
||
return
|
||
}
|
||
|
||
if (addStageToOptions(customStage1Name.value)) {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_1 = customStage1Name.value.trim()
|
||
}
|
||
customStage1Name.value = ''
|
||
nextTick(() => {
|
||
stage1InputRef.value?.focus()
|
||
})
|
||
}
|
||
}
|
||
|
||
// 添加备选关卡-2
|
||
const addCustomStage2 = () => {
|
||
if (!validateStageName(customStage2Name.value)) {
|
||
message.error('请输入有效的关卡名称')
|
||
return
|
||
}
|
||
|
||
if (addStageToOptions(customStage2Name.value)) {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_2 = customStage2Name.value.trim()
|
||
}
|
||
customStage2Name.value = ''
|
||
nextTick(() => {
|
||
stage2InputRef.value?.focus()
|
||
})
|
||
}
|
||
}
|
||
|
||
// 添加备选关卡-3
|
||
const addCustomStage3 = () => {
|
||
if (!validateStageName(customStage3Name.value)) {
|
||
message.error('请输入有效的关卡名称')
|
||
return
|
||
}
|
||
|
||
if (addStageToOptions(customStage3Name.value)) {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_3 = customStage3Name.value.trim()
|
||
}
|
||
customStage3Name.value = ''
|
||
nextTick(() => {
|
||
stage3InputRef.value?.focus()
|
||
})
|
||
}
|
||
}
|
||
|
||
// 添加剩余理智关卡
|
||
const addCustomStageRemain = () => {
|
||
if (!validateStageName(customStageRemainName.value)) {
|
||
message.error('请输入有效的关卡名称')
|
||
return
|
||
}
|
||
|
||
if (addStageToOptions(customStageRemainName.value)) {
|
||
if (!isPlanMode.value) {
|
||
formData.Info.Stage_Remain = customStageRemainName.value.trim()
|
||
}
|
||
customStageRemainName.value = ''
|
||
nextTick(() => {
|
||
stageRemainInputRef.value?.focus()
|
||
})
|
||
}
|
||
}
|
||
|
||
const handleCancel = () => {
|
||
if (maaWebsocketId.value) {
|
||
unsubscribe(maaWebsocketId.value)
|
||
maaWebsocketId.value = null
|
||
}
|
||
router.push('/scripts')
|
||
}
|
||
|
||
// 初始化加载
|
||
onMounted(() => {
|
||
if (!scriptId) {
|
||
message.error('缺少脚本ID参数')
|
||
handleCancel()
|
||
return
|
||
}
|
||
|
||
loadScriptInfo()
|
||
loadStageModeOptions()
|
||
loadStageOptions()
|
||
|
||
// 设置StageMode变化监听器
|
||
watch(
|
||
() => formData.Info.StageMode,
|
||
async newStageMode => {
|
||
if (newStageMode === 'Fixed') {
|
||
// 切换到固定模式,清除计划配置
|
||
planModeConfig.value = null
|
||
} else if (newStageMode && newStageMode !== '') {
|
||
// 切换到计划模式,加载计划配置
|
||
try {
|
||
const response = await getPlans(newStageMode)
|
||
|
||
if (response && response.code === 200 && response.data[newStageMode]) {
|
||
const planData = response.data[newStageMode]
|
||
const currentConfig = getPlanCurrentConfig(planData)
|
||
planModeConfig.value = currentConfig
|
||
|
||
console.log('计划配置加载成功:', {
|
||
planId: newStageMode,
|
||
currentConfig,
|
||
planModeConfigValue: planModeConfig.value,
|
||
})
|
||
|
||
// 从stageModeOptions中查找对应的计划名称
|
||
const planOption = stageModeOptions.value.find(option => option.value === newStageMode)
|
||
const planName = planOption ? planOption.label : newStageMode
|
||
|
||
message.success(`已切换到计划模式:${planName}`)
|
||
} else {
|
||
message.warning('计划配置加载失败,请检查计划是否存在')
|
||
planModeConfig.value = null
|
||
}
|
||
} catch (error) {
|
||
console.error('加载计划配置失败:', error)
|
||
message.error('加载计划配置时发生错误')
|
||
planModeConfig.value = null
|
||
}
|
||
}
|
||
},
|
||
{ immediate: false }
|
||
)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.user-edit-container {
|
||
padding: 32px;
|
||
min-height: 100vh;
|
||
background: var(--ant-color-bg-layout);
|
||
}
|
||
|
||
.user-edit-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 32px;
|
||
padding: 0 8px;
|
||
}
|
||
|
||
.header-nav {
|
||
flex: 1;
|
||
}
|
||
|
||
.breadcrumb {
|
||
margin: 0;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.subtitle {
|
||
margin: 4px 0 0 0;
|
||
font-size: 16px;
|
||
color: var(--ant-color-text-secondary);
|
||
}
|
||
|
||
.user-edit-content {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.config-card {
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.config-card :deep(.ant-card-body) {
|
||
padding: 32px;
|
||
}
|
||
|
||
.config-form {
|
||
max-width: none;
|
||
}
|
||
|
||
/* form-section 样式 - 来自 ScriptEdit.vue */
|
||
.form-section {
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.form-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.section-header {
|
||
margin-bottom: 20px;
|
||
padding-bottom: 8px;
|
||
border-bottom: 2px solid var(--ant-color-border-secondary);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.section-header h3 {
|
||
margin: 0;
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: var(--ant-color-text);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.section-header h3::before {
|
||
content: '';
|
||
width: 4px;
|
||
height: 24px;
|
||
background: linear-gradient(135deg, var(--ant-color-primary), var(--ant-color-primary-hover));
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* 表单标签 */
|
||
.form-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-weight: 600;
|
||
color: var(--ant-color-text);
|
||
font-size: 14px;
|
||
}
|
||
|
||
.help-icon {
|
||
color: var(--ant-color-text-tertiary);
|
||
font-size: 14px;
|
||
cursor: help;
|
||
transition: color 0.3s ease;
|
||
}
|
||
|
||
.help-icon:hover {
|
||
color: var(--ant-color-primary);
|
||
}
|
||
|
||
.modern-input {
|
||
border-radius: 8px;
|
||
border: 2px solid var(--ant-color-border);
|
||
background: var(--ant-color-bg-container);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.modern-input:hover {
|
||
border-color: var(--ant-color-primary-hover);
|
||
}
|
||
|
||
.modern-input:focus,
|
||
.modern-input.ant-input-focused {
|
||
border-color: var(--ant-color-primary);
|
||
box-shadow: 0 0 0 4px rgba(24, 144, 255, 0.1);
|
||
}
|
||
|
||
/* section标题右侧文档链接 */
|
||
.section-doc-link {
|
||
color: var(--ant-color-primary) !important;
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
border: 1px solid var(--ant-color-primary);
|
||
transition: all 0.2s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.section-doc-link:hover {
|
||
color: var(--ant-color-primary-hover) !important;
|
||
background-color: var(--ant-color-primary-bg);
|
||
border-color: var(--ant-color-primary-hover);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.form-card {
|
||
margin-bottom: 24px;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.form-card :deep(.ant-card-head) {
|
||
border-bottom: 2px solid var(--ant-color-border-secondary);
|
||
}
|
||
|
||
.form-card :deep(.ant-card-head-title) {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--ant-color-text);
|
||
}
|
||
|
||
.user-form :deep(.ant-form-item-label > label) {
|
||
font-weight: 500;
|
||
color: var(--ant-color-text);
|
||
}
|
||
|
||
.switch-description,
|
||
.task-description {
|
||
margin-left: 12px;
|
||
font-size: 13px;
|
||
color: var(--ant-color-text-secondary);
|
||
}
|
||
|
||
.task-description {
|
||
display: block;
|
||
margin-top: 4px;
|
||
margin-left: 0;
|
||
}
|
||
|
||
.cancel-button {
|
||
border: 1px solid var(--ant-color-border);
|
||
background: var(--ant-color-bg-container);
|
||
color: var(--ant-color-text);
|
||
}
|
||
|
||
.cancel-button:hover {
|
||
border-color: var(--ant-color-primary);
|
||
color: var(--ant-color-primary);
|
||
}
|
||
|
||
.save-button {
|
||
background: var(--ant-color-primary);
|
||
border-color: var(--ant-color-primary);
|
||
}
|
||
|
||
.save-button:hover {
|
||
background: var(--ant-color-primary-hover);
|
||
border-color: var(--ant-color-primary-hover);
|
||
}
|
||
|
||
/* 表单标签样式 */
|
||
.form-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-weight: 500;
|
||
color: var(--ant-color-text);
|
||
}
|
||
|
||
.help-icon {
|
||
font-size: 14px;
|
||
color: var(--ant-color-text-tertiary);
|
||
cursor: help;
|
||
transition: color 0.3s ease;
|
||
}
|
||
|
||
.help-icon:hover {
|
||
color: var(--ant-color-primary);
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.user-edit-container {
|
||
padding: 16px;
|
||
}
|
||
|
||
.user-edit-header {
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.header-title h1 {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.user-edit-content {
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
|
||
.float-button {
|
||
width: 60px;
|
||
height: 60px;
|
||
}
|
||
|
||
/* 计划模式提示样式 */
|
||
.plan-mode-hint {
|
||
margin-top: 4px;
|
||
font-size: 12px;
|
||
color: var(--ant-color-primary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 计划模式显示样式 */
|
||
.plan-mode-display {
|
||
min-height: 40px;
|
||
padding: 8px 12px;
|
||
border: 1px solid var(--ant-color-border);
|
||
border-radius: 6px;
|
||
background: var(--ant-color-fill-alter);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.plan-value {
|
||
font-size: 14px;
|
||
color: var(--ant-color-text);
|
||
font-weight: 500;
|
||
flex: 1;
|
||
}
|
||
|
||
.plan-source {
|
||
font-size: 12px;
|
||
color: var(--ant-color-primary);
|
||
font-weight: 500;
|
||
padding: 2px 8px;
|
||
background: var(--ant-color-primary-bg);
|
||
border-radius: 12px;
|
||
border: 1px solid var(--ant-color-primary-border);
|
||
}
|
||
</style>
|