Browse Source

update

dev
zhengzhou 4 years ago
parent
commit
f301b32d02
8 changed files with 2876 additions and 45 deletions
  1. +132
    -0
      src/components/people-item/index.vue
  2. +305
    -0
      src/views/main_web/project/components/members-picker.vue
  3. +0
    -33
      src/views/main_web/project/components/members.vue
  4. +387
    -0
      src/views/main_web/project/components/plain-folder-list.vue
  5. +130
    -0
      src/views/main_web/project/components/temp-selected-user-list.vue
  6. +1820
    -12
      src/views/main_web/project/index.vue
  7. +0
    -0
      src/views/main_web/project/newProject.vue
  8. +102
    -0
      src/views/main_web/project/service.js

+ 132
- 0
src/components/people-item/index.vue View File

@@ -0,0 +1,132 @@
<template>
<div class="people-item" :title="user.cnName">
<div class="people-wrap" :class="{'rt-0': isShowManagerMark}">
<div class="people">
<img v-if="Object.keys(user).length > 0"
class="proj-avatarface"
:class="{'manager-mark': isShowManagerMark }"
:src="user.headImgUrl | resolveAvator" alt
/>
<div v-else-if="!isShowEmptyAvatar" class="add-btn">
<i class="el-icon-plus proj-avatarface"></i>
</div>
<img v-else src="/static/img/暂无2.svg" alt="" class="proj-avatarface empty">
</div>
<div class="people-name" :class="{'rt-2': isEnterEdit && isShowManagerMark, 'rt-4': isEnterEdit && !isShowManagerMark}">{{user.cnName}}</div>
</div>
</div>
</template>

<script>
export default {
data() {
return {
}
},
computed: {

},
props: {
user: {
type: Object,
default() {
return {}
}
},
isShowManagerMark: {//是否显示 圆圈边框
type: Boolean,
default: false,
},
isEnterEdit: {//控制是展示还是编辑
type: Boolean,
default: false,
},
isShowEmptyAvatar: {//是否显示暂无
type: Boolean,
defualt: false
}
},
methods: {
},
}
</script>

<style scoped>
.rt-2 {
top: 2px !important;
}
.rt-0 {
top: 0 !important;
}
.rt-4 {
top: 4px !important;
}
.empty {
background-color: #eaeaea;
}
.people-item {
display: flex;
position: relative;
z-index: 9;
box-sizing: border-box;
text-align: center;
width: 64px;
padding: 0 0 8px 0;
}
.people-item .people-wrap {
position: relative;
top: 4px;
border-radius: 50%;
margin: 0 auto;
width: 100%;
}
.people-item .people {
border-radius: 50%;
}
.active-manager-mark .people-name {
top:-2px;
}

/* 加号选择框 */
.people-item .el-icon-plus {
display: block;
text-align: center;
height: 48px;
line-height: 48px;
font-size: 24px;
color: #999B9D;
}
.proj-avatarface {
vertical-align: bottom;
width: 48px;
height: 48px;
border-radius: 50%;
overflow: hidden;
background-color: rgba(246, 246, 246, 1);
}

.people-name {
position: relative;
width: 100%;

overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;

height: 16px;
line-height: 16px;
color: rgba(17, 19, 21, 100);
font-size: 12px;
text-align: center;
font-family: PingFangSC-Regular;
margin: 8px auto 0;
}


.add-btn {
cursor: pointer;
margin: 0 4px;
}
</style>

+ 305
- 0
src/views/main_web/project/components/members-picker.vue View File

