Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@
},
"interval_method": {
"description": "Interval Method",
"hint": "random 为随机时间,log 为根据消息长度计算,$y=log_<log_base>(x)$,x为字数,y的单位为秒。"
"hint": "random uses a random delay. log calculates delay by message length: $y=log_{log\\_base}(x)$, where x is word count and y is in seconds."
},
"interval": {
"description": "Random Interval Time",
Expand Down
48 changes: 29 additions & 19 deletions dashboard/src/i18n/locales/en-US/features/session-management.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,6 @@
"batchDeleteConfirm": {
"title": "Confirm Batch Delete",
"message": "Are you sure you want to delete {count} selected rules? Global settings will be used after deletion."
},
"batchOperations": {
"title": "Batch Operations",
"hint": "Quick batch modify session settings",
"scope": "Apply to",
"scopeSelected": "Selected sessions",
"scopeAll": "All sessions",
"scopeGroup": "All groups",
"scopePrivate": "All private chats",
"llmStatus": "LLM Status",
"ttsStatus": "TTS Status",
"chatProvider": "Chat Model",
"ttsProvider": "TTS Model",
"apply": "Apply Changes"
},
"status": {
"enabled": "Enabled",
"disabled": "Disabled"
},
"batchOperations": {
"title": "Batch Operations",
Expand All @@ -126,6 +108,25 @@
"ttsProvider": "TTS Model",
"apply": "Apply Changes"
},
"groups": {
"title": "Group Management",
"count": "{count} groups",
"addToGroup": "Add to Group",
"create": "Create Group",
"edit": "Edit Group",
"name": "Group Name",
"sessionsCount": "{count} sessions",
"empty": "No groups yet. Click 'Create Group' to create one.",
"availableSessions": "Available Sessions ({count})",
"selectedSessions": "Selected Sessions ({count})",
"searchPlaceholder": "Search...",
"noMatch": "No matches",
"noMembers": "No members",
"customGroupDivider": "── Custom Groups ──",
"customGroupOption": "📁 {name} ({count})",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's a good practice to separate presentational elements like icons from translation strings. This improves maintainability, as changing the icon won't require updating all translation files. Please remove the '📁' icon from this translation key.

    "customGroupOption": "{name} ({count})"

"groupOption": "{name} ({count} sessions)",
"deleteConfirm": "Are you sure you want to delete group \"{name}\"?"
},
"status": {
"enabled": "Enabled",
"disabled": "Disabled"
Expand All @@ -142,7 +143,16 @@
"noChanges": "No changes to save",
"batchDeleteSuccess": "Batch delete successful",
"batchDeleteError": "Batch delete failed",
"selectSessionsFirst": "Please select sessions first",
"selectAtLeastOneConfig": "Please select at least one setting to modify",
"batchUpdateSuccess": "Batch update successful",
"partialUpdateFailed": "Some updates failed",
"batchUpdateError": "Batch update failed",
"batchUpdateSuccess": "Batch update success"
"groupNameRequired": "Group name cannot be empty",
"saveGroupError": "Failed to save group",
"deleteGroupError": "Failed to delete group",
"selectSessionsToAddFirst": "Please select sessions to add first",
"addToGroupSuccess": "Added {count} sessions to the group",
"addToGroupError": "Failed to add to group"
}
}
32 changes: 30 additions & 2 deletions dashboard/src/i18n/locales/ru-RU/features/session-management.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,25 @@
"ttsProvider": "TTS-модель",
"apply": "Применить"
},
"groups": {
"title": "Управление группами",
"count": "групп: {count}",
"addToGroup": "Добавить в группу",
"create": "Создать группу",
"edit": "Изменить группу",
"name": "Имя группы",
"sessionsCount": "сессий: {count}",
"empty": "Пока нет групп. Нажмите «Создать группу», чтобы добавить.",
"availableSessions": "Доступные сессии ({count})",
"selectedSessions": "Выбранные сессии ({count})",
"searchPlaceholder": "Поиск...",
"noMatch": "Нет совпадений",
"noMembers": "Нет участников",
"customGroupDivider": "── Пользовательские группы ──",
"customGroupOption": "📁 {name} ({count})",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's a good practice to separate presentational elements like icons from translation strings. This improves maintainability, as changing the icon won't require updating all translation files. Please remove the '📁' icon from this translation key.

        "customGroupOption": "{name} ({count})"

"groupOption": "{name} (сессий: {count})",
"deleteConfirm": "Вы уверены, что хотите удалить группу \"{name}\"?"
},
"status": {
"enabled": "Включено",
"disabled": "Выключено"
Expand All @@ -124,7 +143,16 @@
"noChanges": "Изменений не обнаружено",
"batchDeleteSuccess": "Массовое удаление выполнено",
"batchDeleteError": "Ошибка массового удаления",
"selectSessionsFirst": "Пожалуйста, сначала выберите сессии",
"selectAtLeastOneConfig": "Пожалуйста, выберите хотя бы одну настройку для изменения",
"batchUpdateSuccess": "Пакетное обновление успешно выполнено",
"partialUpdateFailed": "Некоторые обновления не выполнены",
"batchUpdateError": "Ошибка пакетного обновления",
"batchUpdateSuccess": "Пакетное обновление успешно выполнено"
"groupNameRequired": "Имя группы не может быть пустым",
"saveGroupError": "Ошибка сохранения группы",
"deleteGroupError": "Ошибка удаления группы",
"selectSessionsToAddFirst": "Пожалуйста, сначала выберите сессии для добавления",
"addToGroupSuccess": "Добавлено сессий в группу: {count}",
"addToGroupError": "Ошибка добавления в группу"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,25 @@
"ttsProvider": "TTS 模型",
"apply": "应用更改"
},
"groups": {
"title": "分组管理",
"count": "{count} 个分组",
"addToGroup": "添加到分组",
"create": "新建分组",
"edit": "编辑分组",
"name": "分组名称",
"sessionsCount": "{count} 个会话",
"empty": "暂无分组,点击「新建分组」创建",
"availableSessions": "可选会话 ({count})",
"selectedSessions": "已选会话 ({count})",
"searchPlaceholder": "搜索...",
"noMatch": "无匹配项",
"noMembers": "暂无成员",
"customGroupDivider": "── 自定义分组 ──",
"customGroupOption": "📁 {name} ({count})",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

将图标等表现层元素与翻译字符串分开是一种好的做法。这能提高可维护性,因为更改图标不需要更新所有翻译文件。请从此翻译键中移除 '📁' 图标。

    "customGroupOption": "{name} ({count})"

"groupOption": "{name} ({count} 个会话)",
"deleteConfirm": "确定要删除分组 \"{name}\" 吗?"
},
"status": {
"enabled": "启用",
"disabled": "禁用"
Expand All @@ -123,6 +142,17 @@
"deleteError": "删除失败",
"noChanges": "没有需要保存的更改",
"batchDeleteSuccess": "批量删除成功",
"batchDeleteError": "批量删除失败"
"batchDeleteError": "批量删除失败",
"selectSessionsFirst": "请先选择要操作的会话",
"selectAtLeastOneConfig": "请至少选择一项要修改的配置",
"batchUpdateSuccess": "批量更新成功",
"partialUpdateFailed": "部分更新失败",
"batchUpdateError": "批量更新失败",
"groupNameRequired": "分组名称不能为空",
"saveGroupError": "保存分组失败",
"deleteGroupError": "删除分组失败",
"selectSessionsToAddFirst": "请先选择要添加的会话",
"addToGroupSuccess": "已添加 {count} 个会话到分组",
"addToGroupError": "添加失败"
}
}
67 changes: 35 additions & 32 deletions dashboard/src/views/SessionManagementPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,24 +156,24 @@
<!-- 分组管理面板 -->
<v-card flat class="mt-4">
<v-card-title class="d-flex align-center py-3 px-4">
<span class="text-h6">分组管理</span>
<span class="text-h6">{{ tm('groups.title') }}</span>
<v-chip size="small" class="ml-2" color="secondary" variant="outlined">
{{ groups.length }} 个分组
{{ tm('groups.count', { count: groups.length }) }}
</v-chip>
<v-spacer></v-spacer>
<v-btn v-if="selectedItems.length > 0 && groups.length > 0" color="info" variant="tonal" size="small" class="mr-2">
<v-icon start>mdi-folder-plus</v-icon>
添加到分组
{{ tm('groups.addToGroup') }}
<v-menu activator="parent">
<v-list density="compact">
<v-list-item v-for="g in groups" :key="g.id" @click="addSelectedToGroup(g.id)">
<v-list-item-title>{{ g.name }} ({{ g.umo_count }})</v-list-item-title>
<v-list-item-title>{{ tm('groups.customGroupOption', { name: g.name, count: g.umo_count }) }}</v-list-item-title>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To separate the icon from the translation string, please add the '📁' icon here in the template. This should be done in conjunction with removing it from the groups.customGroupOption translation key in the i18n JSON files.

                  <v-list-item-title>📁 {{ tm('groups.customGroupOption', { name: g.name, count: g.umo_count }) }}</v-list-item-title>

