2.0
This commit is contained in:
@@ -38,6 +38,11 @@ class SessionManagementRoute(Route):
|
||||
"/session/list-all-with-status": ("GET", self.list_all_umos_with_status),
|
||||
"/session/batch-update-service": ("POST", self.batch_update_service),
|
||||
"/session/batch-update-provider": ("POST", self.batch_update_provider),
|
||||
# 分组管理 API
|
||||
"/session/groups": ("GET", self.list_groups),
|
||||
"/session/group/create": ("POST", self.create_group),
|
||||
"/session/group/update": ("POST", self.update_group),
|
||||
"/session/group/delete": ("POST", self.delete_group),
|
||||
}
|
||||
self.conv_mgr = core_lifecycle.conversation_manager
|
||||
self.core_lifecycle = core_lifecycle
|
||||
@@ -534,7 +539,8 @@ class SessionManagementRoute(Route):
|
||||
请求体:
|
||||
{
|
||||
"umos": ["平台:消息类型:会话ID", ...], // 可选,如果不传则根据 scope 筛选
|
||||
"scope": "all" | "group" | "private", // 可选,批量范围
|
||||
"scope": "all" | "group" | "private" | "custom_group", // 可选,批量范围
|
||||
"group_id": "分组ID", // 当 scope 为 custom_group 时必填
|
||||
"llm_enabled": true/false/null, // 可选,null表示不修改
|
||||
"tts_enabled": true/false/null, // 可选
|
||||
"session_enabled": true/false/null // 可选
|
||||
@@ -544,6 +550,7 @@ class SessionManagementRoute(Route):
|
||||
data = await request.get_json()
|
||||
umos = data.get("umos", [])
|
||||
scope = data.get("scope", "")
|
||||
group_id = data.get("group_id", "")
|
||||
llm_enabled = data.get("llm_enabled")
|
||||
tts_enabled = data.get("tts_enabled")
|
||||
session_enabled = data.get("session_enabled")
|
||||
@@ -554,19 +561,28 @@ class SessionManagementRoute(Route):
|
||||
|
||||
# 如果指定了 scope,获取符合条件的所有 umo
|
||||
if scope and not umos:
|
||||
async with self.db_helper.get_db() as session:
|
||||
session: AsyncSession
|
||||
result = await session.execute(
|
||||
select(ConversationV2.user_id).distinct()
|
||||
)
|
||||
all_umos = [row[0] for row in result.fetchall()]
|
||||
# 如果是自定义分组
|
||||
if scope == "custom_group":
|
||||
if not group_id:
|
||||
return Response().error("请指定分组 ID").__dict__
|
||||
groups = self._get_groups()
|
||||
if group_id not in groups:
|
||||
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
||||
umos = groups[group_id].get("umos", [])
|
||||
else:
|
||||
async with self.db_helper.get_db() as session:
|
||||
session: AsyncSession
|
||||
result = await session.execute(
|
||||
select(ConversationV2.user_id).distinct()
|
||||
)
|
||||
all_umos = [row[0] for row in result.fetchall()]
|
||||
|
||||
if scope == "group":
|
||||
umos = [u for u in all_umos if ":group:" in u.lower() or ":groupmessage:" in u.lower()]
|
||||
elif scope == "private":
|
||||
umos = [u for u in all_umos if ":private:" in u.lower() or ":friend" in u.lower()]
|
||||
elif scope == "all":
|
||||
umos = all_umos
|
||||
if scope == "group":
|
||||
umos = [u for u in all_umos if ":group:" in u.lower() or ":groupmessage:" in u.lower()]
|
||||
elif scope == "private":
|
||||
umos = [u for u in all_umos if ":private:" in u.lower() or ":friend" in u.lower()]
|
||||
elif scope == "all":
|
||||
umos = all_umos
|
||||
|
||||
if not umos:
|
||||
return Response().error("没有找到符合条件的会话").__dict__
|
||||
@@ -652,20 +668,30 @@ class SessionManagementRoute(Route):
|
||||
provider_type_enum = provider_type_map[provider_type]
|
||||
|
||||
# 如果指定了 scope,获取符合条件的所有 umo
|
||||
group_id = data.get("group_id", "")
|
||||
if scope and not umos:
|
||||
async with self.db_helper.get_db() as session:
|
||||
session: AsyncSession
|
||||
result = await session.execute(
|
||||
select(ConversationV2.user_id).distinct()
|
||||
)
|
||||
all_umos = [row[0] for row in result.fetchall()]
|
||||
# 如果是自定义分组
|
||||
if scope == "custom_group":
|
||||
if not group_id:
|
||||
return Response().error("请指定分组 ID").__dict__
|
||||
groups = self._get_groups()
|
||||
if group_id not in groups:
|
||||
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
||||
umos = groups[group_id].get("umos", [])
|
||||
else:
|
||||
async with self.db_helper.get_db() as session:
|
||||
session: AsyncSession
|
||||
result = await session.execute(
|
||||
select(ConversationV2.user_id).distinct()
|
||||
)
|
||||
all_umos = [row[0] for row in result.fetchall()]
|
||||
|
||||
if scope == "group":
|
||||
umos = [u for u in all_umos if ":group:" in u.lower() or ":groupmessage:" in u.lower()]
|
||||
elif scope == "private":
|
||||
umos = [u for u in all_umos if ":private:" in u.lower() or ":friend" in u.lower()]
|
||||
elif scope == "all":
|
||||
umos = all_umos
|
||||
if scope == "group":
|
||||
umos = [u for u in all_umos if ":group:" in u.lower() or ":groupmessage:" in u.lower()]
|
||||
elif scope == "private":
|
||||
umos = [u for u in all_umos if ":private:" in u.lower() or ":friend" in u.lower()]
|
||||
elif scope == "all":
|
||||
umos = all_umos
|
||||
|
||||
if not umos:
|
||||
return Response().error("没有找到符合条件的会话").__dict__
|
||||
@@ -700,3 +726,144 @@ class SessionManagementRoute(Route):
|
||||
except Exception as e:
|
||||
logger.error(f"批量更新 Provider 失败: {e!s}")
|
||||
return Response().error(f"批量更新 Provider 失败: {e!s}").__dict__
|
||||
|
||||
# ==================== 分组管理 API ====================
|
||||
|
||||
def _get_groups(self) -> dict:
|
||||
"""获取所有分组"""
|
||||
return sp.get("session_groups", {})
|
||||
|
||||
def _save_groups(self, groups: dict) -> None:
|
||||
"""保存分组"""
|
||||
sp.put("session_groups", groups)
|
||||
|
||||
async def list_groups(self):
|
||||
"""获取所有分组列表"""
|
||||
try:
|
||||
groups = self._get_groups()
|
||||
# 转换为列表格式,方便前端使用
|
||||
groups_list = []
|
||||
for group_id, group_data in groups.items():
|
||||
groups_list.append({
|
||||
"id": group_id,
|
||||
"name": group_data.get("name", ""),
|
||||
"umos": group_data.get("umos", []),
|
||||
"umo_count": len(group_data.get("umos", [])),
|
||||
})
|
||||
return Response().ok({"groups": groups_list}).__dict__
|
||||
except Exception as e:
|
||||
logger.error(f"获取分组列表失败: {e!s}")
|
||||
return Response().error(f"获取分组列表失败: {e!s}").__dict__
|
||||
|
||||
async def create_group(self):
|
||||
"""创建新分组"""
|
||||
try:
|
||||
data = await request.json
|
||||
name = data.get("name", "").strip()
|
||||
umos = data.get("umos", [])
|
||||
|
||||
if not name:
|
||||
return Response().error("分组名称不能为空").__dict__
|
||||
|
||||
groups = self._get_groups()
|
||||
|
||||
# 生成唯一 ID
|
||||
import uuid
|
||||
group_id = str(uuid.uuid4())[:8]
|
||||
|
||||
groups[group_id] = {
|
||||
"name": name,
|
||||
"umos": umos,
|
||||
}
|
||||
|
||||
self._save_groups(groups)
|
||||
|
||||
return Response().ok({
|
||||
"message": f"分组 '{name}' 创建成功",
|
||||
"group": {
|
||||
"id": group_id,
|
||||
"name": name,
|
||||
"umos": umos,
|
||||
"umo_count": len(umos),
|
||||
}
|
||||
}).__dict__
|
||||
except Exception as e:
|
||||
logger.error(f"创建分组失败: {e!s}")
|
||||
return Response().error(f"创建分组失败: {e!s}").__dict__
|
||||
|
||||
async def update_group(self):
|
||||
"""更新分组(改名、增删成员)"""
|
||||
try:
|
||||
data = await request.json
|
||||
group_id = data.get("id")
|
||||
name = data.get("name")
|
||||
umos = data.get("umos")
|
||||
add_umos = data.get("add_umos", [])
|
||||
remove_umos = data.get("remove_umos", [])
|
||||
|
||||
if not group_id:
|
||||
return Response().error("分组 ID 不能为空").__dict__
|
||||
|
||||
groups = self._get_groups()
|
||||
|
||||
if group_id not in groups:
|
||||
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
||||
|
||||
group = groups[group_id]
|
||||
|
||||
# 更新名称
|
||||
if name is not None:
|
||||
group["name"] = name.strip()
|
||||
|
||||
# 直接设置 umos 列表
|
||||
if umos is not None:
|
||||
group["umos"] = umos
|
||||
else:
|
||||
# 增量更新
|
||||
current_umos = set(group.get("umos", []))
|
||||
if add_umos:
|
||||
current_umos.update(add_umos)
|
||||
if remove_umos:
|
||||
current_umos.difference_update(remove_umos)
|
||||
group["umos"] = list(current_umos)
|
||||
|
||||
self._save_groups(groups)
|
||||
|
||||
return Response().ok({
|
||||
"message": f"分组 '{group['name']}' 更新成功",
|
||||
"group": {
|
||||
"id": group_id,
|
||||
"name": group["name"],
|
||||
"umos": group["umos"],
|
||||
"umo_count": len(group["umos"]),
|
||||
}
|
||||
}).__dict__
|
||||
except Exception as e:
|
||||
logger.error(f"更新分组失败: {e!s}")
|
||||
return Response().error(f"更新分组失败: {e!s}").__dict__
|
||||
|
||||
async def delete_group(self):
|
||||
"""删除分组"""
|
||||
try:
|
||||
data = await request.json
|
||||
group_id = data.get("id")
|
||||
|
||||
if not group_id:
|
||||
return Response().error("分组 ID 不能为空").__dict__
|
||||
|
||||
groups = self._get_groups()
|
||||
|
||||
if group_id not in groups:
|
||||
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
||||
|
||||
group_name = groups[group_id].get("name", group_id)
|
||||
del groups[group_id]
|
||||
|
||||
self._save_groups(groups)
|
||||
|
||||
return Response().ok({
|
||||
"message": f"分组 '{group_name}' 已删除"
|
||||
}).__dict__
|
||||
except Exception as e:
|
||||
logger.error(f"删除分组失败: {e!s}")
|
||||
return Response().error(f"删除分组失败: {e!s}").__dict__
|
||||
|
||||
Binary file not shown.
@@ -153,6 +153,119 @@
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- 分组管理面板 -->
|
||||
<v-card flat class="mt-4">
|
||||
<v-card-title class="d-flex align-center py-3 px-4">
|
||||
<span class="text-h6">分组管理</span>
|
||||
<v-chip size="small" class="ml-2" color="secondary" variant="outlined">
|
||||
{{ 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>
|
||||
添加到分组
|
||||
<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>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-btn>
|
||||
<v-btn color="success" variant="tonal" size="small" @click="openCreateGroupDialog" prepend-icon="mdi-folder-plus">
|
||||
新建分组
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="groups.length > 0">
|
||||
<v-row dense>
|
||||
<v-col v-for="group in groups" :key="group.id" cols="12" sm="6" md="4" lg="3">
|
||||
<v-card variant="outlined" class="pa-3">
|
||||
<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>
|
||||
<div>
|
||||
<v-btn icon size="small" variant="text" @click="openEditGroupDialog(group)">
|
||||
<v-icon size="small">mdi-pencil</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon size="small" variant="text" color="error" @click="deleteGroup(group)">
|
||||
<v-icon size="small">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-text v-else class="text-center text-grey py-6">
|
||||
暂无分组,点击「新建分组」创建
|
||||
</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' ? '新建分组' : '编辑分组' }}
|
||||
</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-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>
|
||||
<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>
|
||||
<v-icon size="small" color="grey">mdi-plus</v-icon>
|
||||
</template>
|
||||
<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>
|
||||
<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>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-col>
|
||||
<!-- 中间:操作按钮 -->
|
||||
<v-col cols="2" class="d-flex flex-column align-center justify-center">
|
||||
<v-btn icon size="small" variant="tonal" color="primary" class="mb-2" @click="addAllToGroup" :disabled="unselectedUmos.length === 0">
|
||||
<v-icon>mdi-chevron-double-right</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon size="small" variant="tonal" color="error" @click="removeAllFromGroup" :disabled="editingGroup.umos.length === 0">
|
||||
<v-icon>mdi-chevron-double-left</v-icon>
|
||||
</v-btn>
|
||||
</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>
|
||||
<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>
|
||||
<v-icon size="small" color="error">mdi-minus</v-icon>
|
||||
</template>
|
||||
<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>
|
||||
</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-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- 添加规则对话框 - 选择 UMO -->
|
||||
<v-dialog v-model="addRuleDialog" max-width="600">
|
||||
<v-card>
|
||||
@@ -497,12 +610,28 @@ export default {
|
||||
quickEditNameValue: '',
|
||||
// 批量操作
|
||||
batchScope: 'selected',
|
||||
batchGroupId: null,
|
||||
batchLlmStatus: null,
|
||||
batchTtsStatus: null,
|
||||
batchChatProvider: null,
|
||||
batchTtsProvider: null,
|
||||
batchUpdating: false,
|
||||
|
||||
// 分组管理
|
||||
groups: [],
|
||||
groupsLoading: false,
|
||||
groupDialog: false,
|
||||
groupDialogMode: 'create',
|
||||
editingGroup: {
|
||||
id: null,
|
||||
name: '',
|
||||
umos: [],
|
||||
},
|
||||
groupMemberDialog: false,
|
||||
groupMemberTarget: null,
|
||||
groupMemberSearch: '',
|
||||
groupSelectedSearch: '',
|
||||
|
||||
// 提示信息
|
||||
snackbar: false,
|
||||
snackbarText: '',
|
||||
@@ -578,12 +707,27 @@ export default {
|
||||
}))
|
||||
},
|
||||
batchScopeOptions() {
|
||||
return [
|
||||
const options = [
|
||||
{ label: this.tm('batchOperations.scopeSelected'), value: 'selected' },
|
||||
{ label: this.tm('batchOperations.scopeAll'), value: 'all' },
|
||||
{ label: this.tm('batchOperations.scopeGroup'), value: 'group' },
|
||||
{ label: this.tm('batchOperations.scopePrivate'), value: 'private' },
|
||||
]
|
||||
// 添加自定义分组选项
|
||||
if (this.groups.length > 0) {
|
||||
options.push({ label: '── 自定义分组 ──', value: '_divider', disabled: true })
|
||||
this.groups.forEach(g => {
|
||||
options.push({ label: `📁 ${g.name} (${g.umo_count})`, value: `custom_group:${g.id}` })
|
||||
})
|
||||
}
|
||||
return options
|
||||
},
|
||||
|
||||
groupOptions() {
|
||||
return this.groups.map(g => ({
|
||||
label: `${g.name} (${g.umo_count} 个会话)`,
|
||||
value: g.id
|
||||
}))
|
||||
},
|
||||
|
||||
statusOptions() {
|
||||
@@ -601,6 +745,26 @@ export default {
|
||||
}
|
||||
return hasChanges
|
||||
},
|
||||
|
||||
// 穿梭框:未选中的UMO列表
|
||||
unselectedUmos() {
|
||||
const selected = new Set(this.editingGroup.umos || [])
|
||||
return this.availableUmos.filter(u => !selected.has(u))
|
||||
},
|
||||
|
||||
// 穿梭框:过滤后的未选中列表
|
||||
filteredUnselectedUmos() {
|
||||
if (!this.groupMemberSearch) return this.unselectedUmos
|
||||
const search = this.groupMemberSearch.toLowerCase()
|
||||
return this.unselectedUmos.filter(u => u.toLowerCase().includes(search))
|
||||
},
|
||||
|
||||
// 穿梭框:过滤后的已选中列表
|
||||
filteredSelectedUmos() {
|
||||
if (!this.groupSelectedSearch) return this.editingGroup.umos || []
|
||||
const search = this.groupSelectedSearch.toLowerCase()
|
||||
return (this.editingGroup.umos || []).filter(u => u.toLowerCase().includes(search))
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -619,6 +783,7 @@ export default {
|
||||
|
||||
mounted() {
|
||||
this.loadData()
|
||||
this.loadGroups()
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
@@ -1148,9 +1313,16 @@ export default {
|
||||
async applyBatchChanges() {
|
||||
this.batchUpdating = true
|
||||
try {
|
||||
const scope = this.batchScope
|
||||
let scope = this.batchScope
|
||||
let groupId = null
|
||||
let umos = []
|
||||
|
||||
// 处理自定义分组
|
||||
if (scope.startsWith('custom_group:')) {
|
||||
groupId = scope.split(':')[1]
|
||||
scope = 'custom_group'
|
||||
}
|
||||
|
||||
if (scope === 'selected') {
|
||||
umos = this.selectedItems.map(item => item.umo)
|
||||
if (umos.length === 0) {
|
||||
@@ -1163,7 +1335,7 @@ export default {
|
||||
const tasks = []
|
||||
|
||||
if (this.batchLlmStatus !== null || this.batchTtsStatus !== null) {
|
||||
const serviceData = { scope, umos }
|
||||
const serviceData = { scope, umos, group_id: groupId }
|
||||
if (this.batchLlmStatus !== null) {
|
||||
serviceData.llm_enabled = this.batchLlmStatus
|
||||
}
|
||||
@@ -1177,6 +1349,7 @@ export default {
|
||||
tasks.push(axios.post('/api/session/batch-update-provider', {
|
||||
scope,
|
||||
umos,
|
||||
group_id: groupId,
|
||||
provider_type: 'chat_completion',
|
||||
provider_id: this.batchChatProvider || null
|
||||
}))
|
||||
@@ -1186,6 +1359,7 @@ export default {
|
||||
tasks.push(axios.post('/api/session/batch-update-provider', {
|
||||
scope,
|
||||
umos,
|
||||
group_id: groupId,
|
||||
provider_type: 'text_to_speech',
|
||||
provider_id: this.batchTtsProvider || null
|
||||
}))
|
||||
@@ -1215,6 +1389,162 @@ export default {
|
||||
}
|
||||
this.batchUpdating = false
|
||||
},
|
||||
|
||||
// ==================== 分组管理方法 ====================
|
||||
|
||||
async loadGroups() {
|
||||
this.groupsLoading = true
|
||||
try {
|
||||
const response = await axios.get('/api/session/groups')
|
||||
if (response.data.status === 'ok') {
|
||||
this.groups = response.data.data.groups || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载分组失败:', error)
|
||||
}
|
||||
this.groupsLoading = false
|
||||
},
|
||||
|
||||
async loadAvailableUmos() {
|
||||
if (this.availableUmos.length > 0) return
|
||||
this.loadingUmos = true
|
||||
try {
|
||||
const response = await axios.get('/api/session/active-umos')
|
||||
if (response.data.status === 'ok') {
|
||||
this.availableUmos = response.data.data.umos || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载会话列表失败:', error)
|
||||
}
|
||||
this.loadingUmos = false
|
||||
},
|
||||
|
||||
openCreateGroupDialog() {
|
||||
this.groupDialogMode = 'create'
|
||||
this.editingGroup = { id: null, name: '', umos: [] }
|
||||
this.groupMemberSearch = ''
|
||||
this.groupSelectedSearch = ''
|
||||
this.groupDialog = true
|
||||
},
|
||||
|
||||
openEditGroupDialog(group) {
|
||||
this.groupDialogMode = 'edit'
|
||||
this.editingGroup = { ...group, umos: [...(group.umos || [])] }
|
||||
this.groupMemberSearch = ''
|
||||
this.groupSelectedSearch = ''
|
||||
this.groupDialog = true
|
||||
},
|
||||
|
||||
// 穿梭框操作方法
|
||||
addToGroup(umo) {
|
||||
if (!this.editingGroup.umos.includes(umo)) {
|
||||
this.editingGroup.umos.push(umo)
|
||||
}
|
||||
},
|
||||
|
||||
removeFromGroup(umo) {
|
||||
const idx = this.editingGroup.umos.indexOf(umo)
|
||||
if (idx > -1) {
|
||||
this.editingGroup.umos.splice(idx, 1)
|
||||
}
|
||||
},
|
||||
|
||||
addAllToGroup() {
|
||||
this.unselectedUmos.forEach(umo => {
|
||||
if (!this.editingGroup.umos.includes(umo)) {
|
||||
this.editingGroup.umos.push(umo)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
removeAllFromGroup() {
|
||||
this.editingGroup.umos = []
|
||||
},
|
||||
|
||||
formatUmoShort(umo) {
|
||||
// 简化显示:平台:类型:ID -> 只显示ID部分
|
||||
const parts = umo.split(':')
|
||||
if (parts.length >= 3) {
|
||||
return `${parts[0]}:${parts[2]}`
|
||||
}
|
||||
return umo
|
||||
},
|
||||
|
||||
async saveGroup() {
|
||||
if (!this.editingGroup.name.trim()) {
|
||||
this.showError('分组名称不能为空')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let response
|
||||
if (this.groupDialogMode === 'create') {
|
||||
response = await axios.post('/api/session/group/create', {
|
||||
name: this.editingGroup.name,
|
||||
umos: this.editingGroup.umos
|
||||
})
|
||||
} else {
|
||||
response = await axios.post('/api/session/group/update', {
|
||||
id: this.editingGroup.id,
|
||||
name: this.editingGroup.name,
|
||||
umos: this.editingGroup.umos
|
||||
})
|
||||
}
|
||||
|
||||
if (response.data.status === 'ok') {
|
||||
this.showSuccess(response.data.data.message)
|
||||
this.groupDialog = false
|
||||
await this.loadGroups()
|
||||
} else {
|
||||
this.showError(response.data.message)
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError(error.response?.data?.message || '保存分组失败')
|
||||
}
|
||||
},
|
||||
|
||||
async deleteGroup(group) {
|
||||
if (!confirm(`确定要删除分组 "${group.name}" 吗?`)) return
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/session/group/delete', { id: group.id })
|
||||
if (response.data.status === 'ok') {
|
||||
this.showSuccess(response.data.data.message)
|
||||
await this.loadGroups()
|
||||
} else {
|
||||
this.showError(response.data.message)
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError(error.response?.data?.message || '删除分组失败')
|
||||
}
|
||||
},
|
||||
|
||||
openGroupMemberDialog(group) {
|
||||
this.groupMemberTarget = { ...group }
|
||||
this.groupMemberDialog = true
|
||||
},
|
||||
|
||||
async addSelectedToGroup(groupId) {
|
||||
if (this.selectedItems.length === 0) {
|
||||
this.showError('请先选择要添加的会话')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/session/group/update', {
|
||||
id: groupId,
|
||||
add_umos: this.selectedItems.map(item => item.umo)
|
||||
})
|
||||
if (response.data.status === 'ok') {
|
||||
this.showSuccess(`已添加 ${this.selectedItems.length} 个会话到分组`)
|
||||
await this.loadGroups()
|
||||
} else {
|
||||
this.showError(response.data.message)
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError(error.response?.data?.message || '添加失败')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -1231,4 +1561,20 @@ code {
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.transfer-list {
|
||||
max-height: 280px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.transfer-item {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
|
||||
.transfer-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user