@@ -0,0 +1,305 @@
<template>
<div class="member-list">
<div class="top-title">选择成员</div>
<div class="search-bar">
<i class="search-icon"></i>
<input placeholder="搜索功能正在内测中" class="search-input" disabled/>
</div>
<!-- 下面按企业 / 部门 显示人员 -->
<div class="hori-line"></div>
<div class="content-wrap">
<!-- 折叠面板 -->
<div class="people-wrap">
<el-collapse v-model="activeCompanyName">
<!-- 添加协作群与工作 -->
<el-collapse-item
v-for="topNode in treeList"
:key="topNode.id"
:name="topNode.id"
>
<template slot="title">
<span class="company-title">{{ topNode.label }}</span>
</template>
<el-collapse v-model="activeDeptNameList" v-if="topNode.hasChildren">
<!-- 二级折叠面板 -->
<el-collapse-item v-for="deptNode in topNode.children" v-show="deptNode.hasChildren" :name="deptNode.id" :key="deptNode.id">
<template slot="title" class="dept-entry">
<span class="dept-name">{{ deptNode.label }}</span>
<div class="check-all-wrap"
@click.stop="toggleAllDept(deptNode)"
>
<div class="checkbox" :class="{checked: deptNodeSelectedMap[deptNode.id]}"/><span>全选</span>
</div>
</template>
<div class="user-info-item" v-for="memberNode in deptNode.children" :label="memberNode.label" :key="memberNode.id"
@click.stop="toggleMember(memberNode)"
>
<img :src="memberNode.data.headImgUrl | resolveAvator" alt="" class="avatar-img" />
<span class="user-info-name">{{memberNode.label}}</span>
<div class="checkbox" :class="{checked: isSelected(memberNode.id)}" />
</div>
</el-collapse-item>
</el-collapse>
<div class="empty-people-tips" v-else>暂未添加人员</div>
</el-collapse-item>
<!-- v-if="linkCompanyList.length" -->
</el-collapse>
</div>
</div>
</div>
</template>

<script>
// 选择人员 人员列表组件
export default {
data() {
// console.log(this.treeList);
const firstTopNode = this.treeList[0];
const activeCompanyName = firstTopNode ? [firstTopNode.id]:[];
const activeDeptNameList = firstTopNode ? firstTopNode.mapChildren(deptNode => deptNode.id): [];
this.clacAllSelected();
return {
ischeckAll: false,
activeCompanyName,
activeDeptNameList,
}
},
methods: {
isSelected(id) {
return !!this.selectedKeys.find(key => key === id);
},
isChildrenAllSelected(deptNode) {
const selectedKeys = this.selectedKeys;
return deptNode.everyChildren(node => selectedKeys.find(key => key === node.id));
},
toggleAllDept(deptNode) {
const targetChecked = !this.deptNodeSelectedMap[deptNode.id];
this.$emit('select', deptNode.children, targetChecked);
},
toggleMember(memberNode) {
const targetChecked = !this.isSelected(memberNode.id);
this.$emit('select', [memberNode], targetChecked);
},
clacAllSelected() {
const hash = {};
this.treeList.forEach(topNode => {
topNode.mapChildren(deptNode => {
const flag = this.isChildrenAllSelected(deptNode);
if(flag) { hash[deptNode.id] = true; }
})
});
this.deptNodeSelectedMap = hash;
},
},
props: {
treeList: {
type: Array,
default() {
return []
}
},
selectedKeys: {
type: Array,
default() {
return []
}
},
},
watch: {
selectedKeys: {
immediate: true,
handler(val) {
this.clacAllSelected();
}
},
treeList: {
immediate: true,
handler(val) {
this.clacAllSelected();
}
},
}
}
</script>

