Browse Source

update admin

warrior 7 months ago
parent
commit
df2b2dcdf6
26 changed files with 2306 additions and 299 deletions
  1. 54 47
      projects/textbook/src/app/textbook/textbook.component.html
  2. 32 5
      projects/textbook/src/app/textbook/textbook.component.ts
  3. 500 0
      projects/textbook/src/modules/common/textbook-details/textbook-details.component.html
  4. 95 0
      projects/textbook/src/modules/common/textbook-details/textbook-details.component.scss
  5. 24 0
      projects/textbook/src/modules/common/textbook-details/textbook-details.component.spec.ts
  6. 52 0
      projects/textbook/src/modules/common/textbook-details/textbook-details.component.ts
  7. 1 2
      projects/textbook/src/modules/login/account-info/account-info.component.ts
  8. 10 0
      projects/textbook/src/modules/nav-admin/modules.routes.ts
  9. 1 2
      projects/textbook/src/modules/nav-admin/page-process/process-create/process-create.component.html
  10. 2 2
      projects/textbook/src/modules/nav-admin/page-process/process-create/process-create.component.ts
  11. 64 1
      projects/textbook/src/modules/nav-admin/page-process/process-list/process-list.component.html
  12. 61 29
      projects/textbook/src/modules/nav-admin/page-process/process-list/process-list.component.ts
  13. 74 31
      projects/textbook/src/modules/nav-admin/page-role/page-role.component.html
  14. 60 0
      projects/textbook/src/modules/nav-admin/page-role/page-role.component.scss
  15. 207 81
      projects/textbook/src/modules/nav-admin/page-role/page-role.component.ts
  16. 16 15
      projects/textbook/src/modules/nav-admin/page-user/page-user.component.html
  17. 104 79
      projects/textbook/src/modules/nav-admin/page-user/page-user.component.ts
  18. 443 0
      projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.html
  19. 173 0
      projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.scss
  20. 24 0
      projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.spec.ts
  21. 280 0
      projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.ts
  22. 18 1
      projects/textbook/src/modules/nav-admin/user-edit/user-edit.component.ts
  23. 1 1
      projects/textbook/src/modules/nav-author/textbook-details/textbook-details.component.html
  24. 0 1
      projects/textbook/src/modules/nav-province-contact/page-process/process-list/process-list.component.html
  25. 2 2
      projects/textbook/src/modules/nav-province-contact/page-role/page-role.component.html
  26. 8 0
      projects/textbook/src/modules/nav-province-contact/page-role/page-role.component.scss

+ 54 - 47
projects/textbook/src/app/textbook/textbook.component.html

@@ -1,19 +1,19 @@
 <div class="textbook-table">
   <div class="tool">
-     <div class="search">
-        <nz-input-group style="width: 280px" [nzPrefix]="prefixTemplateUser">
-          <input
-            type="text"
-            nz-input
-            placeholder="输入教材名称 / ISBN / 作者名搜索"
-            [(ngModel)]="searchValue"
-            (ngModelChange)="onSearch($event)"
-          />
-        </nz-input-group>
-        <ng-template #prefixTemplateUser
-          ><span nz-icon nzType="search"></span
-        ></ng-template>
-      </div>
+    <div class="search">
+      <nz-input-group style="width: 280px" [nzPrefix]="prefixTemplateUser">
+        <input
+          type="text"
+          nz-input
+          placeholder="输入教材名称 / ISBN / 作者名搜索"
+          [(ngModel)]="searchValue"
+          (ngModelChange)="onSearch($event)"
+        />
+      </nz-input-group>
+      <ng-template #prefixTemplateUser
+        ><span nz-icon nzType="search"></span
+      ></ng-template>
+    </div>
   </div>
   <nz-table
     #tableData
@@ -48,34 +48,43 @@
     <tbody>
       @for (data of textbookList; track data.id) {
       <tr>
-        <td nzEllipsis
+        <td
+          nzEllipsis
           nzLeft
           [nzChecked]="setOfCheckedId.has(data.id)"
           (nzCheckedChange)="onItemChecked(data.id, $event)"
         ></td>
-        <td nzEllipsis
+        <td
+          nzEllipsis
           nzLeft
           nzEllipsis
           class="activeTd"
+          (click)="toUrl(path + '/' + data.id)"
         >
-          {{ data?.get('ISBN') || "-" }}
+          {{ data?.get("ISBN") || "-" }}
         </td>
         <td nzEllipsis>
-          {{ data?.get('title') || "-" }}
+          {{ data?.get("title") || "-" }}
         </td>
         <td nzEllipsis>
-          {{ data?.get('type') == '单册' ? '单册' : data?.get('typeNumber') || "-"  }}
+          {{
+            data?.get("type") == "单册"
+              ? "单册"
+              : data?.get("typeNumber") || "-"
+          }}
         </td>
         <td nzEllipsis>
-          {{ data?.get('author') || "-" }}
+          {{ data?.get("author") || "-" }}
         </td>
         <td nzEllipsis>
-          {{ data?.get('major').name || "-" }}
+          {{ data?.get("major").name || "-" }}
         </td>
         <td nzEllipsis>
-          {{ data?.get('lang') || "-" }}
+          {{ data?.get("lang") || "-" }}
         </td>
         <td nzEllipsis nzRight>
+          @if(data?.get('status') == '200'){
+
           <button
             nz-button
             nz-dropdown
@@ -87,21 +96,19 @@
           <nz-dropdown-menu #menu="nzDropdownMenu">
             <ul nz-menu>
               <li nz-menu-item>
-                <button
-                  nz-button
-                  nzType="link"
-                  style="color: #231c1f"
-                >
+                <button nz-button nzType="link" style="color: #231c1f">
                   <span
                     nz-icon
                     nzType="caret-right"
                     nzTheme="outline"
+                    (click)="reject(data)"
                   ></span
                   >退回教材
                 </button>
               </li>
             </ul>
           </nz-dropdown-menu>
+          }@else { - }
         </td>
       </tr>
       }
@@ -113,28 +120,28 @@
 </div>
 <!-- 全选操作:批量操作 -->
 @if (setOfCheckedId.size) {
-  <div class="batch-toolbar-modal">
-    <div class="batch-toolbar">
-      <div class="styles_counter__18S08">
-        <span>已选</span>
-        <span class="styles_num__178Wa">{{ setOfCheckedId.size }}</span>
-      </div>
-      <div class="batch-toolbar-actions">
-        <div class="ant-space ant-space-horizontal ant-space-align-center">
-          <div class="ant-space-item" style="margin-right: 16px">
-            <button nz-button nzType="text" (click)="deleteSelected()">
-              <span nz-icon nzType="delete"></span>
-              删除
-            </button>
-          </div>
+<div class="batch-toolbar-modal">
+  <div class="batch-toolbar">
+    <div class="styles_counter__18S08">
+      <span>已选</span>
+      <span class="styles_num__178Wa">{{ setOfCheckedId.size }}</span>
+    </div>
+    <div class="batch-toolbar-actions">
+      <div class="ant-space ant-space-horizontal ant-space-align-center">
+        <div class="ant-space-item" style="margin-right: 16px">
+          <button nz-button nzType="text" (click)="rejectSelected()">
+            <span nz-icon nzType="delete"></span>
+            退回
+          </button>
         </div>
       </div>
-      <div class="styles_cancel__AARoT">
-        <button nz-button nzType="text" (click)="onAllChecked(false)">
-          取消选中
-        </button>
-      </div>
+    </div>
+    <div class="styles_cancel__AARoT">
+      <button nz-button nzType="text" (click)="onAllChecked(false)">
+        取消选中
+      </button>
     </div>
   </div>
-  
+</div>
+
 }

+ 32 - 5
projects/textbook/src/app/textbook/textbook.component.ts

@@ -32,6 +32,9 @@ export class TextbookComponent implements OnInit {
   @Input('depart') depart: any; //指定申报单位
   @Input('recommend') recommend: any; //是否推荐
   @Input('uid') uid: string = ''; //个人空间
+  @Input('path') path: string = '/nav-admin/manage/textbook/details';
+  @Input('power') power: Boolean = false; //权限
+  @Input('isSelf') isSelf: Boolean = false; //是否自己创建
 
   loading: boolean = false;
   checkedAll: boolean = false; //全选
@@ -83,6 +86,10 @@ export class TextbookComponent implements OnInit {
     this.recommend && query.equalTo('recommend', true);
     this.uid && query.equalTo('user', this.uid);
     query.notEqualTo('isDeleted', true);
+    if(!this.isSelf){
+      query.equalTo('render',true)
+      query.containedIn('status',['200','400'])
+    }
     this.textbookList = await query.find();
     console.log(this.textbookList);
     this.loading = false;
@@ -114,10 +121,10 @@ export class TextbookComponent implements OnInit {
       this.setOfCheckedId.has(item.id)
     );
   }
-  deleteSelected() {
+  rejectSelected() {
     this.modal.confirm({
-      nzTitle: '批量删除',
-      nzContent: `删除后数据不可恢复,请谨慎操作`,
+      nzTitle: '批量退回',
+      nzContent: `确定退回吗?`,
       nzOkText: '确认',
       nzOkType: 'primary',
       nzOkDanger: true,
@@ -127,16 +134,36 @@ export class TextbookComponent implements OnInit {
         );
         let deletePromiseList = selectedList.map((item: any) => {
           return new Promise((resolve) => {
-            item.set('isDeleted', true);
-            item.save();
+            item.set('status', '300');
+            item.save().then(() => resolve(true));
           });
         });
         try {
           await Promise.all(deletePromiseList);
+          this.getTextbook();
         } catch (err) {}
       },
       nzCancelText: '取消',
       nzOnCancel: () => console.log('Cancel'),
     });
   }
+  reject(data: Parse.Object) {
+    this.modal.confirm({
+      nzTitle: '退回',
+      nzContent: `确定退回吗?`,
+      nzOkText: '确认',
+      nzOkType: 'primary',
+      nzOkDanger: true,
+      nzOnOk: async () => {
+        data.set('status', '300');
+        await data.save();
+        this.getTextbook();
+      },
+      nzCancelText: '取消',
+      nzOnCancel: () => console.log('Cancel'),
+    });
+  }
+  toUrl(url: string) {
+    this.route.navigate([url]);
+  }
 }

+ 500 - 0
projects/textbook/src/modules/common/textbook-details/textbook-details.component.html

