Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master'

lanceJiang 1 anno fa
parent
commit
1564f75ebb

+ 1 - 0
src/api/demo/index.ts

@@ -57,6 +57,7 @@ export function getAdminList(data: any): AxiosPromise {
 	// 	params: data
 	// })
 }
+
 export function queryEdit(data: any) {
 	return new Promise(resolve => {
 		// console.warn(data, 'data')

+ 86 - 0
src/api/system/user.ts

@@ -0,0 +1,86 @@
+import request from '@/utils/request'
+import { AxiosPromise } from 'axios'
+
+// apiUrl
+const api = {
+	page: '/sys/user/page',
+	create: '/sys/user/create',
+	update: '/sys/user/update',
+	delete: '/sys/user/delete',
+	resetPassword: '/sys/user/reset-password',
+	assignRoles: '/sys/user/assign-roles',
+	roleIds: '/sys/user/role-ids'
+}
+
+/**
+ * 用户管理 - 列表
+ */
+function userPageApi(data: any): AxiosPromise {
+	return request({
+		url: api.page,
+		method: 'post',
+		data
+	})
+}
+
+/**
+ * 用户管理 - 新增编辑保存
+ */
+function userAddOrEditSaveApi(data: any): AxiosPromise {
+	return request({
+		url: data.id ? api.update : api.create,
+		method: 'post',
+		data
+	})
+}
+
+/**
+ * 用户管理 - 删除
+ */
+function userDeleteApi(data: any): AxiosPromise {
+	return request({
+		url: api.delete,
+		method: 'post',
+		data
+	})
+}
+
+/**
+ * 用户管理 - 重置密码
+ */
+function userResetPasswordApi(data: any): AxiosPromise {
+	return request({
+		url: api.resetPassword,
+		method: 'post',
+		data
+	})
+}
+/**
+ * 用户管理 - 重置密码
+ */
+function userAssignRolesApi(data: any): AxiosPromise {
+	return request({
+		url: api.assignRoles,
+		method: 'post',
+		data
+	})
+}
+/**
+ * 用户管理 - 重置密码
+ */
+function userRoleIdsApi(data: any): AxiosPromise {
+	return request({
+		url: `${api.roleIds}?id=${data.id}`,
+		method: 'post'
+	})
+}
+
+const user = {
+	userPageApi,
+	userAddOrEditSaveApi,
+	userDeleteApi,
+	userResetPasswordApi,
+	userAssignRolesApi,
+	userRoleIdsApi
+}
+export default user

+ 14 - 0
src/router/index.ts

@@ -146,6 +146,20 @@ export const constantRoutes: Array<AppRouteRecordRaw> = [
 		]
 	},
 	// 仅用于研发测试 END
+	{
+		path: '/setting',
+		component: Layout,
+		meta: { title: 'Setting', icon: 'guide' },
+		redirect: '/setting/user',
+		children: [
+			{
+				path: 'user',
+				component: () => import('@/views/setting/user/index.vue'),
+				name: 'user',
+				meta: { title: '用户管理', icon: '' }
+			}
+		]
+	},
 	// 外部链接
 	{
 		path: '/external-link',

+ 122 - 0
src/styles/flex.scss

@@ -0,0 +1,122 @@
+/* ============================================================
+   flex:定义布局为盒模型
+   flex-v:盒模型垂直布局
+   flex-1:子元素占据剩余的空间
+   flex-align-center:子元素垂直居中
+   flex-pack-center:子元素水平居中
+   flex-pack-justify:子元素两端对齐
+   兼容性:ios 4+、android 2.3+、winphone8+、IE10+、Safari、Opera、Chrome、Firefox
+   ============================================================ */
+.flex {
+	display: -webkit-box;
+	display: -webkit-flex;
+	display: -ms-flexbox;
+	display: -moz-box;
+	display: -moz-flex;
+	display: flex;
+}
+.flex-align-pack-center {
+	-webkit-box-pack: center;
+	-webkit-justify-content: center;
+	-moz-box-pack: center;
+	-moz-justify-content: center;
+	-ms-flex-pack: center;
+	justify-content: center;
+	-webkit-box-align: center;
+	-webkit-align-items: center;
+	-moz-box-align: center;
+	-moz-align-items: center;
+	-ms-flex-align: center;
+	align-items: center;
+}
+.flex-v {
+	-webkit-box-orient: vertical;
+	-webkit-flex-direction: column;
+	-moz-box-orient: vertical;
+	-moz-flex-direction: column;
+	-ms-flex-direction: column;
+	flex-direction: column;
+}
+.flex-1 {
+	-webkit-box-flex: 1;
+	-webkit-flex: 1;
+	-moz-box-flex: 1;
+	-moz-flex: 1;
+	-ms-flex: 1;
+	flex: 1;
+}
+.flex-align-center {
+	-webkit-box-align: center;
+	-webkit-align-items: center;
+	-moz-box-align: center;
+	-moz-align-items: center;
+	-ms-flex-align: center;
+	align-items: center;
+}
+// flex-align-start
+.flex-align-start {
+	-webkit-box-align: flex-start;
+	-webkit-align-items: flex-start;
+	-moz-box-align: flex-start;
+	-moz-align-items: flex-start;
+	-ms-flex-align: flex-start;
+	align-items: flex-start;
+}
+
+.flex-align-end {
+	-webkit-box-align: flex-end;
+	-webkit-align-items: flex-end;
+	-moz-box-align: flex-end;
+	-moz-align-items: flex-end;
+	-ms-flex-align: flex-end;
+	align-items: flex-end;
+}
+
+.flex-align-baseline {
+	-webkit-box-align: baseline;
+	-webkit-align-items: baseline;
+	-moz-box-align: baseline;
+	-moz-align-items: baseline;
+	-ms-flex-align: baseline;
+	align-items: baseline;
+}
+.flex-pack-center {
+	-webkit-box-pack: center;
+	-webkit-justify-content: center;
+	-moz-box-pack: center;
+	-moz-justify-content: center;
+	-ms-flex-pack: center;
+	justify-content: center;
+}
+.flex-pack-end {
+	-webkit-box-pack: flex-end;
+	-webkit-justify-content: flex-end;
+	-moz-box-pack: flex-end;
+	-moz-justify-content: flex-end;
+	-ms-flex-pack: flex-end;
+	justify-content: flex-end;
+}
+.flex-pack-justify {
+	-webkit-box-pack: justify;
+	-webkit-justify-content: space-between;
+	-moz-box-pack: justify;
+	-moz-justify-content: space-between;
+	-ms-flex-pack: justify;
+	justify-content: space-between;
+}
+.flex-pack-around {
+	-webkit-box-pack: justify;
+	-webkit-justify-content: space-around;
+	-moz-box-pack: justify;
+	-moz-justify-content: space-around;
+	-ms-flex-pack: justify;
+	justify-content: space-around;
+}
+
+.flex_align-center {
+	display: flex;
+	align-items: center;
+}
+.flex-wrap {
+	flex-wrap: wrap;
+}

+ 3 - 1
src/styles/index.scss

@@ -9,6 +9,8 @@
 @import './lance-element-ui.scss';
 // 涉及lance-element-ui的全局组件样式
 @import './lance-element-vue.scss';
+// 加入布局样式
+@import "./flex.scss";
 body {
 	margin: 0;
 	padding: 0;
@@ -128,4 +130,4 @@ div:focus {
 // table下 按钮集合的外壳样式
 .#{$prefix}button-wrap {
 	padding-bottom: 12px;
-}
+}

+ 68 - 0
src/views/setting/user/assign-role.vue

@@ -0,0 +1,68 @@
+<template>
+	<el-dialog title="分配角色" v-model="visible" :width="550" destroy-on-close @closed="$emit('closed')">
+		<el-select v-model="roleIds" style="width: 500px;" multiple filterable
+				   placeholder="请选择分配角色">
+			<el-option
+				v-for="item in options"
+				:key="item.id"
+				:label="item.name"
+				:value="item.id"
+			>
+				<span style="float: left">{{ item.name }}</span>
+				<span style="float: right;color: var(--el-text-color-secondary); font-size: 13px;">{{
+						item.alias
+					}}</span>
+			</el-option>
+		</el-select>
+		<template #footer>
+			<el-button @click="visible=false">取 消</el-button>
+			<el-button type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
+		</template>
+	</el-dialog>
+</template>
+
+<script>
+export default {
+	emits: ['success', 'closed'],
+	data() {
+		return {
+			visible: false,
+			isSaveing: false,
+			options: [],
+			roleIds: [],
+			userIds: []
+		}
+	},
+	mounted() {
+		this.loadRoles();
+	},
+	methods: {
+		async open(userIds) {
+			this.visible = true;
+			this.userIds = userIds;
+			if (userIds.length == 1) {
+				let res = await this.$API.user.roleIds(userIds[0]);
+				this.roleIds = res.data;
+			}
+		},
+		async submit() {
+			this.isSaveing = true;
+			let res = await this.$API.user.assignRoles({
+				userIds: this.userIds,
+				roleIds: this.roleIds
+			});
+			if (res.data) {
+				this.isSaveing = false;
+				this.visible = false;
+				this.$message.success("分配成功")
+			} else {
+				this.$message.error("分配失败")
+			}
+		},
+		async loadRoles() {
+			let res = await this.$API.role.listAll();
+			this.options = res.data;
+		}
+	}
+}
+</script>

+ 143 - 0
src/views/setting/user/index.vue

@@ -0,0 +1,143 @@
+<template>
+	<div class="flex-column-page-wrap pageWrap">
+		<el-card class="box-card">
+			<el-container>
+				<el-aside width="200px">
+					<el-container>
+						<el-header>
+							<el-input v-model="groupFilterText" placeholder="输入关键字进行过滤" clearable />
+						</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-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-container>
+			</el-container>
+		</el-card>
+	</div>
+</template>
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+import { ElTree } from 'element-plus'
+import { Plus, Delete, Search } from '@element-plus/icons-vue'
+
+interface Tree {
+	[key: string]: any
+}
+
+const groupFilterText = ref('')
+const treeRef = ref<InstanceType<typeof ElTree>>()
+
+const defaultProps = {
+	children: 'children',
+	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[] = [
+	{
+		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'
+					}
+				]
+			}
+		]
+	},
+	{
+		id: 2,
+		label: 'Level one 2',
+		children: [
+			{
+				id: 5,
+				label: 'Level two 2-1'
+			},
+			{
+				id: 6,
+				label: 'Level two 2-2'
+			}
+		]
+	},
+	{
+		id: 3,
+		label: 'Level one 3',
+		children: [
+			{
+				id: 7,
+				label: 'Level two 3-1'
+			},
+			{
+				id: 8,
+				label: 'Level two 3-2'
+			}
+		]
+	}
+]
+</script>
+<style scoped lang="scss">
+.box-card {
+	// 单独自己写的
+	height: 100%;
+}
+
+// todo 样式问题 应该要提成公用
+.pageWrap {
+	//padding-top: 12px;
+	padding: 12px 12px 0 12px;
+	overflow: auto;
+	background-color: #f5f6f7;
+}
+
+// 其他样式
+.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;
+	}
+}
+</style>