<style scoped lang="scss">
/* 水平的线 */
.hori-line {
border: 0.5px solid #eaeaea;
margin: 13px 0 0 0;
}
.member-list {
width: 100%;
min-width: 375px;
max-width: 375px;
background-color: #fcfcfc;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
box-shadow: 0px 4px 10px 1px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
.top-title {
height: 50px;
line-height: 50px;
padding: 0 0 0 24px;
color: rgba(12, 13, 16, 100);
font-size: 20px;
text-align: left;
font-family: PingFangSC-Medium;
}
.search-bar {
position: relative;
margin: 6px 15px 7px;
padding: 0 5px;
.search-icon {
position: absolute;
left: 10px;
width: 22px;
height:22px;
background: url('/static/img/搜索.svg') center/100%;
margin: 4px 0;
}
.search-input {
box-sizing: border-box;
outline: none;
width:100%;
height: 30px;
padding: 0 0 0 30px;
border-radius: 8px;
background-color: rgba(233, 233, 235, 1);
border: 0.5px solid rgba(174, 174, 174, 1);
cursor: not-allowed;
}
}
.content-wrap {
// 右侧滚动条有8px的宽度
padding: 0 0 0 8px;
overflow-y: scroll;
height: calc(100vh - 177px);
/* 我的企业文字样式 */
.company-title {
margin: 12px 0 10px 16px;
height: 22px;
line-height: 22px;
color: rgba(50, 50, 60, 100);
font-size: 18px;
text-align: left;
font-family: PingFangSC-Regular;
}
.dept-name {
margin: 0 0 0 16px;
flex: 1;
}
}
.people-wrap {
padding: 0 0 40px 0;
}
}
.search-input::-webkit-input-placeholder {
color: rgba(0, 0, 0, 0.56);
font-size: 14px;
font-family:'Courier New', Courier, monospace;
color: rgba(0, 0, 0, 0.56);
height: 20px !important;
line-height: 20px !important;
}
.user-info-item:hover {
background-color: rgba(50, 50, 60, 0.1)
}
.user-info-item {
display: flex;
padding:0 16px;
height: 44px;
line-height: 44px;
align-items: center;
cursor: pointer;
border-radius: 8px;
.avatar-img {
width: 32px;
height:32px;
border-radius: 50%;
margin: 0 13px 0 0;
}
.user-info-name {
flex: 1;
font-size: 14px;
font-family: PingFangSC-Regular;
}
}


</style>

<style lang="scss">
.member-list .el-collapse-item__wrap {
border-bottom: none !important;
}
.dept-select-all-btn .el-checkbox__label {
padding: 0 0 0 6px !important;
margin: 0 8px 0 0;
}
.member-list .el-collapse {
border: none !important;
}
.member-list .el-collapse-item__arrow {
position: relative;
left: 2px;
}
.el-checkbox.dept-select-all-btn {
/* //margin: 0 0 0 auto; */
position: absolute;
right: 15px;

}
.member-list .el-collapse-item__header {
height: 44px !important;
line-height: 44px !important;
border: none !important;
position: relative;
}
.user-info-item .el-checkbox__inner {
width: 20px;
height: 20px;
color: #cbcbce;
}

.member-list .el-collapse-item__header {
margin: 0 4px 0 0 !important;
}

.check-all-wrap {
display: flex;
align-items: center;
margin: 0 10px 0 0;
.checkbox {
width: 10px;
height: 10px;
margin: 0 6px 0 0;
}
}
.checkbox{
float: right;
width: 20px;
height: 20px;
border: 1px solid rgba(203, 203, 206, 1);
border-radius: 50%;
&.checked{
background-color: #7850ff
}
}

.member-list .app-header-content {
background-color: #f8f8f8;
}

.empty-people-tips {
font-size: 16px;
text-align: center;
margin: 20px 0;
}
</style>

+ 0
- 33
src/views/main_web/project/components/members.vue View File

@@ -1,33 +0,0 @@
<!--
选择成员弹窗
-->
<template>
<div class="dialog members">
<h3>选择成员</h3>
<el-input placeholder="搜索职员">
<el-button slot="prepend" icon="el-icon-search" />
</el-input>
</div>
</template>


<style scoped>
.dialog {
position: absolute;
background-color: rgba(252, 252, 252, 1);
box-shadow: 0px 3px 11px 1px rgba(0, 0, 0, 0.06);
border-radius: 8px 8px 0 0;
}
.members {
left: 868px;
top: 48px;
width: 375px;
height: 976px;
}
.members h3 {
margin-top: 16px;
margin-bottom: 28px;
line-height: 1em;
padding: 0 24px;
}
</style>

+ 387
- 0
src/views/main_web/project/components/plain-folder-list.vue View File

@@ -0,0 +1,387 @@
<template>
<div>
<div v-for="folder in folderList" :key="folder.id">
<div>
<div
@click.stop="activeFolderClick(folder)"
class="folder-wrap folder-hover"
ref="activeFolder"
:class="{
'active-folder': folder.id == activeFolderId && isEnterEdit,
'folder-click': !isEnterEdit,
}"
>
<div
class="folder-name"
:class="{ 'opacity-5': !isAllowedAssignedStaffFolder }"
>
<span>{{ folder.folderName }}</span>
<span v-if="!isProjManager" class="user-perm-text"></span>
</div>

