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

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