UserInput.vue 20 KB

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