<div
v-if="!isEnterEdit || activeFolderId !== folder.id"
class="clear-fix border-box"
:class="{
'mb-36': folder.id === activeFolderId && isEnterEdit,
'ml-4': !isEnterEdit,
'adjust-position': isEnterEdit,
}"
>
<div class="fl">
<div v-if="folder.manageUser && folder.manageUser.length == 0">
<div
class="use-relative"
v-if="isAllowedAssignedFolder(folder)"
>
<people-item
v-if="!isEnterEdit"
:class="{ 'mr-8': !isEnterEdit }"
@click.native.stop="
aloneAddUserForFolder(folder),
enterEditFolderClick(folder)
"
/>
<people-item v-else />
</div>
<div class="notclick" v-else>
<people-item
:isShowEmptyAvatar="true"
:class="{ 'mr-8': !isEnterEdit }"
/>
</div>
</div>

<div class="use-flex" v-else>
<div v-for="(user, index) in folder.manageUser" :key="user.id">
<div class="use-relative">
<people-item
:user="user"
:isShowManagerMark="isEnterEdit"
:class="{
notclick: !isAllowedAssignedStaffFolder(folder),
'mr-8': !isEnterEdit,
}"
/>
</div>
</div>
</div>
</div>
<!-- 竖线 -->
<div class="vertical-line" v-show="!isEnterEdit" />

<div
class="use-flex"
:class="{ 'mb-6': isEnterEdit && activeFolderId !== folder.id }"
>
<div
v-for="user in listUserComputed(folder.listUser)"
:key="user.id"
:class="{
notclick: !(
isAllowedAssignedStaffFolder(folder) || isStaff(folder)
),
}"
>
<people-item :user="user" :isEnterEdit="isEnterEdit" />
</div>

<div
class="add-btn"
v-if="
folder.manageUser &&
folder.manageUser.length > 0 &&
isAllowedAssignedStaffFolder(folder)
"
>
<people-item
v-if="!isEnterEdit"
@click.native.stop="
aloneAddUserOnlyStaff(folder), enterEditFolderClick(folder)
"
/>
</div>
<div class="stand-place" v-if="isNest(folder.nodeId)" />
<!-- 嵌套模板入口 -->
<div
class="add_nest"
v-if="isNest(folder.nodeId) && !isEnterEdit"
>
<div class="add_nest_box" @click.stop="enterNestTemp(folder)">
<i class="el-icon-plus"></i>
<p>嵌套模板</p>
</div>
</div>
</div>
</div>
<div
v-if="isEnterEdit && activeFolderId == folder.id"
class="use-relative ml-4 mb-36 use-flex border-box"
:class="{ 'adjust-margin': folder.id === lastFolderId }"
>
<temp-selected-user-list
:selectedUserList="selectedUser"
@changeManagerClick="changeManagerClick"
@setFirstPeopleToManager="setFirstPeopleToManager"
/>
</div>
</div>
</div>

<template v-if="folder.listChildren && folder.listChildren.length > 0">
<plain-folder-list
:folderList="folder.listChildren"
:id="id"
:lastFolderId="lastFolderId"
:allManager="allManager"
:allManagerChild="allManagerChild"
:aloneAddUserForFolder="aloneAddUserForFolder"
:aloneAddUserOnlyStaff="aloneAddUserOnlyStaff"
:allChildrenLists="allChildrenLists"
:isEnterEdit="isEnterEdit"
:activeFolderId="activeFolderId"
:selectedUser="selectedUser"
:isProjManager="isProjManager"
:nestNodeList="nestNodeList"
/>
</template>
</div>
</div>
</template>

<script>
import PeopleItem from "@/components/people-item";
import TempSelectedUserList from "./temp-selected-user-list";

