瀏覽代碼

Merge branch 'master' of http://git.wdklian.com/allen/ncs_ui

* 'master' of http://git.wdklian.com/allen/ncs_ui:
  开发医院级的空间结构、设备管理、员工管理、用户管理
  创建空间和设备的枚举类,替换所有里面的数字,优化新建设备时对空间的选择,优化模拟设备的mac地址输入
  增加紧急按钮,调整遥控器mac地址必输
  新增485转换盒设备添加,增加模拟分机设备对语音转换盒和485转换盒id的添加
  修改护士组长与护士的逻辑、优化换班初始化
  优化护士组长换班

# Conflicts:
#	src/views/ncs-clerk/clerkList.vue
#	src/views/ncs-device/components/deviceManager.vue
wuyunfeng 3 年之前
父節點
當前提交
6cef620795

+ 11 - 2
src/api/ncs_clerk.js

@@ -17,6 +17,15 @@ export function getList(params) {
     headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
   })
 }
+export function getClerkList(params) {
+  return request({
+    url: '/ncs/clerk/getClerkAndMemberByShopId',
+    method: 'POST',
+    loading: true,
+    data: params,
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+  })
+}
 export function add(params) {
   params = JSON.parse(JSON.stringify(params))
   params.password = md5(params.password)
@@ -62,9 +71,9 @@ export function getNurseByRoleId(roleId, partId) {
     method: 'GET'
   })
 }
-export function getNurseIds(partId, memberId) {
+export function getNurseIdsByPartId(partId) {
   return request({
-    url: `/ncs/clerk/getNurseIds/${partId}/${memberId}`,
+    url: `/ncs/clerk/getNurseIdsByPartId/${partId}`,
     method: 'GET'
   })
 }

+ 1 - 1
src/api/ncs_device.js

@@ -65,7 +65,7 @@ export function getDeviceById(id) {
 /** 查询某类设备 */
 export function getDeviceByType(partid, type) {
   return request({
-    url: `/ncs/device/${partid}/${type}`,
+    url: `/ncs/device/getNurseDeviceList/${partid}/${type}`,
     method: 'get',
     loading: false
   })

+ 15 - 0
src/api/ncs_hospitalFrame.js

@@ -14,6 +14,14 @@ export function getHospitalFrameList(id) {
     headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
   })
 }
+/** 根据空间类型查找空间 */
+export function getFrameByPartIdAndFrameType(partId, frameType) {
+  return request({
+    url: `/ncs/frame/getFrameByPartIdAndFrameType/${partId}/${frameType}`,
+    loading: false,
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+  })
+}
 /** 新增医院结构 */
 export function addHospitalFrame(params) {
   // params = JSON.parse(JSON.stringify(params))
@@ -89,6 +97,13 @@ export function getFramesPartId(part_id) {
     loading: false
   })
 }
