Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

listView.vue 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /* eslint-disable no-restricted-syntax */ /* eslint-disable guard-for-in */
  2. <template>
  3. <div>
  4. <div style="height:5px"></div>
  5. <div class="d-flex justify-content-between table-title">
  6. <div class="p-2" v-if="!hideSearch">
  7. <input v-model="searchItem" class="form-control" placeholder="Search...." />
  8. </div>
  9. <div class="p-2" v-if="title">
  10. <h2>{{ title }}</h2>
  11. </div>
  12. <div class="p-2">
  13. <div class="d-flex flex-row">
  14. <div class="p2" v-if="showColumnChooser">
  15. <div
  16. class="btn btn-primary myBackground btn-width cursor-pointer"
  17. data-toggle="modal"
  18. data-target="#myModal"
  19. >Column Chooser</div>
  20. <div class="col-md-12">
  21. <div id="myModal" class="modal fade" role="dialog">
  22. <div class="modal-dialog modal-lg">
  23. <!-- Modal content-->
  24. <div class="modal-content">
  25. <div class="modal-header">
  26. <h5>Column Chooser</h5>
  27. <button type="button" class="close" data-dismiss="modal">&times;</button>
  28. </div>
  29. <div style="margin-left:50px; margin-right:50px;margin-bottom:50px;">
  30. <ListViewControl :items="allColumn" @checkItem="checkItem" />
  31. </div>
  32. </div>
  33. </div>
  34. </div>
  35. </div>
  36. </div>
  37. <div class="p2" v-if="selectedItems.length > 0">
  38. <div
  39. class="btn btn-primary myBackground btn-width cursor-pointer"
  40. @click="onClearSelected()"
  41. >Clear Selected</div>
  42. </div>
  43. <div class="p2" v-if="showNew">
  44. <div class="btn btn-primary myBackground btn-width cursor-pointer" @click="onNew()">New</div>
  45. </div>
  46. </div>
  47. </div>
  48. </div>
  49. <div style="height:5px"></div>
  50. <div v-if="items && items.length > 0" class="table-responsive">
  51. <table
  52. id="table"
  53. :class="{ 'table table-hover': 1 === 1, 'table-sm': compact, 'table-bordered': bordered }"
  54. >
  55. <thead>
  56. <tr class="dnd-moved">
  57. <th v-for="(column, c) in Columns" :key="c">
  58. <div
  59. @click="sortBy(column)"
  60. @mouseover="hover = c"
  61. @mouseleave="hover = -1"
  62. :class="{ active: hover === c }"
  63. >
  64. <div class="d-flex bd-highlight">
  65. <div
  66. v-if="displayHeaders.length === 0"
  67. class="p-2 w-100 bd-highlight"
  68. >{{ column | toProper }}</div>
  69. <div
  70. v-else
  71. class="p-2 w-100 bd-highlight"
  72. >{{ displayHeaders[c] !== "" ? displayHeaders[c] : column | toProper }}</div>
  73. <div class="p-2 flex-shrink-1 bd-highlight">
  74. <img
  75. src="../../../public/img/sort-up.png"
  76. height="8px;"
  77. v-if="sortKey === column && reverse"
  78. />
  79. <img
  80. src="../../../public/img/sort-down.png"
  81. height="8px;"
  82. v-if="sortKey === column && !reverse"
  83. />
  84. </div>
  85. </div>
  86. </div>
  87. </th>
  88. <th v-if="showCustomAction"></th>
  89. <th v-if="editable"></th>
  90. <th v-if="deleteable"></th>
  91. </tr>
  92. </thead>
  93. <tbody class="my-table">
  94. <tr
  95. v-for="(item, i) in DisplayItems"
  96. :key="i"
  97. @click="onRowClick(item, i)"
  98. :class="{ selected: isSelected(i), 'cursor-pointer': allowSelect }"
  99. >
  100. <td v-for="(column, c) in Columns" :key="c">
  101. <div
  102. v-if="displayFormats.length === 0"
  103. >{{ isObject(item[column]) ? item[column].display : item[column] }}</div>
  104. <div v-else-if="displayFormats.length > 0 && displayFormats[c] === 'date'">
  105. <div
  106. v-if="item[column] !== '0001-01-01T00:00:00'"
  107. >{{ isObject(item[column]) ? item[column].display : item[column] | toDate }}</div>
  108. </div>
  109. <div
  110. v-else-if="displayFormats.length > 0 && displayFormats[c] === 'money'"
  111. >{{ isObject(item[column]) ? item[column].display : item[column] | toCurrency }}</div>
  112. <div v-else-if="displayFormats.length > 0 && displayFormats[c] === 'image'">
  113. <img :src="item[column]" style="height:100px; width:100px; object-fit: cover;" />
  114. </div>
  115. <div v-else>{{ isObject(item[column]) ? item[column].display : item[column] }}</div>
  116. </td>
  117. <td v-if="showCustomAction" class="my-width">
  118. <button
  119. type="button"
  120. class="btn my-btn"
  121. @click="onCustomClick(item)"
  122. >{{ CustomActionHeading }}</button>
  123. </td>
  124. <td v-if="editable" class="my-width">
  125. <button type="button" class="btn my-btn" @click="onEdit(item)">Edit</button>
  126. </td>
  127. <td v-if="deleteable" class="my-width">
  128. <button type="button" class="btn my-btn" @click="onDelete(item)">Delete</button>
  129. </td>
  130. </tr>
  131. </tbody>
  132. </table>
  133. <div class="d-flex justify-content-between" v-if="showPager">
  134. <div class="p-1">
  135. {{
  136. currentPage +
  137. " / " +
  138. PageCount +
  139. (!hideItemCount ? " - (" + FilteredItems.length + " items)" : "")
  140. }}
  141. </div>
  142. <div class="p-1">
  143. <BasePagination
  144. :currentPage="currentPage"
  145. :pageCount="PageCount"
  146. @nextPage="pageChangeHandle('next')"
  147. @previousPage="pageChangeHandle('previous')"
  148. @loadPage="pageChangeHandle"
  149. />
  150. </div>
  151. <div class="p-2">
  152. <div class="d-flex flex-row">
  153. <div>
  154. <select
  155. class="form-control"
  156. v-model="visibleItemsPerPageCount"
  157. @change="onChangeItemsPerPage()"
  158. >
  159. <option v-for="(item, i) in itemsPerPageList" :key="i">{{ item }}</option>
  160. </select>
  161. </div>
  162. </div>
  163. </div>
  164. </div>
  165. </div>
  166. <div v-else>
  167. <Alert :text="'No items found ^-^'" :type="'INFO'" />
  168. </div>
  169. </div>
  170. </template>
  171. <script>
  172. import _ from "lodash";
  173. import ItemsPerPageList from "../../assets/staticData/itemsPerPage";
  174. import BasePagination from "./basePagination.vue";
  175. import Alert from "./alert.vue";
  176. import ListViewControl from "./listViewControl.vue";
  177. export default {
  178. components: {
  179. BasePagination,
  180. Alert,
  181. ListViewControl
  182. },
  183. mounted() {
  184. try {
  185. // to assign initial value to itemsPerPage
  186. if (this.itemsPerPageList && this.itemsPerPageList.length > 0) {
  187. const [startItem] = this.itemsPerPageList;
  188. this.visibleItemsPerPageCount = startItem;
  189. }
  190. } catch (error) {
  191. throw error;
  192. }
  193. this.getInitColumn();
  194. },
  195. props: {
  196. compact: {
  197. default: true
  198. },
  199. allowSelect: {
  200. default: true
  201. },
  202. allowMultipleSelect: {
  203. default: false
  204. },
  205. hideSearch: {
  206. default: false
  207. },
  208. showNew: {
  209. default: true
  210. },
  211. items: undefined,
  212. editable: {
  213. default: false
  214. },
  215. deleteable: {
  216. default: false
  217. },
  218. columnCount: {
  219. default: 6
  220. },
  221. showPager: {
  222. default: true
  223. },
  224. title: {
  225. default: undefined
  226. },
  227. sortKey: {
  228. default: "id"
  229. },
  230. hideItemCount: {
  231. default: false
  232. },
  233. currentPage: {
  234. default: 1
  235. },
  236. bordered: {
  237. default: true
  238. },
  239. striped: {
  240. default: true
  241. },
  242. showColumnChooser: {
  243. default: true
  244. },
  245. displayColumns: {
  246. default: []
  247. },
  248. displayFormats: {
  249. default: []
  250. },
  251. displayHeaders: {
  252. default: []
  253. },
  254. showCustomAction: {
  255. default: false
  256. },
  257. CustomActionHeading: {
  258. default: ""
  259. },
  260. CustomActionCondition: {
  261. default: ""
  262. }
  263. },
  264. data() {
  265. return {
  266. hover: -1,
  267. selectedItems: [],
  268. showControl: false,
  269. sortKey: "",
  270. reverse: false,
  271. searchItem: "",
  272. visibleItemsPerPageCount: 20,
  273. itemsPerPageList: ItemsPerPageList,
  274. visibleColumn: [],
  275. allColumn: []
  276. };
  277. },
  278. methods: {
  279. checkItem(column, show) {
  280. const list = [];
  281. for (const i in this.allColumn) {
  282. const item = this.allColumn[i];
  283. if (item && item.column === column) {
  284. item.show = show;
  285. }
  286. list.push(item);
  287. }
  288. this.allColumn = list;
  289. },
  290. getInitColumn() {
  291. const list = [];
  292. const listAll = [];
  293. if (this.items) {
  294. for (const i in Object.keys(this.items)) {
  295. const item = this.items[i];
  296. for (const o in Object.keys(item)) {
  297. if (
  298. !listAll.includes(Object.keys(item)[o]) &&
  299. !Array.isArray(Object.values(item)[o])
  300. ) {
  301. const columnName = Object.keys(item)[o];
  302. if (!listAll.some(x => x.column === columnName)) {
  303. listAll.push({
  304. column: columnName,
  305. show: _.filter(listAll, x => x.show).length < this.columnCount
  306. });
  307. }
  308. }
  309. }
  310. }
  311. }
  312. this.allColumn = listAll;
  313. },
  314. onClearSelected() {
  315. this.selectedItems = [];
  316. this.$emit("onClearSelected");
  317. },
  318. isSelected(i) {
  319. const ind = this.getActualIndex(i);
  320. return _.some(this.selectedItems, x => x === ind);
  321. },
  322. onNew() {
  323. this.$emit("onNew");
  324. },
  325. isObject(item) {
  326. return !!item && item.constructor === Object;
  327. },
  328. isDate(item) {
  329. return !!item && item.constructor === Date;
  330. },
  331. isDecimal(item) {
  332. if (!!item && item.constructor === Number && item.indexOf(".") > 0) {
  333. return true;
  334. }
  335. return false;
  336. },
  337. isImage(item) {
  338. return (
  339. !!item &&
  340. item.constructor === String &&
  341. item.length > 9 &&
  342. item.substring(0, 9) === "data:image"
  343. );
  344. },
  345. onEdit(item) {
  346. this.$emit("onEdit", item);
  347. },
  348. onDelete(item) {
  349. this.$emit("onDelete", item);
  350. },
  351. onCustomClick(item) {
  352. this.$emit("onCustomClick", item);
  353. },
  354. onRowClick(item, i) {
  355. const ind = this.getActualIndex(i);
  356. if (_.some(this.selectedItems, x => x === ind)) {
  357. this.selectedItems = this.selectedItems.filter(x => x !== ind);
  358. } else {
  359. if (!this.allowMultipleSelect) {
  360. this.selectedItems = [];
  361. }
  362. this.selectedItems.push(item);
  363. }
  364. this.$emit("onRowClick", this.selectedItems);
  365. },
  366. getActualIndex(index) {
  367. return (this.currentPage - 1) * this.visibleItemsPerPageCount + index;
  368. },
  369. changeColumn(title, checked) {
  370. if (checked) {
  371. this.myColumns.push(title);
  372. } else {
  373. const ind = this.myColumns.indexOf(title);
  374. if (ind > -1) {
  375. this.myColumns.splice(ind, 1);
  376. }
  377. }
  378. },
  379. onControlVisibilityChange() {
  380. this.showControl = !this.showControl;
  381. },
  382. sortBy(sortKey) {
  383. this.reverse = this.sortKey === sortKey ? !this.reverse : false;
  384. this.sortKey = sortKey;
  385. },
  386. async pageChangeHandle(value) {
  387. console.log(value);
  388. switch (value) {
  389. case "next":
  390. this.currentPage += 1;
  391. break;
  392. case "previous":
  393. this.currentPage -= 1;
  394. break;
  395. default:
  396. this.currentPage = value;
  397. }
  398. },
  399. onChangeItemsPerPage() {
  400. if (this.currentPage !== 1) {
  401. this.currentPage = 1;
  402. }
  403. },
  404. inArray(array, value) {
  405. const lenght = array.length;
  406. for (let i = 0; i < lenght; i++) {
  407. if (array[i] === value) return true;
  408. }
  409. return false;
  410. }
  411. },
  412. computed: {
  413. ListWidth() {
  414. if (this.showControl) {
  415. return "col-md-9";
  416. }
  417. return "col-md-12";
  418. },
  419. SortDirection() {
  420. return this.reverse ? "desc" : "asc";
  421. },
  422. PageCount() {
  423. return this.visibleItemsPerPageCount !== 0
  424. ? Math.ceil(this.FilteredItems.length / this.visibleItemsPerPageCount)
  425. : 1;
  426. },
  427. Columns() {
  428. const listColumns = [];
  429. if (!this.allColumn || this.allColumn.length === 0) {
  430. this.getInitColumn();
  431. }
  432. if (this.displayColumns.length > 0) {
  433. for (let i = 0; i < this.displayColumns.length; i++) {
  434. listColumns.push(this.displayColumns[i]);
  435. }
  436. } else {
  437. const list = _.filter(this.allColumn, x => x.show);
  438. for (const i in list) {
  439. const item = list[i];
  440. if (item) {
  441. listColumns.push(item.column);
  442. }
  443. }
  444. }
  445. return listColumns;
  446. },
  447. AllColumns() {
  448. const list = [];
  449. if (this.items) {
  450. }
  451. return list;
  452. },
  453. FilteredItems() {
  454. const list = _.filter(this.items, item =>
  455. Object.values(item).some(
  456. i =>
  457. JSON.stringify(i)
  458. .toLowerCase()
  459. .indexOf(this.searchItem.toLowerCase()) > -1
  460. )
  461. );
  462. return _.orderBy(list, this.sortKey, this.SortDirection);
  463. },
  464. DisplayItems() {
  465. const list = this.FilteredItems;
  466. const startSlice = (this.currentPage - 1) * this.visibleItemsPerPageCount;
  467. let endSlice = this.currentPage * this.visibleItemsPerPageCount;
  468. if (endSlice > list.length) {
  469. endSlice = list.length;
  470. }
  471. return list.slice(startSlice, endSlice);
  472. },
  473. GetAllColumn() {}
  474. }
  475. };
  476. </script>
  477. <style scoped>
  478. th[draggable] a,
  479. th[draggable] {
  480. cursor: move;
  481. }
  482. th[draggable] a:hover,
  483. th[draggable] a {
  484. display: block;
  485. text-decoration: none;
  486. color: #333333;
  487. }
  488. .table-striped > tbody > tr:nth-child(2n + 1) > td,
  489. .table-striped > tbody > tr:nth-child(2n + 1) > th {
  490. background-color: rgba(225, 225, 225, 0.8);
  491. }
  492. .active {
  493. background-color: rgba(255, 255, 255, 0.5);
  494. cursor: pointer;
  495. }
  496. .table > tbody > tr > td {
  497. vertical-align: middle;
  498. }
  499. .my-width {
  500. width: 20px;
  501. }
  502. .drag {
  503. background-color: rgba(0, 255, 0, 0.35);
  504. opacity: 0.25;
  505. }
  506. .dnd-drag {
  507. opacity: 0.25;
  508. }
  509. .over {
  510. background-color: rgba(0, 0, 255, 0.35);
  511. }
  512. .my-border {
  513. border: solid 3px silver;
  514. }
  515. .selected {
  516. background-color: rgba(96, 203, 235, 0.5);
  517. border: white 2px double;
  518. }
  519. .selected:hover {
  520. background-color: rgba(96, 203, 235, 0.85);
  521. }
  522. .btn-width {
  523. width: 125px;
  524. }
  525. .table-title {
  526. padding: 5px;
  527. border: rgba(200, 200, 200, 0.66) double 2px;
  528. border-radius: 5px;
  529. background-color: rgba(96, 203, 235, 0.25);
  530. }
  531. .table-title:hover {
  532. background-color: rgba(96, 203, 235, 0.4);
  533. }
  534. .table-header {
  535. background-color: rgba(200, 200, 200, 0.66);
  536. }
  537. .my-table {
  538. border: rgba(150, 150, 150, 0.75) 3px double;
  539. }
  540. </style>