2021-04-03 13:37:52 +08:00

589 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="system-user">
<div class="pane">
<!-- 组织架构 -->
<div class="dept" :class="[isExpand ? '_expand' : '_collapse']">
<cl-dept-tree
@row-click="onDeptRowClick"
@user-add="onDeptUserAdd"
@list-change="onDeptListChange"
/>
</div>
<!-- 成员列表 -->
<div class="user">
<div class="header">
<div class="icon" @click="deptExpand">
<i class="el-icon-arrow-left" v-if="isExpand"></i>
<i class="el-icon-arrow-right" v-else></i>
</div>
<span>成员列表</span>
</div>
<div class="container">
<cl-crud :ref="setRefs('crud')" :on-refresh="onRefresh" @load="onLoad">
<el-row type="flex">
<cl-refresh-btn />
<cl-add-btn />
<cl-multi-delete-btn />
<el-button
v-permission="service.system.user.permission.move"
size="mini"
type="success"
:disabled="selects.ids.length == 0"
@click="toMove()"
>转移</el-button
>
<cl-flex1 />
<cl-search-key />
</el-row>
<el-row>
<cl-table
:ref="setRefs('table')"
v-bind="table"
@selection-change="onSelectionChange"
>
<!-- 头像 -->
<template #column-headImg="{ scope }">
<cl-avatar
shape="square"
size="medium"
:src="scope.row.headImg"
:style="{ margin: 'auto' }"
/>
</template>
<!-- 权限 -->
<template #column-roleName="{ scope }">
<el-tag
v-for="(item, index) in scope.row.roleNameList"
:key="index"
disable-transitions
size="small"
effect="dark"
style="margin: 2px"
>{{ item }}</el-tag
>
</template>
<!-- 单个转移 -->
<template #slot-move-btn="{ scope }">
<el-button
v-permission="service.system.user.permission.move"
type="text"
size="mini"
@click="toMove(scope.row)"
>转移</el-button
>
</template>
</cl-table>
</el-row>
<el-row type="flex">
<cl-flex1 />
<cl-pagination />
</el-row>
<cl-upsert
:ref="setRefs('upsert')"
:items="upsert.items"
:on-submit="onUpsertSubmit"
>
<template #slot-tips>
<div>
<i class="el-icon-warning"></i>
<span style="margin-left: 6px">新增用户默认密码为123456</span>
</div>
</template>
</cl-upsert>
</cl-crud>
</div>
</div>
</div>
<!-- 部门移动 -->
<cl-dept-move :ref="setRefs('dept-move')" @success="refresh({ page: 1 })" />
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, reactive, ref, watch } from "vue";
import { useStore } from "vuex";
import { useRefs } from "/@/core";
import { Table, Upsert } from "/$/crud/types";
export default defineComponent({
name: "sys-user",
setup() {
const service = inject<any>("service");
const store = useStore();
const { refs, setRefs } = useRefs();
// 是否展开
const isExpand = ref<boolean>(true);
// 选择项
const selects = reactive<any>({
dept: {},
ids: []
});
// 部门列表
const dept = ref<any[]>([]);
// 表格配置
const table = reactive<Table>({
props: {
"default-sort": {
prop: "createTime",
order: "descending"
}
},
columns: [
{
type: "selection",
width: 60
},
{
prop: "headImg",
label: "头像"
},
{
prop: "name",
label: "姓名",
minWidth: 150
},
{
prop: "username",
label: "用户名",
minWidth: 150
},
{
prop: "nickName",
label: "昵称",
minWidth: 150
},
{
prop: "departmentName",
label: "部门名称",
minWidth: 150
},
{
prop: "roleName",
label: "角色",
headerAlign: "center",
minWidth: 200
},
{
prop: "phone",
label: "手机号码",
minWidth: 150
},
{
prop: "remark",
label: "备注",
minWidth: 150
},
{
prop: "status",
label: "状态",
minWidth: 120,
dict: [
{
label: "启用",
value: 1,
type: "success"
},
{
label: "禁用",
value: 0,
type: "danger"
}
]
},
{
prop: "createTime",
label: "创建时间",
sortable: "custom",
minWidth: 150
},
{
type: "op",
buttons: ["slot-move-btn", "edit", "delete"],
width: 160
}
]
});
// 新增、编辑配置
const upsert = reactive<Upsert>({
items: [
{
prop: "headImg",
label: "头像",
span: 24,
component: {
name: "cl-upload",
props: {
text: "选择头像",
icon: "el-icon-picture"
}
}
},
{
prop: "name",
label: "姓名",
span: 24,
component: {
name: "el-input",
props: {
placeholder: "请填写姓名"
}
},
rules: {
required: true,
message: "姓名不能为空"
}
},
{
prop: "nickName",
label: "昵称",
span: 12,
component: {
name: "el-input",
props: {
placeholder: "请填写昵称"
}
},
rules: {
required: true,
message: "昵称不能为空"
}
},
{
prop: "username",
label: "用户名",
span: 12,
component: {
name: "el-input",
props: {
placeholder: "请填写用户名"
}
},
rules: [
{
required: true,
message: "用户名不能为空"
}
]
},
{
prop: "password",
label: "密码",
span: 12,
hidden: ":isAdd",
component: {
name: "el-input",
props: {
placeholder: "请填写密码",
type: "password"
}
},
rules: [
{
min: 6,
max: 16,
message: "密码长度在 6 到 16 个字符"
}
]
},
{
prop: "roleIdList",
label: "角色",
span: 24,
value: [],
component: {
name: "cl-role-select",
props: {
props: {
"multiple-limit": 3
}
}
},
rules: {
required: true,
message: "角色不能为空"
}
},
{
prop: "phone",
label: "手机号码",
span: 12,
component: {
name: "el-input",
props: {
placeholder: "请填写手机号码"
}
}
},
{
prop: "email",
label: "邮箱",
span: 12,
component: {
name: "el-input",
props: {
placeholder: "请填写邮箱"
}
}
},
{
prop: "remark",
label: "备注",
span: 24,
component: {
name: "el-input",
props: {
placeholder: "请填写备注",
type: "textarea",
rows: 4
}
}
},
{
prop: "status",
label: "状态",
value: 1,
component: {
name: "el-radio-group",
options: [
{
label: "开启",
value: 1
},
{
label: "关闭",
value: 0
}
]
}
},
{
prop: "tips",
hidden: ":isEdit",
component: {
name: "slot-tips"
}
}
]
});
// 浏览器信息
const browser = computed(() => store.getters.browser);
// 监听屏幕大小变化
watch(
() => browser.value.isMini,
(val: boolean) => {
isExpand.value = !val;
},
{
immediate: true
}
);
// crud 加载
function onLoad({ ctx, app }: any) {
ctx.service(service.system.user).done();
app.refresh();
}
// 刷新列表
function refresh(params: any) {
refs.value.crud.refresh(params);
}
// 刷新监听
async function onRefresh(params: any, { next, render }: any) {
const { list } = await next(params);
list.map((e: any) => {
if (e.roleName) {
e.roleNameList = e.roleName.split(",");
}
e.status = Boolean(e.status);
});
render(list);
}
// 提交钩子
function onUpsertSubmit(_: boolean, data: any, { next }: any) {
let departmentId = data.departmentId;
if (!departmentId) {
departmentId = selects.dept.id;
if (!departmentId) {
departmentId = dept.value[0].id;
}
}
next({
...data,
departmentId
});
}
// 多选监听
function onSelectionChange(selection: any[]) {
selects.ids = selection.map((e) => e.id);
}
// 部门选择监听
function onDeptRowClick({ item, ids }: any) {
selects.dept = item;
refresh({
page: 1,
departmentIds: ids
});
// 收起
if (browser.value.isMini) {
isExpand.value = false;
}
}
// 部门下新增成员
function onDeptUserAdd(item: any) {
refs.value.crud.rowAppend({
departmentId: item.id
});
}
// 部门列表监听
function onDeptListChange(list: any[]) {
dept.value = list;
}
// 是否显示部门
function deptExpand() {
isExpand.value = !isExpand.value;
}
// 移动成员
async function toMove(e?: any) {
let ids = [];
if (!e) {
ids = selects.ids;
} else {
ids = [e.id];
}
refs.value["dept-move"].toMove(ids);
}
return {
service,
refs,
isExpand,
selects,
dept,
table,
upsert,
browser,
setRefs,
onLoad,
refresh,
onRefresh,
onUpsertSubmit,
onSelectionChange,
onDeptRowClick,
onDeptUserAdd,
onDeptListChange,
deptExpand,
toMove
};
}
});
</script>
<style lang="scss" scoped>
.system-user {
.pane {
display: flex;
height: 100%;
width: 100%;
position: relative;
}
.dept {
height: 100%;
width: 300px;
max-width: calc(100% - 50px);
background-color: #fff;
transition: width 0.3s;
margin-right: 10px;
flex-shrink: 0;
&._collapse {
margin-right: 0;
width: 0;
}
}
.user {
width: calc(100% - 310px);
flex: 1;
.header {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
position: relative;
background-color: #fff;
span {
font-size: 14px;
white-space: nowrap;
overflow: hidden;
}
.icon {
position: absolute;
left: 0;
top: 0;
font-size: 18px;
cursor: pointer;
background-color: #fff;
height: 40px;
width: 80px;
line-height: 40px;
padding-left: 10px;
}
}
}
.dept,
.user {
overflow: hidden;
.container {
height: calc(100% - 40px);
}
}
@media only screen and (max-width: 768px) {
.dept {
width: calc(100% - 100px);
}
}
}
</style>