本地 RAG 知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

155 lines
5.2 KiB

/**
* 账号管理
* 管理对话账号,并为账号绑定默认客服角色。
*/
import { ref, reactive, onMounted } from 'vue'
import { getAllAccounts, createAccount, updateAccount, deleteAccount, getRoleList } from '../js/api.js'
import { toast } from '../js/utils.js'
export default {
template: `
<div class="card">
<h2>账号管理</h2>
<p style="font-size:12px;color:var(--sub);margin:4px 0 12px;">
为每个账号绑定一个默认角色。对话页选择账号后,系统会按账号绑定的角色回答,并把会话历史归属到该账号。
</p>
<div style="border:1px solid var(--border);border-radius:8px;padding:12px;margin-bottom:16px;">
<div class="input-row">
<input class="input" v-model="form.name" placeholder="账号名称 *">
<input class="input" v-model="form.description" placeholder="账号说明(可选)">
<select class="select" v-model="form.roleId">
<option value="">未绑定角色</option>
<option v-for="role in roles" :key="role.id" :value="role.id">{{ role.name }}</option>
</select>
<label class="toggle-line"><input type="checkbox" v-model="form.enabled"><span>启用</span></label>
</div>
<div class="input-row" style="margin-top:8px;">
<button class="btn btn-success" @click="submit">{{ editingId ? '保存修改' : '新增账号' }}</button>
<button v-if="editingId" class="btn btn-outline btn-sm" @click="resetForm">取消编辑</button>
<button class="btn btn-outline btn-sm" @click="reload">刷新</button>
</div>
</div>
<div v-if="!accounts.length" style="font-size:13px;color:var(--sub);">暂无账号</div>
<div v-else style="overflow-x:auto;">
<table class="data-table">
<thead>
<tr>
<th>账号</th>
<th>说明</th>
<th>绑定角色</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="account in accounts" :key="account.id">
<td>
<strong>{{ account.name }}</strong>
<div style="font-size:11px;color:var(--sub);">{{ account.account_key }}</div>
</td>
<td>{{ account.description || '-' }}</td>
<td>{{ account.role_name || '未绑定' }}</td>
<td>{{ account.enabled === false ? '停用' : '启用' }}</td>
<td>
<button class="btn btn-outline btn-sm" @click="startEdit(account)">编辑</button>
<button class="btn btn-danger btn-sm" @click="remove(account)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`,
setup() {
const accounts = ref([])
const roles = ref([])
const editingId = ref('')
const form = reactive({ name: '', description: '', roleId: '', enabled: true })
async function loadRoles() {
const json = await getRoleList()
if (json.success) {
roles.value = json.data || []
}
}
async function reload() {
try {
const json = await getAllAccounts()
if (json.success) {
accounts.value = json.data || []
}
} catch (e) {
toast('加载账号失败:' + e.message, 'error')
}
}
function resetForm() {
editingId.value = ''
form.name = ''
form.description = ''
form.roleId = ''
form.enabled = true
}
function startEdit(account) {
editingId.value = account.id
form.name = account.name || ''
form.description = account.description || ''
form.roleId = account.role_id || ''
form.enabled = account.enabled !== false
}
async function submit() {
if (!form.name.trim()) {
toast('请输入账号名称', 'error')
return
}
const payload = {
name: form.name,
description: form.description,
roleId: form.roleId || null,
enabled: form.enabled
}
try {
const json = editingId.value
? await updateAccount(editingId.value, payload)
: await createAccount(payload)
if (json.success) {
toast(editingId.value ? '账号已更新' : '账号已创建', 'success')
resetForm()
reload()
} else {
toast(json.message || '操作失败', 'error')
}
} catch (e) {
toast('操作失败:' + e.message, 'error')
}
}
async function remove(account) {
if (!confirm(`确定删除账号「${account.name}」?历史会话记录会保留归属信息。`)) return
try {
const json = await deleteAccount(account.id)
if (json.success) {
toast('账号已删除', 'success')
if (editingId.value === account.id) resetForm()
reload()
} else {
toast(json.message || '删除失败', 'error')
}
} catch (e) {
toast('删除失败:' + e.message, 'error')
}
}
onMounted(() => {
loadRoles()
reload()
})
return { accounts, roles, editingId, form, reload, resetForm, startEdit, submit, remove }
}
}