@@ -0,0 +1,500 @@
+<div class="site-page-header">
+  <nz-page-header
+    nzTitle="教材详情 - {{textBook.title}}"
+  >
+    <nz-breadcrumb nz-page-header-breadcrumb>
+      <div class="back" (click)="back()">
+        <span nz-icon nzType="left" nzTheme="outline"></span>返回
+      </div>
+    </nz-breadcrumb>
+  </nz-page-header>
+</div>
+<div class="content">
+  <div nz-row>
+    <div nz-col nzSpan="18" #scroll>
+      <div class="templ1" #templ1 style="width: 500px; height: 700px">
+        <div class="title">教材基本信息</div>
+        <div class="base-content">
+          <div class="row">
+            <div class="label">申报教材名称</div>
+            <div class="value">{{textBook.title}}</div>
+          </div>
+          <div class="row">
+            <div class="label">第一主编(作者)</div>
+            <div class="value">{{textBook.author}}</div>
+          </div>
+          <div class="row">
+            <div class="label">第一主编(作者)单位</div>
+            <div class="value">{{textBook.unit}}</div>
+          </div>
+          <div class="row">
+            <div class="label">申报类型</div>
+            <div class="value">{{textBook.type}} {{textBook.type == '全册' ? textBook.typeNumber + '册': ''}}</div>
+          </div>
+          <div class="row">
+            <div class="label">教材应用对象及所属学科专业类</div>
+            <div class="value">{{textBook.majorPoniter}}</div>
+          </div>
+          <div class="row">
+            <div class="label">教材主要语种类型</div>
+            <div class="value">{{textBook.lang}}</div>
+          </div>
+          <div class="row">
+            <div class="label">国际标准书号</div>
+            <div class="value">{{textBook.ISBN}}</div>
+          </div>
+          <div class="row">
+            <div class="label">其他主编姓名</div>
+            <div class="value">{{textBook.authors}}</div>
+          </div>
+          <div class="row">
+            <div class="label">其他编者姓名</div>
+            <div class="value">{{textBook.editor}}</div>
+          </div>
+          <div class="row">
+            <div class="label">是否为重点立项教材</div>
+            <div class="value">{{textBook.approval ? '是否' : '否'}}</div>
+          </div>
+          <div class="row">
+            <div class="label">出版单位</div>
+            <div class="value">{{textBook.editionUnit}}</div>
+          </div>
+          <div class="row">
+            <div class="label">初版时间</div>
+            <div class="value">{{textBook.editionFirst?.iso | date :'yyyy-MM'}}</div>
+          </div>
+          <div class="row">
+            <div class="label">载体形式</div>
+            <div class="value">{{textBook.carrierShape}}</div>
+          </div>
+          <div class="row">
+            <div class="label">本版出版时间及版次</div>
+            <div class="value">{{textBook.editionDate?.iso | date :'yyyy-MM'}}/{{textBook.editionNumber}}</div>
+          </div>
+          <div class="row">
+            <div class="label">最新印次时间及印次</div>
+            <div class="value">{{textBook.printDate?.iso | date :'yyyy-MM'}}/{{textBook.printNumber}}</div>
+          </div>
+          <div class="row">
+            <div class="label">初版以来合计印数</div>
+            <div class="value">{{textBook.printSum}}</div>
+          </div>
+          <div class="row">
+            <div class="label">初版以来是否曾列为重点项目</div>
+            <div class="value">{{textBook.importantProject}}</div>
+          </div>
+          <div class="row">
+            <div class="label">版权页截图</div>
+            <div class="value"><a href="textBook.copyrightImgUrl">截图文件</a></div>
+          </div>
+          <div class="row">
+            <div class="label">中国版本图书馆 CIP 查询截图</div>
+            <div class="value"><a href="textBook.CIPImgUrl">查询截图</a></div>
+          </div>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">教材适用情况</div>
+        <div class="base-content">
+          <div class="row">
+            <div class="label">适用专业代码(六位)及名称</div>
+            <div class="value">{{textBook.major.code}}/{{textBook.major.name}}</div>
+          </div>
+          <div class="row">
+            <div class="label">适用课程</div>
+            <div class="value">{{textBook.lessons}}</div>
+          </div>
+          <div class="row">
+            <div class="label">课程学时</div>
+            <div class="value">{{textBook.period}}</div>
+          </div>
+          <div class="row">
+            <div class="label">适用课程性质</div>
+            <div class="value">
+              @for (tag of textBook.characteristic; track tag) {
+                @if (tag.checked){
+                  <nz-tag>{{tag.label}}</nz-tag>
+                }
+              }</div>
+          </div>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">教材适用情况</div>
+        <div class="text mar10">
+          作者(含主编,不超过 6 人,教材中明确出现姓名)
+        </div>
+        <div class="base-content">
+          <nz-table
+            #basicTable
+            [nzShowPagination]="false"
+            [nzData]="textBook.authorList"
+            [nzScroll]="{
+              x: (templ1.style.width || '1000') + 'px',
+              y: '240px'
+            }"
+          >
+            <thead>
+              <tr>
+                <th nzWidth="160px" nzLeft>姓名</th>
+                <th nzWidth="160px">单位</th>
+                <th nzWidth="160px">出生年月</th>
+                <th nzWidth="160px">国籍</th>
+                <th nzWidth="160px">职务</th>
+                <th nzWidth="160px">职称</th>
+                <th nzWidth="160px">手机号码</th>
+                <th nzWidth="160px">电子邮箱</th>
+                <th nzWidth="160px" nzRight>分工</th>
+              </tr>
+            </thead>
+            <tbody>
+              @for (data of textBook.authorList; track data.mobile;let index =
+              $index) {
+              <tr>
+                <td nzLeft>
+                  {{ data.name }}
+                </td>
+                <td>
+                  {{ data.unit }}
+                </td>
+                <td>
+                  {{ data.birth?.iso | date : "yyyy-MM" }}
+                </td>
+                <td>
+                  {{ data.nationality }}
+                </td>
+                <td>
+                  {{ data.job }}
+                </td>
+                <td>
+                  {{ data.title }}
+                </td>
+                <td>
+                  {{ data.mobile }}
+                </td>
+                <td>
+                  {{ data.email }}
+                </td>
+                <td nzRight>
+                  {{ data.work }}
+                </td>
+              </tr>
+              }
+            </tbody>
+          </nz-table>
+        </div>
+
+        <div class="text mar10">第一主编(作者)情况</div>
+        <div class="base-content">
+          <div class="text-tips">相关教学经历</div>
+          <div class="note">
+            {{ textBook.authorDetails }}
+          </div>
+          <div class="text-tips">相关科学研究项目、成果或论文专著(限5项)</div>
+          <nz-table
+            #basicTable
+            [nzShowPagination]="false"
+            [nzData]="textBook.achievementOptions"
+            [nzScroll]="{
+              x: (templ1.style.width || '1000') + 'px',
+              y: '240px'
+            }"
+          >
+            <thead>
+              <tr>
+                <th nzWidth="80px" nzLeft nzAlign="right">序号</th>
+                <th nzWidth="160px" nzLeft>名称</th>
+                <th nzWidth="160px">来源/出版单位</th>
+                <th nzWidth="80px">时间</th>
+              </tr>
+            </thead>
+            <tbody>
+              @for (data of textBook.achievementOptions; track data.name;let
+              index = $index) {
+              <tr>
+                <td nzLeft nzAlign="right">{{ index + 1 }}</td>
+                <td nzLeft>
+                  {{ data.name }}
+                </td>
+                <td>
+                  {{ data.unit }}
+                </td>
+                <td>
+                  {{ data.date?.iso | date : "yyyy-MM" }}
+                </td>
+              </tr>
+              }
+            </tbody>
+          </nz-table>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">申报教材建设历程</div>
+        <div class="base-content">
+          <nz-table
+            #basicTable
+            [nzShowPagination]="false"
+            [nzData]="textBook.courses"
+            [nzScroll]="{
+              x: (templ1.style.width || '1000') + 'px',
+              y: '240px'
+            }"
+          >
+            <thead>
+              <tr>
+                <th nzWidth="80px" nzLeft nzAlign="right">版次</th>
+                <th nzWidth="120px">出版时间</th>
+                <th nzWidth="100px">字数</th>
+                <th nzWidth="100px">重印次数</th>
+                <th nzWidth="100px">本版总印数</th>
+                <th nzWidth="200px">获奖励情况</th>
+              </tr>
+            </thead>
+            <tbody>
+              @for (data of textBook.courses; track data;let index = $index) {
+              <tr>
+                <td nzLeft nzAlign="right">{{ index + 1 }}</td>
+                <td>
+                  {{ data.date?.iso | date : "yyyy-MM" }}
+                </td>
+                <td>
+                  {{ data.wordage }}
+                </td>
+                <td>
+                  {{ data.num }}
+                </td>
+                <td>
+                  {{ data.sumNum }}
+                </td>
+                <td>
+                  {{ data.accolade }}
+                </td>
+              </tr>
+              }
+            </tbody>
+          </nz-table>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">申报教材特色及创新</div>
+        <div class="base-content">
+          <div class="note">
+            {{ textBook.innovateExplain }}
+          </div>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">申报教材应用情况及社会影响力</div>
+        <div class="base-content">
+          <div class="note">
+            {{ textBook.influence }}
+          </div>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">附件材料</div>
+        <div class="base-content">
+          <div class="row">
+            <div class="label">教材电子版</div>
+            <div class="value">
+              @for (item of textBook.textbookFiles; track item.name) {
+              <a style="margin-right: 10px" [href]="item.url">{{
+                item.name
+              }}</a>
+              }
+            </div>
+          </div>
+          <div class="row">
+            <div class="label">教材数字内容链接地址、账号</div>
+            <div class="value">
+              @for (item of textBook.links; track item) {
+              <li>{{ item }}</li>
+              }
+            </div>
+          </div>
+          <div class="text-tips">所有作者政治审查意见</div>
+          <nz-table
+            #basicTable
+            [nzShowPagination]="false"
+            [nzData]="textBook.opinions"
+            [nzScroll]="{
+              x: (templ1.style.width || '1000') + 'px',
+              y: '240px'
+            }"
+          >
+            <thead>
+              <tr>
+                <th nzWidth="80px" nzLeft nzAlign="right">序号</th>
+                <th nzWidth="100px">作者</th>
+                <th nzWidth="100px">单位</th>
+                <th nzWidth="100px">出生年月</th>
+                <th nzWidth="100px">国籍</th>
+                <th nzWidth="100px" nzRight>作者政治审查表</th>
+              </tr>
+            </thead>
+            <tbody>
+              @for (data of textBook.opinions; track data.name;let index =
+              $index) {
+              <tr>
+                <td nzLeft nzAlign="right">{{ index + 1 }}</td>
+                <td>
+                  {{ data.name }}
+                </td>
+                <td>
+                  {{ data.unit }}
+                </td>
+                <td>
+                  {{ data.birth | date : "yyyy-MM" }}
+                </td>
+                <td>
+                  {{ data.nationality }}
+                </td>
+                <td nzRight>
+                  <a [href]="data.reviewFile.url">{{ data.reviewFile.name }}</a>
+                </td>
+              </tr>
+              }
+            </tbody>
+          </nz-table>
+          <div class="row" style="margin-top: 10px">
+            <div class="label">图书编校质量自查结果记录表</div>
+            <div class="value">
+              <a [href]="textBook.selfResults.url">{{
+                textBook.selfResults.name
+              }}</a>
+            </div>
+          </div>
+          <div class="row">
+            <div class="label">专家审核意见表</div>
+            <div class="value">
+              <a [href]="textBook.expertOpinion.url">{{
+                textBook.expertOpinion.name
+              }}</a>
+            </div>
+          </div>
+          <div class="row">
+            <div class="label">教材使用情况证明材料</div>
+            <div class="value">
+              <a [href]="textBook.evidence.url">{{ textBook.evidence.name }}</a>
+            </div>
+          </div>
+          <div class="row">
+            <div class="label">其他材料</div>
+            <div class="value">
+              @for (item of textBook.moreMaterial; track item.name) {
+              <a style="margin-right: 10px" [href]="item.url">{{
+                item.name
+              }}</a>
+              }
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">教材作者诚信承诺</div>
+        <div class="base-content">
+          <nz-table
+            #basicTable
+            [nzShowPagination]="false"
+            [nzData]="textBook.authorSign"
+            [nzScroll]="{
+              x: (templ1.style.width || '1000') + 'px',
+              y: '240px'
+            }"
+          >
+            <thead>
+              <tr>
+                <th nzWidth="80px" nzLeft nzAlign="right">序号</th>
+                <th nzWidth="100px">作者</th>
+                <th nzWidth="100px">单位</th>
+                <th nzWidth="100px">出生年月</th>
+                <th nzWidth="100px">国籍</th>
+                <th nzWidth="100px" nzRight>作者签名</th>
+              </tr>
+            </thead>
+            <tbody>
+              @for (data of textBook.authorSign; track data.name;let index =
+              $index) {
+              <tr>
+                <td nzLeft nzAlign="right">{{ index + 1 }}</td>
+                <td>
+                  {{ data.name }}
+                </td>
+                <td>
+                  {{ data.unit }}
+                </td>
+                <td>
+                  {{ data.birth?.iso | date : "yyyy-MM" }}
+                </td>
+                <td>
+                  {{ data.nationality }}
+                </td>
+                <td nzRight>
+                  <a [href]="data.reviewFile.url">{{ data.reviewFile.name }}</a>
+                </td>
+              </tr>
+              }
+            </tbody>
+          </nz-table>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">申报单位承诺意见</div>
+        <div class="base-content">
+          <div class="row">
+            <div class="label">申报单位承诺意见材料</div>
+            <div class="value">
+              <a style="margin-right: 10px" [href]="textBook.unitMaterial.url">{{textBook.unitMaterial.name}}</a>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="templ">
+        <div class="title">初评推荐意见</div>
+        <div class="base-content">
+          <div class="row">
+            <div class="label">省级教育行政部门初评专家组意见</div>
+            <div class="value">
+              <span style="color:#231C1F99">暂未填写</span>
+            </div>
+          </div>
+          <div class="row">
+            <div class="label">省级教育行政部门意见</div>
+            <div class="value">
+              <span style="color:#231C1F99">暂无</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div nz-col nzSpan="6">
+      <nz-anchor
+        style="position: fixed"
+        (nzClick)="handleClick($event)"
+        [nzShowInkInFixed]="true"
+        [nzShowInkInFixed]="true"
+      >
+        <nz-link nzHref="#templ1" nzTitle="教材基本信息"></nz-link>
+        <nz-link
+          nzHref="#components-anchor-demo-static"
+          nzTitle="教材适用情况"
+        ></nz-link>
+        <nz-link nzHref="#api" nzTitle="作者信息">
+          <nz-link nzHref="#nz-anchor" nzTitle="作者"></nz-link>
+          <nz-link nzHref="#nz-link" nzTitle="第一主编(作者)情况"></nz-link>
+        </nz-link>
+        <nz-link nzHref="#base" nzTitle="申报教材建设历程"></nz-link>
+        <nz-link nzHref="#base" nzTitle="申报教材特色及创新"></nz-link>
+        <nz-link
+          nzHref="#base"
+          nzTitle="申报教材应用情况及社会影响力"
+        ></nz-link>
+        <nz-link nzHref="#base" nzTitle="附件材料清单"></nz-link>
+        <nz-link nzHref="#base" nzTitle="教材作者诚信承诺"></nz-link>
+        <nz-link nzHref="#base" nzTitle="申报单位承诺意见"></nz-link>
+        <nz-link nzHref="#base" nzTitle="初评推荐意见"></nz-link>
+      </nz-anchor>
+    </div>
+  </div>
+</div>

