This commit is contained in:
2026-01-21 22:53:25 +08:00
parent 638c846aed
commit 155ec5c986
37 changed files with 3468 additions and 729 deletions
+202 -164
View File
@@ -1,180 +1,218 @@
<template>
<div class="system-user">
<a-card title="用户管理">
<template #extra>
<a-button type="primary" @click="handleAdd">
<PlusOutlined />
新增用户
</a-button>
</template>
<el-container>
<el-aside width="200px" v-loading="showGrouploading">
<el-container>
<el-header>
<el-input placeholder="输入关键字进行过滤" v-model="groupFilterText" clearable></el-input>
</el-header>
<el-main class="nopadding">
<el-tree ref="group" class="menu" node-key="id" :data="group" :props="{label: 'title'}" :current-node-key="''" :highlight-current="true" :expand-on-click-node="false" :filter-node-method="groupFilterNode" @node-click="groupClick"></el-tree>
</el-main>
</el-container>
</el-aside>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button type="danger" plain icon="el-icon-delete" :disabled="selection.length==0" @click="batch_del"></el-button>
<el-button type="primary" plain :disabled="selection.length!=1" @click="roleSet">分配角色</el-button>
</div>
<div class="right-panel">
<div class="right-panel-search">
<el-input v-model="search.name" placeholder="登录账号 / 姓名" clearable></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</div>
</div>
</el-header>
<el-main class="nopadding">
<scTable ref="table" :apiObj="list.apiObj" :column="list.column" @selection-change="selectionChange" stripe remoteSort remoteFilter>
<el-table-column type="selection" width="50"></el-table-column>
<template #username="scope">
<div style="display: flex; flex-direction: row; align-items: center; gap: 8px;">
<el-avatar :src="scope.row.avatar" shape="square" :size="50"></el-avatar>
<div style="display: flex; flex-direction: column;">
<span>{{ scope.row.username }}</span>
<span>{{ scope.row.nickname }}</span>
</div>
</div>
</template>
<template #roleName="scope">
<el-tag v-for="item in scope.row?.roles" :key="item.id">{{item.title}}</el-tag>
</template>
<template #department_name="scope">
{{scope.row.department?.title}}
</template>
<template #operation="scope">
<el-button-group>
<el-button type="success" @click="table_show(scope.row, scope.$index)">查看</el-button>
<el-button type="primary" @click="table_edit(scope.row, scope.$index)">编辑</el-button>
<el-popconfirm title="确定删除吗?" @confirm="table_del(scope.row, scope.$index)">
<template #reference>
<el-button type="danger">删除</el-button>
</template>
</el-popconfirm>
</el-button-group>
</template>
</scTable>
</el-main>
</el-container>
</el-container>
<div class="search-form">
<sc-form :form-items="formItems" :initial-values="searchForm" :show-actions="true" submit-text="查询"
reset-text="重置" @finish="handleSearch" @reset="handleReset" layout="inline" />
</div>
<save-dialog v-if="dialog.save" ref="saveDialog" @success="handleSuccess" @closed="dialog.save=false"></save-dialog>
<role-dialog v-if="dialog.role" ref="roleDialog" @success="handleSuccess" @closed="dialog.role=false"></role-dialog>
<sc-table :columns="columns" :data-source="dataSource" :loading="loading" :pagination="pagination"
:show-action-column="true" :action-column="{
title: '操作',
key: 'action',
width: 150,
}">
<template #status="{ record }">
<a-tag :color="record.status === 1 ? 'green' : 'red'">
{{ record.status === 1 ? '正常' : '禁用' }}
</a-tag>
</template>
<template #action="{ record }">
<a-space>
<a-button type="link" size="small" @click="handleEdit(record)">编辑</a-button>
<a-button type="link" size="small" danger @click="handleDelete(record)">删除</a-button>
</a-space>
</template>
</sc-table>
</a-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import ScTable from '@/components/scTable/index.vue'
import ScForm from '@/components/scForm/index.vue'
<script>
import saveDialog from './save'
import roleDialog from './role'
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
export default {
name: 'auth.user',
components: {
saveDialog,
roleDialog
},
{
title: '用户名',
dataIndex: 'username',
key: 'username',
data() {
return {
dialog: {
save: false
},
showGrouploading: false,
groupFilterText: '',
group: [],
list: {
apiObj: this.$API.auth.users.list,
column: [
{prop: 'uid', label: 'UID', width: '80'},
{prop: 'username', label: '登录账号', width: '220'},
{prop: 'mobile', label: '手机号码', width: '120'},
{prop: 'email', label: '邮箱', width: '160'},
{prop: 'department_name', label: '所属部门', width: '120'},
{prop: 'roleName', label: '所属角色', width:'120'},
{prop: 'created_at', label: '加入时间', width:'180'},
{prop: 'last_login_at', label: '上次登录时间', width:'180'},
{prop: 'operation', label: '操作', width:'160', fixed: 'right'}
]
},
selection: [],
search: {
name: null
}
}
},
{
title: '昵称',
dataIndex: 'nickname',
key: 'nickname',
watch: {
groupFilterText(val) {
this.$refs.group.filter(val);
}
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
mounted() {
this.getGroup()
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
},
]
methods: {
//添加
add(){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open()
})
},
//编辑
table_edit(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('edit').setData(row)
})
},
//查看
table_show(row){
this.dialog.save = true
this.$nextTick(() => {
this.$refs.saveDialog.open('show').setData(row)
})
},
//删除
async table_del(row, index){
var reqData = {id: row.uid}
var res = await this.$API.auth.users.delete.post(reqData);
if(res.code == 1){
this.$refs.table.refresh()
this.$message.success("删除成功")
}else{
this.$message.error(res.message)
}
},
//批量删除
async batch_del(){
this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning'
}).then(async () => {
var ids = this.selection.map(item => item.uid)
var reqData = {ids: ids}
var res = await this.$API.auth.users.delete.post(reqData);
if(res.code == 1){
this.$refs.table.refresh()
this.$message.success("删除成功")
}else{
this.$message.error(res.message)
}
}).catch(() => {
// 搜索表单配置
const formItems = [
{
field: 'username',
label: '用户名',
type: 'input',
placeholder: '请输入用户名',
allowClear: true,
},
{
field: 'status',
label: '状态',
type: 'select',
placeholder: '请选择状态',
options: [
{ label: '全部', value: '' },
{ label: '正常', value: 1 },
{ label: '禁用', value: 0 },
],
allowClear: true,
style: 'width: 120px',
},
]
const searchForm = ref({
username: '',
status: '',
})
const dataSource = ref([])
const loading = ref(false)
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total) => `${total}`,
})
// 模拟数据
const mockData = [
{
id: 1,
username: 'admin',
nickname: '管理员',
status: 1,
createTime: '2024-01-01 10:00:00',
},
{
id: 2,
username: 'user',
nickname: '普通用户',
status: 1,
createTime: '2024-01-02 10:00:00',
},
]
const loadData = () => {
loading.value = true
// 模拟接口请求
setTimeout(() => {
dataSource.value = mockData
pagination.value.total = mockData.length
loading.value = false
}, 500)
}
const handleSearch = () => {
loadData()
}
const handleReset = () => {
searchForm.value = {
username: '',
status: '',
})
},
//权限设置
roleSet(){
if(this.selection.length != 1){
this.$message.error("请选择一条数据")
return false;
}
this.dialog.role = true
this.$nextTick(() => {
this.$refs.roleDialog.open().setData(this.selection[0])
})
},
insertData(){
this.dialog.insert = true
this.$nextTick(() => {
this.$refs.insertDialog.open()
})
},
//表格选择后回调事件
selectionChange(selection){
this.selection = selection;
},
//加载树数据
async getGroup(){
this.showGrouploading = true;
var res = await this.$API.auth.department.list.get({is_tree: 1});
this.showGrouploading = false;
var allNode ={id: '', title: '所有'}
res.data.unshift(allNode);
this.group = res.data;
},
//树过滤
groupFilterNode(value, data){
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
//树点击事件
groupClick(data){
var params = {
department_id: data.id
}
this.$refs.table.reload(params)
},
//搜索
upsearch(){
this.$refs.table.upData(this.search)
},
//本地更新数据
handleSuccess(){
this.$refs.table.refresh()
}
}
loadData()
}
const handleAdd = () => {
message.info('新增用户功能开发中')
}
const handleEdit = (record) => {
message.info('编辑用户功能开发中')
}
const handleDelete = (record) => {
message.info('删除用户功能开发中')
}
onMounted(() => {
loadData()
})
</script>
<style scoped lang="scss">
.system-user {
.search-form {
margin-bottom: 16px;
padding: 16px;
background-color: #fafafa;
border-radius: 4px;
}
}
<style>
</style>