UserInput.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. <template>
  2. <div v-clickoutside="handleClose" @click="handleClose">
  3. <!--已选列表-->
  4. <div v-if="multipleLists.length > 0" class="user-id-multiple">
  5. <div v-for="(item, index) in multipleLists" :key="index" class="user-id-multiple-item">
  6. <Tag @on-close="multipleLists.splice(index,1)" :closable="!existMultipleDisabled(item.username)"><UserView :username="item.username" showimg/></Tag>
  7. </div>
  8. </div>
  9. <!--输入框区域-->
  10. <div class="user-id-input" ref="reference">
  11. <Input v-model="nickName"
  12. :placeholder="placeholder"
  13. :disabled="disabled"
  14. icon="md-search"
  15. @on-click="searchEnter"
  16. @on-enter="searchEnter"
  17. @on-blur="searchEnter(true)"
  18. @on-change="inputChange">
  19. <div v-if="$slots.prepend !== undefined" slot="prepend"><slot name="prepend"></slot></div>
  20. <div v-if="$slots.append !== undefined" slot="append"><slot name="append"></slot></div>
  21. </Input>
  22. <div v-if="userName" class="user-id-subtitle">{{$L('用户名')}}: {{userName}}</div>
  23. <div v-if="spinShow" class="user-id-spin"><div><w-loading></w-loading></div></div>
  24. </div>
  25. <!--弹出选择表-->
  26. <transition name="fade">
  27. <div
  28. v-show="!disabled && visible"
  29. ref="popper"
  30. class="user-id-input-body"
  31. :data-transfer="transfer"
  32. v-transfer-dom>
  33. <div class="user-id-input-table">
  34. <Table highlight-row
  35. v-if="searchShow"
  36. ref="myTable"
  37. size="small"
  38. class="tableSelection"
  39. :style="tableStyle"
  40. :columns="columns"
  41. :data="userLists"
  42. @on-row-click="userChange"
  43. @on-selection-change="userSelect"
  44. :no-data-text="noDataText"></Table>
  45. <div v-if="isConfirm&&searchShow" class="user-id-input-bottom">
  46. <Button type="default" size="small" @click="visible=false">{{$L('取消')}}</Button>
  47. <Button type="primary" size="small" @click="onConfirm">{{$L('确定')}}</Button>
  48. </div>
  49. </div>
  50. </div>
  51. </transition>
  52. </div>
  53. </template>
  54. <style lang="scss" scoped>
  55. .user-id-multiple {
  56. margin-bottom: 6px;
  57. overflow: auto;
  58. white-space: normal;
  59. .ivu-tag {
  60. padding-left: 7px;
  61. }
  62. .user-id-multiple-item {
  63. display: inline-block;
  64. }
  65. .user-view-inline {
  66. height: 20px;
  67. line-height: 20px;
  68. vertical-align: top;
  69. }
  70. }
  71. .user-id-input {
  72. display: inline-block;
  73. width: 100%;
  74. position: relative;
  75. vertical-align: middle;
  76. z-index: 5;
  77. .user-id-subtitle {
  78. position: absolute;
  79. top: 2px;
  80. right: 32px;
  81. height: 30px;
  82. line-height: 30px;
  83. color: #cccccc;
  84. z-index: 2;
  85. }
  86. .user-id-spin {
  87. width: 14px;
  88. height: 14px;
  89. position: absolute;
  90. top: 50%;
  91. right: 34px;
  92. transform: translate(0, -50%);
  93. display: flex;
  94. align-items: center;
  95. > div {
  96. width: 100%;
  97. height: 100%;
  98. }
  99. }
  100. }
  101. </style>
  102. <style lang="scss">
  103. .user-id-input-body {
  104. z-index: 99999
  105. }
  106. .user-id-input-table {
  107. display: flex;
  108. flex-direction: column;
  109. border-radius: 4px;
  110. overflow: hidden;
  111. box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.1);
  112. background-color: #ffffff;
  113. .ivu-table table {
  114. width: 100% !important;
  115. }
  116. .ivu-table:before, .ivu-table:after {
  117. display: none !important;
  118. }
  119. .ivu-table-body {
  120. max-height: 180px;
  121. overflow: auto;
  122. }
  123. .ivu-table-small td {
  124. cursor: pointer;
  125. }
  126. .user-id-input-bottom {
  127. padding: 8px;
  128. display: flex;
  129. align-items: center;
  130. justify-content: flex-end;
  131. > button {
  132. font-size: 13px;
  133. margin-left: 10px;
  134. }
  135. }
  136. }
  137. </style>
  138. <script>
  139. import clickoutside from '../../_modules/directives/clickoutside';
  140. import TransferDom from '../../_modules/directives/transfer-dom';
  141. import Popper from '../../_modules/directives/popper-novalue';
  142. export default {
  143. name: 'UserInput',
  144. directives: {clickoutside, TransferDom},
  145. mixins: [Popper],
  146. props: {
  147. placement: {
  148. default: 'bottom'
  149. },
  150. value: {
  151. default: ''
  152. },
  153. identity: {
  154. default: ''
  155. },
  156. noidentity: {
  157. default: ''
  158. },
  159. nousername: {
  160. default: ''
  161. },
  162. noprojectid: {
  163. default: ''
  164. },
  165. projectid: {
  166. default: ''
  167. },
  168. nobookid: {
  169. default: ''
  170. },
  171. nogroupid: {
  172. default: ''
  173. },
  174. placeholder: {
  175. default: ''
  176. },
  177. disabled: {
  178. type: Boolean,
  179. default: false
  180. },
  181. transfer: {
  182. type: Boolean,
  183. default () {
  184. return true;
  185. }
  186. },
  187. loadstatus: {
  188. default: false
  189. },
  190. multiple: {
  191. type: Boolean,
  192. default: false
  193. },
  194. multipleDisabled: {
  195. default: ''
  196. }
  197. },
  198. data () {
  199. return {
  200. multipleLists: [],
  201. userName: '',
  202. nickName: '',
  203. nickName__: '',
  204. seleName: '',
  205. searchShow: false,
  206. spinShow: false,
  207. skipSearch: false,
  208. isConfirm: false,
  209. tempName: '',
  210. winStyle: {},
  211. columns: [],
  212. userLists: [],
  213. noDataText: '',
  214. }
  215. },
  216. watch: {
  217. value (val) {
  218. if (this.multiple) {
  219. this.multipleLists = this.formatMultipleLists(val);
  220. return;
  221. }
  222. this.userName = $A.cloneData(val)
  223. },
  224. userName (val) {
  225. if (this.skipSearch === true) {
  226. this.skipSearch = false;
  227. }else{
  228. this.nickName = '';
  229. if (val) {
  230. let where = { usernameequal: val };
  231. if (typeof this.identity === "string") {
  232. where['identity'] = this.identity;
  233. }
  234. if (typeof this.noidentity === "string") {
  235. where['noidentity'] = this.noidentity;
  236. }
  237. if (typeof this.nousername === "string") {
  238. where['nousername'] = this.nousername;
  239. }
  240. if (this.noprojectid) {
  241. where['noprojectid'] = this.noprojectid;
  242. }
  243. if (this.projectid) {
  244. where['projectid'] = this.projectid;
  245. }
  246. if (this.nobookid) {
  247. where['nobookid'] = this.nobookid;
  248. }
  249. if (this.nogroupid) {
  250. where['nogroupid'] = this.nogroupid;
  251. }
  252. this.noDataText = this.$L("数据加载中.....");
  253. $A.apiAjax({
  254. url: 'users/searchinfo',
  255. data: {
  256. where: where,
  257. take: 1
  258. },
  259. beforeSend: () => {
  260. this.spinShow = true;
  261. },
  262. complete: () => {
  263. this.spinShow = false;
  264. this.noDataText = this.$L("没有相关的数据");
  265. },
  266. error: () => {
  267. this.noDataText = this.$L("数据加载失败!");
  268. },
  269. success: (res) => {
  270. if (res.ret === 1 && $A.count(res.data) > 0) {
  271. let tmpData = res.data[0];
  272. if (this.multiple) {
  273. this.addMultipleLists(tmpData);
  274. } else {
  275. this.userName = tmpData.username;
  276. this.seleName = tmpData.nickname || tmpData.username;
  277. this.nickName = tmpData.nickname || tmpData.username;
  278. this.nickName__ = tmpData.nickname || tmpData.username;
  279. this.$emit('input', this.userName);
  280. this.$emit('change', tmpData);
  281. }
  282. }
  283. }
  284. });
  285. }
  286. }
  287. },
  288. nickName(val) {
  289. if (val != this.seleName || val == '') {
  290. this.userName = '';
  291. if (!this.multiple) {
  292. this.$emit('input', this.userName);
  293. this.$emit('change', {});
  294. }
  295. }
  296. },
  297. spinShow(val) {
  298. if (typeof this.loadstatus === 'number') {
  299. this.$emit('update:loadstatus', val ? this.loadstatus + 1 : this.loadstatus - 1);
  300. }else if (typeof this.loadstatus === 'boolean') {
  301. this.$emit('update:loadstatus', val);
  302. }
  303. },
  304. searchShow(val) {
  305. if (val) {
  306. this.handleShowPopper();
  307. this.updateMultipleLists();
  308. } else {
  309. this.handleClosePopper();
  310. }
  311. },
  312. multipleLists: {
  313. handler() {
  314. if (this.searchShow) {
  315. this.updateMultipleLists();
  316. }
  317. this.emitMultipleLists();
  318. },
  319. deep: true
  320. }
  321. },
  322. computed: {
  323. tableStyle() {
  324. return this.winStyle;
  325. }
  326. },
  327. methods: {
  328. initLanguage() {
  329. this.columns = [
  330. {
  331. "title": this.$L("头像"),
  332. "width": 60,
  333. "align": 'center',
  334. render: (h, params) => {
  335. return h('UserImg', {
  336. props: {
  337. info: params.row,
  338. },
  339. style: {
  340. width: "26px",
  341. height: "26px",
  342. fontSize: "13px",
  343. lineHeight: "26px",
  344. borderRadius: "50%",
  345. verticalAlign: "middle"
  346. },
  347. });
  348. }
  349. }, {
  350. "title": this.$L("用户名"),
  351. "key": "username",
  352. "minWidth": 80,
  353. "ellipsis": true,
  354. }, {
  355. "title": this.$L("昵称"),
  356. "key": "nickname",
  357. "minWidth": 80,
  358. "ellipsis": true,
  359. render: (h, params) => {
  360. return h('span', params.row.nickname || '-');
  361. }
  362. }
  363. ];
  364. if (this.multiple) {
  365. this.columns.unshift({
  366. type: 'selection',
  367. width: 30,
  368. align: 'center'
  369. });
  370. }
  371. this.noDataText = this.$L("数据加载中.....");
  372. this.isConfirm = this.$listeners['on-confirm'];
  373. },
  374. inputChange() {
  375. const val = this.nickName;
  376. this.spinShow = false;
  377. this.tempName = '';
  378. if (val != '') {
  379. setTimeout(() => {
  380. if (val == this.nickName && val != this.tempName) {
  381. this.searchEnter(false, (res) => {
  382. return val == this.nickName;
  383. });
  384. }
  385. }, 500)
  386. } else {
  387. this.searchShow = false;
  388. }
  389. },
  390. handleShowPopper() {
  391. if (this.timeout) clearTimeout(this.timeout);
  392. this.timeout = setTimeout(() => {
  393. this.visible = true;
  394. }, this.delay);
  395. },
  396. handleClosePopper() {
  397. if (this.timeout) {
  398. clearTimeout(this.timeout);
  399. if (!this.controlled) {
  400. this.timeout = setTimeout(() => {
  401. this.visible = false;
  402. }, 100);
  403. }
  404. }
  405. },
  406. updateStyle() {
  407. this.winStyle = {
  408. width: `${Math.max(this.$el.offsetWidth, 230)}px`,
  409. };
  410. },
  411. emptyAll() {
  412. this.userName = '';
  413. this.nickName = '';
  414. this.nickName__ = '';
  415. this.seleName = '';
  416. this.searchShow = false;
  417. this.spinShow = false;
  418. },
  419. searchEnter(verify, callback) {
  420. if (this.disabled === true) {
  421. return;
  422. }
  423. if (this.spinShow === true) {
  424. return;
  425. }
  426. if (verify === true) {
  427. if (this.nickName === '') {
  428. this.nickName__ = this.nickName;
  429. }
  430. if (this.nickName__ === this.nickName) {
  431. return;
  432. }
  433. }
  434. this.updateStyle();
  435. this.nickName__ = this.nickName;
  436. //
  437. let where = {username: this.nickName};
  438. if (typeof this.identity === "string") {
  439. where['identity'] = this.identity;
  440. }
  441. if (typeof this.noidentity === "string") {
  442. where['noidentity'] = this.noidentity;
  443. }
  444. if (typeof this.nousername === "string") {
  445. where['nousername'] = this.nousername;
  446. }
  447. if (this.noprojectid) {
  448. where['noprojectid'] = this.noprojectid;
  449. }
  450. if (this.projectid) {
  451. where['projectid'] = this.projectid;
  452. }
  453. if (this.nobookid) {
  454. where['nobookid'] = this.nobookid;
  455. }
  456. if (this.nogroupid) {
  457. where['nogroupid'] = this.nogroupid;
  458. }
  459. this.tempName = where.username;
  460. this.noDataText = this.$L("数据加载中.....");
  461. $A.apiAjax({
  462. url: 'users/searchinfo',
  463. data: {
  464. where: where,
  465. take: 30
  466. },
  467. beforeSend: () => {
  468. this.spinShow = true;
  469. },
  470. complete: () => {
  471. this.spinShow = false;
  472. this.noDataText = this.$L("没有相关的数据");
  473. },
  474. error: () => {
  475. this.noDataText = this.$L("数据加载失败!");
  476. },
  477. success: (res) => {
  478. if (typeof callback === "function" && callback(res) === false) {
  479. return;
  480. }
  481. if (res.ret === 1) {
  482. this.userLists = res.data;
  483. this.userLists.forEach((item) => {
  484. if (this.multiple) {
  485. if (this.existMultipleDisabled(item.username)) {
  486. item._disabled = true;
  487. }
  488. } else {
  489. if (item.username == this.userName) {
  490. item._highlight = true;
  491. }
  492. }
  493. });
  494. this.searchShow = true;
  495. } else {
  496. this.$Message.warning(res.msg);
  497. this.emptyAll();
  498. }
  499. }
  500. });
  501. },
  502. userChange(item) {
  503. if (this.multiple) {
  504. if (this.existMultipleDisabled(item.username)) {
  505. return;
  506. }
  507. let tempLists = this.multipleLists.filter(({username}) => username == item.username);
  508. if (tempLists.length > 0) {
  509. this.multipleLists = this.multipleLists.filter(({username}) => username != item.username);
  510. } else {
  511. this.addMultipleLists(item);
  512. }
  513. } else {
  514. this.userName = item.username;
  515. this.seleName = item.nickname || item.username;
  516. this.nickName = item.nickname || item.username;
  517. this.nickName__ = item.nickname || item.username;
  518. this.skipSearch = true;
  519. this.searchShow = false;
  520. this.$emit('input', this.userName);
  521. this.$emit('change', item);
  522. }
  523. },
  524. userSelect() {
  525. if (this.multiple) {
  526. let lists = this.$refs.myTable.objData, item, inThe;
  527. for (let index in lists) {
  528. if (lists.hasOwnProperty(index)) {
  529. item = lists[index];
  530. inThe = this.multipleLists.find(({username}) => username == item.username);
  531. if (item._isChecked) {
  532. !inThe && this.multipleLists.push(item);
  533. } else {
  534. inThe && (this.multipleLists = this.multipleLists.filter(({username}) => username != item.username));
  535. }
  536. }
  537. }
  538. }
  539. },
  540. handleClose(e) {
  541. if (this.multiple && $A(e.target).parents('.user-id-input-table').length > 0) {
  542. return;
  543. }
  544. if (this.searchShow === true) {
  545. this.searchShow = false;
  546. }
  547. },
  548. existMultipleDisabled(username) {
  549. return this.multipleDisabled && $A.strExists(`,${this.multipleDisabled},`, `,${username},`)
  550. },
  551. addMultipleLists(item) {
  552. let inThe = this.multipleLists.find(({username}) => username == item.username);
  553. if (!inThe) {
  554. this.multipleLists.push(item);
  555. }
  556. },
  557. updateMultipleLists() {
  558. this.$nextTick(() => {
  559. let lists = this.$refs.myTable.objData, item, inThe;
  560. for (let index in lists) {
  561. if (lists.hasOwnProperty(index)) {
  562. item = lists[index];
  563. inThe = this.multipleLists.find(({username}) => username == item.username);
  564. this.$set(item, "_isChecked", !!inThe);
  565. }
  566. }
  567. });
  568. },
  569. emitMultipleLists() {
  570. let val = '';
  571. this.multipleLists.forEach((tmp) => {
  572. if (val) {
  573. val+= ",";
  574. }
  575. val+= tmp.username;
  576. });
  577. this.$emit('input', val);
  578. },
  579. formatMultipleLists(val) {
  580. let arr = (val + ",").split(",");
  581. let narr = [];
  582. arr.forEach((uname) => {
  583. if (uname) {
  584. let inn = false;
  585. narr.some((tmp) => {
  586. if (tmp.username == uname) {
  587. return inn = true;
  588. }
  589. })
  590. if (!inn) {
  591. narr.push({
  592. username: uname,
  593. });
  594. }
  595. }
  596. });
  597. return narr;
  598. },
  599. onConfirm(e) {
  600. this.searchShow = false;
  601. this.$emit('on-confirm', e);
  602. }
  603. },
  604. mounted() {
  605. this.updatePopper();
  606. //
  607. if (this.multiple) {
  608. this.multipleLists = this.formatMultipleLists(this.value);
  609. } else if ($A.count(this.value) > 0) {
  610. this.userName = this.value;
  611. }
  612. }
  613. };
  614. </script>