export default {
name: "PlainFolderList",
components: {
PeopleItem,
TempSelectedUserList,
},
props: {
id: String, //用户的id,
lastFolderId: String,
allManager: Object,
allManagerChild: Array,
folderList: Array,
aloneAddUserForFolder: Function,
aloneAddUserOnlyStaff: Function,
allChildrenLists: Array,
isEnterEdit: {
type: Boolean,
default: false,
},
nestNodeList: Array,
isProjManager: {
type: Boolean,
default: false,
},
activeFolderId: {
type: String,
defualt: "",
},
selectedUser: {
type: Array,
default() {
return [];
},
},
},
data() {
return {
showAll: false,
exsitNest: false,
};
},
methods: {
//判断是否存在可嵌套节点
isNest(id) {
let len = this.nestNodeList.length;
for (let i = 0; i < len; i++) {
if (this.nestNodeList[i].ParentNodeId == id) {
return true;
}
}
return false;
},
enterNestTemp(folder) {
this.$bus.$emit("enterNestTemp", folder);
//this.$bus.$emit('enterNestTemp');
},
// 设置第一个选择的人员为节点负责人
setFirstPeopleToManager(user) {
this.$bus.$emit("setFirstPeopleToManager", user);
},
// 切换工作负责人
changeManagerClick(user) {
this.$bus.$emit("changeManagerClick", user);
},
// 点击进入文件夹编辑
activeFolderClick(folder) {
if (this.isEnterEdit && this.isAllowedAssignedStaffFolder(folder)) {
this.$bus.$emit("activeFolderClick", folder);
}
},
// 点击了添加职员的按钮 进入编辑状态
// 因为这里用的是递归组件 所以就不用父子组件通信 用事件总线
enterEditFolderClick(folder) {
if (!this.isEnterEdit) {
this.$bus.$emit("enterEditFolderClick", folder);
}
},
showAllClick() {
this.showAll = true;
},
toggleFolderExpand(folder) {
folder.expanded = !folder.expanded;
this.$forceUpdate();
},
isAllowedAssignedFolder(folder) {
return (
this.allManager.id == this.id || //总负责人
(this.allManagerChild.length > 0 &&
this.allManagerChild.find((item) => item.id == this.id)) || //是不是负责人
(this.allChildrenLists.length > 0 &&
this.allChildrenLists.includes(folder))
); //它的所有子节点
//||folder.manageUser && folder.manageUser[0].id == this.id
},
isAllowedAssignedStaffFolder(folder) {
return (
(folder.manageUser &&
folder.manageUser.length > 0 &&
folder.manageUser[0].id == this.id) ||
this.isAllowedAssignedFolder(folder)
);
},
isStaff(folder) {
return folder.listUser.some((item) => {
return item.id === this.id;
});
},
},
computed: {
listUserComputed: function () {
var thisApp = this;
return function (listUsers) {
return listUsers.filter(function (user) {
return user.folderPerm != 2;
});
};
},
},
};
</script>


<style lang="scss" scoped>
.stand-place {
width: 64px;
height: 72px;
}
.add_nest {
position: absolute;
right: 10px;
bottom: -6px;
z-index: 9;
width: 64px;
height: 80px;
box-sizing: border-box;
.add_nest_box {
cursor: pointer;
width: 64px;
height: 64px;
box-sizing: border-box;
border: 1px solid #7850ff;
border-radius: 8px;
.el-icon-plus {
font-size: 24px;
color: #7850ff;
}
p {
color: #7850ff;
font-size: 14px;
text-align: center;
margin-top: 10px;
}
}
}

.opacity-5 {
opacity: 0.5;
}

.user-perm-text {
margin: 0 0 0 8px;
}
.notclick {
cursor: not-allowed !important;
opacity: 0.5;
}
.notclick .avtarface {
cursor: not-allowed;
}
.allopacity {
opacity: 1;
}
.noManagerText {
font-size: 0;
}

.active-folder .people-item {
cursor: pointer;
}

.folder-name {
color: #32323c;
height: 20px;
line-height: 20px;

margin: 0 0 6px 12px;
color: rgba(50, 50, 60, 100);
font-size: 14px;
text-align: left;
font-family: PingFangSC-Regular;
}

.adjust-position {
position: relative;
left: 4px;
}
.folder-wrap {
position: relative;
padding: 6px 0 0 0;
margin: 12px 0 0 0;
border-radius: 8px;
background-color: #fff;
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.02);
}
/* 编辑按钮 */
.edit-btn {
position: absolute;
top: 4px;
right: 4px;

width: 64px;
height: 24px;
line-height: 24px;
font-size: 14px;
text-align: center;
font-family: PingFangSC-Regular;

color: #979797;
border-radius: 4px;
background-color: rgba(255, 255, 255, 1);
border: 0.5px solid rgba(151, 151, 151, 1);
cursor: pointer;
}
// 最后一个元素不显示32px的间距 因为这样会和原先设置的底部28px的留白重复
.adjust-margin {
margin-bottom: 0;
}