+ 95 - 0
projects/textbook/src/modules/common/textbook-details/textbook-details.component.scss

@@ -0,0 +1,95 @@
+.site-page-header {
+  position: sticky;
+  top: 0;
+  z-index: 9;
+  padding: 24px 24px 10px;
+  background-color: white;
+  font-family: PingFang SC;
+  .steps {
+    max-width: 1000px;
+    margin: 10px 0;
+  }
+  .back {
+    font-family: PingFang SC;
+    font-size: 14px;
+    font-weight: 400;
+    line-height: 22px;
+    text-align: left;
+    cursor: pointer;
+  }
+}
+.content {
+  font-family: PingFang SC;
+  padding: 10px 24px;
+  .title {
+    font-family: PingFang SC;
+    font-size: 20px;
+    font-weight: 500;
+    line-height: 28px;
+    text-align: left;
+    margin-bottom: 20px;
+  }
+  .templ {
+    margin-top: 40px;
+    .text {
+      font-family: PingFang SC;
+      font-size: 16px;
+      font-weight: 500;
+      line-height: 22px;
+      text-align: left;
+    }
+  }
+  .base-content {
+    .row {
+      display: flex;
+      margin-bottom: 14px;
+      .label {
+        width: 220px;
+        font-family: PingFang SC;
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 20px;
+        text-align: left;
+        color: #231c1f99;
+      }
+      .value {
+        margin-left: 20px;
+        flex: 1;
+        font-family: PingFang SC;
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 20px;
+        text-align: left;
+      }
+    }
+    .text-tips {
+      font-family: PingFang SC;
+      font-size: 14px;
+      font-weight: 400;
+      line-height: 20px;
+      text-align: left;
+      color: #231c1f99;
+      margin: 10px 0;
+    }
+    .note{
+      font-family: PingFang SC;
+      font-size: 14px;
+      font-weight: 400;
+      line-height: 20px;
+      text-align: left;
+      color: #231C1F;
+    }
+  }
+}
+.mar10{
+  margin: 20px 0;
+}
+.footer{
+  display: flex;
+  margin: 10px auto;
+  align-items: center;
+  justify-content: space-evenly;
+  // width: 200px;
+  justify-content: end;
+  padding-right: 100px;
+}

+ 24 - 0
projects/textbook/src/modules/common/textbook-details/textbook-details.component.spec.ts

