frameTreeView-admin.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. <template>
  2. <div>
  3. <el-container :style="{height: asideHeight+'px'}">
  4. <el-aside width="280px" style="border-color: rgb(238, 241, 246)">
  5. <div class="el-row--flex">
  6. <el-input
  7. v-model="filterText"
  8. :placeholder="this.$t('frameManage.keywordsFilter')"
  9. clearable
  10. />
  11. <el-button
  12. type="text"
  13. size="mini"
  14. style="margin-left: 10px;color: #67C23A"
  15. @click="quickCreateFrame"
  16. >{{ this.$t('frameManage.quickCreate') }}</el-button>
  17. </div>
  18. <el-tree
  19. ref="frameTree"
  20. :data="treeData"
  21. :show-checkbox="false"
  22. node-key="id"
  23. :default-expand-all="true"
  24. :auto-expand-parent="true"
  25. :expand-on-click-node="false"
  26. :highlight-current="true"
  27. :current-node-key="selectedNodeId"
  28. draggable
  29. :accordion="true"
  30. :filter-node-method="filterNode"
  31. @node-drag-start="nodeDragStart"
  32. @node-drop="nodeDrop"
  33. @node-click="nodeClick"
  34. >
  35. <span slot-scope="{ node, data }" class="custom-tree-node">
  36. <!-- <span><svg-icon :icon-class="data.type===4?'sickroom':data.type===5?'bed':'area'" />{{ data.full_name }}</span>-->
  37. <span><svg-icon :style="data.customer_id ? 'color: #0a901c' : ''" :icon-class="data.type===4?'sickroom':data.type===5?'bed':'area'" />{{ data.full_name }}</span>
  38. <span>
  39. <el-button
  40. v-if="data.type!==5"
  41. type="text"
  42. size="mini"
  43. icon="el-icon-plus"
  44. @click.stop="() => append(data)"
  45. />
  46. <el-button
  47. type="text"
  48. :disabled="data.type===1 || data.type===3"
  49. size="mini"
  50. icon="el-icon-edit"
  51. @click.stop="() => edit(data)"
  52. />
  53. <el-button
  54. type="text"
  55. :disabled="data.type===1"
  56. size="mini"
  57. icon="el-icon-delete"
  58. @click.stop="() => remove(data)"
  59. />
  60. </span>
  61. </span>
  62. </el-tree>
  63. </el-aside>
  64. <el-main>
  65. <el-tabs v-model="activeName" style="margin:0px;" type="border-card">
  66. <el-tab-pane :label="this.$t('frameManage.memberList')" name="frameInfo">
  67. <keep-alive>
  68. <customer-manager v-if="uiVersion===2" :frame="selectedNode" @saved="handleCustomerChange" />
  69. <patient-manager v-if="uiVersion===1" :frame="selectedNode" @saved="handleCustomerChange" />
  70. </keep-alive>
  71. </el-tab-pane>
  72. <el-tab-pane :label="this.$t('frameManage.deviceList')" name="deviceList">
  73. <keep-alive>
  74. <device-manager :frame="selectedNode" />
  75. </keep-alive>
  76. </el-tab-pane>
  77. </el-tabs>
  78. </el-main>
  79. </el-container>
  80. <!---添加空间结构弹窗 -->
  81. <el-dialog :title="frameEditTitle" :visible.sync="frameDialogVisible" width="500px">
  82. <el-form ref="editForm" :model="frameInfo" :rules="rules" label-width="130px">
  83. <el-row>
  84. <el-col :span="24">
  85. <!--医院结构名称-->
  86. <el-form-item :label="this.$t('action.name')" prop="name">
  87. <el-input v-model="frameInfo.name" :maxlength="20" @change="frameChange">
  88. <template slot="append">{{ frameInfo.type === 4? this.$t('frameManage.room') : this.$t('frameManage.bed') }}</template>
  89. </el-input>
  90. </el-form-item>
  91. </el-col>
  92. </el-row>
  93. <el-row>
  94. <el-col :span="24">
  95. <!--医院结构别名-->
  96. <el-form-item :label="this.$t('action.alias')" prop="alias">
  97. <el-input v-model="frameInfo.alias" :maxlength="20" />
  98. </el-form-item>
  99. </el-col>
  100. </el-row>
  101. <el-row>
  102. <el-col :span="24">
  103. <!--医院结构别名-->
  104. <el-form-item :label="this.$t('action.fullName')" prop="full_name">
  105. <el-input v-model="frameInfo.full_name" :maxlength="20" />
  106. </el-form-item>
  107. </el-col>
  108. </el-row>
  109. <el-row hidden>
  110. <el-col :span="12">
  111. <el-form-item :label="this.$t('action.type')">
  112. <el-radio v-model="frameInfo.type" :label="1">{{ this.$t('frameManage.room') }}</el-radio>
  113. <el-radio v-model="frameInfo.type" :label="2">{{ this.$t('frameManage.bed') }}</el-radio>
  114. </el-form-item>
  115. </el-col>
  116. </el-row>
  117. <el-form-item>
  118. <el-button type="primary" class="save" @click="handleFrameSubmit('editForm')">{{ this.$t('action.yes') }}</el-button>
  119. </el-form-item>
  120. </el-form>
  121. </el-dialog>
  122. <!---添加空间结构弹窗 -->
  123. <!---快速创建结构弹窗 -->
  124. <el-dialog :title="this.$t('frameManage.quickCreateFrame')" :visible.sync="frameQuickCreateVisible" width="600px">
  125. <el-form ref="createFrameForm" :model="createFrameModel" :rules="createFrameRules" label-width="200px">
  126. <el-row>
  127. <el-col :span="24">
  128. <!--开始房间号-->
  129. <el-form-item :label="this.$t('frameManage.startRoom')" prop="start_no">
  130. <el-input-number v-model="createFrameModel.room_start_no" :min="1" @change="roomStartChange" />
  131. </el-form-item>
  132. </el-col>
  133. </el-row>
  134. <el-row>
  135. <el-col :span="24">
  136. <!--结束房间号-->
  137. <el-form-item :label="this.$t('frameManage.endRoom')" prop="end_no">
  138. <el-input-number v-model="createFrameModel.room_end_no" :min="createFrameModel.room_start_no" />
  139. </el-form-item>
  140. </el-col>
  141. </el-row>
  142. <el-row>
  143. <el-col :span="24">
  144. <!--每房床位数-->
  145. <el-form-item :label="this.$t('frameManage.bedQuantity')" prop="beds_per_room">
  146. <el-input-number v-model="createFrameModel.beds_per_room" :min="1" :max="20" />
  147. </el-form-item>
  148. </el-col>
  149. </el-row>
  150. <el-row>
  151. <el-col :span="18">
  152. <!--每房床位数-->
  153. <el-form-item :label="this.$t('frameManage.showRoomDigit')" prop="room_num_bits">
  154. <el-input-number v-model="createFrameModel.room_num_bits" :min="1" :max="4" @change="(val)=>{bitNumChange(val,'room')}" />
  155. </el-form-item>
  156. </el-col>
  157. <el-col :span="6">
  158. <div class="el-form-item__label">
  159. {{ this.$t('frameManage.example') }}:{{ room_num_demo }}
  160. </div>
  161. </el-col>
  162. </el-row>
  163. <el-row>
  164. <el-col :span="18">
  165. <!--每房床位数-->
  166. <el-form-item :label="this.$t('frameManage.showBedDigit')" prop="bed_num_bits">
  167. <el-input-number v-model="createFrameModel.bed_num_bits" :min="1" :max="4" @change="(val)=>{bitNumChange(val,'bed')}" />
  168. </el-form-item>
  169. </el-col>
  170. <el-col :span="6">
  171. <div class="el-form-item__label">
  172. {{ this.$t('frameManage.example') }}:{{ bed_num_demo }}
  173. </div>
  174. </el-col>
  175. </el-row>
  176. <el-form-item>
  177. <el-button type="primary" class="save" @click="quickCreateSubmit()">{{ this.$t('action.yes') }}</el-button>
  178. </el-form-item>
  179. </el-form>
  180. </el-dialog>
  181. <!---快速创建结构弹窗 -->
  182. </div>
  183. </template>
  184. <script>
  185. import * as HospitalFrame_API from '@/api/ncs_hospitalFrame'
  186. import * as API_FrameGroup from '@/api/ncs_frameGroup'
  187. import CustomerManager from '../customer/components/customerManager'
  188. import DeviceManager from '../ncs-device/components/deviceManager'
  189. import PatientManager from '../customer/components/patientManager'
  190. // import { uiVersion } from '@/utils/domain'
  191. import { FRAME_TYPE } from '@/utils/enum/FrameTypeEnum'
  192. const uiVersion = domain.uiVersion
  193. export default {
  194. name: 'FrameTreeView',
  195. components: { PatientManager, DeviceManager, CustomerManager },
  196. data() {
  197. return {
  198. treeData: [],
  199. treeDataClone: [],
  200. /** 当前选中的树节点 */
  201. selectedNodeId: 0,
  202. selectedNode: {},
  203. filterText: '',
  204. activeName: 'frameInfo',
  205. /** 上级机构数组 **/
  206. parents: [],
  207. /** frame 编辑弹窗 **/
  208. frameEditTitle: this.$t('action.add'),
  209. frameDialogVisible: false,
  210. frameInfo: {},
  211. rules: {
  212. name: [
  213. this.MixinRequired(this.$t('frameManage.inputFrameName')),
  214. { min: 2, max: 10, message: this.$t('frameManage.inputLong'), trigger: 'blur' }
  215. ],
  216. alias: [
  217. { min: 2, max: 10, message: this.$t('frameManage.inputLong'), trigger: 'blur' }
  218. ],
  219. full_name: [
  220. this.MixinRequired(this.$t('frameManage.inputFrameFullName')),
  221. { min: 2, max: 10, message: this.$t('frameManage.inputLong'), trigger: 'blur' }
  222. ]
  223. },
  224. /** 快速创建空间结构弹窗 **/
  225. frameQuickCreateVisible: false,
  226. createFrameModel: {
  227. room_start_no: 1,
  228. room_end_no: 2,
  229. beds_per_room: 4,
  230. room_num_bits: 2,
  231. bed_num_bits: 2
  232. },
  233. bed_num_demo: '01',
  234. room_num_demo: '01',
  235. createFrameRules: {
  236. },
  237. uiVersion: uiVersion
  238. }
  239. },
  240. computed: {
  241. asideHeight() {
  242. return this.mainAreaHeight
  243. }
  244. },
  245. watch: {
  246. selectedNodeId(newval, old) {
  247. console.log('watch', newval)
  248. this.selectedNode = this.findNodeById(this.treeData, newval)
  249. },
  250. filterText(val) {
  251. this.$refs.frameTree.filter(val)
  252. }
  253. },
  254. mounted() {
  255. this.getFrameTree().then(() => {
  256. this.selectedNodeId = this.treeData[0].id
  257. this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
  258. }).catch(err => {
  259. this.$message.error(err.message)
  260. })
  261. },
  262. methods: {
  263. /**
  264. * 获取空间结构树形数据
  265. * */
  266. getFrameTree() {
  267. return new Promise((resolve, reject) => {
  268. API_FrameGroup.getAllFrameTreeByType(FRAME_TYPE.HOSPITAL).then(res => {
  269. this.treeData = res.frameTree
  270. resolve()
  271. }).catch(err => {
  272. reject(err)
  273. })
  274. })
  275. },
  276. append(data) {
  277. if (data.type === FRAME_TYPE.HOSPITAL) {
  278. this.formmodel = {}
  279. this.formmodel.parent_id = data.hospital_id
  280. this.formmodel.frame_parent_id = data.id
  281. this.formmodel.full_name = data.name
  282. console.log('this.formmodel.parent_id=', this.formmodel.parent_id)
  283. this.formshow = true
  284. } else {
  285. if (data.type === FRAME_TYPE.ROOM) {
  286. this.frameEditTitle = '【' + data.full_name + '】' + this.$t('frameManage.addBed')
  287. } else {
  288. this.frameEditTitle = '【' + data.full_name + '】' + this.$t('frameManage.addRoom')
  289. }
  290. this.frameInfo = {
  291. hospital_id: data.hospital_id,
  292. parent_id: data.id,
  293. type: data.type === FRAME_TYPE.ROOM ? FRAME_TYPE.BED : FRAME_TYPE.ROOM,
  294. parent_name: data.name,
  295. name: '',
  296. alias: ''
  297. }
  298. this.frameDialogVisible = true
  299. console.log('append', data)
  300. }
  301. },
  302. edit(data, e) {
  303. if (data.type === FRAME_TYPE.PART || data.type === FRAME_TYPE.HOSPITAL) {
  304. this.$message.info(this.$t('action.notDevelopedYet') + '')
  305. return
  306. }
  307. const parentNode = this.findNodeById(this.treeData, data.parent_id)
  308. this.frameInfo = {
  309. ...data,
  310. parent_name: parentNode === null ? '' : parentNode.name
  311. }
  312. this.frameEditTitle = this.$t('frameManage.editFrame')
  313. this.frameDialogVisible = true
  314. },
  315. remove(data) {
  316. let warning = ''
  317. if (data.type === FRAME_TYPE.ROOM) {
  318. warning = this.$t('frameManage.sureDelete') + '【' + data.full_name + '】' + this.$t('frameManage.allBad')
  319. } else {
  320. warning = this.$t('frameManage.sureDeleteBed') + '【' + data.full_name + '】?'
  321. }
  322. this.$confirm(warning, this.$t('action.waring' + ''), {
  323. confirmButtonText: this.$t('action.yes'),
  324. cancelButtonText: this.$t('action.cancel'),
  325. type: 'warning'
  326. }).then(() => {
  327. HospitalFrame_API.deleteHospitalFrame(data.id).then(response => {
  328. this.$message({
  329. type: 'success',
  330. message: this.$t('action.deleted')
  331. })
  332. this.getFrameTree().then(() => {
  333. // 判断选中节点是否被删除,如果被删除需要重新选择根节点
  334. const selectNode = this.findNodeById(this.treeData, this.selectedNodeId)
  335. if (selectNode === null) {
  336. this.selectedNodeId = this.treeData[0].id
  337. }
  338. this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
  339. })
  340. }).catch(response => {
  341. this.$message({
  342. type: 'info',
  343. message: response.message
  344. })
  345. })
  346. }).catch(() => {
  347. })
  348. },
  349. handleFrameSubmit(formName) {
  350. this.$refs[formName].validate(valid => {
  351. if (valid) {
  352. const params = this.MixinClone(this.frameInfo)
  353. if (params.id) {
  354. HospitalFrame_API.updateHospitalFrame(params.id, params).then(response => {
  355. this.$message.success(this.$t('action.editSuccess') + '')
  356. this.frameDialogVisible = false
  357. this.getFrameTree().then(() => {
  358. this.selectedNodeId = response.id
  359. this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
  360. })
  361. })
  362. } else {
  363. if (!params.part_id) {
  364. params.part_id = this.$store.getters.partId
  365. }
  366. HospitalFrame_API.addHospitalFrame(params).then(response => {
  367. this.$message.success(this.$t('action.addSuccess') + '')
  368. this.frameDialogVisible = false
  369. this.getFrameTree()
  370. })
  371. }
  372. } else {
  373. this.$message.error(this.$t('action.fromError'))
  374. return false
  375. }
  376. })
  377. },
  378. /** 开始拖拽之前保存treeData数据,拖拽完成后判定能否拖拽到目标节点,不允许拖拽把初始数据覆盖拖拽后的数据,否则拖拽成功
  379. * @param node
  380. * @param event
  381. */
  382. nodeDragStart(node, event) {
  383. this.treeDataClone = this.MixinClone(this.treeData)
  384. },
  385. /** 拖拽结束 **/
  386. nodeDrop(node, target, position, event) {
  387. var success = true
  388. const nparent = this.findNodeById(this.treeDataClone, node.data.parent_id)
  389. const tparent = this.findNodeById(this.treeDataClone, target.data.parent_id)
  390. if (position === 'inner') { // 进入了某个节点之中,不能将某一节点拖入同级节点,也不能跨级拖动
  391. if (Number(node.data.type) <= Number(target.data.type) || Number(node.data.type) - Number(target.data.type) > 1) {
  392. this.treeData = this.treeDataClone
  393. success = false
  394. }
  395. } else {
  396. // 查找Target和node的parent 判定target的parent类型与node的parent类型是否一致
  397. if (nparent === null || tparent === null || nparent.type !== tparent.type) {
  398. this.treeData = this.treeDataClone
  399. success = false
  400. }
  401. }
  402. if (success) { // 拖拽完成,更新node和target排序
  403. HospitalFrame_API.sort(node.data.id, target.data.id, position).then(res => {
  404. this.selectedNodeId = node.data.id
  405. this.getFrameTree().then(() => {
  406. this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
  407. console.log('sid', this.selectedNodeId)
  408. })
  409. })
  410. }
  411. },
  412. /** 树形结构中查找指定Id节点 */
  413. findNodeById(data, id) {
  414. let node = null
  415. if (data.length > 0) {
  416. for (var i = 0; i < data.length; i++) {
  417. if (data[i].id === id) {
  418. node = data[i]
  419. break
  420. }
  421. if (data[i].children && data[i].children.length > 0) {
  422. const subresult = this.findNodeById(data[i].children, id)
  423. if (subresult !== null) {
  424. node = subresult
  425. break
  426. }
  427. }
  428. }
  429. }
  430. return node
  431. },
  432. /** 节点过滤方法 **/
  433. filterNode(value, data) {
  434. if (!value) return true
  435. return data.full_name.indexOf(value) !== -1
  436. },
  437. /** 点击树形节点 **/
  438. nodeClick(data, node, leaf) {
  439. this.$set(this, 'selectedNodeId', data.id)
  440. },
  441. /** 快速创建空间结构弹窗 **/
  442. quickCreateFrame() {
  443. this.frameQuickCreateVisible = true
  444. },
  445. quickCreateSubmit(formname) {
  446. const params = this.createFrameModel
  447. params.part_frame_id = this.treeData[0].id
  448. HospitalFrame_API.quickCreate(params).then(res => {
  449. this.$message.success(this.$t('action.createSuccess') + '')
  450. this.frameQuickCreateVisible = false
  451. this.getFrameTree().then(() => {
  452. this.$refs.frameTree.setCurrentKey(this.selectedNodeId)
  453. })
  454. }).catch(err => {
  455. this.$message.error(err.message)
  456. })
  457. },
  458. /** 显示位数变化 **/
  459. bitNumChange(val, frame_type) {
  460. const s = Array(val).join(0) + 1
  461. if (frame_type === 'room') {
  462. this.room_num_demo = s
  463. } else {
  464. this.bed_num_demo = s
  465. }
  466. },
  467. /** 起始位数变化 结束位置要根据起始位置的大小来变化 */
  468. roomStartChange(val) {
  469. console.log(val)
  470. if (val > this.createFrameModel.room_end_no) {
  471. this.createFrameModel.room_end_no = val
  472. }
  473. },
  474. /**
  475. * 名称输入变化
  476. * @param val
  477. */
  478. frameChange(val) {
  479. console.log('s', this.frameInfo)
  480. if (!this.frameInfo.full_name) {
  481. if (this.frameInfo.type === FRAME_TYPE.ROOM) {
  482. this.$set(this.frameInfo, 'full_name', val + this.$t('frameManage.room'))
  483. // this.frameInfo.full_name = val
  484. } else {
  485. this.$set(this.frameInfo, 'full_name', this.frameInfo.parent_name + '-' + val + this.$t('frameManage.bed'))
  486. // this.frameInfo.full_name =
  487. }
  488. }
  489. },
  490. handleCustomerChange() {
  491. this.getFrameTree()
  492. }
  493. }
  494. }
  495. </script>
  496. <style scoped>
  497. .el-aside{
  498. margin: 8px;
  499. padding: 8px;
  500. border-width: 1px;
  501. border-style: solid;
  502. background: #fff;
  503. }
  504. .custom-tree-node {
  505. flex: 1;
  506. display: flex;
  507. align-items: center;
  508. justify-content: space-between;
  509. font-size: 14px;
  510. padding-right: 8px;
  511. }
  512. .el-tree{margin-top: 8px}
  513. .el-main{
  514. margin: 8px;
  515. padding: 0;
  516. }
  517. </style>