</v-list-item>
</v-list>
</v-menu>
</v-btn>
<v-btn color="success" variant="tonal" size="small" @click="openCreateGroupDialog" prepend-icon="mdi-folder-plus">
新建分组
{{ tm('groups.create') }}
</v-btn>
</v-card-title>
<v-card-text v-if="groups.length > 0">
Expand All @@ -183,7 +183,7 @@
<div class="d-flex align-center justify-space-between">
<div>
<div class="font-weight-bold">{{ group.name }}</div>
<div class="text-caption text-grey">{{ group.umo_count }} 个会话</div>
<div class="text-caption text-grey">{{ tm('groups.sessionsCount', { count: group.umo_count }) }}</div>
</div>
<div>
<v-btn icon size="small" variant="text" @click="openEditGroupDialog(group)">
Expand All @@ -199,23 +199,23 @@
</v-row>
</v-card-text>
<v-card-text v-else class="text-center text-grey py-6">
暂无分组,点击「新建分组」创建
{{ tm('groups.empty') }}
</v-card-text>
</v-card>

<!-- 分组编辑对话框 -->
<v-dialog v-model="groupDialog" max-width="800" @after-enter="loadAvailableUmos">
<v-card>
<v-card-title class="py-3 px-4">
{{ groupDialogMode === 'create' ? '新建分组' : '编辑分组' }}
{{ groupDialogMode === 'create' ? tm('groups.create') : tm('groups.edit') }}
</v-card-title>
<v-card-text>
<v-text-field v-model="editingGroup.name" label="分组名称" variant="outlined" hide-details class="mb-4"></v-text-field>
<v-text-field v-model="editingGroup.name" :label="tm('groups.name')" variant="outlined" hide-details class="mb-4"></v-text-field>
<v-row dense>
<!-- 左侧:可选会话 -->
<v-col cols="5">
<div class="text-subtitle-2 mb-2">可选会话 ({{ unselectedUmos.length }})</div>
<v-text-field v-model="groupMemberSearch" placeholder="搜索..." variant="outlined" density="compact" hide-details class="mb-2" clearable prepend-inner-icon="mdi-magnify"></v-text-field>
<div class="text-subtitle-2 mb-2">{{ tm('groups.availableSessions', { count: unselectedUmos.length }) }}</div>
<v-text-field v-model="groupMemberSearch" :placeholder="tm('groups.searchPlaceholder')" variant="outlined" density="compact" hide-details class="mb-2" clearable prepend-inner-icon="mdi-magnify"></v-text-field>
<v-list density="compact" class="transfer-list" lines="one">
<v-list-item v-for="umo in filteredUnselectedUmos" :key="umo" @click="addToGroup(umo)" class="transfer-item">
<template v-slot:prepend>
Expand All @@ -224,7 +224,7 @@
<v-list-item-title class="text-caption">{{ formatUmoShort(umo) }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="filteredUnselectedUmos.length === 0 && !loadingUmos">
<v-list-item-title class="text-caption text-grey text-center">无匹配项</v-list-item-title>
<v-list-item-title class="text-caption text-grey text-center">{{ tm('groups.noMatch') }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="loadingUmos">
<v-list-item-title class="text-center"><v-progress-circular indeterminate size="20"></v-progress-circular></v-list-item-title>
Expand All @@ -242,8 +242,8 @@
</v-col>
<!-- 右侧:已选会话 -->
<v-col cols="5">
<div class="text-subtitle-2 mb-2">已选会话 ({{ editingGroup.umos.length }})</div>
<v-text-field v-model="groupSelectedSearch" placeholder="搜索..." variant="outlined" density="compact" hide-details class="mb-2" clearable prepend-inner-icon="mdi-magnify"></v-text-field>
<div class="text-subtitle-2 mb-2">{{ tm('groups.selectedSessions', { count: editingGroup.umos.length }) }}</div>
<v-text-field v-model="groupSelectedSearch" :placeholder="tm('groups.searchPlaceholder')" variant="outlined" density="compact" hide-details class="mb-2" clearable prepend-inner-icon="mdi-magnify"></v-text-field>
<v-list density="compact" class="transfer-list" lines="one">
<v-list-item v-for="umo in filteredSelectedUmos" :key="umo" @click="removeFromGroup(umo)" class="transfer-item">
<template v-slot:prepend>
Expand All @@ -252,16 +252,16 @@
<v-list-item-title class="text-caption">{{ formatUmoShort(umo) }}</v-list-item-title>
</v-list-item>
<v-list-item v-if="editingGroup.umos.length === 0">
<v-list-item-title class="text-caption text-grey text-center">暂无成员</v-list-item-title>
<v-list-item-title class="text-caption text-grey text-center">{{ tm('groups.noMembers') }}</v-list-item-title>
</v-list-item>
</v-list>
</v-col>
</v-row>
</v-card-text>
<v-card-actions class="px-4 pb-4">
<v-spacer></v-spacer>
<v-btn variant="text" @click="groupDialog = false">取消</v-btn>
<v-btn color="primary" variant="tonal" @click="saveGroup">保存</v-btn>
<v-btn variant="text" @click="groupDialog = false">{{ tm('buttons.cancel') }}</v-btn>
<v-btn color="primary" variant="tonal" @click="saveGroup">{{ tm('buttons.save') }}</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
Expand Down Expand Up @@ -721,17 +721,20 @@ export default {
]
// 添加自定义分组选项
if (this.groups.length > 0) {
options.push({ label: '── 自定义分组 ──', value: '_divider', disabled: true })
options.push({ label: this.tm('groups.customGroupDivider'), value: '_divider', disabled: true })
this.groups.forEach(g => {
Comment on lines 722 to 725
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Preserving the visual divider formatting in translations could avoid inconsistent UX across locales.

Previously, the hard-coded label (── 自定义分组 ──) ensured the divider always looked visually distinct. Now that the whole string is localized via groups.customGroupDivider, each locale must remember to include the decorative dashes or the divider may resemble a normal option. Consider keeping the dashes in code and localizing only the inner text, e.g. ``label: `── ${this.tm('groups.customGroupDivider')} ──``` to keep a consistent visual pattern across locales.

Suggested change
// 添加自定义分组选项
if (this.groups.length > 0) {
options.push({ label: '── 自定义分组 ──', value: '_divider', disabled: true })
options.push({ label: this.tm('groups.customGroupDivider'), value: '_divider', disabled: true })
this.groups.forEach(g => {
// 添加自定义分组选项
if (this.groups.length > 0) {
options.push({ label: `── ${this.tm('groups.customGroupDivider')} ──`, value: '_divider', disabled: true })
this.groups.forEach(g => {

options.push({ label: `📁 ${g.name} (${g.umo_count})`, value: `custom_group:${g.id}` })
options.push({
label: this.tm('groups.customGroupOption', { name: g.name, count: g.umo_count }),
value: `custom_group:${g.id}`
})
Comment on lines +726 to +729
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To separate the icon from the translation string, please prepend the '📁' icon to the label here. This change corresponds to the suggested removal of the icon from the groups.customGroupOption translation key.

          options.push({
            label: `📁 ${this.tm('groups.customGroupOption', { name: g.name, count: g.umo_count })}`,
            value: `custom_group:${g.id}`
          })

})
}
return options
},

groupOptions() {
return this.groups.map(g => ({
label: `${g.name} (${g.umo_count} 个会话)`,
label: this.tm('groups.groupOption', { name: g.name, count: g.umo_count }),
value: g.id
}))
},
Expand Down Expand Up @@ -1331,7 +1334,7 @@ export default {
if (scope === 'selected') {
umos = this.selectedItems.map(item => item.umo)
if (umos.length === 0) {
this.showError('请先选择要操作的会话')
this.showError(this.tm('messages.selectSessionsFirst'))
this.batchUpdating = false
return
}
Expand Down Expand Up @@ -1371,7 +1374,7 @@ export default {
}

if (tasks.length === 0) {
this.showError('请至少选择一项要修改的配置')
this.showError(this.tm('messages.selectAtLeastOneConfig'))
this.batchUpdating = false
return
}
Expand All @@ -1380,17 +1383,17 @@ export default {
const allOk = results.every(r => r.data.status === 'ok')

if (allOk) {
this.showSuccess('批量更新成功')
this.showSuccess(this.tm('messages.batchUpdateSuccess'))
this.batchLlmStatus = null
this.batchTtsStatus = null
this.batchChatProvider = null
this.batchTtsProvider = null
await this.loadData()
} else {
this.showError('部分更新失败')
this.showError(this.tm('messages.partialUpdateFailed'))
}
} catch (error) {
this.showError(error.response?.data?.message || '批量更新失败')
this.showError(error.response?.data?.message || this.tm('messages.batchUpdateError'))
}
this.batchUpdating = false
},
Expand Down Expand Up @@ -1477,7 +1480,7 @@ export default {

async saveGroup() {
if (!this.editingGroup.name.trim()) {
this.showError('分组名称不能为空')
this.showError(this.tm('messages.groupNameRequired'))
return
}

Expand All @@ -1504,12 +1507,12 @@ export default {
this.showError(response.data.message)
}
} catch (error) {
this.showError(error.response?.data?.message || '保存分组失败')
this.showError(error.response?.data?.message || this.tm('messages.saveGroupError'))
}
},

async deleteGroup(group) {
const message = `确定要删除分组 "${group.name}" 吗?`
const message = this.tm('groups.deleteConfirm', { name: group.name })
if (!(await askForConfirmationDialog(message, this.confirmDialog))) return

try {
Expand All @@ -1521,7 +1524,7 @@ export default {
this.showError(response.data.message)
}
} catch (error) {
this.showError(error.response?.data?.message || '删除分组失败')
this.showError(error.response?.data?.message || this.tm('messages.deleteGroupError'))
}
},

Expand All @@ -1532,7 +1535,7 @@ export default {

async addSelectedToGroup(groupId) {
if (this.selectedItems.length === 0) {
this.showError('请先选择要添加的会话')
this.showError(this.tm('messages.selectSessionsToAddFirst'))
return
}

Expand All @@ -1542,13 +1545,13 @@ export default {
add_umos: this.selectedItems.map(item => item.umo)
})
if (response.data.status === 'ok') {
this.showSuccess(`已添加 ${this.selectedItems.length} 个会话到分组`)
this.showSuccess(this.tm('messages.addToGroupSuccess', { count: this.selectedItems.length }))
await this.loadGroups()
} else {
this.showError(response.data.message)
}
} catch (error) {
this.showError(error.response?.data?.message || '添加失败')
this.showError(error.response?.data?.message || this.tm('messages.addToGroupError'))
}
},
},
Expand Down
Loading