select.vue 15 KB

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