Переглянути джерело

Merge branch 'develop' into feature/1.3.7-broadcast-20230110

vothin 2 роки тому
батько
коміт
bc72a81478
59 змінених файлів з 12914 додано та 5521 видалено
  1. 76 5
      languages/en.js
  2. 75 3
      languages/es.js
  3. 28 2
      languages/ru-RU.js
  4. 77 4
      languages/zh-CN.js
  5. 0 172
      node_modules/zrender/src/svg/helper/ClippathManager.js
  6. 0 212
      node_modules/zrender/src/svg/helper/ShadowManager.js
  7. 6313 5064
      package-lock.json
  8. 2 1
      package.json
  9. 69 0
      src/api/ncs_board_item.js
  10. 83 0
      src/api/ncs_board_title.js
  11. 26 1
      src/api/ncs_clerk.js
  12. 28 0
      src/api/ncs_entrace_guard_user.js
  13. 33 0
      src/api/ncs_frameGroup.js
  14. 7 0
      src/api/ncs_functions.js
  15. 41 0
      src/api/ncs_functions_role_mapping.js
  16. 35 0
      src/api/ncs_interaction-chain.js
  17. 15 0
      src/api/role.js
  18. 9 4
      src/components/MarkdownEditor/index.vue
  19. 1 0
      src/icons/svg/delete.svg
  20. 1 0
      src/icons/svg/designer.svg
  21. 1 0
      src/icons/svg/frame_manage.svg
  22. 1 0
      src/icons/svg/list-move.svg
  23. 1 0
      src/icons/svg/pass_through.svg
  24. 1 0
      src/icons/svg/pen-leather.svg
  25. 1 0
      src/icons/svg/role.svg
  26. 1 0
      src/icons/svg/setting.svg
  27. 1 0
      src/icons/svg/squence.svg
  28. 1 0
      src/layout/components/Navbar.vue
  29. 87 0
      src/router/index.js
  30. 102 0
      src/styles/element-ui.scss
  31. 5 2
      src/utils/mixin.js
  32. 1 0
      src/views/calling-board/index.vue
  33. 426 0
      src/views/custom-infoboard/board-title.vue
  34. 69 0
      src/views/custom-infoboard/mixin.js
  35. 459 0
      src/views/custom-infoboard/screen-designer.vue
  36. 184 0
      src/views/custom-infoboard/templates/board-item-setting.vue
  37. 166 0
      src/views/custom-infoboard/templates/board-module-setting.vue
  38. 9 0
      src/views/custom-infoboard/templates/common/index.js
  39. 438 0
      src/views/custom-infoboard/templates/common/tpl-one-cloum.vue
  40. 466 0
      src/views/custom-infoboard/templates/common/tpl-three-colum.vue
  41. 451 0
      src/views/custom-infoboard/templates/common/tpl-two-cloum.vue
  42. 14 0
      src/views/custom-infoboard/templates/index.js
  43. 304 0
      src/views/custom-infoboard/templates/layout-item.vue
  44. 407 0
      src/views/entrace-guard/users.vue
  45. 143 0
      src/views/function-mapping/components/RoleForFunction.vue
  46. 43 0
      src/views/function-mapping/index.vue
  47. 2 0
      src/views/hospitalFrame/nurse_watch_frame.vue
  48. 552 0
      src/views/interaction-chain/index.vue
  49. 31 2
      src/views/ncs-auth/compontents/roleEdit.vue
  50. 2 1
      src/views/ncs-auth/superadmin/defaultRoleManager.vue
  51. 1 0
      src/views/ncs-channel/index.vue
  52. 480 0
      src/views/ncs-clerk-frame-manage/components/employeeView.vue
  53. 500 0
      src/views/ncs-clerk-frame-manage/components/frameView.vue
  54. 500 0
      src/views/ncs-clerk-frame-manage/components/multiClerkManageFrame.vue
  55. 49 0
      src/views/ncs-clerk-frame-manage/index.vue
  56. 84 47
      src/views/ncs-clerk/components/clerkList.vue
  57. 1 1
      src/views/ncs-device/components/deviceManager.vue
  58. 6 0
      src/views/ncs-orginazition/components/partInfoEdit.vue
  59. 5 0
      src/views/ncs-orginazition/partInfoSetting.vue

+ 76 - 5
languages/en.js

