index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. <!--编写穿梭框的插件,左右都有很多搜索条件-->
  2. <template>
  3. <div>
  4. <div style="width: 98%;">
  5. <div class="tags-box" :class="{'tags-box-disabled':disabled,'tags-box-mini':size === 'mini'}" @click="showDialog">
  6. <el-tag type="success" :size="size" v-for="v in currentTempList" :key="v.id">{{ v.shortName }}</el-tag>
  7. </div>
  8. <div class="tags_last" :class="{'tags-box-disabled':disabled}">
  9. <i v-if="currentTempList.length > 0 && !disabled" class="el-icon-circle-close close-icon" @click="clear"></i>
  10. <span v-show="currentTempList.length > 0" class="tags-num">{{ currentTempList.length }}</span>
  11. </div>
  12. </div>
  13. <DialogCom
  14. :title="title"
  15. :visible.sync="visible"
  16. width="80%"
  17. top="10vh"
  18. :close-on-click-modal="false"
  19. append-to-body
  20. @opened="open"
  21. @closed="closed"
  22. >
  23. <el-row class="el-row-top">
  24. <el-col :span="11">
  25. <el-card class="box-card leftbox" :body-style="{ height: '100%' }">
  26. <el-row class="result_row">
  27. <el-col :span="12">搜索条件</el-col>
  28. </el-row>
  29. <el-form
  30. ref="formleft"
  31. v-model="left.condition"
  32. label-width="120px"
  33. size="mini"
  34. style="width: 95%"
  35. >
  36. <el-row>
  37. <el-col :span="12">
  38. <el-form-item label="上级机构:">
  39. <tree-select
  40. v-model="left.condition.orgId"
  41. :searchable="searchable"
  42. :default-expand-level="level"
  43. :normalizer="tenantIdnormalizer"
  44. :options="treeList"
  45. :show-count="true"
  46. :props="{ checkStrictly: true, label: 'name' }"
  47. placeholder="请选择机构"
  48. clearValueText="清除"
  49. :noChildrenText="''"
  50. @select="leftTreeSelect"
  51. noOptionsText="没有数据"
  52. noResultsText="没有搜索结果"
  53. />
  54. </el-form-item>
  55. </el-col>
  56. <el-col :span="12">
  57. <el-form-item label="机构类型:">
  58. <el-select
  59. prop="type"
  60. label="机构类型"
  61. v-model="left.condition.orgType"
  62. style="width: 100%"
  63. placeholder="请选择机构类型"
  64. @change="leftSearch"
  65. clearable
  66. >
  67. <el-option
  68. v-for="dict in dict.type.sys_org_type"
  69. :key="dict.value"
  70. :label="dict.label"
  71. :value="dict.value"
  72. />
  73. </el-select>
  74. </el-form-item>
  75. </el-col>
  76. </el-row>
  77. <!-- <el-form-item >
  78. <el-checkbox v-model="left.condition.searchChild" @change="leftSearch">是否包含下级</el-checkbox>
  79. </el-form-item>-->
  80. <el-row>
  81. <el-col :span="12">
  82. <el-form-item label="机构名称:">
  83. <el-input
  84. v-model="left.condition.orgName"
  85. clearable
  86. @input="leftSearch"
  87. ></el-input>
  88. </el-form-item>
  89. </el-col>
  90. </el-row>
  91. </el-form>
  92. <el-row class="result_row">
  93. <el-col :span="12">搜索结果</el-col>
  94. <el-col :span="12" style="text-align: right"
  95. >{{ left.selectedRows.length }}/{{ left.data.length }}
  96. </el-col
  97. >
  98. </el-row>
  99. <el-table
  100. style="width: 99%;margin:auto;"
  101. height="calc(100% - 148px)"
  102. :data="left.data"
  103. @selection-change="handleLeftSelectionChange"
  104. >
  105. <el-table-column type="selection" width="55"></el-table-column>
  106. <el-table-column prop="affiliatedArea" align="center" label="地区"></el-table-column>
  107. <el-table-column prop="affiliatedBank" label="行社" align="center"></el-table-column>
  108. <el-table-column prop="shortName" label="机构名称" align="center"></el-table-column>
  109. </el-table>
  110. </el-card>
  111. </el-col>
  112. <el-col :span="2">
  113. <div class="controller">
  114. <el-button type="primary" @click="handleAdd">添加 >></el-button>
  115. <el-button class="remove" type="primary" @click="handleRemove">移除 <<</el-button>
  116. </div>
  117. </el-col>
  118. <el-col :span="11">
  119. <el-card class="box-card" :body-style="{ height: '100%' }">
  120. <el-row class="result_row">
  121. <el-col :span="12">搜索条件</el-col>
  122. </el-row>
  123. <el-form
  124. ref="formRight"
  125. v-model="right.condition"
  126. label-width="120px"
  127. size="mini"
  128. style="width: 95%"
  129. >
  130. <el-row>
  131. <el-col :span="12">
  132. <el-form-item label="组织机构:">
  133. <tree-select
  134. v-model="right.condition.orgId"
  135. :searchable="searchable"
  136. :options="treeList"
  137. :default-expand-level="level"
  138. :normalizer="tenantIdnormalizer"
  139. :show-count="true"
  140. :props="{ checkStrictly: true, label: 'name' }"
  141. placeholder="请选择归属机构"
  142. clearValueText="清除"
  143. :noChildrenText="''"
  144. @select="rightTreeSelect"
  145. noOptionsText="没有数据"
  146. noResultsText="没有搜索结果"
  147. />
  148. </el-form-item>
  149. </el-col>
  150. <el-col :span="12">
  151. <el-form-item label="机构类型:">
  152. <el-select
  153. prop="type"
  154. label="机构类型"
  155. v-model="right.condition.orgType"
  156. style="width: 100%"
  157. placeholder="请选择机构类型"
  158. @change="rightSearch"
  159. clearable
  160. >
  161. <el-option
  162. v-for="dict in dict.type.sys_org_type"
  163. :key="dict.value"
  164. :label="dict.label"
  165. :value="dict.value"
  166. />
  167. </el-select>
  168. </el-form-item>
  169. </el-col>
  170. </el-row>
  171. <!-- <el-form-item >
  172. <el-checkbox v-model="right.condition.searchChild" @change="rightSearch">是否包含下级</el-checkbox>
  173. </el-form-item>-->
  174. <el-row>
  175. <el-col :span="12">
  176. <el-form-item label="机构名称:">
  177. <el-input
  178. v-model="right.condition.orgName"
  179. clearable
  180. @input="rightSearch"
  181. ></el-input>
  182. </el-form-item>
  183. </el-col>
  184. </el-row>
  185. </el-form>
  186. <el-row class="result_row">
  187. <el-col :span="12">搜索结果</el-col>
  188. <el-col :span="12" style="text-align: right"
  189. >{{ right.selectedRows.length }}/{{ right.data.length }}
  190. </el-col
  191. >
  192. </el-row>
  193. <el-table
  194. style="width: 99%;margin:auto;"
  195. height="calc(100% - 148px)"
  196. :data="right.data"
  197. @selection-change="handleRightSelectionChange"
  198. >
  199. <el-table-column type="selection" width="55"></el-table-column>
  200. <el-table-column prop="affiliatedArea" label="地区"></el-table-column>
  201. <el-table-column prop="affiliatedBank" label="行社"></el-table-column>
  202. <el-table-column prop="shortName" label="机构名称"></el-table-column>
  203. </el-table>
  204. </el-card>
  205. </el-col>
  206. </el-row>
  207. <span slot="footer" class="dialog-footer">
  208. <el-button @click="visible = false">关闭</el-button>
  209. <el-button type="primary" @click="onOK">确定</el-button>
  210. </span>
  211. </DialogCom>
  212. </div>
  213. </template>
  214. <script>
  215. import {selectOrgList} from "@/api/system/org.js";
  216. const defaultData = {
  217. left: {
  218. condition: {
  219. orgId: null,
  220. orgName: "",
  221. orgType: null,
  222. searchChild: false,
  223. },
  224. total: 0,
  225. selectedCount: 0,
  226. selectedRows: [],
  227. data: [],
  228. },
  229. right: {
  230. condition: {
  231. orgId: null,
  232. orgName: "",
  233. orgType: null,
  234. searchChild: false,
  235. },
  236. total: 0,
  237. selectedCount: 0,
  238. selectedRows: [],
  239. data: [],
  240. },
  241. }
  242. export default {
  243. name: "orgSelect",
  244. dicts: ["sys_org_type"],
  245. props: {
  246. orgList : {
  247. type: Array,
  248. default: new Array(),
  249. },
  250. size: {
  251. type: String,
  252. default: 'small',
  253. },
  254. trigger: {
  255. type: String,
  256. default: 'click',
  257. },
  258. //组件禁用
  259. disabled: {
  260. type: Boolean,
  261. default: false,
  262. required: false,
  263. },
  264. customRequest: {
  265. type: Function,
  266. },
  267. hangsheTree: {
  268. type: Boolean,
  269. default: false,
  270. },
  271. businessTree: {
  272. type: Boolean,
  273. default: false,
  274. },
  275. wholeTree: {
  276. type: Boolean,
  277. default: false,
  278. },
  279. },
  280. watch: {
  281. orgList: function (data) {
  282. this.currentTempList = this.orgList;
  283. }
  284. },
  285. data() {
  286. return {
  287. orgs: [],
  288. title: "选择机构",
  289. level: 1,
  290. clearable: true,
  291. searchable: true,
  292. visible: false,
  293. deviceType: null,
  294. loading: false,
  295. treeList: [],
  296. deptOptions: [],
  297. currentTempList: [],
  298. boundOrgsClone: [],
  299. left: {
  300. condition: {
  301. orgId: this.$store.getters.orgId,
  302. deviceName: "",
  303. searchChild: false,
  304. },
  305. total: 0,
  306. selectedCount: 0,
  307. selectedRows: [],
  308. data: [],
  309. },
  310. right: {
  311. condition: {
  312. orgId: this.$store.getters.orgId,
  313. deviceName: "",
  314. searchChild: false,
  315. },
  316. total: 0,
  317. selectedCount: 0,
  318. selectedRows: [],
  319. data: [],
  320. },
  321. rightOrgIds: [],
  322. boundOrgIds: [],
  323. searchOrgs: [],
  324. };
  325. },
  326. created() {
  327. this.boundOrgsClone = [];
  328. },
  329. mounted() {
  330. },
  331. methods: {
  332. clear() {
  333. this.currentTempList = [];
  334. this.boundOrgsClone = [];
  335. this.$emit("selectNode", []);
  336. this.$emit("selectNodeId", []);
  337. },
  338. tenantIdnormalizer(node, instanceId) {
  339. if (node.children && !node.children.length) {
  340. delete node.children;
  341. }
  342. return {
  343. id: node.id,
  344. label: node.shortName,
  345. children: node.children,
  346. };
  347. },
  348. orgTree() {
  349. //获取所有机构列表
  350. this.getAllOrgs();
  351. if (this.customRequest) {
  352. this.customRequest().then((response) => {
  353. this.treeList = response.data;
  354. return;
  355. });
  356. } else {
  357. if (this.hangsheTree) {
  358. this.treeList = this.$store.getters.depTree;
  359. } else if (this.businessTree) {
  360. this.treeList = this.$store.getters.businessTree;
  361. } else if (this.wholeTree) {
  362. this.treeList = this.$store.getters.wholeTree;
  363. } else {
  364. this.treeList = this.$store.getters.orgTree;
  365. }
  366. }
  367. },
  368. getChildIds(orgTree, parentId) {
  369. if (parentId) {
  370. orgTree.forEach((v, i) => {
  371. if (v.path.indexOf(parentId) != -1) {
  372. this.rightOrgIds.push(v.id);
  373. }
  374. if (v.children && v.children.length > 0) {
  375. this.getChildIds(v.children, parentId);
  376. }
  377. });
  378. }
  379. },
  380. open() {
  381. this.right.data = JSON.parse(JSON.stringify(this.currentTempList));
  382. this.boundOrgsClone = JSON.parse(JSON.stringify(this.currentTempList));
  383. //this.leftSearch();
  384. },
  385. showDialog() {
  386. this.orgTree();
  387. this.visible = true;
  388. },
  389. hideDialog() {
  390. this.visible = false;
  391. this.left = defaultData.left;
  392. this.right = defaultData.right;
  393. this.searchOrgs = [];
  394. this.boundOrgIds = [];
  395. this.boundOrgsClone = [];
  396. },
  397. closed() {
  398. this.left = defaultData.left;
  399. this.right = defaultData.right;
  400. this.searchOrgs = [];
  401. this.boundOrgIds = [];
  402. this.boundOrgsClone = [];
  403. this.left.data = [];
  404. this.right.data = [];
  405. this.left.condition.orgId = this.$store.getters.orgId;
  406. this.right.condition.orgId = this.$store.getters.orgId;
  407. this.left.condition.orgType = null;
  408. this.right.condition.orgType = null;
  409. this.left.condition.orgName = null;
  410. this.right.condition.orgName = null;
  411. },
  412. leftTreeSelect(node) {
  413. this.left.condition.orgId = node.id;
  414. this.left.condition.orgPath = node.path;
  415. this.leftSearch();
  416. },
  417. rightTreeSelect(node) {
  418. this.right.condition.orgId = node.id;
  419. this.right.condition.orgPath = node.path;
  420. this.rightSearch();
  421. },
  422. handleLeftSelectionChange(val) {
  423. this.left.selectedCount = val.length;
  424. this.left.selectedRows = val;
  425. },
  426. handleRightSelectionChange(val) {
  427. this.right.selectedCount = val.length;
  428. this.right.selectedRows = val;
  429. },
  430. handleAdd() {
  431. this.right.condition.orgId = null;
  432. this.right.condition.orgType = null;
  433. this.right.condition.orgName = null;
  434. let addData = this.left.selectedRows.filter(
  435. (row) => this.boundOrgIds.indexOf(row.id) < 0
  436. );
  437. if (addData.length > 0) {
  438. addData.forEach((i, v) => {
  439. this.right.data.push(i);
  440. this.boundOrgsClone.push(i);
  441. });
  442. this.boundOrgIds = this.boundOrgsClone.map((d) => d.deviceId);
  443. this.left.data = this.searchOrgs.filter((row) => this.boundOrgIds.indexOf(row.id) < 0)
  444. }
  445. this.leftSearch();
  446. },
  447. handleRemove() {
  448. /* this.right.condition.orgId = null;
  449. this.right.condition.orgType = null;
  450. this.right.condition.orgName = null;
  451. this.right.condition.orgPath = null;*/
  452. let rightSelectedIds = this.right.selectedRows.map(
  453. (r) => r.id
  454. );
  455. if (rightSelectedIds.length > 0) {
  456. this.right.data = this.right.data.filter(
  457. (d) => rightSelectedIds.indexOf(d.id) < 0
  458. );
  459. this.boundOrgsClone = this.boundOrgsClone.filter(
  460. (d) => rightSelectedIds.indexOf(d.id) < 0
  461. );
  462. this.boundOrgIds = this.boundOrgsClone.map((d) => d.id);
  463. this.left.data = this.searchOrgs.filter((row) => this.boundOrgIds.indexOf(row.id) < 0)
  464. if (this.left.condition.orgId) {
  465. this.leftSearch();
  466. }
  467. }
  468. },
  469. getAllOrgs() {
  470. selectOrgList({orgId:this.$store.getters.orgId,checkSub:true}).then((result) => {
  471. this.orgs = result;
  472. });
  473. },
  474. leftSearch() {
  475. let {condition} = this.left;
  476. this.left.prevCondition = {...condition};
  477. this.boundOrgIds = this.boundOrgsClone.map((d) => d.id);
  478. this.left.data = this.orgs
  479. .filter(
  480. (d) => this.boundOrgIds.indexOf(d.id) < 0
  481. )
  482. .filter(
  483. (d) => !condition.orgName || d.shortName.includes(condition.orgName)
  484. )
  485. .filter(
  486. // 过滤机构类型
  487. (d) => !condition.orgType || d.type == condition.orgType
  488. )
  489. .filter(
  490. // 过滤父级机构
  491. (d) => !condition.orgPath || d.path.includes(condition.orgPath)
  492. );
  493. },
  494. rightSearch() {
  495. const {condition} = this.right;
  496. if (this.boundOrgsClone.length > 0) {
  497. this.right.data = this.boundOrgsClone
  498. .filter(
  499. // 过滤机构名称
  500. (d) =>
  501. !condition.orgName || d.shortName.includes(condition.orgName)
  502. )
  503. .filter(
  504. // 过滤机构类型
  505. (d) => !condition.orgType || d.type == condition.orgType
  506. )
  507. .filter(
  508. // 过滤父级机构
  509. (d) => !condition.orgPath || d.path.includes(condition.orgPath)
  510. );
  511. }
  512. },
  513. onOK() {
  514. this.currentTempList = this.boundOrgsClone;
  515. this.$emit("selectNodeId", this.boundOrgIds);
  516. this.hideDialog();
  517. },
  518. },
  519. model: {
  520. event: "selectNodeId",
  521. },
  522. };
  523. </script>
  524. <style lang="scss" scoped>
  525. .result_row {
  526. width: 96%;
  527. padding: 6px;
  528. }
  529. .dialog-footer {
  530. float: right;
  531. margin: 10px 0px;
  532. }
  533. ::v-deep .el-dialog__footer {
  534. height: 60px;
  535. }
  536. ::v-deep .el-dialog__body {
  537. padding: 10px 5px;
  538. }
  539. ::v-deep .el-card__body {
  540. padding: 10px 5px;
  541. }
  542. .controller {
  543. margin: auto;
  544. width: 80px;
  545. top: calc(50% - 30px);
  546. position: relative;
  547. .remove {
  548. margin-top: 20px;
  549. margin-left: 0px;
  550. }
  551. }
  552. .el-row-top {
  553. height: 550px;
  554. display: flex;
  555. v-deep & > .el-col {
  556. height: 100%;
  557. width: calc(50% - 50px);
  558. }
  559. v-deep & > .el-col:nth-child(2) {
  560. width: 100px;
  561. }
  562. }
  563. .box-card {
  564. height: 100%;
  565. display: flex;
  566. justify-content: space-between;
  567. v-deep .el-card__body {
  568. height: 100%;
  569. }
  570. }
  571. .leftbox {
  572. v-deep .k-tree-select__clear {
  573. display: none !important;
  574. }
  575. }
  576. .deviceName-column-content {
  577. display: block;
  578. text-align: right;
  579. }
  580. .tags-box {
  581. background-color: #fff;
  582. border-radius: 4px;
  583. border-top: 1px solid #dcdfe6; /* 上边框 */
  584. border-bottom: 1px solid #dcdfe6; /* 下边框 */
  585. border-left: 1px solid #dcdfe6; /* 左边框 */
  586. border-right: none; /* 取消右边框 */
  587. color: #606266;
  588. outline: 0;
  589. padding: 0 40px 0 5px;
  590. width: 90%;
  591. min-height: 40px;
  592. max-height: 40px;
  593. position: relative;
  594. overflow: hidden;
  595. display: inline-block;
  596. text-overflow: ellipsis;
  597. white-space: nowrap;
  598. cursor: pointer;
  599. }
  600. .tags_last{
  601. background-color: #fff;
  602. border-radius: 4px;
  603. border-top: 1px solid #dcdfe6; /* 上边框 */
  604. border-bottom: 1px solid #dcdfe6; /* 下边框 */
  605. border-right: 1px solid #dcdfe6; /* 左边框 */
  606. border-left: none; /* 取消右边框 */
  607. color: #606266;
  608. outline: 0;
  609. width: 10%;
  610. float: right;
  611. min-height: 40px;
  612. max-height: 40px;
  613. > span {
  614. margin: 5px 10px;
  615. color: #161617;
  616. }
  617. .close-icon {
  618. position: absolute;
  619. right: 28px;
  620. cursor: pointer;
  621. margin: 10px 10px;
  622. &:hover {
  623. color: #aaa;
  624. }
  625. }
  626. }
  627. .tags-box-disabled {
  628. background-color: #F5F7FA;
  629. border-color: #E4E7ED;
  630. color: #C0C4CC;
  631. cursor: not-allowed;
  632. }
  633. .tags-box-mini {
  634. min-height: 32px;
  635. max-height: 32px;
  636. line-height: 32px;
  637. > span {
  638. margin: 0 5px;
  639. }
  640. > .tags-num {
  641. line-height: 38px;
  642. }
  643. }
  644. .tags-num {
  645. color: #ccc;
  646. position: absolute;
  647. right: 48px;
  648. top: -4px;
  649. }
  650. .tree-box {
  651. margin-top: 20px;
  652. max-height: 300px;
  653. overflow: auto;
  654. border-radius: 4px;
  655. border: 1px solid #dcdfe6;
  656. user-select: none;
  657. }
  658. </style>