select.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <template>
  2. <el-dialog
  3. v-model="dialogVisible"
  4. class="le-dialog"
  5. :title="curTitle"
  6. :width="[1, 5].indexOf(type) !== -1 ? 720 : 500"
  7. destroy-on-close
  8. append-to-body
  9. @closed="$emit('closed')"
  10. >
  11. <template v-if="type === 1">
  12. <div class="sc-user-select">
  13. <div class="sc-user-select__left">
  14. <div class="sc-user-select__search">
  15. <el-input v-model="username" prefix-icon="Search" placeholder="搜索成员">
  16. <template #append>
  17. <el-button icon="Search" @click="search"></el-button>
  18. </template>
  19. </el-input>
  20. </div>
  21. <div class="sc-user-select__select">
  22. <div v-loading="showGrouploading" class="sc-user-select__tree">
  23. <el-scrollbar>
  24. <el-tree
  25. ref="groupTree"
  26. class="menu"
  27. :data="group"
  28. :node-key="groupProps.key"
  29. :props="groupProps"
  30. highlight-current
  31. :expand-on-click-node="false"
  32. :current-node-key="groupId"
  33. @node-click="groupClick"
  34. />
  35. </el-scrollbar>
  36. </div>
  37. <div v-loading="showUserloading" class="sc-user-select__user">
  38. <div class="sc-user-select__user__list">
  39. <el-scrollbar ref="userScrollbar">
  40. <el-tree
  41. ref="userTree"
  42. class="menu"
  43. :data="user"
  44. :node-key="userProps.key"
  45. :props="userProps"
  46. :default-checked-keys="selectedIds"
  47. show-checkbox
  48. check-on-click-node
  49. @check-change="userClick"
  50. ></el-tree>
  51. </el-scrollbar>
  52. </div>
  53. <footer>
  54. <el-pagination
  55. v-model:currentPage="currentPage"
  56. background
  57. layout="prev,next"
  58. small
  59. :total="total"
  60. :page-size="pageSize"
  61. @current-change="paginationChange"
  62. ></el-pagination>
  63. </footer>
  64. </div>
  65. </div>
  66. </div>
  67. <div class="sc-user-select__toicon">
  68. <el-icon><arrow-right /></el-icon>
  69. </div>
  70. <div class="sc-user-select__selected">
  71. <header>已选 ({{ selected.length }})</header>
  72. <ul style="margin: 0; padding: 0">
  73. <el-scrollbar>
  74. <li v-for="(item, index) in selected" :key="item.id">
  75. <span class="name">
  76. <el-avatar class="shrink-0" size="small">{{ item.name?.substring(0, 1) }}</el-avatar>
  77. <le-text :value="item.name || '-'"></le-text>
  78. </span>
  79. <span class="actions">
  80. <el-button v-if="index !== 0" icon="ArrowUp" circle size="small" @click="sortItem(index, 'up')" />
  81. <el-button v-if="index !== selected.length - 1" icon="ArrowDown" circle size="small" @click="sortItem(index, 'down')" />
  82. <el-button type="danger" icon="Delete" circle size="small" @click="deleteSelected(index)" />
  83. </span>
  84. </li>
  85. </el-scrollbar>
  86. </ul>
  87. </div>
  88. </div>
  89. </template>
  90. <template v-if="type === 3">
  91. <div class="sc-user-select sc-user-select-role">
  92. <div class="sc-user-select__left">
  93. <div class="sc-user-select__select">
  94. <div v-loading="showGrouploading" class="sc-user-select__tree">
  95. <el-scrollbar>
  96. <el-tree
  97. ref="groupTree"
  98. class="menu"
  99. :data="role"
  100. :node-key="roleProps.key"
  101. :props="roleProps"
  102. show-checkbox
  103. check-strictly
  104. check-on-click-node
  105. :expand-on-click-node="false"
  106. :default-checked-keys="selectedIds"
  107. @check-change="roleClick"
  108. />
  109. </el-scrollbar>
  110. </div>
  111. </div>
  112. </div>
  113. <div class="sc-user-select__toicon">
  114. <el-icon><ArrowRight /></el-icon>
  115. </div>
  116. <div class="sc-user-select__selected">
  117. <header>已选 ({{ selected.length }})</header>
  118. <ul style="padding: 0; margin: 0">
  119. <el-scrollbar>
  120. <li v-for="(item, index) in selected" :key="item.id">
  121. <span class="name">
  122. <label>{{ item.name || '-' }}</label>
  123. </span>
  124. <span class="actions">
  125. <el-button type="danger" icon="Delete" circle size="small" @click="deleteSelected(index)" />
  126. </span>
  127. </li>
  128. </el-scrollbar>
  129. </ul>
  130. </div>
  131. </div>
  132. </template>
  133. <template v-if="type === 5">
  134. <div class="sc-user-select">
  135. <div class="sc-user-select__left">
  136. <div class="sc-user-select__search">
  137. <el-input v-model="keyword" prefix-icon="Search" placeholder="搜索表单">
  138. <template #append>
  139. <el-button icon="Search" @click="searchTemplate"></el-button>
  140. </template>
  141. </el-input>
  142. </div>
  143. <div class="sc-user-select__select">
  144. <div class="sc-user-select__tree">
  145. <el-scrollbar>
  146. <el-tree
  147. ref="formCategoryTreeRef"
  148. class="menu"
  149. :data="formCategoryTree"
  150. :node-key="formCategoryProps.key"
  151. :props="formCategoryProps"
  152. highlight-current
  153. :expand-on-click-node="false"
  154. :current-node-key="formCategoryId"
  155. @node-click="formCategoryClick"
  156. />
  157. </el-scrollbar>
  158. </div>
  159. <div class="sc-user-select__user">
  160. <div class="sc-user-select__user__list">
  161. <el-scrollbar ref="formTemplateScrollbar">
  162. <el-tree
  163. ref="formTemplateTree"
  164. class="menu"
  165. :data="formTemplate"
  166. :node-key="formTemplateProps.key"
  167. :props="formTemplateProps"
  168. :default-checked-keys="selectedIds"
  169. show-checkbox
  170. check-on-click-node
  171. @check-change="formTemplateClick"
  172. ></el-tree>
  173. </el-scrollbar>
  174. </div>
  175. <footer>
  176. <el-pagination
  177. v-model:currentPage="currentPage"
  178. background
  179. layout="prev,next"
  180. small
  181. :total="total"
  182. :page-size="pageSize"
  183. @current-change="paginationChange"
  184. ></el-pagination>
  185. </footer>
  186. </div>
  187. </div>
  188. </div>
  189. <div class="sc-user-select__toicon">
  190. <el-icon><arrow-right /></el-icon>
  191. </div>
  192. <div class="sc-user-select__selected">
  193. <header>已选 ({{ selected.length }})</header>
  194. <ul style="margin: 0; padding: 0">
  195. <el-scrollbar>
  196. <li v-for="(item, index) in selected" :key="item.id">
  197. <span class="name">
  198. <el-avatar size="small">{{ item.name?.substring(0, 1) }}</el-avatar>
  199. <label>{{ item.name || '-' }}</label>
  200. </span>
  201. <span class="actions">
  202. <el-button type="danger" icon="Delete" circle size="small" @click="deleteSelected(index)" />
  203. </span>
  204. </li>
  205. </el-scrollbar>
  206. </ul>
  207. </div>
  208. </div>
  209. </template>
  210. <template #footer>
  211. <el-button @click="dialogVisible = false">取 消</el-button>
  212. <el-button type="primary" @click="save">确 认</el-button>
  213. </template>
  214. </el-dialog>
  215. </template>
  216. <script>
  217. import config from '@/config/workflow'
  218. import department from '@/api/system/department'
  219. import user from '@/api/system/user'
  220. import role from '@/api/system/role'
  221. import formCategory from '@/api/flow/formcategory'
  222. import formtemplate from '@/api/flow/formtemplate'
  223. import { ElMessage } from 'element-plus'
  224. export default {
  225. props: {
  226. modelValue: { type: Boolean, default: false },
  227. minSelected: { type: Number, default: 0 },
  228. maxSelected: { type: Number, default: 999999 }
  229. },
  230. data() {
  231. return {
  232. groupProps: {
  233. key: 'id',
  234. label: 'name',
  235. children: 'children'
  236. },
  237. userProps: {
  238. key: 'id',
  239. label: 'realName'
  240. },
  241. roleProps: {
  242. key: 'id',
  243. label: 'name',
  244. children: 'children'
  245. },
  246. formCategoryProps: {
  247. key: 'id',
  248. label: 'name',
  249. children: 'children'
  250. },
  251. formTemplateProps: {
  252. key: 'id',
  253. label: 'name'
  254. },
  255. dialogVisible: false,
  256. showGrouploading: false,
  257. showUserloading: false,
  258. keyword: '',
  259. username: '',
  260. groupId: '',
  261. pageSize: config.user.pageSize,
  262. total: 0,
  263. currentPage: 1,
  264. group: [],
  265. user: [],
  266. role: [],
  267. type: 1, // 1: 用户 3: 角色 5: 子表单
  268. selected: [],
  269. value: [],
  270. // 表单分类
  271. formCategoryTree: [],
  272. formCategoryId: '',
  273. formTemplate: []
  274. }
  275. },
  276. computed: {
  277. selectedIds() {
  278. return this.selected.map(t => t.id)
  279. },
  280. curTitle() {
  281. return {
  282. 1: '人员选择',
  283. 3: '角色选择',
  284. 5: '表单分类选择'
  285. }[this.type]
  286. }
  287. },
  288. mounted() {},
  289. methods: {
  290. //打开赋值
  291. open(type, data) {
  292. // 1: 用户 3: 角色 5: 子表单
  293. this.type = type
  294. this.value = JSON.parse(JSON.stringify(data || []))
  295. this.selected = JSON.parse(JSON.stringify(data || []))
  296. this.dialogVisible = true
  297. if (this.type === 1) {
  298. this.getGroup()
  299. this.getUser()
  300. } else if (this.type === 3) {
  301. this.getRole()
  302. } else if (this.type === 5) {
  303. this.getFormCategoryListTreeListEv()
  304. this.getFormTemplateListEv()
  305. }
  306. },
  307. //获取组织
  308. async getGroup() {
  309. this.showGrouploading = true
  310. var res = await department.departmentPageApi({ page: 1, pageSize: 20 })
  311. this.showGrouploading = false
  312. var allNode = { id: '', name: '所有' }
  313. res.unshift(allNode)
  314. this.group = res
  315. },
  316. //获取用户
  317. async getUser() {
  318. this.showUserloading = true
  319. var params = {
  320. data: {
  321. username: this.username || null,
  322. departmentId: this.groupId || null
  323. },
  324. page: this.currentPage,
  325. pageSize: this.pageSize
  326. }
  327. var res = await user.userPageApi(params)
  328. this.showUserloading = false
  329. this.user = res.records
  330. this.total = res.total || 0
  331. this.$refs.userScrollbar.setScrollTop(0)
  332. },
  333. //获取角色
  334. async getRole() {
  335. this.showGrouploading = true
  336. var res = await role.rolePageApi({ page: 1, pageSize: 999 })
  337. this.showGrouploading = false
  338. this.role = res?.records
  339. },
  340. //组织点击
  341. groupClick(data) {
  342. this.keyword = ''
  343. this.currentPage = 1
  344. this.groupId = data.id
  345. this.getUser()
  346. },
  347. //用户点击
  348. userClick(data, checked) {
  349. if (checked) {
  350. this.selected.push({
  351. id: data.id,
  352. name: data.realName
  353. })
  354. } else {
  355. this.selected = this.selected.filter(item => item.id != data.id)
  356. }
  357. },
  358. //表单模板点击
  359. formTemplateClick(data, checked) {
  360. if (checked) {
  361. this.selected.push({
  362. id: data.id,
  363. name: data.name
  364. })
  365. } else {
  366. this.selected = this.selected.filter(item => item.id != data.id)
  367. }
  368. },
  369. //用户分页点击
  370. paginationChange() {
  371. if (this.type === 1) {
  372. this.getUser()
  373. } else if (this.type === 5) {
  374. this.getFormTemplateListEv()
  375. }
  376. },
  377. //用户搜索
  378. search() {
  379. this.groupId = ''
  380. this.$refs.groupTree.setCurrentKey(this.groupId)
  381. this.currentPage = 1
  382. this.getUser()
  383. },
  384. // 表单模板搜索
  385. searchTemplate() {
  386. this.formCategoryId = ''
  387. this.$refs.formCategoryTreeRef.setCurrentKey(this.formCategoryId)
  388. this.currentPage = 1
  389. this.getFormTemplateListEv()
  390. },
  391. // 顺序调整
  392. sortItem(index, type) {
  393. const _list = this.selected
  394. const cur = _list[index]
  395. let nextIdx = index + 1
  396. if (type === 'up') {
  397. nextIdx = index - 1
  398. }
  399. _list[index] = _list[nextIdx]
  400. _list[nextIdx] = cur
  401. },
  402. //删除已选
  403. deleteSelected(index) {
  404. this.selected.splice(index, 1)
  405. if (this.type === 1) {
  406. this.$refs.userTree.setCheckedKeys(this.selectedIds)
  407. } else if (this.type === 3) {
  408. this.$refs.groupTree.setCheckedKeys(this.selectedIds)
  409. } else if (this.type === 5) {
  410. this.$refs.formTemplateTree.setCheckedKeys(this.selectedIds)
  411. }
  412. },
  413. //角色点击
  414. roleClick(data, checked) {
  415. if (checked) {
  416. this.selected.push({
  417. id: data.id,
  418. name: data.name
  419. })
  420. } else {
  421. this.selected = this.selected.filter(item => item.id != data.id)
  422. }
  423. },
  424. //提交保存
  425. save() {
  426. this.value.splice(0, this.value.length)
  427. this.selected.map(item => {
  428. this.value.push(item)
  429. })
  430. // 最小限制
  431. const minSelected = this.minSelected
  432. // 最大限制
  433. const maxSelected = this.type === 5 ? 1 : this.maxSelected
  434. const selected_l = this.selected.length
  435. if (selected_l < minSelected) {
  436. return this.$message.warning(`选中的数据个数不能小于${minSelected}条`)
  437. }
  438. if (selected_l > maxSelected) {
  439. return this.$message.warning(`选中的数据个数不能大于${maxSelected}条`)
  440. }
  441. this.$emit('update:selected', this.value)
  442. this.dialogVisible = false
  443. },
  444. // 获取表单分类树结构
  445. async getFormCategoryListTreeListEv() {
  446. let data = await formCategory.formCategoryListTreeApi({})
  447. this.formCategoryTree = [{ id: '', name: '所有', children: data }]
  448. },
  449. // 分类点击
  450. formCategoryClick(data) {
  451. this.keyword = ''
  452. this.currentPage = 1
  453. this.formCategoryId = data.id
  454. this.getFormTemplateListEv()
  455. },
  456. // 获取表单分类下的表单模板
  457. async getFormTemplateListEv() {
  458. var params = {
  459. data: {
  460. name: this.keyword || null,
  461. formCategoryId: this.formCategoryId || null
  462. },
  463. page: this.currentPage,
  464. pageSize: this.pageSize
  465. }
  466. const data = await formtemplate.formTemplateSimpleListApi(params)
  467. this.formTemplate = data.records
  468. this.total = data.total || 0
  469. this.$refs.formTemplateScrollbar.setScrollTop(0)
  470. }
  471. }
  472. }
  473. </script>
  474. <style scoped>
  475. .sc-user-select {
  476. display: flex;
  477. }
  478. .sc-user-select__left {
  479. width: 400px;
  480. }
  481. .sc-user-select__right {
  482. flex: 1;
  483. }
  484. .sc-user-select__search {
  485. padding-bottom: 10px;
  486. }
  487. .sc-user-select__select {
  488. display: flex;
  489. border: 1px solid var(--el-border-color-light);
  490. background: var(--el-color-white);
  491. }
  492. .sc-user-select__tree {
  493. width: 200px;
  494. height: 300px;
  495. border-right: 1px solid var(--el-border-color-light);
  496. }
  497. .sc-user-select__user {
  498. width: 200px;
  499. height: 300px;
  500. display: flex;
  501. flex-direction: column;
  502. }
  503. .sc-user-select__user__list {
  504. flex: 1;
  505. overflow: auto;
  506. }
  507. .sc-user-select__user footer {
  508. height: 36px;
  509. padding-top: 5px;
  510. border-top: 1px solid var(--el-border-color-light);
  511. }
  512. .sc-user-select__toicon {
  513. display: flex;
  514. justify-content: center;
  515. align-items: center;
  516. margin: 0 10px;
  517. }
  518. .sc-user-select__toicon i {
  519. display: flex;
  520. justify-content: center;
  521. align-items: center;
  522. background: #ccc;
  523. width: 20px;
  524. height: 20px;
  525. text-align: center;
  526. line-height: 20px;
  527. border-radius: 50%;
  528. color: #fff;
  529. }
  530. .sc-user-select__selected {
  531. height: 345px;
  532. width: 240px;
  533. border: 1px solid var(--el-border-color-light);
  534. background: var(--el-color-white);
  535. }
  536. .sc-user-select__selected header {
  537. height: 43px;
  538. line-height: 43px;
  539. border-bottom: 1px solid var(--el-border-color-light);
  540. padding: 0 15px;
  541. font-size: 12px;
  542. }
  543. .sc-user-select__selected ul {
  544. height: 300px;
  545. overflow: auto;
  546. }
  547. .sc-user-select__selected li {
  548. display: flex;
  549. align-items: center;
  550. justify-content: space-between;
  551. padding: 5px 5px 5px 15px;
  552. height: 38px;
  553. }
  554. .sc-user-select__selected li .name {
  555. display: flex;
  556. align-items: center;
  557. }
  558. .sc-user-select__selected li .name .el-avatar {
  559. background: var(--el-color-primary);
  560. margin-right: 6px;
  561. }
  562. .sc-user-select__selected li .name label {
  563. }
  564. .sc-user-select__selected li .actions {
  565. //display: none;
  566. display: inline-flex;
  567. gap: 6px;
  568. .el-button + .el-button {
  569. margin-left: 0;
  570. }
  571. }
  572. .sc-user-select__selected li:hover {
  573. background: var(--el-color-primary-light-9);
  574. }
  575. /*.sc-user-select__selected li:hover .actions {
  576. display: inline-block;
  577. }*/
  578. .sc-user-select-role .sc-user-select__left {
  579. width: 200px;
  580. }
  581. .sc-user-select-role .sc-user-select__tree {
  582. border: none;
  583. height: 343px;
  584. }
  585. .sc-user-select-role .sc-user-select__selected {
  586. }
  587. [data-theme='dark'] .sc-user-select__selected li:hover {
  588. background: rgba(0, 0, 0, 0.2);
  589. }
  590. [data-theme='dark'] .sc-user-select__toicon i {
  591. background: #383838;
  592. }
  593. </style>