mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-09-24 16:39:52 +08:00
feat: allow copy config from existing configs (#6785)
* feat: allow copy config from existing configs * fix: issues mentioned by reviewer bot - duplicated logic for initializing/resetting the config - check whitespace-only names
This commit is contained in:
@@ -77,13 +77,16 @@
|
||||
"title": "Configuration Management",
|
||||
"description": "AstrBot supports separate configuration files for different bots. The `default` configuration is used by default.",
|
||||
"newConfig": "New Configuration",
|
||||
"copyConfig": "Copy Configuration",
|
||||
"editConfig": "Edit Configuration",
|
||||
"manageConfigs": "Manage Configurations...",
|
||||
"configName": "Name",
|
||||
"fillConfigName": "Enter configuration name",
|
||||
"confirmDelete": "Are you sure you want to delete the configuration \"{name}\"? This action cannot be undone.",
|
||||
"pleaseEnterName": "Please enter a configuration name",
|
||||
"nameExists": "Configuration name already exists. Please use another name.",
|
||||
"createFailed": "Failed to create new configuration",
|
||||
"copyFailed": "Failed to copy configuration",
|
||||
"deleteFailed": "Failed to delete configuration",
|
||||
"updateFailed": "Failed to update configuration"
|
||||
},
|
||||
|
||||
@@ -77,13 +77,16 @@
|
||||
"title": "Управление конфигурациями",
|
||||
"description": "AstrBot поддерживает несколько конфигураций для разных ботов. По умолчанию используется «default».",
|
||||
"newConfig": "Новая конфигурация",
|
||||
"copyConfig": "Копировать конфигурацию",
|
||||
"editConfig": "Изменить конфигурацию",
|
||||
"manageConfigs": "Управление файлами...",
|
||||
"configName": "Имя",
|
||||
"fillConfigName": "Введите имя конфигурации",
|
||||
"confirmDelete": "Вы уверены, что хотите удалить конфигурацию «{name}»? Это действие необратимо.",
|
||||
"pleaseEnterName": "Пожалуйста, введите имя",
|
||||
"nameExists": "Имя конфигурации уже существует. Используйте другое имя.",
|
||||
"createFailed": "Ошибка создания конфигурации",
|
||||
"copyFailed": "Ошибка копирования конфигурации",
|
||||
"deleteFailed": "Ошибка удаления",
|
||||
"updateFailed": "Ошибка обновления"
|
||||
},
|
||||
@@ -126,4 +129,4 @@
|
||||
"cancel": "Отмена"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,13 +77,16 @@
|
||||
"title": "配置文件管理",
|
||||
"description": "AstrBot 支持针对不同机器人分别设置配置文件。默认会使用 `default` 配置。",
|
||||
"newConfig": "新建配置文件",
|
||||
"copyConfig": "复制配置文件",
|
||||
"editConfig": "编辑配置文件",
|
||||
"manageConfigs": "管理配置文件...",
|
||||
"configName": "名称",
|
||||
"fillConfigName": "填写配置文件名称",
|
||||
"confirmDelete": "确定要删除配置文件 \"{name}\" 吗?此操作不可恢复。",
|
||||
"pleaseEnterName": "请填写配置名称",
|
||||
"nameExists": "配置文件名称已存在,请使用其他名称",
|
||||
"createFailed": "新配置文件创建失败",
|
||||
"copyFailed": "复制配置文件失败",
|
||||
"deleteFailed": "删除配置文件失败",
|
||||
"updateFailed": "更新配置文件失败"
|
||||
},
|
||||
|
||||
@@ -126,11 +126,15 @@
|
||||
<!-- Config List -->
|
||||
<v-list lines="two">
|
||||
<v-list-item v-for="config in configInfoList" :key="config.id" :title="config.name">
|
||||
<template v-slot:append v-if="config.id !== 'default'">
|
||||
<template v-slot:append>
|
||||
<div class="d-flex align-center" style="gap: 8px;">
|
||||
<v-btn icon="mdi-content-copy" size="small" variant="text" color="primary"
|
||||
@click="startCopyConfig(config)"></v-btn>
|
||||
<v-btn icon="mdi-pencil" size="small" variant="text" color="warning"
|
||||
v-if="config.id !== 'default'"
|
||||
@click="startEditConfig(config)"></v-btn>
|
||||
<v-btn icon="mdi-delete" size="small" variant="text" color="error"
|
||||
v-if="config.id !== 'default'"
|
||||
@click="confirmDeleteConfig(config)"></v-btn>
|
||||
</div>
|
||||
</template>
|
||||
@@ -141,7 +145,7 @@
|
||||
<v-divider v-if="showConfigForm" class="my-6"></v-divider>
|
||||
|
||||
<div v-if="showConfigForm">
|
||||
<h3 class="mb-4">{{ isEditingConfig ? tm('configManagement.editConfig') : tm('configManagement.newConfig') }}</h3>
|
||||
<h3 class="mb-4">{{ configFormTitle }}</h3>
|
||||
|
||||
<h4>{{ tm('configManagement.configName') }}</h4>
|
||||
|
||||
@@ -151,7 +155,7 @@
|
||||
<div class="d-flex justify-end mt-4" style="gap: 8px;">
|
||||
<v-btn variant="text" @click="cancelConfigForm">{{ tm('buttons.cancel') }}</v-btn>
|
||||
<v-btn color="primary" @click="saveConfigForm"
|
||||
:disabled="!configFormData.name">
|
||||
:disabled="isConfigFormSaveDisabled">
|
||||
{{ isEditingConfig ? tm('buttons.update') : tm('buttons.create') }}
|
||||
</v-btn>
|
||||
</div>
|
||||
@@ -297,6 +301,19 @@ export default {
|
||||
selectedConfigInfo() {
|
||||
return this.configInfoList.find(info => info.id === this.selectedConfigID) || {};
|
||||
},
|
||||
configFormTitle() {
|
||||
if (this.isEditingConfig) {
|
||||
return this.tm('configManagement.editConfig');
|
||||
}
|
||||
if (this.isCopyingConfig) {
|
||||
return this.tm('configManagement.copyConfig');
|
||||
}
|
||||
return this.tm('configManagement.newConfig');
|
||||
},
|
||||
isConfigFormSaveDisabled() {
|
||||
const isNameEmpty = !this.normalizeConfigName(this.configFormData.name);
|
||||
return isNameEmpty || (this.isCopyingConfig && !this.copySourceConfigId);
|
||||
},
|
||||
configSelectItems() {
|
||||
const items = [...this.configInfoList];
|
||||
items.push({
|
||||
@@ -343,6 +360,7 @@ export default {
|
||||
configManageDialog: false,
|
||||
showConfigForm: false,
|
||||
isEditingConfig: false,
|
||||
isCopyingConfig: false,
|
||||
config_data_has_changed: false,
|
||||
config_data_str: "",
|
||||
config_data: {
|
||||
@@ -371,6 +389,7 @@ export default {
|
||||
name: '',
|
||||
},
|
||||
editingConfigId: null,
|
||||
copySourceConfigId: '',
|
||||
|
||||
// 测试聊天
|
||||
testChatDrawer: false,
|
||||
@@ -567,9 +586,9 @@ export default {
|
||||
this.save_message_snack = true;
|
||||
}
|
||||
},
|
||||
createNewConfig() {
|
||||
createNewConfig(configName) {
|
||||
axios.post('/api/config/abconf/new', {
|
||||
name: this.configFormData.name
|
||||
name: configName
|
||||
}).then((res) => {
|
||||
if (res.data.status === "ok") {
|
||||
this.save_message = res.data.message;
|
||||
@@ -589,6 +608,24 @@ export default {
|
||||
this.save_message_success = "error";
|
||||
});
|
||||
},
|
||||
normalizeConfigName(name) {
|
||||
return typeof name === 'string' ? name.trim() : '';
|
||||
},
|
||||
hasDuplicateConfigName(name, excludeId = null) {
|
||||
const normalizedName = this.normalizeConfigName(name);
|
||||
if (!normalizedName) {
|
||||
return false;
|
||||
}
|
||||
return this.configInfoList.some((config) => {
|
||||
if (!config || !config.name) {
|
||||
return false;
|
||||
}
|
||||
if (excludeId && config.id === excludeId) {
|
||||
return false;
|
||||
}
|
||||
return this.normalizeConfigName(config.name) === normalizedName;
|
||||
});
|
||||
},
|
||||
async onConfigSelect(value) {
|
||||
if (value === '_%manage%_') {
|
||||
this.configManageDialog = true;
|
||||
@@ -638,44 +675,91 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
setConfigFormState({ mode = 'create', config = null, visible = true } = {}) {
|
||||
this.showConfigForm = visible;
|
||||
this.isEditingConfig = mode === 'edit';
|
||||
this.isCopyingConfig = mode === 'copy';
|
||||
this.editingConfigId = this.isEditingConfig && config ? config.id : null;
|
||||
this.copySourceConfigId = this.isCopyingConfig && config ? config.id : '';
|
||||
|
||||
let name = '';
|
||||
if (this.isEditingConfig && config) {
|
||||
name = config.name || '';
|
||||
} else if (this.isCopyingConfig && config) {
|
||||
name = `${config.name || ''}-copy`;
|
||||
}
|
||||
this.configFormData = { name };
|
||||
},
|
||||
startCreateConfig() {
|
||||
this.showConfigForm = true;
|
||||
this.isEditingConfig = false;
|
||||
this.configFormData = {
|
||||
name: '',
|
||||
};
|
||||
this.editingConfigId = null;
|
||||
this.setConfigFormState({ mode: 'create' });
|
||||
},
|
||||
startEditConfig(config) {
|
||||
this.showConfigForm = true;
|
||||
this.isEditingConfig = true;
|
||||
this.editingConfigId = config.id;
|
||||
|
||||
this.configFormData = {
|
||||
name: config.name || '',
|
||||
};
|
||||
this.setConfigFormState({ mode: 'edit', config });
|
||||
},
|
||||
startCopyConfig(config) {
|
||||
this.setConfigFormState({ mode: 'copy', config });
|
||||
},
|
||||
cancelConfigForm() {
|
||||
this.showConfigForm = false;
|
||||
this.isEditingConfig = false;
|
||||
this.editingConfigId = null;
|
||||
this.configFormData = {
|
||||
name: '',
|
||||
};
|
||||
this.setConfigFormState({ visible: false });
|
||||
},
|
||||
saveConfigForm() {
|
||||
if (!this.configFormData.name) {
|
||||
const normalizedName = this.normalizeConfigName(this.configFormData.name);
|
||||
if (!normalizedName) {
|
||||
this.save_message = this.tm('configManagement.pleaseEnterName');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isEditingConfig) {
|
||||
this.updateConfigInfo();
|
||||
} else {
|
||||
this.createNewConfig();
|
||||
const excludeId = this.isEditingConfig ? this.editingConfigId : null;
|
||||
if (this.hasDuplicateConfigName(normalizedName, excludeId)) {
|
||||
this.save_message = this.tm('configManagement.nameExists');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
return;
|
||||
}
|
||||
this.configFormData.name = normalizedName;
|
||||
if (this.isEditingConfig) {
|
||||
this.updateConfigInfo(normalizedName);
|
||||
} else if (this.isCopyingConfig) {
|
||||
this.copyConfig(normalizedName);
|
||||
} else {
|
||||
this.createNewConfig(normalizedName);
|
||||
}
|
||||
},
|
||||
copyConfig(configName) {
|
||||
axios.get('/api/config/abconf', {
|
||||
params: { id: this.copySourceConfigId }
|
||||
}).then((res) => {
|
||||
const sourceConfig = res.data?.data?.config;
|
||||
if (!sourceConfig) {
|
||||
this.save_message = this.tm('configManagement.copyFailed');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
return;
|
||||
}
|
||||
return axios.post('/api/config/abconf/new', {
|
||||
name: configName,
|
||||
config: sourceConfig
|
||||
});
|
||||
}).then((res) => {
|
||||
if (!res) return;
|
||||
if (res.data.status === "ok") {
|
||||
this.save_message = res.data.message;
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "success";
|
||||
this.getConfigInfoList(res.data.data.conf_id);
|
||||
this.cancelConfigForm();
|
||||
} else {
|
||||
this.save_message = res.data.message;
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
this.save_message = err?.response?.data?.message || this.tm('configManagement.copyFailed');
|
||||
this.save_message_snack = true;
|
||||
this.save_message_success = "error";
|
||||
});
|
||||
},
|
||||
async confirmDeleteConfig(config) {
|
||||
const message = this.tm('configManagement.confirmDelete').replace('{name}', config.name);
|
||||
@@ -706,10 +790,10 @@ export default {
|
||||
this.save_message_success = "error";
|
||||
});
|
||||
},
|
||||
updateConfigInfo() {
|
||||
updateConfigInfo(configName) {
|
||||
axios.post('/api/config/abconf/update', {
|
||||
id: this.editingConfigId,
|
||||
name: this.configFormData.name
|
||||
name: configName
|
||||
}).then((res) => {
|
||||
if (res.data.status === "ok") {
|
||||
this.save_message = res.data.message;
|
||||
|
||||
Reference in New Issue
Block a user