34 lines
6.0 KiB
Vue
34 lines
6.0 KiB
Vue
<script setup lang="ts">
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
import { EditOutlined, PlusOutlined, TeamOutlined } from '@ant-design/icons-vue'
|
||
import { message } from 'ant-design-vue'
|
||
import { api, apiMessage } from '@/api/client'
|
||
import { useAuthStore } from '@/stores/auth'
|
||
import type { Member, Role } from '@/types'
|
||
|
||
const auth=useAuthStore();const loading=ref(false);const open=ref(false);const editing=ref<Member|null>(null);const items=ref<Member[]>([]);const roles=ref<Role[]>([]);const saving=ref(false)
|
||
const form=reactive({username:'',display_name:'',password:'',role_code:'operator'})
|
||
const editForm=reactive({role_code:'operator',status:'active'})
|
||
const roleOptions=computed(()=>roles.value.map(role=>({value:role.code,label:role.name})))
|
||
const columns=[{title:'成员',key:'member'},{title:'角色',dataIndex:'role_name',width:150},{title:'状态',dataIndex:'status',width:110},{title:'',key:'action',width:80}]
|
||
async function load(){loading.value=true;try{const data=await api.get<{items:Member[];roles:Role[]}>('/members');items.value=data.items||[];roles.value=data.roles||[]}catch(error){message.error(apiMessage(error))}finally{loading.value=false}}
|
||
async function create(){saving.value=true;try{await api.post('/members',form);message.success('成员已创建');open.value=false;Object.assign(form,{username:'',display_name:'',password:'',role_code:'operator'});await load()}catch(error){message.error(apiMessage(error))}finally{saving.value=false}}
|
||
function edit(item:Member){editing.value=item;Object.assign(editForm,{role_code:item.role_code,status:item.status})}
|
||
async function update(){if(!editing.value)return;saving.value=true;try{await api.put(`/members/${editing.value.id}`,editForm);message.success('成员权限已更新');editing.value=null;await load()}catch(error){message.error(apiMessage(error))}finally{saving.value=false}}
|
||
onMounted(load)
|
||
</script>
|
||
|
||
<template>
|
||
<div class="page-shell members-page">
|
||
<div class="page-heading"><div><h1>企业成员</h1><p>分配场景维护、SOP 编辑和一线执行职责,权限变更会立即生效。</p></div><a-button type="primary" @click="open=true"><PlusOutlined />添加成员</a-button></div>
|
||
<section class="member-summary"><TeamOutlined /><div><b>{{ items.filter(item=>item.status==='active').length }}</b><span>在用账号</span></div><p>管理员拥有全部权限;维护者配置场景契约、知识和 SOP;执行者使用可用 SOP 和自己的记录。</p></section>
|
||
<section class="surface"><a-table row-key="id" :loading="loading" :data-source="items" :columns="columns" :pagination="false"><template #bodyCell="{column,record}"><template v-if="column.key==='member'"><div class="member-cell"><span>{{ record.display_name.slice(0,1) }}</span><div><b>{{ record.display_name }}</b><small>{{ record.username }}</small></div></div></template><template v-else-if="column.dataIndex==='role_name'"><a-tag>{{ record.role_name }}</a-tag></template><template v-else-if="column.dataIndex==='status'"><a-tag :color="record.status==='active'?'green':'default'">{{ record.status==='active'?'使用中':'已停用' }}</a-tag></template><template v-else-if="column.key==='action'"><a-button v-if="record.user_id!==auth.user?.user_id" type="text" aria-label="编辑成员权限" @click="edit(record)"><EditOutlined /></a-button></template></template></a-table></section>
|
||
<a-drawer v-model:open="open" title="添加企业成员" width="480"><a-form layout="vertical" :model="form" @finish="create"><div class="form-grid"><a-form-item label="登录用户名" name="username" :rules="[{required:true,message:'请输入用户名'}]"><a-input v-model:value="form.username" /></a-form-item><a-form-item label="显示名称" name="display_name" :rules="[{required:true,message:'请输入显示名称'}]"><a-input v-model:value="form.display_name" /></a-form-item></div><a-form-item label="初始密码" name="password" :rules="[{required:true,min:6,message:'密码至少 6 位'}]"><a-input-password v-model:value="form.password" /></a-form-item><a-form-item label="角色" name="role_code" :rules="[{required:true}]"><a-select v-model:value="form.role_code" :options="roleOptions" /></a-form-item><a-button type="primary" html-type="submit" block :loading="saving">创建成员</a-button></a-form></a-drawer>
|
||
<a-modal :open="Boolean(editing)" title="调整成员权限" ok-text="保存" :confirm-loading="saving" @ok="update" @cancel="editing=null"><div v-if="editing" class="editing-name"><b>{{ editing.display_name }}</b><span>{{ editing.username }}</span></div><a-form layout="vertical"><a-form-item label="角色"><a-select v-model:value="editForm.role_code" :options="roleOptions" /></a-form-item><a-form-item label="账号状态"><a-segmented v-model:value="editForm.status" :options="[{value:'active',label:'使用中'},{value:'disabled',label:'停用'}]" /></a-form-item></a-form></a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.members-page{max-width:1180px}.member-summary{min-height:86px;display:grid;grid-template-columns:38px 90px 1fr;align-items:center;gap:14px;margin-bottom:16px;padding:16px 20px;color:white;background:#202825;border-radius:6px}.member-summary>span{font-size:25px;color:#6bc5a7}.member-summary>div{display:flex;flex-direction:column}.member-summary b{font-family:"Noto Serif SC",serif;font-size:24px}.member-summary div span{color:#a9b8b2;font-size:11px}.member-summary p{margin:0;color:#b6c3be;line-height:1.7}.member-cell{display:flex;align-items:center;gap:11px}.member-cell>span{width:34px;height:34px;display:grid;place-items:center;color:#17664f;background:#e5f2ed;border-radius:4px;font-weight:700}.member-cell>div{display:flex;flex-direction:column}.member-cell small{margin-top:3px;color:var(--muted)}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.editing-name{display:flex;flex-direction:column;margin-bottom:18px;padding:12px 14px;background:#f4f7f5;border-left:3px solid var(--green)}.editing-name span{margin-top:4px;color:var(--muted);font-size:12px}@media(max-width:650px){.member-summary{grid-template-columns:34px 1fr}.member-summary p{grid-column:1/3}.form-grid{grid-template-columns:1fr;gap:0}}
|
||
</style>
|