.mb-6 {
margin: 0 0 6px 0;
}
</style>

+ 130
- 0
src/views/main_web/project/components/temp-selected-user-list.vue View File

@@ -0,0 +1,130 @@

<!-- 用来编辑人员时 存储编辑的状态 -->
<template>
<div class="temp-selected-user-list" :style="{height: showAdjustHeight}">
<div class="use-flex" ref="selectedUserList">
<div v-for="(user, index) in selectedUserList" :key="user.id" class="use-relative">
<people-item @click.native.stop="changeManagerClick(user)"
:class="{'active-manager-mark': user.folderPerm == 2}"
:user="user"/>
<div v-if="user.folderPerm == 2" class="active-manager-mark-bottom-name">工作负责人</div>
</div>
<div v-if="selectedUserList.length == 0" class="use-relative" @click.stop="openPersonnelListClick">
<people-item class="active-manager-mark"/>
<div class="active-manager-mark-bottom-name">工作负责人</div>
</div>
</div>
</div>
</template>

<script>
import PeopleItem from '@/components/people-item';
export default {
data() {
return {
selectedUserListEl: null,//渲染选中人员的dom元素
selectedUserListElWidth: 0,
selectedUserListElHeight: 0,
renderHeight: 0,
isAdjustHeight: false,

}
},
components: {
PeopleItem
},
props: {
selectedUserList: {//选择的人员列表 利用folderPerm是否为2来区分是否是普通人员
type: Array,
default() {
return []
}
},
},
mounted() {
document.body.addEventListener('resize',()=> {
this.adjustHeightOperate();
})
this.selectedUserListEl = this.$refs.selectedUserList;
if(this.selectedUserListEl) {
this.selectedUserListElWidth = this.selectedUserListEl.clientWidth;
this.selectedUserListElHeight = this.selectedUserListEl.clientHeight;
this.adjustHeightOperate();
}
},
computed: {
selectedUserListLength() {
return this.selectedUserList.length;
},
showAdjustHeight() {
return this.isAdjustHeight ? `${this.selectedUserListElHeight - 31}px` : `auto`;
}
},
watch: {
selectedUserList: {
immediate: true,
handler(val) {
if(val.length > 0) {
let isExistsManager = val.find(user => user.folderPerm && user.folderPerm == 2);
if(!isExistsManager) {
val[0].folderPerm = 2;
this.$emit('setFirstPeopleToManager', val[0]);
}
}
}
},
selectedUserListLength(val) {
setTimeout(()=> {
this.selectedUserListElWidth = this.selectedUserListEl.clientWidth;
this.selectedUserListElHeight = this.selectedUserListEl.clientHeight;
this.adjustHeightOperate();
}, 50)
}
},
methods: {
// 工作负责人为空的时候 点击 -》 展开右侧的人员列表
openPersonnelListClick() {
this.$bus.$emit('openPersonnelListClick')
},
// 如果点击到最后一行的话 要动态改变盒子的高度
changeManagerClick(user) {
this.$emit('changeManagerClick', user);

this.adjustHeightOperate();
},
adjustHeightOperate() {
this.isAdjustHeight = false;
const num = parseInt(this.selectedUserListElWidth / 64);//一行排列了几个人
const totalNum = this.selectedUserListLength;//总人数
const row = (totalNum / num);//行数
let currentUserIndex = 0;
if(row <= 1) {
// 只有一行
this.isAdjustHeight = true;
this.renderHeight = this.selectedUserListElHeight - 31;
return;
}
// 获取当前选中的人员的索引
for(let i = 0; i < totalNum; i++) {
if(this.selectedUserList[i].folderPerm == 2) {
currentUserIndex = i;
break;
}
}
// 当前选中的负责人位于最后一行以及不满一行的时候 那么需要调整高度
if(parseInt(row)*num < currentUserIndex+1 && currentUserIndex+1 <= (parseInt(row)+1)*num) {
this.isAdjustHeight = true;
this.renderHeight = this.selectedUserListElHeight - 31;
}
}
}
}
</script>