@@ -250,7 +250,8 @@ module.exports = {
     disabled: 'Disabled!',
     disableCancel: 'Disabled',
     deleteMember: 'Are you sure you want to delete this user information?',
-    cannotDeleteAdmin: 'Cannot delete the person in charge of the organization'
+    cannotDeleteAdmin: 'Cannot delete the person in charge of the organization',
+    leaderName:'Leader Name'
   },
   home: {
     todayTask: "Today's task",
@@ -910,6 +911,7 @@ module.exports = {
     visitation: 'Visitor',
     transferDevice: 'Transfer box',
     lcdDevice: 'LCD corridor screen',
+    entraceguard_device: 'Access control APP',
     organizationAdd: 'New Organization',
     boardShowEmptyBed: 'The board shows empty beds',
     nursingColorRgb: 'Nursing door light color',
@@ -920,7 +922,8 @@ module.exports = {
     hidden: 'Turn on hide',
     channelImHistoryStoreDays: 'The number of days that channel messages are retained',
     recordEnabled: 'Turn on the audio and video recording function',
-    recordAble: 'Turn on the audio and video recording function'
+    recordAble: 'Turn on the audio and video recording function',
+    screenLight: 'Turn on screen bright and triggers the function'
   },
   role: {
     roleName: 'Role name',
@@ -933,7 +936,11 @@ module.exports = {
     roleEvent: 'Role event',
     roleDelete: 'Delete role',
     roleEdit: 'Edit role event',
-    onChoiceRole: 'No role has been selected!'
+    onChoiceRole: 'No role has been selected!',
+    leaderRole: 'Superior role',
+    leaderRolePlaceholder: 'Choose the superior role',
+    roleEnum: 'Type of the role',
+    roleEnumPlaceholder: 'Choose the Type'
   },
   his: {
     hisPart: 'Department Query',
@@ -980,6 +987,8 @@ module.exports = {
     hisManage: 'His query',
     interactionChars: 'Interaction statistics',
     partSetting: 'Organization settings',
+    functionRoleMapping:'Function Role Setting',
+    interactionChain:'Interaction Accept Order',
     allClerk: 'All employees',
     allCustomer: 'All users',
     allDevice: 'All devices',
@@ -991,7 +1000,11 @@ module.exports = {
     roleManage: 'Role management',
     debugging485: '485 debugging',
     deviceFrame: 'Device space',
-    ledDevice: 'LED devices'
+    ledDevice: 'LED devices',
+    entraceguardUser:'Passage setting',
+    customBoardManage: 'Custom Board Screen',
+    customBoardDesigner:'Designer Board Screen',
+    staffManageFrames:'Staff Serve Structure'
   },
   deviceType: {
     NURSE_HOST: 'Nurse Host',
@@ -1031,7 +1044,6 @@ module.exports = {
     OWON_X5_GATEWAY: 'X5 Gateway',
     FALL_DETECTION_RADAR: 'Fall Detection Radar',
     HUMAN_DETECTION_RADAR: 'Sleep Detection Radar'
-
   },
   vitalSignsDeviceType: {
     BLOOD_SUGAR: 'Blood Pressure Meter',
@@ -1100,6 +1112,44 @@ module.exports = {
     ChooseDate: 'Choose Date',
     SleepData: 'Sleep Data'
   },
+
+  interactionChain:{
+    ActionType:'Interaction Type',
+    FirstRole:'The First Acceptor',
+    SpreadRoleNamePath:'Acception Role Sequence',
+    AddInteractionSpreadPath:'Create a new recorder',
+    AllRoles:'All Roles',
+    AcceptRoles:'Acception',
+    NoticeSequence:'Notice Sequence',
+    SearchPlaceHolder:'Enter a role name',
+    Customer:'Customer',
+    NURSE_HOST: 'Nurse Host',
+    AcceptSequence:'Accept Sequence',
+    chooseActionTypeTip:'Choose the type of interaction pls',
+    chooseRoleSquence:'Choose the role to accept pls',
+    sketchMap:'The Sketch Map Of Acception'
+  },
+  functionRoleMapping:{
+    configurateLeft:'configurate the roles own[',
+    configurateRight:']'
+  },
+  functions:{
+    MANAGE_BED:'Manage Bed'
+  },
+  clerkFrameManage: {
+    employeeView: 'Staff View',
+    frameView: 'Structure View',
+    multiClerkView: 'Structure With Multiple Staff',
+    managedFrames: 'Served Structures',
+    notManagedFrames: 'Unserved Structures',
+    settableEmployee: 'Settable Staff',
+    settedEmployee: 'Setted Staff',
+    allEmployee: 'All Staff',
+    phoneAcceptorLabel: 'The Phone Call Acceptor',
+    phoneAcceptorTipsLeft: 'If Other Employee Of Same Role With ',
+    phoneAcceptorTipsMid: ' Manage Space Frame Overlaping',
+    phoneAcceptorTipsRight: ' Charge The Phone Call'
+  },
   errorType: {
     REFRESH_TOKEN_FAILED: 'The current refresh token has expired',
     TOKEN_FAILED: 'The current token has expired',
@@ -1140,5 +1190,26 @@ module.exports = {
     LICENSE_EXPIRE: 'The authorization expires, please contact after-sales service',
     LICENSE_FAILED: 'Invalid authorization, please contact after-sales service',
     FILE_UPLOAD_FAILED: 'No file storage directory configured!'
+  },
+  entraceguardUser:{
+    named: 'User Name',
+    idNo:'ID number',
+    ic:'IC Card No',
+    phone:'cell-phone number',
+    password:'Access code',
+    forbidden:'No Entry',
+    refreshUser:'Refresh Users',
+    yes:'YES',
+    nop:'NO'
+  },
+  boardTitle:{
+    add:'Add information board screen',
+    screenTitle:'Screen Title',
+    showIndex:'Display order',
+    partId:'Department ID',
+    design:'Design content',
+    titleRequire:'Screen title cannot be empty',
+    createTitle:'New information board custom screen',
+    editTitle:'Edit information board custom screen'
   }
 }

+ 75 - 3
languages/es.js

@@ -250,6 +250,8 @@ module.exports = {
     disabled: '¡Deshabilitado!',
     disableCancel: 'Deshabilitado',
     deleteMember: '¿Está seguro de que desea eliminar esta información de usuario?',
+    cantDeleteAdmin: 'No se puede eliminar al responsable de la organización',
+    leaderName:'Nombre del líder',
     cannotDeleteAdmin: 'No se puede eliminar al responsable de la organización'
   },
   home: {
@@ -910,6 +912,7 @@ module.exports = {
     visitation: 'Visitante',
     transferDevice: 'Caja de transferencia',
     lcdDevice: 'Pantalla de pasillo LCD',
+    entraceguard_device: 'Control de acceso APP',
     organizationAdd: 'Nueva Organización',
     boardShowEmptyBed: 'El tablero muestra camas vacías',
     nursingColorRgb: 'Color de la luz de la puerta de enfermería',
@@ -920,7 +923,8 @@ module.exports = {
     hidden: 'Activar ocultar',
     channelImHistoryStoreDays: 'El número de días que se retienen los mensajes del canal',
     recordEnabled: 'Active la función de grabación de audio y video',
-    recordAble: 'Active la función de grabación de audio y video'
+    recordAble: 'Active la función de grabación de audio y video',
+    screenLight: 'Enciende la pantalla y activa la función'
   },
   role: {
     roleName: 'Nombre de la función',
@@ -933,7 +937,11 @@ module.exports = {
     roleEvent: 'Evento de rol',
     roleDelete: 'Borrar rol',
     roleEdit: 'Editar evento de rol',
-    onChoiceRole: '¡No se ha seleccionado ningún rol!'
+    onChoiceRole: '¡No se ha seleccionado ningún rol!',
+    leaderRole:'Función superior',
+    leaderRolePlaceholder:'Selección de funciones superiores',
+    roleEnum:'Tipos de funciones',
+    roleEnumPlaceholder:'Seleccionar tipo'
   },
   his: {
     hisPart: 'Consulta de sección',
@@ -980,6 +988,8 @@ module.exports = {
     hisManage: 'Su consulta',
     interactionChars: 'Interacción Estadísticas',
     partSetting: 'Organización construir',
+    functionRoleMapping:'Configuración funciones',
+    interactionChain:'Aceptar órdenes de forma interactiva',
     allClerk: 'Todos los empleados',
     allCustomer: 'Todos los usuarios',
     allDevice: 'Todos los dispositivos',
@@ -991,7 +1001,11 @@ module.exports = {
     roleManage: 'Administración de roles',
     debugging485: '485 depurando',
     deviceFrame: 'Espacio del dispositivo',
-    ledDevice: 'Dispositivos LED'
+    ledDevice: 'Dispositivos LED',
+    entraceguardUser:'Configuración de paso',
+    customBoardManage: 'Pantalla de tablero personalizada',
+    customBoardDesigner:'Pantalla del tablero de diseño',
+    staffManageFrames:'Estructura del servicio al personal'
   },
   deviceType: {
     NURSE_HOST: 'Enfermera anfitriona',
@@ -1099,6 +1113,43 @@ module.exports = {
     ChooseDate: 'fecha',
     SleepData: 'Datos de sueño'
   },
+  interactionChain:{
+    ActionType:'Tipo de interacción',
+    FirstRole:'Primera parte receptora',
+    SpreadRoleNamePath:'Aceptar secuencia de funciones',
+    AddInteractionSpreadPath:'Crear un nuevo registrador',
+    AllRoles:'Todos los papeles',
+    AcceptRoles:'Aceptación',
+    NoticeSequence:'Orden de notificación',
+    SearchPlaceHolder:'Introduzca el nombre del papel',
+    Customer:'Cliente',
+    NURSE_HOST: 'Enfermera presentadora',
+    AcceptSequence:'Secuencia de aceptación',
+    chooseActionTypeTip:'Seleccione el tipo de interacción',
+    chooseRoleSquence:'Seleccione la función de recepción',
+    sketchMap:'Diagrama de aceptación'
+  },
+  functionRoleMapping:{
+    configurateLeft:'Configurar su propio papel[',
+    configurateRight:']'
+  },
+  functions:{
+    MANAGE_BED:'Gestión de camas'
+  },
+  clerkFrameManage: {
+    employeeView: 'Vista de empleados',
+    frameView: 'Vista de estructura',
+    multiClerkView: 'Estructura multijugador',
+    managedFrames: 'Estructuras de servicios',
+    notManagedFrames: 'Estructura de los servicios no prestados',
+    settableEmployee: 'Personal configurable',
+    settedEmployee: 'Personal fijo',
+    allEmployee: 'Todo el personal',
+    phoneAcceptorLabel: 'Receptor de llamadas telefónicas',
+    phoneAcceptorTipsLeft: 'Si otros empleados del mismo papel ',
+    phoneAcceptorTipsMid: ' Superposición de marcos espaciales de gestión',
+    phoneAcceptorTipsRight: ' Cargar el teléfono'
+  },
   errorType: {
     REFRESH_TOKEN_FAILED: 'El token de actualización actual ha expirado',
     TOKEN_FAILED: 'El Token actual ha caducado',
@@ -1139,5 +1190,26 @@ module.exports = {
     LICENSE_EXPIRE: 'La autorización expira, por favor póngase en contacto con post - venta',
     LICENSE_FAILED: 'Autorización inválida, por favor póngase en contacto con post - venta',
     FILE_UPLOAD_FAILED: '¡Directorio de almacenamiento de archivos no configurado!'
+  },
+  entraceguardUser:{
+    named: 'Nombre de usuario',
+    idNo:'Número de identificación',
+    ic:'Número de tarjeta IC',
+    phone:'Número de teléfono móvil',
+    password:'Código de acceso',
+    forbidden:'Prohibición de entrar',
+    refreshUser:'Actualizar usuarios',
+    yes:'Sí.',
+    nop:'No.'
+  },
+  boardTitle:{
+    add:'Añadir pantalla de visualización de información',
+    screenTitle:'Título de la pantalla',
+    showIndex:'Orden de visualización',
+    partId:'Departamento ID',
+    design:'Contenido del diseño',
+    titleRequire:'El título de la pantalla no puede estar vacío',
+    createTitle:'Nueva pantalla personalizada de la Junta de información',
+    editTitle:'Editar la pantalla personalizada del tablero de información'
   }
 }

+ 28 - 2
languages/ru-RU.js

@@ -906,6 +906,7 @@ module.exports = {
     visitation: 'Посетитель',
     transferDevice: 'Раздаточная коробка',
     lcdDevice: 'ЖК-экран коридора',
+    entraceguard_device: 'Вход',
     organizationAdd: 'Новая организация',
     boardShowEmptyBed: 'На доске показаны пустые кровати',
     nursingColorRgb: 'Светлый цвет двери медсестер',
@@ -916,7 +917,8 @@ module.exports = {
     hidden: 'открыть',
     channelImHistoryStoreDays: 'Количество дней, в течение которых сообщения канала хранятся',
     recordEnabled: 'Включение функции записи аудио и видео',
-    recordAble: 'Включение функции записи аудио и видео'
+    recordAble: 'Включение функции записи аудио и видео',
+    screenLight: 'Включите экран и запустите эту функцию.'
   },
   role: {
     roleName: 'Имя роли',
@@ -968,7 +970,10 @@ module.exports = {
     roleManage: 'Управление ролями',
     debugging485: '485 отладка',
     deviceFrame: 'Пространство устройства',
-    ledDevice: 'Светодиодные устройства'
+    ledDevice: 'Светодиодные устройства',
+    entraceguardUser:'Параметры прохода',
+    customBoardManage: 'Настройка экрана панели',
+    customBoardDesigner:'Экран панели'
   },
   deviceType: {
     NURSE_HOST: 'Устройство медсестры',
@@ -1116,5 +1121,26 @@ module.exports = {
     LICENSE_EXPIRE: 'срок действия разрешения истекает после продажи',
     LICENSE_FAILED: 'Неверный Авторизация после продажи',
     FILE_UPLOAD_FAILED: 'каталог без профиля!'
+  },
+  entraceguardUser:{
+    named: 'Имя пользователя',
+    idNo:'Номер удостоверения личности',
+    ic:'Номер карты IC',
+    phone:'Номер телефона',
+    password:'Пароль доступа',
+    forbidden:'Запрет на передвижение',
+    refreshUser:'Actualizar usuarios',
+    yes:'Да.',
+    nop:'Нет'
+  },
+  boardTitle:{
+    add:'Добавить информационный экран',
+    screenTitle:'Заголовок экрана',
+    showIndex:'Показать последовательность',
+    partId:'Сектор ID',
+    design:'Содержание проекта',
+    titleRequire:'Заголовок экрана не может быть пустым',
+    createTitle:'Дополнительный экран панели',
+    editTitle:'Изменить панель сообщений'
   }
 }

+ 77 - 4
languages/zh-CN.js

@@ -250,7 +250,8 @@ module.exports = {
     disabled: '已禁用!',
     disableCancel: '已取消禁用',
     deleteMember: '你确定要删除此用户信息?',
-    cannotDeleteAdmin: '不能删除机构负责人'
+    cannotDeleteAdmin: '不能删除机构负责人',
+    leaderName:'上级职员'
   },
   home: {
     todayTask: '今日任务',
@@ -910,6 +911,7 @@ module.exports = {
     vistitation: '探视机',
     transferDevice: '转换盒',
     lcdDevice: 'LCD走廊屏',
+    entraceguard_device: '门禁设备APP',
     organizationAdd: '新建组织',
     boardShowEmptyBed: '看板显示空床',
     nursingColorRgb: '护理门灯颜色',
@@ -920,7 +922,8 @@ module.exports = {
     hidden: '开启隐藏',
     channelImHistoryStoreDays: '频道留言保留天数',
     recordEnabled: '开启录音录像功能',
-    recordAble: '开启录音录像功能'
+    recordAble: '开启录音录像功能',
+    screenLight: '开启按钮亮屏并触发功能'
   },
   role: {
     roleName: '角色名称',
@@ -933,7 +936,11 @@ module.exports = {
     roleEvent: '角色事件',
     roleDelete: '删除角色',
     roleEdit: '编辑角色事件',
-    onChoiceRole: '没有选中任何角色!'
+    onChoiceRole: '没有选中任何角色!',
+    leaderRole:'上级角色',
+    leaderRolePlaceholder:'请选择上级角色',
+    roleEnum:'角色类型',
+    roleEnumPlaceholder:'请选择角色类型'
   },
   his: {
     hisPart: '科室查询',
@@ -981,6 +988,8 @@ module.exports = {
     hisManage: 'his查询',
     interactionChars: '交互统计',
     partSetting: '机构设置',
+    functionRoleMapping:'功能角色设置',
+    interactionChain:'交互接收顺序',
     allClerk: '所有员工',
     allCustomer: '所有用户',
     allDevice: '所有设备',
@@ -992,7 +1001,11 @@ module.exports = {
     roleManage: '角色管理',
     debugging485: '485调试',
     deviceFrame: '设备空间',
-    ledDevice: 'LED点阵屏'
+    ledDevice: 'LED点阵屏',
+    entraceguardUser:'用户通行设置',
+    customBoardManage: '自定义信息看板屏',
+    customBoardDesigner:'看板屏设计',
+    staffManageFrames:'空间管理设置'
   },
   deviceType: {
     NURSE_HOST: '护士主机',
@@ -1033,6 +1046,7 @@ module.exports = {
     FALL_DETECTION_RADAR: '防跌倒雷达',
     HUMAN_DETECTION_RADAR: '睡眠监测雷达',
     ALARM_INFUSION: '输液报警器'
+
   },
   vitalSignsDeviceType: {
     BLOOD_SUGAR: '血糖仪',
@@ -1101,6 +1115,44 @@ module.exports = {
     ChooseDate: '日期',
     SleepData: '睡眠数据'
   },
+
+  interactionChain:{
+    ActionType:'交互类型',
+    FirstRole:'最先接收角色',
+    SpreadRoleNamePath:'交互接收角色顺序',
+    AddInteractionSpreadPath:'新增交互接收顺序',
+    AllRoles:'所有角色',
+    AcceptRoles:'接收角色',
+    NoticeSequence:'注意顺序',
+    SearchPlaceHolder:'请输入角色名称',
+    Customer:'客户',
+    NURSE_HOST: '护士主机',
+    AcceptSequence:'接收顺序',
+    chooseActionTypeTip:'请选择交互类型',
+    chooseRoleSquence:'请选择接收角色的顺序',
+    sketchMap:'交互接收流程示意图'
+  },
+  functionRoleMapping:{
+    configurateLeft:'配置拥有[',
+    configurateRight:']功能的角色'
+  },
+  functions:{
+    MANAGE_BED:'管理床位'
+  },
+  clerkFrameManage:{
+    employeeView:'职员视图',
+    frameView:'空间视图',
+    multiClerkView:'多人共管空间',
+    managedFrames:'已管理空间',
+    notManagedFrames:'未管理空间',
+    settableEmployee:'可设置职员',
+    settedEmployee:'已设置职员',
+    allEmployee:'所有职员',
+    phoneAcceptorLabel:'普通电话接收人',
+    phoneAcceptorTipsLeft:'如有其他同角色职员与',
+    phoneAcceptorTipsMid:'管理空间重叠',
+    phoneAcceptorTipsRight:'负责处理普通电话呼叫'
+},
   errorType: {
     REFRESH_TOKEN_FAILED: '当前刷新Token已经失效',
     TOKEN_FAILED: '当前token已经失效',
@@ -1141,5 +1193,26 @@ module.exports = {
     LICENSE_EXPIRE: '授权到期,请联系售后',
     LICENSE_FAILED: '无效授权,请联系售后',
     FILE_UPLOAD_FAILED: '未配置文件存放目录!'
+  },
+  entraceguardUser:{
+    named: '用户姓名',
+    idNo:'身份证号',
+    ic:'IC卡号',
+    phone:'手机号',
+    password:'通行密码',
+    forbidden:'禁止通行',
+    refreshUser:'刷新可用用户',
+    yes:'是',
+    nop:'否'
+  },
+  boardTitle:{
+    add:'添加看板屏幕',
+    screenTitle:'屏幕标题',
+    showIndex:'显示顺序',
+    partId:'科室ID',
+    design:'设计内容',
+    titleRequire:'屏幕标题不能为空',
+    createTitle:'新增看板自定义屏',
+    editTitle:'编辑自定义看板屏'
   }
 }

+ 0 - 172
node_modules/zrender/src/svg/helper/ClippathManager.js

@@ -1,172 +0,0 @@
-/**
- * @file Manages SVG clipPath elements.
- * @author Zhang Wenli
- */
-
-import Definable from './Definable';
-import * as zrUtil from '../../core/util';
-import * as matrix from '../../core/matrix';
-
-/**
- * Manages SVG clipPath elements.
- *
- * @class
- * @extends Definable
- * @param   {number}     zrId    zrender instance id
- * @param   {SVGElement} svgRoot root of SVG document
- */
-function ClippathManager(zrId, svgRoot) {
-    Definable.call(this, zrId, svgRoot, 'clipPath', '__clippath_in_use__');
-}
-
-
-zrUtil.inherits(ClippathManager, Definable);
-
-
-/**
- * Update clipPath.
- *
- * @param {Displayable} displayable displayable element
- */
-ClippathManager.prototype.update = function (displayable) {
-    var svgEl = this.getSvgElement(displayable);
-    if (svgEl) {
-        this.updateDom(svgEl, displayable.__clipPaths, false);
-    }
-
-    var textEl = this.getTextSvgElement(displayable);
-    if (textEl) {
-        // Make another clipPath for text, since it's transform
-        // matrix is not the same with svgElement
-        this.updateDom(textEl, displayable.__clipPaths, true);
-    }
-
-    this.markUsed(displayable);
-};
-
-
-/**
- * Create an SVGElement of displayable and create a <clipPath> of its
- * clipPath
- *
- * @param {Displayable} parentEl  parent element
- * @param {ClipPath[]}  clipPaths clipPaths of parent element
- * @param {boolean}     isText    if parent element is Text
- */
-ClippathManager.prototype.updateDom = function (
-    parentEl,
-    clipPaths,
-    isText
-) {
-    if (clipPaths && clipPaths.length > 0) {
-        // Has clipPath, create <clipPath> with the first clipPath
-        var defs = this.getDefs(true);
-        var clipPath = clipPaths[0];
-        var clipPathEl;
-        var id;
-
-        var dom = isText ? '_textDom' : '_dom';
-
-        if (clipPath[dom]) {
-            // Use a dom that is already in <defs>
-            id = clipPath[dom].getAttribute('id');
-            clipPathEl = clipPath[dom];
-
-            // Use a dom that is already in <defs>
-            if (!defs.contains(clipPathEl)) {
-                // This happens when set old clipPath that has
-                // been previously removed
-                defs.appendChild(clipPathEl);
-            }
-        }
-        else {
-            // New <clipPath>
-            id = 'zr' + this._zrId + '-clip-' + this.nextId;
-            ++this.nextId;
-            clipPathEl = this.createElement('clipPath');
-            clipPathEl.setAttribute('id', id);
-            defs.appendChild(clipPathEl);
-
-            clipPath[dom] = clipPathEl;
-        }
-
-        // Build path and add to <clipPath>
-        var svgProxy = this.getSvgProxy(clipPath);
-        if (clipPath.transform
-            && clipPath.parent.invTransform
-            && !isText
-        ) {
-            /**
-             * If a clipPath has a parent with transform, the transform
-             * of parent should not be considered when setting transform
-             * of clipPath. So we need to transform back from parent's
-             * transform, which is done by multiplying parent's inverse
-             * transform.
-             */
-            // Store old transform
-            var transform = Array.prototype.slice.call(
-                clipPath.transform
-            );
-
-            // Transform back from parent, and brush path
-            matrix.mul(
-                clipPath.transform,
-                clipPath.parent.invTransform,
-                clipPath.transform
-            );
-            svgProxy.brush(clipPath);
-
-            // Set back transform of clipPath
-            clipPath.transform = transform;
-        }
-        else {
-            svgProxy.brush(clipPath);
-        }
-
-        var pathEl = this.getSvgElement(clipPath);
-
-        clipPathEl.innerHTML = '';
-        /**
-         * Use `cloneNode()` here to appendChild to multiple parents,
-         * which may happend when Text and other shapes are using the same
-         * clipPath. Since Text will create an extra clipPath DOM due to
-         * different transform rules.
-         */
-        clipPathEl.appendChild(pathEl.cloneNode());
-
-        parentEl.setAttribute('clip-path', 'url(#' + id + ')');
-
-        if (clipPaths.length > 1) {
-            // Make the other clipPaths recursively
-            this.updateDom(clipPathEl, clipPaths.slice(1), isText);
-        }
-    }
-    else {
-        // No clipPath
-        if (parentEl) {
-            parentEl.setAttribute('clip-path', 'none');
-        }
-    }
-};
-
-/**
- * Mark a single clipPath to be used
- *
- * @param {Displayable} displayable displayable element
- */
-ClippathManager.prototype.markUsed = function (displayable) {
-    var that = this;
-    if (displayable.__clipPaths && displayable.__clipPaths.length > 0) {
-        zrUtil.each(displayable.__clipPaths, function (clipPath) {
-            if (clipPath._dom) {
-                Definable.prototype.markUsed.call(that, clipPath._dom);
-            }
-            if (clipPath._textDom) {
-                Definable.prototype.markUsed.call(that, clipPath._textDom);
-            }
-        });
-    }
-};
-
-
-export default ClippathManager;

+ 0 - 212
node_modules/zrender/src/svg/helper/ShadowManager.js

@@ -1,212 +0,0 @@
-/**
- * @file Manages SVG shadow elements.
- * @author Zhang Wenli
- */
-
-import Definable from './Definable';
-import * as zrUtil from '../../core/util';
-
-/**
- * Manages SVG shadow elements.
- *
- * @class
- * @extends Definable
- * @param   {number}     zrId    zrender instance id
- * @param   {SVGElement} svgRoot root of SVG document
- */
-function ShadowManager(zrId, svgRoot) {
-    Definable.call(
-        this,
-        zrId,
-        svgRoot,
-        ['filter'],
-        '__filter_in_use__',
-        '_shadowDom'
-    );
-}
-
-
-zrUtil.inherits(ShadowManager, Definable);
-
-
-/**
- * Create new shadow DOM for fill or stroke if not exist,
- * but will not update shadow if exists.
- *
- * @param {SvgElement}  svgElement   SVG element to paint
- * @param {Displayable} displayable  zrender displayable element
- */
-ShadowManager.prototype.addWithoutUpdate = function (
-    svgElement,
-    displayable
-) {
-    if (displayable && hasShadow(displayable.style)) {
-        var style = displayable.style;
-
-        // Create dom in <defs> if not exists
-        var dom;
-        if (style._shadowDom) {
-            // Gradient exists
-            dom = style._shadowDom;
-
-            var defs = this.getDefs(true);
-            if (!defs.contains(style._shadowDom)) {
-                // _shadowDom is no longer in defs, recreate
-                this.addDom(dom);
-            }
-        }
-        else {
-            // New dom
-            dom = this.add(displayable);
-        }
-
-        this.markUsed(displayable);
-
-        var id = dom.getAttribute('id');
-        svgElement.style.filter = 'url(#' + id + ')';
-    }
-};
-
-
-/**
- * Add a new shadow tag in <defs>
- *
- * @param {Displayable} displayable  zrender displayable element
- * @return {SVGFilterElement} created DOM
- */
-ShadowManager.prototype.add = function (displayable) {
-    var dom = this.createElement('filter');
-    var style = displayable.style;
-
-    // Set dom id with shadow id, since each shadow instance
-    // will have no more than one dom element.
-    // id may exists before for those dirty elements, in which case
-    // id should remain the same, and other attributes should be
-    // updated.
-    style._shadowDomId = style._shadowDomId || this.nextId++;
-    dom.setAttribute('id', 'zr' + this._zrId
-        + '-shadow-' + style._shadowDomId);
-
-    this.updateDom(displayable, dom);
-    this.addDom(dom);
-
-    return dom;
-};
-
-
-/**
- * Update shadow.
- *
- * @param {Displayable} displayable  zrender displayable element
- */
-ShadowManager.prototype.update = function (svgElement, displayable) {
-    var style = displayable.style;
-    if (hasShadow(style)) {
-        var that = this;
-        Definable.prototype.update.call(this, displayable, function (style) {
-            that.updateDom(displayable, style._shadowDom);
-        });
-    }
-    else {
-        // Remove shadow
-        this.remove(svgElement, style);
-    }
-};
-
-
-/**
- * Remove DOM and clear parent filter
- */
-ShadowManager.prototype.remove = function (svgElement, style) {
-    if (style._shadowDomId != null) {
-        this.removeDom(style);
-        svgElement.style.filter = '';
-    }
-};
-
-
-/**
- * Update shadow dom
- *
- * @param {Displayable} displayable  zrender displayable element
- * @param {SVGFilterElement} dom DOM to update
- */
-ShadowManager.prototype.updateDom = function (displayable, dom) {
-    var domChild = dom.getElementsByTagName('feDropShadow');
-    if (domChild.length === 0) {
-        domChild = this.createElement('feDropShadow');
-    }
-    else {
-        domChild = domChild[0];
-    }
-
-    var style = displayable.style;
-    var scaleX = displayable.scale ? (displayable.scale[0] || 1) : 1;
-    var scaleY = displayable.scale ? (displayable.scale[1] || 1) : 1;
-
-    // TODO: textBoxShadowBlur is not supported yet
-    var offsetX, offsetY, blur, color;
-    if (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY) {
-        offsetX = style.shadowOffsetX || 0;
-        offsetY = style.shadowOffsetY || 0;
-        blur = style.shadowBlur;
-        color = style.shadowColor;
-    }
-    else if (style.textShadowBlur) {
-        offsetX = style.textShadowOffsetX || 0;
-        offsetY = style.textShadowOffsetY || 0;
-        blur = style.textShadowBlur;
-        color = style.textShadowColor;
-    }
-    else {
-        // Remove shadow
-        this.removeDom(dom, style);
-        return;
-    }
-
-    domChild.setAttribute('dx', offsetX / scaleX);
-    domChild.setAttribute('dy', offsetY / scaleY);
-    domChild.setAttribute('flood-color', color);
-
-    // Divide by two here so that it looks the same as in canvas
-    // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur
-    var stdDx = blur / 2 / scaleX;
-    var stdDy = blur / 2 / scaleY;
-    var stdDeviation = stdDx + ' ' + stdDy;
-    domChild.setAttribute('stdDeviation', stdDeviation);
-
-    // Fix filter clipping problem
-    dom.setAttribute('x', '-100%');
-    dom.setAttribute('y', '-100%');
-    dom.setAttribute('width', Math.ceil(blur / 2 * 200) + '%');
-    dom.setAttribute('height', Math.ceil(blur / 2 * 200) + '%');
-
-    dom.appendChild(domChild);
-
-    // Store dom element in shadow, to avoid creating multiple
-    // dom instances for the same shadow element
-    style._shadowDom = dom;
-};
-
-/**
- * Mark a single shadow to be used
- *
- * @param {Displayable} displayable displayable element
- */
-ShadowManager.prototype.markUsed = function (displayable) {
-    var style = displayable.style;
-    if (style && style._shadowDom) {
-        Definable.prototype.markUsed.call(this, style._shadowDom);
-    }
-};
-
-function hasShadow(style) {
-    // TODO: textBoxShadowBlur is not supported yet
-    return style
-        && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY
-            || style.textShadowBlur || style.textShadowOffsetX
-            || style.textShadowOffsetY);
-}
-
-
-export default ShadowManager;

Різницю між файлами не показано, бо вона завелика
+ 6313 - 5064
package-lock.json


+ 2 - 1
package.json

@@ -45,7 +45,7 @@
     "screenfull": "4.2.0",
     "script-loader": "0.7.2",
     "sortablejs": "1.8.4",
-    "tui-editor": "1.3.3",
+    "@toast-ui/editor": "^3.1.3",
     "vcolorpicker": "^1.0.3",
     "vue": "2.6.10",
     "vue-baidu-map": "^0.21.22",
@@ -59,6 +59,7 @@
     "vue-seamless-scroll": "^1.1.23",
     "vue-splitpane": "1.0.4",
     "vuedraggable": "^2.20.0",
+    "vue-lazyload": "^1.2.6",
     "vuex": "3.1.0",
     "xlsx": "0.14.1"
   },

+ 69 - 0
src/api/ncs_board_item.js

@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+/**
+ * 自定义看板项相关接口
+ * @param params
+ * @returns {*|Promise|Promise<unknown>}
+ */
+export function getList(params) {
+    return request({
+        url: '/ncs/boarditem/page',
+        method: 'POST',
+        loading: true,
+        data: params,
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+    })
+}
+
+/** 新增自定义看板项 */
+export function add(params) {
+    return request({
+        url: '/ncs/boarditem',
+        method: 'POST',
+        loading: true,
+        data: params
+    })
+}
+
+/** 删除自定义看板项 */
+export function remove(params) {
+    const ids = params.toString()
+    return request({
+        url: `/ncs/boarditem/${ids}`,
+        method: 'DELETE',
+        loading: true,
+        data: params
+    })
+}
+
+/** 更新自定义看板项 */
+export function update(id, params) {
+    return request({
+        url: `/ncs/boarditem/${id}`,
+        method: 'put',
+        data: params
+    })
+}
+
+/** 查询自定义看板项 */
+export function get(id, params) {
+    return request({
+        url: `/ncs/boarditem/${id}`,
+        method: 'get',
+        loading: false,
+        params
+    })
+}
+
+
+/** 获取某科室下的所有看板项 */
+export function getPartList(part_id) {
+    return request({
+        url: `/ncs/boarditem/partboarditem/${part_id}`,
+        method: 'get',
+        loading: false
+    })
+}
+
+
+

+ 83 - 0
src/api/ncs_board_title.js

@@ -0,0 +1,83 @@
+import request from '@/utils/request'
+
+/**
+ * 自定义看板屏幕相关接口
+ * @param params
+ * @returns {*|Promise|Promise<unknown>}
+ */
+export function getList(params) {
+    return request({
+        url: '/ncs/boardtitle/page',
+        method: 'POST',
+        loading: true,
+        data: params,
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+    })
+}
+
+/** 新增自定义看板屏幕 */
+export function add(params) {
+    return request({
+        url: '/ncs/boardtitle',
+        method: 'POST',
+        loading: true,
+        data: params
+    })
+}
+
+/** 删除自定义看板屏幕 */
+export function remove(params) {
+    const ids = params.toString()
+    return request({
+        url: `/ncs/boardtitle/${ids}`,
+        method: 'DELETE',
+        loading: true,
+        data: params
+    })
+}
+
+/** 更新自定义看板屏幕 */
+export function update(id, params) {
+    return request({
+        url: `/ncs/boardtitle/${id}`,
+        method: 'put',
+        data: params
+    })
+}
+
+/** 查询自定义看板屏幕 */
+export function get(id, params) {
+    return request({
+        url: `/ncs/boardtitle/${id}`,
+        method: 'get',
+        loading: false,
+        params
+    })
+}
+
+/**
+ * 获取频道的订阅者列表 返回List<ClerkDO> 或null
+ * @param id 频道Id
+ * @returns {AxiosPromise | * | Promise | Promise<unknown>}
+ */
+export function getChannelSubscribers(id) {
+    return request({
+        url: `/channelsubscribe/subscribers/${id}`,
+        method: 'get',
+        loading: false
+    })
+}
+
+/**
+ * 批量设置频道订阅者
+ * @param params {channel_id:12,member_ids:[]}
+ * @returns {*|Promise|Promise<unknown>}
+ */
+export function setChannelSubscribers(params) {
+    return request({
+        url: `/channelsubscribe/subscribers`,
+        method: 'post',
+        loading: false,
+        data: params
+    })
+}

+ 26 - 1
src/api/ncs_clerk.js

@@ -54,7 +54,8 @@ export function update(id, params) {
   return request({
     url: `/ncs/clerk/${id}`,
     method: 'put',
-    data: params
+    data: params,
+    headers: { 'Content-Type': 'application/json' }
   })
 }
 export function getRoles(params) {
@@ -85,3 +86,27 @@ export function updateParentById(params) {
     data: params
   })
 }
+export function getRoleGroupClerk(partId) {
+  return request({
+    url: `/ncs/clerk/getrolegroupclerk/${partId}`,
+    method: 'GET'
+  })
+}
+
+export function getClerkManageFrame(clerkId,frameRootType) {
+  return request({
+    url: `/ncs/clerk/getclerkmanageframe/${clerkId}/${frameRootType}`,
+    method: 'GET'
+  })
+}
+
+
+export function saveClerkManageFrame(params) {
+  return request({
+    url: '/ncs/clerk/saveclerkmanageframe',
+    method: 'POST',
+    loading: true,
+    data: params,
+    headers: { 'Content-Type': 'application/json' }
+  })
+}

+ 28 - 0
src/api/ncs_entrace_guard_user.js

@@ -0,0 +1,28 @@
+import request from '@/utils/request'
+export function getList(params) {
+    return request({
+        url: '/ncs/entrace_guard_user/page',
+        method: 'POST',
+        loading: true,
+        data: params,
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+    })
+}
+
+/** 更新用户设置 */
+export function update(id, params) {
+    return request({
+        url: `/ncs/entrace_guard_user/${id}`,
+        method: 'put',
+        loading: false,
+        data: params
+    })
+}
+
+/** 更新用户记录 */
+export function refreshUser(part_id) {
+    return request({
+        url: `/ncs/entrace_guard_user/refresh/${part_id}`,
+        method: 'post'
+    })
+}

+ 33 - 0
src/api/ncs_frameGroup.js

@@ -81,3 +81,36 @@ export function getAllFrameTreeByType(frame_type) {
   })
 }
 
+
+/** 查询空间结构的管理人员信息 */
+export function getFrameManagedClerk(frameId) {
+  return request({
+    url: `/ncs/frame/getframemanagedclerk/${frameId}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+
+/** 保存空间结构的管理人员信息 */
+export function saveFrameManagedClerk(params) {
+  return request({
+    url: `/ncs/frame/saveframemanagedclerk`,
+    method: 'POST',
+    loading: true,
+    headers: { 'Content-Type': 'application/json' },
+    data:params
+  })
+}
+
+/** 查询某科室下多人共管的房间结构 */
+export function getframemultimanagedstruct(part_id, frame_type) {
+  return request({
+    url: `/ncs/frame/getframemultimanagedstruct/${part_id}/${frame_type}`,
+    method: 'get',
+    loading: false
+  })
+}
+
+
+

+ 7 - 0
src/api/ncs_functions.js

@@ -0,0 +1,7 @@
+import request from '@/utils/request'
+export function getAll() {
+    return request({
+        url: '/ncs/functions/getall',
+        method: 'get'
+    })
+}

+ 41 - 0
src/api/ncs_functions_role_mapping.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+export function getList(params) {
+    return request({
+        url: '/ncs/functionrolemapping/page',
+        method: 'POST',
+        data: params,
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+    })
+}
+export function getFunctionsRoleMappingByCodeAndPartId(function_code,part_id) {
+    return request({
+        url: `/ncs/functionrolemapping/${function_code}/${part_id}`,
+        method: 'GET'
+    })
+}
+export function remove(id) {
+    return request({
+        url: `/ncs/functionrolemapping/${id}`,
+        method: 'DELETE',
+        loading: true
+    })
+}
+/** 修改 */
+export function update(id, params) {
+    return request({
+        url: `/ncs/functionrolemapping/${id}`,
+        method: 'put',
+        data: params,
+        headers: { 'Content-Type': 'application/json' }
+    })
+}
+/** 新增 */
+export function add(params) {
+    return request({
+        url: '/ncs/functionrolemapping',
+        method: 'POST',
+        loading: true,
+        data: params,
+        headers: { 'Content-Type': 'application/json' }
+    })
+}

+ 35 - 0
src/api/ncs_interaction-chain.js

@@ -0,0 +1,35 @@
+import request from '@/utils/request'
+export function getList(params) {
+    return request({
+        url: '/ncs/spreadchain/page',
+        method: 'POST',
+        data: params,
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+    })
+}
+
+export function remove(id) {
+    return request({
+        url: `/ncs/spreadchain/${id}`,
+        method: 'DELETE',
+        loading: true
+    })
+}
+/** 修改 */
+export function update(id, params) {
+    return request({
+        url: `/ncs/spreadchain/${id}`,
+        method: 'put',
+        data: params
+    })
+}
+/** 新增 */
+export function add(params) {
+    return request({
+        url: '/ncs/spreadchain',
+        method: 'POST',
+        loading: true,
+        data: params,
+        headers: { 'Content-Type': 'application/json' }
+    })
+}

+ 15 - 0
src/api/role.js

@@ -60,3 +60,18 @@ export function editRole(id, params) {
     data: params
   })
 }
+
+/**
+ * 获取代码中角色枚举类型
+ * @param id
+ */
+export function getRoleEnums() {
+  return request({
+    url: `/mgr/role/rolenums`,
+    method: 'get'
+  })
+}
+
+
+
+

+ 9 - 4
src/components/MarkdownEditor/index.vue

@@ -4,11 +4,16 @@
 
 <script>
 // deps for editor
-import 'codemirror/lib/codemirror.css' // codemirror
-import 'tui-editor/dist/tui-editor.css' // editor ui
-import 'tui-editor/dist/tui-editor-contents.css' // editor content
+// import 'codemirror/lib/codemirror.css' // codemirror
+// import 'tui-editor/dist/tui-editor.css' // editor ui
+// import 'tui-editor/dist/tui-editor-contents.css' // editor content
 
-import Editor from 'tui-editor'
+// import Editor from 'tui-editor'
+// import defaultOptions from './default-options'
+
+import 'codemirror/lib/codemirror.css'
+import '@toast-ui/editor/dist/toastui-editor.css'
+import Editor from '@toast-ui/editor'
 import defaultOptions from './default-options'
 
 export default {

Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/delete.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/designer.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/frame_manage.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/list-move.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/pass_through.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/pen-leather.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/role.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/setting.svg


Різницю між файлами не показано, бо вона завелика
+ 1 - 0
src/icons/svg/squence.svg


+ 1 - 0
src/layout/components/Navbar.vue

@@ -157,6 +157,7 @@ export default {
     switchLanguage(value) {
       if (value === 'zh') {
         this.$i18n.locale = 'zh'
+
       } else if (value === 'en') {
         this.$i18n.locale = 'en'
       } else if (value === 'es') {

+ 87 - 0
src/router/index.js

@@ -146,6 +146,34 @@ export const partRoutes = [
       }
     ]
   },
+  {
+    path: '/entrace-guard',
+    component: Layout,
+    redirect: '/entrace-guard/users',
+    children: [
+      {
+        path: 'users',
+        component: () => import('@/views/entrace-guard/users'),
+        name: 'users',
+        meta: {title: i18n.t('tab.entraceguardUser'), icon: 'pass_through', noCache: true}
+      }]
+
+  },
+  {
+    path: '/ncs-clerk-frame-manage',
+    component: Layout,
+    redirect: '/ncs-clerk-frame-manage/index',
+    children: [
+      {
+        path: 'clerkList',
+        component: () => import('@/views/ncs-clerk-frame-manage/index'),
+        name: 'clerkList',
+        meta: {title: i18n.t('tab.staffManageFrames'), icon: 'frame_manage', noCache: true}
+
+      }
+    ]
+  },
+
   // {
   //   path: '/customer',
   //   component: Layout,
@@ -305,6 +333,7 @@ export const partRoutes = [
       }
     ]
   },
+
   // {
   //   path: '/calling-message',
   //   component: Layout,
@@ -318,6 +347,7 @@ export const partRoutes = [
   //     }
   //   ]
   // },
+
   {
     path: '',
     component: Layout,
@@ -395,6 +425,37 @@ export const partRoutes = [
     ],
     hidden: uiVersion === 2
   },
+
+  {
+    path: '/board-title',
+    component: Layout,
+    redirect: '/custom-infoboard/board-title',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/custom-infoboard/board-title'),
+        name: 'BoardTitle',
+        meta: { title: i18n.t('tab.customBoardManage'), icon: 'designer', noCache: true }
+      }
+    ],
+    hidden: uiVersion === 2
+  },
+  {
+    path: '/calling-board-designer',
+    component: Layout,
+    redirect: '/custom-infoboard/screen-designer',
+    children: [
+      {
+        path: 'index/:id?',
+        component: () => import('@/views/custom-infoboard/screen-designer'),
+        name: 'BoardDesigner',
+        meta: { title: i18n.t('tab.customBoardDesigner'), icon: 'el-icon-data-board', noCache: true }
+      }
+    ],
+    hidden: true
+  },
+
+
   {
     path: '/ncs-event',
     component: Layout,
@@ -436,6 +497,32 @@ export const partRoutes = [
     ]
   },
   {
+    path: '/function-mapping',
+    component: Layout,
+    redirect: '/function-mapping/index',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/function-mapping/index'),
+        name: 'functionMapping',
+        meta: { title: i18n.t('tab.functionRoleMapping'), icon: 'function', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/interaction-chain',
+    component: Layout,
+    redirect: '/interaction-chain/index',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/interaction-chain/index'),
+        name: 'interactionChain',
+        meta: { title: i18n.t('tab.interactionChain'), icon: 'squence', noCache: true }
+      }
+    ]
+  },
+  {
     path: '/calling-setting',
     component: Layout,
     redirect: '/calling-setting/index',

+ 102 - 0
src/styles/element-ui.scss

@@ -82,3 +82,105 @@
 .el-range-separator {
   box-sizing: content-box;
 }
+
+/*  -- flex弹性布局 -- */
+
+.flex {
+  display: flex;
+}
+
+.basis-xs {
+  flex-basis: 20%;
+}
+
+.basis-sm {
+  flex-basis: 40%;
+}
+
+.basis-df {
+  flex-basis: 50%;
+}
+
+.basis-lg {
+  flex-basis: 60%;
+}
+
+.basis-xl {
+  flex-basis: 80%;
+}
+
+.flex-sub {
+  flex: 1;
+}
+
+.flex-twice {
+  flex: 2;
+}
+
+.flex-treble {
+  flex: 3;
+}
+
+.flex-direction {
+  flex-direction: column;
+}
+
+.flex-wrap {
+  flex-wrap: wrap;
+}
+
+.align-start {
+  align-items: flex-start;
+}
+
+.align-end {
+  align-items: flex-end;
+}
+
+.align-center {
+  align-items: center;
+}
+
+.align-stretch {
+  align-items: stretch;
+}
+
+.self-start {
+  align-self: flex-start;
+}
+
+.self-center {
+  align-self: flex-center;
+}
+
+.self-end {
+  align-self: flex-end;
+}
+
+.self-stretch {
+  align-self: stretch;
+}
+
+.align-stretch {
+  align-items: stretch;
+}
+
+.justify-start {
+  justify-content: flex-start;
+}
+
+.justify-end {
+  justify-content: flex-end;
+}
+
+.justify-center {
+  justify-content: center;
+}
+
+.justify-between {
+  justify-content: space-between;
+}
+
+.justify-around {
+  justify-content: space-around;
+}

+ 5 - 2
src/utils/mixin.js

@@ -35,9 +35,10 @@ export default {
           picker.$emit('pick', [start, end])
         }
       }],
-      mainAreaHeight: Number(document.documentElement.clientHeight) - 84
+      mainAreaHeight:Number(document.documentElement.clientHeight) - 84
     }
   },
+
   computed: {
     /**
      * 缓存页面数组
@@ -46,6 +47,7 @@ export default {
     cachedViews() {
       return this.$store.state.tagsView.cachedViews
     },
+
     /**
      * 返回默认时间 + 5分钟
      * 用于日期时间选择器的默认时间
@@ -64,8 +66,9 @@ export default {
     }
   },
   methods: {
-    /** 返回克隆后的对象 */
 
+
+    /** 返回克隆后的对象 */
     MixinClone(obj) {
       return JSON.parse(JSON.stringify(obj))
     },

+ 1 - 0
src/views/calling-board/index.vue

@@ -440,6 +440,7 @@ export default {
     },
 
     updateBoard() {
+      this.boardConfig.staySeconds=[this.boardConfig.staySeconds[0],this.boardConfig.staySeconds[1],...this.boardTitles.map((p,index)=>Number(this.boardConfig.staySeconds[index+2]))]
       if (this.boardConfig.staySeconds.length !== this.boardTitles.length + 2) {
         this.$message.success(this.$t('board.statusTimeSetMsg'))
         return false

+ 426 - 0
src/views/custom-infoboard/board-title.vue

@@ -0,0 +1,426 @@
+<template>
+    <div>
+        <ag-grid-layout
+                toolbar
+                :table-height="tableHeight"
+                theme="ag-theme-alpine"
+                :column-defs="columnDefs"
+                :row-data="rowData"
+                :locale-text="localeText"
+                :grid-options="gridOptions"
+                :default-col-def="defaultColDef"
+                :animate-rows="true"
+                :row-selection="rowSelection"
+                :enable-cell-change-flash="true"
+                @filterChanged="filterModifed"
+                @sortChanged="gridSortChange"
+        >
+            <!--        @rowDoubleClicked="getList"-->
+            <div slot="toolbar" class="inner-toolbar">
+                <div class="toolbar-search">
+                    <en-table-search :placeholder="this.$t('action.keywords')" @search="handlerSearch" />
+                </div>
+                <div class="toolbar-btns">
+                    <el-button type="primary" size="mini" @click="createChannel">{{ this.$t('boardTitle.add') }}</el-button>
+                </div>
+            </div>
+            <el-pagination
+                    v-if="pageData"
+                    slot="pagination"
+                    :current-page="pageData.page_no"
+                    :page-sizes="[10, 20, 50, 100]"
+                    :page-size="pageData.page_size"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="pageData.data_total"
+                    @size-change="handlePageSizeChange"
+                    @current-change="handlePageCurrentChange"
+            />
+        </ag-grid-layout>
+        <el-dialog :title.sync="formtitle" :visible.sync="formshow" width="35%">
+            <div>
+                <el-form ref="editform" :rules="rules" label-width="120px" :model="formmodel">
+
+                    <el-row>
+                        <el-col :span="24">
+                            <el-form-item :label="this.$t('boardTitle.screenTitle')" prop="board_title">
+                                <el-input
+                                        v-model="formmodel.board_title"
+                                        clearable
+                                        :maxlength="100"
+                                        :placeholder="this.$t('boardTitle.screenTitle')"
+                                />
+                            </el-form-item>
+                        </el-col>
+                        <el-col :span="12">
+                            <el-form-item :label="this.$t('boardTitle.showIndex')" prop="board_index">
+                                <el-input-number v-model="formmodel.board_index" :min="3" :max="100" :label="this.$t('boardTitle.showIndex')" />
+                            </el-form-item>
+                        </el-col>
+                    </el-row>
+                </el-form>
+
+            </div>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="formshow = false">{{ this.$t('action.cancel') }}</el-button>
+                <el-button type="primary" @click="handlerFormSubmit('editform')">{{ this.$t('action.yes') }}</el-button>
+            </div>
+        </el-dialog>
+
+    </div>
+
+</template>
+
+<script>
+    import { AG_GRID_LOCALE_CN } from '@/utils/AgGridVueLocaleCn'
+    import ButtonCellRender from '../../components/AgGridCellRender/ButtonCellRender'
+    import * as API_BoardTitle from '@/api/ncs_board_title'
+    import { unix2Date } from '@/utils/Foundation'
+    import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
+    import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+    import ButtonCellRenderList from "@/components/AgGridCellRender/ButtonCellRenderList";
+
+    export default {
+        name: "board-title",
+        components: { ButtonCellRenderList, ButtonCellRender, ListFilter, RadioFilter },
+        data() {
+            return {
+                tableData: [],
+                /** 列表参数 */
+                params: {
+                    page_size: 20,
+                    page_no: 1,
+                    fixedCondition: ' part_id = ' + this.$store.getters.partId
+                },
+                /** 新建组织弹出参数 **/
+                formtitle: this.$t('boardTitle.createTitle'),
+                formshow: false,
+                /** 频道订阅设置 */
+                formSubscribe: false,
+                formmodel: {},
+                frameGroups: [],
+                rules: {
+                    board_title: [
+                        { required: true, message: this.$t('boardTitle.titleRequire'), trigger: 'blur' }
+                    ]
+                },
+                /** ag-grid参数 **/
+                pageData: [],
+                loading: false,
+                errorId: null,
+                shopVisible: false,
+                columnDefs: null,
+                rowData: null,
+                defaultColDef: null,
+                gridOptions: null,
+                gridApi: null,
+                columnApi: null,
+                localeText: AG_GRID_LOCALE_CN,
+                filterState: null,
+                rowSelection: null,
+                frameworkComponents: null,
+                /** 频道订阅者情况数组 */
+                channelSubscribers: [],
+                subscribeTitle: '',
+                choiceAll: this.$t('action.choiceAll')
+            }
+        },
+        computed: {
+            tableHeight() {
+                return this.mainAreaHeight - 130
+            }
+
+        },
+        beforeMount() {
+            this.gridOptions = {}
+            this.columnDefs = [
+                {
+                    headerName: '#',
+                    headerCheckboxSelection: true,
+                    headerCheckboxSelectionFilteredOnly: true,
+                    checkboxSelection: true,
+                    sortable: false, filter: false,
+                    width: 100,
+                    resizable: false,
+                    valueGetter: this.hashValueGetter
+                },
+                { headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 130 },
+                {
+                    headerName: this.$t('boardTitle.screenTitle'), field: 'board_title', sortable: true, filter: 'agTextColumnFilter', flex: 1
+                },
+                {
+                    headerName: this.$t('boardTitle.showIndex'), field: 'board_index', sortable: true, filter: 'agNumberColumnFilter', flex: 1
+                },
+                {
+                    headerName: this.$t('boardTitle.partId'), field: 'part_id', sortable: true, filter: 'agNumberColumnFilter', flex: 1
+                },
+
+                // lockPosition 锁定位置,会在第一列
+                // lockPinned = true 不能拖动然后固定
+                // resizeable 单元个大小是否可以调整
+                { headerName: this.$t('action.handle'), field: 'id',
+                    cellRendererFramework: 'ButtonCellRenderList',
+                    cellRendererParams: param => {
+                        return {
+                            list: [
+                                {
+                                    onClick: this.handEdit,
+                                    label: this.$t('action.edit'),
+                                    buttonType: 'primary',
+                                    buttonSize: 'mini'
+                                },
+                                {
+                                    onClick: this.deleteSingle,
+                                    label: this.$t('action.delete'),
+                                    buttonType: 'danger',
+                                    buttonSize: 'mini',
+                                },
+                                {
+                                    onClick: this.toDesigner,
+                                    label: this.$t('boardTitle.design'),
+                                    buttonType: 'warning',
+                                    buttonSize: 'mini',
+                                }
+
+                            ]}
+                    },
+                    filter: false,
+                    pinned: 'right',
+                    lockPinned: true,
+                    minWidth: this.$i18n.locale === 'zh' ? 310 : 350,
+                    resizable: false,
+                    sortable: false
+                }
+
+            ]
+            this.defaultColDef = {
+                sortable: true,
+                resizable: true,
+                comparator: this.dateCustomComparator,
+                filterParams: {
+                    debounceMs: 200,
+                    newRowsAction: 'keep',
+                    textCustomComparator: this.textCustomComparator,
+                    comparator: this.dateCustomComparator
+                }
+            }
+            this.rowSelection = 'multiple'
+        },
+        mounted() {
+            window.onresize = this.windowResize
+            this.gridApi = this.gridOptions.api
+            this.gridColumnApi = this.gridOptions.columnApi
+            // 设置默认排序字段,应用列状态之后会触发 gridSortChange 函数,会调用getlist,后面不需要再调用this.getlist
+            this.gridColumnApi.applyColumnState({
+                state: [
+                    {
+                        colId: 'id',
+                        sort: 'asc'
+                    }
+                ]
+            })
+
+        },
+        methods: {
+            windowResize() {
+                this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
+            },
+            handlerDelete(ids) {
+                this.$confirm(this.$t('action.sureDelete'), this.$t('action.waring'), {
+                    confirmButtonText: this.$t('action.yes'),
+                    cancelButtonText: this.$t('action.cancel'),
+                    type: 'warning'
+                }).then(() => {
+                    API_BoardTitle.remove(ids).then(
+                        response => {
+                            this.getList()
+                            this.$message({
+                                type: 'success',
+                                message: this.$t('action.deleted')
+                            })
+                        }
+                    ).catch(response => {
+                        this.$message({
+                            type: 'info',
+                            message: response.message
+                        })
+                    })
+                }).catch(() => {
+                    this.$message({
+                        type: 'info',
+                        message: this.$t('action.cancelDelete')
+                    })
+                })
+            },
+            deleteSingle(row) {
+                this.handlerDelete(row.id)
+            },
+
+            /**
+             * 创建频道
+             */
+            createChannel() {
+                this.formshow = true
+                this.formmodel = {
+                    channel_name: '',
+                    part_id: this.$store.getters.partId
+                }
+            },
+            /** 分页大小发生改变 */
+            handlePageSizeChange(size) {
+                this.params.page_size = size
+                this.getList()
+            },
+
+            /** 分页页数发生改变 */
+            handlePageCurrentChange(page) {
+                this.params.page_no = page
+                this.getList()
+            },
+            /** 加载列表数据 */
+            getList() {
+                this.loading = true
+                const param = this.MixinClone(this.params)
+                this.gridApi.showLoadingOverlay()
+                API_BoardTitle.getList(param).then(response => {
+                    this.loading = false
+                    // this.tableData = [...response.data]
+                    this.pageData = {
+                        page_no: response.page_no,
+                        page_size: response.page_size,
+                        data_total: response.data_total
+                    }
+                    this.$nextTick(() => {
+                        const node = this.gridApi.getDisplayedRowAtIndex(0)
+                        if (node !== null && node !== undefined) {
+                            node.setSelected(true)
+                        }
+                    })
+                    this.rowData = [...response.data]
+                    this.refreshPlayStatus()
+                }).catch(() => {
+                    this.loading = false
+                })
+            },
+            /** 处理搜索 */
+            handlerSearch(keywords) {
+                this.params.query = keywords
+                this.getList()
+            },
+            /** 处理字段排序 */
+            tableSort(column) {
+                if (column.order !== null) {
+                    this.params.sort = column.prop
+                    this.params.dir = column.order === 'ascending' ? 'asc' : 'desc'
+                } else {
+                    this.params.sort = null
+                    this.params.dir = null
+                }
+                this.getList()
+            },
+            /**
+             * 格式化unix时间戳
+             **/
+            unixDateFormatter(param) {
+                if (!param.value) return ''
+                return unix2Date(param.value * 1000)
+            },
+
+            gridSortChange(param) {
+                const columnState = param.columnApi.getColumnState()
+                // 排序状态
+                const sortState = columnState.filter(function(s) {
+                    return s.sort != null
+                }).map(function(s) {
+                    return {
+                        colId: s.colId,
+                        sort: s.sort,
+                        sortIndex: s.sortIndex
+                    }
+                }).sort(function(a, b) {
+                    return a.sortIndex - b.sortIndex
+                })
+                if (sortState.length > 0) {
+                    if (sortState.length === 1) {
+                        this.params.sort = sortState[0].colId
+                        this.params.dir = sortState[0].sort
+                    } else {
+                        let sortstring = ''
+                        sortState.forEach(function(item) {
+                            sortstring += item.colId + ' ' + item.sort + ','
+                        })
+                        this.params.sort = sortstring.substring(0, sortstring.length - 1)
+                        this.params.dir = ' '
+                    }
+                } else {
+                    delete this.params.sort
+                    delete this.params.dir
+                }
+                this.getList()
+            },
+
+            filterModifed(param) { // todo 通过转换后的数值过滤,需要转回原始数值
+                var model = param.api.getFilterModel()
+                this.params.filter = JSON.stringify(model)
+                this.getList()
+            },
+
+            handEdit(row) {
+                this.formtitle=this.$t('boardTitle.editTitle')
+                this.formmodel = {
+                    ...row
+                }
+                this.formshow = true
+            },
+            /**
+             * 提交新增表单
+             * @param formname
+             */
+            handlerFormSubmit(formName) {
+                this.$refs[formName].validate((valid) => {
+                    if (valid) {
+                        if (!this.formmodel.id) {
+                            /** 新增 */
+                            this.formmodel.part_id = this.$store.getters.partId
+
+                            API_BoardTitle.add(this.formmodel).then(() => {
+                                this.formshow = false
+                                this.$message.success(this.$t('action.addSuccess'))
+                                this.getList()
+                            })
+                        } else {
+                            API_BoardTitle.update(this.formmodel.id, this.formmodel).then(() => {
+                                this.formshow = false
+                                this.$message.success(this.$t('action.editSuccess'))
+                                this.getList()
+                            })
+                        }
+                    } else {
+                        this.$message.error(this.$t('action.fromError'))
+                    }
+                })
+            },
+
+            handlerSubscribeSubmit() {
+                let subscribeIds = []
+                this.channelSubscribers.forEach(item => {
+                    subscribeIds = [...item.clerks.filter(p => p.checked).map(p => p.member_id), ...subscribeIds]
+                })
+                API_BoardTitle.setChannelSubscribers({ channel_id: this.formmodel.id, member_ids: subscribeIds.length === 0 ? null : [...subscribeIds] }).then(() => {
+                    this.formSubscribe = false
+                    this.$message.success(this.$t('action.editSuccess'))
+                })
+            },
+
+            toDesigner(row) {
+                this.$router.push({ name: 'BoardDesigner', params: { id:row.id }})
+            }
+
+
+        }
+    }
+
+</script>
+
+<style scoped>
+
+</style>

+ 69 - 0
src/views/custom-infoboard/mixin.js

@@ -0,0 +1,69 @@
+import layoutItem from './templates/layout-item'
+export default {
+    props: {
+        /** 数据 */
+        data: {
+            type: Object,
+            default: () => ({})
+        },
+        /** 是否为编辑模式 */
+        isEdit: {
+            type: Boolean,
+            default: false
+        }
+    },
+    components: {
+        layoutItem
+    },
+    methods: {
+        /** 获取颜色相关信息 */
+        colors(columnIndex = 0) {
+            const _colors = this.data.columnList[columnIndex].titleColors
+            return {
+                title: `background-color: ${_colors[0]}; background-image: linear-gradient(90deg, ${_colors.join(',')});`,
+                color: (colorIndex = 0) => `color: ${_colors[colorIndex]}`
+            }
+        },
+        /** 获取区块链接 */
+        blockHref(block) {
+            const { opt_type, opt_value } = block.block_opt
+            switch (opt_type) {
+                // 链接地址
+                case 'URL': return opt_value
+                // 商品
+                case 'GOODS': return `/goods/${opt_value}`
+                // 关键字
+                case 'KEYWORD': return `/goods?keyword=${encodeURIComponent(opt_value)}`
+                // 店铺
+                case 'SHOP': return `/shop/${opt_value}`
+                // 分类
+                case 'CATEGORY': return `/goods?category=${opt_value}`
+                default: return '/'
+            }
+        },
+        /** 构建空的block */
+        emptyBlock(num = 3, type) {
+            return [...new Array(num)].map(() => ({
+                block_type: type,
+                block_value: '',
+                block_opt: {
+                    opt_type: 'NONE',
+                    opt_value: ''
+                }
+            }))
+        },
+        /** 编辑区块 */
+        handleEditBlock(columnIndex, blockIndex) {
+            console.log(JSON.stringify(this.data))
+            this.$emit('edit-block', JSON.parse(JSON.stringify(this.data)), columnIndex, blockIndex)
+        },
+        /** 编辑标题 */
+        handleEditTitle(columnIndex) {
+            this.$emit('edit-title', JSON.parse(JSON.stringify(this.data)), columnIndex)
+        },
+        /** 编辑标签 */
+        handleEditTags(columnIndex) {
+            this.$emit('edit-tags', JSON.parse(JSON.stringify(this.data)), columnIndex)
+        }
+    }
+}

+ 459 - 0
src/views/custom-infoboard/screen-designer.vue

@@ -0,0 +1,459 @@
+<template>
+    <div class="container">
+        <el-header class="bg-blue" style="width: 1210px;margin:0 auto;">
+            <!--        <el-image :src="logo" style="width: 50px; height: 50px" fit="fill" />-->
+            <el-row>
+                <el-col :span="8">
+                    <div class="title text-shadow text-white text-left " style="font-size: 26px;overflow: hidden;text-overflow: ellipsis;width: 100%;height:60px;">{{ partInfo.full_name }} {{ partInfo.part_name }}</div>
+                </el-col>
+                <el-col :span="8">
+                    <div class="content_title" data-aos="zoom-in-up">{{ titleData.board_title }}</div>
+                </el-col>
+                <el-col :span="8" >
+                    <div class="time text-right" style="font-size: 26px">{{ currentTime }}</div>
+                </el-col>
+            </el-row>
+        </el-header>
+        <div class="floor-container" :style="{height:floorContainerHeight+'px' }">
+
+            <div class="tpl-box" :style="tplBoxStyle">
+                <draggable v-model="templateArray" :options="tplOptions" class="tpl-list">
+                    <div v-for="item in templateArray" :class="'item-' + item.tpl_id" class="tpl-item">
+                        <el-row>
+                            <el-col :span="24/item.tpl_id" v-for="size in item.tpl_id" :key="size" class="grid-content" :class="'bg-purple-'+size"></el-col>
+                        </el-row>
+                        <span class="text-tpl">{{ templates[item.tpl_id].title }}</span>
+                    </div>
+                </draggable>
+                <el-button type="primary" @click="handleSaveFloor" class="save-btn">保存发布</el-button>
+                <div class="tpl-btns">
+                    <div class="btn-item" @click="tplBoxShow = !tplBoxShow">
+                        <i v-if="tplBoxShow" class="el-icon-d-arrow-left"></i>
+                        <i v-else class="el-icon-d-arrow-right"></i>
+                    </div>
+                    <div style="border-top: 1px dashed #ccc;margin: 2px 0"></div>
+                    <el-tooltip class="item" effect="dark" content="快捷保存" placement="right">
+                        <div class="btn-item" @click="handleSaveFloor">
+                            <i class="el-icon-upload"></i>
+                        </div>
+                    </el-tooltip>
+                </div>
+            </div>
+
+            <div class="draggable-box floor">
+                <div class="floor-body">
+                    <draggable v-model="floorList" :options="floorOptions" class="floor-list">
+                        <div v-for="(item, index) in floorList" :class="'item-' + item.tpl_id" class="floor-item">
+                            <component
+                                    :is="templates[item.tpl_id]"
+                                    :data="JSON.parse(JSON.stringify(item))"
+                                    is-edit
+                                    @edit-block="(...props) => {  handleEditBlock(index, ...props) }"
+                                    @edit-title="(...props) => { handleEditTitle(index, ...props) }"
+                                    @edit-tags="(...props) => { handleEditTags(index, ...props) }"
+                            ></component>
+                            <div class="panel-handle">
+                                <span class="icon-handle handle-move"><svg-icon icon-class="drag"/></span>
+                                <span class="icon-handle handle-setting" @click="() => { handleModule(index, JSON.parse(JSON.stringify(item))) }"><svg-icon icon-class="setting"/></span>
+                                <span class="icon-handle handle-delete" @click="floorList.splice(index, 1)"><svg-icon icon-class="delete"/></span>
+                            </div>
+                        </div>
+                    </draggable>
+                </div>
+
+            </div>
+
+        </div>
+
+        <board-item-setting
+        :default-data="boardItemConfig"
+        :show="settingShow"
+        :board-items="boardItems"
+        @close="settingShow=false"
+        @confirm="handleBoardItemSet"
+        ></board-item-setting>
+
+        <board-module-setting
+                :default-data="boardModuleConfig"
+                :show="moduleSettingShow"
+                @close="moduleSettingShow=false"
+                @confirm="handleBoardModuleSet"
+        >
+
+        </board-module-setting>
+    </div>
+</template>
+
+<script>
+    import Vue from 'vue'
+    import draggable from 'vuedraggable'
+    import templates, { templateArray } from './templates'
+    import VueLazyload from 'vue-lazyload'
+    import BoardItemSetting from "./templates/board-item-setting";
+    import BoardModuleSetting from "./templates/board-module-setting";
+    import * as API_BoardTitle from '@/api/ncs_board_title'
+    import * as API_BoardItem from '@/api/ncs_board_item'
+    Vue.use(VueLazyload)
+    import moment from 'moment'
+    require('moment/locale/zh-cn')
+    export default {
+        name: "screen-designer",
+        components: {BoardModuleSetting, BoardItemSetting, draggable },
+        data(){
+            return{
+                currentTime: '',
+                templates,
+                templateArray,
+                boardItemConfig:{},
+                boardModuleConfig:{},
+                settingShow:false,
+                moduleSettingShow:false,
+                //看板标题
+                titleData:{},
+                //标题id
+                titleId:this.$route.params.id,
+                /** 模板列表 */
+                tplList: [],
+                /** 模板配置 */
+                tplOptions: {
+                    group: { name: 'tplGroup', pull: 'clone', put: false },
+                    sort: false
+                },
+                tplBoxShow:true,
+                /** 楼层列表 */
+                floorList: [],
+                /** 楼层配置 */
+                floorOptions: {
+                    animation: 150,
+                    group: { name: 'tplGroup', put: true },
+                    sort: true,
+                    handle: '.handle-move'
+                },
+                boardItems:[{
+                    area_content: "2,5,7",
+                    area_content_style: "color:green;",
+                    area_label: "今日出院",
+                    area_label_style: "color:red;",
+                    area_size: 1,
+                    his_board_title_keyval: "1",
+                    his_keyval: "1",
+                    his_part_keyval: "1"
+                }]
+
+            }
+        },
+        computed: {
+            // 楼层模板盒子样式
+            tplBoxStyle() {
+                const { sidebar } = this.$store.getters
+                let left = (sidebar.opened ? 210 : 60)
+                left = this.tplBoxShow ? left : left - 300
+                return {
+                    left: left + 'px'
+                }
+            },
+            floorContainerHeight() {
+                return  this.mainAreaHeight-60
+            },
+            partInfo() {
+                return this.$store.getters.organization
+            }
+        },
+        beforeRouteUpdate(to, from, next) {
+            this.titleId = to.params.id
+            next()
+        },
+        activated() {
+            this.titleId = this.$route.params.id
+        },
+       async mounted(){
+           this.boardItems = await API_BoardItem.getPartList(this.$store.getters.partId)
+            moment.locale()
+            this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss dddd')
+            this.getBoardTitle()
+
+        },
+        methods:{
+            /** 保存发布 */
+            handleSaveFloor() {
+                this.titleData.content_config= JSON.stringify(this.floorList)
+                API_BoardTitle.update(this.titleData.id, this.titleData).then(() => this.$message.success('保存发布成功!'))
+            },
+
+            handleBoardItemSet(settingData){
+              console.log(settingData)
+                const { index, target, columnIndex } = this.editOptions
+                const blockData = {...settingData}
+                target.columnList[columnIndex]=blockData
+                this.$set(this.floorList, index, target)
+            },
+            handleBoardModuleSet(settingData){
+                const { index, blockdata } = this.editOptions
+                const moduleSetting = {...settingData}
+                blockdata.moduleStyle=moduleSetting
+                this.$set(this.floorList, index, blockdata)
+            },
+
+            /** 编辑楼层区块 */
+            handleEditBlock(index, target, columnIndex) {
+
+                const blockData = target.columnList[columnIndex]
+
+                this.settingShow=true
+                this.boardItemConfig = {...blockData}
+                // const block = target.columnList[columnIndex].blockList[blockIndex]
+                // const type = block.block_type
+                 this.editOptions = { index, target, columnIndex }
+                // const blockData = JSON.parse(JSON.stringify(block))
+                // if (type === 'IMAGE') {
+                //     this.defaultImageData = blockData.block_value ? [{
+                //         url: blockData.block_value,
+                //         opt: blockData.block_opt
+                //     }] : null
+                //     this.dialogImageShow = true
+                // } else if (type === 'GOODS') {
+                //     // 填充默认数据
+                //     // this.defaultGoodsData = blockData.block_value ? [blockData.block_value.goods_id] : []
+                //     // this.dialogGoodsShow = true
+                // } else if (type === 'BRAND') {
+                //     console.log('品牌模块')
+                // }
+            },
+
+            handleModule(index,blockdata){
+               this.boardModuleConfig = {...blockdata.moduleStyle}
+                this.editOptions = { index, blockdata }
+                this.moduleSettingShow=true
+              console.log(index,blockdata)
+            },
+
+            /** 编辑楼层标题 */
+            handleEditTitle(index, target, columnIndex) {
+                this.editOptions = { index, target, columnIndex }
+                const column = target.columnList[columnIndex]
+                const columnData = JSON.parse(JSON.stringify(column))
+                this.defaultTitleData = {
+                    text: column.title,
+                    start_color: column.titleColors[0],
+                    end_color: column.titleColors[1]
+                }
+                this.dialogTitleShow = true
+            },
+            /** 编辑楼层标签 */
+            handleEditTags(index, target, columnIndex) {
+                this.editOptions = { index, target, columnIndex }
+                const column = target.columnList[columnIndex]
+                const columnData = JSON.parse(JSON.stringify(column))
+                this.defaultTagsData = columnData.tagList
+                this.dialogTagsShow = true
+            },
+            getBoardTitle(){
+                API_BoardTitle.get(this.titleId,{}).then(
+                    res=>{
+                        console.log('res',res)
+                     this.titleData={...res}
+                     if(res.content_config!==null&&res.content_config!==''){
+                         this.floorList=JSON.parse(res.content_config)
+                         this.floorList.forEach(item=>{
+                             item.columnList.forEach(col=>{
+                                 if(col.boardItemHisKeyVal!==''){
+                                     console.log('col', this.boardItems)
+                                     const boardItem = this.boardItems.filter(p=>p.his_keyval===col.boardItemHisKeyVal)[0]
+                                     console.log('boardItem',boardItem)
+                                     if(boardItem!==null&&boardItem!==undefined){
+                                         col.content=boardItem.area_content
+                                         col.label=boardItem.area_label
+                                     }
+                                 }
+                             })
+                         })
+                     }else{
+                         this.floorList=[]
+                     }
+                    }
+                ).catch(error=>{
+                  this.$message.error(error.message)
+                })
+            }
+
+        }
+    }
+</script>
+
+<style scoped type="text/scss">
+    .container {
+        min-width: 1366px;
+    }
+    .floor-container {
+        display: flex;
+        justify-content: space-around;
+        background-color: #E5E7EA;
+        padding: 10px;
+    }
+    .draggable-box {
+        position: relative;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-direction: column;
+        width: 100%;
+
+    }
+    .draggable-box .floor {
+        width: 800px + 50px;
+        flex-shrink: 0;
+        align-items: center;
+    }
+    .tpl-list {
+        display: flex;
+        flex-wrap: wrap;
+        overflow: hidden;
+        width: 100%;
+        background-color: #fff;
+    }
+    .tpl-item {
+        display: flex;
+        width: 100%;
+        flex-direction: column;
+        justify-content: center;
+        box-sizing: border-box;
+        border-bottom: 2px solid #D9E0E7;
+        margin-bottom: 10px;
+        /*&.item-1 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-01.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+        /*&.item-2 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-02.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+        /*&.item-3 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-03.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+        /*&.item-4 .img-tpl {*/
+        /*    background: url("../../../assets/pc-tpl-04.png") no-repeat;*/
+        /*    background-size: 100%;*/
+        /*}*/
+    }
+    .img-tpl {
+        width: 100%;
+        min-height: 150px;
+    }
+    .text-tpl {
+        text-align: center;
+        margin: 5px 0;
+        color: #ACB0B9;
+        font-size: 12px;
+    }
+    .floor-body {
+        display: flex;
+        justify-content: center;
+        width: 100%;
+        height: 100%;
+        overflow-y: scroll;
+    }
+    .floor-list {
+        background-color: #E5E7EA;
+        min-width: 75%;
+        min-height: 500px;
+    }
+    .floor-item {
+        position: relative;
+        box-sizing: border-box;
+
+    }
+
+
+    .floor-item .panel-handle {
+        display: none;
+        position: absolute;
+        top: 0;
+        right: -25px;
+
+    }
+    .floor-item .panel-handle .icon-handle {
+        display: block;
+        cursor: pointer;
+        text-align: center;
+    }
+    .floor-item .panel-handle .svg-icon {
+        width: 25px;
+        height: 25px;
+        background-color: #fff;
+    }
+    .floor-item:hover .panel-handle{
+        display: block;
+    }
+    .floor-item:first-child .floor-layout {
+        margin-top: 0;
+    }
+    .tpl-box {
+        position: fixed;
+        top: 20%;
+        left: 180px;
+        z-index: 99;
+        width: 300px;
+        margin-top: (-500px - 32px + 84px) / 2;
+        border-top: 10px solid #fff;
+        box-shadow: 4px 5px 20px 0 rgba(0,0,0,.6);
+        transition: all ease .3s;
+    }
+
+    .tpl-box .tpl-list {
+        height: 300px;
+        overflow-y: scroll;
+    }
+    .tpl-box .save-btn {
+        width: 100%;
+    }
+    .tpl-box .tpl-btns {
+        position: absolute;
+        top: 50%;
+        right: -25px;
+        margin-top: -35px;
+        width: 25px;
+        height: 70px;
+        background-color: #fff;
+        text-align: center;
+        padding: 5px 0;
+
+    }
+
+    .tpl-box .tpl-btns .btn-item {
+        cursor: pointer;
+        padding: 5px 0;
+
+    }
+    .tpl-box .tpl-btns .btn-item + .btn-item {
+        margin-top: 8px;
+    }
+    .tpl-box .tpl-btns .btn-item:hover {
+        background-color: #46A0FC;
+        color: #fff
+    }
+    .bg-blue {
+        background-color: #0081ff;
+        color: #ffffff;
+    }
+    /deep/ .el-header{
+        line-height: 60px;
+    }
+    .content_title{font-size: 26px}
+    .grid-content {
+        border-radius: 4px;
+        min-height: 36px;
+    }
+    .el-col {
+        border-radius: 4px;
+    }
+    .bg-purple-1 {
+        background: #99a9bf;
+    }
+    .bg-purple-2 {
+        background: #d3dce6;
+    }
+    .bg-purple-3 {
+        background: #e5e9f2;
+    }
+</style>

+ 184 - 0
src/views/custom-infoboard/templates/board-item-setting.vue

@@ -0,0 +1,184 @@
+<template>
+    <el-dialog
+            :visible.sync="dialogVisible"
+            :close-on-click-modal="false"
+            :close-on-press-escape="false"
+            append-to-body
+            width="805px">
+        <div slot="title" class="image-picker-title">设置看板项</div>
+        <div class="image-picker-body">
+            <el-form :model="formmodel" label-width="100px">
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="标题文字颜色">
+                            <el-input placeholder="标题文字颜色" v-model="formmodel.labelColor">
+                                <el-color-picker slot="append" v-model="formmodel.labelColor" size="mini"></el-color-picker>
+                            </el-input>
+
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="标题背景颜色">
+                            <el-input placeholder="标题背景颜色" v-model="formmodel.labelBgColor">
+                                <el-color-picker slot="append" v-model="formmodel.labelBgColor" size="mini"></el-color-picker>
+                            </el-input>
+
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="内容文字颜色">
+                            <el-input placeholder="内容文字颜色" v-model="formmodel.contentColor">
+                                <el-color-picker slot="append" v-model="formmodel.contentColor" size="mini"></el-color-picker>
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+
+                            <el-form-item label="内容背景颜色">
+                                <el-input placeholder="内容背景颜色" v-model="formmodel.contentBgColor">
+                                    <el-color-picker slot="append" v-model="formmodel.contentBgColor" size="mini"></el-color-picker>
+                                </el-input>
+                            </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+
+                        <el-form-item label="标题区域宽度">
+                            <el-input placeholder="标题区域宽度" v-model="formmodel.labelWidth" type="number">
+                                <template slot="append">px</template>
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+
+                        <el-form-item label="标题文字大小">
+                            <el-input placeholder="标题文字大小" v-model="formmodel.labelTextSize" type="number">
+                                <template slot="append">px</template>
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+
+                    <el-col :span="12">
+                        <el-form-item label="内容文字大小">
+                            <el-input placeholder="内容文字大小" v-model="formmodel.contentTextSize" type="number">
+                                <template slot="append" class="padding-content">px</template>
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+               <el-col :span="24">
+                   <el-form-item label="显示看板项" prop="boardItemHisKeyVal">
+                       <el-select v-model="formmodel.boardItemHisKeyVal"
+                                  placeholder="请选择要显示的看板项"
+                                  @change="boardItemChange"
+                                  filterable
+                                  clearable
+                                  >
+                           <el-option v-for="(item, index) in boardItems" :key="index"
+                                      :label="item.area_label" :value="item.his_keyval"/>
+                       </el-select>
+                   </el-form-item>
+               </el-col>
+                </el-row>
+            </el-form>
+        </div>
+        <div slot="footer" class="image-picker-footer">
+            <span>
+        <el-button @click="dialogVisible = false">取 消</el-button>
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      </span>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    export default {
+        name: "board-item-setting",
+        props: {
+            /** 显示dialog */
+            show: {
+                type: Boolean,
+                default: false
+            },
+            /** 默认数据 */
+            defaultData: {
+                type: Object,
+                default: () => {
+                }
+            },
+            boardItems:{
+                type:Array,
+                default:()=>([])
+            }
+        },
+        data() {
+            return {
+                dialogVisible: this.show,
+                color:'#ffffff',
+                formmodel:{...this.defaultData},
+                predefineColors: [
+                    '#ff4500',
+                    '#ff8c00',
+                    '#ffd700',
+                    '#90ee90',
+                    '#00ced1',
+                    '#1e90ff',
+                    '#c71585',
+                    'rgba(255, 69, 0, 0.68)',
+                    'rgb(255, 120, 0)',
+                    'hsv(51, 100, 98)',
+                    'hsva(120, 40, 94, 0.5)',
+                    'hsl(181, 100%, 37%)',
+                    'hsla(209, 100%, 56%, 0.73)',
+                    '#c7158577'
+                ]
+            }
+        },
+        methods: {
+            handleConfirm() {
+                this.$emit('confirm', this.formmodel)
+                this.$emit('close')
+            },
+            boardItemChange(val){
+                console.log('selectchagne',val)
+                if(val!==''){
+                    const selected = this.boardItems.filter(p=>p.his_keyval===val)[0]
+                    this.formmodel.label = selected.area_label
+                    this.formmodel.content=selected.area_content
+                }else{
+                    this.formmodel.label = '标题'
+                    this.formmodel.content='内容'
+                }
+            }
+        },
+        watch: {
+            show(newVal) {
+                console.log('show', newVal)
+                this.dialogVisible = newVal
+            },
+            dialogVisible(newVal) {
+                newVal === false && this.$emit('close')
+            },
+            defaultData(newVal) {
+                console.log('newVal',newVal)
+              this.formmodel={...newVal}
+            }
+        }
+    }
+</script>
+
+<style scoped type="text/scss" >
+    /deep/ .el-input-group__append {
+        padding: 0 10px;
+    .el-color-picker {
+        display: table-cell;
+        padding: 0;
+        border: none;
+    }
+    .el-color-picker__trigger {
+        display: inline-table;
+    }
+    }
+    .color-preview {
+        width: 100%;
+        height: 32px;
+    }
+</style>

+ 166 - 0
src/views/custom-infoboard/templates/board-module-setting.vue

@@ -0,0 +1,166 @@
+<template>
+    <el-dialog
+            :visible.sync="dialogVisible"
+            :close-on-click-modal="false"
+            :close-on-press-escape="false"
+            append-to-body
+            width="805px">
+        <div slot="title" class="image-picker-title">模块设置</div>
+        <div class="image-picker-body">
+            <el-form :model="formmodel" label-width="100px">
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="上边框">
+                            <el-switch
+                                    v-model="formmodel.borderTopShow"
+                                    active-color="#13ce66"
+                                    >
+                            </el-switch>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="右边框">
+                            <el-switch
+                                    v-model="formmodel.borderRightShow"
+                                    active-color="#13ce66"
+                                    >
+                            </el-switch>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="下边框">
+                            <el-switch
+                                    v-model="formmodel.borderBottomShow"
+                                    active-color="#13ce66"
+                                    >
+                            </el-switch>
+                        </el-form-item>
+                    </el-col>
+
+                    <el-col :span="12">
+                        <el-form-item label="左边框">
+                            <el-switch
+                                    v-model="formmodel.borderLeftShow"
+                                    active-color="#13ce66"
+                                    >
+                            </el-switch>
+                        </el-form-item>
+                    </el-col>
+
+                    <el-col :span="12">
+                        <el-form-item label="边框颜色">
+                            <el-input placeholder="分割线颜色" v-model="formmodel.borderColor">
+                                <el-color-picker slot="append" v-model="formmodel.borderColor" size="mini"></el-color-picker>
+                            </el-input>
+
+                        </el-form-item>
+                    </el-col>
+
+                    <el-col :span="12">
+                        <el-form-item label="边框大小">
+                            <el-input placeholder="边框大小" v-model="formmodel.borderWidth" type="number">
+                                <template slot="append">px</template>
+                            </el-input>
+
+                        </el-form-item>
+                    </el-col>
+
+                    <el-col :span="12">
+                        <el-form-item label="模块高度">
+                            <el-input placeholder="模块高度" v-model="formmodel.blockHeight" type="number">
+                                <template slot="append">px</template>
+                            </el-input>
+
+                        </el-form-item>
+                    </el-col>
+
+                </el-row>
+            </el-form>
+        </div>
+        <div slot="footer" class="image-picker-footer">
+            <span>
+        <el-button @click="dialogVisible = false">取 消</el-button>
+      <el-button type="primary" @click="handleConfirm">确 定</el-button>
+      </span>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    export default {
+        name: "board-module-setting",
+        props: {
+            /** 显示dialog */
+            show: {
+                type: Boolean,
+                default: false
+            },
+            /** 默认数据 */
+            defaultData: {
+                type: Object,
+                default: () => {
+                }
+            }
+        },
+        data() {
+            return {
+                dialogVisible: this.show,
+                color:'#ffffff',
+                formmodel:{...this.defaultData},
+                predefineColors: [
+                    '#ff4500',
+                    '#ff8c00',
+                    '#ffd700',
+                    '#90ee90',
+                    '#00ced1',
+                    '#1e90ff',
+                    '#c71585',
+                    'rgba(255, 69, 0, 0.68)',
+                    'rgb(255, 120, 0)',
+                    'hsv(51, 100, 98)',
+                    'hsva(120, 40, 94, 0.5)',
+                    'hsl(181, 100%, 37%)',
+                    'hsla(209, 100%, 56%, 0.73)',
+                    '#c7158577'
+                ]
+            }
+        },
+        methods: {
+            handleConfirm() {
+                this.$emit('confirm', this.formmodel)
+                this.$emit('close')
+            }
+        },
+        watch: {
+            show(newVal) {
+                console.log('show', newVal)
+                this.dialogVisible = newVal
+            },
+            dialogVisible(newVal) {
+                newVal === false && this.$emit('close')
+            },
+            defaultData(newVal) {
+                console.log('hl',newVal)
+                this.formmodel={...newVal}
+            }
+        }
+    }
+</script>
+
+<style scoped type="text/scss" >
+    /deep/ .el-input-group__append {
+        padding: 0 10px;
+        .el-color-picker {
+            display: table-cell;
+            padding: 0;
+            border: none;
+        }
+        .el-color-picker__trigger {
+            display: inline-table;
+        }
+    }
+    .color-preview {
+        width: 100%;
+        height: 32px;
+    }
+</style>

+ 9 - 0
src/views/custom-infoboard/templates/common/index.js

@@ -0,0 +1,9 @@
+import tpl_one_cloum from './tpl-one-cloum'
+import tpl_two_cloum from './tpl-two-cloum'
+import tpl_three_cloum from './tpl-three-colum'
+
+export default {
+    1: tpl_one_cloum,
+    2: tpl_two_cloum,
+    3: tpl_three_cloum
+}

Різницю між файлами не показано, бо вона завелика
+ 438 - 0
src/views/custom-infoboard/templates/common/tpl-one-cloum.vue


Різницю між файлами не показано, бо вона завелика
+ 466 - 0
src/views/custom-infoboard/templates/common/tpl-three-colum.vue


Різницю між файлами не показано, бо вона завелика
+ 451 - 0
src/views/custom-infoboard/templates/common/tpl-two-cloum.vue


+ 14 - 0
src/views/custom-infoboard/templates/index.js

@@ -0,0 +1,14 @@
+import common from './common'
+
+const templates = {
+    ...common
+}
+
+const templateArray = []
+Object.keys(common).forEach(key => {
+    if (common[key].dataTpl) templateArray.push(common[key].dataTpl)
+})
+
+
+export { templateArray }
+export default templates

Різницю між файлами не показано, бо вона завелика
+ 304 - 0
src/views/custom-infoboard/templates/layout-item.vue


+ 407 - 0
src/views/entrace-guard/users.vue

@@ -0,0 +1,407 @@
+<template>
+    <div>
+
+        <ag-grid-layout
+                toolbar
+                :table-height="tableHeight"
+                theme="ag-theme-alpine"
+                :column-defs="columnDefs"
+                :row-data="rowData"
+                :locale-text="localeText"
+                :grid-options="gridOptions"
+                :default-col-def="defaultColDef"
+                :animate-rows="true"
+                :row-selection="rowSelection"
+                :framework-components="frameworkComponents"
+                @filterChanged="filterModifed"
+                @sortChanged="gridSortChange"
+        >
+
+            <div slot="toolbar" class="inner-toolbar">
+                <div class="toolbar-search">
+                    <en-table-search :placeholder="this.$t('action.keywords')" @search="handlerSearch"/>
+                </div>
+                <div class="toolbar-btns">
+                    <el-button type="primary" size="mini" @click="refreshUser">{{ this.$t('entraceguardUser.refreshUser') }}
+                    </el-button>
+                </div>
+            </div>
+            <el-pagination
+                    v-if="pageData"
+                    slot="pagination"
+                    :current-page="pageData.page_no"
+                    :page-sizes="[10, 20, 50, 100]"
+                    :page-size="pageData.page_size"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="pageData.data_total"
+                    @size-change="handlePageSizeChange"
+                    @current-change="handlePageCurrentChange"
+            />
+        </ag-grid-layout>
+
+        <!-- 用户信息 -->
+        <el-dialog :visible.sync="customerFormVisible" :title="this.$t('customerManage.customerInfo')"
+                   class="customer-dialog" width="50%">
+
+            <el-form ref="editForm" :model="formmodel" :rules="rules" label-width="145px">
+                <el-row>
+                    <el-col :span="12">
+                        <el-form-item :label="this.$t('entraceguardUser.named')" prop="name">
+                            <el-input v-model="formmodel.name" clearable
+                                      :placeholder="this.$t('entraceguardUser.named')" :maxlength="20"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item :label="this.$t('entraceguardUser.idNo')">
+                            <el-input v-model="formmodel.id_card" clearable
+                                      :placeholder="this.$t('entraceguardUser.idNo')" :maxlength="20"/>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row>
+                    <el-col :span="12">
+                        <el-form-item :label="this.$t('entraceguardUser.ic')" prop="name">
+                            <el-input v-model="formmodel.ic" clearable
+                                      :placeholder="this.$t('entraceguardUser.ic')" :maxlength="20"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item :label="this.$t('entraceguardUser.phone')">
+                            <el-input v-model="formmodel.phone" clearable
+                                      :placeholder="this.$t('entraceguardUser.phone')" :maxlength="13"/>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+
+                <el-row>
+                    <el-col :span="12">
+                        <el-form-item :label="this.$t('entraceguardUser.password')" prop="name">
+                            <el-input v-model="formmodel.password" clearable
+                                      :placeholder="this.$t('entraceguardUser.password')" :maxlength="20"/>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item :label="this.$t('entraceguardUser.forbidden')">
+                            <el-switch
+                                    v-model="formmodel.forbidden"
+                                    >
+                            </el-switch>
+
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+
+                <el-form-item>
+                    <el-button type="primary" class="save" @click="handlerSubmit('editForm')">{{ this.$t('action.save')
+                        }}
+                    </el-button>
+                </el-form-item>
+            </el-form>
+
+        </el-dialog>
+        <!-- 用户信息 -->
+
+    </div>
+</template>
+
+<script>
+    import {AG_GRID_LOCALE_CN} from '@/utils/AgGridVueLocaleCn'
+    import ButtonCellRender from '@/components/AgGridCellRender/ButtonCellRender'
+    import ButtonCellRenderList from '@/components/AgGridCellRender/ButtonCellRenderList'
+    import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
+    import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+    import * as API_Customer from '@/api/ncs_entrace_guard_user'
+    import {unix2Date, unixToDate} from '@/utils/Foundation'
+
+    export default {
+        name: "users",
+        components: {ButtonCellRenderList, ButtonCellRender, ListFilter, RadioFilter},
+        data() {
+            return {
+                /** ag-grid参数 **/
+                pageData: {}, // 翻页数据
+                columnDefs: null,
+                rowData: null,
+                defaultColDef: null,
+                gridOptions: null,
+                gridApi: null,
+                columnApi: null,
+                localeText: AG_GRID_LOCALE_CN,
+                filterState: null,
+                rowSelection: null,
+                frameworkComponents: null,
+                formmodel: {},
+                customerFormVisible: false,
+                /** 列表参数 */
+                params: {
+                    page_size: 20,
+                    page_no: 1,
+                    fixedCondition: 'partment_id=' + this.$store.getters.partId
+                },
+                booleanRender: [
+                    {key: this.$t('entraceguardUser.yes'), value: true, color: 'green'},
+                    {key: this.$t('entraceguardUser.nop'), value: false, color: 'red'}
+                ],
+                rules: {}
+            }
+        },
+        computed: {
+            tableHeight() {
+                return this.mainAreaHeight - 130
+            }
+        },
+        beforeMount() {
+            const _this = this
+            this.gridOptions = {
+                onCellDoubleClicked: function (row) {
+                    _this.formmodel = {
+                        ...row.data
+                    }
+                    _this.detailsVisible = true
+                }
+            }
+            this.columnDefs = [
+                {
+                    headerName: '#',
+                    headerCheckboxSelection: true,
+                    headerCheckboxSelectionFilteredOnly: true,
+                    checkboxSelection: true,
+                    sortable: false, filter: false,
+                    width: 50,
+                    resizable: false,
+                    valueGetter: this.hashValueGetter
+                },
+                {headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 100},
+                {
+                    headerName: this.$t('entraceguardUser.named'),
+                    field: 'name',
+                    sortable: true,
+                    filter: 'agTextColumnFilter',
+                    width: 160
+                },
+                {
+                    headerName: this.$t('entraceguardUser.idNo'),
+                    field: 'id_card',
+                    sortable: true,
+                    filter: 'agNumberColumnFilter',
+                    flex: 1
+                },
+
+                {
+                    headerName: this.$t('entraceguardUser.ic'),
+                    field: 'ic',
+                    sortable: true,
+                    filter: 'agTextColumnFilter',
+                    width: 160
+                },
+                {
+                    headerName: this.$t('entraceguardUser.phone'),
+                    field: 'phone',
+                    sortable: true,
+                    filter: 'agTextColumnFilter'
+                },
+                {
+                    headerName: this.$t('entraceguardUser.password'),
+                    field: 'password',
+                    sortable: true,
+                    filter: 'agTextColumnFilter',
+                    width: 160
+                },
+                // lockPosition 锁定位置,会在第一列
+                // lockPinned = true 不能拖动然后固定
+                // resizeable 单元个大小是否可以调整
+                {
+                    headerName: this.$t('entraceguardUser.forbidden'),
+                    field: 'forbidden',
+                    sortable: true,
+                    filterFramework: 'RadioFilter',
+                    filterParams: {
+                        listData: this.booleanRender
+                    },
+                    width: 150,
+                    cellRenderer: (para) => {
+                        return this.radioFilterFormatter(para, this.booleanRender)
+                    }
+                },
+
+
+                {
+                    headerName: this.$t('action.handle'), field: 'id',
+                    cellRendererFramework: 'ButtonCellRenderList',
+                    cellRendererParams: param => {
+                        return {
+                            list: [
+                                {
+                                    onClick: this.handleEdit,
+                                    label: this.$t('action.edit'),
+                                    buttonType: 'primary',
+                                    buttonSize: 'mini'
+                                }
+                            ]
+                        }
+                    },
+                    filter: false,
+                    pinned: 'right',
+                    lockPinned: true,
+                    minWidth: this.$i18n.locale === 'zh' ? 260 : 380,
+                    resizable: false,
+                    sortable: false
+                },
+            ]
+            this.defaultColDef = {
+                // filter: 'agTextColumnFilter',
+                sortable: true,
+                resizable: true,
+                // comparator: this.dateCustomComparator,
+                filterParams: {
+                    debounceMs: 500,
+                    newRowsAction: 'keep',
+                    textCustomComparator: this.textCustomComparator
+                }
+            }
+            this.rowSelection = 'multiple'
+        },
+        mounted() {
+            window.onresize = this.windowResize()
+            this.gridApi = this.gridOptions.api
+            this.gridColumnApi = this.gridOptions.columnApi
+            this.gridColumnApi.applyColumnState({
+                state: [
+                    {
+                        colId: 'id',
+                        sort: 'asc'
+                    }
+                ]
+            })
+        },
+
+        methods: {
+            windowResize() {
+                this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
+            },
+            /** 分页大小发生改变 */
+            handlePageSizeChange(size) {
+                this.params.page_size = size
+                this.getList()
+            },
+
+            /** 分页页数发生改变 */
+            handlePageCurrentChange(page) {
+                this.params.page_no = page
+                this.getList()
+            },
+            /** 加载列表数据 */
+            getList() {
+                const param = this.MixinClone(this.params)
+                this.gridApi.showLoadingOverlay()
+                API_Customer.getList(param).then(response => {
+                    this.rowData = [...response.data]
+                    this.pageData = {
+                        page_no: response.page_no,
+                        page_size: response.page_size,
+                        data_total: response.data_total
+                    }
+                    this.$nextTick(() => {
+                        const node = this.gridApi.getDisplayedRowAtIndex(0)
+                        if (node !== null && node !== undefined) {
+                            node.setSelected(true)
+                        }
+                    })
+                })
+            },
+            /**
+             * 过滤状态发生变化,发送到服务器检索数据
+             */
+            filterModifed(param) {
+                var model = param.api.getFilterModel()
+                this.params.filter = JSON.stringify(model)
+                this.getList()
+            },
+            gridSortChange(param) {
+                console.log('sortparam', param)
+                const columnState = param.columnApi.getColumnState()
+                // 排序状态
+                const sortState = columnState.filter(function (s) {
+                    return s.sort != null
+                }).map(function (s) {
+                    return {
+                        colId: s.colId,
+                        sort: s.sort,
+                        sortIndex: s.sortIndex
+                    }
+                }).sort(function (a, b) {
+                    return a.sortIndex - b.sortIndex
+                })
+                if (sortState.length > 0) {
+                    if (sortState.length === 1) {
+                        this.params.sort = sortState[0].colId
+                        this.params.dir = sortState[0].sort
+                    } else {
+                        let sortstring = ''
+                        sortState.forEach(function (item) {
+                            sortstring += item.colId + ' ' + item.sort + ','
+                        })
+                        this.params.sort = sortstring.substring(0, sortstring.length - 1)
+                        this.params.dir = ' '
+                    }
+                } else {
+                    delete this.params.sort
+                    delete this.params.dir
+                }
+
+                this.getList()
+            },
+            /** 处理搜索 */
+            handlerSearch(keywords) {
+                this.params.query = keywords
+                this.getList()
+            },
+
+            handleEdit(row) {
+                this.formmodel = {
+                    ...row
+                }
+                this.customerFormVisible = true
+            },
+            refreshUser() {
+                API_Customer.refreshUser(this.$store.getters.partId).then(res => {
+                    this.getList()
+                }).catch(err => {
+                    this.$message.error(err.message)
+                })
+            },
+            radioFilterFormatter(params, array) {
+                const item = array.filter(p => p.value === params.value)[0]
+                if (item) {
+                    if (item.color) {
+                        return '<span style="color:' + item.color + '">' + item.key + '</span>'
+                    } else {
+                        return item.key
+                    }
+                } else {
+                    return ''
+                }
+            },
+            handlerSubmit(formName){
+                this.$refs[formName].validate(valid => {
+                    if (valid) {
+
+                            API_Customer.update(this.formmodel.id,this.formmodel).then(res => {
+                                this.getList()
+                                this.$message.success(this.$t('action.editSuccess'))
+                                this.customerFormVisible = false
+                            })
+
+                    } else {
+                        this.$message.error(this.$t('action.fromError'))
+                        return false
+                    }
+                })
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 143 - 0
src/views/function-mapping/components/RoleForFunction.vue

@@ -0,0 +1,143 @@
+<template>
+    <div>
+        <el-card>
+
+            <fieldset style="margin-top: 10px">
+                <legend><span
+                        style="margin-right: 10px">{{this.$t('functionRoleMapping.configurateLeft')+this.$t(functions.language_dependence_code)+this.$t('functionRoleMapping.configurateRight')}}</span>
+                    <el-checkbox v-model="allCkeck" style="float: right;" :indeterminate="indeterminate"
+                                 @change="(checked)=>{handleCheckAll(checked)}">{{ this.$t('action.choiceAll') }}
+                    </el-checkbox>
+                </legend>
+                <el-row :gutter="20" type="flex" style="padding: 10px">
+                    <el-col :span="24">
+                        <el-checkbox v-for="(role,_index) in roles" :key="_index" v-model="role.checked"
+                                     @change="handleCheckboxChanged(role)">{{ role.role_name }}
+                        </el-checkbox>
+                    </el-col>
+                </el-row>
+            </fieldset>
+
+
+            <div align="center" class="margin-top-sm">
+                <el-button type="success" @click="handlerSubscribeSubmit">{{ this.$t('action.saveSettings') }}
+                </el-button>
+
+            </div>
+
+
+        </el-card>
+
+    </div>
+</template>
+
+<script>
+    import * as API_Role from '@/api/role'
+    import * as API_FunctionRoleMapping from '@/api/ncs_functions_role_mapping'
+
+    export default {
+        name: "RoleForFunction",
+        props: {
+            functions: {
+                type: Object,
+                default: () => {
+                }
+            }
+        },
+        data() {
+            return {
+                allCkeck: false,
+                roles: [],
+                indeterminate: false,
+                functionRoleMapping: {}
+            }
+        },
+       async mounted() {
+            this.roles = await API_Role.getRoleList(-1)
+            this.getFunctionsRoleMappingByCodeAndPartId()
+        },
+        methods: {
+            handleCheckboxChanged(role) {
+
+                this.caculateCheckAllState()
+            },
+            handleCheckAll(checked) {
+                this.roles.forEach(item => {
+                    this.$set(item, 'checked', checked)
+                })
+                this.caculateCheckAllState()
+            },
+
+            caculateCheckAllState(){
+                const checkedLength = this.roles.filter(p => p.checked).length
+                this.allCkeck = checkedLength === this.roles.length
+                this.indeterminate = checkedLength !== 0 && checkedLength !== this.roles.length
+            },
+
+            // getRoleList() { // roleEdit 保存成功时回调会传入当前role实体,新增时需要根据当前role选择新添加的角色
+            //     API_Role.getRoleList(-1).then(res => {
+            //         this.roles = [...res]
+            //     }).catch(err => {
+            //         this.$error(err.message)
+            //     })
+            // },
+            getFunctionsRoleMappingByCodeAndPartId() {
+                API_FunctionRoleMapping.getFunctionsRoleMappingByCodeAndPartId(this.functions.function_code, this.$store.getters.partId).then(res => {
+                    if (res !== null && res !== '') {
+                        this.functionRoleMapping = {...res}
+                        const roleids = res.role_ids.split(',').map(p=>Number(p))
+                        this.roles.forEach(item => {
+                            this.$set(item, 'checked', roleids.includes(item.role_id))
+                        })
+
+                        this.caculateCheckAllState()
+                    }
+                }).catch(err => {
+                    this.$message.error(err.message)
+                })
+            },
+            handlerSubscribeSubmit() {
+
+                const checkedRoleId = this.roles.filter(p => p.checked).map(p => p.role_id).join(',')
+                if(Object.keys(this.functionRoleMapping).length>0){
+                    this.functionRoleMapping.role_ids =checkedRoleId
+                }
+                else{
+                    this.functionRoleMapping={
+                        function_code:this.functions.function_code,
+                        part_id:this.$store.getters.partId,
+                        function_id:this.functions.id,
+                        role_ids:checkedRoleId
+                    }
+                }
+                if(this.functionRoleMapping.id!==null&&this.functionRoleMapping.id!==undefined){
+                    API_FunctionRoleMapping.update(this.functionRoleMapping.id,this.functionRoleMapping).then(res=>{
+                     this.functionRoleMapping={...res}
+                        this.$message.success(this.$t('action.settingsSuccess'))
+                    }).catch(err=>{
+                        this.$message.error(err.message)
+                    })
+                }else{
+                    API_FunctionRoleMapping.add(this.functionRoleMapping).then(res=>{
+                        this.functionRoleMapping={...res}
+                        this.$message.success(this.$t('action.settingsSuccess'))
+                    }).catch(err=>{
+                        this.$message.error(err.message)
+                    })
+                }
+
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    fieldset {
+        border: 1px solid #DCDFE6;
+        border-radius: 5px;
+    }
+
+    .margin-top-sm {
+        margin-top: 20px;
+    }
+</style>

+ 43 - 0
src/views/function-mapping/index.vue

@@ -0,0 +1,43 @@
+<template>
+    <div class="app-container">
+        <el-tabs tab-position="left" :style="{height: (mainAreaHeight-40)+'px'}">
+            <el-tab-pane :label="$t(item.language_dependence_code)" v-for="(item,index) in functions" :key="index">
+                <role-for-function :functions="item"></role-for-function>
+            </el-tab-pane>
+
+
+        </el-tabs>
+    </div>
+</template>
+
+<script>
+    import * as API_Function from '@/api/ncs_functions'
+    import RoleForFunction from "./components/RoleForFunction";
+    export default {
+        name: "index",
+        components: {RoleForFunction},
+        data(){
+            return{
+                functions:[]
+            }
+        },
+        mounted(){
+            this.getAllFunctions()
+        },
+        methods:{
+            getAllFunctions(){
+                API_Function.getAll().then(
+                    res=>{
+                        this.functions=[...res]
+                    }
+                ).catch(err=>{
+                    this.$error(err.message)
+                })
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+</style>

+ 2 - 0
src/views/hospitalFrame/nurse_watch_frame.vue

@@ -181,6 +181,7 @@ export default {
       if (this.roleId === 8) {
         API_hospitalFrame.getFrameByRoleGroupId(this.$store.getters.partId, this.memberId).then(res => {
           _this.rooms = _this.filterGroupFrames(res)
+
           const room_group = groupBy(_this.rooms, item => item.group_name)
           _this.roomGroup = Object.entries(room_group)
           console.log(_this.roomGroup)
@@ -188,6 +189,7 @@ export default {
       } else {
         API_hospitalFrame.getFrameByRoleId(this.$store.getters.partId, this.roleId).then(res => {
           _this.rooms = _this.filterGroupFrames(res)
+          console.log('rooms',_this.rooms)
           const room_group = groupBy(_this.rooms, item => item.group_name)
           _this.roomGroup = Object.entries(room_group)
         })

+ 552 - 0
src/views/interaction-chain/index.vue

@@ -0,0 +1,552 @@
+<template>
+    <div>
+        <ag-grid-layout
+                toolbar
+                :table-height="tableHeight"
+                theme="ag-theme-alpine"
+                :column-defs="columnDefs"
+                :row-data="rowData"
+                :locale-text="localeText"
+                :grid-options="gridOptions"
+                :default-col-def="defaultColDef"
+                :animate-rows="true"
+                :row-selection="rowSelection"
+                :enable-cell-change-flash="true"
+                @filterChanged="filterModifed"
+                @sortChanged="gridSortChange"
+        >
+            <!--        @rowDoubleClicked="getList"-->
+            <div slot="toolbar" class="inner-toolbar">
+                <div class="toolbar-search">
+                    <en-table-search :placeholder="this.$t('action.keywords')" @search="handlerSearch" />
+                </div>
+                <div class="toolbar-btns">
+                    <el-button type="primary" size="mini" @click="createBroadcast">{{ this.$t('action.add') }}</el-button>
+                </div>
+            </div>
+            <el-pagination
+                    v-if="pageData"
+                    slot="pagination"
+                    :current-page="pageData.page_no"
+                    :page-sizes="[10, 20, 50, 100]"
+                    :page-size="pageData.page_size"
+                    layout="total, sizes, prev, pager, next, jumper"
+                    :total="pageData.data_total"
+                    @size-change="handlePageSizeChange"
+                    @current-change="handlePageCurrentChange"
+            />
+        </ag-grid-layout>
+
+
+
+        <el-dialog :title.sync="formtitle" :visible.sync="formshow" width="55%">
+            <div>
+                <el-form ref="editform" :rules="rules" label-width="120px" :model="formmodel">
+
+                    <el-form-item :label="this.$t('interactionChain.ActionType')" prop="action_type">
+                        <el-select v-model="formmodel.action_type"
+                                   clearable >
+                            <el-option v-for="(item,index) in interactionTypes" :key="index" :label="item.key" :value="item.value"/>
+                        </el-select>
+                    </el-form-item>
+                    <el-row>
+                        <el-col :span="24" >
+                            <el-form-item :label="this.$t('interactionChain.AcceptSequence')" prop="role_path">
+                            <el-transfer
+                                    filterable
+                                    :filter-method="filterMethod"
+                                    :titles="[this.$t('interactionChain.AllRoles'), this.$t('interactionChain.AcceptRoles')+'('+this.$t('interactionChain.NoticeSequence')+')']"
+                                    :filter-placeholder="this.$t('interactionChain.SearchPlaceHolder')"
+                                    v-model="role_path"
+                                    :props="{key:'role_id',label:'role_name'}"
+                                    :data="role_data"
+                                    target-order="push"
+                            @change="roleDataChange"
+                            >
+                            </el-transfer>
+
+
+</el-form-item>
+                        </el-col>
+
+                    </el-row>
+                    <el-row>
+                        <el-col :span="24">
+                            <h3>{{this.$t('interactionChain.sketchMap')}}</h3>
+                            <el-steps  finish-status="success">
+
+                                <el-step :title="item.role_name" v-for="(item,index) in role_path_step" :key="index" status="finish"></el-step>
+
+                            </el-steps>
+                        </el-col>
+                    </el-row>
+                </el-form>
+
+            </div>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="formshow = false">{{ this.$t('action.cancel') }}</el-button>
+                <el-button type="primary" @click="handlerFormSubmit('editform')">{{ this.$t('action.yes') }}</el-button>
+            </div>
+        </el-dialog>
+    </div>
+
+</template>
+
+<script>
+    import { AG_GRID_LOCALE_CN } from '@/utils/AgGridVueLocaleCn'
+    import ButtonCellRenderList from '@/components/AgGridCellRender/ButtonCellRenderList'
+    import * as API_InteractionChain from '@/api/ncs_interaction-chain'
+    import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
+    import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+    const serverUrl = domain.serverUrl
+    const DeviceUrl = domain.DeviceUrl
+    import * as API_FunctionRoleMapping from '@/api/ncs_functions_role_mapping'
+    import * as API_Role from '@/api/role'
+    import i18n from "../../utils/i18n";
+    const customer={role_name:i18n.t('interactionChain.Customer') ,role_id:0}
+    const nurse_host={role_name:i18n.t('interactionChain.NURSE_HOST'),role_id:-1}
+    export default {
+        name: 'Index',
+        components: { ButtonCellRenderList, ListFilter, RadioFilter },
+        data() {
+            return {
+                tableData: [],
+                role_data:[],
+                role_path:[],
+                role_path_step:[customer,nurse_host],
+                /** 列表参数 */
+                params: {
+                    page_size: 20,
+                    page_no: 1,
+                    fixedCondition: ' part_id = ' + this.$store.getters.partId
+                },
+                /** 新建组织弹出参数 **/
+                formtitle: this.$t('interactionChain.AddInteractionSpreadPath'),
+                formshow: false,
+                formmodel: {
+                    repeat_times: 1,
+                    group_ids: []
+                },
+                rules: {
+                    action_type: [
+                        { required: true, message: this.$t('interactionChain.chooseActionTypeTip'), trigger: 'blur' }
+                    ],
+                    role_path:[{
+                        required: true,
+                        validator: (rule, value, callback) => {
+                            console.log('rule',value)
+                            if (this.role_path.length>0) {
+                                callback()
+                            } else {
+                                callback(new Error(this.$t('interactionChain.chooseRoleSquence')))
+                            }
+                        },
+                        trigger: 'change'
+                    }]
+                },
+                /** ag-grid参数 **/
+                pageData: [],
+                loading: false,
+                errorId: null,
+                columnDefs: null,
+                rowData: null,
+                defaultColDef: null,
+                gridOptions: null,
+                gridApi: null,
+                columnApi: null,
+                localeText: AG_GRID_LOCALE_CN,
+                filterState: null,
+                rowSelection: null,
+                frameworkComponents: null,
+                manageBedRole:{},
+                canManageBedRoleIds:[],
+                canManageBedRole:[],
+                interactionTypes:[{
+                    key:this.$t('tcpType.EVENT'),
+                    value:'EVENT'
+                },{
+                    key:this.$t('tcpType.VOICE'),
+                    value:'VOICE'
+                },{
+                    key:this.$t('tcpType.VIDEO'),
+                    value:'VIDEO'
+                },{
+                    key:this.$t('tcpType.IM'),
+                    value:'IM'
+                }]
+            }
+        },
+        computed: {
+            tableHeight() {
+                return this.mainAreaHeight - 130
+            }
+        },
+        beforeMount() {
+            this.gridOptions = {}
+            this.columnDefs = [
+                {
+                    headerName: '#',
+                    headerCheckboxSelection: true,
+                    headerCheckboxSelectionFilteredOnly: true,
+                    checkboxSelection: true,
+                    sortable: false, filter: false,
+                    width: 100,
+                    resizable: false,
+                    valueGetter: this.hashValueGetter
+                },
+                { headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 130 },
+                {
+                    headerName: this.$t('interactionChain.ActionType'), field: 'action_type', sortable: true, filterFramework: 'ListFilter', filterParams: {
+                        listData: this.interactionTypes
+                    }, width: 180,
+                    valueGetter: this.actionTypeGetter
+
+                },
+                {
+                    headerName: this.$t('interactionChain.FirstRole'), field: 'first_role_id', sortable: true, filterFramework: 'ListFilter', filterParams: {
+                        listData: this.canManageBedRole
+                    }, width: 180, cellRenderer: this.firstRoleFormatter
+                },
+                {
+                    headerName: this.$t('interactionChain.SpreadRoleNamePath'), field: 'spread_role_name_path', sortable: true, filter: 'agTextColumnFilter',
+                    flex:1,
+                    cellRenderer: this.chainFormatter
+                },
+
+                {
+                    headerName: this.$t('action.edit'), field: 'shop_id',
+                    cellRendererFramework: 'ButtonCellRenderList',
+                    cellRendererParams: param => {
+                        return {
+                            list: [
+                                {
+                                    onClick: this.handEdit,
+                                    label: this.$t('action.edit'),
+                                    buttonType: 'primary',
+                                    buttonSize: 'mini'
+                                },
+                                {
+                                    onClick: this.deleteSingle,
+                                    label: this.$t('action.delete'),
+                                    buttonType: 'danger',
+                                    buttonSize: 'mini'
+                                }
+                            ]}
+                    },
+                    filter: false,
+                    pinned: 'right',
+                    lockPinned: true,
+                    width: this.$i18n.locale === 'zh' ? 290 : 300,
+                    resizable: false,
+                    sortable: false
+                }
+            ]
+            this.defaultColDef = {
+                sortable: true,
+                resizable: true,
+                comparator: this.dateCustomComparator,
+                filterParams: {
+                    debounceMs: 200,
+                    newRowsAction: 'keep',
+                    textCustomComparator: this.textCustomComparator,
+                    comparator: this.dateCustomComparator
+                }
+            }
+            this.rowSelection = 'multiple'
+        },
+       async mounted() {
+
+           this.manageBedRole = await API_FunctionRoleMapping.getFunctionsRoleMappingByCodeAndPartId('MANAGE_BED', this.$store.getters.partId)
+           const {role_ids} = this.manageBedRole
+           if(role_ids!==undefined){
+               this.canManageBedRoleIds = role_ids.split(',').map(p=>Number(p))
+           }
+            window.onresize = this.windowResize
+            this.gridApi = this.gridOptions.api
+            this.gridColumnApi = this.gridOptions.columnApi
+            // 设置默认排序字段,应用列状态之后会触发 gridSortChange 函数,会调用getlist,后面不需要再调用this.getlist
+            this.gridColumnApi.applyColumnState({
+                state: [
+                    {
+                        colId: 'id',
+                        sort: 'asc'
+                    }
+                ]
+            })
+            this.getRoleList()
+
+
+
+        },
+        methods: {
+            windowResize() {
+                this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
+            },
+           getRoleList(){
+               API_Role.getRoleList(-1).then(res => {
+
+                    res.forEach(item=>{
+                        this.$set(item,'can_manage_bed',this.canManageBedRoleIds.includes(item.role_id))
+                        })
+                   this.role_data=[...res]
+                   this.canManageBedRole =[...res.filter(p=>p['can_manage_bed']).map(p=>{return {'key':p.role_name,'value':p.role_id}})]
+                   this.columnDefs[3].filterParams.listData=this.canManageBedRole
+                   this.gridApi.setColumnDefs(this.columnDefs);
+                   this.setRoleStatus()
+                    console.log(this.role_data)
+               })
+           },
+           setRoleStatus(){
+               let allRoleIds= this.role_data.map(p=>p.role_id)
+               let disabledRoleIds=[] //要被禁止的ids
+               if(this.role_path.length==0){ //没有选择任何项,只需把不管床的角色禁用
+                   disabledRoleIds = this.role_data.filter(p=>!p['can_manage_bed']).map(p=>p.role_id)
+               }else{
+                   let choosedNoManageBed = this.role_path.filter(p=>!new Set(this.canManageBedRoleIds).has(p))
+                   let choosedManageBed = this.role_path.filter(p=>new Set(this.canManageBedRoleIds).has(p))
+                   if(choosedNoManageBed.length===this.role_path){ //选择的都不能管床,有问题
+                       disabledRoleIds=[]
+                       this.role_path=[]
+                   }else if(choosedNoManageBed.length>0){
+                     disabledRoleIds=[...choosedManageBed]
+                   }
+
+               }
+
+                this.role_data.forEach(item=>{
+                        this.$set(item,'disabled',disabledRoleIds.includes(item.role_id))
+                })
+           },
+            handlerDelete(ids) {
+                this.$confirm(this.$t('action.sureDelete'), this.$t('action.waring'), {
+                    confirmButtonText: this.$t('action.yes'),
+                    cancelButtonText: this.$t('action.cancel'),
+                    type: 'warning'
+                }).then(() => {
+                    API_InteractionChain.remove(ids).then(
+                        response => {
+                            this.$message({
+                                type: 'success',
+                                message: this.$t('action.deleted')
+                            })
+                            this.getList()
+                        }
+                    ).catch(response => {
+                        this.$message({
+                            type: 'info',
+                            message: response.message
+                        })
+                    })
+                }).catch(() => {
+                    this.$message({
+                        type: 'info',
+                        message: this.$t('action.cancelDelete')
+                    })
+                })
+            },
+            filterMethod(query,item){
+                return item.role_name.indexOf(query) > -1;
+            },
+            deleteSingle(row) {
+                this.handlerDelete(row.id)
+            },
+
+
+            /** 获取交互类型文字显示,从deviceTypeTransfer 中找出value值对应的key显示出来 */
+            actionTypeGetter(params) {
+                const gridVal = params.data.action_type
+                // return this.deviceTypeTransfer.filter(p => p.value === gridVal).map(p => p.key)
+                return this.interactionTypes.filter(p=>p.value===gridVal).map(p=>p.key)[0]
+            },
+            /**
+             * 创建接收顺序
+             */
+            createBroadcast() {
+                delete this.formmodel.id
+                this.formmodel={
+
+                }
+                this.role_path=[]
+                this.role_path_step=[customer,nurse_host]
+                this.setRoleStatus()
+                this.formshow = true
+            },
+            /** 分页大小发生改变 */
+            handlePageSizeChange(size) {
+                this.params.page_size = size
+                this.getList()
+            },
+            /** 分页页数发生改变 */
+            handlePageCurrentChange(page) {
+                this.params.page_no = page
+                this.getList()
+            },
+            /** 加载列表数据 */
+            getList() {
+                this.loading = true
+                const param = this.MixinClone(this.params)
+                this.gridApi.showLoadingOverlay()
+                API_InteractionChain.getList(param).then(response => {
+                    this.loading = false
+                    // this.tableData = [...response.data]
+                    this.pageData = {
+                        page_no: response.page_no,
+                        page_size: response.page_size,
+                        data_total: response.data_total
+                    }
+                    this.$nextTick(() => {
+                        const node = this.gridApi.getDisplayedRowAtIndex(0)
+                        if (node !== null && node !== undefined) {
+                            node.setSelected(true)
+                        }
+                    })
+                    this.rowData = [...response.data]
+                    this.refreshPlayStatus()
+                }).catch(() => {
+                    this.loading = false
+                })
+            },
+            /** 处理搜索 */
+            handlerSearch(keywords) {
+                this.params.query = keywords
+                this.getList()
+            },
+            /** 处理字段排序 */
+            tableSort(column) {
+                if (column.order !== null) {
+                    this.params.sort = column.prop
+                    this.params.dir = column.order === 'ascending' ? 'asc' : 'desc'
+                } else {
+                    this.params.sort = null
+                    this.params.dir = null
+                }
+                this.getList()
+            },
+
+            chainFormatter(params){
+                return params.value.replaceAll(',','<i class="el-icon-right"></i>')
+            },
+            firstRoleFormatter(params) {
+                console.log('para', params)
+                // return
+               return params.data.spread_role_name_path.split(',')[0]
+
+            },
+
+            gridSortChange(param) {
+                const columnState = param.columnApi.getColumnState()
+                // 排序状态
+                const sortState = columnState.filter(function(s) {
+                    return s.sort != null
+                }).map(function(s) {
+                    return {
+                        colId: s.colId,
+                        sort: s.sort,
+                        sortIndex: s.sortIndex
+                    }
+                }).sort(function(a, b) {
+                    return a.sortIndex - b.sortIndex
+                })
+                if (sortState.length > 0) {
+                    if (sortState.length === 1) {
+                        this.params.sort = sortState[0].colId
+                        this.params.dir = sortState[0].sort
+                    } else {
+                        let sortstring = ''
+                        sortState.forEach(function(item) {
+                            sortstring += item.colId + ' ' + item.sort + ','
+                        })
+                        this.params.sort = sortstring.substring(0, sortstring.length - 1)
+                        this.params.dir = ' '
+                    }
+                } else {
+                    delete this.params.sort
+                    delete this.params.dir
+                }
+
+                this.getList()
+                // console.log(sortState)
+            },
+            filterModifed(param) { // todo 通过转换后的数值过滤,需要转回原始数值
+                // console.log(param)
+                var model = param.api.getFilterModel()
+                // console.log('model', JSON.stringify(model))
+                this.params.filter = JSON.stringify(model)
+                this.getList()
+            },
+            handEdit(row) {
+               this.formmodel ={
+                   ...row
+               }
+               this.role_path=row.spread_role_path.split(',').map(p=>Number(p))
+
+                this.roleDataChange(this.role_path)
+
+                this.setRoleStatus()
+               this.formshow=true
+
+            },
+            /**
+             * 提交新增表单
+             * @param formname
+             */
+            handlerFormSubmit(formName) {
+                this.$refs[formName].validate((valid) => {
+                    if (valid) {
+                        this.formmodel.part_id = this.$store.getters.partId
+                        this.formmodel.spread_role_path = this.role_path.join(',')
+                        this.formmodel.first_role_id = this.role_path[0]
+                        let chosedRoleName = []
+                        this.role_path.forEach(item => {
+                            chosedRoleName.push(this.role_data.filter(p => item === p.role_id).map(p => p.role_name))
+                        })
+                        this.formmodel.spread_role_name_path = chosedRoleName.join(',')
+                        if (this.formmodel.id === undefined) {
+                            /** 新增 */
+
+                            API_InteractionChain.add(this.formmodel).then(result => {
+                                console.log(result)
+                                this.formshow = false
+                                this.$message.success(this.$t('action.saveSuccess'))
+                                this.getList()
+                            })
+                        }else{
+                         API_InteractionChain.update(this.formmodel.id,this.formmodel).then(res=>{
+                             this.formshow = false
+                             this.$message.success(this.$t('action.saveSuccess'))
+                             this.getList()
+                         }).catch(err=>{
+                             this.$message.error(err.message)
+                         })
+                        }
+                    } else {
+                        this.$message.error(this.$t('action.fromError'))
+                    }
+
+                })
+            },
+
+            roleDataChange(val){
+                console.log(val)
+                let  chosedRole =[]
+                val.forEach(item=>{
+                    chosedRole.push(this.role_data.filter(p=>item===p.role_id)[0])
+                })
+                console.log(chosedRole)
+                this.role_path_step=[customer,...chosedRole,nurse_host]
+                this.setRoleStatus()
+            },
+
+
+        }
+    }
+    </script>
+
+<style scoped>
+
+/deep/
+.el-transfer-panel{
+    width:300px
+}
+
+</style>

+ 31 - 2
src/views/ncs-auth/compontents/roleEdit.vue

@@ -51,6 +51,21 @@
           </el-row>
         </div>
       </el-form-item>
+
+      <el-form-item :label="this.$t('role.leaderRole')">
+        <el-select v-model="permissionForm.leader_role_id" :placeholder="this.$t('role.leaderRolePlaceholder')" clearable>
+          <!--                <el-option v-for="item in sosDeviceSettingsTypeOptions" :key="item.value" :label="item.key" :value="item.value" />-->
+          <el-option v-for="(item, index) in leaderRoles" :key="index" :label="item.role_name" :value="item.role_id" />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item :label="this.$t('role.roleEnum')">
+        <el-select v-model="permissionForm.role_type" :placeholder="this.$t('role.roleEnumPlaceholder')" clearable>
+          <!--                <el-option v-for="item in sosDeviceSettingsTypeOptions" :key="item.value" :label="item.key" :value="item.value" />-->
+          <el-option v-for="(item, index) in roleTypeEnums" :key="index" :label="item.key" :value="item.key" />
+        </el-select>
+      </el-form-item>
+
       <el-form-item label="">
         <el-button type="primary" size="small" style="margin-top: 15px" @click="saveRolePermission">{{ this.$t('action.saveSettings') }}</el-button>
       </el-form-item>
@@ -69,6 +84,11 @@ export default {
       type: Number,
       required: true,
       default: 0
+    },
+    allRoles:{
+      type:Array,
+      require:true,
+      default:()=>[]
     }
   },
   data() {
@@ -90,15 +110,22 @@ export default {
       // 全选状态
       allCheck: false,
       // 全选不确定状态
-      allIndeterminate: false
+      allIndeterminate: false,
+      roleTypeEnums:[]
     }
   },
   watch: {
     role_id: 'GET_RolePermission'
   },
-  mounted() {
+  async mounted() {
     // this.role_id = Number(this.$route.params.id)
     this.GET_RolePermission()
+    this.roleTypeEnums = await API_Auth.getRoleEnums()
+  },
+  computed:{
+    leaderRoles(){
+      return this.allRoles.filter(p=>p.role_id!==this.role_id)
+    }
   },
   methods: {
     /** 全选 */
@@ -193,6 +220,8 @@ export default {
           API_Auth.getRolePermission(this.role_id).then(response => {
             this.permissionForm.role_name = response.role_name
             this.permissionForm.role_describe = response.role_describe
+            this.$set(this.permissionForm,'role_type',response.role_type)
+            this.$set(this.permissionForm,'leader_role_id',response.leader_role_id)
             // this.permissionForm.hidden_in_seller = response.hidden_in_seller
             const checkedIds = this.expandRouters(response.menus)
             this.$set(this, 'permissions', this.filterRoleRouter(res, checkedIds))

+ 2 - 1
src/views/ncs-auth/superadmin/defaultRoleManager.vue

@@ -23,7 +23,7 @@
           <el-button :disabled="role_id === 0" type="success" size="mini" @click="roleEvent">{{ this.$t('role.roleEvent') }}</el-button>
           <el-button :disabled="role_id === 0" type="danger" size="mini" @click="deleteRole">{{ this.$t('role.roleDelete') }}</el-button>
         </el-header>
-        <role-edit :role_id="role_id" @aftersave="getRoleList" />
+        <role-edit :role_id="role_id" :all-roles="tableData" @aftersave="getRoleList" />
       </el-main>
     </el-container>
     <el-dialog :title="this.$t('role.roleEdit')" :visible.sync="formShow" width="50%">
@@ -62,6 +62,7 @@ export default {
       allCheck: false,
       newCheckList: [],
       oldCheckList: []
+
     }
   },
   computed: {

+ 1 - 0
src/views/ncs-channel/index.vue

@@ -532,6 +532,7 @@ export default {
         _this.nurses.forEach(item => {
           this.channelSubscribers.push({ 'roleName': item[0], 'clerks': item[1].map(i => { return { ...i, 'checked': false } }), 'allCheck': false, 'indeterminate': false })
         })
+
       })
     },
     handleCheckboxChanged(item) {

+ 480 - 0
src/views/ncs-clerk-frame-manage/components/employeeView.vue

@@ -0,0 +1,480 @@
+<template>
+    <el-container :style="{height: asideHeight+'px'}">
+        <el-aside width="220px" style="overflow: hidden">
+            <el-scrollbar wrap-class="scrollbar-wrapper">
+                <el-menu
+                        default-active="0-0"
+                        class="el-menu-vertical-demo"
+                        @open="handleOpen"
+                        @select="handleSelect"
+                        @close="handleClose"
+                >
+                    <el-submenu v-for="(key,index) in Object.keys(clerks)" :index="index+''" :key="index">
+                        <template slot="title">
+                            <svg-icon icon-class="role"/>
+                            <span>{{key}}</span>
+                        </template>
+                        <el-menu-item-group v-if="clerks[key].length>0">
+                            <el-menu-item :index="index+'-'+_idx" v-for="(item,_idx) in clerks[key]" :key="_idx">
+                                {{item.clerk_name}}
+                            </el-menu-item>
+                        </el-menu-item-group>
+                    </el-submenu>
+                </el-menu>
+
+            </el-scrollbar>
+
+        </el-aside>
+        <el-main :style="{height: (asideHeight-16)+'px'}">
+            <el-tabs v-model="activeName" type="border-card">
+                <el-tab-pane :label="this.$t('clerkFrameManage.managedFrames')" name="first">
+                    <div class="inner-toolbar">
+                        <div class="toolbar-search">
+                            <en-table-search :placeholder="this.$t('action.keywords')" @search="(keyword)=>handlerSearchManaged(keyword,1)"/>
+                        </div>
+                    </div>
+                    <el-tabs v-model="activeName2" class="inner-tab-panel" tab-position="left">
+
+                        <el-tab-pane :label="$t('watch.allFrame')" name="allManaged">
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-171)+'px'}">
+                                <div style="padding: 0 15px">
+                                    <!--        <fieldset class="margin-top-sm">-->
+                                    <!--          <legend>{{ watchDeviceFrame }}</legend>-->
+                                    <el-row :gutter="20" type="flex">
+                                        <el-col v-for="(item,index) in clerkManageFrame.managed_frames" :key="index"
+                                                :xs="8" :sm="8" :md="6" :lg="4" :xl="4">
+                                            <el-card class="box-card">
+                                                <div  slot="header" class="clearfix">
+
+                                                        <svg-icon icon-class="sickroom"
+                                                                  style="color: #9aaabf;margin-right: 5px"/>
+                                                        <span>{{ item.name }}</span>
+
+                                                    <el-checkbox v-model="item.allCkeck" style="float: right"
+                                                                 :indeterminate="item.indeterminate"
+                                                                 :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                                 @change="(checked)=>{handleCheckAll(checked,item)}">
+                                                        {{ $t('action.choiceAll') }}
+                                                    </el-checkbox>
+                                                </div>
+                                                <div v-for="(bed,_index) in item.children" :key="_index"
+                                                     class="text item">
+                                                    <el-checkbox
+                                                            v-model="bed.checked"
+                                                                 :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                                 @change="handleCheckboxChanged(item)">
+                                                        <svg-icon icon-class="bed"
+                                                                  style="color: #9aaabf;margin-right: 5px"/>
+                                                        <span v-if="bed.device_id && !bed.checked"
+                                                              style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>
+                                                        <span v-else>{{ bed.full_name }}</span>
+                                                    </el-checkbox>
+                                                </div>
+                                            </el-card>
+                                        </el-col>
+                                    </el-row>
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+
+                        <el-tab-pane v-for="(group,idx) in managedRoomGroup" v-if="group[0]!=='null'" :key="idx"
+                                     :label="group[0]" :name="'zone-'+idx">
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-171)+'px'}">
+                                <div style="padding: 0 15px">
+                            <el-row v-if="group[1].length > 1" :gutter="20" type="flex">
+                                <el-col v-for="(item,index) in group[1]" :key="index" :xs="6" :sm="6" :md="4" :lg="4"
+                                        :xl="4">
+                                    <el-card class="box-card">
+                                        <div slot="header" class="clearfix">
+
+                                                <svg-icon icon-class="sickroom"
+                                                          style="color: #9aaabf;margin-right: 5px"/>
+                                                <span>{{ item.name }}</span>
+
+                                            <el-checkbox v-model="item.allCkeck" style="float: right"
+                                                         :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                         :indeterminate="item.indeterminate"
+                                                         @change="(checked)=>{handleCheckAll(checked,item)}">
+                                                {{ $t('action.choiceAll') }}
+                                            </el-checkbox>
+                                        </div>
+                                        <div v-for="(bed,_index) in item.children" :key="_index" class="text item">
+                                            <el-checkbox v-model="bed.checked" :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')">
+                                                <svg-icon icon-class="bed" style="color: #9aaabf;margin-right: 5px"/>
+                                                <span v-if="bed.device_id && !bed.checked" style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>
+                                                <span v-else>{{ bed.full_name }}</span>
+                                            </el-checkbox>
+                                            <!--                                        <div v-if="roleId === 8">-->
+                                            <!--                                            <svg-icon icon-class="bed" style="color: #9aaabf;margin-right: 5px" />-->
+                                            <!--                                            <span v-if="bed.device_id && !bed.checked" style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>-->
+                                            <!--                                            <span v-else>{{ bed.full_name }}</span>-->
+                                            <!--                                        </div>-->
+                                        </div>
+                                    </el-card>
+                                </el-col>
+                            </el-row>
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+                    </el-tabs>
+                    <div class="margin-top-xs text-center padding-content">
+                        <el-row>
+                            <el-col :span="8"> <el-checkbox v-if="clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')" v-model="clerkManageFrameSource.bool_phone_acceptor">{{this.$t('clerkFrameManage.phoneAcceptorTipsLeft')}}{{clerkManageFrameSource.clerk_name}}{{this.$t('clerkFrameManage.phoneAcceptorTipsMid')}},{{clerkManageFrameSource.clerk_name}}{{this.$t('clerkFrameManage.phoneAcceptorTipsRight')}}</el-checkbox></el-col>
+                            <el-col :span="8"> <el-button type="primary" :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')" @click="saveClerkManageFrame">{{this.$t('action.saveSettings')}}</el-button></el-col>
+                            <el-col :span="8"></el-col>
+                        </el-row>
+
+                    </div>
+                </el-tab-pane>
+                <el-tab-pane :label="this.$t('clerkFrameManage.notManagedFrames')" name="second">
+                    <div class="inner-toolbar">
+                        <div class="toolbar-search">
+                            <en-table-search :placeholder="this.$t('action.keywords')" @search="(keyword)=>handlerSearchManaged(keyword,2)"/>
+                        </div>
+                    </div>
+                    <el-tabs v-model="activeName1" class="inner-tab-panel" tab-position="left">
+
+                        <el-tab-pane :label="$t('watch.allFrame')" name="allNoManange">
+
+                            <!--        <fieldset class="margin-top-sm">-->
+                            <!--          <legend>{{ watchDeviceFrame }}</legend>-->
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-171)+'px'}">
+                                <div style="padding: 0 15px">
+                                    <el-row :gutter="20" type="flex">
+
+                                        <el-col v-for="(item,index) in clerkManageFrame.no_manage_frames" :key="index"
+                                                :xs="8" :sm="8" :md="6" :lg="4" :xl="4">
+                                            <el-card class="box-card">
+                                                <div slot="header" class="clearfix">
+                                                    <svg-icon icon-class="sickroom"
+                                                              style="color: #9aaabf;margin-right: 5px"/>
+                                                    <span>{{ item.name }}</span>
+                                                    <span v-if="uiVersion !== 1"><svg-icon icon-class="sickroom"
+                                                                                           style="color: #9aaabf;margin-right: 5px"/><span>{{ item.name }}</span></span>
+                                                    <el-checkbox v-model="item.allCkeck" style="float: right"
+                                                                 :indeterminate="item.indeterminate" :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                                 @change="(checked)=>{handleCheckAll(checked,item)}">
+                                                        {{ $t('action.choiceAll') }}
+                                                    </el-checkbox>
+                                                </div>
+                                                <div v-for="(bed,_index) in item.children" :key="_index"
+                                                     class="text item">
+                                                    <el-checkbox v-model="bed.checked"
+                                                                 :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                                 @change="handleCheckboxChanged(item)">
+                                                        <svg-icon icon-class="bed"
+                                                                  style="color: #9aaabf;margin-right: 5px"/>
+                                                        <span v-if="bed.device_id && !bed.checked"
+                                                              style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>
+                                                        <span v-else>{{ bed.full_name }}</span>
+                                                    </el-checkbox>
+                                                    <!--                                            <div >-->
+                                                    <!--                                                <svg-icon icon-class="bed" style="color: #9aaabf;margin-right: 5px" />-->
+                                                    <!--                                                <span v-if="bed.device_id && !bed.checked" style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>-->
+                                                    <!--                                                <span v-else>{{ bed.full_name }}</span>-->
+                                                    <!--                                            </div>-->
+                                                </div>
+                                            </el-card>
+                                        </el-col>
+
+                                    </el-row>
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+
+                        <el-tab-pane v-for="(group,idx) in noManagedRoomGroup" v-if="group[0]!=='null'" :key="idx"
+                                     :label="group[0]" :name="'zone-'+idx">
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-171)+'px'}">
+                                <div style="padding: 0 15px">
+                                    <el-row v-if="group[1].length > 1" :gutter="20" type="flex">
+                                        <el-col v-for="(item,index) in group[1]" :key="index" :xs="8" :sm="8" :md="6"
+                                                :lg="4" :xl="4">
+                                            <el-card class="box-card">
+                                                <div slot="header" class="clearfix">
+                                                    <svg-icon icon-class="sickroom"
+                                                              style="color: #9aaabf;margin-right: 5px"/>
+                                                    <span>{{ item.name }}</span>
+                                                    <span v-if="uiVersion !== 1"><svg-icon icon-class="sickroom"
+                                                                                           style="color: #9aaabf;margin-right: 5px"/><span>{{ item.name }}</span></span>
+                                                    <el-checkbox v-model="item.allCkeck" style="float: right"
+                                                                 :indeterminate="item.indeterminate"
+                                                                 :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                                 @change="(checked)=>{handleCheckAll(checked,item)}">
+                                                        {{ $t('action.choiceAll') }}
+                                                    </el-checkbox>
+                                                </div>
+                                                <div v-for="(bed,_index) in item.children" :key="_index"
+                                                     class="text item">
+                                                    <el-checkbox v-model="bed.checked"
+                                                                 :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')"
+                                                                 @change="handleCheckboxChanged(item)">
+                                                        <svg-icon icon-class="bed"
+                                                                  style="color: #9aaabf;margin-right: 5px"/>
+                                                        <span v-if="bed.device_id && !bed.checked"
+                                                              style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>
+                                                        <span v-else>{{ bed.full_name }}</span>
+                                                    </el-checkbox>
+                                                    <!--                                        <div v-if="roleId === 8">-->
+                                                    <!--                                            <svg-icon icon-class="bed" style="color: #9aaabf;margin-right: 5px" />-->
+                                                    <!--                                            <span v-if="bed.device_id && !bed.checked" style="color: #13ce66">{{ bed.full_name }} {{ bed.clerk_name }}</span>-->
+                                                    <!--                                            <span v-else>{{ bed.full_name }}</span>-->
+                                                    <!--                                        </div>-->
+                                                </div>
+                                            </el-card>
+                                        </el-col>
+                                    </el-row>
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+                    </el-tabs>
+                    <div class="margin-top-xs text-center padding-content">
+                        <el-row>
+                            <el-col :span="8"> <el-checkbox v-if="clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')" v-model="clerkManageFrameSource.bool_phone_acceptor">{{this.$t('clerkFrameManage.phoneAcceptorTipsLeft')}}{{clerkManageFrameSource.clerk_name}}{{this.$t('clerkFrameManage.phoneAcceptorTipsMid')}},{{clerkManageFrameSource.clerk_name}}{{this.$t('clerkFrameManage.phoneAcceptorTipsRight')}}</el-checkbox></el-col>
+                            <el-col :span="8"> <el-button type="primary" :disabled="!clerkManageFrame.permissions.some(k=>k==='MANAGE_BED')" @click="saveClerkManageFrame">{{this.$t('action.saveSettings')}}</el-button></el-col>
+                            <el-col :span="8"></el-col>
+                        </el-row>
+
+                    </div>
+
+                </el-tab-pane>
+            </el-tabs>
+
+        </el-main>
+    </el-container>
+
+</template>
+
+<script>
+    import * as API_Clerk from '@/api/ncs_clerk'
+
+    const uiVersion = domain.uiVersion
+    const groupBy = (arr, func) =>
+        arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
+            acc[val] = (acc[val] || []).concat(arr[i])
+            return acc
+        }, {})
+    export default {
+        name: "employeeView",
+        computed: {
+            asideHeight() {
+                return this.mainAreaHeight - 80
+            }
+        },
+        data() {
+            return {
+                clerks: {},
+                activeName: 'first',
+                testdata: [{key: 1, check: false},
+                    {key: 2, check: false},
+                    {key: 3, check: false},
+                    {key: 4, check: false}
+                ],
+                showdata: [],
+                clerkManageFrame: {
+                    permissions:[]
+                },
+                clerkManageFrameSource: {},
+                managedRoomGroup: [],
+                noManagedRoomGroup: [],
+                uiVersion: uiVersion,
+                activeName1: 'allNoManange',
+                activeName2: 'allManaged',
+                currentClerk:{}
+            }
+        },
+        mounted() {
+            this.getRoleGroupClerk()
+            this.showdata = this.testdata
+        },
+        methods: {
+            getRoleGroupClerk() {
+                API_Clerk.getRoleGroupClerk(this.$store.getters.partId).then(res => {
+                    this.clerks = {...res}
+                    this.currentClerk = {...this.clerks[Object.keys(this.clerks)[0]][0]}
+                    this.getClerkManageFrame(this.currentClerk.clerk_id, 4)
+                })
+            },
+            handleOpen() {
+
+            },
+            handleClose() {
+
+            },
+            handleSelect(val){
+                let path =val.split('-').map(p=>Number(p))
+                this.currentClerk = {...this.clerks[Object.keys(this.clerks)[path[0]]][path[1]]}
+                this.getClerkManageFrame(this.currentClerk.clerk_id, 4)
+            },
+
+            getClerkManageFrame(clerkId, rootType) {
+
+                API_Clerk.getClerkManageFrame(clerkId, rootType).then(res => {
+                    this.clerkManageFrameSource = {...res}
+                    this.clerkManageFrame = {...this.clerkManageFrameSource}
+                    this.clerkManageFrame.managed_frames.forEach(item=>{
+                        this.countAllFrame(item)
+                    })
+
+                    this.clerkManageFrame.no_manage_frames.forEach(item=>{
+                        this.countAllFrame(item)
+                    })
+
+                    const managedGroup = groupBy(this.clerkManageFrame.managed_frames, item => item.group_name)
+                    this.managedRoomGroup = Object.entries(managedGroup)
+                    const noManagedGroup = groupBy(this.clerkManageFrame.no_manage_frames, item => item.group_name)
+                    this.noManagedRoomGroup = Object.entries(noManagedGroup)
+                })
+            },
+            handleCheckAll(checked, item) {
+                this.$set(item, 'indeterminate', false)
+                this.setFrameCheck(item, checked)
+            },
+            /** 设置选择状态 */
+            setFrameCheck(item, checked) {
+                const perm = this.MixinClone(item)
+                if (!Array.isArray(perm)) {
+                    this.$set(item, 'checked', checked)
+                    if (item.children && item.children.length) {
+                        this.$set(item, 'children', this.setFrameCheck(item.children, checked))
+                    }
+                } else {
+                    perm.map(item => {
+                        item.checked = checked
+                        this.$set(item, 'checked', checked)
+                        if (item.children && item.children.length) {
+                            this.$set(item, 'children', this.setFrameCheck(item.children, checked))
+                        }
+                    })
+                }
+                return perm
+            },
+            /** 选择 */
+            handleCheckboxChanged(item) {
+                this.countAllFrame(item)
+            },
+            /** 获取所有frame的长度、被选中的长度 */
+            countAllFrame(frame) {
+                const _list = []
+                if (!Array.isArray(frame)) {
+                    // _list.push(frame)
+                    if (frame.children) _list.push(...this.countAllFrame(frame.children))
+                } else {
+                    frame.forEach(item => {
+                         _list.push(item)
+                        if (item.children) _list.push(...this.countAllFrame(item.children))
+                    })
+                }
+                const length = _list.length
+                const length_checked = _list.filter(_item => _item.checked).length
+                this.$set(frame, 'allCkeck', length === _list.filter(_item => _item.checked).length)
+                this.$set(frame, 'indeterminate', (length_checked !== 0) && (length !== length_checked))
+                return _list
+            },
+            handlerSearchManaged(keywords,view) {
+                if (keywords !== '') {
+                    if(view===1){
+                        this.clerkManageFrame.managed_frames =this.clerkManageFrameSource.managed_frames.filter(p => p.full_name.indexOf(keywords) > -1 || p.chidren !== null && p.children.length > 0 && p.children.some(k => k.full_name.indexOf(keywords) > -1))
+                    }else{
+                        this.clerkManageFrame.no_manage_frames = this.clerkManageFrameSource.no_manage_frames.filter(p => p.full_name.indexOf(keywords) > -1 || p.chidren !== null && p.children.length > 0 && p.children.some(k => k.full_name.indexOf(keywords) > -1))
+                    }
+                } else {
+                    if(view===1){
+                        this.clerkManageFrame.managed_frames = [...this.clerkManageFrameSource.managed_frames]
+                    }else{
+                        this.clerkManageFrame.no_manage_frames = [...this.clerkManageFrameSource.no_manage_frames]
+                    }
+
+
+                }
+                const managedGroup = groupBy(this.clerkManageFrame.managed_frames, item => item.group_name)
+                this.managedRoomGroup = Object.entries(managedGroup)
+                const noManagedGroup = groupBy(this.clerkManageFrame.no_manage_frames, item => item.group_name)
+                this.noManagedRoomGroup = Object.entries(noManagedGroup)
+
+            },
+            saveClerkManageFrame() {
+                API_Clerk.saveClerkManageFrame(this.clerkManageFrameSource).then(res => {
+                    this.getClerkManageFrame(this.currentClerk.clerk_id, 4)
+                    this.$message.success(this.$t('action.saveSuccess'))
+                })
+            }
+        }
+    }
+</script>
+
+<style scoped>
+
+    .el-aside {
+        margin: 8px;
+        padding: 8px;
+        /*border-width: 1px;*/
+        /*border-style: solid;*/
+        background: #fff;
+    }
+
+    .el-submenu .el-menu-item {
+        width: 100% !important;
+    }
+
+    .custom-tree-node {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        font-size: 14px;
+        padding-right: 8px;
+    }
+
+    .el-tree {
+        margin-top: 8px
+    }
+
+    /deep/ .el-main {
+        margin: 8px;
+        padding: 0;
+        overflow: hidden;
+    }
+
+    /deep/ .scrollbar-wrapper {
+        height: 100%;
+        overflow-x: hidden;
+    }
+
+    /deep/ .inner-tab-panel .el-tabs__content {
+        /*height:calc(100vh - 260px)*/
+    }
+
+    /deep/ .el-menu-item-group__title {
+        display: none;
+    }
+
+    .el-scrollbar {
+        height: 100%;
+    }
+
+    /deep/ .svg-icon {
+        margin-right: 16px;
+    }
+
+    /deep/ .el-tabs--border-card > .el-tabs__content {
+        padding: 15px;
+        padding-right: 0;
+    }
+
+    .el-row--flex {
+        flex-wrap: wrap !important;
+        margin-top: 20px;
+    }
+
+    .el-col {
+        margin-bottom: 20px !important;
+    }
+
+    /deep/.el-checkbox__input.is-disabled.is-checked + span.el-checkbox__label{
+        color: #1890ff;
+    }
+ /deep/ .el-checkbox__label{
+     white-space: normal;
+     text-align: left;
+ }
+</style>

+ 500 - 0
src/views/ncs-clerk-frame-manage/components/frameView.vue

@@ -0,0 +1,500 @@
+<template>
+
+    <el-container :style="{height: asideHeight+'px'}">
+        <el-aside width="280px" style="border-color: rgb(238, 241, 246)">
+            <div class="el-row--flex">
+                <el-input
+                        v-model="filterText"
+                        :placeholder="this.$t('frameManage.keywordsFilter')"
+                        clearable
+                />
+            </div>
+            <el-tree
+                    ref="frameTree"
+                    :data="treeData"
+                    :show-checkbox="false"
+                    node-key="id"
+                    :default-expand-all="true"
+                    :auto-expand-parent="true"
+                    :expand-on-click-node="false"
+                    :highlight-current="true"
+                    :current-node-key="selectedNodeId"
+                    :accordion="true"
+                    :filter-node-method="filterNode"
+                    @node-click="nodeClick"
+            >
+          <span slot-scope="{ node, data }" class="custom-tree-node">
+
+            <!--            <span><svg-icon :icon-class="data.type===4?'sickroom':data.type===5?'bed':'area'" />{{ data.full_name }}</span>-->
+              <span
+              ><svg-icon
+                      :style="data.customer_id ? 'color: #0a901c' : ''"
+                      :icon-class="data.type===4?'sickroom':data.type===5?'bed':'area'"
+              />{{ data.full_name }}</span>
+
+
+          </span>
+            </el-tree>
+        </el-aside>
+        <el-main>
+
+            <el-card :style="{height: asideHeight -20 +'px'}">
+                <el-form>
+                    <el-tabs v-model="activeName">
+                        <el-tab-pane :label="this.$t('clerkFrameManage.settableEmployee')" name="first">
+
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-160)+'px'}">
+                                <div style="padding: 0 15px">
+
+
+                                    <el-row v-if="frameManagedClerk.settable!==undefined && Object.keys(frameManagedClerk.settable).length>0">
+                                        <el-col :span="24">
+
+                                            <div class="text item">
+
+                                                <fieldset style="margin-bottom: 10px"
+                                                          v-for="(roleName,_index) in Object.keys(frameManagedClerk.settable)"
+                                                          :key="_index">
+                                                    <legend><span
+                                                            style="margin-right: 10px">{{roleName}}</span>
+                                                    </legend>
+                                                    <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                        <el-col :span="24">
+                                                            <el-checkbox
+                                                                    v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                    @change="(val)=>{checkChange(val,roleName)}"
+                                                                    :key="index" v-model="clerk.checked"
+                                                            >{{ clerk.clerk_name }}
+                                                            </el-checkbox>
+                                                        </el-col>
+                                                    </el-row>
+                                                    <el-divider
+                                                            v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"></el-divider>
+                                                    <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                            v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)">
+                                                        <el-col :span="24">
+                                                            <el-form-item
+                                                                    :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                                <el-select
+                                                                        v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                        @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                        >
+                                                                    <el-option
+                                                                            v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                            v-if="clerk.checked"
+                                                                            :key="clerk.clerk_id"
+                                                                            :label="clerk.clerk_name"
+                                                                            :value="clerk.clerk_id">
+                                                                    </el-option>
+                                                                </el-select>
+                                                            </el-form-item>
+                                                        </el-col>
+                                                    </el-row>
+                                                </fieldset>
+                                            </div>
+
+
+                                        </el-col>
+                                    </el-row>
+
+
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+                        <el-tab-pane :label="this.$t('clerkFrameManage.settedEmployee')" name="second">
+
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-160)+'px'}">
+                                <div style="padding: 0 15px">
+
+
+                                    <el-row v-if="frameManagedClerk.settable!==undefined && Object.keys(frameManagedClerk.settable).length>0">
+                                        <el-col :span="24">
+
+                                            <div class="text item">
+
+                                                <fieldset style="margin-bottom: 10px"
+                                                          v-for="(roleName,_index) in Object.keys(frameManagedClerk.settable)"
+                                                          v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"
+                                                          :key="_index">
+                                                    <legend><span
+                                                            style="margin-right: 10px">{{roleName}}</span>
+                                                    </legend>
+                                                    <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                        <el-col :span="24">
+                                                            <el-checkbox
+                                                                    v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                    v-if="clerk.checked"
+                                                                    @change="(val)=>{checkChange(val,roleName)}"
+                                                                    :key="index" v-model="clerk.checked">{{
+                                                                clerk.clerk_name }}
+                                                            </el-checkbox>
+                                                        </el-col>
+                                                    </el-row>
+                                                    <el-divider
+                                                            v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"></el-divider>
+                                                    <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                            v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)">
+                                                        <el-col :span="24">
+                                                            <el-form-item
+                                                                    :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                                <el-select
+                                                                        v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                        @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                       >
+                                                                    <el-option
+                                                                            v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                            v-if="clerk.checked"
+                                                                            :key="clerk.clerk_id"
+                                                                            :label="clerk.clerk_name"
+                                                                            :value="clerk.clerk_id">
+                                                                    </el-option>
+                                                                </el-select>
+                                                            </el-form-item>
+                                                        </el-col>
+                                                    </el-row>
+                                                </fieldset>
+                                            </div>
+
+
+                                        </el-col>
+                                    </el-row>
+
+
+                                    <el-row class="margin-top-sm"
+                                            v-if="frameManagedClerk.un_settable!==undefined && Object.keys(frameManagedClerk.un_settable).length>0">
+                                        <el-col :span="24">
+
+                                            <div class="text item">
+
+                                                <fieldset style="margin-bottom: 10px"
+                                                          v-for="(roleName,_index) in Object.keys(frameManagedClerk.un_settable)"
+                                                          :key="_index"
+                                                          v-if="frameManagedClerk.un_settable[roleName].length>0&&frameManagedClerk.un_settable[roleName].some(p=>p.checked)">
+                                                    <legend><span
+                                                            style="margin-right: 10px">{{roleName}}</span>
+                                                    </legend>
+                                                    <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                        <el-col :span="24">
+                                                            <el-checkbox
+                                                                    v-for="(clerk,index) in frameManagedClerk.un_settable[roleName]"
+                                                                    v-if="clerk.checked" disabled :key="index"
+                                                                    v-model="clerk.checked"
+                                                            >{{ clerk.clerk_name }}
+                                                            </el-checkbox>
+                                                        </el-col>
+                                                    </el-row>
+                                                </fieldset>
+                                            </div>
+
+
+                                        </el-col>
+                                    </el-row>
+
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+
+                        <el-tab-pane :label="this.$t('clerkFrameManage.allEmployee')" name="third">
+                            <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-160)+'px'}">
+                                <div style="padding: 0 15px">
+
+
+                                    <el-row v-if="frameManagedClerk.settable!==undefined && Object.keys(frameManagedClerk.settable).length>0">
+                                        <el-col :span="24">
+
+                                            <div class="text item">
+
+                                                <fieldset style="margin-bottom: 10px"
+                                                          v-for="(roleName,_index) in Object.keys(frameManagedClerk.settable)"
+                                                          :key="_index">
+                                                    <legend><span
+                                                            style="margin-right: 10px">{{roleName}}</span>
+                                                    </legend>
+                                                    <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                        <el-col :span="24">
+                                                            <el-checkbox
+                                                                    v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                    :key="index" v-model="clerk.checked"
+                                                                    @change="(val)=>{checkChange(val,roleName)}"
+                                                            >{{ clerk.clerk_name }}
+                                                            </el-checkbox>
+                                                        </el-col>
+                                                    </el-row>
+                                                    <el-divider
+                                                            v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"></el-divider>
+                                                    <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                            v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)">
+                                                        <el-col :span="24">
+                                                            <el-form-item
+                                                                    :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                                <el-select
+                                                                        v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                        @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                       >
+                                                                    <el-option
+                                                                            v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                            v-if="clerk.checked"
+                                                                            :key="clerk.clerk_id"
+                                                                            :label="clerk.clerk_name"
+                                                                            :value="clerk.clerk_id">
+                                                                    </el-option>
+                                                                </el-select>
+                                                            </el-form-item>
+                                                        </el-col>
+                                                    </el-row>
+                                                </fieldset>
+                                            </div>
+
+
+                                        </el-col>
+                                    </el-row>
+
+
+                                    <el-row class="margin-top-sm"
+                                            v-if="frameManagedClerk.un_settable!==undefined && Object.keys(frameManagedClerk.un_settable).length>0">
+                                        <el-col :span="24">
+
+                                            <div class="text item">
+
+                                                <fieldset style="margin-bottom: 10px"
+                                                          v-for="(roleName,_index) in Object.keys(frameManagedClerk.un_settable)"
+                                                          :key="_index">
+                                                    <legend><span
+                                                            style="margin-right: 10px">{{roleName}}</span>
+                                                    </legend>
+                                                    <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                        <el-col :span="24">
+                                                            <el-checkbox
+                                                                    v-for="(clerk,index) in frameManagedClerk.un_settable[roleName]"
+                                                                    disabled :key="index" v-model="clerk.checked"
+                                                            >{{ clerk.clerk_name }}
+                                                            </el-checkbox>
+                                                        </el-col>
+                                                    </el-row>
+                                                    <el-divider
+                                                            v-if="frameManagedClerk.un_settable[roleName].length>0&&frameManagedClerk.un_settable[roleName].some(p=>p.checked)"></el-divider>
+                                                    <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                            v-if="frameManagedClerk.un_settable[roleName].length>0&&frameManagedClerk.un_settable[roleName].some(p=>p.checked)">
+                                                        <el-col :span="24">
+                                                            <el-form-item
+                                                                    :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                                <el-select
+                                                                        v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                        @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                        disabled>
+                                                                    <el-option
+                                                                            v-for="(clerk,index) in frameManagedClerk.un_settable[roleName]"
+                                                                            v-if="clerk.checked"
+                                                                            :key="clerk.clerk_id"
+                                                                            :label="clerk.clerk_name"
+                                                                            :value="clerk.clerk_id">
+                                                                    </el-option>
+                                                                </el-select>
+                                                            </el-form-item>
+                                                        </el-col>
+                                                    </el-row>
+                                                </fieldset>
+                                            </div>
+
+
+                                        </el-col>
+                                    </el-row>
+
+                                </div>
+                            </el-scrollbar>
+                        </el-tab-pane>
+                    </el-tabs>
+
+                </el-form>
+                <div class="margin-top-xs text-center padding-content">
+                    <el-button type="primary" :disabled="frameManagedClerk['type']!==5" @click="saveFrameManagedClerk">
+                        {{this.$t('action.saveSettings')}}
+                    </el-button>
+                </div>
+            </el-card>
+
+        </el-main>
+    </el-container>
+</template>
+
+<script>
+    import * as API_FrameGroup from '@/api/ncs_frameGroup'
+    import {FRAME_TYPE} from '@/utils/enum/FrameTypeEnum'
+    import i18n from "../../../utils/i18n"
+
+    export default {
+        name: "frameView",
+        computed: {
+            asideHeight() {
+                return this.mainAreaHeight - 80
+            }
+        },
+        watch: {
+            selectedNodeId(newval, old) {
+                // this.selectedNode = this.findNodeById(this.treeData, newval)
+            },
+            filterText(val) {
+                this.$refs.frameTree.filter(val)
+            }
+        },
+        mounted() {
+            this.getFrameTree().then(() => {
+                this.selectedNodeId = this.treeData[0].id
+                this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
+            }).catch(err => {
+                this.$message.error(err.message)
+            })
+        },
+        data() {
+            return {
+                treeData: [],
+                treeDataClone: [],
+                isShowTooltip: false,
+                /** 当前选中的树节点 */
+                selectedNodeId: 0,
+                selectedNode: {},
+                filterText: '',
+                abc: [],
+                frameManagedClerk: {},
+                activeName: 'first',
+                i18n:i18n
+            }
+        },
+        methods: {
+            /**
+             * 获取空间结构树形数据
+             * */
+            getFrameTree() {
+                return new Promise((resolve, reject) => {
+                    API_FrameGroup.getframestruct(this.$store.getters.partId, FRAME_TYPE.PART).then(res => {
+                        this.treeData = res.frameTree
+                        resolve()
+                    }).catch(err => {
+                        reject(err)
+                    })
+                })
+            },
+            checkChange(val, roleName) {
+
+                const roleCheckedClerk = this.frameManagedClerk.settable[roleName].filter(p => p.checked)
+
+                if (roleCheckedClerk.length === 0) {
+                    this.frameManagedClerk.phone_acceptor[roleName]=null
+                    this.frameManagedClerk.settable[roleName].forEach(item => {
+                        item.bool_phone_acceptor = false
+                    })
+                } else if (roleCheckedClerk.length === 1) {  //角色群组中只选中了一个职员,把该职员设为默认的电话接收人
+                    this.frameManagedClerk.phone_acceptor[roleName] = roleCheckedClerk[0].clerk_id
+                    this.frameManagedClerk.settable[roleName].forEach(item => {
+                        if (item.clerk_id === roleCheckedClerk[0].clerk_id) {
+                            item.bool_phone_acceptor = true
+                        } else {
+                            item.bool_phone_acceptor = false
+                        }
+                    })
+                } else if (roleCheckedClerk.length > 1) {//查看当前电话接收人是否是选中状态,如果不是选中状态,修改电话接收人
+                    const currentPhoneAcceptor = this.frameManagedClerk.settable[roleName].filter(p => p.clerk_id === this.frameManagedClerk.phone_acceptor[roleName])[0]
+                    if (currentPhoneAcceptor !== undefined && !currentPhoneAcceptor.checked) {
+                        this.frameManagedClerk.phone_acceptor[roleName] = roleCheckedClerk[0].clerk_id
+                        this.frameManagedClerk.settable[roleName].forEach(item => {
+                            if (item.clerk_id === roleCheckedClerk[0].clerk_id) {
+                                item.bool_phone_acceptor = true
+                            } else {
+                                item.bool_phone_acceptor = false
+                            }
+                        })
+                    }
+
+                }
+            },
+            phoneAcceptorChange(val, roleName) {
+                const phoneAcceptor = this.frameManagedClerk.phone_acceptor[roleName]
+                this.frameManagedClerk.settable[roleName].forEach(item => {
+                    if (item.clerk_id === phoneAcceptor) {
+                        item.bool_phone_acceptor = true
+                    } else {
+                        item.bool_phone_acceptor = false
+                    }
+                })
+            },
+            getFrameManagedClerk(frameId) {
+                API_FrameGroup.getFrameManagedClerk(frameId).then(res => {
+                    this.frameManagedClerk = {...res}
+                }).catch(err => {
+
+                })
+            },
+
+
+            /** 节点过滤方法 **/
+            filterNode(value, data) {
+                if (!value) return true
+                return data.full_name.indexOf(value) !== -1
+            },
+            /** 点击树形节点  **/
+            nodeClick(data, node, leaf) {
+                this.$set(this, 'selectedNodeId', data.id)
+                this.getFrameManagedClerk(data.id)
+            },
+
+            saveFrameManagedClerk() {
+                API_FrameGroup.saveFrameManagedClerk(this.frameManagedClerk).then(res => {
+
+                    this.getFrameManagedClerk(res.id)
+                    this.$message.success(this.$t('action.saveSuccess'))
+                }).catch(err => {
+                    this.$message.error(err.message)
+                })
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    .el-aside {
+        margin: 8px;
+        padding: 8px;
+        border-width: 1px;
+        border-style: solid;
+        background: #fff;
+    }
+
+    .custom-tree-node {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        font-size: 14px;
+        padding-right: 8px;
+    }
+
+    .el-tree {
+        margin-top: 8px
+    }
+
+    .el-main {
+        margin: 8px;
+        padding: 0;
+    }
+
+    fieldset {
+        border: 1px solid #DCDFE6;
+        border-radius: 5px;
+    }
+
+    .margin-top-sm {
+        margin-top: 10px
+    }
+
+    /deep/ .el-checkbox__input.is-disabled.is-checked + span.el-checkbox__label {
+        color: #1890ff;
+    }
+
+    /deep/ .el-scrollbar__wrap {
+        overflow-x: hidden;
+    }
+
+    /deep/ .el-form-item {
+        margin-bottom: 10px;
+    }
+</style>

+ 500 - 0
src/views/ncs-clerk-frame-manage/components/multiClerkManageFrame.vue

@@ -0,0 +1,500 @@
+<template>
+    <el-container :style="{height: asideHeight+'px'}">
+        <el-aside width="280px" style="border-color: rgb(238, 241, 246)">
+            <div class="el-row--flex">
+                <el-input
+                        v-model="filterText"
+                        :placeholder="this.$t('frameManage.keywordsFilter')"
+                        clearable
+                />
+            </div>
+            <el-tree
+                    ref="frameTree"
+                    :data="treeData"
+                    :show-checkbox="false"
+                    node-key="id"
+                    :default-expand-all="true"
+                    :auto-expand-parent="true"
+                    :expand-on-click-node="false"
+                    :highlight-current="true"
+                    :current-node-key="selectedNodeId"
+                    :accordion="true"
+                    :filter-node-method="filterNode"
+                    @node-click="nodeClick"
+            >
+          <span slot-scope="{ node, data }" class="custom-tree-node">
+
+            <!--            <span><svg-icon :icon-class="data.type===4?'sickroom':data.type===5?'bed':'area'" />{{ data.full_name }}</span>-->
+              <span
+              ><svg-icon
+                      :style="data.customer_id ? 'color: #0a901c' : ''"
+                      :icon-class="data.type===4?'sickroom':data.type===5?'bed':'area'"
+              />{{ data.full_name }}</span>
+
+
+          </span>
+            </el-tree>
+        </el-aside>
+        <el-main>
+
+            <el-card :style="{height: asideHeight -20 +'px'}">
+                <el-form>
+                <el-tabs v-model="activeName" >
+                    <el-tab-pane :label="this.$t('clerkFrameManage.settableEmployee')" name="first">
+
+                        <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-160)+'px'}">
+                            <div style="padding: 0 15px">
+
+
+                                <el-row v-if="frameManagedClerk.settable!==undefined && Object.keys(frameManagedClerk.settable).length>0">
+                                    <el-col :span="24">
+
+                                        <div class="text item">
+
+                                            <fieldset style="margin-bottom: 10px"  v-for="(roleName,_index) in Object.keys(frameManagedClerk.settable)"  :key="_index">
+                                                <legend><span
+                                                        style="margin-right: 10px">{{roleName}}</span>
+                                                </legend>
+                                                <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                    <el-col :span="24">
+                                                        <el-checkbox v-for="(clerk,index) in frameManagedClerk.settable[roleName]"  :key="index" v-model="clerk.checked"
+                                                                     @change="(val)=>{checkChange(val,roleName)}"
+                                                        >{{ clerk.clerk_name }}
+                                                        </el-checkbox>
+                                                    </el-col>
+                                                </el-row>
+                                                <el-divider
+                                                        v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"></el-divider>
+                                                <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                        v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)">
+                                                    <el-col :span="24">
+                                                        <el-form-item
+                                                                :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                            <el-select
+                                                                    v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                    @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                    >
+                                                                <el-option
+                                                                        v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                        v-if="clerk.checked"
+                                                                        :key="clerk.clerk_id"
+                                                                        :label="clerk.clerk_name"
+                                                                        :value="clerk.clerk_id">
+                                                                </el-option>
+                                                            </el-select>
+                                                        </el-form-item>
+                                                    </el-col>
+                                                </el-row>
+                                            </fieldset>
+                                        </div>
+
+
+                                    </el-col>
+                                </el-row>
+
+
+
+
+                            </div>
+                        </el-scrollbar>
+                    </el-tab-pane>
+                    <el-tab-pane :label="this.$t('clerkFrameManage.settedEmployee')" name="second">
+
+                        <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-160)+'px'}">
+                            <div style="padding: 0 15px">
+
+
+                                <el-row v-if="frameManagedClerk.settable!==undefined && Object.keys(frameManagedClerk.settable).length>0">
+                                    <el-col :span="24">
+
+                                        <div class="text item">
+
+                                            <fieldset style="margin-bottom: 10px"  v-for="(roleName,_index) in Object.keys(frameManagedClerk.settable)" v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)" :key="_index">
+                                                <legend><span
+                                                        style="margin-right: 10px">{{roleName}}</span>
+                                                </legend>
+                                                <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                    <el-col :span="24">
+                                                        <el-checkbox v-for="(clerk,index) in frameManagedClerk.settable[roleName]" v-if="clerk.checked" :key="index" v-model="clerk.checked"
+                                                                     @change="(val)=>{checkChange(val,roleName)}"
+                                                        >{{ clerk.clerk_name }}
+                                                        </el-checkbox>
+                                                    </el-col>
+                                                </el-row>
+                                                <el-divider
+                                                        v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"></el-divider>
+                                                <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                        v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)">
+                                                    <el-col :span="24">
+                                                        <el-form-item
+                                                                :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                            <el-select
+                                                                    v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                    @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                    >
+                                                                <el-option
+                                                                        v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                        v-if="clerk.checked"
+                                                                        :key="clerk.clerk_id"
+                                                                        :label="clerk.clerk_name"
+                                                                        :value="clerk.clerk_id">
+                                                                </el-option>
+                                                            </el-select>
+                                                        </el-form-item>
+                                                    </el-col>
+                                                </el-row>
+                                            </fieldset>
+                                        </div>
+
+
+                                    </el-col>
+                                </el-row>
+
+
+                                <el-row class="margin-top-sm" v-if="frameManagedClerk.un_settable!==undefined && Object.keys(frameManagedClerk.un_settable).length>0" >
+                                    <el-col :span="24">
+
+                                        <div class="text item">
+
+                                            <fieldset style="margin-bottom: 10px"  v-for="(roleName,_index) in Object.keys(frameManagedClerk.un_settable)" :key="_index" v-if="frameManagedClerk.un_settable[roleName].length>0&&frameManagedClerk.un_settable[roleName].some(p=>p.checked)">
+                                                <legend><span
+                                                        style="margin-right: 10px">{{roleName}}</span>
+                                                </legend>
+                                                <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                    <el-col :span="24">
+                                                        <el-checkbox v-for="(clerk,index) in frameManagedClerk.un_settable[roleName]" v-if="clerk.checked" disabled :key="index" v-model="clerk.checked"
+                                                        >{{ clerk.clerk_name }}
+                                                        </el-checkbox>
+                                                    </el-col>
+                                                </el-row>
+                                                <el-divider
+                                                        v-if="frameManagedClerk.un_settable[roleName].length>0&&frameManagedClerk.un_settable[roleName].some(p=>p.checked)"></el-divider>
+                                                <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                        v-if="frameManagedClerk.un_settable[roleName].length>0&&frameManagedClerk.un_settable[roleName].some(p=>p.checked)">
+                                                    <el-col :span="24">
+                                                        <el-form-item
+                                                                :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                            <el-select
+                                                                    v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                    @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                    disabled>
+                                                                <el-option
+                                                                        v-for="(clerk,index) in frameManagedClerk.un_settable[roleName]"
+                                                                        v-if="clerk.checked"
+                                                                        :key="clerk.clerk_id"
+                                                                        :label="clerk.clerk_name"
+                                                                        :value="clerk.clerk_id">
+                                                                </el-option>
+                                                            </el-select>
+                                                        </el-form-item>
+                                                    </el-col>
+                                                </el-row>
+                                            </fieldset>
+                                        </div>
+
+
+                                    </el-col>
+                                </el-row>
+
+                            </div>
+                        </el-scrollbar>
+                    </el-tab-pane>
+
+                    <el-tab-pane :label="this.$t('clerkFrameManage.allEmployee')" name="third">
+                        <el-scrollbar wrap-class="scrollbar-wrapper" :style="{height: (asideHeight-160)+'px'}">
+                            <div style="padding: 0 15px">
+
+
+                                <el-row v-if="frameManagedClerk.settable!==undefined && Object.keys(frameManagedClerk.settable).length>0">
+                                    <el-col :span="24">
+
+                                        <div class="text item">
+
+                                            <fieldset style="margin-bottom: 10px"  v-for="(roleName,_index) in Object.keys(frameManagedClerk.settable)" :key="_index">
+                                                <legend><span
+                                                        style="margin-right: 10px">{{roleName}}</span>
+                                                </legend>
+                                                <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                    <el-col :span="24">
+                                                        <el-checkbox v-for="(clerk,index) in frameManagedClerk.settable[roleName]" :key="index" v-model="clerk.checked"
+                                                                     @change="(val)=>{checkChange(val,roleName)}"
+                                                        >{{ clerk.clerk_name }}
+                                                        </el-checkbox>
+                                                    </el-col>
+                                                </el-row>
+                                                <el-divider
+                                                        v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)"></el-divider>
+                                                <el-row type="flex" :gutter="20" class="margin-top-sm"
+                                                        v-if="frameManagedClerk.settable[roleName].length>0&&frameManagedClerk.settable[roleName].some(p=>p.checked)">
+                                                    <el-col :span="24">
+                                                        <el-form-item
+                                                                :label="i18n.t('clerkFrameManage.phoneAcceptorLabel')">
+                                                            <el-select
+                                                                    v-model="frameManagedClerk.phone_acceptor[roleName]"
+                                                                    @change="(val)=>{phoneAcceptorChange(val,roleName)}"
+                                                                    >
+                                                                <el-option
+                                                                        v-for="(clerk,index) in frameManagedClerk.settable[roleName]"
+                                                                        v-if="clerk.checked"
+                                                                        :key="clerk.clerk_id"
+                                                                        :label="clerk.clerk_name"
+                                                                        :value="clerk.clerk_id">
+                                                                </el-option>
+                                                            </el-select>
+                                                        </el-form-item>
+                                                    </el-col>
+                                                </el-row>
+                                            </fieldset>
+                                        </div>
+
+
+                                    </el-col>
+                                </el-row>
+
+
+                                <el-row class="margin-top-sm" v-if="frameManagedClerk.un_settable!==undefined && Object.keys(frameManagedClerk.un_settable).length>0">
+                                    <el-col :span="24">
+
+                                        <div class="text item">
+
+                                            <fieldset style="margin-bottom: 10px"  v-for="(roleName,_index) in Object.keys(frameManagedClerk.un_settable)" :key="_index">
+                                                <legend><span
+                                                        style="margin-right: 10px">{{roleName}}</span>
+                                                </legend>
+                                                <el-row :gutter="20" type="flex" style="padding: 10px">
+                                                    <el-col :span="24">
+                                                        <el-checkbox v-for="(clerk,index) in frameManagedClerk.un_settable[roleName]" disabled :key="index" v-model="clerk.checked"
+                                                        >{{ clerk.clerk_name }}
+                                                        </el-checkbox>
+                                                    </el-col>
+                                                </el-row>
+                                            </fieldset>
+                                        </div>
+
+
+                                    </el-col>
+                                </el-row>
+
+                            </div>
+                        </el-scrollbar></el-tab-pane>
+                </el-tabs>
+
+
+                <div class="margin-top-xs text-center padding-content">
+                    <el-button type="primary"  :disabled="frameManagedClerk['type']===4" @click="saveFrameManagedClerk">{{this.$t('action.saveSettings')}}</el-button>
+                </div>
+                </el-form>
+            </el-card>
+
+        </el-main>
+    </el-container>
+</template>
+
+<script>
+    import * as API_FrameGroup from '@/api/ncs_frameGroup'
+    import {FRAME_TYPE} from '@/utils/enum/FrameTypeEnum'
+    import i18n from "../../../utils/i18n"
+    export default {
+        name: "multiClerkManageFrame",
+        computed: {
+            asideHeight() {
+                return this.mainAreaHeight - 80
+            }
+        },
+        watch: {
+            selectedNodeId(newval, old) {
+                // this.selectedNode = this.findNodeById(this.treeData, newval)
+            },
+            filterText(val) {
+                this.$refs.frameTree.filter(val)
+            }
+        },
+        mounted() {
+            this.getFrameTree().then(() => {
+                if(this.treeData[0]!=null){
+                    this.selectedNodeId = this.treeData[0].children[0].children[0].id
+                    this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
+                    this.getFrameManagedClerk(this.selectedNodeId)
+                }else{
+                    this.frameManagedClerk={}
+                }
+            }).catch(err => {
+                this.$message.error(err.message)
+            })
+        },
+        data() {
+            return {
+                treeData: [],
+                treeDataClone: [],
+                isShowTooltip: false,
+                /** 当前选中的树节点 */
+                selectedNodeId: 0,
+                selectedNode: {},
+                filterText: '',
+                frameManagedClerk:{
+
+                },
+                activeName:'first',
+                i18n:i18n
+            }
+        },
+        methods: {
+            /**
+             * 获取空间结构树形数据
+             * */
+            getFrameTree() {
+                return new Promise((resolve, reject) => {
+                    API_FrameGroup.getframemultimanagedstruct(this.$store.getters.partId, FRAME_TYPE.PART).then(res => {
+                        this.treeData = [...res]
+                        resolve()
+                    }).catch(err => {
+                        reject(err)
+                    })
+                })
+            },
+
+            getFrameManagedClerk(frameId) {
+                API_FrameGroup.getFrameManagedClerk(frameId).then(res => {
+                    this.frameManagedClerk={...res}
+                }).catch(err => {
+
+                })
+            },
+            checkChange(val, roleName) {
+                const roleCheckedClerk = this.frameManagedClerk.settable[roleName].filter(p => p.checked)
+                if (roleCheckedClerk.length === 0) {
+                    this.frameManagedClerk.phone_acceptor[roleName]=null
+                    this.frameManagedClerk.settable[roleName].forEach(item => {
+                        item.bool_phone_acceptor = false
+                    })
+                } else if (roleCheckedClerk.length === 1) {  //角色群组中只选中了一个职员,把该职员设为默认的电话接收人
+                    this.frameManagedClerk.phone_acceptor[roleName] = roleCheckedClerk[0].clerk_id
+                    this.frameManagedClerk.settable[roleName].forEach(item => {
+                        if (item.clerk_id === roleCheckedClerk[0].clerk_id) {
+                            item.bool_phone_acceptor = true
+                        } else {
+                            item.bool_phone_acceptor = false
+                        }
+                    })
+                } else if (roleCheckedClerk.length > 1) {//查看当前电话接收人是否是选中状态,如果不是选中状态,修改电话接收人
+                    const currentPhoneAcceptor = this.frameManagedClerk.settable[roleName].filter(p => p.clerk_id === this.frameManagedClerk.phone_acceptor[roleName])[0]
+                    if (currentPhoneAcceptor !== undefined && !currentPhoneAcceptor.checked) {
+                        this.frameManagedClerk.phone_acceptor[roleName] = roleCheckedClerk[0].clerk_id
+                        this.frameManagedClerk.settable[roleName].forEach(item => {
+                            if (item.clerk_id === roleCheckedClerk[0].clerk_id) {
+                                item.bool_phone_acceptor = true
+                            } else {
+                                item.bool_phone_acceptor = false
+                            }
+                        })
+                    }
+
+                }
+            },
+            phoneAcceptorChange(val, roleName) {
+                const phoneAcceptor = this.frameManagedClerk.phone_acceptor[roleName]
+                this.frameManagedClerk.settable[roleName].forEach(item => {
+                    if (item.clerk_id === phoneAcceptor) {
+                        item.bool_phone_acceptor = true
+                    } else {
+                        item.bool_phone_acceptor = false
+                    }
+                })
+            },
+
+            /** 节点过滤方法 **/
+            filterNode(value, data) {
+                if (!value) return true
+                return data.full_name.indexOf(value) !== -1
+            },
+            /** 点击树形节点  **/
+            nodeClick(data, node, leaf) {
+                this.$set(this, 'selectedNodeId', data.id)
+                this.getFrameManagedClerk(data.id)
+            },
+
+            saveFrameManagedClerk(){
+                API_FrameGroup.saveFrameManagedClerk(this.frameManagedClerk).then(res=>{
+                    this.getFrameTree().then(() => {
+                        if(this.treeData[0]!=null){
+                            let node = this.findNodeById(this.treeData,this.selectedNodeId)
+                            if(node!=null){ //原来选选择的node没有了
+                            this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
+                            }else{
+                                this.selectedNodeId = this.treeData[0].children[0].children[0].id
+                                this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
+                            }
+                            this.getFrameManagedClerk(this.selectedNodeId)
+                        }else{
+                            this.frameManagedClerk={}
+                        }})
+                    this.$message.success(this.$t('action.saveSuccess'))
+                }).catch(err=>{
+                    this.$message.error(err.message)
+                })
+            },
+            /** 树形结构中查找指定Id节点 */
+            findNodeById(data, id) {
+                let node = null
+                if (data.length > 0) {
+                    for (var i = 0; i < data.length; i++) {
+                        if (data[i].id === id) {
+                            node = data[i]
+                            break
+                        }
+                        if (data[i].children && data[i].children.length > 0) {
+                            const subresult = this.findNodeById(data[i].children, id)
+                            if (subresult !== null) {
+                                node = subresult
+                                break
+                            }
+                        }
+                    }
+                }
+                return node
+            },
+        }
+    }
+
+</script>
+
+<style scoped>
+    .el-aside {
+        margin: 8px;
+        padding: 8px;
+        border-width: 1px;
+        border-style: solid;
+        background: #fff;
+    }
+
+    .custom-tree-node {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        font-size: 14px;
+        padding-right: 8px;
+    }
+
+    .el-tree {
+        margin-top: 8px
+    }
+
+    .el-main {
+        margin: 8px;
+        padding: 0;
+    }
+
+    fieldset {
+        border: 1px solid #DCDFE6;
+        border-radius: 5px;
+    }
+    .margin-top-sm{margin-top: 20px}
+    /deep/.el-checkbox__input.is-disabled.is-checked + span.el-checkbox__label{
+        color: #1890ff;
+    }
+    /deep/ .el-scrollbar__wrap{
+        overflow-x: hidden;
+    }
+
+</style>

+ 49 - 0
src/views/ncs-clerk-frame-manage/index.vue

@@ -0,0 +1,49 @@
+<template>
+    <div class="container">
+        <el-container>
+            <el-header height="36px" align="center" style="padding: 10px">
+                        <el-radio-group v-model="radio1">
+                            <el-radio-button :label="1">{{this.$t('clerkFrameManage.employeeView')}}</el-radio-button>
+                            <el-radio-button :label="2">{{this.$t('clerkFrameManage.frameView')}}</el-radio-button>
+                            <el-radio-button :label="3">{{this.$t('clerkFrameManage.multiClerkView')}}</el-radio-button>
+                        </el-radio-group>
+
+            </el-header>
+            <el-divider></el-divider>
+            <el-main>
+                <employee-view v-if="radio1===1"></employee-view>
+                <frame-view v-if="radio1===2"></frame-view>
+                <multi-clerk-manage-frame v-if="radio1===3"></multi-clerk-manage-frame>
+            </el-main>
+        </el-container>
+    </div>
+</template>
+
+<script>
+    import EmployeeView from "./components/employeeView";
+    import FrameView from "./components/frameView";
+    import MultiClerkManageFrame from "./components/multiClerkManageFrame";
+
+    export default {
+        name: "index",
+        components: {MultiClerkManageFrame, FrameView, EmployeeView},
+        data() {
+            return {
+                radio1: 1
+            }
+        }
+
+    }
+</script>
+
+<style scoped>
+/deep/ .el-divider--horizontal{
+    margin:0;
+    margin-top: 20px;
+}
+
+/deep/ .el-main{
+    margin: 8px;
+    padding: 0;
+}
+</style>

+ 84 - 47
src/views/ncs-clerk/components/clerkList.vue

@@ -147,26 +147,41 @@
             </el-form-item>
           </el-col>
         </el-row>
-        <el-row v-if="nurseList.length > 0">
+        <el-row v-if="addMemberForm.subordinate!==undefined && Object.keys(addMemberForm.subordinate).length>0">
           <el-col :span="24">
             <el-form-item :label="this.$t('clerkManage.clerkList')">
               <div class="text item">
-                <el-row>
-                  <el-checkbox-group v-model="newCheckList">
-                    <el-col v-for="(bed,_index) in nurseClerkList" :key="_index" :xs="8" :sm="8" :md="6" :lg="6" :xl="6">
-                      <el-checkbox :label="bed.clerk_id" @change="handleCheckboxChanged(bed)">
-                        <span v-if="bed.group_id && !bed.checked" style="color: #13ce66">{{ bed.clerk_name }}  <span style="color: #302db4">{{ bed.group_name }}</span></span>
-                        <span v-else>{{ bed.clerk_name }}</span>
+
+                <fieldset style="margin-bottom: 10px"  v-for="(roleName,_index) in Object.keys(addMemberForm.subordinate)" :key="_index">
+                  <legend><span
+                          style="margin-right: 10px">{{roleName}}</span>
+                  </legend>
+                  <el-row :gutter="20" type="flex" style="padding: 10px">
+                    <el-col :span="24">
+                      <el-checkbox v-for="(clerk,index) in addMemberForm.subordinate[roleName]" :key="index" v-model="clerk.checked"
+                                   >{{ clerk.clerk_name }}<span v-if="!clerk.checked&&clerk.leader_id!==addMemberForm.clerk_id" style="color: #67C23A;margin-left: 10px;">{{ clerk.leader_name }}</span>
                       </el-checkbox>
                     </el-col>
-                  </el-checkbox-group>
-                  <el-col v-for="(bed,_index) in nurseClerkList" :key="_index" :xs="8" :sm="8" :md="6" :lg="6" :xl="6">
-                    <!--                    <el-checkbox v-model="bed.checked">-->
-                    <!--                      <span v-if="bed.group_id && !bed.checked" style="color: #13ce66">{{ bed.clerk_name }}  <span style="color: #302db4">{{ bed.group_name }}</span></span>-->
-                    <!--                      <span v-else>{{ bed.clerk_name }}</span>-->
-                    <!--                    </el-checkbox>-->
-                  </el-col>
-                </el-row>
+                  </el-row>
+                </fieldset>
+
+
+<!--                <el-row>-->
+<!--                  <el-checkbox-group v-model="newCheckList">-->
+<!--                    <el-col v-for="(bed,_index) in addMemberForm.subordinate" :key="_index" :xs="8" :sm="8" :md="6" :lg="6" :xl="6">-->
+<!--                      <el-checkbox :label="bed.clerk_id" @change="handleCheckboxChanged(bed)">-->
+<!--                        <span v-if="bed.group_id && !bed.checked" style="color: #13ce66">{{ bed.clerk_name }}  <span style="color: #302db4">{{ bed.group_name }}</span></span>-->
+<!--                        <span v-else>{{ bed.clerk_name }}</span>-->
+<!--                      </el-checkbox>-->
+<!--                    </el-col>-->
+<!--                  </el-checkbox-group>-->
+<!--                  <el-col v-for="(bed,_index) in nurseClerkList" :key="_index" :xs="8" :sm="8" :md="6" :lg="6" :xl="6">-->
+<!--                    &lt;!&ndash;                    <el-checkbox v-model="bed.checked">&ndash;&gt;-->
+<!--                    &lt;!&ndash;                      <span v-if="bed.group_id && !bed.checked" style="color: #13ce66">{{ bed.clerk_name }}  <span style="color: #302db4">{{ bed.group_name }}</span></span>&ndash;&gt;-->
+<!--                    &lt;!&ndash;                      <span v-else>{{ bed.clerk_name }}</span>&ndash;&gt;-->
+<!--                    &lt;!&ndash;                    </el-checkbox>&ndash;&gt;-->
+<!--                  </el-col>-->
+<!--                </el-row>-->
               </div>
             </el-form-item>
 
@@ -204,13 +219,14 @@
 
 <script>
 // import { serverUrl } from '@/utils/domain'
-import * as clerk_API from '@/api/ncs_clerk'
+import * as API_Clerk from '@/api/ncs_clerk'
 import * as RegExp from '@/utils/RegExp'
 import { AG_GRID_LOCALE_CN } from '@/utils/AgGridVueLocaleCn'
 import ButtonCellRender from '@/components/AgGridCellRender/ButtonCellRender'
 import AgGridImg from '@/components/AgGridImg/AgGridImg'
 import ImageViewer from 'element-ui/packages/image/src/image-viewer'
 import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
 import * as API_User from "@/api/user";
 import {ROLE_TYPE} from "@/utils/enum/RoleTypeEnum";
 import ButtonCellRenderList from "@/components/AgGridCellRender/ButtonCellRenderList";
@@ -218,7 +234,8 @@ let prevOverflow = ''
 const serverUrl = domain.serverUrl
 export default {
   name: 'CareDoctorManager',
-  components: { ButtonCellRenderList, ButtonCellRender, AgGridImg, ImageViewer, RadioFilter },
+  components: { ButtonCellRenderList, ButtonCellRender, AgGridImg, ImageViewer, RadioFilter,ListFilter },
+
   props: {
     partId: {
       type: Number,
@@ -310,6 +327,7 @@ export default {
         { key: this.$t('member.man'), value: 1 },
         { key: this.$t('member.woman'), value: 0 }
       ],
+      roleTransfer:[],
       roleId: null,
       roleZzId: null,
       newCheckList: [],
@@ -331,7 +349,7 @@ export default {
       } else {
         this.shopId = val
       }
-      this.params.fixedCondition = this.part_view ? 'nc.shop_id=' + this.shopId : '1 = 1'
+      this.params.fixedCondition = this.part_view ? 'shop_id=' + this.shopId : '1 = 1'
       this.GET_MemberList()
     }
   },
@@ -345,11 +363,11 @@ export default {
         headerCheckboxSelectionFilteredOnly: true,
         checkboxSelection: true,
         sortable: false, filter: false,
-        width: 50,
+        width: 100,
         resizable: false,
         valueGetter: this.hashValueGetter
       },
-      { headerName: this.$t('member.face'), field: 'face', sortable: true, filter: false, width: 70,
+      { headerName: this.$t('member.face'), field: 'face', sortable: true, filter: false, width: 180,
         cellRendererFramework: 'AgGridImg',
         cellRendererParams: param => {
           return {
@@ -358,15 +376,19 @@ export default {
           }
         }
       },
-      { headerName: this.$t('member.uname'), field: 'uname', sortable: true, filter: true, minWidth: 170 },
-      { headerName: this.$t('member.nickname'), field: 'clerk_name', sortable: true, filter: true, minWidth: 120 },
-      { headerName: this.$t('member.sex'), field: 'sex', sortable: true, valueFormatter: this.formatterSex, width: 100, filterFramework: 'RadioFilter',
+      { headerName: this.$t('member.uname'), field: 'uname', sortable: true, filter: true, width: 180 },
+      { headerName: this.$t('member.nickname'), field: 'clerk_name', sortable: true, filter: true, width: 180 },
+      { headerName: this.$t('member.sex'), field: 'sex', sortable: true, valueFormatter: this.formatterSex, width: 180, filterFramework: 'RadioFilter',
         filterParams: {
           listData: this.sexTransfer
         }
       },
-      { headerName: this.$t('member.mobile'), field: 'mobile', sortable: true, filter: true, minWidth: 170 },
-      { headerName: this.$t('clerkManage.role'), field: 'role_name', sortable: true, filter: false, valueFormatter: this.formatterRole, width: 120 },
+      { headerName: this.$t('member.mobile'), field: 'mobile', sortable: true, filter: true, width: 200 },
+      { headerName: this.$t('clerkManage.role'), field: 'role_name', sortable: true, filter: false, valueFormatter: this.formatterRole,   filterFramework: 'ListFilter', flex:1,
+        filterParams: {
+          listData: this.roleTransfer
+        } },
+      { headerName: this.$t('member.leaderName'), field: 'leader_name', sortable: true, filter: true, width: 180 },
       { headerName: this.$t('action.handle'), field: 'id',
         cellRendererFramework: 'ButtonCellRender',
         cellRendererParams: param => {
@@ -380,7 +402,7 @@ export default {
         filter: false,
         pinned: 'right',
         lockPinned: true,
-        width: 100,
+        width: 160,
         resizable: false,
         sortable: false
       },
@@ -432,7 +454,7 @@ export default {
       // },
     ]
     this.defaultColDef = {
-      filter: 'agTextColumnFilter',
+      // filter: 'agTextColumnFilter',
       sortable: true,
       resizable: true,
       comparator: this.testComparator,
@@ -445,9 +467,11 @@ export default {
     }
     this.rowSelection = 'multiple'
   },
-  mounted() {
+  async mounted() {
     this.gridApi = this.gridOptions.api
-    this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
+    this.gridApi = this.gridOptions.api
+    this.gridColumnApi = this.gridOptions.columnApi
+
     if (this.partId === 0) {
       this.shopId = this.$store.getters.partId
     } else {
@@ -455,6 +479,8 @@ export default {
     }
     this.params.fixedCondition = this.part_view ? 'shop_id=' + this.shopId : '1 = 1'
     this.GET_MemberList()
+    this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
+
   },
   activated() {
     this.GET_MemberList()
@@ -468,8 +494,8 @@ export default {
     GET_MemberList() {
       this.loading = true
       this.gridApi.showLoadingOverlay()
-      this.gridApi.sizeColumnsToFit()
-      clerk_API.getList(this.params).then(response => {
+      // this.gridApi.sizeColumnsToFit()
+      API_Clerk.getList(this.params).then(response => {
         this.loading = false
         const size = 0
         // response.data.forEach((item, index) => {
@@ -504,7 +530,7 @@ export default {
         cancelButtonText: this.$t('action.cancel'),
         type: 'warning'
       }).then(() => {
-        clerk_API.remove(ids).then(
+        API_Clerk.remove(ids).then(
           response => {
             this.GET_MemberList()
             this.$message({
@@ -527,8 +553,14 @@ export default {
     },
     getRoles(param) {
       // 获取角色
-      clerk_API.getRoles(param).then(res => {
+      API_Clerk.getRoles(param).then(res => {
         this.rolesOptions = res.data
+        this.roleTransfer =[...res.data.map(p=>{return {'key':p.role_name,'value':p.role_name}})]
+        this.columnDefs[6].filterParams.listData=this.roleTransfer
+        console.log(this.columnDefs)
+        this.gridApi.setColumnDefs(this.columnDefs);
+        this.gridApi.redrawRows()
+        console.log(this.roleTransfer)
         const nurseZz = res.data.find(item => item.role_name === '护士组长')
         if (nurseZz) {
           this.roleZzId = nurseZz.role_id
@@ -594,24 +626,24 @@ export default {
           }
           params.role_name = this.rolesOptions.filter( p => p.role_id === params.role_id)[0].role_name
           if (params.member_id) {
-            if (this.roleZzId === params.role_id) {
-              const addIds = this.newCheckList.filter(function(val) { return _this.oldCheckList.indexOf(val) === -1 })
-              const delIds = this.oldCheckList.filter(function(val) { return _this.newCheckList.indexOf(val) === -1 })
-              const data = {
-                clerkId: params.clerk_id,
-                addIds: addIds.join(','),
-                delIds: delIds.join(',')
-              }
-              clerk_API.updateParentById(data)
-            }
-            clerk_API.update(params.clerk_id, params).then(response => {
+            // if (this.roleZzId === params.role_id) {
+            //   const addIds = this.newCheckList.filter(function(val) { return _this.oldCheckList.indexOf(val) === -1 })
+            //   const delIds = this.oldCheckList.filter(function(val) { return _this.newCheckList.indexOf(val) === -1 })
+            //   const data = {
+            //     clerkId: params.clerk_id,
+            //     addIds: addIds.join(','),
+            //     delIds: delIds.join(',')
+            //   }
+            //   API_Clerk.updateParentById(data)
+            // }
+            API_Clerk.update(params.clerk_id, params).then(response => {
               this.dialogAddMemberVisible = false
               this.imageUrl = null
               this.GET_MemberList()
             })
           } else {
             params.shop_id = this.shopId
-            clerk_API.add(params).then(response => {
+            API_Clerk.add(params).then(response => {
               this.dialogAddMemberVisible = false
               this.imageUrl = null
               this.GET_MemberList()
@@ -655,7 +687,7 @@ export default {
       const _this = this
       this.newCheckList = []
       this.oldCheckList = []
-      clerk_API.getNurseByRoleId(_this.roleId, this.shopId).then(r => {
+      API_Clerk.getNurseByRoleId(_this.roleId, this.shopId).then(r => {
         _this.nurseList = r
         _this.getNurseIds(id)
         // r.forEach(item => {
@@ -668,7 +700,7 @@ export default {
     },
     getNurseIds(id) {
       const _this = this
-      clerk_API.getNurseIdsByPartId(this.shopId).then(r => {
+      API_Clerk.getNurseIdsByPartId(this.shopId).then(r => {
         r.forEach(item => {
           _this.nurseList.forEach(t => {
             if (t.clerk_id === item.nurse_id) {
@@ -808,6 +840,11 @@ export default {
     cursor: pointer;
   }
 
+  fieldset {
+    border: 1px solid #DCDFE6;
+    border-radius: 5px;
+  }
+
   .formwrap /deep/ .el-tabs__content {
     overflow: visible !important;
   }

+ 1 - 1
src/views/ncs-device/components/deviceManager.vue

@@ -777,7 +777,7 @@
                         if (this.onlineDevice.length > 0) {
                             if (this.isCannotBeOnline(item.device_type)) {
                                 if (item.eth_mac) {
-                                    const mac = this.onlineDevice.filter(p => p.toLowerCase() === item.eth_mac.toLowerCase())[0]
+                                    const mac = this.onlineDevice.filter(p => p != null && (p.toLowerCase() === item.eth_mac.toLowerCase()))[0]
                                     // console.log('在线设备1', mac)
                                     if (mac !== undefined && mac !== null) {
                                         item['online_state'] = this.$t('deviceManage.connectTrue')

+ 6 - 0
src/views/ncs-orginazition/components/partInfoEdit.vue

@@ -330,6 +330,11 @@
                 <el-checkbox v-model="formmodel.record_enabled" :true-label="1" :false-label="0">{{ this.$t('partInfo.recordAble') }}</el-checkbox>
               </el-form-item>
             </el-col>
+            <el-col :span="8">
+              <el-form-item :label="this.$t('partInfo.screenLight')" prop="screen_light">
+                <el-checkbox v-model="formmodel.screen_light" :true-label="1" :false-label="0">{{ this.$t('partInfo.screenLight') }}</el-checkbox>
+              </el-form-item>
+            </el-col>
           </el-row>
 
 
@@ -676,6 +681,7 @@ export default {
                 clerk_name_hidden: 0,
                 channel_im_history_store_days: 30,
                 record_enabled: false,
+                screen_light: 0,
                 auto_accept: 0,
                 door_nurse_title: '呼叫护士',
                 door_nurse_valid: 1,

+ 5 - 0
src/views/ncs-orginazition/partInfoSetting.vue

@@ -95,6 +95,11 @@
           <app-version-manager :part-id="part_id" :device-type="131" />
         </keep-alive>
       </el-tab-pane>
+      <el-tab-pane :label="this.$t('partInfo.entraceguard_device')" name="entraceguardDevice">
+        <keep-alive>
+          <app-version-manager :part-id="part_id" :device-type="18" />
+        </keep-alive>
+      </el-tab-pane>
     </el-tabs>
   </div>
 </template>