select.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <template>
  2. <el-dialog
  3. v-model="dialogVisible"
  4. :title="titleMap[type - 1]"
  5. :width="type == 1 ? 680 : 460"
  6. destroy-on-close
  7. append-to-body
  8. @closed="$emit('closed')"
  9. >
  10. <template v-if="type == 1">
  11. <div class="sc-user-select">
  12. <div class="sc-user-select__left">
  13. <div class="sc-user-select__search">
  14. <el-input v-model="keyword" prefix-icon="el-icon-search" placeholder="搜索成员">
  15. <template #append>
  16. <el-button icon="el-icon-search" @click="search"></el-button>
  17. </template>
  18. </el-input>
  19. </div>
  20. <div class="sc-user-select__select">
  21. <div v-loading="showGrouploading" class="sc-user-select__tree">
  22. <el-scrollbar>
  23. <el-tree
  24. ref="groupTree"
  25. class="menu"
  26. :data="group"
  27. :node-key="groupProps.key"
  28. :props="groupProps"
  29. highlight-current
  30. :expand-on-click-node="false"
  31. :current-node-key="groupId"
  32. @node-click="groupClick"
  33. />
  34. </el-scrollbar>
  35. </div>
  36. <div v-loading="showUserloading" class="sc-user-select__user">
  37. <div class="sc-user-select__user__list">
  38. <el-scrollbar ref="userScrollbar">
  39. <el-tree
  40. ref="userTree"
  41. class="menu"
  42. :data="user"
  43. :node-key="userProps.key"
  44. :props="userProps"
  45. :default-checked-keys="selectedIds"
  46. show-checkbox
  47. check-on-click-node
  48. @check-change="userClick"
  49. ></el-tree>
  50. </el-scrollbar>
  51. </div>
  52. <footer>
  53. <el-pagination
  54. v-model:currentPage="currentPage"
  55. background
  56. layout="prev,next"
  57. small
  58. :total="total"
  59. :page-size="pageSize"
  60. @current-change="paginationChange"
  61. ></el-pagination>
  62. </footer>
  63. </div>
  64. </div>
  65. </div>
  66. <div class="sc-user-select__toicon">
  67. <el-icon><arrow-right /></el-icon>
  68. </div>
  69. <div class="sc-user-select__selected">
  70. <header>已选 ({{ selected.length }})</header>
  71. <ul>
  72. <el-scrollbar>
  73. <li v-for="(item, index) in selected" :key="item.id">
  74. <span class="name">
  75. <el-avatar size="small">{{ item.username }}</el-avatar>
  76. <label>{{ item.username }}</label>
  77. </span>
  78. <span class="delete">
  79. <el-button type="danger" icon="el-icon-delete" circle size="small" @click="deleteSelected(index)"></el-button>
  80. </span>
  81. </li>
  82. </el-scrollbar>
  83. </ul>
  84. </div>
  85. </div>
  86. </template>
  87. <template v-if="type == 2">
  88. <div class="sc-user-select sc-user-select-role">
  89. <div class="sc-user-select__left">
  90. <div class="sc-user-select__select">
  91. <div v-loading="showGrouploading" class="sc-user-select__tree">
  92. <el-scrollbar>
  93. <el-tree
  94. ref="groupTree"
  95. class="menu"
  96. :data="role"
  97. :node-key="roleProps.key"
  98. :props="roleProps"
  99. show-checkbox
  100. check-strictly
  101. check-on-click-node
  102. :expand-on-click-node="false"
  103. :default-checked-keys="selectedIds"
  104. @check-change="roleClick"
  105. />
  106. </el-scrollbar>
  107. </div>
  108. </div>
  109. </div>
  110. <div class="sc-user-select__toicon">
  111. <el-icon><arrow-right /></el-icon>
  112. </div>
  113. <div class="sc-user-select__selected">
  114. <header>已选 ({{ selected.length }})</header>
  115. <ul>
  116. <el-scrollbar>
  117. <li v-for="(item, index) in selected" :key="item.id">
  118. <span class="name">
  119. <label>{{ item.username }}</label>
  120. </span>
  121. <span class="delete">
  122. <el-button type="danger" icon="el-icon-delete" circle size="small" @click="deleteSelected(index)"></el-button>
  123. </span>
  124. </li>
  125. </el-scrollbar>
  126. </ul>
  127. </div>
  128. </div>
  129. </template>
  130. <template #footer>
  131. <el-button @click="dialogVisible = false">取 消</el-button>
  132. <el-button type="primary" @click="save">确 认</el-button>
  133. </template>
  134. </el-dialog>
  135. </template>
  136. <script>
  137. import config from '@/config/workflow'
  138. import department from '@/api/system/department'
  139. import user from '@/api/system/user'
  140. export default {
  141. props: {
  142. modelValue: { type: Boolean, default: false }
  143. },
  144. data() {
  145. return {
  146. groupProps: {
  147. key: 'id',
  148. label: 'name',
  149. children: 'children'
  150. },
  151. userProps: {
  152. key: 'id',
  153. label: 'username'
  154. },
  155. roleProps: {
  156. key: 'id',
  157. label: 'label',
  158. children: 'children'
  159. },
  160. titleMap: ['人员选择', '角色选择'],
  161. dialogVisible: false,
  162. showGrouploading: false,
  163. showUserloading: false,
  164. keyword: '',
  165. groupId: '',
  166. pageSize: config.user.pageSize,
  167. total: 0,
  168. currentPage: 1,
  169. group: [],
  170. user: [],
  171. role: [],
  172. type: 1,
  173. selected: [],
  174. value: []
  175. }
  176. },
  177. computed: {
  178. selectedIds() {
  179. return this.selected.map(t => t.id)
  180. }
  181. },
  182. mounted() {},
  183. methods: {
  184. //打开赋值
  185. open(type, data) {
  186. this.type = type
  187. this.value = data || []
  188. this.selected = JSON.parse(JSON.stringify(data || []))
  189. this.dialogVisible = true
  190. if (this.type == 1) {
  191. this.getGroup()
  192. this.getUser()
  193. } else if (this.type == 2) {
  194. this.getRole()
  195. }
  196. },
  197. //获取组织
  198. async getGroup() {
  199. this.showGrouploading = true
  200. var res = await department.departmentPageApi({ page: 1, pageSize: 20 })
  201. this.showGrouploading = false
  202. var allNode = { id: '', name: '所有' }
  203. res.unshift(allNode)
  204. this.group = res
  205. },
  206. //获取用户
  207. async getUser() {
  208. this.showUserloading = true
  209. var params = {
  210. data: {
  211. keyword: this.keyword || null,
  212. departmentId: this.groupId || null,
  213. },
  214. page: this.currentPage,
  215. pageSize: this.pageSize
  216. }
  217. var res = await user.userPageApi(params)
  218. this.showUserloading = false
  219. this.user = res.records
  220. this.total = res.total || 0
  221. this.$refs.userScrollbar.setScrollTop(0)
  222. },
  223. //获取角色
  224. async getRole() {
  225. this.showGrouploading = true
  226. var res = await config.role.apiObj.get()
  227. this.showGrouploading = false
  228. this.role = config.role.parseData(res).rows
  229. },
  230. //组织点击
  231. groupClick(data) {
  232. this.keyword = ''
  233. this.currentPage = 1
  234. this.groupId = data[config.group.props.key]
  235. this.getUser()
  236. },
  237. //用户点击
  238. userClick(data, checked) {
  239. if (checked) {
  240. this.selected.push({
  241. id: data[config.user.props.key],
  242. name: data[config.user.props.label]
  243. })
  244. } else {
  245. this.selected = this.selected.filter(item => item.id != data[config.user.props.key])
  246. }
  247. },
  248. //用户分页点击
  249. paginationChange() {
  250. this.getUser()
  251. },
  252. //用户搜索
  253. search() {
  254. this.groupId = ''
  255. this.$refs.groupTree.setCurrentKey(this.groupId)
  256. this.currentPage = 1
  257. this.getUser()
  258. },
  259. //删除已选
  260. deleteSelected(index) {
  261. this.selected.splice(index, 1)
  262. if (this.type == 1) {
  263. this.$refs.userTree.setCheckedKeys(this.selectedIds)
  264. } else if (this.type == 2) {
  265. this.$refs.groupTree.setCheckedKeys(this.selectedIds)
  266. }
  267. },
  268. //角色点击
  269. roleClick(data, checked) {
  270. if (checked) {
  271. this.selected.push({
  272. id: data[config.role.props.key],
  273. name: data[config.role.props.label]
  274. })
  275. } else {
  276. this.selected = this.selected.filter(item => item.id != data[config.role.props.key])
  277. }
  278. },
  279. //提交保存
  280. save() {
  281. this.value.splice(0, this.value.length)
  282. this.selected.map(item => {
  283. this.value.push(item)
  284. })
  285. this.dialogVisible = false
  286. }
  287. }
  288. }
  289. </script>
  290. <style scoped>
  291. .sc-user-select {
  292. display: flex;
  293. }
  294. .sc-user-select__left {
  295. width: 400px;
  296. }
  297. .sc-user-select__right {
  298. flex: 1;
  299. }
  300. .sc-user-select__search {
  301. padding-bottom: 10px;
  302. }
  303. .sc-user-select__select {
  304. display: flex;
  305. border: 1px solid var(--el-border-color-light);
  306. background: var(--el-color-white);
  307. }
  308. .sc-user-select__tree {
  309. width: 200px;
  310. height: 300px;
  311. border-right: 1px solid var(--el-border-color-light);
  312. }
  313. .sc-user-select__user {
  314. width: 200px;
  315. height: 300px;
  316. display: flex;
  317. flex-direction: column;
  318. }
  319. .sc-user-select__user__list {
  320. flex: 1;
  321. overflow: auto;
  322. }
  323. .sc-user-select__user footer {
  324. height: 36px;
  325. padding-top: 5px;
  326. border-top: 1px solid var(--el-border-color-light);
  327. }
  328. .sc-user-select__toicon {
  329. display: flex;
  330. justify-content: center;
  331. align-items: center;
  332. margin: 0 10px;
  333. }
  334. .sc-user-select__toicon i {
  335. display: flex;
  336. justify-content: center;
  337. align-items: center;
  338. background: #ccc;
  339. width: 20px;
  340. height: 20px;
  341. text-align: center;
  342. line-height: 20px;
  343. border-radius: 50%;
  344. color: #fff;
  345. }
  346. .sc-user-select__selected {
  347. height: 345px;
  348. width: 200px;
  349. border: 1px solid var(--el-border-color-light);
  350. background: var(--el-color-white);
  351. }
  352. .sc-user-select__selected header {
  353. height: 43px;
  354. line-height: 43px;
  355. border-bottom: 1px solid var(--el-border-color-light);
  356. padding: 0 15px;
  357. font-size: 12px;
  358. }
  359. .sc-user-select__selected ul {
  360. height: 300px;
  361. overflow: auto;
  362. }
  363. .sc-user-select__selected li {
  364. display: flex;
  365. align-items: center;
  366. justify-content: space-between;
  367. padding: 5px 5px 5px 15px;
  368. height: 38px;
  369. }
  370. .sc-user-select__selected li .name {
  371. display: flex;
  372. align-items: center;
  373. }
  374. .sc-user-select__selected li .name .el-avatar {
  375. background: #409eff;
  376. margin-right: 10px;
  377. }
  378. .sc-user-select__selected li .name label {
  379. }
  380. .sc-user-select__selected li .delete {
  381. display: none;
  382. }
  383. .sc-user-select__selected li:hover {
  384. background: var(--el-color-primary-light-9);
  385. }
  386. .sc-user-select__selected li:hover .delete {
  387. display: inline-block;
  388. }
  389. .sc-user-select-role .sc-user-select__left {
  390. width: 200px;
  391. }
  392. .sc-user-select-role .sc-user-select__tree {
  393. border: none;
  394. height: 343px;
  395. }
  396. .sc-user-select-role .sc-user-select__selected {
  397. }
  398. [data-theme='dark'] .sc-user-select__selected li:hover {
  399. background: rgba(0, 0, 0, 0.2);
  400. }
  401. [data-theme='dark'] .sc-user-select__toicon i {
  402. background: #383838;
  403. }
  404. </style>