<style scoped>

</style>

+ 1820
- 12
src/views/main_web/project/index.vue
File diff suppressed because it is too large
View File


+ 0
- 0
src/views/main_web/project/newProject.vue View File


+ 102
- 0
src/views/main_web/project/service.js View File

@@ -0,0 +1,102 @@
import { fetchApi, wrapErrorHint } from '@/utils/request';
import { firstCharToLowerCase } from '@/utils/tool';
import { fetchDeptList, getUserListByNode } from '@/services/user.js';

export async function fetchMemberTree(companyId) {
const [{ list: mainMemberList }, deptList, otherCompanyMemberListRes] = await Promise.all([
getUserListByNode({ id: companyId, nodeType: 'company' }, 1, 10000),
fetchDeptList(companyId),
fetchApi('user/queryAllLinkCompanyRoleUserByCompanyId', { companyId }),
]);

const mainCompanyNode = new Node(companyId, '我的企业', null, 'company', {});
const emptyDeptNode = new Node('emptyDeptNode', sessionStorage.CompanyName || '', companyId, 'dept', {});
const nodeMap = { [emptyDeptNode.id]: emptyDeptNode };
const memberMap = {};

deptList.forEach(dept => {
const deptNode = new Node(dept.id, dept.label, companyId, 'dept', dept.data);
nodeMap[deptNode.id] = deptNode;
mainCompanyNode.append(deptNode);
});
mainMemberList.forEach((member) => {
const parentNode = nodeMap[member.deptId] || emptyDeptNode;
const memberNode = new Node(member.id, member.cnName, parentNode.id, 'user', member);
memberMap[memberNode.id] = memberNode;
parentNode.append(memberNode);
});

if(emptyDeptNode.hasChildren) { mainCompanyNode.append(emptyDeptNode); }

const linkCompanyParentNode = new Node('linkCompanyParentNode', '互链企业', null, 'empty', {});
const linkCompanyList = otherCompanyMemberListRes.Data || [];
linkCompanyList.forEach((data) => {
const { CompanyId, CompanyName, Users } = data;
const linkCompanyNode = new Node(CompanyId, CompanyName, linkCompanyParentNode.id, 'company', { companyId: CompanyId, companyName: CompanyName });
Users.forEach(upperMember => {
const member = firstCharToLowerCase(upperMember);
const memberNode = new Node(member.id, member.cnName, linkCompanyNode.id, 'user', member);
memberMap[memberNode.id] = memberNode;
linkCompanyNode.append(memberNode);
});
linkCompanyNode.sortChildren();
nodeMap[linkCompanyNode.id] = linkCompanyNode;
linkCompanyParentNode.append(linkCompanyNode);

});

const outputList = [mainCompanyNode];
if(linkCompanyParentNode.hasChildren) { outputList.push(linkCompanyParentNode); }
// 排序
mainCompanyNode.children?.forEach(deptNode=>{
deptNode.sortChildren();
});

return {
treeList: outputList,
memberMap,
}
}


// function plainTreeNodes(nodesList, userList, outputList = []) {
// (nodesList || []).forEach(node => {
// // node.id
// const data = node.data;
// data.listUser = userList.filter(user => user.deptId === node.id);
// outputList.push(data);
// if(node.children && node.children.length) {
// plainTreeNodes(node.children, userList, outputList);
// }
// });
// return outputList;
// }

const sortFunc = (nodeA, nodeB) => nodeA.label.localeCompare(nodeB.label);

class Node {
// type: 'company' | 'dept' | 'user' | 'empty'
constructor(id, label, parentId, type, data) {
Object.assign(this, { id, label, parentId, type, data });
}
append(childNode) {
if(!this.children) { this.children = []; }
this.children.push(childNode);
}
sortChildren(f = sortFunc) {
if(!this.hasChildren) { return this; }
this.children.sort(f);
return this;
}
mapChildren(f) {
if(!this.hasChildren) { return []; }
return this.children.map(f);
}
everyChildren(f) {
if(!this.hasChildren) { return false; }
return this.children.every(f);
}
get hasChildren() {
return this.children && !!this.children.length;
}
}

Loading…
Cancel
Save