Quellcode durchsuchen

feat: user 列表 + 左侧的部门列表

luoyali vor 1 Jahr
Ursprung
Commit
654082d76a

+ 30 - 0
src/api/system/role.ts

@@ -0,0 +1,30 @@
+import request from '@/utils/request'
+import { AxiosPromise } from 'axios'
+
+// apiUrl
+const api = {
+	page: '/sys/role/page',
+	listTree: '/sys/role/list-tree',
+	listAll: '/sys/role/list-all',
+	create: '/sys/role/create',
+	update: '/sys/role/update',
+	delete: '/sys/role/delete',
+	resourceSet: '/sys/role/resource-set',
+	resourceIds: '/sys/role/resource-ids'
+}
+
+const role = {
+	rolePageApi: (params): AxiosPromise =>
+		request({
+			url: api.page,
+			method: 'post',
+			params
+		}),
+	roleListTreeApi: (): AxiosPromise => request({ url: api.listTree, method: 'get' }),
+	roleListAllApi: (): AxiosPromise => request({ url: api.listAll, method: 'get' }),
+	roleSaveApi: (params): AxiosPromise => request({ url: params.id ? api.update : api.create, method: 'post', params }),
+	roleDeleteApi: (ids): AxiosPromise => request({ url: api.delete, method: 'post', ids }),
+	roleResourceSetApi: (params): AxiosPromise => request({ url: api.resourceSet, method: 'post', params }),
+	roleResourceIdsApi: (id): AxiosPromise => request({ url: api.resourceIds + '?id=' + id, method: 'get' })
+}
+export default role

+ 67 - 0
src/components/StatusIndicator/index.vue

@@ -0,0 +1,67 @@
+<template>
+	<span class="sc-state" :class="[{ 'sc-status-processing': pulse }, 'sc-state-bg--' + type]"></span>
+</template>
+
+<script>
+export default {
+	props: {
+		type: { type: String, default: 'primary' },
+		pulse: { type: Boolean, default: false }
+	}
+}
+</script>
+
+<style scoped>
+.sc-state {
+	display: inline-block;
+	background: #000;
+	width: 8px;
+	height: 8px;
+	border-radius: 50%;
+	vertical-align: middle;
+}
+.sc-status-processing {
+	position: relative;
+}
+.sc-status-processing:after {
+	position: absolute;
+	top: 0px;
+	left: 0px;
+	width: 100%;
+	height: 100%;
+	border-radius: 50%;
+	background: inherit;
+	content: '';
+	animation: warn 1.2s ease-in-out infinite;
+}
+
+.sc-state-bg--primary {
+	background: var(--el-color-primary);
+}
+.sc-state-bg--success {
+	background: var(--el-color-success);
+}
+.sc-state-bg--warning {
+	background: var(--el-color-warning);
+}
+.sc-state-bg--danger {
+	background: var(--el-color-danger);
+}
+.sc-state-bg--info {
+	background: var(--el-color-info);
+}
+
+@keyframes warn {
+	0% {
+		transform: scale(0.5);
+		opacity: 1;
+	}
+	30% {
+		opacity: 0.7;
+	}
+	100% {
+		transform: scale(2.5);
+		opacity: 0;
+	}
+}
+</style>

+ 0 - 1
src/components/Table/index.vue