+export function getFramesHospital(hospital_id) {
+  return request({
+    url: `/ncs/frame/getFramesByHospitalId/${hospital_id}`,
+    method: 'GET',
+    loading: false
+  })
+}
 export function getFrameByType(id, deviceId, roleId) {
   return request({
     url: `/ncs/frame/getFrameByType/${id}/${deviceId}/${roleId}`,

+ 9 - 0
src/api/ncs_shop.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function getShopList(partId) {
+  return request({
+    url: `/ncs/shop/getShopByHospitalId/${partId}`,
+    method: 'GET'
+  })
+}
+

+ 44 - 1
src/router/index.js

@@ -375,7 +375,50 @@ export const hospitalRoutes = [
         path: 'index',
         component: () => import('@/views/hospital/ncs_frame/frameTreeView'),
         name: 'hospital_frameTreeView',
-        meta: { title: '空间管理', icon: 'component', noCache: true }
+        meta: { title: '空间管理', icon: 'tree', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/hospital/ncs_device',
+    component: Layout,
+    redirect: '/ncs_device/index',
+    children: [
+      {
+        path: 'index',
+        // component: () => import('@/views/hospital/ncs_device/deviceManager'),
+        component: () => import('@/views/hospital/deviceManagement'),
+        name: 'hospital_deviceList',
+        meta: { title: '设备管理', icon: 'component', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/hospital/ncs_clerk',
+    component: Layout,
+    redirect: '/ncs_clerk/index',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/hospital/clerkManager'),
+        name: 'hospital_clerkList',
+        meta: { title: '员工管理', icon: 'peoples', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/hospital/ncs_customer',
+    component: Layout,
+    redirect: '/ncs_customer/index',
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/hospital/customerManagement'),
+        name: 'hospital_customerList',
+        meta: { title: '用户管理', icon: 'el-icon-s-custom', noCache: true }
+        // component: () => uiVersion === 1 ? import('@/views/customer/patientManagement') : import('@/views/customer/customerManagement'),
+        // name: uiVersion === 1 ? 'hospital_patientList' : 'hospital_customerList',
+        // meta: { title: uiVersion === 1 ? '病人管理' : '用户管理', icon: 'el-icon-s-custom', noCache: true }
       }
     ]
   },

+ 4 - 0
src/utils/device_type.js

@@ -34,6 +34,10 @@ export function returnDeviceType(value) {
       return '信标'
     case 17:
       return '看板'
+    case 20:
+      return '485转换盒'
+    case 21:
+      return '紧急按钮'
     default:
       return '未知设备'
   }

+ 27 - 0
src/utils/enum/DeviceTypeEnum.js

@@ -0,0 +1,27 @@
+import createEnum from '@/utils/enum/createEnum'
+
+export const DEVICE_TYPE = createEnum(
+  {
+    NURSE_HOST: [1, '护士主机'],
+    DOCTOR_HOST: [2, '医生主机'],
+    DOOR_DEVICE: [3, '门口机'],
+    DIGIT_BED_DEVICE: [4, '病床分机'],
+    LED_SCREEN: [5, 'LCD走廊屏'],
+    LCD_SCREEN: [6, 'LED点阵屏'],
+    NURSE_WATCH: [7, '护士移动'],
+    WORKER_WATCH: [8, '护工移动'],
+    USER_WATCH: [9, '用户移动'],
+    CELL_PHONE: [10, '手机App'],
+    TRANSFER_DEVICE: [11, '总线转换盒'],
+    SIMULATE_BED_DEVICE: [12, '模拟分机'],
+    SIMULATE_EMERGENCY_BUTTON: [13, '模拟紧急按钮'],
+    SIMULATE_DOOR_LIGHT: [14, '模拟门灯'],
+    REMOTE_CONTROL: [15, '遥控器'],
+    BEACON: [16, '信标'],
+    INFORMATION_BOARD: [17, '信息看版'],
+    ENTRANCE_GUARD: [18, '门禁设备'],
+    VISITATION: [19, '探视机'],
+    RS485_TRANSFER: [20, '485转换盒'],
+    EMERGENCY_BUTTON: [21, '紧急按钮']
+  }
+)

+ 25 - 0
src/utils/enum/FrameTypeEnum.js

@@ -0,0 +1,25 @@
+// const frameType = {
+//   'HOSPITAL': 1,
+//   'BUILDING': 2,
+//   'PART': 3,
+//   'ROOM': 4,
+//   'BED': 5
+// }
+import createEnum from '@/utils/enum/createEnum'
+
+export const FRAME_TYPE = createEnum(
+  {
+    HOSPITAL: [1, '机构'],
+    BUILDING: [2, '楼栋'],
+    PART: [3, '科室'],
+    ROOM: [4, '房间'],
+    BED: [5, '床位']
+  }
+)
+// enum FRAME_TYPE {
+//     HOSPITAL = 1,
+//     BUILDING = 2,
+//     PART = 3,
+//     ROOM = 4,
+//     BED = 5
+// }

+ 16 - 0
src/utils/enum/RoleTypeEnum.js

@@ -0,0 +1,16 @@
+import createEnum from '@/utils/enum/createEnum'
+
+export const ROLE_TYPE = createEnum(
+  {
+    PRINCIPAL: [0, '机构负责人'],
+    ADMINISTRATORS: [1, '管理员'],
+    DOCTOR: [2, '医生'],
+    NURSE: [3, '护士'],
+    WORKER: [4, '房间'],
+    NURSE_SUPERVISOR: [5, '护士主管'],
+    DIETITIAN: [6, '营养师'],
+    HOUSEKEEPER: [7, '管家'],
+    NURSE_HEAD: [8, '护士组长'],
+    LIFE_ASSISTANT: [9, '生活助理']
+  }
+)

+ 30 - 0
src/utils/enum/createEnum.js

@@ -0,0 +1,30 @@
+/**
+ * 枚举定义工具
+ * 示例:
+ * const STATUS = createEnum({
+ *     AUDIT_WAIT: [1, '审核中'],
+ *     AUDIT_PASS: [2, '审核通过']
+ * })
+ * 获取枚举值:STATUS.AUDIT_WAIT
+ * 获取枚举描述:STATUS.getDesc('AUDIT_WAIT')
+ * 通过枚举值获取描述:STATUS.getDescFromValue(STATUS.WAIT)
+ *
+ */
+export default function createEnum(definition) {
+  const strToValueMap = {}
+  const numToDescMap = {}
+  for (const enumName of Object.keys(definition)) {
+    const [value, desc] = definition[enumName]
+    strToValueMap[enumName] = value
+    numToDescMap[value] = desc
+  }
+  return {
+    ...strToValueMap,
+    getDesc(enumName) {
+      return (definition[enumName] && definition[enumName][1]) || ''
+    },
+    getDescFromValue(value) {
+      return numToDescMap[value] || ''
+    }
+  }
+}

+ 3 - 2
src/views/customer/components/customerManager.vue

@@ -432,6 +432,7 @@ import * as RegExp from '@/utils/RegExp'
 import { serverUrl } from '@/utils/domain'
 import * as API_Remark from '@/api/ncs_remark'
 import * as API_User from '@/api/user'
+import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
 export default {
   name: 'CustomerManager',
   components: { ButtonCellRender, ListFilter, RadioFilter },
@@ -587,9 +588,9 @@ export default {
   },
   watch: {
     frame(val, oldvalue) {
-      if (val.type === 3) { // 科室
+      if (val.type === FRAME_TYPE.PART) { // 科室
         this.params.fixedCondition = ' part_id =' + this.frame.part_id
-      } else if (val.type === 4) { // 房间
+      } else if (val.type === FRAME_TYPE.ROOM) { // 房间
         const ids = []
         this.frame.children.forEach(item => {
           ids.push(item.id)

+ 840 - 0
src/views/hospital/clerkManager.vue

@@ -0,0 +1,840 @@
+<template>
+  <div>
+    <ag-grid-layout
+      :table-height="tableHeight"
+      theme="ag-theme-alpine"
+      :column-defs="columnDefs"
+      :row-data="tableData"
+      :locale-text="localeText"
+      :grid-options="gridOptions"
+      :default-col-def="defaultColDef"
+      :animate-rows="true"
+      :row-selection="rowSelection"
+      row-height="50"
+      @filterChanged="filterModifed"
+      @sortChanged="gridSortChange"
+    >
+      <div slot="toolbar" class="inner-toolbar">
+        <div class="toolbar-search">
+          <en-table-search placeholder="请输入搜索关键字" @search="handlerSearch" />
+        </div>
+        <div class="toolbar-btns">
+          <el-button type="primary" @click="handleAddMember">新增</el-button>
+          <!-- <el-button type="danger" @click="batchDelete">禁用</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>
+    <!--添加会员 dialog-->
+    <el-dialog
+      title="编辑成员信息"
+      :visible.sync="dialogAddMemberVisible"
+      width="60%"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+    >
+      <el-form ref="addMemberForm" :model="addMemberForm" :rules="addMemberRules" label-width="110px" class="formwrap">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="头像">
+              <el-upload
+                class="avatar-uploader"
+                :action="`${uploadurl}?scene=avatar`"
+                :show-file-list="false"
+                :on-success="uploaded"
+                :before-upload="handleShopLogoBefore"
+              >
+                <img v-if="imageUrl" :src="imageUrl" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon" />
+              </el-upload>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="登录名" prop="uname">
+              <el-input v-model="addMemberForm.uname" :maxlength="20" />
+            </el-form-item>
+            <el-form-item label="密码" prop="password">
+              <el-input v-model="addMemberForm.password" :type="pwdType" :maxlength="20" />
+              <span class="show-pwd" @click="pwdType = pwdType === 'password' ? 'text' : 'password'">
+                <svg-icon :icon-class="pwdType === 'password' ? 'eye' : 'eye-open'" />
+              </span>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="真实姓名" prop="nickname">
+              <el-input v-model="addMemberForm.nickname" :maxlength="20" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="手机号码" prop="mobile">
+              <el-input v-model.number="addMemberForm.mobile" :maxlength="11" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="性别" class="form-item-sex">
+              <el-radio v-model="addMemberForm.sex" :label="1">男</el-radio>
+              <el-radio v-model="addMemberForm.sex" :label="0">女</el-radio>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <!--固定电话-->
+            <el-form-item label="身份证号">
+              <el-input v-model="addMemberForm.midentity" :maxlength="20" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12" />
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <!--生日-->
+            <el-form-item label="生日" prop="birthday">
+              <el-date-picker
+                v-model="addMemberForm.birthday"
+                type="date"
+                :editable="false"
+                value-format="timestamp"
+                placeholder="选择生日"
+                :picker-options="{disabledDate(time) { return time.getTime() > Date.now() }}"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="角色" prop="role_id">
+              <el-select
+                v-model="addMemberForm.role_id"
+                placeholder="请选择角色"
+                :disabled="roleTypeDisabled"
+                clearable
+              >
+
+                <el-option
+                  v-for="item in rolesOptions"
+                  :key="item.role_id"
+                  :label="item.role_name"
+                  :value="item.role_id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <!--详细地址-->
+            <el-form-item label="详细地址" prop="address">
+              <el-input v-model="addMemberForm.address" :maxlength="50" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="所负责机构" prop="shop_id">
+              <el-select v-model="addMemberForm.shop_id" placeholder="选择机构" :disabled="shopDisabled" clearable>
+                <el-option v-for="(item, index) in shopOptions" :key="index" :label="item.shop_name" :value="item.shop_id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row v-if="nurseList.length > 0">
+          <el-col :span="24">
+            <el-form-item label="小组成员">
+              <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>
+                      </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>
+              </div>
+            </el-form-item>
+
+          </el-col>
+        </el-row>
+
+
+        <!--        <el-row>-->
+        <!--          <el-col :span="12">-->
+        <!--            &lt;!&ndash;地区&ndash;&gt;-->
+        <!--            <el-form-item label="地区" prop="region" class="form-item-region">-->
+        <!--              <en-region-picker :api="MixinRegionApi" @changed="(object) => { addMemberForm.region = object.last_id }"/>-->
+        <!--            </el-form-item>-->
+        <!--          </el-col>-->
+        <!--          <el-col :span="12">-->
+
+        <!--            &lt;!&ndash;邮箱&ndash;&gt;-->
+        <!--            <el-form-item label="邮箱" prop="email">-->
+        <!--              <el-input v-model="addMemberForm.email"></el-input>-->
+        <!--            </el-form-item>-->
+        <!--          </el-col>-->
+        <!--        </el-row>-->
+
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogAddMemberVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submitAddMemberForm('addMemberForm')">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <template>
+      <image-viewer v-if="showViewer" :on-close="closeViewer" :url-list="srcList" />
+    </template>
+  </div>
+</template>
+
+<script>
+import { serverUrl } from '@/utils/domain'
+import * as clerk_API from '@/api/ncs_clerk'
+import * as shop_API from '@/api/ncs_shop'
+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 { ROLE_TYPE } from '@/utils/enum/RoleTypeEnum'
+let prevOverflow = ''
+
+export default {
+  name: 'CareDoctorManager',
+  components: { ButtonCellRender, AgGridImg, ImageViewer, RadioFilter },
+  props: {
+    partId: {
+      type: Number,
+      default: 0
+    }
+  },
+  data: function() {
+    return {
+      uploadurl: serverUrl + '/ncs/upload/uploadFile',
+      imageUrl: '',
+      /** 列表loading状态 */
+      loading: false,
+      /** 列表参数 */
+      params: {
+        page_size: 10,
+        page_no: 1,
+        sort: 'nc.member_id',
+        dir: 'asc'
+      },
+      /** 列表数据 */
+      tableData: [],
+      pageData: [],
+      /** 添加会员弹出框指示 */
+      dialogAddMemberVisible: false,
+      /** 添加会员 表单数据 */
+      addMemberForm: {},
+      /** 选中行数据 */
+      multipleSelection: [],
+      rolesOptions: [],
+      shopOptions: [],
+      /**
+       * 角色选择,如果是机构负责人无法选择
+       */
+      roleTypeDisabled: false,
+      /**
+       * 机构选择,如果是机构负责人无法选择
+       */
+      shopDisabled: false,
+      /** 添加会员 表单规则 */
+      addMemberRules: {
+        uname: [
+          this.MixinRequired('请输入用户名'),
+          { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
+        ],
+        password: [
+          this.MixinRequired('请输入密码!'),
+          {
+            validator: (rule, value, callback) => {
+              if (!RegExp.password.test(value)) {
+                callback(new Error('密码格式有误,密码只能包含字母数字和!#$%^&*.~,字符,长度为6-20位'))
+              } else {
+                callback()
+              }
+            },
+            trigger: 'blur'
+          }
+        ],
+        nickname: [
+          this.MixinRequired('请输入真实姓名'),
+          { min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
+        ],
+        role_id: [this.MixinRequired('请选择角色')],
+        mobile: [
+          this.MixinRequired('请输入手机号码!'),
+          {
+            validator: (rule, value, callback) => {
+              if (!RegExp.mobile.test(value)) {
+                callback(new Error('手机格式格式有误'))
+              } else {
+                callback()
+              }
+            },
+            trigger: 'blur'
+          }
+        ],
+        address: [
+          { max: 50, message: '最长50个字符', trigger: 'blur' }
+        ],
+        shop_id: [
+          { required: true, message: '请选择机构', trigger: 'blur' }
+        ]
+      },
+      pwdType: 'password',
+      srcList: ['1'],
+      columnDefs: null, // 新表格
+      defaultColDef: null,
+      gridOptions: null,
+      gridApi: null,
+      columnApi: null,
+      localeText: AG_GRID_LOCALE_CN,
+      rowSelection: null,
+      showViewer: false,
+      sexTransfer: [
+        { key: '男', value: 1 },
+        { key: '女', value: 0 }
+      ],
+      roleId: null,
+      roleZzId: null,
+      newCheckList: [],
+      oldCheckList: [],
+      nurseList: [],
+      shopId: null,
+      nurseClerkList: []
+    }
+  },
+  computed: {
+    tableHeight() {
+      return this.mainAreaHeight - 130
+    }
+  },
+  watch: {
+    partId(val, oldvalue) {
+      if (val === null) {
+        this.shopId = this.$store.getters.partId
+      } else {
+        this.shopId = val
+      }
+      this.params.fixedCondition = 'nc.shop_id=' + this.shopId
+      this.GET_MemberList()
+    }
+  },
+  beforeMount() {
+    this.gridOptions = {
+    }
+    this.columnDefs = [
+      {
+        headerName: '#',
+        headerCheckboxSelection: true,
+        headerCheckboxSelectionFilteredOnly: true,
+        checkboxSelection: true,
+        sortable: false, filter: false,
+        width: 50,
+        resizable: false,
+        valueGetter: this.hashValueGetter
+      },
+      { headerName: '头像', field: 'face', sortable: true, filter: false, width: 70,
+        cellRendererFramework: 'AgGridImg',
+        cellRendererParams: param => {
+          return {
+            onClick: this.lookBigImg,
+            face: 'face'
+          }
+        }
+      },
+      { headerName: '登录名', field: 'uname', sortable: true, filter: true, minWidth: 170 },
+      { headerName: '真实名字', field: 'clerk_name', sortable: true, filter: true, minWidth: 120 },
+      { headerName: '性别', field: 'sex', sortable: true, valueFormatter: this.formatterSex, width: 100, filterFramework: 'RadioFilter',
+        filterParams: {
+          listData: this.sexTransfer
+        }
+      },
+      { headerName: '手机', field: 'mobile', sortable: true, filter: true, minWidth: 170 },
+      { headerName: '角色', field: 'role_name', sortable: true, filter: false, valueFormatter: this.formatterRole, width: 120 },
+      { headerName: '操作', field: 'id',
+        cellRendererFramework: 'ButtonCellRender',
+        cellRendererParams: param => {
+          return {
+            onClick: this.handlerEdit,
+            label: '编辑',
+            buttonType: 'primary',
+            buttonSize: 'mini'
+          }
+        },
+        filter: false,
+        pinned: 'right',
+        lockPinned: true,
+        width: 100,
+        resizable: false,
+        sortable: false
+      }
+    ]
+    this.defaultColDef = {
+      filter: 'agTextColumnFilter',
+      sortable: true,
+      resizable: true,
+      comparator: this.testComparator,
+      filterParams: {
+        debounceMs: 200,
+        newRowsAction: 'keep',
+        textCustomComparator: this.textCustomComparator,
+        comparator: this.testComparator
+      }
+    }
+    this.rowSelection = 'multiple'
+  },
+  mounted() {
+    this.gridApi = this.gridOptions.api
+    this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
+    if (this.partId === 0) {
+      this.shopId = this.$store.getters.partId
+    } else {
+      this.shopId = this.partId
+    }
+    this.params.fixedCondition = 'ns.shop_id = ' + this.shopId + ' or ns.parent_id = ' + this.shopId
+    this.GET_MemberList()
+    this.getShopOptions(this.shopId)
+  },
+  activated() {
+    this.GET_MemberList()
+  },
+  methods: {
+    /** 选择行变化时,记录选中的行数据 */
+    selectFun(val) {
+      this.multipleSelection = val
+    },
+    /** 加载用户列表 */
+    GET_MemberList() {
+      this.loading = true
+      this.gridApi.showLoadingOverlay()
+      this.gridApi.sizeColumnsToFit()
+      clerk_API.getClerkList(this.params).then(response => {
+        this.loading = false
+        const size = 0
+        // response.data.forEach((item, index) => {
+        //   if (item.founder === 1) {
+        //     delete response.data[index]
+        //     size++
+        //   }
+        // })
+        this.tableData = response.data
+        this.pageData = {
+          page_no: response.page_no,
+          page_size: response.page_size,
+          data_total: response.data_total - size
+        }
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    /** 处理搜索 **/
+    handlerSearch(keywords) {
+      this.params.query = keywords
+      this.GET_MemberList()
+    },
+    /** 单条数据删除处理 */
+    handlerDelete(ids) {
+      this.$confirm('你确定要禁用此用户?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        clerk_API.remove(ids).then(
+          response => {
+            this.GET_MemberList()
+            this.$message({
+              type: 'success',
+              message: '已禁用!'
+            })
+          }
+        ).catch(response => {
+          this.$message({
+            type: 'info',
+            message: response.message
+          })
+        })
+      }).catch((response) => {
+        this.$message({
+          type: 'info',
+          message: '已取消禁用'
+        })
+      })
+    },
+    getRoles(param) {
+      // 获取角色
+      clerk_API.getRoles(param).then(res => {
+        this.rolesOptions = res.data
+        const nurseZz = res.data.find(item => item.role_name === '护士组长')
+        if (nurseZz) {
+          this.roleZzId = nurseZz.role_id
+          const nurse = res.data.find(item => item.role_name === '护士')
+          if (nurse) {
+            this.roleId = nurse.role_id
+          }
+        }
+      })
+    },
+    /** 批量数据删除处理(删除选中的行) */
+    batchDelete: function() {
+      if (this.multipleSelection.length === 0) {
+        this.$alert('没有选择任何记录!', '系统提示', {
+          confirmButtonText: '确定',
+          callback: action => {
+          }
+        })
+      } else {
+        const ids = []
+        this.multipleSelection.forEach(function(item) {
+          ids.push(item.member_id)
+        })
+        this.handlerDelete(ids.join(','))
+      }
+    },
+    /** 性别格式化 */
+    formatterSex(row) {
+      return row.data.sex === 1 ? '男' : row.data.sex === 0 ? '女' : '未知'
+    },
+    formatterRole(row) {
+      if (row.data.founder === 1) {
+        return '机构负责人'
+      } else {
+        return row.data.role_name
+      }
+    },
+    /** 提交添加会员表单 */
+    submitAddMemberForm(formName) {
+      const _this = this
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          const params = this.MixinClone(this.addMemberForm)
+          if (isNaN(params.birthday)) {
+            params.birthday = null
+          } else {
+            params.birthday = parseInt(params.birthday / 1000)
+          }
+          if (this.imageUrl) {
+            params.face = this.imageUrl
+          }
+          if (params.member_id) {
+            if (this.roleZzId === params.role_id) {
+              const addIds = this.newCheckList.filter(function(val) { return _this.oldCheckList.indexOf(val) === -1 })
+              console.log('addIds=', addIds)
+              const delIds = this.oldCheckList.filter(function(val) { return _this.newCheckList.indexOf(val) === -1 })
+              console.log('delIds=', delIds)
+              const data = {
+                clerkId: params.clerk_id,
+                addIds: addIds.join(','),
+                delIds: delIds.join(',')
+              }
+              console.log('data==', data)
+              clerk_API.updateParentById(data)
+            }
+            clerk_API.update(params.clerk_id, params).then(response => {
+              this.dialogAddMemberVisible = false
+              this.imageUrl = null
+              this.GET_MemberList()
+            })
+          } else {
+            clerk_API.add(params).then(response => {
+              this.dialogAddMemberVisible = false
+              this.imageUrl = null
+              this.GET_MemberList()
+            })
+          }
+        } else {
+          this.$message.error('表单填写有误,请检查!')
+          return false
+        }
+      })
+    },
+    /** 添加会员 */
+    handleAddMember() {
+      this.nurseList = []
+      this.imageUrl = null
+      this.addMemberForm = { sex: 1 }
+      this.addMemberRules.password[0].required = true
+      this.dialogAddMemberVisible = true
+      this.roleTypeDisabled = false
+      this.shopDisabled = false
+    },
+    /** 编辑用户 */
+    handlerEdit(row) {
+      this.addMemberForm = Object.assign({}, row)
+      this.nurseList = []
+      if (this.roleZzId && this.roleZzId === row.role_id) { // 只有护士组长才能编辑小组成员
+        // this.getNurseByRoleId(row.member_id)
+        this.getNurseByRoleId(row.member_id, this.addMemberForm.shop_id)
+        this.shopDisabled = false
+      } else if (row.role_id === ROLE_TYPE.PRINCIPAL) {
+        this.roleTypeDisabled = true
+        this.shopDisabled = true
+      } else {
+        this.roleTypeDisabled = false
+        this.shopDisabled = false
+      }
+      this.imageUrl = row.face
+      delete this.addMemberForm.password
+      this.addMemberRules.password[0].required = false
+      if (row.birthday) {
+        this.addMemberForm.birthday *= 1000
+      }
+      this.dialogAddMemberVisible = true
+    },
+    // getNurseByRoleId(id) {
+    getNurseByRoleId(id, shop_id) {
+      const _this = this
+      this.newCheckList = []
+      this.oldCheckList = []
+
+      // clerk_API.getNurseByRoleId(_this.roleId, this.shopId).then(r => {
+      clerk_API.getNurseByRoleId(_this.roleId, shop_id).then(r => {
+        _this.nurseList = r
+        // _this.getNurseIds(id)
+        _this.getNurseIds(id, shop_id)
+        // r.forEach(item => {
+        //   if (item.parent_id=== id) {
+        //     _this.newCheckList.push(item.clerk_id)
+        //     _this.oldCheckList.push(item.clerk_id)
+        //   }
+        // })
+      })
+    },
+    // getNurseIds(id) {
+    getNurseIds(id, shop_id) {
+      const _this = this
+      // clerk_API.getNurseIdsByPartId(this.shopId).then(r => {
+      clerk_API.getNurseIdsByPartId(shop_id).then(r => {
+        r.forEach(item => {
+          _this.nurseList.forEach(t => {
+            if (t.clerk_id === item.nurse_id) {
+              t.group_name = item.clerk_name
+              t.group_id = item.member_id
+              if (item.member_id === id) {
+                t.checked = true
+                _this.oldCheckList.push(item.nurse_id)
+                _this.newCheckList.push(item.nurse_id)
+              }
+            }
+          })
+        })
+        _this.nurseClerkList = _this.nurseList
+      })
+    },
+    handleCheckboxChanged(item) {
+      item.checked = !item.checked
+    },
+    /** 分页大小发生改变 */
+    handlePageSizeChange(size) {
+      this.params.page_size = size
+      this.GET_MemberList()
+    },
+
+    /** 分页页数发生改变 */
+    handlePageCurrentChange(page) {
+      this.params.page_no = page
+      this.GET_MemberList()
+    },
+    /** 图片上传之前的校验 */
+    handleShopLogoBefore(file) {
+      return new Promise((resolve, reject) => {
+        const isImg = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif'
+        const isLt2M = file.size / 1024 / 1024 < 2
+
+        if (!isImg) {
+          this.$message.error('上传头像图片只能是 JPG、PNG、GIF 格式!')
+          reject()
+        }
+        if (!isLt2M) {
+          this.$message.error('上传头像图片大小不能超过 2MB!')
+          reject()
+        }
+
+        const reader = new FileReader()
+        reader.onload = (event) => {
+          const image = new Image()
+          image.onload = () => {
+            const width = image.width
+            const height = image.height
+            if (width > 500 || width < 100) {
+              this.$message.error('图片宽度必须在100~500之间,宽高比为1:1!')
+              reject()
+            }
+            if (width !== height) {
+              this.$message.error('请上传宽高比为1:1的图片')
+              reject()
+            }
+            if (height > 500 || height < 100) {
+              this.$message.error('图片高度必须在100~500之间!')
+              reject()
+            }
+            resolve()
+          }
+          image.src = event.target.result
+        }
+        reader.readAsDataURL(file)
+      })
+    },
+
+    /** 上传成功后的钩子 更换图片 置空存储数组*/
+    uploaded(res) {
+      this.imageUrl = serverUrl + '/' + res
+      this.addMemberForm.face = this.imageUrl
+    },
+    lookBigImg(row) {
+      this.srcList = []
+      this.srcList.push(row.face)
+      prevOverflow = document.body.style.overflow
+      document.body.style.overflow = 'hidden'
+      this.showViewer = true
+    },
+    closeViewer() {
+      document.body.style.overflow = prevOverflow
+      this.showViewer = false
+    },
+    filterModifed(param) {
+      const model = param.api.getFilterModel()
+      this.params.filter = JSON.stringify(model)
+      this.GET_MemberList()
+    },
+    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.GET_MemberList()
+    },
+    getShopOptions(part_id) {
+      shop_API.getShopList(part_id).then(res => {
+        this.shopOptions = res
+      })
+    }
+
+  }
+
+}
+</script>
+
+<style type="text/scss" scoped>
+
+  .show-pwd {
+    position: absolute;
+    top: 0;
+    right: 10px;
+    cursor: pointer;
+  }
+
+  .formwrap /deep/ .el-tabs__content {
+    overflow: visible !important;
+  }
+
+  .formwrap /deep/ .app-address-title {
+    height: 32px !important;
+    line-height: 32px !important;
+  }
+
+  .formwrap /deep/ .app-address {
+    width: 100% !important;
+  }
+
+  /deep/ div.toolbar {
+    height: 70px;
+    padding: 20px 0;
+  }
+
+  /deep/ .el-date-editor {
+    width: 100%;
+  }
+
+  /deep/ .el-table {
+    width: 100%;
+    overflow-x: scroll;
+
+    & td:not(.is-left) {
+      text-align: center;
+    }
+  }
+
+  .el-tag + .el-tag {
+    margin-left: 10px;
+    margin-bottom: 10px;
+  }
+
+  /deep/ .avatar-uploader .el-upload {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+  }
+
+  /deep/ .avatar-uploader .el-upload:hover {
+    border-color: #409EFF;
+  }
+
+  /deep/ .avatar-uploader-icon {
+    font-size: 28px;
+    color: #8c939d;
+    width: 110px;
+    height: 110px;
+    line-height: 110px;
+    text-align: center;
+  }
+
+  /deep/ .avatar {
+    width: 110px;
+    height: 110px;
+    display: block;
+  }
+
+</style>

+ 22 - 0
src/views/hospital/customerManagement.vue

@@ -0,0 +1,22 @@
+<template>
+  <div>
+    <customer-manager :frame="frame" />
+  </div>
+</template>
+
+<script>
+import CustomerManager from './ncs_customer/customerManager'
+export default {
+  name: 'CustomerManagement',
+  components: { CustomerManager },
+  data() {
+    return {
+      frame: {}
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 22 - 0
src/views/hospital/deviceManagement.vue

@@ -0,0 +1,22 @@
+<template>
+  <div>
+    <device-manager :frame="frame" />
+  </div>
+</template>
+
+<script>
+import DeviceManager from './ncs_device/deviceManager'
+export default {
+  name: 'DeviceManagement',
+  components: { DeviceManager },
+  data() {
+    return {
+      frame: {}
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 130 - 35
src/views/hospital/ncs_customer/customerManager.vue

@@ -138,6 +138,13 @@
                         <el-input v-model="formmodel.nickname" clearable placeholder="请输入用户其他称呼" :maxlength="20" />
                       </el-form-item>
                     </el-col>
+                    <el-col :span="12">
+                      <el-form-item label="入住床位" prop="frame_id">
+                        <el-select v-model="formmodel.frame_id" :disabled="bedSelectabled" placeholder="请选择床位">
+                          <el-option v-for="(item,index) in emptyBeds" :key="index" :label="item.full_name" :value="item.id" />
+                        </el-select>
+                      </el-form-item>
+                    </el-col>
                   </el-row>
                   <el-form-item label="用户情况简述">
                     <el-input
@@ -351,14 +358,57 @@
     <!-- 用户换床-->
     <el-dialog :visible.sync="changeBedFormVisible" title="用户换床">
       <el-form ref="changeBedForm" :model="changeBedFormModel" :rules="changeBedRules" label-width="120px">
-        <el-form-item label="当前病床:"><svg-icon icon-class="bed" style="color: #9aaabf;margin-top: 5px" />{{ changeBedFormModel.current_bed }}</el-form-item>
-        <el-form-item label="可换病床:">
-          <el-radio-group v-model="changeBedFormModel.frame_id">
-            <el-radio v-for="(item, index) in emptyBeds" :key="index" :label="item.id">
-              <svg-icon icon-class="bed" style="color: #9aaabf;margin-top: 5px" />{{ item.full_name }}
-            </el-radio>
-          </el-radio-group>
-        </el-form-item>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="用户姓名" prop="named">
+              <el-input v-model="changeBedFormModel.named" clearable placeholder="请输入姓名" readonly :maxlength="20" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="入住编号">
+              <el-input v-model="changeBedFormModel.card_no" clearable placeholder="请输入病人编号" readonly :maxlength="20" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="性别" class="form-item-sex">
+              <el-radio v-model="changeBedFormModel.sex" :label="0" disabled>女</el-radio>
+              <el-radio v-model="changeBedFormModel.sex" :label="1" disabled>男</el-radio>
+              <el-radio v-model="changeBedFormModel.sex" :label="2" disabled>未知</el-radio>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="年龄" prop="age">
+              <el-input v-model="changeBedFormModel.age" clearable :maxlength="4" readonly placeholder="请输入年龄">
+                <el-select slot="append" v-model="changeBedFormModel.age_unit" disabled placeholder="请选择年龄单位">
+                  <el-option label="岁" value="岁" />
+                  <el-option label="月" value="月" />
+                  <el-option label="天" value="天" />
+                </el-select>
+              </el-input>
+            </el-form-item>
+          </el-col>
+
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="当前床位" prop="nickname">
+              <el-input v-model="changeBedFormModel.current_bed" clearable readonly :maxlength="20" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="换到床位" prop="frame_id">
+              <el-select v-model="changeBedFormModel.frame_id" clearable filterable placeholder="请选择床位">
+                <el-option v-for="(item,index) in emptyBeds" :key="index" :label="item.full_name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+        </el-row>
+
         <el-form-item>
           <el-button type="primary" class="save" @click="handleChangeBedSubmit('changeBedForm')">确定</el-button>
         </el-form-item>
@@ -382,6 +432,8 @@ import { serverUrl } from '@/utils/domain'
 import * as API_Remark from '@/api/ncs_remark'
 import * as API_User from '@/api/user'
 import * as API_hospitalFrame from '@/api/ncs_hospitalFrame'
+import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
+import * as API_Frame from '@/api/ncs_hospitalFrame'
 export default {
   name: 'CustomerManager',
   components: { ButtonCellRender, ListFilter, RadioFilter },
@@ -489,7 +541,7 @@ export default {
       /** 空床位 **/
       emptyBeds: [],
       /** frame 是否为空床位 */
-      isEmptyFrame: false,
+      isEmptyFrame: true,
       rules: {
         named: [
           this.MixinRequired('请输入用户姓名!')
@@ -536,11 +588,11 @@ export default {
   },
   watch: {
     frame(val, oldvalue) {
-      if (val.type === 1) { // 医院
+      if (val.type === FRAME_TYPE.HOSPITAL) { // 医院
         this.params.fixedCondition = '1=1'
-      } else if (val.type === 3) { // 科室
+      } else if (val.type === FRAME_TYPE.PART) { // 科室
         this.params.fixedCondition = ' part_id =' + this.frame.part_id
-      } else if (val.type === 4) { // 房间
+      } else if (val.type === FRAME_TYPE.ROOM) { // 房间
         const ids = []
         this.frame.children.forEach(item => {
           ids.push(item.id)
@@ -554,6 +606,7 @@ export default {
         this.params.fixedCondition = ' frame_id = ' + this.frame.id
       }
       this.getList()
+      this.infoChanged()
     }
   },
   beforeMount() {
@@ -570,7 +623,10 @@ export default {
         resizable: false,
         valueGetter: this.hashValueGetter
       },
+      // { headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 100 },
       { headerName: '用户姓名', field: 'named', sortable: true, filter: 'agTextColumnFilter', width: 160 },
+      { headerName: '昵称', field: 'nickname', sortable: true, filter: 'agTextColumnFilter', width: 130 },
+      { headerName: '入住床位', field: 'full_name', sortable: true, filter: 'agTextColumnFilter' },
       { headerName: '当前状态', field: 'status', sortable: true, filterFramework: 'RadioFilter', width: 130,
         filterParams: {
           listData: this.customerStatusTransfer
@@ -584,27 +640,35 @@ export default {
           listData: this.sexTransfer
         },
         cellRenderer: this.sexRenderer },
-      { headerName: '入住床位', field: 'full_name', sortable: true, filter: 'agTextColumnFilter', valueFormatter: this.fullNameFormatter },
-      { headerName: '年龄', field: 'age', sortable: true, filter: 'agNumberColumnFilter', valueFormatter: this.formatterAge, width: 130 },
-      { headerName: '手机号码', field: 'mobile', sortable: true, filter: 'agTextColumnFilter' },
-      { headerName: '住院时间', field: 'in_date', valueFormatter: this.formatterDate, sortable: true, minWidth: 220, filter: 'agDateColumnFilter',
-        filterParams: {
-          comparator: (filterLocalDateAtMidnight, cellValue) => { // 所有数据都由服务器端过滤,此处只需返回0 即可
-            const celldate = unixToDate(cellValue, 'yyyy-MM-dd 00:00:00')
-            return (new Date(celldate).getTime() / 1000) - (filterLocalDateAtMidnight.getTime() / 1000)
-          }
+
+      { headerName: '生日', field: 'birthday', sortable: true, filter: 'agDateColumnFilter', valueFormatter: this.unixDateFormatter, filterParams: {
+        comparator: (filterLocalDateAtMidnight, cellValue) => { // 所有数据都由服务器端过滤,此处只需返回0 即可
+          const celldate = unixToDate(cellValue, 'yyyy-MM-dd 00:00:00')
+          return (new Date(celldate).getTime() / 1000) - (filterLocalDateAtMidnight.getTime() / 1000)
         }
+      }},
+      { headerName: '年龄', field: 'age', sortable: true, filter: 'agNumberColumnFilter', width: 130
+      },
+      { headerName: '年龄单位', field: 'age_unit', sortable: true, filter: 'agTextColumnFilter', width: 130
       },
       { headerName: '入住编号', field: 'card_no', sortable: true, filter: 'agTextColumnFilter' },
       { headerName: '身份证件类型', field: 'id_type', sortable: true, filter: 'agTextColumnFilter' },
       { headerName: '证件号码', field: 'id_no', sortable: true, filter: 'agNumberColumnFilter', width: 160 },
-      { headerName: '生日', field: 'birthday', sortable: true, filter: 'agDateColumnFilter', valueFormatter: this.unixDateFormatter, filterParams: {
+      { headerName: '手机号码', field: 'mobile', sortable: true, filter: 'agTextColumnFilter' },
+      { headerName: '家庭住址', field: 'address', sortable: true, filter: 'agTextColumnFilter', width: 260 },
+
+      { headerName: '入住时间', field: 'in_date', sortable: true, filter: 'agDateColumnFilter', valueFormatter: this.unixDateFormatter, filterParams: {
+        comparator: (filterLocalDateAtMidnight, cellValue) => { // 所有数据都由服务器端过滤,此处只需返回0 即可
+          const celldate = unixToDate(cellValue, 'yyyy-MM-dd 00:00:00')
+          return (new Date(celldate).getTime() / 1000) - (filterLocalDateAtMidnight.getTime() / 1000)
+        }
+      }},
+      { headerName: '离开时间', field: 'out_date', sortable: true, filter: 'agDateColumnFilter', valueFormatter: this.unixDateFormatter, filterParams: {
         comparator: (filterLocalDateAtMidnight, cellValue) => { // 所有数据都由服务器端过滤,此处只需返回0 即可
           const celldate = unixToDate(cellValue, 'yyyy-MM-dd 00:00:00')
           return (new Date(celldate).getTime() / 1000) - (filterLocalDateAtMidnight.getTime() / 1000)
         }
       }},
-      { headerName: '家庭住址', field: 'address', sortable: true, filter: 'agTextColumnFilter', width: 260 },
       { headerName: '编辑', field: 'id',
         cellRendererFramework: 'ButtonCellRender',
         cellRendererParams: {
@@ -661,7 +725,7 @@ export default {
             label: '删除',
             buttonType: 'danger',
             buttonSize: 'mini',
-            disabled: param.data['member_name'] === 'superadmin' || param.data['status'] === 0
+            disabled: param.data['member_name'] === 'superadmin'
           }
         },
         pinned: 'right',
@@ -696,11 +760,23 @@ export default {
         }
       ]
     })
+    this.getEmptyBeds()
   },
   methods: {
     windowResize() {
       this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
     },
+    getEmptyBeds() {
+      return new Promise((resolve, reject) => {
+        API_Frame.getSickbedByPartId(this.$store.getters.partId).then(res => {
+          this.emptyBeds = [...res]
+          resolve()
+        }).catch(err => {
+          this.$message.error('获取空床位:' + err)
+          reject()
+        })
+      })
+    },
     /** 分页大小发生改变 */
     handlePageSizeChange(size) {
       this.params.page_size = size
@@ -723,15 +799,15 @@ export default {
           page_size: response.page_size,
           data_total: response.data_total
         }
-        if (this.frame.type === 5) { // 空间机构为床位
-          if (this.rowData.length > 0) {
-            this.isEmptyFrame = this.rowData.filter(p => p.status === 0).length === 0
-          } else {
-            this.isEmptyFrame = true
-          }
-        } else {
-          this.isEmptyFrame = false
-        }
+        // if (this.frame.type === FRAME_TYPE.BED) { // 空间机构为床位
+        //   if (this.rowData.length > 0) {
+        //     this.isEmptyFrame = this.rowData.filter(p => p.status === 0).length === 0
+        //   } else {
+        //     this.isEmptyFrame = true
+        //   }
+        // } else {
+        //   this.isEmptyFrame = false
+        // }
       }).catch(err => {
         this.$message.error(err)
       })
@@ -991,6 +1067,8 @@ export default {
       this.formmodel = {
         ...row
       }
+      // 编辑时把当前用户床位加入到空床位,否则会显示一个床位Id号
+      this.emptyBeds.push({ id: this.formmodel.frame_id, full_name: this.formmodel.full_name })
       if (this.formmodel.in_date) {
         this.$set(this.formmodel, 'in_date', this.formmodel.in_date * 1000)
       }
@@ -1017,18 +1095,24 @@ export default {
           this.$emit('saved')
           this.getList()
         })
+      }).catch(() => {
+        this.$message.info('取消操作!')
       })
     },
     /** 换床 **/
     handleChangeBed(row) {
       this.changeBedFormVisible = true
       this.changeBedFormModel = {
-        id: row.id,
+        ...row,
+        // id: row.id,
         current_bed: row.full_name,
         frame_id: null,
         status: row.status
       }
-      API_hospitalFrame.getSickbedByPartId(row.part_id).then(res => {
+      /** 只替换到当前科室 */
+      // API_hospitalFrame.getSickbedByPartId(row.part_id).then(res => {
+      /** 替换到所有科室下 */
+      API_hospitalFrame.getSickbedByPartId(this.$store.getters.partId).then(res => {
         this.emptyBeds = res
       })
     },
@@ -1162,6 +1246,17 @@ export default {
       } else {
         return '未知'
       }
+    },
+    /** 信息变化后操作,如床位状态改变,传入的空间结构变化了 **/
+    infoChanged() {
+      this.getEmptyBeds().then(() => {
+        // 传入结构是否为空床位
+        if (Object.keys(this.frame).length > 0) {
+          this.isEmptyFrame = this.emptyBeds.filter(p => p.id === this.frame.id).length > 0
+        }
+      }).catch(() => {
+
+      })
     }
   }
 }

+ 123 - 10
src/views/hospital/ncs_device/deviceManager.vue

@@ -114,9 +114,16 @@
           </el-col>
         </el-row>
         <el-row>
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="设备位置">-->
+<!--              <span>{{ deviceModel.full_name }}</span>-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
           <el-col :span="12">
-            <el-form-item label="设备位置">
-              <span>{{ deviceModel.full_name }}</span>
+            <el-form-item label="设备位置" prop="frame_id">
+              <el-select v-model="deviceModel.frame_id" filterable :disabled="frameSelectabled" placeholder="请选择安装位置">
+                <el-option v-for="(item,index) in typeFrames" :key="index" :label="item.full_name" :value="item.id" />
+              </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -149,6 +156,22 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row v-if="hasAudioId">
+          <el-col :span="12">
+            <el-form-item label="上属总线转换盒" prop="trans_audio_id">
+              <el-select v-model="deviceModel.trans_audio_id" placeholder="上属总线转换盒" clearable>
+                <el-option v-for="item in audioOptions" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="上属485转换盒" prop="trans_rs485_id">
+              <el-select v-model="deviceModel.trans_rs485_id" placeholder="上属485转换盒" clearable>
+                <el-option v-for="item in rs485Options" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="handlerFormSubmit('deviceEditForm')">确 定</el-button>
@@ -166,6 +189,10 @@ import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
 import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
 import * as API_Device from '@/api/ncs_device'
 import * as clerk_API from '@/api/ncs_clerk'
+import { DEVICE_TYPE } from '@/utils/enum/DeviceTypeEnum'
+import * as API_Frame from '@/api/ncs_hospitalFrame'
+import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
+import * as shop_API from '@/api/ncs_shop'
 export default {
   name: 'DeviceManager',
   components: { ButtonCellRender, ListFilter, RadioFilter },
@@ -225,8 +252,13 @@ export default {
         ],
         role_id: [
           { required: true, message: '请选择适用人', trigger: 'blur' }
+        ],
+        trans_audio_id: [
+          { required: true, message: '请选择总线转换盒', trigger: 'blur' }
         ]
       },
+      partFrames: [],
+      typeFrames: [],
       /** 设备类型转换数组 **/
       deviceTypeTransfer: [
         { key: '护士主机', value: 1 },
@@ -245,7 +277,9 @@ export default {
         { key: '模拟门灯', value: 14 },
         { key: '遥控器', value: 15 },
         { key: '信标', value: 16 },
-        { key: '看板', value: 17 }
+        { key: '看板', value: 17 },
+        { key: '485转换盒', value: 20 },
+        { key: '紧急按钮', value: 21 }
       ],
       deviceStatusTransfer: [
         { key: '启用', value: 1, color: 'green' },
@@ -253,6 +287,9 @@ export default {
       ],
       rolesOptions: [],
       hasRoleId: false,
+      audioOptions: [],
+      rs485Options: [],
+      hasAudioId: false,
       hasAdd: true
     }
   },
@@ -380,6 +417,7 @@ export default {
         }
       ]
     })
+    this.getTypeFrame(this.$store.getters.partId)
   },
   methods: {
     /** 加载列表数据 */
@@ -396,8 +434,21 @@ export default {
       }).catch(err => {
         this.$message.error(err)
       })
+      this.getTypeFrame(this.$store.getters.partId)
+      this.getShopOptions(this.$store.getters.partId)
+    },
+    getTypeFrame(hospital_id) {
+      // API_Frame.getFramesPartId(partId).then(res => {
+      API_Frame.getFramesHospital(hospital_id).then(res => {
+        this.partFrames = [...res]
+      })
+    },
+    /** 将partFrames做分割 */
+    getFramesByType(device_type) {
+      if (this.partFrames != null) {
+        this.typeFrames = this.partFrames.filter(item => item.type === device_type)
+      }
     },
-
     /**
      * 过滤状态发生变化,发送到服务器检索数据
      */
@@ -466,7 +517,7 @@ export default {
       }
     },
     fullNameFormatter(params) {
-      if (params.data.type === 3) {
+      if (params.data.type === FRAME_TYPE.PART) {
         return params.value
       } else {
         return params.data.shop_name + '-' + params.value
@@ -502,8 +553,50 @@ export default {
     },
     /** 设备类型选中变化  **/
     deviceTypeChange(val) {
-      this.deviceRules.eth_mac[0].required = !(val === 12 || val === 13 || val === 14)
-      this.hasRoleId = val === 7
+      this.deviceTypeChangeToFrameTypeChange(val)
+      if (val === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+          val === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+          val === DEVICE_TYPE.SIMULATE_DOOR_LIGHT ||
+          val === DEVICE_TYPE.EMERGENCY_BUTTON) { // 模拟设备不需要mac地址
+        this.deviceRules.eth_mac[0].required = false
+        this.deviceRules.eth_mac[1].pattern = null
+      } else if (val === DEVICE_TYPE.REMOTE_CONTROL) {
+        this.deviceRules.eth_mac[1].pattern = null
+        this.deviceRules.eth_mac[0].required = true
+      } else {
+        this.deviceRules.eth_mac[1].pattern = /^([0-9A-Fa-f]{2}:?){6}/gi
+        this.deviceRules.eth_mac[0].required = true
+      }
+      this.hasRoleId = val === DEVICE_TYPE.NURSE_WATCH
+      this.hasAudioId = val === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+          val === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+          val === DEVICE_TYPE.SIMULATE_DOOR_LIGHT
+    },
+    /** 设备类型选中对应的空间结构  **/
+    deviceTypeChangeToFrameTypeChange(val) {
+      if (val === DEVICE_TYPE.NURSE_HOST ||
+          val === DEVICE_TYPE.DOCTOR_HOST ||
+          val === DEVICE_TYPE.LED_SCREEN ||
+          val === DEVICE_TYPE.LCD_SCREEN ||
+          val === DEVICE_TYPE.NURSE_WATCH ||
+          val === DEVICE_TYPE.WORKER_WATCH ||
+          val === DEVICE_TYPE.TRANSFER_DEVICE ||
+          val === DEVICE_TYPE.INFORMATION_BOARD ||
+          val === DEVICE_TYPE.RS485_TRANSFER) {
+        this.getFramesByType(FRAME_TYPE.PART)
+      } else if (val === DEVICE_TYPE.DOOR_DEVICE ||
+          val === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+          val === DEVICE_TYPE.SIMULATE_DOOR_LIGHT ||
+          val === DEVICE_TYPE.BEACON) {
+        this.getFramesByType(FRAME_TYPE.ROOM)
+      } else if (val === DEVICE_TYPE.DIGIT_BED_DEVICE ||
+          val === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+          val === DEVICE_TYPE.REMOTE_CONTROL ||
+          val === DEVICE_TYPE.CELL_PHONE) {
+        this.getFramesByType(FRAME_TYPE.BED)
+      } else {
+        this.typeFrames = this.partFrames
+      }
     },
     /** 添加设备事件 **/
     handleAdd() {
@@ -519,16 +612,23 @@ export default {
       delete this.deviceModel.id
       this.hasRoleId = false
       this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
+      this.hasAudioId = false
+      this.getDevices(this.$store.getters.partId)
       this.deviceEditTitle = '添加设备'
       this.deviceDialogVisible = true
       this.deviceTypeDisabled = false // 新增设备可以选择设备类型
       this.deviceModel.full_name = this.frame.full_name
+      this.typeFrames = this.partFrames
     },
     /** 修改设备  **/
     handleEdit(params) {
       this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
-      console.log('param', params)
-      this.hasRoleId = params.device_type === 7
+      this.getDevices(this.$store.getters.partId)
+      this.deviceTypeChangeToFrameTypeChange(params.device_type)
+      this.hasRoleId = params.device_type === DEVICE_TYPE.NURSE_WATCH
+      this.hasAudioId = params.device_type === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+          params.device_type === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+          params.device_type === DEVICE_TYPE.SIMULATE_DOOR_LIGHT
       this.deviceModel = {
         ...params
       }
@@ -580,8 +680,21 @@ export default {
       clerk_API.getRoles(param).then(response => {
         this.rolesOptions = response.data
       })
+    },
+    getDevices(partId) {
+      // 获取设备列表
+      API_Device.getDeviceByType(partId, DEVICE_TYPE.TRANSFER_DEVICE).then(response => {
+        this.audioOptions = response
+      })
+      API_Device.getDeviceByType(partId, DEVICE_TYPE.RS485_TRANSFER).then(response => {
+        this.rs485Options = response
+      })
+    },
+    getShopOptions(part_id) {
+      shop_API.getShopList(part_id).then(res => {
+        this.shopOptions = res
+      })
     }
-
   }
 }
 </script>

+ 60 - 29
src/views/hospital/ncs_frame/frameTreeView.vue

@@ -8,13 +8,13 @@
             placeholder="输入关键字进行过滤"
             clearable
           />
-<!--          <el-button-->
-<!--            type="text"-->
-<!--            size="mini"-->
-<!--            style="margin-left: 10px;color: #67C23A"-->
-<!--            @click="quickCreateFrame"-->
-<!--          >快速创建-->
-<!--          </el-button>-->
+          <!--          <el-button-->
+          <!--            type="text"-->
+          <!--            size="mini"-->
+          <!--            style="margin-left: 10px;color: #67C23A"-->
+          <!--            @click="quickCreateFrame"-->
+          <!--          >快速创建-->
+          <!--          </el-button>-->
 
         </div>
         <el-tree
@@ -133,9 +133,9 @@
               <device-manager :frame="selectedNode" />
             </keep-alive>
           </el-tab-pane>
-          <el-tab-pane label="机构成员" name="clerkList">
+          <el-tab-pane label="机构成员" name="clerkManager">
             <keep-alive>
-              <clerk-list :part-id="selectedNode.part_id" />
+              <clerk-manager :part-id="selectedNode.part_id" />
             </keep-alive>
           </el-tab-pane>
         </el-tabs>
@@ -149,7 +149,7 @@
           <el-col :span="24">
             <!--医院结构名称-->
             <el-form-item label="名称" prop="name">
-              <el-input v-model="frameInfo.name" :maxlength="20">
+              <el-input v-model="frameInfo.name" :maxlength="20" @change="frameChange">
                 <template slot="append">{{ frameInfo.type === 4?"房":"床" }}</template>
               </el-input>
             </el-form-item>
@@ -158,6 +158,14 @@
         <el-row>
           <el-col :span="24">
             <!--医院结构别名-->
+            <el-form-item label="全称" prop="full_name">
+              <el-input v-model="frameInfo.full_name" :maxlength="20" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <!--医院结构别名-->
             <el-form-item label="别名" prop="alias">
               <el-input v-model="frameInfo.alias" :maxlength="20" />
             </el-form-item>
@@ -288,13 +296,15 @@ import * as HospitalFrame_API from '@/api/ncs_hospitalFrame'
 import * as API_FrameGroup from '@/api/ncs_frameGroup'
 import CustomerManager from '@/views/hospital/ncs_customer/customerManager'
 import DeviceManager from '@/views/hospital/ncs_device/deviceManager'
-import ClerkList from '@/views/ncs-clerk/clerkList'
+// import ClerkList from '@/views/ncs-clerk/clerkList
+import ClerkManager from '@/views/hospital/clerkManager'
 import CountTo from 'vue-count-to'
 import * as API_PartInfo from '@/api/ncs_partInfo'
+import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
 
 export default {
   name: 'FrameTreeView',
-  components: { DeviceManager, CustomerManager, CountTo, ClerkList },
+  components: { DeviceManager, CustomerManager, CountTo, ClerkManager },
   data() {
     return {
       treeData: [],
@@ -318,6 +328,10 @@ export default {
         alias: [
           { min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
         ],
+        full_name: [
+          this.MixinRequired('请输入全称!'),
+          { min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
+        ],
         shop_name: [
           { required: true, message: '组织名称必须选择', trigger: 'blur' }
         ],
@@ -347,7 +361,8 @@ export default {
       /** 新建组织弹出参数 **/
       formtitle: '新建组织',
       formshow: false,
-      formmodel: {}
+      formmodel: {},
+      isShop: true
     }
   },
   computed: {
@@ -381,7 +396,7 @@ export default {
      * */
     getFrameTree() {
       return new Promise((resolve, reject) => {
-        API_FrameGroup.getframestruct(this.$store.getters.partId, 1).then(res => {
+        API_FrameGroup.getframestruct(this.$store.getters.partId, FRAME_TYPE.HOSPITAL).then(res => {
           console.log(res)
           this.bfCount = res.bfCount
           this.zcCount = res.zcCount
@@ -396,7 +411,7 @@ export default {
     },
 
     append(data) {
-      if (data.type === 1) {
+      if (data.type === FRAME_TYPE.HOSPITAL) {
         this.formmodel = {}
         this.formmodel.parent_id = data.hospital_id
         this.formmodel.frame_parent_id = data.id
@@ -404,14 +419,16 @@ export default {
         console.log('this.formmodel.parent_id=', this.formmodel.parent_id)
         this.formshow = true
       } else {
-        if (data.type === 4) {
+        if (data.type === FRAME_TYPE.ROOM) {
           this.frameEditTitle = '【' + data.full_name + '】添加床位'
         } else {
           this.frameEditTitle = '【' + data.full_name + '】添加房间'
         }
         this.frameInfo = {
+          part_id: data.part_id,
+          hospital_id: data.hospital_id,
           parent_id: data.id,
-          type: data.type === 4 ? 5 : 4,
+          type: data.type === FRAME_TYPE.ROOM ? FRAME_TYPE.BED : FRAME_TYPE.ROOM,
           parent_name: data.name,
           name: '',
           alias: ''
@@ -421,7 +438,7 @@ export default {
       }
     },
     edit(data, e) {
-      if (data.type === 3 || data.type === 1) {
+      if (data.type === FRAME_TYPE.PART || data.type === FRAME_TYPE.HOSPITAL) {
         console.log('暂未开发')
         return
       }
@@ -435,7 +452,7 @@ export default {
     },
     remove(data) {
       let warning = ''
-      if (data.type === 4) {
+      if (data.type === FRAME_TYPE.ROOM) {
         warning = '确定要删除【' + data.full_name + '】及其中的所有床位吗?'
       } else {
         warning = '确定删除床位【' + data.full_name + '】吗?'
@@ -474,11 +491,11 @@ export default {
         if (valid) {
           const params = this.MixinClone(this.frameInfo)
           if (params.id) {
-            if (params.type === 4) {
-              params.full_name = params.name
-            } else {
-              params.full_name = params.parent_name + '-' + params.name + '床'
-            }
+            // if (params.type === FRAME_TYPE.ROOM) {
+            //   params.full_name = params.name
+            // } else {
+            //   params.full_name = params.parent_name + '-' + params.name + '床'
+            // }
             HospitalFrame_API.updateHospitalFrame(params.id, params).then(response => {
               this.$message.success('修改成功!')
               this.frameDialogVisible = false
@@ -491,11 +508,11 @@ export default {
             if (!params.part_id) {
               params.part_id = this.$store.getters.partId
             }
-            if (params.type === 4) {
-              params.full_name = params.name
-            } else {
-              params.full_name = params.parent_name + '-' + params.name + '床'
-            }
+            // if (params.type === FRAME_TYPE.ROOM) {
+            //   params.full_name = params.name
+            // } else {
+            //   params.full_name = params.parent_name + '-' + params.name + '床'
+            // }
 
             HospitalFrame_API.addHospitalFrame(params).then(response => {
               this.$message.success('添加成功!')
@@ -609,6 +626,20 @@ export default {
       }
     },
     /**
+     * 名称输入变化
+     * @param val
+     */
+    frameChange(val) {
+      console.log('s', this.frameInfo)
+      if (!this.frameInfo.full_name) {
+        if (this.frameInfo.type === FRAME_TYPE.ROOM) {
+          this.$set(this.frameInfo, 'full_name', val + '房')
+        } else {
+          this.$set(this.frameInfo, 'full_name', this.frameInfo.parent_name + '-' + val + '床')
+        }
+      }
+    },
+    /**
      * 提交新增表单
      * @param formName
      */

+ 8 - 6
src/views/hospitalFrame/frameTreeView.vue

@@ -196,6 +196,7 @@ import CustomerManager from '../customer/components/customerManager'
 import DeviceManager from '../ncs-device/components/deviceManager'
 import PatientManager from '../customer/components/patientManager'
 import { uiVersion } from '@/utils/domain'
+import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
 export default {
   name: 'FrameTreeView',
   components: { PatientManager, DeviceManager, CustomerManager },
@@ -272,7 +273,7 @@ export default {
      * */
     getFrameTree() {
       return new Promise((resolve, reject) => {
-        API_FrameGroup.getframestruct(this.$store.getters.partId, 3).then(res => {
+        API_FrameGroup.getframestruct(this.$store.getters.partId, FRAME_TYPE.PART).then(res => {
           this.treeData = res.frameTree
           resolve()
         }).catch(err => {
@@ -282,14 +283,15 @@ export default {
     },
 
     append(data) {
-      if (data.type === 4) {
+      if (data.type === FRAME_TYPE.ROOM) {
         this.frameEditTitle = '【' + data.full_name + '】添加床位'
       } else {
         this.frameEditTitle = '【' + data.full_name + '】添加房间'
       }
       this.frameInfo = {
+        hospital_id: data.hospital_id,
         parent_id: data.id,
-        type: data.type === 4 ? 5 : 4,
+        type: data.type === FRAME_TYPE.ROOM ? FRAME_TYPE.BED : FRAME_TYPE.ROOM,
         parent_name: data.name,
         name: '',
         alias: ''
@@ -308,7 +310,7 @@ export default {
     },
     remove(data) {
       let warning = ''
-      if (data.type === 4) {
+      if (data.type === FRAME_TYPE.ROOM) {
         warning = '确定要删除【' + data.full_name + '】及其中的所有床位吗?'
       } else {
         warning = '确定删除床位【' + data.full_name + '】吗?'
@@ -477,8 +479,8 @@ export default {
     frameChange(val) {
       console.log('s', this.frameInfo)
       if (!this.frameInfo.full_name) {
-        if (this.frameInfo.type === 4) {
-          this.$set(this.frameInfo, 'full_name', val)
+        if (this.frameInfo.type === FRAME_TYPE.ROOM) {
+          this.$set(this.frameInfo, 'full_name', val + '房')
           // this.frameInfo.full_name = val
         } else {
           this.$set(this.frameInfo, 'full_name', this.frameInfo.parent_name + '-' + val + '床')

+ 22 - 8
src/views/hospitalFrame/nurse_watch_frame.vue

@@ -7,16 +7,21 @@
           <el-row v-if="rooms.length > 1" :gutter="20" type="flex">
             <el-col v-for="(item,index) in rooms" :key="index" :xs="8" :sm="8" :md="6" :lg="6" :xl="6">
               <el-card class="box-card">
-                <div slot="header" class="clearfix">
+                <div v-if="roleName !== '护士组长'" slot="header" class="clearfix">
                   <el-checkbox v-if="uiVersion === 1" v-model="item.checked" @change="handleCheckboxChanged(item)"><svg-icon icon-class="sickroom" style="color: #9aaabf;margin-right: 5px" /><span>{{ item.name }}</span></el-checkbox>
                   <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" @change="(checked)=>{handleCheckAll(checked,item)}">全选</el-checkbox>
                 </div>
                 <div v-for="(bed,_index) in item.children" :key="_index" class="text item">
-                  <el-checkbox v-model="bed.checked" @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.name }} {{ bed.clerk_name }}</span>
-                    <span v-else>{{ bed.name }}</span>
+                  <el-checkbox  v-if="roleName !== '护士组长'" v-model="bed.checked" @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="roleName === '护士组长'">
+                    <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>
@@ -24,17 +29,23 @@
           <el-row v-if="rooms.length === 1" :gutter="20" type="flex">
             <el-col :span="24">
               <div class="text item">
-                <div style="margin: 10px;">
+                <div v-if="roleName !== '护士组长'" style="margin: 10px;">
                   <el-checkbox v-if="uiVersion === 1" v-model="rooms[0].checked" @change="handleCheckboxChanged(rooms[0])"><svg-icon icon-class="sickroom" style="color: #9aaabf;margin-right: 5px" /><span>{{ rooms[0].name }}</span></el-checkbox>
                   <span v-if="uiVersion !== 1"><svg-icon icon-class="sickroom" style="color: #9aaabf;margin-right: 5px" /><span>{{ rooms[0].name }}</span></span>
                   <el-checkbox v-model="rooms[0].allCkeck" style="margin-left: 30px;" :indeterminate="rooms[0].indeterminate" @change="(checked)=>{handleCheckAll(checked,rooms[0])}">全选</el-checkbox>
                 </div>
                 <el-row>
                   <el-col v-for="(bed,_index) in rooms[0].children" :key="_index" :xs="8" :sm="8" :md="6" :lg="6" :xl="6">
-                    <el-checkbox v-model="bed.checked" @change="handleCheckboxChanged(rooms[0])"><svg-icon icon-class="bed" style="color: #9aaabf;margin-right: 5px" />
-                      <span v-if="bed.device_id && !bed.checked" style="color: #13ce66">{{ bed.name }} {{ bed.clerk_name }}</span>
-                      <span v-else>{{ bed.name }}</span>
+                    <el-checkbox v-if="roleName !== '护士组长'" v-model="bed.checked" @change="handleCheckboxChanged(rooms[0])">
+                      <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="roleName === '护士组长'">
+                      <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>
                   </el-col>
                 </el-row>
               </div>
@@ -98,6 +109,9 @@ export default {
   watch: {
     roleId: function(old, newval) {
       this.getFrameByType()
+    },
+    memberId: function(old, newval) {
+      this.getFrameByType()
     }
   },
   mounted() {

+ 43 - 23
src/views/ncs-clerk/clerkList.vue

@@ -145,10 +145,26 @@
         <el-row v-if="nurseList.length > 0">
           <el-col :span="24">
             <el-form-item label="小组成员">
-              <el-checkbox-group v-model="newCheckList">
-                <el-checkbox v-for="(item, index) in nurseList" :key="index" :label="item.clerk_id" style="width: 200px;">{{ item.clerk_name }}</el-checkbox>
-              </el-checkbox-group>
+              <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>
+                      </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>
+              </div>
             </el-form-item>
+
           </el-col>
         </el-row>
 
@@ -186,13 +202,10 @@ import { serverUrl } from '@/utils/domain'
 import * as clerk_API from '@/api/ncs_clerk'
 import * as RegExp from '@/utils/RegExp'
 import { AG_GRID_LOCALE_CN } from '@/utils/AgGridVueLocaleCn'
-import { param } from '@/utils'
 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 * as API_event from '@/api/ncs_event'
-import { getNurseIds } from '@/api/ncs_clerk'
 let prevOverflow = ''
 
 export default {
@@ -286,7 +299,8 @@ export default {
       newCheckList: [],
       oldCheckList: [],
       nurseList: [],
-      shopId: null
+      shopId: null,
+      nurseClerkList: []
     }
   },
   computed: {
@@ -366,7 +380,7 @@ export default {
         textCustomComparator: this.textCustomComparator,
         comparator: this.testComparator
       }
-    },
+    }
     this.rowSelection = 'multiple'
   },
   mounted() {
@@ -465,7 +479,6 @@ export default {
         this.$alert('没有选择任何记录!', '系统提示', {
           confirmButtonText: '确定',
           callback: action => {
-
           }
         })
       } else {
@@ -473,16 +486,13 @@ export default {
         this.multipleSelection.forEach(function(item) {
           ids.push(item.member_id)
         })
-
         this.handlerDelete(ids.join(','))
       }
     },
-
     /** 性别格式化 */
     formatterSex(row) {
       return row.data.sex === 1 ? '男' : row.data.sex === 0 ? '女' : '未知'
     },
-
     formatterRole(row) {
       if (row.data.founder === 1) {
         return '机构负责人'
@@ -548,11 +558,10 @@ export default {
     /** 编辑用户 */
     handlerEdit(row) {
       this.addMemberForm = Object.assign({}, row)
+      this.nurseList = []
       if (this.roleZzId && this.roleZzId === row.role_id) { // 只有护士组长才能编辑小组成员
-        this.getNurseByRoleId()
-        this.getNurseIds(row.member_id)
+        this.getNurseByRoleId(row.member_id)
       }
-      this.nurseList = []
       this.imageUrl = row.face
       delete this.addMemberForm.password
       this.addMemberRules.password[0].required = false
@@ -561,12 +570,13 @@ export default {
       }
       this.dialogAddMemberVisible = true
     },
-    getNurseByRoleId() {
+    getNurseByRoleId(id) {
       const _this = this
       this.newCheckList = []
       this.oldCheckList = []
       clerk_API.getNurseByRoleId(_this.roleId, this.shopId).then(r => {
         _this.nurseList = r
+        _this.getNurseIds(id)
         // r.forEach(item => {
         //   if (item.parent_id=== id) {
         //     _this.newCheckList.push(item.clerk_id)
@@ -577,16 +587,26 @@ export default {
     },
     getNurseIds(id) {
       const _this = this
-      clerk_API.getNurseIds(this.shopId, id).then(r => {
-        if (r) {
-          r.split(',').forEach(item => {
-            const id = parseInt(item)
-            _this.newCheckList.push(id)
-            _this.oldCheckList.push(id)
+      clerk_API.getNurseIdsByPartId(this.shopId).then(r => {
+        r.forEach(item => {
+          _this.nurseList.forEach(t => {
+            if (t.clerk_id === item.nurse_id) {
+              t.group_name = item.clerk_name
+              t.group_id = item.member_id
+              if (item.member_id === id) {
+                t.checked = true
+                _this.oldCheckList.push(item.nurse_id)
+                _this.newCheckList.push(item.nurse_id)
+              }
+            }
           })
-        }
+        })
+        _this.nurseClerkList = _this.nurseList
       })
     },
+    handleCheckboxChanged(item) {
+      item.checked = !item.checked
+    },
     /** 分页大小发生改变 */
     handlePageSizeChange(size) {
       this.params.page_size = size

+ 109 - 19
src/views/ncs-device/components/deviceManager.vue

@@ -68,8 +68,8 @@
         </el-row>
         <el-row>
           <el-col :span="12">
-            <el-form-item label="设备唯一标识" prop="eth_mac">
-              <el-input v-model="deviceModel.eth_mac" clearable placeholder="请输入设备唯一标识" />
+            <el-form-item label="有线物理地址" prop="eth_mac">
+              <el-input v-model="deviceModel.eth_mac" clearable placeholder="请输入物理MAC地址" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -118,7 +118,7 @@
           <el-col :span="12">
             <el-form-item label="设备位置" prop="frame_id">
               <el-select v-model="deviceModel.frame_id" filterable clearable :disabled="frameSelectabled" placeholder="请选择安装位置">
-                <el-option v-for="(item,index) in partFrames" :key="index" :label="item.full_name" :value="item.id" />
+                <el-option v-for="(item,index) in typeFrames" :key="index" :label="item.full_name" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -152,6 +152,22 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row v-if="hasAudioId">
+          <el-col :span="12">
+            <el-form-item label="上属总线转换盒" prop="trans_audio_id">
+              <el-select v-model="deviceModel.trans_audio_id" placeholder="上属总线转换盒" clearable>
+                <el-option v-for="item in audioOptions" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="上属485转换盒" prop="trans_rs485_id">
+              <el-select v-model="deviceModel.trans_rs485_id" placeholder="上属485转换盒" clearable>
+                <el-option v-for="item in rs485Options" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="handlerFormSubmit('deviceEditForm')">确 定</el-button>
@@ -171,6 +187,8 @@ import * as API_Device from '@/api/ncs_device'
 import * as API_Frame from '@/api/ncs_hospitalFrame'
 import * as clerk_API from '@/api/ncs_clerk'
 import { DeviceUrl } from '@/utils/domain'
+import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
+import { DEVICE_TYPE } from '@/utils/enum/DeviceTypeEnum'
 export default {
   name: 'DeviceManager',
   components: { ButtonCellRender, ListFilter, RadioFilter },
@@ -225,16 +243,24 @@ export default {
           { required: true, message: '请输入设备型号', trigger: 'blur' }
         ],
         eth_mac: [
-          { required: true, message: '请输入设备MAC地址', trigger: 'blur' }
+          { required: true, message: '请输入设备MAC地址', trigger: 'blur' },
+          { pattern: /^([0-9A-Fa-f]{2}:?){6}/gi, message: '请输入正确的MAC地址', trigger: 'blur' }
         ],
         frame_id: [
           { required: Object.keys(this.frame).length === 0, message: '请选安装位置!', trigger: 'blur' } // 没有传入frame 属性,必须选择安装位置
         ],
         role_id: [
           { required: true, message: '请选择适用人', trigger: 'blur' }
+        ],
+        trans_audio_id: [
+          { required: true, message: '请选择总线转换盒', trigger: 'blur' }
         ]
+        // trans_rs485_id: [
+        //   { required: true, message: '请选择485转换盒', trigger: 'blur' }
+        // ]
       },
       partFrames: [],
+      typeFrames: [],
       /** 设备类型转换数组 **/
       deviceTypeTransfer: [
         { key: '护士主机', value: 1 },
@@ -255,7 +281,9 @@ export default {
         { key: '信标', value: 16 },
         { key: '看板', value: 17 },
         { key: '门禁设备', value: 18 },
-        { key: '探视机', value: 19 }
+        { key: '探视机', value: 19 },
+        { key: '485转换盒', value: 20 },
+        { key: '紧急按钮', value: 21 }
       ],
       deviceStatusTransfer: [
         { key: '启用', value: 1, color: 'green' },
@@ -263,6 +291,9 @@ export default {
       ],
       rolesOptions: [],
       hasRoleId: false,
+      audioOptions: [],
+      rs485Options: [],
+      hasAudioId: false,
       websock: null,
       onlineDevice: []
     }
@@ -343,7 +374,7 @@ export default {
         cellRenderer: this.onlineStateFormatter
       },
       { headerName: '部署位置', field: 'full_name', sortable: true, filter: 'agTextColumnFilter' },
-      { headerName: '设备唯一标识', field: 'eth_mac', sortable: true, filter: 'agTextColumnFilter' },
+      { headerName: '以太网MAC地址', field: 'eth_mac', sortable: true, filter: 'agTextColumnFilter' },
       { headerName: '以太网IP地址', field: 'eth_ip', sortable: true, filter: 'agTextColumnFilter' },
       { headerName: '通讯端口', field: 'eth_ip_port', sortable: true, filter: 'agNumberColumnFilter' },
       { headerName: '设备型号', field: 'model', sortable: true, filter: 'agTextColumnFilter' },
@@ -413,14 +444,15 @@ export default {
     this.gridColumnApi.applyColumnState({
       state: [
         {
-          colId: 'status',
-          sort: 'asc'
+          colId: 'id',
+          sort: 'desc'
         }
       ]
     })
     /** 加载科室空间节点  **/
     if (this.part_view) {
-      this.getPartFrames(this.$store.getters.partId)
+      // this.getPartFrames(this.$store.getters.partId)
+      this.getTypeFrame(this.$store.getters.partId)
     }
     this.initWebSocket()
   },
@@ -437,7 +469,7 @@ export default {
         deviceData.forEach(item => {
           if (this.onlineDevice.length > 0) {
             const mac = this.onlineDevice.filter(p => p.toLowerCase() === item.eth_mac.toLowerCase())[0]
-            console.log('在线设备1', mac)
+            // console.log('在线设备1', mac)
             if (mac !== undefined && mac !== null) {
               item['online_state'] = '在线'
             } else {
@@ -462,13 +494,19 @@ export default {
       }).catch(err => {
         this.$message.error(err)
       })
+      this.typeFrames = this.partFrames
     },
-    /** 获取科室的所有空间节点 */
-    getPartFrames(partid) {
-      API_Frame.getFramesPartId(partid).then(res => {
+    getTypeFrame(partId) {
+      API_Frame.getFramesPartId(partId).then(res => {
         this.partFrames = [...res]
       })
     },
+    /** 将partFrames做分割 */
+    getFramesByType(device_type) {
+      if (this.partFrames != null) {
+        this.typeFrames = this.partFrames.filter(item => item.type === device_type)
+      }
+    },
 
     /**
          * 过滤状态发生变化,发送到服务器检索数据
@@ -576,12 +614,50 @@ export default {
     },
     /** 设备类型选中变化  **/
     deviceTypeChange(val) {
-      if (val === 12 || val === 13 || val === 14) { // 模拟设备不需要mac地址
+      this.deviceTypeChangeToFrameTypeChange(val)
+      if (val === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+          val === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+          val === DEVICE_TYPE.SIMULATE_DOOR_LIGHT ||
+          val === DEVICE_TYPE.EMERGENCY_BUTTON) { // 模拟设备不需要mac地址
         this.deviceRules.eth_mac[0].required = false
+        this.deviceRules.eth_mac[1].pattern = null
+      } else if (val === DEVICE_TYPE.REMOTE_CONTROL) {
+        this.deviceRules.eth_mac[1].pattern = null
+        this.deviceRules.eth_mac[0].required = true
       } else {
+        this.deviceRules.eth_mac[1].pattern = /^([0-9A-Fa-f]{2}:?){6}/gi
         this.deviceRules.eth_mac[0].required = true
       }
-      this.hasRoleId = val === 7
+      this.hasRoleId = val === DEVICE_TYPE.NURSE_WATCH
+      this.hasAudioId = val === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+                        val === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+                        val === DEVICE_TYPE.SIMULATE_DOOR_LIGHT
+    },
+    /** 设备类型选中对应的空间结构  **/
+    deviceTypeChangeToFrameTypeChange(val) {
+      if (val === DEVICE_TYPE.NURSE_HOST ||
+          val === DEVICE_TYPE.DOCTOR_HOST ||
+          val === DEVICE_TYPE.LED_SCREEN ||
+          val === DEVICE_TYPE.LCD_SCREEN ||
+          val === DEVICE_TYPE.NURSE_WATCH ||
+          val === DEVICE_TYPE.WORKER_WATCH ||
+          val === DEVICE_TYPE.TRANSFER_DEVICE ||
+          val === DEVICE_TYPE.INFORMATION_BOARD ||
+          val === DEVICE_TYPE.RS485_TRANSFER) {
+        this.getFramesByType(FRAME_TYPE.PART)
+      } else if (val === DEVICE_TYPE.DOOR_DEVICE ||
+                 val === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+                 val === DEVICE_TYPE.SIMULATE_DOOR_LIGHT ||
+                 val === DEVICE_TYPE.BEACON) {
+        this.getFramesByType(FRAME_TYPE.ROOM)
+      } else if (val === DEVICE_TYPE.DIGIT_BED_DEVICE ||
+                 val === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+                 val === DEVICE_TYPE.REMOTE_CONTROL ||
+                 val === DEVICE_TYPE.CELL_PHONE) {
+        this.getFramesByType(FRAME_TYPE.BED)
+      } else {
+        this.typeFrames = this.partFrames
+      }
     },
     /** 添加设备事件 **/
     handleAdd() {
@@ -598,17 +674,22 @@ export default {
       delete this.deviceModel.id
       this.hasRoleId = false
       this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
+      this.hasAudioId = false
+      this.getDevices(this.$store.getters.partId)
       this.deviceEditTitle = '添加设备'
       this.deviceDialogVisible = true
       this.deviceTypeDisabled = false // 新增设备可以选择设备类型
+      this.typeFrames = this.partFrames
     },
     /** 修改设备  **/
     handleEdit(params) {
       this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
-      if (!this.part_view) {
-        this.getPartFrames(params.part_id)
-      }
-      this.hasRoleId = params.device_type === 7
+      this.getDevices(params.part_id)
+      this.deviceTypeChangeToFrameTypeChange(params.device_type)
+      this.hasRoleId = params.device_type === DEVICE_TYPE.NURSE_WATCH
+      this.hasAudioId = params.device_type === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+                        params.device_type === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+                        params.device_type === DEVICE_TYPE.SIMULATE_DOOR_LIGHT
       this.deviceModel = {
         ...params
       }
@@ -659,6 +740,15 @@ export default {
         this.rolesOptions = response.data
       })
     },
+    getDevices(partId) {
+      // 获取设备列表
+      API_Device.getDeviceByType(partId, DEVICE_TYPE.TRANSFER_DEVICE).then(response => {
+        this.audioOptions = response
+      })
+      API_Device.getDeviceByType(partId, DEVICE_TYPE.RS485_TRANSFER).then(response => {
+        this.rs485Options = response
+      })
+    },
 
     initWebSocket: function() {
       var stockbase = DeviceUrl.replace('http', 'ws')

+ 57 - 20
src/views/ncs-device/device-edit.vue

@@ -24,6 +24,8 @@
                 <el-option label="遥控器" :value="15" />
                 <el-option label="信标" :value="16" />
                 <el-option label="看板" :value="17" />
+                <el-option label="485转换盒" :value="20" />
+                <el-option label="紧急按钮" :value="21" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -116,6 +118,20 @@
               </el-select>
             </el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="上属总线转换盒" prop="audio_id">
+              <el-select v-model="formmodel.audio_id" placeholder="上属总线转换盒" clearable>
+                <el-option v-for="item in audioOptions" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="上属485转换盒" prop="rs485_id">
+              <el-select v-model="formmodel.rs485_id" placeholder="上属485转换盒" clearable>
+                <el-option v-for="item in rs485Options" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </el-col>
         </el-row>
       </el-form>
 
@@ -129,8 +145,9 @@
 
 <script>
 import * as API_Device from '@/api/ncs_device'
-import { returnDeviceType } from "@/utils/device_type"
-import * as clerk_API from "@/api/ncs_clerk";
+import { returnDeviceType } from '@/utils/device_type'
+import * as clerk_API from '@/api/ncs_clerk'
+import { DEVICE_TYPE } from '@/utils/enum/DeviceTypeEnum'
 
 export default {
 
@@ -191,7 +208,9 @@ export default {
       wifiHostReadonly: true,
       /** 设备类型选择禁用 编辑时不允许修改设备类型 */
       deviceTypeDisabled: false,
-      rolesOptions: []
+      rolesOptions: [],
+      audioOptions: [],
+      rs485Options: []
     }
   },
   computed: {
@@ -219,6 +238,7 @@ export default {
       this.handlerAdd()
     }
     this.getRoles({ page_size: 200, page_no: 1, fixedCondition: ' shop_id = -1', sort: ' role_id', dir: 'desc' })
+    this.getDevices(this.$store.getters.partId)
   },
   methods: {
     getRoles(param) {
@@ -227,6 +247,15 @@ export default {
         this.rolesOptions = response.data
       })
     },
+    getDevices(partId) {
+      // 获取设备列表
+      API_Device.getDeviceByType(partId, DEVICE_TYPE.TRANSFER_DEVICE).then(response => {
+        this.audioOptions = response
+      })
+      API_Device.getDeviceByType(partId, DEVICE_TYPE.RS485_TRANSFER).then(response => {
+        this.rs485Options = response
+      })
+    },
     /** 新增按钮 */
     async handlerAdd() {
       console.log('nextip', this.nextIp)
@@ -288,29 +317,31 @@ export default {
       this.formmodel.eth_ip = this.nextIp
       this.formmodel.eth_mac = null
       this.wifiHostReadonly = true
-      if (value === 3) {
+      if (value === DEVICE_TYPE.DOOR_DEVICE) {
         this.wifiHostReadonly = false
-      } else if (value === 11) {
+      } else if (value === DEVICE_TYPE.TRANSFER_DEVICE) {
         this.rules.code[0].required = false
-      } else if (value === 12) {
+      } else if (value === DEVICE_TYPE.SIMULATE_BED_DEVICE) {
         this.rules.eth_mac[0].required = false
         this.rules.eth_ip[0].required = false
         this.formmodel.eth_ip = null
         this.formmodel.eth_mac = '1000'
         this.deviceEthIPReadonly = true
-      } else if (value === 13) {
+      } else if (value === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON) {
         this.rules.eth_mac[0].required = false
         this.rules.eth_ip[0].required = false
         this.formmodel.eth_ip = null
         this.formmodel.eth_mac = '3000'
         this.deviceEthIPReadonly = true
-      } else if (value === 14) {
+      } else if (value === DEVICE_TYPE.SIMULATE_DOOR_LIGHT) {
         this.rules.eth_mac[0].required = false
         this.rules.eth_ip[0].required = false
         this.formmodel.eth_ip = null
         this.formmodel.eth_mac = '2000'
         this.deviceEthIPReadonly = true
-      } else if (value === 16) {
+      } else if (value === DEVICE_TYPE.REMOTE_CONTROL) {
+        this.rules.eth_mac[0].required = true
+      } else if (value === DEVICE_TYPE.BEACON) {
         this.rules.eth_mac[0].required = true
         this.rules.code[0].required = false
         this.deviceEthIPReadonly = true
@@ -323,23 +354,29 @@ export default {
       this.rules.code[0].required = true // 设备出厂编号 ,总线转换盒不需要填写
       this.deviceEthIPReadonly = false // ip 地址只读
       this.wifiHostReadonly = true
-      if (value === 3) {
+      if (value === DEVICE_TYPE.DOOR_DEVICE) {
         this.wifiHostReadonly = false
-      } else if (value === 11) {
+      } else if (value === DEVICE_TYPE.TRANSFER_DEVICE) {
         this.rules.code[0].required = false
-      } else if (value === 12) {
-        this.rules.eth_mac[0].required = false
-        this.rules.eth_ip[0].required = false
-        this.deviceEthIPReadonly = true
-      } else if (value === 13) {
-        this.rules.eth_mac[0].required = false
-        this.rules.eth_ip[0].required = false
-        this.deviceEthIPReadonly = true
-      } else if (value === 14) {
+      } else if (value === DEVICE_TYPE.SIMULATE_BED_DEVICE ||
+                 value === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON ||
+                 value === DEVICE_TYPE.SIMULATE_DOOR_LIGHT
+      ) {
         this.rules.eth_mac[0].required = false
         this.rules.eth_ip[0].required = false
         this.deviceEthIPReadonly = true
       }
+      // } else if (value === DEVICE_TYPE.SIMULATE_EMERGENCY_BUTTON) {
+      //   this.rules.eth_mac[0].required = false
+      //   this.rules.eth_mac[1].pattern = null
+      //   this.rules.eth_ip[0].required = false
+      //   this.deviceEthIPReadonly = true
+      // } else if (value === DEVICE_TYPE.SIMULATE_DOOR_LIGHT) {
+      //   this.rules.eth_mac[0].required = false
+      //   this.rules.eth_mac[1].pattern = null
+      //   this.rules.eth_ip[0].required = false
+      //   this.deviceEthIPReadonly = true
+      // }
     },
     getDeviceById() {
       const _this = this

+ 83 - 2
src/views/ncs-device/nurse_watch.vue

@@ -118,7 +118,8 @@ export default {
       role_id: null,
       role_name: '',
       activeName: 'huan-ban',
-      member_id: null
+      member_id: null,
+      onlineDevice: []
     }
   },
   computed: {
@@ -149,6 +150,34 @@ export default {
       { headerName: '绑定人', field: 'member_name', sortable: true, filter: 'agTextColumnFilter', minWidth: 140, valueFormatter: this.formatterName },
       // { headerName: '所属位置', field: 'full_name', sortable: true, filter: 'agTextColumnFilter', minWidth: 140 },
       { headerName: '设备标识码', field: 'eth_mac', sortable: true, filter: 'agTextColumnFilter', minWidth: 150 },
+      { headerName: '连接状态', field: 'online_state', sortable: false, filter: 'agTextColumnFilter', minWidth: 120,
+        filterParams: {
+          textCustomComparator: (filter, value, filterText) => {
+            const filterTextLowerCase = filterText.toLowerCase()
+            const valueLowerCase = value.toString().toLowerCase()
+            switch (filter) {
+              case 'contains':
+                return valueLowerCase.indexOf(filterTextLowerCase) >= 0
+              case 'notContains':
+                return valueLowerCase.indexOf(filterTextLowerCase) === -1
+              case 'equals':
+                return valueLowerCase === filterTextLowerCase
+              case 'notEqual':
+                return valueLowerCase !== filterTextLowerCase
+              case 'startsWith':
+                return valueLowerCase.indexOf(filterTextLowerCase) === 0
+              case 'endsWith':
+                var index = valueLowerCase.lastIndexOf(filterTextLowerCase)
+                return index >= 0 && index === (valueLowerCase.length - filterTextLowerCase.length)
+              default:
+                // should never happen
+                console.warn('invalid filter type ' + filter)
+                return false
+            }
+          }
+        },
+        cellRenderer: this.onlineStateFormatter
+      },
       { headerName: '有线IP地址', field: 'eth_ip', sortable: true, filter: 'agTextColumnFilter', minWidth: 150 },
       { headerName: '设备型号', field: 'model', sortable: true, filter: 'agTextColumnFilter', minWidth: 150 },
       { headerName: '出厂编号', field: 'code', sortable: true, filter: 'agTextColumnFilter', minWidth: 150 },
@@ -183,7 +212,7 @@ export default {
         minWidth: 100,
         resizable: false,
         sortable: false
-      },
+      }
       // {
       //   headerName: '管理', field: 'shop_id',
       //   cellRendererFramework: 'ButtonCellRender',
@@ -219,6 +248,7 @@ export default {
     this.gridApi = this.gridOptions.api
     this.getList()
     this.initWebSocket()
+    this.deviceOnlineWebSocket()
   },
   beforeDestroy() {
     clearTimeout(this.timer)
@@ -269,6 +299,34 @@ export default {
       //   }
       // })
     },
+    deviceOnlineWebSocket: function() {
+      var stockbase = DeviceUrl.replace('http', 'ws')
+      this.websock1 = new WebSocket(stockbase + '/deviceonline/' + this.$store.getters.uuid)
+      this.websock1.onopen = this.deviceOnlineWebsocketonopen
+      this.websock1.onerror = this.deviceOnlineWebsocketonerror
+      this.websock1.onmessage = this.deviceOnlineWebsocketonmessage
+      this.websock1.onclose = this.deviceOnlineWebsocketclose
+    },
+    deviceOnlineWebsocketonopen: function() {
+      console.log('WebSocket连接成功')
+    },
+    deviceOnlineWebsocketonerror: function(e) {
+      console.log('WebSocket连接发生错误')
+    },
+    deviceOnlineWebsocketonmessage: function(e) {
+      this.onlineDevice = JSON.parse(e.data)
+      if (this.tableData !== null) {
+        this.tableData.forEach(item => {
+          const mac = this.onlineDevice.filter(p => p.toLowerCase() === item.eth_mac.toLowerCase())[0]
+          item.online_state = (mac !== undefined && mac !== null) ? '在线' : '离线'
+        })
+        const tableData = [...this.tableData]
+        this.$set(this, 'tableData', tableData)
+      }
+    },
+    deviceOnlineWebsocketclose: function(e) {
+      console.log('connection closed (' + e.code + ')')
+    },
     /** 选择行变化时,记录选中的行数据 */
     selectFun(val) {
       this.multipleSelection = val
@@ -290,6 +348,19 @@ export default {
       this.gridApi.sizeColumnsToFit()
       API_Device.getList(this.params).then(response => {
         this.loading = false
+        response.data.forEach(item => {
+          if (this.onlineDevice.length > 0) {
+            const mac = this.onlineDevice.filter(p => p.toLowerCase() === item.eth_mac.toLowerCase())[0]
+            // console.log('在线设备1', mac)
+            if (mac !== undefined && mac !== null) {
+              item['online_state'] = '在线'
+            } else {
+              item['online_state'] = '离线'
+            }
+          } else {
+            item['online_state'] = '离线'
+          }
+        })
         this.tableData = response.data
         this.pageData = {
           page_no: response.page_no,
@@ -386,6 +457,8 @@ export default {
     },
     filterModifed(param) {
       const model = param.api.getFilterModel()
+      // 连接状态不经过服务器过滤
+      delete model.online_state
       this.params.filter = JSON.stringify(model)
       this.getList()
     },
@@ -477,6 +550,14 @@ export default {
     },
     fanhui() {
       this.formshow = false
+    },
+    /** 设备连接状态格式化 **/
+    onlineStateFormatter(params) {
+      if (params.value === '在线') {
+        return '<span style="color:green;">在线</span>'
+      } else {
+        return '<span style="color:gray;">离线</span>'
+      }
     }
   }
 }