wangming 2 달 전
부모
커밋
9febabc15e

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/

+ 9 - 0
.idea/misc.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/project-resouce.iml" filepath="$PROJECT_DIR$/.idea/project-resouce.iml" />
+    </modules>
+  </component>
+</project>

+ 9 - 0
.idea/project-resouce.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 209 - 0
src/app/layout/basic/widgets/layout2user/layout2user.component.ts

@@ -0,0 +1,209 @@
+/* #region  引入 */
+import { Component, OnInit, Injector, ChangeDetectionStrategy, ChangeDetectorRef, Inject, ViewChild } from '@angular/core';
+import { Router } from '@angular/router';
+import { PageDesignHttpService, GeneralUtilsService } from '@core';
+import { LoadingService } from '@delon/abc/loading';
+import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
+import { CacheService } from '@delon/cache';
+import { SettingsService, DrawerHelper, _HttpClient } from '@delon/theme';
+import { environment } from '@env/environment';
+import { NzNotificationService } from 'ng-zorro-antd/notification';
+import { IcPagePartDrawWrapperComponent } from 'src/app/shared/component/ic-page-part/drawer-wrapper/list.component';
+
+import { HeaderChangelogComponent } from '../changelog.component';
+import * as icUtils from './../../../../shared/utils/ic-utils';
+/* #endregion */
+
+@Component({
+  selector: 'layout2-user',
+  templateUrl: './layout2user.component.html',
+  styleUrls: ['./layout2user.component.less'],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class Layout2userComponent implements OnInit {
+  @ViewChild('changeLogTpl', { static: false }) changeLogTpl!: HeaderChangelogComponent;
+  tempAvatarSrc = 'assets/tmp/img/avatar.jpg';
+  userPanelVisible = false;
+  userInfo: any;
+  userCenterId: any;
+  enterpriseCenterId: any;
+  showTenants = false;
+  tenantName = '';
+  showI18n = true;
+  showPwChange = true;
+  constructor(
+    public settings: SettingsService,
+    private router: Router,
+    @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
+    public srv: CacheService,
+    private loadingSrv: LoadingService,
+    private pageHttp: PageDesignHttpService,
+    private injector: Injector,
+    private cdr: ChangeDetectorRef,
+    private notification: NzNotificationService,
+    public http: _HttpClient
+  ) {}
+
+  private goTo(url: string): void {
+    setTimeout(() => this.injector.get(Router).navigateByUrl(url));
+  }
+
+  private get drawer(): DrawerHelper {
+    return this.injector.get(DrawerHelper);
+  }
+
+  private get utils(): GeneralUtilsService {
+    return this.injector.get(GeneralUtilsService);
+  }
+
+  ngOnInit(): void {
+    this.userInfo = this.srv.get('ic_userInfo', { mode: 'none', type: 'm' });
+    const userC: any = this.srv.get('ic_userCollection', { mode: 'none', type: 'm' });
+    const globalConfig = icUtils.getConfigByCode(userC?.configList || [], 'global_config');
+    if (globalConfig) {
+      const userConfigId = globalConfig.configOption.config.customUserCenter;
+      if (icUtils.icIsNotNull(userConfigId)) {
+        this.userCenterId = userConfigId;
+      }
+      const enterpriseCenterId = globalConfig.configOption.config.customEnterpriseCenter;
+      if (icUtils.icIsNotNull(enterpriseCenterId)) {
+        this.enterpriseCenterId = enterpriseCenterId;
+      }
+      const hideI18n = globalConfig.configOption.config.hideI18n;
+      if (hideI18n) {
+        this.showI18n = false;
+      }
+      const hidePwChange = globalConfig.configOption.config.hidePwChange;
+      if (hidePwChange) {
+        this.showPwChange = false;
+      }
+    }
+    if (this.userInfo?.organizationDoList && this.userInfo.organizationDoList.length > 0) {
+      this.userInfo.deptName = this.userInfo.organizationDoList
+        .map((v: any) => {
+          if (icUtils.icIsNotNull(v.shortname)) {
+            return v.shortname;
+          }
+          return v.fullname;
+        })
+        .join('-');
+    }
+    const tenants = this.settings.user.tenants;
+    if (tenants && tenants.length > 0) {
+      this.showTenants = true;
+      this.tenantName = this.userInfo.tenantDo?.tenantname;
+    } else {
+      if (this.settings.user.tenantname) {
+        this.tenantName = this.settings.user.tenantname;
+      }
+    }
+  }
+
+  openUserCenter(): void {
+    if (icUtils.icIsNotNull(this.userCenterId)) {
+      this.goToConfigCenter(this.userCenterId, '我的账号');
+    } else {
+      this.notification.warning('请先在全局配置中增加用户信息配置模块地址', '');
+    }
+  }
+
+  openEnterpriseCenter(): void {
+    if (icUtils.icIsNotNull(this.enterpriseCenterId)) {
+      this.goToConfigCenter(this.enterpriseCenterId, '我的企业');
+    } else {
+      this.notification.warning('请先在全局配置中增加企业信息配置模块地址', '');
+    }
+  }
+
+  goToConfigCenter(compId: string, title: string): void {
+    // 弹窗
+    if (compId.length <= 36) {
+      this.pageHttp.getPagePartById(compId).subscribe({
+        next: (res: any) => {
+          if (!res.data.config) {
+            const tempConfig = JSON.parse(res.data.roption);
+            res.data.config = tempConfig;
+          }
+          this.drawer.create(title, IcPagePartDrawWrapperComponent, { config: res.data }, { size: 600 }).subscribe(res => {
+            if (res.data.row.data.updatecolumns.PORTRAIT) {
+              const ps = res.data.row.data.updatecolumns.PORTRAIT.split('-;-');
+              if (ps && ps.length > 1) {
+                if (['DATASOURCE', 'COLUMN'].includes(environment.tenantmode)) {
+                  this.settings.user.avatar = `${environment.PIC_PREVIEW_URL}/${this.settings.app.tenantid}?fileId=${ps[1]}`;
+                } else if (['NONE'].includes(environment.tenantmode)) {
+                  this.settings.user.avatar = `${environment.PIC_PREVIEW_URL}?fileId=${ps[1]}`;
+                }
+                this.cdr.markForCheck();
+              }
+            } else {
+              this.settings.user.avatar = this.tempAvatarSrc;
+              this.cdr.markForCheck();
+            }
+            console.log('res', res);
+            console.log('avatar', this.settings.user.avatar);
+            this.notification.success('信息保存成功', '修改后的账号、企业等数据需要刷新页面才可看见最新的');
+          });
+        },
+        error: (err: any) => {},
+        complete: () => {
+          this.loadingSrv.close();
+        }
+      });
+      // 跳转外部链接
+    } else if (icUtils.icIsExtraLink(compId)) {
+      window.open(compId);
+      // 路由
+    } else {
+      this.goTo(compId);
+    }
+  }
+
+  changeMyPassword(): void {}
+
+  avatarLoadError(e: any): void {
+    console.log(`头像读取失败: ${this.settings.user.avatar}`);
+    this.settings.user.avatar = this.tempAvatarSrc;
+  }
+
+  openUserPanel(): void {
+    this.userPanelVisible = true;
+  }
+
+  closeUserPanel(): void {
+    this.userPanelVisible = false;
+  }
+
+  logout(): void {
+    const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token || '';
+    this.http
+      .post(`inControlServer/sys/login/logout`, {
+        token: token
+      })
+      .subscribe({
+        next: (res: any) => {},
+        error: (error: any) => {
+          console.error(error);
+        },
+        complete: () => {
+          this.tokenService.clear();
+          window.location.reload();
+        }
+      });
+  }
+
+  showChangeLog(): void {
+    this.changeLogTpl.showLog();
+  }
+
+  userHelp(): void {
+    const userCollection = this.utils.loadCache('ic_userCollection');
+    const globalConfig = icUtils.getConfigByCode(userCollection?.configList || [], 'global_config');
+    // 设置是否开启用户访问历史记录
+    if (globalConfig.configOption.config.userHelpConfig) {
+      const userHelpConfig = globalConfig.configOption.config.userHelpConfig;
+      if (userHelpConfig && userHelpConfig.length > 0 && userHelpConfig.indexOf('http') > -1) {
+        window.open(userHelpConfig, '_blank');
+      }
+    }
+  }
+}

+ 124 - 0
src/app/layout/basic/widgets/user.component.ts

@@ -0,0 +1,124 @@
+import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Injector, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { PageDesignHttpService } from '@core';
+import { LoadingService } from '@delon/abc/loading';
+import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
+import { CacheService } from '@delon/cache';
+import { ModalHelper, SettingsService, _HttpClient } from '@delon/theme';
+import { deepCopy } from '@delon/util';
+import { environment } from '@env/environment';
+import { BasePersonalCenterEditComponent } from 'src/app/routes/base/personal-center/edit/edit.component';
+import { IcPagePartModalWrapperComponent } from 'src/app/shared/component/ic-page-part/modal-wrapper/list.component';
+
+import * as icUtils from '../../../shared/utils/ic-utils';
+
+@Component({
+  selector: 'header-user',
+  template: `
+    <div class="alain-default__nav-item d-flex align-items-center px-sm" nz-dropdown nzPlacement="bottomRight" [nzDropdownMenu]="userMenu">
+      <nz-avatar [nzSrc]="settings.user.avatar" nzSize="small" class="mr-sm" (nzError)="avatarLoadError($event)"></nz-avatar>
+      {{ settings.user.name }}
+    </div>
+    <nz-dropdown-menu #userMenu="nzDropdownMenu">
+      <div nz-menu class="width-sm">
+        <div nz-menu-item (click)="gotoUserCenter()">
+          <i nz-icon nzType="user" class="mr-sm"></i>
+          {{ 'menu.account.center' | i18n }}
+        </div>
+        <li nz-menu-divider></li>
+        <div nz-menu-item (click)="logout()">
+          <i nz-icon nzType="logout" class="mr-sm"></i>
+          {{ 'menu.account.logout' | i18n }}
+        </div>
+      </div>
+    </nz-dropdown-menu>
+  `,
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class HeaderUserComponent implements OnInit {
+  tempAvatarSrc = 'assets/tmp/img/avatar.jpg';
+  userCenterId?: string;
+  constructor(
+    public settings: SettingsService,
+    private router: Router,
+    @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
+    public srv: CacheService,
+    private modal: ModalHelper,
+    private loadingSrv: LoadingService,
+    private pageHttp: PageDesignHttpService,
+    public http: _HttpClient,
+    private injector: Injector
+  ) {}
+
+  logout(): void {
+    const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token || '';
+    this.http
+      .post(`inControlServer/sys/login/logout`, {
+        token: token
+      })
+      .subscribe({
+        next: (res: any) => {},
+        error: (error: any) => {
+          console.error(error);
+        },
+        complete: () => {
+          this.tokenService.clear();
+          window.location.reload();
+        }
+      });
+  }
+
+  gotoUserCenter(): void {
+    if (!this.userCenterId) {
+      this.modal.create(BasePersonalCenterEditComponent, {}).subscribe(res => {});
+    } else {
+      this.loadingSrv.open({ type: 'spin' });
+      const currentUser = this.srv.get('ic_userInfo', { mode: 'none', type: 'm' });
+      const eventTriggerData = {
+        type: 'select',
+        queryParameter: [
+          {
+            field: 'ID',
+            fieldType: 'string',
+            symbol: 'equal',
+            value: currentUser.id
+          }
+        ],
+        relationId: 'ID',
+        relationData: currentUser.id
+      };
+      this.pageHttp.getPagePartById(this.userCenterId).subscribe({
+        next: (res: any) => {
+          if (!res.data.config) {
+            const tempConfig = JSON.parse(res.data.roption);
+            res.data.config = tempConfig;
+          }
+          this.modal.create(IcPagePartModalWrapperComponent, { config: res.data, eventTriggerData }, { size: 1200 }).subscribe(res => {});
+        },
+        error: (err: any) => {},
+        complete: () => {
+          this.loadingSrv.close();
+        }
+      });
+    }
+  }
+
+  ngOnInit(): void {
+    const ps = (this.settings?.user?.avatar || '-;-').split('-;-');
+    this.settings.user.avatar = `${environment.PIC_PREVIEW_URL}?fileId=${ps[ps.length - 1]}`;
+    const userC: any = this.srv.get('ic_userCollection', { mode: 'none', type: 'm' });
+
+    const globalConfig = icUtils.getConfigByCode(userC?.configList || [], 'global_config');
+    if (globalConfig) {
+      const userConfigId = globalConfig.configOption.config.customUserCenter;
+      if (icUtils.icIsNotNull(userConfigId)) {
+        this.userCenterId = userConfigId;
+      }
+    }
+  }
+
+  avatarLoadError(e: any): void {
+    console.warn('头像加载失败', e);
+    this.settings.user.avatar = this.tempAvatarSrc;
+  }
+}

+ 196 - 0
src/app/routes/console/main/console.main.component.ts

@@ -0,0 +1,196 @@
+/* #region  引入 */
+import { Component, OnInit, Injector, ChangeDetectorRef, ChangeDetectionStrategy, Inject } from '@angular/core';
+import { Router } from '@angular/router';
+import { GeneralHttpService, GeneralUtilsService } from '@core';
+import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
+import { _HttpClient } from '@delon/theme';
+import { NzModalRef } from 'ng-zorro-antd/modal';
+import { NzNotificationService } from 'ng-zorro-antd/notification';
+
+import * as icUtils from '../../../shared/utils/ic-utils';
+import { defaultMenu } from '../schema';
+/* #endregion */
+
+@Component({
+  selector: 'ic-console-main',
+  templateUrl: './console.main.component.html',
+  styleUrls: ['./console.main.component.less'],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ConsoleMainComponent implements OnInit {
+  userApps: any[] = [];
+
+  templates: any[] = [
+    { label: 'OA审批', value: 'oa', desc: '支持请假、出差、报销等多种工作审批', tag: ['审批', '库存管理'] },
+    { label: '项目管理', value: 'projectManagement', desc: '在线协作,轻松实现远程办公', tag: ['审批', '库存管理'] },
+    {
+      label: '设备巡检',
+      value: 'equipmentInspection',
+      desc: '提供全方位的资产管理,涵盖资产入库、领用、归还、转交、报修等',
+      tag: ['审批', '库存管理']
+    },
+    { label: '客户关系管理', value: 'crm', desc: '多维度统计项目及任务,快捷掌握项目进度', tag: ['审批', '库存管理'] },
+    { label: '合同管理', value: 'contractManagement', desc: '精细化、流程化管理物资采购申请与审批', tag: ['审批', '库存管理'] },
+    { label: '库存管理', value: 'inventoryManagement', desc: '集中管理合同,实时跟踪合同执行情况', tag: ['审批', '库存管理'] },
+    {
+      label: '生产质量管理',
+      value: 'pdManagement',
+      desc: '支持请假、出差、报销等多种工作审批;支持请假、出差、报销等多种工作审批;支持请假、出差、报销等多种工作审批',
+      tag: ['审批', '库存管理']
+    },
+    { label: '培训管理', value: 'trainingManagement', desc: '在线协作,轻松实现远程办公', tag: ['审批', '库存管理'] },
+    {
+      label: '绩效管理',
+      value: 'performanceManagement',
+      desc: '提供全方位的资产管理,涵盖资产入库、领用、归还、转交、报修等',
+      tag: ['审批', '库存管理']
+    }
+  ];
+
+  selectedNewRow: any;
+
+  newFormData: any = { APPPLATFORM: 'pc' };
+
+  pageloading = false;
+  editPanel = {
+    visible: false
+  };
+  userInfo: any;
+
+  constructor(
+    private http: GeneralHttpService,
+    private notification: NzNotificationService,
+    private injector: Injector,
+    @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
+    private cdr: ChangeDetectorRef,
+    public httpClient: _HttpClient
+  ) {}
+
+  private get utils(): GeneralUtilsService {
+    return this.injector.get(GeneralUtilsService);
+  }
+  private get modalRef(): NzModalRef {
+    return this.injector.get(NzModalRef);
+  }
+
+  private goTo(url: string): void {
+    setTimeout(() => this.injector.get(Router).navigateByUrl(url));
+  }
+
+  ngOnInit(): void {
+    this.userInfo = this.utils.loadCache('ic_userInfo');
+    let i = 1;
+    for (const item of this.templates) {
+      const iStr = `${i}`;
+      item.icon = `assets/image/ic-icons/ic-icon-${iStr.padStart(3, '0')}.png`;
+      if (i > 70) {
+        i = 0;
+      }
+      i++;
+    }
+    const queryInfo = {
+      current: 1,
+      size: 10,
+      dataType: 'sql',
+      dataSourceCode: 'sys_app_list',
+      columnQueryTypes: [
+        {
+          lastTyepRelation: 'AND',
+          columnInnerRelation: 'AND',
+          columns: [{ field: 'ORGID', value: this.userInfo.id, fieldType: 'string', symbol: 'equal' }]
+        }
+      ]
+    };
+    this.http.getPostData('inControlServer/sys/generalCRUD/getListData', queryInfo).subscribe({
+      next: res => {
+        this.userApps = res.data.records;
+      },
+      error: err => {
+        console.error('err', err);
+      },
+      complete: () => {
+        this.cdr.markForCheck();
+      }
+    });
+  }
+
+  panelVisibleSwitch(e: any, visible: boolean, code?: string): void {
+    this.editPanel.visible = visible;
+    this.cdr.markForCheck();
+    if (code) {
+      this.selectedNewRow = this.templates.find(x => x.value === code);
+      this.newFormData.APPNAME = this.selectedNewRow.label;
+    }
+  }
+
+  createApp(e: any): void {
+    if (icUtils.icIsNotNull(this.newFormData.APPNAME)) {
+      this.editPanel.visible = false;
+      this.pageloading = true;
+      this.cdr.markForCheck();
+      this.notification.info('提示', '正在创建应用');
+      const saveData = { ...this.newFormData };
+      saveData.ID = this.utils.getUUID();
+      saveData.ORGID = this.userInfo.id;
+      saveData.APPICON = `assets/image/ic-icons/ic-icon-${(Math.random() * 60).toFixed(0).padStart(3, '0')}.png`;
+      if (this.selectedNewRow) {
+        const menus = defaultMenu[this.selectedNewRow.value];
+        icUtils.icVisitTree(menus, v => {
+          v.url = '/page/s/2041bd8918ed4281afc913f7e2d3d099';
+        });
+        menus.unshift({ title: '门户', url: '/dashboarditem/31385d80e9df4335ac588c87227d9d64' });
+        menus.unshift({ title: '登录', url: '/passport/login' });
+        saveData.ROPTION = JSON.stringify({ menus: menus });
+        saveData.APPICON = this.selectedNewRow.icon;
+      } else {
+        saveData.ROPTION = JSON.stringify({
+          menus: [
+            { title: '登录', url: '/passport/login' },
+            { title: '门户', url: '/dashboarditem/31385d80e9df4335ac588c87227d9d64' }
+          ]
+        });
+      }
+      this.http
+        .getPostData('inControlServer/sys/generalCRUD/generalInsert', {
+          tablename: 'sys_app',
+          insertcolumns: saveData
+        })
+        .subscribe({
+          next: (res: any) => {
+            this.goTo(`/full/console/project/${saveData.ID}`);
+          },
+          error: (err: any) => {
+            this.notification.error('提示', '创建应用失败');
+          },
+          complete: () => {
+            this.pageloading = false;
+            this.cdr.markForCheck();
+          }
+        });
+    } else {
+      this.notification.error('提示', '请填写应用名称');
+    }
+  }
+
+  logout(e: any) {
+    const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token || '';
+    this.httpClient
+      .post(`inControlServer/sys/login/logout`, {
+        token: token
+      })
+      .subscribe({
+        next: (res: any) => {},
+        error: (error: any) => {
+          console.error(error);
+        },
+        complete: () => {
+          this.tokenService.clear();
+          window.location.reload();
+        }
+      });
+  }
+
+  goToApp(e: any, app: any): void {
+    this.goTo(`/full/console/project/${app.ID}`);
+  }
+}

+ 337 - 0
src/app/routes/console/project/console.project.component.ts

@@ -0,0 +1,337 @@
+/* #region  引入 */
+import {
+  Component,
+  OnInit,
+  Injector,
+  ChangeDetectorRef,
+  ChangeDetectionStrategy,
+  ElementRef,
+  AfterViewInit,
+  HostListener,
+  ViewChild,
+  ViewContainerRef,
+  ComponentFactoryResolver,
+  Inject
+} from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { DashboardHttpService, GeneralHttpService, PageDesignHttpService } from '@core';
+import * as icConstant from '@core';
+import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
+import { _HttpClient, ModalHelper } from '@delon/theme';
+import { deepCopy } from '@delon/util';
+import { DynamicComponentService } from 'src/app/core/utils/dynamic-component.service';
+import { IcLoginConfigH5Component } from 'src/app/shared/component/ic-login-config-h5/ic-login-config-h5.component';
+import { IcLoginConfigPcComponent } from 'src/app/shared/component/ic-login-config-pc/ic-login-config-pc.component';
+import { IcPagePartWrapperComponent } from 'src/app/shared/component/ic-page-part/wrapper/list.component';
+import { IcDashboardWdigetReflectionRegistry } from 'src/app/shared/service/dashboard.widget.reflection.service';
+
+import * as icUtils from '../../../shared/utils/ic-utils';
+
+/* #endregion */
+
+@Component({
+  selector: 'ic-console-main',
+  templateUrl: './console.project.component.html',
+  styleUrls: ['./console.project.component.less'],
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ConsoleProjectComponent implements OnInit, AfterViewInit {
+  pageloading = false;
+  appType: 'pc' | 'h5' = 'pc';
+  treeData = [
+    {
+      title: '登录',
+      key: '10',
+      isLeaf: true,
+      url: '/passport/login'
+    },
+    {
+      title: '仪表盘',
+      key: '100',
+      isLeaf: true,
+      url: '/dashboarditem/31385d80e9df4335ac588c87227d9d64'
+    },
+    {
+      title: '项目管理',
+      key: '1001',
+      children: [
+        { title: '项目列表', key: '10010', isLeaf: true, url: '/page/s/2041bd8918ed4281afc913f7e2d3d099' },
+        { title: '项目里程碑', key: '10011', isLeaf: true },
+        { title: '项目进度填报', key: '10012', isLeaf: true }
+      ]
+    },
+    {
+      title: '合同管理',
+      key: '1002',
+      children: [
+        { title: '经营合同', key: '10020', isLeaf: true },
+        { title: '采购合同', key: '10021', isLeaf: true }
+      ]
+    }
+  ];
+  constant = icConstant;
+  @ViewChild('itemDom', { read: ViewContainerRef }) itemDom!: ViewContainerRef;
+  componentRef: any;
+  graph: any;
+  formId?: string;
+  constructor(
+    private cdr: ChangeDetectorRef,
+    private injector: Injector,
+    private cfr: ComponentFactoryResolver,
+    private componentService: DynamicComponentService,
+    private pageHttp: PageDesignHttpService,
+    private dashboardHttp: DashboardHttpService,
+    private compReflection: IcDashboardWdigetReflectionRegistry,
+    private el: ElementRef,
+    private modal: ModalHelper,
+    private activeRoute: ActivatedRoute,
+    @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
+    private http: GeneralHttpService,
+    public httpClient: _HttpClient
+  ) {}
+
+  ngOnInit(): void {
+    this.formId = this.activeRoute.snapshot.params.formId;
+    const queryInfo = {
+      dataSourceCode: 'sys_app_list',
+      dataType: 'sql',
+      queryType: 'senior',
+      current: 1,
+      size: 10,
+      columnQueryTypes: [
+        {
+          lastTyepRelation: 'AND',
+          columnInnerRelation: 'AND',
+          columns: [
+            { field: 'DELETED', value: '0', fieldType: 'string', symbol: 'equal' },
+            { field: 'ID', value: this.formId, fieldType: 'string', symbol: 'equal' }
+          ]
+        }
+      ],
+      orders: [{ orderName: 'MODIFYDATE', orderType: 'DESC' }]
+    };
+    this.http.getGeneralCRUDListData(queryInfo).subscribe({
+      next: res => {
+        console.log('res', res);
+        const row = res.data.records[0];
+        const config = JSON.parse(row.ROPTION);
+        icUtils.icVisitTree(config.menus, v => {
+          v.key = icUtils.uuid16();
+          if (!icUtils.icIsNotNull(v.children)) {
+            v.isLeaf = true;
+          } else {
+            v.expanded = true;
+          }
+        });
+        console.log('config', config);
+        this.treeData = config.menus;
+      },
+      error: err => {
+        console.log('err', err);
+      },
+      complete: () => {
+        this.cdr.markForCheck();
+      }
+    });
+  }
+  ngAfterViewInit(): void {
+    this.resetLeftHeight();
+  }
+
+  rowSelect(e: any): void {
+    console.log('rowSelect', e);
+    this.itemDom.clear();
+    let url = e.node.origin.url;
+    if (url) {
+      if (url === '/passport/login') {
+        // 登录
+        let tpl: any = IcLoginConfigH5Component;
+        if (this.appType === 'pc') {
+          tpl = IcLoginConfigPcComponent;
+        }
+        let componentFactory = this.cfr.resolveComponentFactory(tpl);
+        this.componentRef = this.itemDom.createComponent(componentFactory);
+      } else if (url.includes('/page/s')) {
+        // 业务页面
+        this.pageloading = true;
+        let sourceUrl = url.split('?')[0].split('/');
+        let pageId = sourceUrl[sourceUrl.length - 1];
+        this.pageHttp.getPagePartById(pageId).subscribe({
+          next: (res: any) => {
+            const data = res.data;
+            let compConfig = res.data.config;
+            if (!compConfig) {
+              compConfig = JSON.parse(res.data.roption);
+            }
+            data.config = compConfig;
+            let componentFactory = this.cfr.resolveComponentFactory(IcPagePartWrapperComponent);
+            this.componentRef = this.itemDom.createComponent(componentFactory);
+            this.componentRef.instance.config = data;
+            this.componentRef.instance.refreshComp();
+            setTimeout(() => {
+              this.pageloading = false;
+              this.cdr.markForCheck();
+            }, 50);
+          },
+          error: error => console.error(error),
+          complete: () => {}
+        });
+      } else if (url.includes('/dashboarditem/')) {
+        // 门户
+        this.pageloading = true;
+        let sourceUrl = url.split('?')[0].split('/');
+        let pageId = sourceUrl[sourceUrl.length - 1];
+        this.dashboardHttp.getItemConfigById(pageId).subscribe({
+          next: (res: any) => {
+            if (res.data.roption) {
+              res.data.roption = JSON.parse(res.data.roption);
+            }
+            const itemConfig = res.data;
+            let componentFactory = this.cfr.resolveComponentFactory(this.compReflection.getType(itemConfig['instcode']));
+            this.componentRef = this.itemDom.createComponent(componentFactory);
+            this.componentRef.instance.config = itemConfig;
+            if (this.componentRef.instance?.icEchartsChange) {
+              this.componentRef.instance.icEchartsChange.subscribe({
+                next: (res: any) => {
+                  if ((res?.type || '') === 'after_init') {
+                    this.componentRef.instance.refresh();
+                    setTimeout(() => {
+                      this.pageloading = false;
+                      this.cdr.markForCheck();
+                    }, 50);
+                  }
+                },
+                error: (error: any) => console.error(error),
+                complete: () => {}
+              });
+            } else if (this.componentRef.instance?.icDashboardItemChange) {
+              setTimeout(() => {
+                this.componentRef.instance.refresh();
+                this.pageloading = false;
+                this.cdr.markForCheck();
+              }, 200);
+            } else {
+              console.error(`搜索不到id为${pageId}的组件`);
+            }
+          },
+          error: error => console.error(error),
+          complete: () => {}
+        });
+      } else if (url.includes('/page/lr/')) {
+        // 左右结构
+      }
+      this.cdr.markForCheck();
+    }
+  }
+  rowOperation(e: any, oper: string, row: any): void {
+    console.log('row', row);
+    const stylemy = deepCopy(this.constant.pageModalConfig);
+    switch (oper) {
+      case 'edit':
+        console.log('编辑', row);
+        const sourceRow = row.origin;
+        if (sourceRow.url.includes('/page/s')) {
+          this.pageloading = true;
+          const id = sourceRow.url.split('/').pop();
+          const queryInfo = {
+            dataSourceCode: `ic_page_part_ex_list`,
+            dataType: 'sql',
+            queryType: 'senior',
+            current: 1,
+            size: 10,
+            columnQueryTypes: [
+              {
+                columns: [
+                  {
+                    field: 'DELETED',
+                    value: '0',
+                    fieldType: 'string',
+                    symbol: 'equal'
+                  },
+                  {
+                    field: 'ID',
+                    value: '104c2e4c7a2d4659bf3e1fbdccc1a08c',
+                    fieldType: 'string',
+                    symbol: 'equal'
+                  }
+                ],
+                lastTyepRelation: 'AND',
+                columnInnerRelation: 'AND'
+              }
+            ]
+          };
+          this.http.getGeneralCRUDListData(queryInfo).subscribe({
+            next: res => {
+              const configs = JSON.parse(res.data.records[0].ROPTION);
+              this.componentService.getSysComponentByName('ic-page-part-ex-edit').then(component => {
+                this.modal
+                  .create(component, { pageConfig: configs, baseFormData: configs.base }, { modalOptions: this.constant.fullModalConfig })
+                  .subscribe((res: any) => {
+                    if (res) {
+                      console.log(res);
+                    }
+                  });
+              });
+            },
+            error: err => console.error(err),
+            complete: () => {
+              this.pageloading = false;
+              this.cdr.markForCheck();
+            }
+          });
+        }
+        break;
+      case 'delete':
+        console.log('删除', row);
+        break;
+      case 'addPage':
+        this.componentService.getSysComponentByName('ic-page-part-ex-edit').then(component => {
+          this.modal.create(component, {}, { modalOptions: this.constant.fullModalConfig }).subscribe((res: any) => {
+            if (res) {
+              console.log(res);
+            }
+          });
+        });
+        break;
+      case 'addDashboard':
+        this.componentService.getPageConfigComponentByName('ic-pageconfig-dashboard').then(component => {
+          this.modal.create(component, {}, { modalOptions: this.constant.fullModalConfig }).subscribe((res: any) => {
+            if (res) {
+              console.log(res);
+            }
+          });
+        });
+        break;
+      default:
+        break;
+    }
+  }
+
+  @HostListener('window:resize', ['$event'])
+  resetLeftHeight(): void {
+    const topLeft = this.el.nativeElement.querySelector('.app-top-left');
+    const topRight = this.el.nativeElement.querySelector('.app-top-right');
+    if (topLeft && topRight) {
+      topLeft.style.height = `${document.body.clientHeight - topLeft.offsetTop}px`;
+      topRight.style.height = `${document.body.clientHeight - topRight.offsetTop - 16}px`;
+    }
+  }
+
+  logout(e: any) {
+    const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token || '';
+    this.httpClient
+      .post(`inControlServer/sys/login/logout`, {
+        token: token
+      })
+      .subscribe({
+        next: (res: any) => {},
+        error: (error: any) => {
+          console.error(error);
+        },
+        complete: () => {
+          this.tokenService.clear();
+          window.location.reload();
+        }
+      });
+  }
+}