page-role.component.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. import { Component, OnInit, ViewChild } from '@angular/core';
  2. import { ActivatedRoute, Route, Router, RouterOutlet } from '@angular/router';
  3. import { CompTableListComponent } from '../../../app/comp-table/comp-table-list/comp-table-list.component';
  4. import _Role from '../../../schemas/_Role';
  5. // import { TranslateService } from '@ngx-translate/core';
  6. import * as Parse from 'parse';
  7. import { CommonModule } from '@angular/common';
  8. import { Department } from '../../../schemas/Department';
  9. import { NzSpaceModule } from 'ng-zorro-antd/space';
  10. import { NzPageHeaderModule } from 'ng-zorro-antd/page-header';
  11. import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb';
  12. import { CommonCompModule } from '../../../services/common.modules';
  13. import { NzModalModule, NzModalService } from 'ng-zorro-antd/modal';
  14. import {
  15. NzFormatEmitEvent,
  16. NzTreeModule,
  17. NzTreeNode,
  18. } from 'ng-zorro-antd/tree';
  19. import {
  20. NzContextMenuService,
  21. NzDropdownMenuComponent,
  22. } from 'ng-zorro-antd/dropdown';
  23. import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
  24. import { NzEmptyModule } from 'ng-zorro-antd/empty';
  25. import { NzRadioModule } from 'ng-zorro-antd/radio';
  26. import { NzMessageService } from 'ng-zorro-antd/message';
  27. import { NzSelectModule } from 'ng-zorro-antd/select';
  28. import { textbookServer } from '../../../services/textbook';
  29. import { NzSpinModule } from 'ng-zorro-antd/spin';
  30. interface nodes {
  31. title: string;
  32. key: string;
  33. isLeaf?: boolean;
  34. branch?: string;
  35. children?: Array<any>;
  36. }
  37. interface depart {
  38. name: string;
  39. id?: string;
  40. code?: string;
  41. desc?: string;
  42. parent?: object | any;
  43. branch: string;
  44. }
  45. @Component({
  46. selector: 'app-page-role',
  47. templateUrl: './page-role.component.html',
  48. styleUrls: ['./page-role.component.scss'],
  49. imports: [
  50. CommonModule,
  51. CommonCompModule,
  52. RouterOutlet,
  53. CompTableListComponent,
  54. NzSpaceModule,
  55. NzPageHeaderModule,
  56. NzBreadCrumbModule,
  57. NzTreeModule,
  58. NzCheckboxModule,
  59. NzEmptyModule,
  60. NzModalModule,
  61. NzRadioModule,
  62. NzSelectModule,
  63. NzSpinModule
  64. ],
  65. standalone: true,
  66. })
  67. export class PageRoleComponent implements OnInit {
  68. @ViewChild(CompTableListComponent) list: CompTableListComponent | undefined;
  69. // _Role = _Role
  70. Department = Department;
  71. user: Parse.User | undefined;
  72. className: string | undefined;
  73. queryParams: any | undefined;
  74. fieldsArray: Array<any> | undefined;
  75. searchValue: string = ''; //搜索内容
  76. searchValueNode: string = '';
  77. nodes: Array<nodes | any> = [];
  78. currentDepart: nodes | any = null;
  79. profiles: Array<Parse.Object> = [];
  80. checkedShowFilter: boolean = false;
  81. checkedAll: boolean = false; //全选
  82. indeterminate = false;
  83. loading = false;
  84. pageSize: number = 10;
  85. pageIndex: number = 1;
  86. profileLength:number = 0
  87. isVisible: boolean = false;
  88. activatedNode: NzTreeNode | any; //当前选择节点
  89. editType: string = 'add'; //弹窗类型
  90. activeDepart?: Parse.Object; //当前编辑部门
  91. editObject: depart = {
  92. name: '',
  93. code: '',
  94. desc: '',
  95. parent: {},
  96. branch: '',
  97. };
  98. parentMap: Array<any> = [];
  99. parentList: Array<any> = [];
  100. radio: string = '';
  101. /* 添加账号 */
  102. accountIsVisible: boolean = false;
  103. account: any = {
  104. name: '',
  105. phone: '',
  106. email: '',
  107. password: '',
  108. identity: '',
  109. department: {},
  110. companyType: '',
  111. };
  112. userType: Array<string> = ['工作联系人', '评审专家', '高校联系人', '个人'];
  113. parents: Array<any> = []; //所有一级列表
  114. nzExpandedKeys:any = [] //默认展开节点数
  115. constructor(
  116. private route: Router,
  117. private activeRoute: ActivatedRoute,
  118. private nzContextMenuService: NzContextMenuService,
  119. public tbookSer: textbookServer,
  120. private modal: NzModalService,
  121. private message: NzMessageService
  122. ) {
  123. this.user = Parse.User.current();
  124. this.className = this.Department.className;
  125. this.fieldsArray = this.Department.fieldsArray;
  126. this.queryParams = {
  127. where: {
  128. // user:this.user?.toPointer(),
  129. isDeleted: { $ne: true },
  130. },
  131. };
  132. }
  133. ngOnInit(): void {
  134. this.activeRoute.paramMap.subscribe(async (params) => {
  135. let nodes:Array<any> = await this.getDepart();
  136. this.parents = [...nodes]
  137. Promise.all(nodes.map(async (item,index)=>{
  138. nodes[index].children = await this.getDepart(item.key);
  139. // nodes[index]['isExpanded'] = true
  140. this.nzExpandedKeys.push(nodes[index]?.key)
  141. })).then(()=>{
  142. this.nodes = nodes
  143. })
  144. });
  145. }
  146. async getDepart(
  147. parent?: string,
  148. searchValue?: string,
  149. filter?: boolean
  150. ): Promise<Array<nodes>> {
  151. let nodes: any = [];
  152. let query = new Parse.Query('Department');
  153. if (!filter) {
  154. query.equalTo('parent', parent ? parent : undefined);
  155. }
  156. searchValue && query.contains('name', searchValue);
  157. query.notEqualTo('isDeleted', true);
  158. query.select('code', 'name', 'branch', 'parent', 'type','hasChildren');
  159. query.descending('createdAt');
  160. query.limit(2000);
  161. if (this.activeDepart) query.notEqualTo('objectId', this.activeDepart?.id);
  162. let res = await query.find();
  163. res.forEach((item) => {
  164. nodes.push({
  165. title: item.get('name'),
  166. key: item.id,
  167. children: [],
  168. branch: item.get('branch'),
  169. parent: item.get('parent')?.id, //上级
  170. isLeaf: !item.get('hasChildren'), //是否是最下级
  171. type: item.get('type')
  172. });
  173. });
  174. return nodes;
  175. }
  176. //搜索
  177. async onSearchNodes(e: string, modal?: boolean) {
  178. if (modal) {
  179. this.parentList = await this.getDepart('', e, true);
  180. return;
  181. }
  182. this.nodes = await this.getDepart('', e, e ? true : false);
  183. }
  184. //展开/合并
  185. async nzEvent(event: NzFormatEmitEvent): Promise<void> {
  186. console.log(event);
  187. let node: any = event.node;
  188. if (event.eventName === 'expand') {
  189. // if (node.origin.isParent) {
  190. // node.addChildren([]);
  191. // return;
  192. // }
  193. if (node?._children.length <= 0) {
  194. let data = await this.getDepart(node.key);
  195. node.addChildren(data);
  196. }
  197. console.log(this.nodes);
  198. } else {
  199. // if (node.origin.isParent) {
  200. this.currentDepart = node.origin;
  201. this.getProfile();
  202. // }
  203. }
  204. }
  205. async getProfile() {
  206. this.profiles = [];
  207. this.loading = true;
  208. let childrens = [this.currentDepart.key]
  209. if(!this.checkedShowFilter){
  210. childrens = await this.tbookSer.getChild(this.currentDepart.key)
  211. }
  212. let queryParams = {
  213. where: {
  214. $or: [
  215. {
  216. user: {
  217. $inQuery: {
  218. where: {
  219. $or: [
  220. {
  221. department: { $in: [childrens] },
  222. },
  223. ],
  224. },
  225. className: '_User',
  226. },
  227. },
  228. },
  229. ],
  230. },
  231. };
  232. let query = Parse.Query.fromJSON('Profile', queryParams);
  233. query.include('user');
  234. query.notEqualTo('identity', '国家级管理员');
  235. query.notEqualTo('isDeleted', true);
  236. this.profileLength = await query.count()
  237. query.limit(this.pageSize)
  238. query.skip((this.pageIndex - 1) * this.pageSize)
  239. let r = await query.find();
  240. this.profiles = r;
  241. this.loading = false;
  242. this.resetChange()
  243. }
  244. //分页切换
  245. pageIndexChange(e: any) {
  246. console.log(e);
  247. this.pageIndex = e;
  248. this.getProfile();
  249. }
  250. onChecked(){
  251. this.pageIndex = 1;
  252. this.getProfile()
  253. }
  254. //搜索触发
  255. onSearch(event: NzFormatEmitEvent) {
  256. this.pageIndex = 1;
  257. console.log(event);
  258. }
  259. contextMenu(
  260. $event: MouseEvent,
  261. menu: NzDropdownMenuComponent,
  262. node?: any
  263. ): void {
  264. console.log(node);
  265. this.activatedNode = node;
  266. this.nzContextMenuService.create($event, menu);
  267. }
  268. //删除部门
  269. async onDelDepart() {
  270. this.modal.confirm({
  271. nzTitle: '删除',
  272. nzContent: '删除后数据不可恢复,请谨慎操作',
  273. nzOkText: '确认',
  274. nzOkType: 'primary',
  275. nzOkDanger: true,
  276. nzOnOk: async () => {
  277. new Promise(async (resolve, reject) => {
  278. if (this.activatedNode) {
  279. let query = new Parse.Query('Department');
  280. let r = await query.get(this.activatedNode?.key);
  281. if (r?.id) {
  282. r.set('isDeleted', true);
  283. await r.save();
  284. }
  285. this.message.success('删除成功');
  286. this.nodes = await this.getDepart();
  287. resolve(true);
  288. }
  289. }).catch(() => console.log('Oops errors!'));
  290. },
  291. nzCancelText: '取消',
  292. nzOnCancel: () => console.log('Cancel'),
  293. });
  294. }
  295. onAllChecked(checked: boolean): void {
  296. this.profiles.forEach((item) => {
  297. if (checked) {
  298. this.setOfCheckedId.add(item.id);
  299. } else {
  300. this.setOfCheckedId.delete(item.id);
  301. }
  302. });
  303. this.checkedAll = checked;
  304. }
  305. onItemChecked(id: string, e: boolean) {
  306. if (e) {
  307. this.setOfCheckedId.add(id);
  308. } else {
  309. this.setOfCheckedId.delete(id);
  310. }
  311. this.checkedAll = this.profiles.every((item) =>
  312. this.setOfCheckedId.has(item.id)
  313. );
  314. }
  315. //添加成员
  316. addMember() {
  317. this.parentList = this.nodes;
  318. this.account = {
  319. name: '',
  320. phone: '',
  321. email: '',
  322. password: '',
  323. identity: '',
  324. department: {},
  325. companyType: '',
  326. };
  327. this.accountIsVisible = true;
  328. }
  329. //新建打开弹窗
  330. async showModalDepart(type: string) {
  331. this.parentList = this.nodes;
  332. this.editObject = {
  333. name: '',
  334. code: '',
  335. desc: '',
  336. parent: '',
  337. branch: '',
  338. };
  339. if (type == 'edit') {
  340. let query = new Parse.Query('Department');
  341. query.include('parent','parent.parent')
  342. let r = await query.get(this.activatedNode?.key);
  343. this.activeDepart = r;
  344. this.editObject = {
  345. name: this.activeDepart.get('name'),
  346. code: this.activeDepart.get('code'),
  347. desc: this.activeDepart.get('desc'),
  348. parent: {
  349. title: this.activeDepart.get('parent')?.get('name'),
  350. id: this.activeDepart.get('parent')?.id,
  351. },
  352. branch: this.activeDepart.get('branch'),
  353. };
  354. this.parentMap = await this.tbookSer.formatNode(
  355. this.activeDepart.get('parent')?.id
  356. );
  357. if (r?.get('parent')?.get('parent')?.id) {
  358. this.parentList = await this.getDepart(r.get('parent')?.get('parent').id);
  359. }else{
  360. this.parentList = this.parents
  361. }
  362. this.radio = this.activeDepart.get('parent')?.id
  363. } else if (type == 'add' && this.activatedNode) {
  364. this.editObject.parent = {
  365. title: this.activatedNode.origin.title,
  366. id: this.activatedNode.origin.key,
  367. };
  368. this.radio = this.activatedNode.origin.key
  369. this.editObject.branch =
  370. this.activatedNode.origin.branch || this.activatedNode.origin.title;
  371. this.parentMap = await this.tbookSer.formatNode(this.activatedNode.origin.key);
  372. if (this.activatedNode?.origin.branch) {
  373. this.parentList = await this.getDepart(
  374. this.activatedNode?.origin.branch
  375. );
  376. }
  377. }
  378. console.log(this.parentMap);
  379. this.editType = type;
  380. this.isVisible = true;
  381. }
  382. async onPre(data?: any, index?: number) {
  383. if (!data) {
  384. this.parentList = await this.getDepart();
  385. this.parentMap = [];
  386. return;
  387. }
  388. if (index == this.parentMap.length - 1) return;
  389. this.parentMap.splice((index || 0) + 1);
  390. this.parentList = await this.getDepart(data?.key);
  391. this.radio = '';
  392. }
  393. //选择所属类别下级列表
  394. async onCheckedDepart(type: string, e: any, checked?: boolean) {
  395. this.radio = e.key;
  396. if (type == 'account') this.account.identity = '';
  397. this.parentMap = await this.tbookSer.formatNode(e.key);
  398. if (checked) {
  399. // this.editObject.name = e.title
  400. if (type == 'account' && e.parent) {
  401. this.account.department = { title: e.title, id: e.key };
  402. this.account.companyType = e.branch || e.title;
  403. this.userType = this.parents.some((item) => e.parent == item.key)
  404. ? !e.type ? ['工作联系人', '评审专家', '个人'] : ['评审专家', '工作联系人', '个人']
  405. : e.type
  406. ? ['评审专家', '高校联系人', '个人']
  407. : ['评审专家', '个人'];
  408. } else {
  409. this.editObject.parent = {
  410. title: e.title,
  411. id: e.key,
  412. };
  413. this.editObject.branch = e.branch || e.title;
  414. }
  415. return;
  416. }
  417. if (e.isLeaf) {
  418. return;
  419. }
  420. this.parentList = await this.getDepart(e?.key);
  421. }
  422. async handleOk(): Promise<void> {
  423. if (!this.editObject?.name || !this.editObject.parent?.id) {
  424. this.message.error('请填写完整信息');
  425. return;
  426. }
  427. if (this.activeDepart?.id && this.editType == 'edit') {
  428. this.activeDepart.set('name', this.editObject?.name);
  429. this.activeDepart.set('code', this.editObject?.code);
  430. this.activeDepart.set('desc', this.editObject.desc);
  431. this.activeDepart.set('parent', {
  432. __type: 'Pointer',
  433. className: 'Department',
  434. objectId: this.editObject.parent?.id,
  435. });
  436. this.activeDepart.set('branch', this.editObject.branch);
  437. } else {
  438. let obj = Parse.Object.extend('Department');
  439. this.activeDepart = new obj();
  440. this.activeDepart?.set('name', this.editObject?.name);
  441. this.activeDepart?.set('code', this.editObject?.code);
  442. this.activeDepart?.set('desc', this.editObject.desc);
  443. this.activeDepart?.set('parent', {
  444. __type: 'Pointer',
  445. className: 'Department',
  446. objectId: this.editObject.parent?.id,
  447. });
  448. this.activeDepart?.set('branch', this.editObject.branch);
  449. }
  450. let type
  451. let filters = ['出版单位','教育部直属高校']
  452. if(filters.includes(this.editObject.parent?.title)){
  453. type = '单位'
  454. }
  455. this.activeDepart?.set('type', type);
  456. await this.activeDepart?.save();
  457. if(!type && this.activeDepart?.id){
  458. //判断添加的是部门还是单位
  459. let leng = await this.tbookSer.formatNode(this.activeDepart.id)
  460. if(leng.length > 2){
  461. console.log(leng.length);
  462. if(filters.includes(leng[0].title)){
  463. this.activeDepart?.set('type', '部门');
  464. }else{
  465. this.activeDepart?.set('type',leng.length > 3 ? '部门' : '单位');
  466. }
  467. leng.slice()
  468. await this.activeDepart?.save();
  469. }
  470. }
  471. await this.updateChildren();
  472. this.isVisible = false;
  473. this.message.success(this.editType == 'edit' ? '保存' : '添加' + '成功');
  474. this.activeDepart = undefined;
  475. this.nodes = await this.getDepart();
  476. }
  477. //更新上级children字段
  478. async updateChildren() {
  479. let query = new Parse.Query('Department');
  480. query.equalTo('objectId', this.editObject.parent?.id);
  481. query.select('hasChildren')
  482. let r = await query.first();
  483. if (r?.id && !r.get('hasChildren')) {
  484. r?.set('hasChildren', true);
  485. await r.save();
  486. }
  487. return;
  488. }
  489. handleCancel(): void {
  490. console.log('Button cancel clicked!');
  491. this.isVisible = false;
  492. this.activatedNode = undefined;
  493. this.parentMap = [];
  494. this.accountIsVisible = false;
  495. this.account = null;
  496. }
  497. /* 组织 */
  498. showModalOrganize() {
  499. this.message.warning('权限灰度中');
  500. }
  501. goDateil(id: string) {
  502. this.route.navigate(['/nav-admin/manage/user/edit', { id: id }]);
  503. }
  504. randomPassword() {
  505. this.account.password = this.tbookSer.randomPassword()
  506. console.log(this.account.password);
  507. }
  508. /* 添加账号 */
  509. isLoadingOne:boolean = false
  510. async accountComplete(){
  511. if(this.isLoadingOne) return
  512. this.isLoadingOne = true
  513. this.account.email = this.account?.email.trim();
  514. this.account.phone = this.account?.phone.trim();
  515. this.account.name = this.account?.name.trim();
  516. this.account.password = this.account?.password.trim();
  517. if(!await this.authVrifly()){
  518. this.isLoadingOne = false
  519. return
  520. }
  521. try{
  522. let obj = Parse.Object.extend('_User');
  523. let user = new obj()
  524. user?.set('username', this.account?.email || this.account?.phone);
  525. user?.set('name', this.account?.name);
  526. user?.set('phone', this.account?.phone);
  527. this.account?.email && user?.set('email', this.account?.email);
  528. user?.set('password', this.account.password);
  529. user?.set('accountState', '已认证');
  530. user?.set('department', {
  531. __type: 'Pointer',
  532. className: 'Department',
  533. objectId: this.account.department?.id,
  534. });
  535. let u = await user.save()
  536. let p = Parse.Object.extend('Profile');
  537. let profile = new p()
  538. profile?.set('user', u?.toPointer());
  539. profile?.set('companyType', this.account.companyType);
  540. profile?.set('email', this.account.email);
  541. profile?.set('identity', this.account.identity);
  542. let res = await profile?.save();
  543. this.isLoadingOne = false
  544. this.accountIsVisible = false;
  545. this.account = null;
  546. this.modal.success({
  547. nzTitle: '添加成功',
  548. nzContent: '',
  549. nzOnOk: () =>{
  550. this.currentDepart && this.getProfile()
  551. },
  552. });
  553. }
  554. catch(err:any){
  555. console.warn('添加用户错误',err);
  556. this.isLoadingOne = false
  557. this.message.error(err?.Error || '错误:请检查用户邮箱或手机号是否已存在');
  558. return;
  559. }
  560. }
  561. async authVrifly():Promise< boolean | undefined>{
  562. this.account.email = this.account?.email.trim();
  563. this.account.phone = this.account?.phone.trim();
  564. this.account.name = this.account?.name.trim();
  565. this.account.password = this.account?.password.trim();
  566. if(!this.account?.name|| !this.account.department?.id || !this.account.password
  567. || !this.account.phone || !this.account?.email){
  568. this.message.warning('请填写必填项');
  569. return
  570. }
  571. if(!this.account.identity){
  572. this.message.error("请选择人员类型");
  573. return;
  574. }
  575. let a = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
  576. if(this.account.phone && !String(this.account.phone).match(a)){
  577. this.message.error("请填写正确手机号");
  578. return;
  579. }
  580. let m = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
  581. if(!String(this.account.email).match(m)){
  582. this.message.error("邮箱格式有误");
  583. return;
  584. }
  585. // if(!this.account?.email && !this.account?.phone){
  586. // this.message.error("邮箱或手机号必须填写一项");
  587. // return;
  588. // }
  589. console.log(this.account.password);
  590. if(!(this.account.password.length >= 6 && this.account.password.length <= 18)){
  591. this.message.error('密码格式错误,请填写6-18位非空字符串(数字、大小写字母或英文符号)');
  592. return;
  593. }
  594. if(!await this.tbookSer.userFind(this.account.phone)){
  595. this.message.error('手机号已存在');
  596. return
  597. }
  598. return true
  599. }
  600. setOfCheckedId = new Set<string>();
  601. //移除部门
  602. async removeBranch(data:Parse.Object){
  603. if(data?.get('user')?.id){
  604. data?.get('user')?.set('department',null)
  605. await data?.get('user')?.save()
  606. this.message.error("移除成功");
  607. this.getProfile();
  608. }
  609. }
  610. removeBranchAll(){
  611. this.modal.confirm({
  612. nzTitle: '批量移除',
  613. nzContent: `请谨慎操作`,
  614. nzOkText: '确认',
  615. nzOkType: 'primary',
  616. nzOkDanger: true,
  617. nzOnOk: async () => {
  618. let selectedList = this.profiles.filter((item: any) =>
  619. this.setOfCheckedId.has(item?.id)
  620. );
  621. let romovePromiseList = selectedList.map((item: any) => {
  622. return new Promise(async (resolve) => {
  623. item?.get('user')?.set('department',null)
  624. await item?.get('user')?.save()
  625. resolve(true)
  626. });
  627. });
  628. try {
  629. await Promise.all(romovePromiseList);
  630. this.message.error("移除成功");
  631. this.getProfile();
  632. this.resetChange()
  633. } catch (err) {}
  634. },
  635. nzCancelText: '取消',
  636. nzOnCancel: () => console.log('Cancel'),
  637. });
  638. }
  639. resetChange(){
  640. this.setOfCheckedId = new Set<string>();
  641. this.checkedAll = false;
  642. }
  643. }