@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { TextbookDetailsComponent } from './textbook-details.component';
+
+describe('TextbookDetailsComponent', () => {
+  let component: TextbookDetailsComponent;
+  let fixture: ComponentFixture<TextbookDetailsComponent>;
+
+  beforeEach(waitForAsync(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TextbookDetailsComponent ],
+      imports: [IonicModule.forRoot()]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(TextbookDetailsComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 52 - 0
projects/textbook/src/modules/common/textbook-details/textbook-details.component.ts

@@ -0,0 +1,52 @@
+import {
+  Component,
+  OnInit,
+  ViewChild,
+} from '@angular/core';
+import { CommonCompModule } from '../../../services/common.modules';
+import { Router, ActivatedRoute } from '@angular/router';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NzAnchorModule } from 'ng-zorro-antd/anchor';
+import { DatePipe } from '@angular/common';
+import Parse from 'parse';
+@Component({
+  selector: 'app-textbook-details',
+  imports: [
+    CommonCompModule,
+    ReactiveFormsModule,
+    NzAnchorModule,
+    DatePipe,
+  ],
+  standalone: true,
+  templateUrl: './textbook-details.component.html',
+  styleUrls: ['./textbook-details.component.scss'],
+  providers: [DatePipe],
+})
+export class TextbookDetailsComponent implements OnInit {
+  @ViewChild('templ1') templ1: any;
+  textBook: object | any = {}
+  constructor(private router: Router, private activeRoute: ActivatedRoute) {}
+
+  ngOnInit() {
+    this.activeRoute.paramMap.subscribe(async (params) => {
+      let id = params.get('id');
+      if (id) {
+        let query = new Parse.Query('EduTextbook');
+        query.equalTo('objectId', id);
+        let r = await query.first();
+        this.textBook = r?.toJSON()
+        console.log(this.textBook);
+      }
+    });
+  }
+
+  handleClick(e: string): void {
+    console.log(e);
+  }
+  back() {
+    history.back();
+  }
+  edit(){
+    this.router?.navigate(['/nav-author/manage/apply',{id:this.textBook.objectId}],{ replaceUrl: true })
+  }
+}

+ 1 - 2
projects/textbook/src/modules/login/account-info/account-info.component.ts

@@ -4,8 +4,7 @@ import { HttpClient } from '@angular/common/http';
 import { Router } from '@angular/router';
 import { catchError } from 'rxjs/operators';
 import { NzUploadModule } from 'ng-zorro-antd/upload';
-import { NzModalModule } from 'ng-zorro-antd/modal';
-import { NzModalService } from 'ng-zorro-antd/modal';
+import { NzModalModule,NzModalService } from 'ng-zorro-antd/modal';
 import {
   FormControl,
   FormGroup,

+ 10 - 0
projects/textbook/src/modules/nav-admin/modules.routes.ts

@@ -10,6 +10,8 @@ import { PageUserComponent } from './page-user/page-user.component';
 import { ProcessCreateComponent } from './page-process/process-create/process-create.component';
 import { UserEditComponent } from './user-edit/user-edit.component';
 import { PageProcessComponent } from './page-process/page-process.component';
+import { TextbookDetailsComponent } from '../common/textbook-details/textbook-details.component';
+import { UserCreateComponent } from './page-user/user-create/user-create.component';
 const routes: Routes = [
   {
     path: '',
@@ -56,10 +58,18 @@ const routes: Routes = [
         path: 'user/edit', //用户管理&编辑
         component: UserEditComponent,
       },
+      {
+        path: 'user/create', //创建用户
+        component: UserCreateComponent,
+      },
       {
         path: 'role', //组织列表
         component: PageRoleComponent,
       },
+      {
+        path: 'textbook/details/:id', //教材详情
+        component: TextbookDetailsComponent,
+      },
     ]
   }
   

+ 1 - 2
projects/textbook/src/modules/nav-admin/page-process/process-create/process-create.component.html

@@ -53,7 +53,6 @@
               [nzNoColon]="true"
               [nzSm]="18"
               [nzXs]="18"
-              nzRequired
               >流程描述</nz-form-label
             >
             <nz-form-control
@@ -345,7 +344,7 @@
   nzCentered
 >
   <ng-container *nzModalContent>
-    <div class="rows" nz-row class="depart-modal">
+    <div nz-row class="depart-modal">
       <div nz-col nzSpan="24">
         <div class="row">
           <div class="label">选择部门</div>

+ 2 - 2
projects/textbook/src/modules/nav-admin/page-process/process-create/process-create.component.ts

@@ -54,7 +54,7 @@ export class ProcessCreateComponent implements OnInit {
     deadline: FormControl<Date>; //结束时间
   }> = this.fb.group({
     name: ['', [Validators.required]],
-    desc: ['', [Validators.required]],
+    desc: ['',],
     code: ['', [Validators.required]],
     num: ['',],
     branch: ['', [Validators.required]],
@@ -91,7 +91,7 @@ export class ProcessCreateComponent implements OnInit {
         this.eduProcess = await query.first();
         this.validateForm = this.fb.group({
           name: [this.eduProcess?.get('name') || '', [Validators.required]],
-          desc: [this.eduProcess?.get('desc') || '', [Validators.required]],
+          desc: [this.eduProcess?.get('desc') || '',],
           code: [this.eduProcess?.get('code') || '', [Validators.required]],
           num: [this.eduProcess?.get('num') || '', [Validators.required]],
           branch: [

+ 64 - 1
projects/textbook/src/modules/nav-admin/page-process/process-list/process-list.component.html

@@ -162,7 +162,7 @@
               {{ data?.get("desc") || "-" }}
             </td>
             <td nzEllipsis>
-              {{ data?.get("num ") || "-" }}
+              {{ data?.get("num") || "-" }}
             </td>
             <td nzEllipsis>
               {{
@@ -297,3 +297,66 @@
     </div>
   </div>
 </div>
+
+<nz-modal
+  [(nzVisible)]="isVisible"
+  nzTitle="新建组织"
+  (nzOnCancel)="handleCancel()"
+  nzWidth="600px"
+  nzCentered
+>
+  <ng-container *nzModalContent>
+    <div nz-row class="depart-modal">
+      <div nz-col nzSpan="12">
+        <div class="row">
+          <div class="label">
+            组织名称 <span style="color: #e8353e">*</span>
+          </div>
+          <div class="value">
+            <input
+              nz-input
+              placeholder="请输入组织名称"
+              [(ngModel)]="branchObj.name"
+              type="text"
+            />
+          </div>
+        </div>
+        <div class="row">
+          <div class="label">code</div>
+          <div class="value">
+            <input
+              nz-input
+              placeholder="请输入code"
+              [(ngModel)]="branchObj.code"
+              type="text"
+            />
+          </div>
+        </div>
+      </div>
+      <div nz-col nzSpan="12">
+        <div class="row">
+          <div class="label">组织描述</div>
+          <div class="value">
+            <textarea
+              [nzAutosize]="{ minRows: 4, maxRows: 4 }"
+              placeholder="请输入组织描述"
+              nz-input
+              [(ngModel)]="branchObj.desc"
+            ></textarea>
+          </div>
+        </div>
+      </div>
+    </div>
+  </ng-container>
+  <div *nzModalFooter>
+    <button nz-button nzType="default" (click)="handleCancel()">取消</button>
+    <button
+      nz-button
+      nzType="primary"
+      style="background: #3e49b3; border: 1px #3e49b3"
+      (click)="handleOk()"
+    >
+      确定
+    </button>
+  </div>
+</nz-modal>

+ 61 - 29
projects/textbook/src/modules/nav-admin/page-process/process-list/process-list.component.ts

@@ -171,6 +171,12 @@ export class ProcessListComponent implements OnInit {
     }
     return;
   };
+  /* 新建组织 */
+  branchObj: any = {
+    name: '',
+    code: '',
+    desc: '',
+  };
   constructor(
     private route: Router,
     private activeRoute: ActivatedRoute,
@@ -221,14 +227,14 @@ export class ProcessListComponent implements OnInit {
     this.nodes = await this.getDepart('', e);
   }
   onSearchPro(e: string) {
-    this.pageIndex = 1
+    this.pageIndex = 1;
     this.getEduProcess();
   }
   changeDepart(e: any) {
     this.currentDepart = e;
     this.setOfCheckedId = new Set<string>();
     this.checkedAll = false;
-    this.pageIndex = 1
+    this.pageIndex = 1;
     this.getEduProcess();
   }
 
@@ -272,10 +278,10 @@ export class ProcessListComponent implements OnInit {
     this.loading = false;
   }
   //分页切换
-  pageIndexChange(e:any){
+  pageIndexChange(e: any) {
     console.log(e);
-    this.pageIndex = e
-    this.getEduProcess()
+    this.pageIndex = e;
+    this.getEduProcess();
   }
   onAllChecked(checked: boolean): void {
     console.log(checked);
@@ -356,9 +362,30 @@ export class ProcessListComponent implements OnInit {
     });
     this.parentList = await this.getDepart(e?.key);
   }
-  handleOk(): void {
-    this.message.warning('权限灰度中');
+
+  /* 组织 */
+  showModalOrganize() {
+    // this.message.warning('权限灰度中');
+    this.branchObj = {
+      name: '',
+      code: '',
+      desc: '',
+    };
+    this.isVisible = true;
+  }
+  async handleOk():Promise<void> {
+    if(!this.branchObj?.name){
+      this.message.warning('组织名称不能为空');
+    }
+    let obj = Parse.Object.extend('Department')
+    let depart = new obj()
+    depart.set('name',this.branchObj?.name)
+    depart.set('desc',this.branchObj?.desc)
+    depart.set('code',this.branchObj?.code)
+    await depart.save()
     this.isVisible = false;
+    this.message.success('新建成功');
+    this.nodes = await this.getDepart();
   }
 
   handleCancel(): void {
@@ -368,19 +395,18 @@ export class ProcessListComponent implements OnInit {
     this.parentMap = [];
   }
 
-  /* 组织 */
-  showModalOrganize() {
-    this.message.warning('权限灰度中');
-  }
-  statusSelected(type:string) {
-    let map:any = {
-      'strat':'开始',
-      'stop':'暂停',
-      'end':'结束',
-    }
+  statusSelected(type: string) {
+    let map: any = {
+      strat: '开始',
+      stop: '暂停',
+      end: '结束',
+    };
     this.modal.confirm({
       nzTitle: '批量删除',
-      nzContent:type == 'del' ? `删除后数据不可恢复,请谨慎操作` : `确认批量${map[type]}吗`,
+      nzContent:
+        type == 'del'
+          ? `删除后数据不可恢复,请谨慎操作`
+          : `确认批量${map[type]}吗`,
       nzOkText: '确认',
       nzOkType: 'primary',
       nzOkDanger: map[type] ? true : false,
@@ -390,12 +416,12 @@ export class ProcessListComponent implements OnInit {
         );
         let deletePromiseList = selectedList.map((item: any) => {
           return new Promise((resolve) => {
-            resolve(this.onStatusChange(item,type))
+            resolve(this.onStatusChange(item, type));
           });
         });
         try {
           await Promise.all(deletePromiseList);
-          this.getEduProcess()
+          this.getEduProcess();
         } catch (err) {}
       },
       nzCancelText: '取消',
@@ -410,18 +436,25 @@ export class ProcessListComponent implements OnInit {
     }
   }
   //暂停流程
-  async onStatusChange(data: Parse.Object, type: string, end?:boolean):Promise<void> {
+  async onStatusChange(
+    data: Parse.Object,
+    type: string,
+    end?: boolean
+  ): Promise<void> {
     console.log(data, type);
     switch (type) {
       case 'strat':
-        if(data?.get('status') == '100'){
+        if (data?.get('status') == '100') {
           data?.set('status', null);
         }
-        if(data?.get('startDate') > new Date()){
+        if (data?.get('startDate') > new Date()) {
           data?.set('startDate', new Date());
         }
-        if(data?.get('deadline') < new Date()){
-          data?.set('deadline', new Date(new Date().getTime() + 60 * 1000 * 60 * 24 * 7));
+        if (data?.get('deadline') < new Date()) {
+          data?.set(
+            'deadline',
+            new Date(new Date().getTime() + 60 * 1000 * 60 * 24 * 7)
+          );
           console.warn('结束时间延长一周之后');
         }
         break;
@@ -436,13 +469,12 @@ export class ProcessListComponent implements OnInit {
         data?.set('isDeleted', true);
         break;
     }
-    await data.save()
-    if(end){
-     this.getEduProcess()
+    await data.save();
+    if (end) {
+      this.getEduProcess();
     }
   }
 
-
   /* 批量预设(临时) */
   // async saveProcess(){
   //   let count = 0

+ 74 - 31
projects/textbook/src/modules/nav-admin/page-role/page-role.component.html

@@ -153,33 +153,33 @@
           </tr>
         </thead>
         <tbody>
-          @for (data of profiles; track data.objectId) {
+          @for (data of profiles; track data.id) {
           <tr>
             <td
               nzLeft
-              [nzChecked]="data.checked"
-              (nzCheckedChange)="onItemChecked(data.objectId, $event)"
+              [nzChecked]="setOfCheckedId.has(data.id)"
+              (nzCheckedChange)="onItemChecked(data.id, $event)"
             ></td>
             <td
               nzLeft
               nzEllipsis
-              (click)="goDateil(data.user?.objectId)"
+              (click)="goDateil(data.get('user')?.id)"
               class="activeTd"
             >
               <nz-avatar nzIcon="user"></nz-avatar>
-              {{ data?.user.name }}
+              {{ data?.get('user')?.get('name') }}
             </td>
             <td>
-              {{ data?.user.phone }}
+              {{ data?.get('user')?.get('phone') }}
             </td>
             <td>
-              {{ data?.user.email || data?.email }}
+              {{ data?.get('user')?.get('email')  || data?.get('email') }}
             </td>
             <td>
-              {{ data?.identity }}
+              {{ data?.get('identity') }}
             </td>
             <td>
-              {{ data?.user.departmentName }}
+              {{ data?.get('user').get('departmentName') }}
             </td>
             <td nzRight>
               <button
@@ -193,7 +193,7 @@
               <nz-dropdown-menu #menu="nzDropdownMenu">
                 <ul nz-menu>
                   <li nz-menu-item>
-                    <button nz-button style="color: #231c1f" nzType="link">
+                    <button (click)="removeBranch(data)" nz-button style="color: #231c1f" nzType="link">
                       <span nz-icon nzType="stop" nzTheme="outline"></span
                       >移除部门
                     </button>
@@ -222,7 +222,9 @@
     <div nz-row class="depart-modal">
       <div nz-col nzSpan="12">
         <div class="row">
-          <div class="label">部门名称 <span style="color: #e8353e">*</span></div>
+          <div class="label">
+            部门名称 <span style="color: #e8353e">*</span>
+          </div>
           <div class="value">
             <input
               nz-input
@@ -257,13 +259,15 @@
       </div>
       <div nz-col nzSpan="12">
         <div class="row">
-          <div class="label">上级部门 <span style="color: #e8353e">*</span></div>
+          <div class="label">
+            上级部门 <span style="color: #e8353e">*</span>
+          </div>
           <div class="value">
             <input
               nz-input
               placeholder="可击下方选择所属上级"
               [ngModel]="editObject.parent?.title"
-              (ngModelChange)="onSearchNodes($event,true)"
+              (ngModelChange)="onSearchNodes($event, true)"
               type="text"
             />
           </div>
@@ -278,12 +282,12 @@
           </div>
           <div class="tree">
             @for (data of parentList; track $index) {
-            <div class="li" (click)="onCheckedDepart(data)">
+            <div class="li" (click)="onCheckedDepart('branch', data)">
               <label
                 nz-radio
                 [ngModel]="data.key == radio"
                 [nzValue]="data.key"
-                (click)="onCheckedDepart(data, true)"
+                (click)="onCheckedDepart('branch', data, true)"
                 >{{ data.title }}</label
               >
               @if (!data.isLeaf) {
@@ -335,6 +339,7 @@
           <div class="value">
             <input
               nz-input
+              maxlength="12"
               placeholder="请输入手机号"
               [(ngModel)]="account.phone"
               type="text"
@@ -348,42 +353,57 @@
               nz-input
               placeholder="请输入邮箱"
               [(ngModel)]="account.email"
-              type="text"
+              type="email"
             />
           </div>
         </div>
         <div class="row">
           <div class="label">密码 <span style="color: #e8353e">*</span></div>
           <div class="value">
-            <input
-              nz-input
-              placeholder="请输入密码"
-              [(ngModel)]="account.password"
-              type="text"
-            />
+            <nz-input-group nzSearch [nzAddOnAfter]="suffixIconButton">
+              <input type="password" [(ngModel)]="account.password" nz-input placeholder="请输入密码" />
+            </nz-input-group>
+            <ng-template #suffixIconButton>
+              <button nz-button (click)="randomPassword()" nzType="primary" nzSearch>自动生成密码</button>
+            </ng-template>
           </div>
         </div>
         <div class="row">
           <div class="label">人员类型</div>
           <div class="value">
-            <input
+            <!-- <input
               nz-input
               placeholder="请选择人员类型"
-              [(ngModel)]="account.companyType"
+              [(ngModel)]="account.identity"
               type="text"
-            />
+            /> -->
+            <nz-select
+              style="width: 100%"
+              nzShowSearch
+              nzAllowClear
+              [(ngModel)]="account.identity"
+              nzPlaceHolder="请选择所属的身份类型"
+            >
+              @for(item of userType; track item;let index = $index){
+              <nz-option nzCustomContent [nzValue]="item" [nzLabel]="item">{{
+                item
+              }}</nz-option>
+              }
+            </nz-select>
           </div>
         </div>
       </div>
       <div nz-col nzSpan="12">
         <div class="row">
-          <div class="label">上级部门 <span style="color: #e8353e">*</span></div>
+          <div class="label">
+            上级部门 <span style="color: #e8353e">*</span>
+          </div>
           <div class="value">
             <input
               nz-input
               placeholder="请选择所属部门"
-              [ngModel]="account.department"
-              (ngModelChange)="onSearchNodes($event,true)"
+              [ngModel]="account.department?.title"
+              (ngModelChange)="onSearchNodes($event, true)"
               type="text"
             />
           </div>
@@ -398,12 +418,12 @@
           </div>
           <div class="tree">
             @for (data of parentList; track $index) {
-            <div class="li" (click)="onCheckedDepart(data)">
+            <div class="li" (click)="onCheckedDepart('account', data)">
               <label
                 nz-radio
                 [ngModel]="data.key == radio"
                 [nzValue]="data.key"
-                (click)="onCheckedDepart(data, true)"
+                (click)="onCheckedDepart('account', data, true)"
                 >{{ data.title }}</label
               >
               @if (!data.isLeaf) {
@@ -422,9 +442,32 @@
       nz-button
       nzType="primary"
       style="background: #3e49b3; border: 1px #3e49b3"
-      (click)="handleOk()"
+      (click)="accountComplete()"
     >
       确定
     </button>
   </div>
 </nz-modal>
+
+<!-- 全选操作:批量操作 -->
+<div class="batch-toolbar-modal" *ngIf="setOfCheckedId.size">
+  <div class="batch-toolbar">
+    <div class="styles_counter__18S08">
+      <span>已选</span>
+      <span class="styles_num__178Wa">{{ setOfCheckedId.size }}</span>
+    </div>
+    <div class="batch-toolbar-actions">
+      <div class="ant-space ant-space-horizontal ant-space-align-center">
+        <div class="ant-space-item" style="margin-right: 16px">
+          <button nz-button nzType="text" (click)="removeBranchAll()">
+            <span nz-icon nzType="delete"></span>
+            移除部门
+          </button>
+        </div>
+      </div>
+    </div>
+    <div class="styles_cancel__AARoT">
+      <button nz-button nzType="text" (click)="resetChange()">取消选中</button>
+    </div>
+  </div>
+</div>

+ 60 - 0
projects/textbook/src/modules/nav-admin/page-role/page-role.component.scss

@@ -94,6 +94,66 @@
     }
   }
 }
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active {
+  -webkit-transition-delay: 99999s;
+  -webkit-transition: color 99999s ease-out, background-color 99999s ease-out;
+}
+// 选中,批量操作区域
+.batch-toolbar-modal{
+  position: fixed;
+  display: flex;
+  justify-content: center;
+  bottom: 80px;
+  left: calc(50% - 134px);
+  -webkit-transform: translate(0);
+  transform: translate(0);
+}
+.batch-toolbar {
+  display: flex;
+  align-items: center;
+  height: 56px;
+  // min-width: 600px;
+  background: #fff;
+  border: 1px solid #e5e6eb;
+  box-sizing: border-box;
+  box-shadow: 0 16px 32px -10px rgba(4, 24, 115, .1);
+  border-radius: 4px;
+  button{
+      color:#545968;
+  }
+}
+.batch-toolbar .styles_counter__18S08 {
+  color: #fff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  background: #C6233F;
+  width: 100px;
+  font-size: 12px;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+.batch-toolbar .batch-toolbar-actions {
+  display: flex;
+  margin: auto;
+  padding: 0 20px;
+}
+.batch-toolbar .styles_cancel__AARoT {
+  font-size: 16px;
+  border-left: 1px solid #a9aeb8;
+  // padding-left: 20px;
+  display: flex;
+  justify-content: center;
+  width: 128px;
+}
+.batch-toolbar .styles_counter__18S08 .styles_num__178Wa {
+  font-size: 24px;
+  margin-left: 8px;
+}
 ::ng-deep .ant-page-header-heading-title{
   white-space: normal;
 }

+ 207 - 81
projects/textbook/src/modules/nav-admin/page-role/page-role.component.ts

@@ -24,6 +24,7 @@ import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
 import { NzEmptyModule } from 'ng-zorro-antd/empty';
 import { NzRadioModule } from 'ng-zorro-antd/radio';
 import { NzMessageService } from 'ng-zorro-antd/message';
+import { NzSelectModule } from 'ng-zorro-antd/select';
 interface nodes {
   title: string;
   key: string;
@@ -56,6 +57,7 @@ interface depart {
     NzEmptyModule,
     NzModalModule,
     NzRadioModule,
+    NzSelectModule,
   ],
   standalone: true,
 })
@@ -73,7 +75,7 @@ export class PageRoleComponent implements OnInit {
   nodes: Array<nodes | any> = [];
   currentDepart: nodes | any = null;
 
-  profiles: Array<any> = [];
+  profiles: Array<Parse.Object> = [];
   checkedShowFilter: boolean = false;
 
   checkedAll: boolean = false; //全选
@@ -103,9 +105,10 @@ export class PageRoleComponent implements OnInit {
     email: '',
     password: '',
     identity: '',
-    department: '',
+    department: {},
     companyType: '',
   };
+  userType: Array<string> = ['工作联系人', '评审专家', '高校联系人', '个人'];
 
   constructor(
     private route: Router,
@@ -173,20 +176,6 @@ export class PageRoleComponent implements OnInit {
     }
     this.nodes = await this.getDepart('', e, e ? true : false);
   }
-  //添加成员
-  addMember() {
-    // this.message.warning('权限灰度中')
-    this.account = {
-      name: '',
-      phone: '',
-      email: '',
-      password: '',
-      identity: '',
-      department: '',
-      companyType: '',
-    };
-    this.accountIsVisible = true;
-  }
   //展开/合并
   async nzEvent(event: NzFormatEmitEvent): Promise<void> {
     console.log(event);
@@ -236,13 +225,13 @@ export class PageRoleComponent implements OnInit {
     query.notEqualTo('identity', '国家级管理员');
     query.notEqualTo('isDeleted', true);
     let r = await query.find();
-    let profiles: any[] = [];
-    r.forEach((item) => {
-      let _item = item.toJSON();
-      _item['checked'] = false;
-      profiles.push(_item);
-    });
-    this.profiles = profiles;
+    // let profiles: any[] = [];
+    // r.forEach((item) => {
+    //   let _item = item.toJSON();
+    //   _item['checked'] = false;
+    //   profiles.push(_item);
+    // });
+    this.profiles = r;
     this.loading = false;
   }
   //搜索触发
@@ -262,24 +251,24 @@ export class PageRoleComponent implements OnInit {
   async onDelDepart() {
     this.modal.confirm({
       nzTitle: '删除',
-      nzContent:'删除后数据不可恢复,请谨慎操作',
+      nzContent: '删除后数据不可恢复,请谨慎操作',
       nzOkText: '确认',
       nzOkType: 'primary',
       nzOkDanger: true,
       nzOnOk: async () => {
-        new Promise(async(resolve, reject) => {
-          if(this.activatedNode){
+        new Promise(async (resolve, reject) => {
+          if (this.activatedNode) {
             let query = new Parse.Query('Department');
             let r = await query.get(this.activatedNode?.key);
-            if(r?.id){
-              r.set('isDeleted',true)
-              await r.save()
+            if (r?.id) {
+              r.set('isDeleted', true);
+              await r.save();
             }
-            this.message.success('删除成功')
+            this.message.success('删除成功');
             this.nodes = await this.getDepart();
-            resolve(true)
+            resolve(true);
           }
-        }).catch(() => console.log('Oops errors!'))
+        }).catch(() => console.log('Oops errors!'));
       },
       nzCancelText: '取消',
       nzOnCancel: () => console.log('Cancel'),
@@ -287,21 +276,38 @@ export class PageRoleComponent implements OnInit {
   }
 
   onAllChecked(checked: boolean): void {
-    console.log(checked);
-    this.profiles = this.profiles.map((item) => {
-      item.checked = checked;
-      return item;
+    this.profiles.forEach((item) => {
+      if (checked) {
+        this.setOfCheckedId.add(item.id);
+      } else {
+        this.setOfCheckedId.delete(item.id);
+      }
     });
     this.checkedAll = checked;
   }
   onItemChecked(id: string, e: boolean) {
-    let checkedAll = true;
-    this.profiles = this.profiles.map((item) => {
-      if (id == item.objectId) item.checked = e;
-      if (!item.checked) checkedAll = false;
-      return item;
-    });
-    this.checkedAll = checkedAll;
+    if (e) {
+      this.setOfCheckedId.add(id);
+    } else {
+      this.setOfCheckedId.delete(id);
+    }
+    this.checkedAll = this.profiles.every((item) =>
+      this.setOfCheckedId.has(item.id)
+    );
+  }
+  //添加成员
+  addMember() {
+    this.parentList = this.nodes;
+    this.account = {
+      name: '',
+      phone: '',
+      email: '',
+      password: '',
+      identity: '',
+      department: {},
+      companyType: '',
+    };
+    this.accountIsVisible = true;
   }
   //新建打开弹窗
   async showModalDepart(type: string) {
@@ -327,19 +333,22 @@ export class PageRoleComponent implements OnInit {
         },
         branch: this.activeDepart.get('branch'),
       };
-      this.parentMap = await this.formatNode(this.activeDepart.get('parent')?.id);
+      this.parentMap = await this.formatNode(
+        this.activeDepart.get('parent')?.id
+      );
       if (this.activatedNode?.parentNode?.origin?.parentNode?.origin.key) {
         this.parentList = await this.getDepart(
           this.activatedNode?.parentNode?.origin?.parentNode?.origin.key
         );
       }
-    }else if(type == 'add' && this.activatedNode){
+    } else if (type == 'add' && this.activatedNode) {
       console.log(this.activatedNode);
       this.editObject.parent = {
-          title: this.activatedNode.origin.title,
-          id: this.activatedNode.origin.key,
+        title: this.activatedNode.origin.title,
+        id: this.activatedNode.origin.key,
       };
-      this.editObject.branch = this.activatedNode.origin.branch || this.activatedNode.origin.title
+      this.editObject.branch =
+        this.activatedNode.origin.branch || this.activatedNode.origin.title;
       this.parentMap = await this.formatNode(this.activatedNode.origin.key);
       if (this.activatedNode?.parentNode?.origin.key) {
         this.parentList = await this.getDepart(
@@ -378,60 +387,65 @@ export class PageRoleComponent implements OnInit {
     this.parentList = await this.getDepart(data?.key);
   }
   //选择所属类别下级列表
-  async onCheckedDepart(e: any, checked?: boolean) {
+  async onCheckedDepart(type: string, e: any, checked?: boolean) {
     this.radio = e.key;
     console.log(this.radio);
     this.parentMap = await this.formatNode(e.key);
     if (checked) {
       // this.editObject.name = e.title
-      this.editObject.parent = {
-        title: e.title,
-        id: e.key,
-      };
-      this.editObject.branch = e.branch || e.title;
+      if (type == 'account') {
+        this.account.department = { title: e.title, id: e.key };
+        this.account.companyType = e.branch || e.title;
+      } else {
+        this.editObject.parent = {
+          title: e.title,
+          id: e.key,
+        };
+        this.editObject.branch = e.branch || e.title;
+      }
       return;
     }
     if (e.isLeaf) {
       return;
     }
-    this.parentMap.push({
-      title: e.title,
-      key: e.key,
-    });
+    // this.parentMap.push({
+    //   title: e.title,
+    //   key: e.key,
+    // });
     this.parentList = await this.getDepart(e?.key);
   }
-  async handleOk():Promise<void> {
-    if(!this.editObject?.name || !this.editObject.parent?.id){
-      this.message.error('请填写完整信息')
-      return
+  async handleOk(): Promise<void> {
+    if (!this.editObject?.name || !this.editObject.parent?.id) {
+      this.message.error('请填写完整信息');
+      return;
     }
-    if(this.activeDepart?.id && this.editType == 'edit'){
-      this.activeDepart.set('name',this.editObject?.name)
-      this.activeDepart.set('code',this.editObject?.code)
-      this.activeDepart.set('desc',this.editObject.desc)
-      this.activeDepart.set('parent',{
+    if (this.activeDepart?.id && this.editType == 'edit') {
+      this.activeDepart.set('name', this.editObject?.name);
+      this.activeDepart.set('code', this.editObject?.code);
+      this.activeDepart.set('desc', this.editObject.desc);
+      this.activeDepart.set('parent', {
         __type: 'Pointer',
         className: 'Department',
         objectId: this.editObject.parent?.id,
-      })
-      this.activeDepart.set('branch',this.editObject.branch)
-    }else{
-      let obj = Parse.Object.extend('Department')
-      this.activeDepart = new obj()
-      this.activeDepart?.set('name',this.editObject?.name)
-      this.activeDepart?.set('code',this.editObject?.code)
-      this.activeDepart?.set('desc',this.editObject.desc)
-      this.activeDepart?.set('parent',{
+      });
+      this.activeDepart.set('branch', this.editObject.branch);
+    } else {
+      let obj = Parse.Object.extend('Department');
+      this.activeDepart = new obj();
+      this.activeDepart?.set('name', this.editObject?.name);
+      this.activeDepart?.set('code', this.editObject?.code);
+      this.activeDepart?.set('desc', this.editObject.desc);
+      this.activeDepart?.set('parent', {
         __type: 'Pointer',
         className: 'Department',
         objectId: this.editObject.parent?.id,
-      })
-      this.activeDepart?.set('branch',this.editObject.branch)
+      });
+      this.activeDepart?.set('branch', this.editObject.branch);
     }
-    await this.activeDepart?.save()
+    await this.activeDepart?.save();
     this.isVisible = false;
-    this.message.success(this.editType == 'edit' ? '保存' : '添加' + '成功')
-    this.activeDepart = undefined
+    this.message.success(this.editType == 'edit' ? '保存' : '添加' + '成功');
+    this.activeDepart = undefined;
     this.nodes = await this.getDepart();
   }
 
@@ -452,4 +466,116 @@ export class PageRoleComponent implements OnInit {
   goDateil(id: string) {
     this.route.navigate(['/nav-admin/manage/user/edit', { id: id }]);
   }
+
+  randomPassword() {
+    let sCode =
+      'A,B,C,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,W,X,Y,Z,1,2,3,4,5,6,7,8,9,0,q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m';
+    let aCode = sCode.split(',');
+    let aLength = aCode.length; //获取到数组的长度
+    let str = []
+    for (let i = 0; i <= 16; i++) {
+      let j = Math.floor(Math.random() * aLength); //获取到随机的索引值
+      let txt = aCode[j]; //得到随机的一个内容
+      str.push(txt)
+    }
+    this.account.password = str.join('')
+    console.log(this.account.password);
+  }
+  /* 添加账号 */
+  async accountComplete(){
+    if(!this.account?.name.trim() || !this.account.department?.id || !this.account.password.trim()){
+      this.message.warning('请填写必填项');
+      return
+    }
+    if(!this.account.identity){
+      this.message.error("请选择人员类型");
+      return;
+    }
+    let a = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/
+    if(this.account.phone && !String(this.account.phone).match(a)){
+      this.message.error("请填写正确手机号");
+      return;
+    }
+    try{
+      let obj = Parse.Object.extend('_User');
+      let user = new obj()
+      user?.set('username', this.account?.email);
+      user?.set('name', this.account?.name);
+      user?.set('phone', this.account?.phone);
+      user?.set('email', this.account?.email);
+      user?.set('password', this.account.password);
+      user?.set('accountState', '已认证');
+      user?.set('department', {
+        __type: 'Pointer',
+        className: 'Department',
+        objectId: this.account.department?.id,
+      });
+      let u = await user.save()
+      let p = Parse.Object.extend('Profile');
+      let profile = new p()
+      profile?.set('user', u?.toPointer());
+      profile?.set('companyType', this.account.companyType);
+      profile?.set('email', this.account.email);
+      profile?.set('identity', this.account.identity);
+      let res = await profile?.save();
+      this.accountIsVisible = false;
+      this.account = null;
+      this.modal.success({
+        nzTitle: '添加成功',
+        nzContent: '',
+        nzOnOk: () =>{
+          this.currentDepart && this.getProfile()
+        },
+      });
+    }
+    catch(err:any){
+      console.warn('添加用户错误',err);
+      this.message.error(err?.Error || '错误:请检查用户邮箱是否已存在');
+      return;
+    }
+  }
+  setOfCheckedId = new Set<string>();
+  //移除部门
+  async removeBranch(data:Parse.Object){
+    if(data?.get('user')?.id){
+      data?.get('user')?.set('department',null)
+      await data?.get('user')?.save()
+      this.message.error("移除成功");
+      this.getProfile();
+    }
+  }
+  removeBranchAll(){
+    this.modal.confirm({
+      nzTitle: '批量移除',
+      nzContent: `请谨慎操作`,
+      nzOkText: '确认',
+      nzOkType: 'primary',
+      nzOkDanger: true,
+      nzOnOk: async () => {
+        let selectedList = this.profiles.filter((item: any) =>
+          this.setOfCheckedId.has(item?.id)
+        );
+        let romovePromiseList = selectedList.map((item: any) => {
+          return new Promise(async (resolve) => {
+            item?.get('user')?.set('department',null)
+            await item?.get('user')?.save()
+            resolve(true)
+          });
+        });
+        try {
+          await Promise.all(romovePromiseList);
+          this.message.error("移除成功");
+          this.getProfile();
+          this.resetChange()
+        } catch (err) {}
+      },
+      nzCancelText: '取消',
+      nzOnCancel: () => console.log('Cancel'),
+    });
+  }
+  resetChange(){
+    this.setOfCheckedId = new Set<string>();
+    this.checkedAll = false;
+  }
+
 }

+ 16 - 15
projects/textbook/src/modules/nav-admin/page-user/page-user.component.html

@@ -72,14 +72,15 @@
     #tableData
     [nzData]="profiles"
     [nzTotal]="profileLength"
-    [nzTotal]="profiles.length"
     [nzPageSize]="pageSize"
     [nzPageIndex]="pageIndex"
     style="margin: 10px 0"
     [nzLoading]="loading"
     nzSize="middle"
+    [nzFrontPagination]="false"
     [nzNoResult]="emptyResult"
     nzTableLayout="fixed"
+    (nzPageIndexChange)="pageIndexChange($event)"
   >
     <thead>
       <tr>
@@ -91,7 +92,7 @@
           nzLabel="Select all"
           (nzCheckedChange)="onAllChecked($event)"
         ></th>
-        <th nzWidth="120px" nzLeft>用户</th>
+        <th nzWidth="120px">用户</th>
         <th nzWidth="120px">手机号</th>
         <th nzWidth="120px">邮箱</th>
         <th nzWidth="120px">人员类型</th>
@@ -101,38 +102,38 @@
       </tr>
     </thead>
     <tbody>
-      @for (data of profiles; track data.objectId) {
+      @for (data of profiles; track data.id) {
       <tr>
         <td
           nzLeft
-          [nzChecked]="setOfCheckedId.has(data.user.objectId)"
+          [nzChecked]="setOfCheckedId.has(data.id)"
           (nzCheckedChange)="
-            onItemChecked(data.objectId, data.user.objectId, $event)
+            onItemChecked(data.id,$event)
           "
         ></td>
-        <td nzLeft nzEllipsis (click)="goDateil(data.user?.objectId)" class="activeTd">
+        <td nzEllipsis (click)="goDateil(data.get('user')?.id)" class="activeTd">
           <nz-avatar nzIcon="user"></nz-avatar>
-          {{ data?.user.name }}
+          {{ data?.get('user')?.get('name') || '-'  }}
         </td>
         <td>
-          {{ data?.user.phone }}
+          {{ data?.get('user')?.get('phone') || '-' }}
         </td>
         <td nzEllipsis>
           <div class="email">
             <span
               class="state"
               [style.background]="
-                data?.user.accountState == '已认证' ? '#1EB76D' : '#C9CDD4'
+                data?.get('user')?.get('accountState') == '已认证' ? '#1EB76D' : '#C9CDD4'
               "
             ></span>
-            <div class="text">{{ data?.user.email || data?.email }}</div>
+            <div class="text">{{ data?.get('user')?.get('email') || data?.get('email') }}</div>
           </div>
         </td>
         <td>
-          {{ data?.identity }}
+          {{ data?.get('identity') }}
         </td>
         <td>
-          @switch (data?.user.accountState) { @case ('待认证') {
+          @switch (data?.get('user')?.get('accountState')) { @case ('待认证') {
           <nz-tag [nzBordered]="false" [nzColor]="'geekblue'">待认证</nz-tag>
           } @case ('已认证') {
           <nz-tag [nzBordered]="false" [nzColor]="'success'">已认证</nz-tag>
@@ -141,7 +142,7 @@
           } }
         </td>
         <td nzEllipsis>
-          {{ data?.companyType }}
+          {{ data?.get('companyType') }}
         </td>
         <td nzRight>
           <button
@@ -154,7 +155,7 @@
           </button>
           <nz-dropdown-menu #menu="nzDropdownMenu">
             <ul nz-menu>
-              @if (data?.user.accountState == '待认证') {
+              @if (data?.get('user')?.get('accountState') == '待认证') {
               <li nz-menu-item>
                 <button
                   nz-button
@@ -240,7 +241,7 @@
       </div>
     </div>
     <div class="styles_cancel__AARoT">
-      <button nz-button nzType="text" (click)="onAllChecked(false)">取消选中</button>
+      <button nz-button nzType="text" (click)="resetChange()">取消选中</button>
     </div>
   </div>
 </div>

+ 104 - 79
projects/textbook/src/modules/nav-admin/page-user/page-user.component.ts

@@ -37,10 +37,10 @@ export class PageUserComponent implements OnInit {
   // queryParams: any | undefined;
   // fieldsArray: Array<any> | undefined;
   user: Parse.User | undefined;
-  profiles: Array<any> = [];
-  profileLength:number = 0 //总数据
-  pageSize:number = 10
-  pageIndex:number = 1
+  profiles: Array<Parse.Object> = [];
+  profileLength: number = 0; //总数据
+  pageSize: number = 10;
+  pageIndex: number = 1;
   checkedAll: boolean = false; //全选
   indeterminate = false;
   loading = false;
@@ -51,7 +51,7 @@ export class PageUserComponent implements OnInit {
     public tbookSer: textbookServer,
     private modal: NzModalService,
     private route: Router,
-    private activeRoute: ActivatedRoute,
+    private activeRoute: ActivatedRoute
   ) {
     this.user = Parse.User.current();
     // this.className = this.ProfileList.className;
@@ -66,71 +66,97 @@ export class PageUserComponent implements OnInit {
 
   ngOnInit(): void {
     this.activeRoute.paramMap.subscribe(async (params) => {
-      this.getProfile()
+      this.getProfile();
     });
   }
   async getProfile() {
     this.profiles = [];
     this.loading = true;
-    let queryParams:any = {
-      where : {
-        "$or": [{
-          "user": {
-            "$inQuery": {
-              "where": {
-                "$or": [{
-                  "name": { "$regex": `.*${this.searchValue}.*`},
-                  },
-                ],
+    let queryParams: any = {
+      where: {
+        $or: [
+          {
+            user: {
+              $inQuery: {
+                where: {
+                  $or: [
+                    {
+                      name: { $regex: `.*${this.searchValue}.*` },
+                    },
+                    {
+                      username: { $regex: `.*${this.searchValue}.*` },
+                    },
+                    {
+                      phone: { $regex: `.*${this.searchValue}.*` },
+                    },
+                  ],
+                },
+                className: '_User',
               },
-              "className": "_User"
-            }
+            },
           }
-        }]
-      }
-    }
-    if(this.tbookSer.profile.identity != '国家级管理员' && this.tbookSer.profile.user.department){
-      this.tbookSer.profile.user.department
-      queryParams['where']['$or'][0]['user']['$inQuery']['where']['department'] = {
-        "$eq": this.tbookSer.profile.user.department?.objectId
-      }
+        ],
+      },
+    };
+    if (
+      this.tbookSer.profile.identity != '国家级管理员'
+    ) {
+      this.tbookSer.profile.user.department;
+      queryParams['where']['$or'][0]['user']['$inQuery']['where'][
+        'department'
+      ] = {
+        $eq: this.tbookSer.profile.user.department?.objectId,
+      };
     }
-    let query = Parse.Query.fromJSON('Profile',queryParams);
+    let query = Parse.Query.fromJSON('Profile', queryParams);
     query.include('user');
     query.notEqualTo('identity', '国家级管理员');
-    this.profileLength = await query.count()
-    query.limit(this.pageSize)
-    query.skip((this.pageIndex - 1) * this.pageSize)
+    if(this.tbookSer.profile.identity == '工作联系人'){
+      query.containedIn('identity', ['个人', '评审专家','高校联系人']);
+    }else if(this.tbookSer.profile.identity == '高校联系人'){
+      query.containedIn('identity', ['个人','评审专家']);
+    }
+    this.profileLength = await query.count();
+    query.limit(this.pageSize);
+    query.skip((this.pageIndex - 1) * this.pageSize);
     let r = await query.find();
-    let profiles: any[] = [];
-    r.forEach((item) => {
-      let _item = item.toJSON();
-      _item['checked'] = false;
-      profiles.push(_item);
-    });
-    this.profiles = profiles;
+    this.profiles = r;
     this.loading = false;
   }
-  createUser() {}
-  onItemChecked(id: string,user:string, e: boolean) {
-    if(e){
-      this.setOfCheckedId.add(user)
-    }else{
-      this.setOfCheckedId.delete(user)
+  //分页切换
+  pageIndexChange(e: any) {
+    console.log(e);
+    this.pageIndex = e;
+    this.getProfile();
+  }
+  createUser() {
+    this.route.navigate(['/nav-admin/manage/user/create'])
+  }
+  onItemChecked(id: string, e: boolean) {
+    if (e) {
+      this.setOfCheckedId.add(id);
+    } else {
+      this.setOfCheckedId.delete(id);
     }
-    this.checkedAll = this.profiles.every((item) => this.setOfCheckedId.has(item.user.objectId))
+    this.checkedAll = this.profiles.every((item) =>
+      this.setOfCheckedId.has(item.id)
+    );
   }
   onAllChecked(checked: boolean): void {
     this.profiles.forEach((item) => {
-      if(checked){
-        this.setOfCheckedId.add(item.user.objectId)
-      }else{
-        this.setOfCheckedId.delete(item.user.objectId)
+      if (checked) {
+        this.setOfCheckedId.add(item.id);
+      } else {
+        this.setOfCheckedId.delete(item.id);
       }
     });
     this.checkedAll = checked;
   }
-  async updateUser(data:any, type: string) {
+  resetChange(){
+    this.setOfCheckedId = new Set<string>();
+    this.checkedAll = false;
+  }
+  async updateUser(data: Parse.Object, type: string) {
     console.log(type);
     this.modal.confirm({
       nzTitle: '操作提示',
@@ -138,11 +164,11 @@ export class PageUserComponent implements OnInit {
       nzOkText: '确认',
       nzOkType: 'primary',
       nzOkDanger: type == '删除' ? true : false,
-      nzOnOk:async () => {
-        let query = new Parse.Query('_User')
-        query.equalTo('objectId',data.user.objectId)
-        let r = await query.first()
-        if(r?.id){
+      nzOnOk: async () => {
+        let query = new Parse.Query('_User');
+        query.equalTo('objectId', data?.get('user')?.id);
+        let r = await query.first();
+        if (r?.id) {
           switch (type) {
             case '通过认证':
               r?.set('accountState', '已认证');
@@ -151,7 +177,9 @@ export class PageUserComponent implements OnInit {
               r?.set('accountState', '已禁用');
               break;
             case '删除':
-              r?.set('isDeleted', true);
+              // r?.set('isDeleted', true);
+              await data.destroy()
+              await r.destroy()
               break;
           }
           await r?.save();
@@ -159,41 +187,38 @@ export class PageUserComponent implements OnInit {
         this.getProfile();
       },
       nzCancelText: '取消',
-      nzOnCancel: () => console.log('Cancel')
+      nzOnCancel: () => console.log('Cancel'),
     });
-    
   }
 
-  deleteSelected(){
+  deleteSelected() {
     this.modal.confirm({
       nzTitle: '批量删除',
       nzContent: `删除后数据不可恢复,请谨慎操作`,
       nzOkText: '确认',
       nzOkType: 'primary',
       nzOkDanger: true,
-      nzOnOk:async () => {
-        let selectedList = this.profiles.filter((item:any)=>this.setOfCheckedId.has(item?.id))
-        let deletePromiseList = selectedList.map((item:any)=>{
-          return new Promise((resolve=>{
-            item.set("isDeleted",true);
-            item.save();
-          }))
-        })
-        try{
-          await Promise.all(deletePromiseList)
-        }
-        catch(err){
-
-        }
+      nzOnOk: async () => {
+        let selectedList = this.profiles.filter((item: any) =>
+          this.setOfCheckedId.has(item?.id)
+        );
+        let deletePromiseList = selectedList.map((item: any) => {
+          return new Promise((resolve) => {
+            // item.set('isDeleted', true);
+            // item.save();
+            item.destroy().then(()=> resolve)
+          });
+        });
+        try {
+          await Promise.all(deletePromiseList);
+          this.getProfile();
+        } catch (err) {}
       },
       nzCancelText: '取消',
-      nzOnCancel: () => console.log('Cancel')
+      nzOnCancel: () => console.log('Cancel'),
     });
-   }
-   goDateil(id:string){
-    this.route.navigate([
-      '/nav-admin/manage/user/edit',
-      { id:id },
-    ])
+  }
+  goDateil(id: string) {
+    this.route.navigate(['/nav-admin/manage/user/edit', { id: id }]);
   }
 }

+ 443 - 0
projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.html

@@ -0,0 +1,443 @@
+<nz-page-header>
+  <nz-breadcrumb nz-page-header-breadcrumb>
+    <nz-breadcrumb-item>用户管理</nz-breadcrumb-item>
+    <nz-breadcrumb-item>用户列表</nz-breadcrumb-item>
+    <nz-breadcrumb-item><a>创建用户</a></nz-breadcrumb-item>
+  </nz-breadcrumb>
+  <nz-page-header-content>
+    <div class="user-header">
+      <div class="header-left">
+        <nz-avatar
+          nzSize="large"
+          nzIcon="user"
+          nzSrc="/img/avatar.png"
+        ></nz-avatar>
+        <div class="name-data">
+          <div style="display: flex; align-items: center">
+            <div class="name">{{ validateForm.value.name }}</div>
+            <div class="status"></div>
+          </div>
+          <div class="id">ID:-</div>
+        </div>
+      </div>
+      <div class="header-right">
+        <div style="margin-right: 10px"></div>
+        <button
+          style="background: #3e49b3; border: 1px #3e49b3"
+          nz-button
+          nzType="primary"
+          (click)="submitForm()"
+        >
+          创建用户
+        </button>
+      </div>
+    </div>
+  </nz-page-header-content>
+</nz-page-header>
+<div class="edit-content">
+  <nz-tabset>
+    <nz-tab nzTitle="用户信息">
+      <form
+        nz-form
+        [formGroup]="validateForm"
+        class="login-form"
+        (ngSubmit)="submitForm()"
+      >
+        <div class="title">账号信息</div>
+        <div class="fill-template">
+          <div nz-row>
+            <div nz-col nzSpan="8">
+              <div class="lable">创建时间</div>
+              <div class="value">-</div>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">最后登录时间</div>
+              <div class="value">-</div>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">最后登录IP</div>
+              <div class="value">-</div>
+            </div>
+          </div>
+          <div nz-row>
+            <div nz-col nzSpan="8">
+              <div class="lable">登录次数</div>
+              <div class="value">-</div>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">
+                用户类型 <span style="color: #ff4d4f">*</span>
+              </div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请选择所属的身份类型"
+                  style="width: 100%"
+                >
+                  <nz-select
+                    style="width: 100%"
+                    nzShowSearch
+                    nzAllowClear
+                    nzPlaceHolder="请选择所属的身份类型"
+                    formControlName="identity"
+                  >
+                    @for(item of userType; track item;let index = $index){
+                    <nz-option
+                      nzCustomContent
+                      [nzValue]="item"
+                      [nzLabel]="item"
+                      >{{ item }}</nz-option
+                    >
+                    }
+                  </nz-select>
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">认证文件</div>
+              <app-comp-upload
+                [width]="320"
+                (change)="upload($event)"
+                title="上传认证文件"
+              ></app-comp-upload>
+            </div>
+          </div>
+        </div>
+        <div class="title">个人信息</div>
+        <div class="fill-template">
+          <div nz-row>
+            <div nz-col nzSpan="8">
+              <div class="lable">手机号</div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写手机号"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写手机号"
+                    formControlName="phone"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">
+                邮箱 <span style="color: #ff4d4f">*</span>
+              </div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写邮箱"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="email"
+                    placeholder="请填写邮箱"
+                    formControlName="email"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">
+                姓名 <span style="color: #ff4d4f">*</span>
+              </div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写姓名"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写姓名"
+                    formControlName="name"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+          </div>
+          <div nz-row>
+            <div nz-col nzSpan="8">
+              <div class="lable">
+                用户名(默认为邮箱) <span style="color: #ff4d4f">*</span>
+              </div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写用户名"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写用户名"
+                    formControlName="username"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+          </div>
+        </div>
+        <div class="title">扩展信息</div>
+        <div class="fill-template">
+          <div nz-row>
+            <div nz-col nzSpan="8">
+              <div class="lable">办公电话</div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写办公电话"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写办公电话"
+                    formControlName="telephone"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">
+                单位类型 <span style="color: #ff4d4f">*</span>
+              </div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请选择单位类型"
+                  style="width: 100%"
+                >
+                  <nz-select
+                    style="width: 100%"
+                    nzShowSearch
+                    nzAllowClear
+                    nzPlaceHolder="请选择所在单位类型"
+                    (ngModelChange)="provinceChange($event)"
+                    [(ngModel)]="companyType"
+                    [ngModelOptions]="{ standalone: true }"
+                  >
+                    @for(item of unitTypes; track item.name;let index = $index){
+                    <nz-option
+                      nzCustomContent
+                      [nzValue]="item.name"
+                      [nzLabel]="item.name"
+                      >{{ item.name }}</nz-option
+                    >
+                    }
+                  </nz-select>
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">省份</div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请选择省份"
+                  style="width: 100%"
+                >
+                  <nz-form-item nzJustify="space-between">
+                    <nz-input-group>
+                      <nz-select
+                        style="width: 100%"
+                        nzShowSearch
+                        nzAllowClear
+                        nzPlaceHolder="请选择省份"
+                        formControlName="province"
+                      >
+                        @for(item of provinces; track item;let index = $index){
+                        <nz-option
+                          nzCustomContent
+                          [nzValue]="item"
+                          [nzLabel]="item"
+                          >{{ item }}</nz-option
+                        >
+                        }
+                      </nz-select>
+                    </nz-input-group>
+                  </nz-form-item>
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+          </div>
+          <div nz-row>
+            <div nz-col nzSpan="8">
+              <div class="lable">所在部门</div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写所在部门"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写所在部门"
+                    formControlName="departmentName"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">职务</div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写职务"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写职务"
+                    formControlName="postName"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+            <div nz-col nzSpan="8">
+              <div class="lable">主要学科</div>
+              <nz-form-item style="width: 80%">
+                <nz-form-control
+                  class="val"
+                  nzErrorTip="请填写主要学科"
+                  style="width: 100%"
+                >
+                  <input
+                    nz-input
+                    type="text"
+                    placeholder="请填写主要学科"
+                    formControlName="majorSubject"
+                    nzStatus=""
+                  />
+                </nz-form-control>
+              </nz-form-item>
+            </div>
+          </div>
+        </div>
+        <div class="title-row">
+          <div class="bar">
+            所属组织部门
+            <a class="btn" (click)="showModalDepart()">选择部门</a>
+          </div>
+        </div>
+        <div class="fill-setp">
+          @if (department?.id) {
+          <nz-tag
+            [nzBordered]="false"
+            style="color: black"
+            [nzColor]="'#efefef'"
+            >“十四五”高等教育国家规划教材申报系统</nz-tag
+          >
+          /
+          <nz-tag
+            [nzBordered]="false"
+            style="color: black"
+            [nzColor]="'#efefef'"
+            >{{ companyType }}</nz-tag
+          >
+          /
+          <nz-tag [nzBordered]="false" [nzColor]="'geekblue'">{{
+            department?.get("name")
+          }}</nz-tag>
+          }@else { 待选择 }
+        </div>
+        <!-- <div class="title">原始JSON数据</div>
+        <div class="fill-template">
+          <div nz-row>
+            <div nz-col nzSpan="24">
+              <textarea
+                rows="4"
+                disabled="true"
+                nz-input
+                [(ngModel)]="inputValue"
+                [ngModelOptions]="{ standalone: true }"
+                class="view-block"
+              ></textarea>
+            </div>
+          </div>
+        </div> -->
+      </form>
+    </nz-tab>
+  </nz-tabset>
+</div>
+
+<nz-modal
+  [(nzVisible)]="isVisible"
+  nzTitle="选择部门"
+  (nzOnCancel)="handleCancel()"
+  nzWidth="400px"
+  nzCentered
+>
+  <ng-container *nzModalContent>
+    <div nz-row class="depart-modal">
+      <div nz-col nzSpan="24">
+        <div class="row">
+          <div class="label">选择部门</div>
+          <div class="value">
+            <input
+              nz-input
+              [ngModel]="department?.get('name')"
+              (ngModelChange)="provinceChange(department?.id, $event)"
+              placeholder="搜索或选择一个下方部门"
+              type="text"
+            />
+          </div>
+        </div>
+        <div class="select">
+          <div class="bar">
+            <a style="color: #86909c">教材遴选</a>
+            @for (data of parentMap; track data.title) {
+            <span style="margin: 0 10px">/</span>
+            @if($index == 0){
+            <a (click)="onCheck(data)">{{ data.title }}</a>
+            }@else {
+            <a style="color: #86909c">{{ data.title }}</a>
+            } }
+          </div>
+          <div class="tree">
+            @for (data of parentList; track $index) {
+            <div class="li" (click)="onCheckedDepart(data)">
+              <label
+                nz-radio
+                [ngModel]="department?.id == data.id"
+                [nzValue]="data.id"
+                (click)="onCheckedDepart(data)"
+                >{{ data.get("name") }}</label
+              >
+              @if (!data.get("branch")) {
+              <span nz-icon nzType="right" nzTheme="outline"></span>
+              }
+            </div>
+            }
+          </div>
+        </div>
+      </div>
+    </div>
+  </ng-container>
+  <div *nzModalFooter>
+    <button nz-button nzType="default" (click)="handleCancel()">取消</button>
+    <button
+      nz-button
+      nzType="primary"
+      style="background: #3e49b3; border: 1px #3e49b3"
+      (click)="handleOk()"
+    >
+      确定
+    </button>
+  </div>
+</nz-modal>

+ 173 - 0
projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.scss

@@ -0,0 +1,173 @@
+.subtitle {
+  margin-right: 12px;
+  color: #00000073;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 1.5715;
+  // overflow: hidden;
+  // white-space: nowrap;
+  // text-overflow: ellipsis;
+}
+.user-header {
+  display: flex;
+  justify-content: space-between;
+  .header-left {
+    display: flex;
+    align-items: center;
+    .name-data {
+      display: flex;
+      flex-direction: column;
+      margin-left: 6px;
+      .name {
+        font-family: PingFang SC;
+        font-size: 24px;
+        font-weight: 600;
+        line-height: 32px;
+        text-align: left;
+      }
+      .status{
+        margin-left: 4px;
+      }
+      .id {
+        font-family: PingFang SC;
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 20px;
+        text-align: left;
+        color: #231c1f99;
+      }
+    }
+  }
+  .header-right {
+    display: flex;
+    align-items: center;
+    .form-button {
+      width: 80px;
+      height: 40px;
+      margin: 20px 0;
+      color: white !important;
+      background-color: #3E49B3!important;
+      border-radius: 4px;
+      font-family: PingFang SC;
+      font-size: 14px;
+      font-weight: 400;
+      line-height: 20px;
+      text-align: center;
+    }
+  }
+}
+.edit-content {
+  margin: 0 0 20px;
+  padding: 0 24px;
+  height: calc(100vh - 250px);
+  min-width: 1000px;
+  .title {
+    //styleName: 字体/标题-中-Medium;
+    font-family: PingFang SC;
+    font-size: 20px;
+    font-weight: 500;
+    line-height: 32px;
+    text-align: left;
+    margin: 28px 0 10px;
+  }
+  .fill-template{
+    font-family: PingFang SC;
+    font-size: 14px;
+    .lable{
+      color: #24272299;
+      margin-bottom: 6px;
+    }
+    .value{
+      width: 80%;
+      padding: 4px 8px;
+      background: #f9f9f9;
+      border-radius: 4px;
+      color: #242722;
+      margin-bottom: 10px;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+  }
+  .view-block{
+    width: 100%;
+    min-height: 150px;
+  }
+  .fill-setp {
+    background-color: #fafbfc;
+    font-family: PingFang SC;
+    font-size: 14px;
+    padding: 10px;
+    .lable {
+      color: #24272299;
+      margin-bottom: 6px;
+    }
+    .value {
+      width: 80%;
+      padding: 4px 8px;
+      background: #f9f9f9;
+      border-radius: 4px;
+      color: #242722;
+      margin-bottom: 10px;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+    }
+  }
+  .title-row {
+    font-family: PingFang SC;
+    font-size: 20px;
+    font-weight: 500;
+    line-height: 32px;
+    text-align: left;
+    margin: 28px 0 10px;
+    .btn {
+      margin-left: 20px;
+      flex-shrink: 0;
+      color: #006ded;
+      font-size: 14px;
+      font-weight: 400;
+    }
+    .bar {
+      display: flex;
+      justify-content: space-between;
+      width: 100%;
+    }
+  }
+}
+.depart-modal{
+  .row{
+    width: 90%;
+    margin-bottom: 20px;
+  }
+  .tree{
+    height: 180px;
+    overflow-y: scroll;
+    .li{
+      display: flex;
+      justify-content: space-between;
+      padding: 8px 4px;
+      span{
+        flex-shrink: 0;
+      }
+    }
+    .li:hover{
+      background-color: #f9f9f9;
+    }
+  }
+}
+::ng-deep .ant-page-header-heading-title {
+  white-space: normal;
+}
+::ng-deep .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
+  color: #c6233f;
+}
+::ng-deep .ant-tabs-ink-bar {
+  background: #c6233f;
+}
+::ng-deep .ant-tabs-tab:hover {
+  color: #e97488;
+}
+::ng-deep .ant-tabs-tab-btn:active {
+  color: #e97488;
+}

+ 24 - 0
projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.spec.ts

@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { UserCreateComponent } from './user-create.component';
+
+describe('UserCreateComponent', () => {
+  let component: UserCreateComponent;
+  let fixture: ComponentFixture<UserCreateComponent>;
+
+  beforeEach(waitForAsync(() => {
+    TestBed.configureTestingModule({
+      declarations: [ UserCreateComponent ],
+      imports: [IonicModule.forRoot()]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(UserCreateComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 280 - 0
projects/textbook/src/modules/nav-admin/page-user/user-create/user-create.component.ts

@@ -0,0 +1,280 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { NzSpaceModule } from 'ng-zorro-antd/space';
+import { CommonCompModule } from '../../../../services/common.modules';
+import { NzTabsModule } from 'ng-zorro-antd/tabs';
+import { ActivatedRoute, Router } from '@angular/router';
+import Parse from 'parse';
+import { NzAvatarModule } from 'ng-zorro-antd/avatar';
+import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
+import { NzPopoverModule } from 'ng-zorro-antd/popover';
+import { NzTagModule } from 'ng-zorro-antd/tag';
+import { NzMessageService } from 'ng-zorro-antd/message';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NzSelectModule } from 'ng-zorro-antd/select';
+import { NzRadioModule } from 'ng-zorro-antd/radio';
+import {
+  FormControl,
+  FormGroup,
+  NonNullableFormBuilder,
+  Validators,
+} from '@angular/forms';
+import { provinces } from '../../../../services/provinces';
+import { CompUploadComponent } from '../../../../app/comp-upload/comp-upload.component';
+import { textbookServer } from '../../../../services/textbook';
+import { NzModalModule, NzModalService } from 'ng-zorro-antd/modal';
+
+@Component({
+  selector: 'app-user-edit',
+  templateUrl: './user-create.component.html',
+  styleUrls: ['./user-create.component.scss'],
+  imports: [
+    CommonModule,
+    NzSpaceModule,
+    CommonCompModule,
+    NzTabsModule,
+    NzAvatarModule,
+    NzDropDownModule,
+    NzPopoverModule,
+    NzTagModule,
+    CompUploadComponent,
+    NzModalModule,
+    ReactiveFormsModule,
+    NzSelectModule,
+    NzRadioModule,
+  ],
+  standalone: true,
+})
+export class UserCreateComponent implements OnInit {
+  validateForm: FormGroup<{
+    name: FormControl<string>; //姓名
+    username: FormControl<string>; //用户名
+    phone: FormControl<string>; //手机号
+    email: FormControl<string>; //电子邮箱
+    telephone: FormControl<string>; //办公电话
+    province: FormControl<string>; //省份
+    identity: FormControl<string>; //人员类型
+    // idcard: FormControl<string>; //身份证号
+    departmentName: FormControl<string>; //所在部门
+    postName: FormControl<string>; //职务
+    majorSubject: FormControl<string>; //学科
+  }> = this.fb.group({
+    name: ['', [Validators.required]],
+    username: ['', [Validators.required]],
+    phone: [''],
+    email: ['', [Validators.required]],
+    telephone: [''],
+    province: [''],
+    identity: ['', [Validators.required]],
+    // idcard: ['', [Validators.required]],
+    departmentName: [''],
+    postName: [''],
+    majorSubject: [''],
+  });
+  companyType: string = ''; //单位类型
+
+  provinces: Array<string> = provinces.options; //省份
+
+  unitTypes: Array<any> = [];
+  userType: Array<string> = ['工作联系人', '评审专家', '高校联系人', '个人'];
+  identityFile: string = ''; //单位联系人认证文件
+  department?: Parse.Object; //所属单位
+  parentMap: Array<any> = [];
+  parentList: Array<any> = []; //单位列表
+  inputValue: string = ``;
+  isVisible: boolean = false;
+  searchValue: string = ''; //搜索部门内容
+  constructor(
+    public tbookSer: textbookServer,
+    private activeRoute: ActivatedRoute,
+    private fb: NonNullableFormBuilder,
+    private router: Router,
+    private message: NzMessageService,
+    private modal: NzModalService
+  ) {}
+
+  async ngOnInit() {
+    if (this.tbookSer.profile.identity != '国家级管理员') {
+      this.userType = ['个人'];
+      this.getUnitTypes(this.tbookSer.profile.companyType);
+      return;
+    }
+    await this.getUnitTypes();
+  }
+  async getUnitTypes(name?: string) {
+    let query = new Parse.Query('Department');
+    query.equalTo('branch', undefined);
+    query.equalTo('parent', undefined);
+    query.notEqualTo('isDeleted', true);
+    name && query.equalTo('name', name);
+    query.select('name');
+    let r = await query.find();
+    r.forEach((item) => {
+      this.unitTypes.push({ id: item.id, name: item.get('name') });
+    });
+  }
+  upload(e: any) {
+    console.log(e);
+    let file = e[0];
+    this.identityFile = file;
+  }
+  //根据所选单位类型获取对应单位
+  async provinceChange(id?: string, val?: string) {
+    let query = new Parse.Query('Department');
+    query.equalTo('parent', id ? id : null);
+    query.select('name', 'branch', 'parent');
+    if (this.tbookSer.profile.identity != '国家级管理员') {
+      query.equalTo('branch', this.tbookSer.profile.companyType);
+    }
+    query.limit(100);
+    val && query.contains('name', val);
+    let r = await query.find();
+    this.parentList = r;
+  }
+  //选择部门
+  async showModalDepart() {
+    let parent = this.unitTypes.find((item) => item.name == this.companyType);
+    if (parent?.id) {
+      this.parentMap = await this.formatNode(parent.id);
+    }
+    this.provinceChange();
+    this.isVisible = true;
+  }
+  async formatNode(id: string): Promise<Array<any>> {
+    let arr = [];
+    if (id) {
+      let query = new Parse.Query('Department');
+      query.equalTo('objectId', id);
+      query.select('parent', 'name');
+      let r = await query.first();
+      arr.push({
+        title: r?.get('name'),
+        id: r?.id,
+      });
+      if (r?.get('parent')) {
+        arr.unshift(...(await this.formatNode(r?.get('parent')?.id)));
+      }
+    }
+    return arr;
+  }
+  onCheck(e: any) {
+    console.log(e);
+    this.provinceChange();
+  }
+  //选择部门
+  async onCheckedDepart(e: any) {
+    this.department = e;
+    if (e?.get('parent')?.id) {
+      console.log(e);
+      this.department = e;
+    } else {
+      this.provinceChange(e.id);
+    }
+    this.parentMap = await this.formatNode(e.id);
+  }
+  handleCancel(): void {
+    console.log('Button cancel clicked!');
+    this.isVisible = false;
+  }
+  async handleOk(): Promise<void> {
+    if (this.department?.id) {
+      this.companyType = this.department.get('branch');
+    }
+    this.isVisible = false;
+  }
+  submitForm(): void {
+    if (this.validateForm.valid) {
+      console.log('submit', this.validateForm.value);
+      if (!this.companyType) {
+        this.message.error('请选择单位类型');
+        return;
+      }
+      let a = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/;
+      if (
+        this.validateForm.value.phone &&
+        !String(this.validateForm.value.phone).match(a)
+      ) {
+        this.message.error('请填写正确手机号');
+        return;
+      }
+      let params = this.validateForm.value;
+      this.profileSave(params);
+    } else {
+      this.message.error('请填写必填信息');
+      Object.values(this.validateForm.controls).forEach((control) => {
+        if (control.invalid) {
+          control.markAsDirty();
+          control.updateValueAndValidity({ onlySelf: true });
+        }
+      });
+    }
+  }
+  async profileSave(params: any) {
+    this.randomPassword()
+    if (!this.department?.id) {
+      this.message.error('请选择所属组织部门');
+      return;
+    }
+    try {
+      let obj = Parse.Object.extend('_User');
+      let user = new obj();
+      user.set('username', params.username);
+      user.set('name', params?.name);
+      user.set('phone', params?.phone.trim());
+      user.set('email', params?.email.trim());
+      user.set('password', this.password);
+      user.set('province', params.province);
+      user.set('departmentName', params?.departmentName);
+      user.set('accountState', '已认证');
+      user.set('department', {
+        __type: 'Pointer',
+        className: 'Department',
+        objectId: this.department?.id,
+      });
+      user.set('companyName', this.department?.get('name'));
+      let u = await user.save();
+
+      let p = Parse.Object.extend('Profile');
+      let profile = new p();
+      profile.set('user', u?.toPointer());
+      profile.set('company', {
+        __type: 'Pointer',
+        className: 'Company',
+        objectId: this.tbookSer.company,
+      });
+      profile?.set('telephone', params.telephone);
+      profile.set('companyType', this.companyType);
+      profile.set('email', params.email);
+      profile?.set('postName', params.postName);
+      profile.set('identity', params.identity);
+      profile.set('identityFile', this.identityFile);
+      let res = await profile?.save();
+      console.log(res);
+      this.modal.success({
+        nzTitle: '创建成功',
+        nzContent: '',
+        nzOnOk: () =>history.back()
+      });
+    } catch (err: any) {
+      console.warn('创建失败', err);
+      this.message.error(err?.Error || '创建失败:请检查用户邮箱是否已存在');
+      return;
+    }
+  }
+
+  password: string = '';
+  randomPassword() {
+    let sCode =
+      'A,B,C,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,W,X,Y,Z,1,2,3,4,5,6,7,8,9,0,q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m';
+    let aCode = sCode.split(',');
+    let aLength = aCode.length; //获取到数组的长度
+    let str = [];
+    for (let i = 0; i <= 16; i++) {
+      let j = Math.floor(Math.random() * aLength); //获取到随机的索引值
+      let txt = aCode[j]; //得到随机的一个内容
+      str.push(txt);
+    }
+    this.password = str.join('');
+    console.log(this.password);
+  }
+}

+ 18 - 1
projects/textbook/src/modules/nav-admin/user-edit/user-edit.component.ts

@@ -13,6 +13,7 @@ import { NzPopoverModule } from 'ng-zorro-antd/popover';
 import { NzTagModule } from 'ng-zorro-antd/tag';
 import { NzModalModule } from 'ng-zorro-antd/modal';
 import { NzMessageService } from 'ng-zorro-antd/message';
+import { textbookServer } from '../../../services/textbook';
 @Component({
   selector: 'app-user-edit',
   templateUrl: './user-edit.component.html',
@@ -44,8 +45,9 @@ export class UserEditComponent implements OnInit {
   user: Parse.Object | any;
   profile: Parse.Object | any;
   password: string = '';
-
+  edit:boolean = false //编辑权限
   constructor(
+    public tbookSer: textbookServer,
     private activeRoute: ActivatedRoute,
     private router: Router,
     private message: NzMessageService
@@ -63,10 +65,21 @@ export class UserEditComponent implements OnInit {
         queryProfile.equalTo('user', id);
         this.profile = await queryProfile.first();
       }
+      let arr = ['个人', '评审专家','高校联系人']
+      if ( this.tbookSer.profile.identity == '国家级管理员'
+      || (this.tbookSer.profile.identity =='工作联系人' && arr.includes(this.profile.identity))
+      || (this.tbookSer.profile.identity =='高校联系人' && this.profile.identity == '个人')
+      ) {
+        this.edit = true
+      }
     });
   }
   async updateUser(type: string) {
     console.log(type);
+    if(!this.edit){
+      this.message.warning('同级身份暂无权限操作')
+      return
+    }
     switch (type) {
       case '已认证':
         this.user.set('accountState', '已认证');
@@ -82,6 +95,10 @@ export class UserEditComponent implements OnInit {
     this.ngOnInit();
   }
   async handleOk() {
+    if(!this.edit){
+      this.message.warning('同级身份暂无权限操作')
+      return
+    }
     this.password = this.password.trim()
     if(!this.password){
       this.message.warning('密码格式错误');

+ 1 - 1
projects/textbook/src/modules/nav-author/textbook-details/textbook-details.component.html

@@ -1,6 +1,6 @@
 <div class="site-page-header">
   <nz-page-header
-    nzTitle="教材详情 - {{textBook.type}}"
+    nzTitle="教材详情 - {{textBook.title}}"
     nzSubtitle=""
     style="padding: 0"
   >

+ 0 - 1
projects/textbook/src/modules/nav-province-contact/page-process/process-list/process-list.component.html

@@ -96,7 +96,6 @@
             ></td>
             <td
               nzEllipsis
-              nzLeft
               (click)="
                 toUrl('/nav-province-contact/manage/process/page', {
                   id: data?.objectId

+ 2 - 2
projects/textbook/src/modules/nav-province-contact/page-role/page-role.component.html

@@ -8,7 +8,7 @@
   </nz-page-header-title>
   <nz-page-header-extra>
     <nz-space>
-      <button
+      <!-- <button
         style="background: #3e49b3; border: 1px #3e49b3"
         *nzSpaceItem
         nz-button
@@ -16,7 +16,7 @@
         (click)="addMember()"
       >
         添加账号
-      </button>
+      </button> -->
     </nz-space>
   </nz-page-header-extra>
 </nz-page-header>

+ 8 - 0
projects/textbook/src/modules/nav-province-contact/page-role/page-role.component.scss

@@ -94,6 +94,14 @@
     }
   }
 }
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active {
+  -webkit-transition-delay: 99999s;
+  -webkit-transition: color 99999s ease-out, background-color 99999s ease-out;
+}
+
 ::ng-deep .ant-page-header-heading-title{
   white-space: normal;
 }