Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dashboard/src/i18n/locales/en-US/features/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
5 changes: 4 additions & 1 deletion dashboard/src/i18n/locales/ru-RU/features/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,16 @@
"title": "Управление конфигурациями",
"description": "AstrBot поддерживает несколько конфигураций для разных ботов. По умолчанию используется «default».",
"newConfig": "Новая конфигурация",
"copyConfig": "Копировать конфигурацию",
"editConfig": "Изменить конфигурацию",
"manageConfigs": "Управление файлами...",
"configName": "Имя",
"fillConfigName": "Введите имя конфигурации",
"confirmDelete": "Вы уверены, что хотите удалить конфигурацию «{name}»? Это действие необратимо.",
"pleaseEnterName": "Пожалуйста, введите имя",
"nameExists": "Имя конфигурации уже существует. Используйте другое имя.",
"createFailed": "Ошибка создания конфигурации",
"copyFailed": "Ошибка копирования конфигурации",
"deleteFailed": "Ошибка удаления",
"updateFailed": "Ошибка обновления"
},
Expand Down Expand Up @@ -126,4 +129,4 @@
"cancel": "Отмена"
}
}
}
}
3 changes: 3 additions & 0 deletions dashboard/src/i18n/locales/zh-CN/features/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,16 @@
"title": "配置文件管理",
"description": "AstrBot 支持针对不同机器人分别设置配置文件。默认会使用 `default` 配置。",
"newConfig": "新建配置文件",
"copyConfig": "复制配置文件",
"editConfig": "编辑配置文件",
"manageConfigs": "管理配置文件...",
"configName": "名称",
"fillConfigName": "填写配置文件名称",
"confirmDelete": "确定要删除配置文件 \"{name}\" 吗?此操作不可恢复。",
"pleaseEnterName": "请填写配置名称",
"nameExists": "配置文件名称已存在,请使用其他名称",
"createFailed": "新配置文件创建失败",
"copyFailed": "复制配置文件失败",
"deleteFailed": "删除配置文件失败",
"updateFailed": "更新配置文件失败"
},
Expand Down
144 changes: 114 additions & 30 deletions dashboard/src/views/ConfigPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>

Expand All @@ -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>
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -343,6 +360,7 @@ export default {
configManageDialog: false,
showConfigForm: false,
isEditingConfig: false,
isCopyingConfig: false,
config_data_has_changed: false,
config_data_str: "",
config_data: {
Expand Down Expand Up @@ -371,6 +389,7 @@ export default {
name: '',
},
editingConfigId: null,
copySourceConfigId: '',

// 测试聊天
testChatDrawer: false,
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -638,45 +675,92 @@ 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;
}

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();
this.updateConfigInfo(normalizedName);
} else if (this.isCopyingConfig) {
this.copyConfig(normalizedName);
} else {
this.createNewConfig();
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);
if (await askForConfirmationDialog(message, this.confirmDialog)) {
Expand Down Expand Up @@ -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;
Expand Down
Loading