page-user.component.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. import { Component, OnInit, ViewChild } from '@angular/core';
  2. import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
  3. import { CompTableListComponent } from '../../../app/comp-table/comp-table-list/comp-table-list.component';
  4. // import _User from '../../../schemas/_User';
  5. import * as Parse from 'parse';
  6. import { CommonModule } from '@angular/common';
  7. import { NzPageHeaderModule } from 'ng-zorro-antd/page-header';
  8. import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb';
  9. import { NzSpaceModule } from 'ng-zorro-antd/space';
  10. import { CommonCompModule } from '../../../services/common.modules';
  11. // import { Profile } from '../../../schemas/Profile-list';
  12. import { NzEmptyModule } from 'ng-zorro-antd/empty';
  13. import { NzModalService } from 'ng-zorro-antd/modal';
  14. import { textbookServer } from '../../../services/textbook';
  15. import { NzMessageService } from 'ng-zorro-antd/message';
  16. import { NzSelectModule } from 'ng-zorro-antd/select';
  17. import { NzRadioModule } from 'ng-zorro-antd/radio';
  18. @Component({
  19. selector: 'app-page-user',
  20. templateUrl: './page-user.component.html',
  21. styleUrls: ['./page-user.component.scss'],
  22. imports: [
  23. CommonModule,
  24. RouterOutlet,
  25. NzSpaceModule,
  26. NzPageHeaderModule,
  27. CompTableListComponent,
  28. NzBreadCrumbModule,
  29. CommonCompModule,
  30. NzRadioModule,
  31. NzEmptyModule,
  32. NzSelectModule,
  33. ],
  34. standalone: true,
  35. })
  36. export class PageUserComponent implements OnInit {
  37. @ViewChild(CompTableListComponent) list: CompTableListComponent | undefined;
  38. listOfColumns: any = {
  39. identity: {
  40. listOfFilter: [
  41. { value: '工作联系人', text: '工作联系人' },
  42. { value: '高校联系人', text: '高校联系人' },
  43. { value: '评审专家', text: '评审专家' },
  44. { value: '个人', text: '个人' },
  45. ],
  46. },
  47. companyType: {
  48. listOfFilter: [
  49. { value: '教育部直属高校', text: '教育部直属高校' },
  50. { value: '中央有关部门(单位)教育司(局)', text: '中央有关部门(单位)教育司(局)' },
  51. { value: '省级教育行政部门', text: '省级教育行政部门' },
  52. { value: '出版单位', text: '出版单位' },
  53. ],
  54. },
  55. }
  56. // _User = _User;
  57. // ProfileList = Profile;
  58. // className: string | undefined;
  59. // queryParams: any | undefined;
  60. // fieldsArray: Array<any> | undefined;
  61. user: Parse.User | undefined;
  62. profiles: Array<Parse.Object> = [];
  63. profileLength: number = 0; //总数据
  64. pageSize: number = 10;
  65. pageIndex: number = 1;
  66. checkedAll: boolean = false; //全选
  67. indeterminate = false;
  68. loading = false;
  69. searchValue: string = ''; //搜索内容
  70. setOfCheckedId = new Set<string>();
  71. userType: Array<string> = ['工作联系人', '评审专家', '高校联系人', '个人'];
  72. parentMap: Array<any> = [];
  73. parentList: Array<any> = [];
  74. radio: string = '';
  75. loadingParent: boolean = false;
  76. /* 添加账号 */
  77. accountIsVisible: boolean = false;
  78. account: any = {
  79. name: '',
  80. username: '',
  81. phone: '',
  82. email: '',
  83. password: '',
  84. identity: '',
  85. department: {},
  86. companyType: '',
  87. };
  88. parents: Array<any> = []; //所有一级列表
  89. //筛选条件
  90. filters: any = {
  91. identity: { type: 'string', value: [] },
  92. companyType: { type: 'string', value: [] },
  93. };
  94. constructor(
  95. public tbookSer: textbookServer,
  96. private modal: NzModalService,
  97. private route: Router,
  98. private message: NzMessageService,
  99. private activeRoute: ActivatedRoute
  100. ) {
  101. this.user = Parse.User.current();
  102. if (this.tbookSer.profile.identity != '国家级管理员') {
  103. this.userType = ['评审专家', '高校联系人', '个人'];
  104. }
  105. }
  106. ngOnInit(): void {
  107. this.activeRoute.paramMap.subscribe(async (params) => {
  108. this.getProfile();
  109. this.parents = await this.getDepart();
  110. });
  111. }
  112. async getProfile() {
  113. this.profiles = [];
  114. this.loading = true;
  115. let queryParams: any = {
  116. where: {
  117. $or: [
  118. {
  119. user: {
  120. $inQuery: {
  121. where: {
  122. $or: [
  123. {
  124. name: { $regex: `.*${this.searchValue}.*` },
  125. },
  126. {
  127. username: { $regex: `.*${this.searchValue}.*` },
  128. },
  129. {
  130. phone: { $regex: `.*${this.searchValue}.*` },
  131. },
  132. {
  133. email: { $regex: `.*${this.searchValue}.*` },
  134. },
  135. ],
  136. },
  137. className: '_User',
  138. },
  139. },
  140. },
  141. ],
  142. },
  143. };
  144. if (this.tbookSer.profile.identity != '国家级管理员') {
  145. this.tbookSer.profile.user.department;
  146. queryParams['where']['$or'][0]['user']['$inQuery']['where'][
  147. 'department'
  148. ] = {
  149. $eq: this.tbookSer.profile.user.department?.objectId,
  150. };
  151. }
  152. let query = Parse.Query.fromJSON('Profile', queryParams);
  153. for (const key in this.filters) {
  154. const element = this.filters[key];
  155. if (element.value?.length > 0) {
  156. query.containedIn(key, element.value);
  157. }
  158. }
  159. query.include('user');
  160. query.notEqualTo('identity', '国家级管理员');
  161. query.descending('createdAt');
  162. if (this.tbookSer.profile.identity == '工作联系人') {
  163. query.containedIn('identity', ['个人', '评审专家', '高校联系人']);
  164. } else if (this.tbookSer.profile.identity == '高校联系人') {
  165. query.containedIn('identity', ['个人', '评审专家']);
  166. }
  167. this.profileLength = await query.count();
  168. query.limit(this.pageSize);
  169. query.skip((this.pageIndex - 1) * this.pageSize);
  170. let r = await query.find();
  171. this.profiles = r;
  172. this.loading = false;
  173. }
  174. //分页切换
  175. pageIndexChange(e: any) {
  176. console.log(e);
  177. this.pageIndex = e;
  178. this.getProfile();
  179. }
  180. //筛选条件
  181. onChangeFilter(data: any, type:string){
  182. console.log(data);
  183. this.filters[type].value = data
  184. this.profiles = []
  185. this.pageIndex = 1
  186. this.getProfile()
  187. }
  188. createUser() {
  189. this.route.navigate(['/nav-admin/manage/user/create']);
  190. }
  191. onItemChecked(id: string, e: boolean) {
  192. if (e) {
  193. this.setOfCheckedId.add(id);
  194. } else {
  195. this.setOfCheckedId.delete(id);
  196. }
  197. this.checkedAll = this.profiles.every((item) =>
  198. this.setOfCheckedId.has(item.id)
  199. );
  200. }
  201. onAllChecked(checked: boolean): void {
  202. this.profiles.forEach((item) => {
  203. if (checked) {
  204. this.setOfCheckedId.add(item.id);
  205. } else {
  206. this.setOfCheckedId.delete(item.id);
  207. }
  208. });
  209. this.checkedAll = checked;
  210. }
  211. resetChange() {
  212. this.setOfCheckedId = new Set<string>();
  213. this.checkedAll = false;
  214. }
  215. async updateUser(data: Parse.Object, type: string) {
  216. console.log(type);
  217. if (
  218. this.tbookSer.profile.identity != '国家级管理员' &&
  219. (data?.get('identity') == '工作联系人' ||
  220. data?.get('identity') == '高校联系人')
  221. ) {
  222. this.message.warning('暂无权限');
  223. return;
  224. }
  225. this.modal.confirm({
  226. nzTitle: '操作提示',
  227. nzContent: `确定${type}吗?`,
  228. nzOkText: '确认',
  229. nzOkType: 'primary',
  230. nzOkDanger: type == '删除' ? true : false,
  231. nzOnOk: async () => {
  232. let query = new Parse.Query('_User');
  233. query.equalTo('objectId', data?.get('user')?.id);
  234. let r = await query.first();
  235. if (r?.id) {
  236. switch (type) {
  237. case '通过认证':
  238. r?.set('accountState', '已认证');
  239. Parse.Cloud.run('aliSmsSend',{
  240. "mobileList": [data?.get('user')?.get('phone')],"templateCode":"SMS_468870790","params":{},"signName":"普通高等教育教材网"
  241. })
  242. await r?.save();
  243. break;
  244. case '禁用':
  245. r?.set('accountState', '已禁用');
  246. await r?.save();
  247. break;
  248. case '删除':
  249. // r?.set('isDeleted', true);
  250. this.setOfCheckedId.delete(data.id);
  251. await data.destroy();
  252. await r.destroy();
  253. break;
  254. }
  255. }
  256. this.getProfile();
  257. },
  258. nzCancelText: '取消',
  259. nzOnCancel: () => console.log('Cancel'),
  260. });
  261. }
  262. deleteSelected() {
  263. this.modal.confirm({
  264. nzTitle: '批量删除',
  265. nzContent: `删除后数据不可恢复,请谨慎操作`,
  266. nzOkText: '确认',
  267. nzOkType: 'primary',
  268. nzOkDanger: true,
  269. nzOnOk: async () => {
  270. let selectedList = this.profiles.filter((item: any) =>
  271. this.setOfCheckedId.has(item?.id)
  272. );
  273. let deletePromiseList = selectedList.map((item: any) => {
  274. return new Promise(async (resolve) => {
  275. await item.get('user')?.destroy()
  276. await item.destroy()
  277. resolve(true)
  278. });
  279. });
  280. try {
  281. await Promise.all(deletePromiseList);
  282. this.setOfCheckedId = new Set<string>();
  283. this.checkedAll = false
  284. this.getProfile();
  285. } catch (err) {}
  286. },
  287. nzCancelText: '取消',
  288. nzOnCancel: () => console.log('Cancel'),
  289. });
  290. }
  291. goDateil(id: string) {
  292. this.route.navigate(['/nav-admin/manage/user/edit', { id: id }]);
  293. }
  294. /* 添加用户 */
  295. async addMember() {
  296. this.radio = '';
  297. this.account = {
  298. name: '',
  299. username: '',
  300. phone: '',
  301. email: '',
  302. password: '',
  303. identity: '',
  304. department: {},
  305. companyType: '',
  306. };
  307. if (this.tbookSer.profile.identity != '国家级管理员') {
  308. // let query = new Parse.Query('Department');
  309. // query.select('code', 'name', 'branch', 'parent', 'type', 'hasChildren');
  310. // let res = await query.get(
  311. // this.tbookSer.profile.user.department?.objectId
  312. // );
  313. // this.parentList = [
  314. // {
  315. // title: res.get('name'),
  316. // key: res.id,
  317. // branch: res.get('branch'),
  318. // parent: res.get('parent')?.id, //上级
  319. // isLeaf: !res.get('hasChildren'), //是否是最下级
  320. // type: res.get('type'),
  321. // },
  322. // ];
  323. this.parentList = await this.getDepart(this.tbookSer.profile.user.department?.objectId);
  324. this.parentMap = await this.formatNode(this.tbookSer.profile.user.department?.objectId);
  325. } else {
  326. this.parentList = await this.getDepart();
  327. }
  328. this.accountIsVisible = true;
  329. }
  330. async getDepart(parent?: string, searchValue?: string): Promise<Array<any>> {
  331. let nodes: any = [];
  332. let query = new Parse.Query('Department');
  333. query.equalTo(
  334. 'parent',
  335. searchValue
  336. ? this.parentMap[this.parentMap.length - 1]?.key
  337. : parent
  338. ? parent
  339. : this.tbookSer.profile.identity != '国家级管理员'
  340. ? this.tbookSer.profile.user.department?.objectId
  341. : null
  342. );
  343. searchValue && query.contains('name', searchValue);
  344. query.notEqualTo('isDeleted', true);
  345. query.select('code', 'name', 'branch', 'parent', 'type', 'hasChildren');
  346. query.descending('createdAt');
  347. query.limit(2000);
  348. let res = await query.find();
  349. res.forEach((item) => {
  350. nodes.push({
  351. title: item.get('name'),
  352. key: item.id,
  353. branch: item.get('branch'),
  354. parent: item.get('parent')?.id, //上级
  355. isLeaf: !item.get('hasChildren'), //是否是最下级
  356. type: item.get('type'),
  357. });
  358. });
  359. return nodes;
  360. }
  361. //搜索
  362. async onSearchNodes(e: string) {
  363. this.parentList = await this.getDepart('', e);
  364. return;
  365. }
  366. async onPre(data?: any, index?: number) {
  367. if (!data) {
  368. this.parentList = await this.getDepart();
  369. this.parentMap = [];
  370. return;
  371. }
  372. if (index == this.parentMap.length - 1){
  373. if(this.parentList.length == 0 ) this.parentList = await this.getDepart(data?.key);
  374. return;
  375. }
  376. this.parentMap.splice((index || 0) + 1);
  377. this.parentList = await this.getDepart(data?.key);
  378. }
  379. //选择所属类别下级列表
  380. async onCheckedDepart(e: any, checked?: boolean) {
  381. this.radio = e.key;
  382. this.account.identity = '';
  383. this.parentMap = await this.formatNode(e.key);
  384. if (this.tbookSer.profile.identity != '国家级管理员') {
  385. let index = this.parentMap.findIndex(
  386. (item) => this.tbookSer.profile.user.department?.objectId == item.key
  387. );
  388. if (index != -1) {
  389. this.parentMap.forEach((item, i) => {
  390. if (i >= index) {
  391. this.parentMap[i].verify = true;
  392. }
  393. });
  394. }
  395. }
  396. if (checked && e.parent) {
  397. this.account.department = { title: e.title, id: e.key };
  398. this.account.companyType = e.branch || e.title;
  399. this.userType = this.parents.some((item) => e.parent == item.key)
  400. ? !e.type ? ['工作联系人', '评审专家', '个人'] : ['评审专家', '工作联系人', '个人']
  401. : e.type
  402. ? ['评审专家', '高校联系人', '个人']
  403. : ['评审专家', '个人'];
  404. return;
  405. }
  406. if (e.isLeaf) {
  407. return;
  408. }
  409. this.parentList = await this.getDepart(e?.key);
  410. }
  411. //格式化链
  412. async formatNode(id: string): Promise<Array<any>> {
  413. let query = new Parse.Query('Department');
  414. query.select('name', 'parent');
  415. let r = await query.get(id);
  416. let arr = [
  417. {
  418. title: r.get('name'),
  419. key: r.id,
  420. },
  421. ];
  422. if (r?.get('parent')) {
  423. arr.unshift(...(await this.formatNode(r?.get('parent').id)));
  424. }
  425. return arr;
  426. }
  427. handleCancel(): void {
  428. this.parentMap = [];
  429. this.accountIsVisible = false;
  430. this.account = null;
  431. }
  432. randomPassword() {
  433. this.account.password = this.tbookSer.randomPassword();
  434. console.log(this.account.password);
  435. }
  436. /* 添加账号 */
  437. isLoadingOne:boolean = false
  438. async accountComplete() {
  439. if(this.isLoadingOne) return
  440. this.isLoadingOne = true
  441. this.account.username = this.account?.username.trim();
  442. this.account.email = this.account?.email.trim();
  443. this.account.phone = this.account?.phone.trim();
  444. this.account.name = this.account?.name.trim();
  445. this.account.password = this.account?.password.trim();
  446. if(!await this.authVrifly()){
  447. this.isLoadingOne = false
  448. return
  449. }
  450. try {
  451. let obj = Parse.Object.extend('_User');
  452. let user = new obj();
  453. user?.set(
  454. 'username',
  455. this.account?.username || this.account?.email || this.account?.phone
  456. );
  457. user?.set('name', this.account?.name);
  458. user?.set('phone', this.account?.phone);
  459. this.account?.email && user?.set('email', this.account?.email);
  460. user?.set('password', this.account.password);
  461. user?.set('accountState', '已认证');
  462. user?.set('department', {
  463. __type: 'Pointer',
  464. className: 'Department',
  465. objectId: this.account.department?.id,
  466. });
  467. let u = await user.save();
  468. let p = Parse.Object.extend('Profile');
  469. let profile = new p();
  470. profile?.set('user', u?.toPointer());
  471. profile?.set('companyType', this.account.companyType);
  472. profile?.set('email', this.account.email);
  473. profile?.set('identity', this.account.identity);
  474. let res = await profile?.save();
  475. this.isLoadingOne = false
  476. this.accountIsVisible = false;
  477. this.account = null;
  478. Parse.Cloud.run('aliSmsSend',{
  479. "mobileList": [this.account?.phone],"templateCode":"SMS_469060724","params":{},"signName":"普通高等教育教材网"
  480. })
  481. this.modal.success({
  482. nzTitle: '添加成功',
  483. nzContent: '',
  484. nzOnOk: () => {
  485. this.pageIndex = 1;
  486. this.getProfile();
  487. },
  488. });
  489. } catch (err: any) {
  490. console.warn('添加用户错误', err);
  491. this.isLoadingOne = false
  492. this.message.error(
  493. err?.Error || '错误:请检查用户或邮箱及手机号是否已存在'
  494. );
  495. return;
  496. }
  497. }
  498. async authVrifly():Promise< boolean | undefined>{
  499. this.account.password = this.account?.password.trim();
  500. this.account.email = this.account?.email.trim();
  501. this.account.phone = this.account?.phone.trim();
  502. this.account.name = this.account?.name.trim();
  503. if (
  504. !this.account?.name ||
  505. !this.account.department?.id ||
  506. !this.account.password||
  507. !this.account.phone || !this.account?.email
  508. ) {
  509. this.message.warning('请填写必填项');
  510. return;
  511. }
  512. if (!this.account.identity) {
  513. this.message.error('请选择人员类型');
  514. return;
  515. }
  516. let a = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/;
  517. if (this.account.phone && !String(this.account.phone).match(a)) {
  518. this.message.error('请填写正确手机号');
  519. return;
  520. }
  521. 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,}))$/
  522. if(!String(this.account.email).match(m)){
  523. this.message.error("邮箱格式有误");
  524. return;
  525. }
  526. // if (!this.account?.email&& !this.account?.phone) {
  527. // this.message.error('邮箱或手机号必须填写一项');
  528. // return;
  529. // }
  530. if(!(this.account.password.length >= 6 && this.account.password.length <= 18)){
  531. this.message.error('密码格式错误,请填写6-18位非空字符串(数字、大小写字母或英文符号)');
  532. return;
  533. }
  534. if(!await this.tbookSer.userFind(this.account.phone)){
  535. this.message.error('手机号已存在');
  536. return
  537. }
  538. return true
  539. }
  540. }