+ 159 - 0
src/views/setting/user/save.vue

@@ -0,0 +1,159 @@
+<template>
+	<el-dialog :title="titleMap[mode]" v-model="visible" :width="500" destroy-on-close @closed="$emit('closed')">
+		<el-form :model="form" :rules="rules" :disabled="mode=='show'" ref="dialogForm" label-width="100px"
+				 label-position="left">
+			<!--			<el-form-item label="头像" prop="avatar">-->
+			<!--				<sc-upload v-model="form.avatar" title="上传头像"></sc-upload>-->
+			<!--			</el-form-item>-->
+			<el-form-item label="登录账号" prop="username">
+				<el-input v-model="form.username" placeholder="用于登录系统" clearable></el-input>
+			</el-form-item>
+			<template v-if="mode=='add'">
+				<el-form-item label="登录密码" prop="password">
+					<el-input type="password" v-model="form.password" clearable show-password></el-input>
+				</el-form-item>
+				<el-form-item label="确认密码" prop="password2">
+					<el-input type="password" v-model="form.password2" clearable show-password></el-input>
+				</el-form-item>
+			</template>
+			<el-form-item label="昵称" prop="nickName">
+				<el-input v-model="form.nickName" placeholder="用于登录系统" clearable></el-input>
+			</el-form-item>
+			<el-form-item label="姓名" prop="realName">
+				<el-input v-model="form.realName" placeholder="请输入完整的真实姓名" clearable></el-input>
+			</el-form-item>
+			<el-form-item label="性别" prop="sex">
+				<el-radio-group v-model="form.sex">
+					<el-radio label="男">男</el-radio>
+					<el-radio label="女">女</el-radio>
+				</el-radio-group>
+			</el-form-item>
+			<el-form-item label="状态" prop="status">
+				<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
+			</el-form-item>
+			<el-form-item label="所属角色" prop="roleIds">
+				<el-select v-model="form.roleIds" placeholder="请选择分配角色" multiple clearable style="width: 100%;">
+					<el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id">
+						<span style="float: left">{{ item.name }}</span>
+						<span
+							style="float: right;color: var(--el-text-color-secondary); font-size: 13px;">{{ item.alias }}</span>
+					</el-option>
+				</el-select>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<el-button @click="visible=false">取 消</el-button>
+			<el-button v-if="mode!='show'" type="primary" :loading="isSaveing" @click="submit()">保 存</el-button>
+		</template>
+	</el-dialog>
+</template>
+
+<script>
+export default {
+	emits: ['success', 'closed'],
+	data() {
+		return {
+			mode: "add",
+			titleMap: {
+				add: '新增用户',
+				edit: '编辑用户',
+				show: '查看'
+			},
+			visible: false,
+			isSaveing: false,
+			//表单数据
+			form: {
+				id: "",
+				username: "",
+				password: "",
+				nickName: "",
+				avatar: "",
+				realName: "",
+				group: "",
+				sex: "男",
+				status: 1,
+				roleIds: []
+			},
+			//验证规则
+			rules: {
+				avatar: [
+					{required: true, message: '请上传头像'}
+				],
+				username: [
+					{required: true, message: '请输入登录账号'}
+				],
+				password: [
+					{required: true, message: '请输入登录密码'},
+					{
+						validator: (rule, value, callback) => {
+							if (this.form.password2 !== '') {
+								this.$refs.dialogForm.validateField('password2');
+							}
+							callback();
+						}
+					}
+				],
+				password2: [
+					{required: true, message: '请再次输入密码'},
+					{
+						validator: (rule, value, callback) => {
+							if (value !== this.form.password) {
+								callback(new Error('两次输入密码不一致!'));
+							} else {
+								callback();
+							}
+						}
+					}
+				],
+				group: [
+					{required: true, message: '请选择所属角色'}
+				]
+			},
+			//所需数据选项
+			roles: []
+		}
+	},
+	mounted() {
+		this.loadRoles()
+	},
+	methods: {
+		open(mode = 'add') {
+			this.mode = mode;
+			this.visible = true;
+			return this
+		},
+		async loadRoles() {
+			let res = await this.$API.role.listAll();
+			this.roles = res.data;
+		},
+		//表单提交方法
+		submit() {
+			this.$refs.dialogForm.validate(async (valid) => {
+				if (valid) {
+					this.isSaveing = true;
+					var res = await this.$API.user.save(this.form);
+					this.isSaveing = false;
+					if (res.code == 200) {
+						this.$emit('success', this.form, this.mode)
+						this.visible = false;
+						this.$message.success("操作成功")
+					} else {
+						this.$alert(res.message, "提示", {type: 'error'})
+					}
+				} else {
+					return false;
+				}
+			})
+		},
+		//表单注入数据
+		async setData(data) {
+			this.form = data
+			let res = await this.$API.user.roleIds(data.id);
+			this.form.roleIds = res.data;
+		}
+	}
+}
+</script>
+
+<style>
+</style>