@@ -262,7 +262,6 @@ const TableComponent = defineComponent({
 								class="le-table"
 								ref={tableRef}
 								v-loading={unref(computedOptions).loading}
-								border
 								element-loading-text="加载中..."
 								element-loading-background="rgba(0, 0, 0, 0.1)"
 								{...unref(computedOptions)}

+ 1 - 1
src/views/demo/pageConfig/index.vue

@@ -630,4 +630,4 @@ const submitHandler = params => {
 .btn-icon {
 	margin-left: 6px;
 }
-</style>
+</style>

+ 230 - 94
src/views/setting/user/index.vue

@@ -5,46 +5,96 @@
 				<el-aside width="200px">
 					<el-container>
 						<el-header>
-							<el-input v-model="groupFilterText" placeholder="输入关键字进行过滤" clearable />
+							<el-input v-model="groupFilterText" placeholder="输入关键字进行过滤" clearable style="margin-top: 10px" />
 						</el-header>
 						<el-main class="nopadding">
-							<el-tree ref="treeRef" class="filter-tree" :data="data" :props="defaultProps" default-expand-all :filter-node-method="filterNode" />
+							<el-tree
+								ref="treeRef"
+								class="menu-tree"
+								:data="treeData"
+								node-key="id"
+								:current-node-key="''"
+								:highlight-current="true"
+								:expand-on-click-node="false"
+								@node-click="roleClick"
+								:props="defaultProps"
+								default-expand-all
+								:filter-node-method="filterNode"
+							/>
 						</el-main>
 					</el-container>
 				</el-aside>
 				<el-divider direction="vertical" style="height: 100vh" />
+
 				<el-container>
-					<el-header>
-						<div class="flex flex-pack-justify">
-							<div class="left-panel">
-								<el-button type="primary" :icon="Plus"><Plus /></el-button>
-								<el-button type="danger" plain :icon="Delete"></el-button>
-								<el-button type="primary" plain>分配角色</el-button>
-								<el-button type="primary" plain>密码重置</el-button>
-							</div>
-							<div class="right-panel">
-								<div class="right-panel-search flex">
-									<el-input placeholder="登录账号 / 姓名" clearable></el-input>
-									<el-button type="primary" :icon="Search"></el-button>
-								</div>
-							</div>
-						</div>
-					</el-header>
-					<el-main class="nopadding"> </el-main>
+					<el-main class="nopadding">
+						<!-- 公用搜索组件 -->
+						<LeSearchForm ref="searchForm" v-model:searchData="searchData" :forms="forms" :loading="tableOpts.options.loading"> </LeSearchForm>
+
+						<!--  LeTable 组件使用  -->
+						<LeTable
+							ref="tableRef"
+							v-model:searchParams="tableOpts.searchParams"
+							v-bind="tableOpts"
+							:columns="activeColumns"
+							v-model:curRow="tableOpts.curRow"
+							v-model:checked-options="checkedColumns"
+						>
+							<template #toolLeft>
+								<el-button type="primary">
+									<el-icon class="btn-icon">
+										<Plus />
+									</el-icon>
+								</el-button>
+								<el-button type="danger">
+									<el-icon class="btn-icon">
+										<Delete />
+									</el-icon>
+								</el-button>
+								<el-button plain> 分配角色 </el-button>
+								<el-button plain> 密码重置 </el-button>
+							</template>
+
+							<template #filterAvatarSlot="scope">
+								<el-avatar :src="scope.row.avatar" size="small"></el-avatar>
+							</template>
+
+							<template #statusSlot="scope">
+								<status-indicator pulse type="success" v-if="scope.row.status === 1"></status-indicator>
+								<status-indicator pulse type="danger" v-else></status-indicator>
+							</template>
+
+							<template #actionSlot="">
+								<el-tooltip content="编辑" placement="bottom" effect="light">
+									<el-icon class="ibt0">
+										<Edit />
+									</el-icon>
+								</el-tooltip>
+								<el-divider direction="vertical"></el-divider>
+								<el-popconfirm title="确定删除吗?">
+									<template #reference>
+										<el-icon class="ibt0">
+											<Delete />
+										</el-icon>
+									</template>
+								</el-popconfirm>
+							</template>
+						</LeTable>
+					</el-main>
 				</el-container>
 			</el-container>
 		</el-card>
 	</div>
 </template>
-<script lang="ts" setup>
-import { ref, watch } from 'vue'
+<script lang="tsx" setup>
+import role from '@/api/system/role'
+import user from '@/api/system/user'
+import { nextTick, ref, watch } from 'vue'
 import { ElTree } from 'element-plus'
-import { Plus, Delete, Search } from '@element-plus/icons-vue'
-
-interface Tree {
-	[key: string]: any
-}
-
+import { useTablePage } from '@/hooks/useTablePage'
+import { Plus, Delete } from '@element-plus/icons-vue'
+import StatusIndicator from '@/components/StatusIndicator'
+const showGroupLoading = ref(false)
 const groupFilterText = ref('')
 const treeRef = ref<InstanceType<typeof ElTree>>()
 
@@ -53,91 +103,177 @@ const defaultProps = {
 	label: 'label'
 }
 
-watch(groupFilterText, val => {
-	treeRef.value!.filter(val)
-})
-
 const filterNode = (value: string, data: Tree) => {
 	if (!value) return true
 	return data.label.includes(value)
 }
 
-const data: Tree[] = [
+const treeData = ref([])
+
+// 获取左侧菜单数据
+const getGroup = async () => {
+	showGroupLoading.value = true
+	let data = await role.roleListTreeApi()
+	showGroupLoading.value = false
+	data.unshift({ id: '', label: '所有' })
+	treeData.value = data // console.log('获取左侧菜单数据')
+}
+
+// 左侧菜单点击
+const roleClick = data => {
+	console.log(data.id, 'data.id')
+	// 刷新右侧的表格
+}
+
+// 搜索条件
+const forms = ref([
+	{
+		prop: 'name',
+		label: '账号/姓名:',
+		itemType: 'input',
+		placeholder: '请输入登录账号 / 姓名'
+	}
+])
+
+// table列表数据请求
+const queryList = async () => {
+	const { options, searchParams } = tableOpts
+	options.loading = true
+	console.log('搜索参数: ', JSON.stringify(tableOpts.searchParams))
+	try {
+		const { records: list, total } = await user.userPageApi(searchParams)
+		tableOpts.total = total
+		tableOpts.list = list
+	} catch {
+		console.log('获取列表数据失败')
+		tableOpts.total = 0
+		tableOpts.list = []
+		options.loading = false // 更改加载中的 loading值
+	} finally {
+		options.loading = false
+	}
+}
+
+// table 参数
+const columns = [
+	{
+		prop: 'filterAvatar',
+		label: '头像',
+		minWidth: 80,
+		slots: {
+			default: 'filterAvatarSlot'
+		}
+	},
+	{
+		prop: 'username',
+		label: '登录账号',
+		minWidth: 100
+	},
 	{
-		id: 1,
-		label: 'Level one 1',
-		children: [
-			{
-				id: 4,
-				label: 'Level two 1-1',
-				children: [
-					{
-						id: 9,
-						label: 'Level three 1-1-1'
-					},
-					{
-						id: 10,
-						label: 'Level three 1-1-2'
-					}
-				]
-			}
-		]
+		prop: 'status',
+		label: '状态',
+		minWidth: 50,
+		slots: {
+			default: 'statusSlot'
+		}
 	},
 	{
-		id: 2,
-		label: 'Level one 2',
-		children: [
-			{
-				id: 5,
-				label: 'Level two 2-1'
-			},
-			{
-				id: 6,
-				label: 'Level two 2-2'
-			}
-		]
+		prop: 'nickName',
+		label: '昵称',
+		minWidth: 100
 	},
 	{
-		id: 3,
-		label: 'Level one 3',
-		children: [
-			{
-				id: 7,
-				label: 'Level two 3-1'
-			},
-			{
-				id: 8,
-				label: 'Level two 3-2'
-			}
-		]
+		prop: 'realName',
+		label: '姓名',
+		minWidth: 100
+	},
+	{
+		prop: 'sex',
+		label: '性别',
+		minWidth: 80
+	},
+	{
+		prop: 'updateBy',
+		label: '修改人',
+		minWidth: 100
+	},
+	{
+		prop: 'updateTime',
+		label: '修改时间',
+		minWidth: 126
+	},
+	{
+		prop: 'createBy',
+		label: '创建人',
+		minWidth: 100
+	},
+	{
+		prop: 'createTime',
+		label: '创建时间',
+		minWidth: 126
+	},
+	{
+		prop: 'action',
+		label: '操作',
+		width: 100,
+		fixed: 'right',
+		slots: {
+			default: 'actionSlot'
+		}
 	}
 ]
+
+const { searchData, tableOpts, checkedColumns, activeColumns } = useTablePage(
+	{
+		options: {
+			showIndex: false
+		},
+		searchParams: {
+			page: 1,
+			size: 10
+		},
+		// 需要展示的列
+		columns,
+		// 控制列配置
+		columnsConfig: {
+			columns
+		}
+	},
+	{
+		queryList,
+		fetchImmediate: false
+	}
+)
+
+nextTick(() => {
+	getGroup()
+	queryList()
+})
+
+watch(groupFilterText, val => {
+	treeRef.value!.filter(val)
+})
 </script>
 <style scoped lang="scss">
-.box-card {
-	// 单独自己写的
+// 单独自己写的
+:deep(.box-card) {
 	height: 100%;
+	.el-card__body {
+		padding: 0;
+	}
 }
 
-// todo 样式问题 应该要提成公用
-.pageWrap {
-	//padding-top: 12px;
-	padding: 12px 12px 0 12px;
-	overflow: auto;
-	background-color: #f5f6f7;
+// 角色的树结构样式
+:deep(.menu-tree) {
+	.el-tree-node__content {
+		height: 36px;
+	}
+	.el-tree-node__content .el-tree-node__label .icon {
+		margin-right: 5px;
+	}
 }
 
-// 其他样式
-.local_table {
-	//padding: 0 12px;
-	box-shadow: 0 0 6px 4px rgb(145 159 175 / 6%);
-	border-top: 1px solid #eaedf0;
-	border-radius: 6px 6px 0 0;
-	background-color: #fff;
-	//padding: 12px 12px 0 12px;
-	&.tabs_content-wrap {
-		border-top: 0;
-		border-radius: 0;
-	}
+.nopadding {
+	padding: 0px;
 }
 </style>