SelectPopup.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <template>
  2. <u-popup class="jnpf-tree-select-popup" mode="right" v-model="showPopup" width="100%" @close="close">
  3. <view class="jnpf-tree-select-body">
  4. <view class="jnpf-tree-select-title">
  5. <text class="icon-ym icon-ym-report-icon-preview-pagePre backIcon" @tap="close()"></text>
  6. <view class="title">选择用户</view>
  7. </view>
  8. <view class="jnpf-tree-select-search">
  9. <u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
  10. :show-action="false" @change="handleSearch" bg-color="#f0f2f6" shape="square">
  11. </u-search>
  12. </view>
  13. <view class="jnpf-tree-selected">
  14. <view class="jnpf-tree-selected-head">
  15. <view>{{$t('component.jnpf.common.selected')}}({{selectedList.length||0}})</view>
  16. <view v-if="multiple" class="clear-btn" @click="setCheckAll">
  17. {{$t('component.jnpf.common.clearAll')}}
  18. </view>
  19. </view>
  20. <view class="jnpf-tree-selected-box">
  21. <scroll-view scroll-y="true" class="select-list">
  22. <u-tag closeable @close="delSelect(index)" v-for="(item,index) in selectedList" :key="index"
  23. :text="item.orgNameTree" class="u-selectTag" />
  24. </scroll-view>
  25. </view>
  26. </view>
  27. <view class="jnpf-tree-selected-line"></view>
  28. <view class="jnpf-tree-selected-tabs">
  29. <view class="tab-item" :class="{'tab-item-active':activeKey==='user'}" @click="toggloActive('user')">
  30. 用户
  31. </view>
  32. <view class="tab-item" :class="{'tab-item-active':activeKey==='position'}"
  33. @click="toggloActive('position')">
  34. 组织岗位
  35. </view>
  36. <view class="tab-item" :class="{'tab-item-active':activeKey==='role'}" @click="toggloActive('role')">
  37. 角色
  38. </view>
  39. <view class="tab-item" :class="{'tab-item-active':activeKey==='group'}" @click="toggloActive('group')">
  40. 用户组
  41. </view>
  42. <view class="tab-item" :class="{'tab-item-active':activeKey==='system'}" @click="toggloActive('system')"
  43. v-if="hasSys">
  44. 动态参数
  45. </view>
  46. </view>
  47. <view class="jnpf-tree-selected-breadcrumb" v-if="['user','position'].includes(activeKey)">
  48. <view class="breadcrumb-item" :class="{'breadcrumb-item-active':!currStep}" @click="handleToFirst()"
  49. v-if="activeKey==='user'">
  50. 用户组
  51. </view>
  52. <view class="breadcrumb-item" :class="{'breadcrumb-item-active':!currStep}" @click="handleToFirst()"
  53. v-if="activeKey==='position'">
  54. 组织岗位
  55. </view>
  56. <view class="icon-ym icon-ym-caret-right breadcrumb-item" v-if="currStep"></view>
  57. <view class="breadcrumb-item" :class="{'breadcrumb-item-active':currStep}"
  58. v-if="currStep && activeKey==='user'">
  59. 用户列表
  60. </view>
  61. <view class="breadcrumb-item" :class="{'breadcrumb-item-active':currStep}"
  62. v-if="currStep && activeKey==='position'">
  63. 动态参数
  64. </view>
  65. </view>
  66. <view class="jnpf-tree-select-tree">
  67. <scroll-view :scroll-y="true" style="height: 100%" :scroll-top="scrollTop" @scroll="handleScroll"
  68. v-show="(!hasPage && !currStep) && ['user','position'].includes(activeKey)">
  69. <ly-tree ref="tree" :props="realProps" :node-key="realProps.value" :load="loadNode" lazy
  70. :tree-data="lazyOptions" show-node-icon :defaultExpandAll='false'
  71. @node-click="handleTreeNodeClick" :expandOnClickNode="false" :checkOnClickNode="false"
  72. v-if="activeKey==='position'" />
  73. <block v-else>
  74. <view class="jnpf-selcet-list" v-if="list.length">
  75. <view class="jnpf-selcet-cell" v-for="item in list" :key="item.id"
  76. @click.stop="handleNodeClick(item)">
  77. <view class="jnpf-selcet-cell-icon" :class='item.icon'></view>
  78. <view class="jnpf-selcet-cell-name">
  79. {{item.fullName}}
  80. </view>
  81. </view>
  82. </view>
  83. <Empty class="h-full" v-else />
  84. </block>
  85. </scroll-view>
  86. <scroll-view :scroll-y="true" style="height: 100%" :refresher-enabled="false" :refresher-threshold="100"
  87. :scroll-with-animation='true' :refresher-triggered="triggered" @scrolltolower="handleScrollToLower"
  88. v-if="hasPage||currStep||!['user','position'].includes(activeKey)">
  89. <view class="jnpf-selcet-list" v-if="userList.length">
  90. <view class="jnpf-selcet-cell" v-for="item in userList" :key="item.id"
  91. @click.stop="handleUserNodeClick(item)">
  92. <view class="jnpf-selcet-cell-action">
  93. <lyCheckbox :type="multiple ? 'checkbox' : 'radio'"
  94. :checked="selectedIds.includes(item.id)" />
  95. </view>
  96. <u-avatar class="jnpf-selcet-cell-avatar" :src="baseURL+item.headIcon" mode="circle"
  97. size="44" v-if="activeKey==='user'"></u-avatar>
  98. <view class="jnpf-selcet-cell-name">
  99. {{item.orgNameTree}}
  100. </view>
  101. </view>
  102. </view>
  103. <Empty class="h-full" v-else />
  104. </scroll-view>
  105. </view>
  106. <!-- 底部按钮 -->
  107. <view class="jnpf-tree-select-actions">
  108. <u-button class="buttom-btn" @click="close()">{{$t('common.cancelText')}}</u-button>
  109. <u-button class="buttom-btn" type="primary"
  110. @click.stop="handleConfirm()">{{$t('common.okText')}}</u-button>
  111. </view>
  112. </view>
  113. </u-popup>
  114. </template>
  115. <script>
  116. import {
  117. getOrgAndPosSelector,
  118. getUserList,
  119. getSelectedUserList
  120. } from '@/api/common'
  121. import lyCheckbox from '@/components/ly-tree/components/ly-checkbox.vue';
  122. import Empty from '../Empty/index.vue'
  123. import {
  124. useBaseStore
  125. } from '@/store/modules/base'
  126. const baseStore = useBaseStore()
  127. const defaultProps = {
  128. label: 'fullName',
  129. value: 'id',
  130. icon: 'icon',
  131. children: 'children'
  132. }
  133. const defaultUserQuery = {
  134. enabledMark: 1,
  135. groupId: '',
  136. organizeId: '',
  137. positionId: '',
  138. roleId: '',
  139. };
  140. const systemFieldList = [{
  141. fullName: '当前用户',
  142. id: '@userId',
  143. orgNameTree: '当前用户'
  144. }];
  145. const organizeSubList = [{
  146. fullName: '当前组织',
  147. id: '--org',
  148. orgNameTree: ''
  149. },
  150. {
  151. fullName: '当前组织及子组织',
  152. id: '--subOrg',
  153. orgNameTree: '及子组织'
  154. },
  155. {
  156. fullName: '当前组织及子孙组织',
  157. id: '--progenyOrg',
  158. orgNameTree: '及子孙组织'
  159. },
  160. ];
  161. const positionSubList = [{
  162. fullName: '当前岗位',
  163. id: '--pos',
  164. orgNameTree: ''
  165. },
  166. {
  167. fullName: '当前岗位及子岗位',
  168. id: '--subPos',
  169. orgNameTree: '及子岗位'
  170. },
  171. {
  172. fullName: '当前岗位及子孙岗位',
  173. id: '--progenyPos',
  174. orgNameTree: '及子孙岗位'
  175. },
  176. ];
  177. export default {
  178. props: {
  179. selectedData: {
  180. type: Array,
  181. default: () => []
  182. },
  183. // 通过双向绑定控制组件的弹出与收起
  184. modelValue: {
  185. type: Boolean,
  186. default: false
  187. },
  188. // 弹出的z-index值
  189. zIndex: {
  190. type: [String, Number],
  191. default: 0
  192. },
  193. props: {
  194. type: Object,
  195. default: () => ({
  196. label: 'fullName',
  197. value: 'id',
  198. icon: 'icon',
  199. children: 'children',
  200. isLeaf: 'isLeaf'
  201. })
  202. },
  203. multiple: {
  204. type: Boolean,
  205. default: true
  206. },
  207. hasSys: {
  208. type: Boolean,
  209. default: false
  210. },
  211. },
  212. components: {
  213. lyCheckbox,
  214. Empty
  215. },
  216. data() {
  217. return {
  218. moving: false,
  219. selectedList: [],
  220. selectedIds: [],
  221. keyword: '',
  222. showPopup: false,
  223. lazyOptions: [],
  224. activeKey: 'user',
  225. hasPage: false,
  226. pagination: {
  227. hasPage: 1,
  228. currentPage: 1,
  229. pageSize: 20
  230. },
  231. triggered: false,
  232. finish: false,
  233. list: [],
  234. userList: [],
  235. scrollTop: 0,
  236. currStep: 0,
  237. userQuery: {},
  238. };
  239. },
  240. watch: {
  241. // 在select弹起的时候,重新初始化所有数据
  242. modelValue: {
  243. handler(val) {
  244. this.showPopup = val
  245. if (val) setTimeout(() => this.init(), 10);
  246. },
  247. immediate: true
  248. },
  249. selectedList: {
  250. handler(val) {
  251. this.selectedIds = val.map((o) => o.id);
  252. this.$refs.tree && this.$refs.tree.setCheckedKeys(this.selectedIds)
  253. },
  254. deep: true
  255. },
  256. selectedData: {
  257. handler(val) {
  258. if (val) setTimeout(() => this.init(), 10);
  259. },
  260. deep: true
  261. }
  262. },
  263. computed: {
  264. baseURL() {
  265. return this.define.baseURL
  266. },
  267. uZIndex() {
  268. // 如果用户有传递z-index值,优先使用
  269. return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
  270. },
  271. realProps() {
  272. return {
  273. ...defaultProps,
  274. ...this.props
  275. }
  276. },
  277. },
  278. methods: {
  279. init() {
  280. this.keyword = ""
  281. this.hasPage = 0
  282. this.currStep = 0
  283. this.activeKey = 'user'
  284. this.resetQuery()
  285. this.$nextTick(() => {
  286. this.triggered = true
  287. })
  288. this.selectedList = JSON.parse(JSON.stringify(this.selectedData))
  289. this.getGroupList()
  290. },
  291. handleToFirst() {
  292. if (!this.currStep) return
  293. this.currStep = 0
  294. this.hasPage = false
  295. this.keyword = ''
  296. this.resetQuery()
  297. },
  298. resetQuery() {
  299. this.userList = []
  300. this.finish = false
  301. this.pagination.currentPage = 1
  302. },
  303. loadNode(node, resolve) {
  304. const id = node.key || '0'
  305. const type = node?.data?.type || 'organize'
  306. const data = {
  307. id,
  308. type
  309. }
  310. getOrgAndPosSelector(data).then(res => {
  311. const list = res.data?.list || []
  312. resolve(list)
  313. })
  314. },
  315. handleScroll(e) {
  316. this.scrollTop = e.detail.scrollTop
  317. },
  318. goTop() {
  319. this.scrollTop = 0
  320. },
  321. handleScrollToLower() {
  322. if (this.finish || this.activeKey !== 'user') return
  323. this.getUserList()
  324. },
  325. getUserList() {
  326. let data = {
  327. keyword: this.keyword,
  328. ...this.pagination,
  329. ...this.userQuery
  330. }
  331. getUserList(data).then(res => {
  332. const list = (res.data.list || []).map((o) => ({
  333. ...o,
  334. id: `${o.id}--user`,
  335. orgNameTree: o.orgNameTree || o.fullName
  336. }));
  337. if (list.length < this.pagination.pageSize) this.finish = true;
  338. this.userList = this.userList.concat(list);
  339. this.pagination.currentPage++
  340. })
  341. },
  342. handleTreeNodeClick(item) {
  343. const data = item.data
  344. this.currStep = 1
  345. this.getSubData(data)
  346. },
  347. getSubData(item) {
  348. const subList = item.type === 'organize' ? organizeSubList : positionSubList;
  349. const data = [];
  350. for (const element of subList) {
  351. data.push({
  352. ...item,
  353. fullName: element.fullName,
  354. icon: '',
  355. id: `${item.id}${element.id}`,
  356. orgNameTree: item.orgNameTree + element.orgNameTree
  357. });
  358. }
  359. this.userList = data;
  360. },
  361. handleNodeClick(data) {
  362. this.userQuery = {
  363. ...defaultUserQuery,
  364. 'groupId': data.id
  365. };
  366. this.currStep = 1
  367. this.keyword = ''
  368. this.resetQuery()
  369. this.getUserList()
  370. },
  371. handleUserNodeClick(data) {
  372. const index = this.selectedList.findIndex((o) => o.id === data.id);
  373. if (index !== -1) return this.selectedList.splice(index, 1);
  374. this.multiple ? this.selectedList.push(data) : (this.selectedList = [data]);
  375. },
  376. delSelect(index) {
  377. this.selectedList.splice(index, 1);
  378. },
  379. setCheckAll() {
  380. this.selectedIds = []
  381. this.selectedList = []
  382. },
  383. handleConfirm() {
  384. this.$emit('confirm', this.selectedList, this.selectedIds);
  385. this.close();
  386. },
  387. close() {
  388. this.$emit('close', false);
  389. },
  390. toggloActive(key) {
  391. if (this.activeKey === key) return
  392. this.currStep = 0
  393. this.keyword = ''
  394. this.resetQuery()
  395. this.$nextTick(async () => {
  396. this.activeKey = key
  397. this.goTop()
  398. if (this.activeKey === 'group') {
  399. const res = await baseStore.getGroupList()
  400. this.userList = res.map((o) => ({
  401. ...o,
  402. id: `${o.id}--group`,
  403. orgNameTree: o.orgNameTree || o.fullName
  404. }));
  405. }
  406. if (this.activeKey === 'role') {
  407. const res = await baseStore.getRoleList()
  408. this.userList = res.map((o) => ({
  409. ...o,
  410. id: `${o.id}--role`,
  411. orgNameTree: o.orgNameTree || o.fullName
  412. }));
  413. return
  414. }
  415. if (this.activeKey === 'system') this.userList = systemFieldList
  416. })
  417. },
  418. async getGroupList() {
  419. const list = await baseStore.getGroupList()
  420. this.list = [{
  421. fullName: '全部用户',
  422. icon: 'icon-ym icon-ym-generator-group1',
  423. id: ''
  424. }, ...list]
  425. },
  426. handleSearch(val) {
  427. this.keyword = val
  428. this.hasPage = !!val
  429. this.currStep = val ? 1 : 0
  430. this.goTop()
  431. if (this.activeKey != 'user') this.activeKey = 'user'
  432. this.$nextTick(() => {
  433. if (this.keyword) {
  434. this.userQuery = {
  435. ...defaultUserQuery
  436. };
  437. this.getGroupList()
  438. this.resetQuery()
  439. this.$u.debounce(this.getUserList, 300)
  440. }
  441. })
  442. },
  443. }
  444. };
  445. </script>