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
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 }
|
|
}
|
|
}
|