@tripup-company/element-ui-extended 0.0.9 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tripup-company/element-ui-extended",
3
- "version": "0.0.9",
3
+ "version": "0.0.13",
4
4
  "main": "./dist/element-ui-extended.umd.js",
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -13,7 +13,6 @@
13
13
  "axios": "^0.24.0",
14
14
  "core-js": "^3.6.5",
15
15
  "element-ui": "^2.15.6",
16
- "mocha": "^9.1.4",
17
16
  "npe": "^1.2.0",
18
17
  "vue": "^2.6.11",
19
18
  "vue-class-component": "^7.2.3",
@@ -37,6 +36,7 @@
37
36
  "expect": "^27.0.6",
38
37
  "jsdom": "^16.6.0",
39
38
  "jsdom-global": "^3.0.2",
39
+ "mocha": "^9.1.4",
40
40
  "mochapack": "^2.1.2",
41
41
  "mock-local-storage": "^1.1.19",
42
42
  "prettier": "^2.2.1",
package/src/App.vue CHANGED
@@ -1,6 +1,12 @@
1
1
  <template>
2
2
  <div id="app">
3
- <composite-table :storageKey="'Project_name'" :fields="fields" :filters="filters" :filters-top="filtersTop" :repository="repository"></composite-table>
3
+ <composite-table
4
+ :storageKey="'Project_name'"
5
+ :fields="fields"
6
+ :filters="filters"
7
+ :top-filter="topFilter"
8
+ :repository="repository"
9
+ ></composite-table>
4
10
  <img alt="Vue logo" src="./assets/logo.png" />
5
11
  </div>
6
12
  </template>
@@ -9,35 +15,39 @@
9
15
  import { Component, Vue } from "vue-property-decorator";
10
16
  import CompositeTable from "@/components/CompositeTable/index.vue";
11
17
  import filters from "./filters";
12
- import filtersTop from "./filters-top";
18
+ import topFilter from "./topFilter";
13
19
  import repository from "./repositories/contacts-repository";
14
20
  @Component({
15
21
  components: { CompositeTable },
16
22
  data: function () {
17
- let repo = new repository('', '/test', null);
23
+ let repo = new repository("", "/test", null);
18
24
  return {
19
25
  filters: filters,
20
- filtersTop: filtersTop,
26
+ topFilter: topFilter,
21
27
  repository: repo,
22
28
  fields: [
23
- {
24
- prop: 'full_name', label: 'Full Name',
25
- formatter: row => {
26
- return row.first_name + ' ' + row.last_name;
29
+ {
30
+ prop: "full_name",
31
+ label: "Full Name",
32
+ formatter: (row) => {
33
+ return row.first_name + " " + row.last_name;
34
+ },
27
35
  },
28
- },
29
- {
30
- prop: 'email', label: 'Email',
31
- },
32
- {
33
- prop: 'phone', label: 'Phone',
34
- },
35
- {
36
- prop: 'created_at', label: 'Created at',
37
- },
38
- ],
39
- }
40
- }
36
+ {
37
+ prop: "email",
38
+ label: "Email",
39
+ },
40
+ {
41
+ prop: "phone",
42
+ label: "Phone",
43
+ },
44
+ {
45
+ prop: "created_at",
46
+ label: "Created at",
47
+ },
48
+ ],
49
+ };
50
+ },
41
51
  })
42
52
  export default class App extends Vue {}
43
53
  </script>
@@ -1,18 +1,17 @@
1
1
  <template>
2
2
  <div v-loading="loading" class="composite-table">
3
3
  <slot>
4
- <slot name="top-filter" v-bind="{ filtersTop, search }">
5
- <generic-top-filter
6
- v-if="filtersTop.length"
7
- :filters="filtersTop"
8
- :search="search"
9
- @apply:filter="handleApplyFilter"
10
- @reset:filter="handleResetFilter"
11
- />
12
- </slot>
13
-
14
4
  <el-row :gutter="20">
15
5
  <el-col :span="18">
6
+ <slot name="quick-search-filter" v-bind="{ topFilter, search }">
7
+ <quick-search-filter
8
+ v-if="topFilter"
9
+ :filter="topFilter"
10
+ :search="topSearch"
11
+ @apply:filter="handleApplyTopFilter"
12
+ @reset:filter="handleResetTopFilter"
13
+ />
14
+ </slot>
16
15
  <slot name="table" v-bind="{ fields, data }">
17
16
  <generic-table
18
17
  :fields="fields"
@@ -58,10 +57,11 @@ import GenericTable from "@/components/GenericTable";
58
57
  import ListQueryService from "@/services/list-query-service";
59
58
  import { LocalStorageService } from "@/services/local-storage-service";
60
59
  import MetaModel from "@/models/meta-model";
61
- import GenericTopFilter from "@/components/GenericFilters/GenericTopFilter";
60
+ import QuickSearchFilter from "@/components/GenericFilters/QuickSearchFilter";
61
+ import FilterConverter from "../GenericFilters/filter-convertor";
62
62
  export default {
63
63
  components: {
64
- GenericTopFilter,
64
+ QuickSearchFilter,
65
65
  GenericFilter,
66
66
  GenericTable,
67
67
  GenericPaginator,
@@ -82,16 +82,18 @@ export default {
82
82
  default: () => [],
83
83
  type: Array,
84
84
  },
85
- filtersTop: {
86
- default: () => [],
87
- type: Array,
85
+ topFilter: {
86
+ default() {
87
+ return {};
88
+ },
89
+ type: Object,
88
90
  },
89
91
  filters: {
90
92
  default: () => [],
91
93
  type: Array,
92
94
  },
93
95
  includes: {
94
- default: '--',
96
+ default: "--",
95
97
  type: String,
96
98
  },
97
99
  storageKey: {
@@ -105,59 +107,66 @@ export default {
105
107
  this.$route ? this.$route.name : "storage.key",
106
108
  localStorage
107
109
  );
110
+ const filterConvertor = new FilterConverter();
108
111
  let search = {};
112
+ let topSearch = {};
109
113
  for (const column of this.filters) {
110
114
  search[column.name] = {
111
115
  operator: "",
112
116
  value: "",
113
117
  };
114
118
  }
115
- for (const column of this.filtersTop) {
116
- search[column.name] = {
117
- position: "top",
119
+ if (this.topFilter) {
120
+ topSearch[this.topFilter.name] = {
118
121
  value: "",
122
+ operator: this.topFilter.operator,
119
123
  };
120
124
  }
125
+
121
126
  search = Object.assign(search, listQueryService.getListQuery().search);
127
+ topSearch = Object.assign(
128
+ topSearch,
129
+ listQueryService.getListQuery().topSearch
130
+ );
122
131
  return {
123
132
  loading: false,
124
133
  filter: {},
125
134
  data: [],
126
135
  search: search,
127
- meta: Object.assign(
128
- new MetaModel(),
129
- listQueryService.getListQuery().meta
130
- ),
136
+ topSearch: topSearch,
137
+ filterConvertor: filterConvertor,
138
+ meta: listQueryService.getListQuery().meta,
139
+ metaModel: new MetaModel(),
131
140
  listQueryService: listQueryService,
132
141
  };
133
142
  },
134
143
  computed: {
135
144
  queryFilter() {
136
- const meta = this.listQueryService.getListQuery().meta;
137
145
  const query = {
138
- page: this.meta?.pagination?.current_page ? this.meta.pagination.current_page : meta.page,
139
- limit: this.meta?.pagination?.per_page ? this.meta.pagination.per_page : meta.limit,
146
+ page: this.meta.page,
147
+ limit: this.meta.limit,
140
148
  };
141
- return { ...query, ...this.filter };
149
+ const filter = this.filterConvertor.applyFilter({
150
+ ...this.search,
151
+ ...this.topSearch,
152
+ });
153
+ return { ...query, ...filter };
142
154
  },
143
155
  currentPage() {
144
- if (this.data.meta?.pagination) {
145
- return this.data.meta.pagination.current_page;
156
+ if (this.meta) {
157
+ return this.meta.page;
146
158
  }
147
159
  return 0;
148
160
  },
149
161
  total() {
150
- if (this.data.meta?.pagination) {
151
- return this.data.meta.pagination.total;
162
+ if (this.metaModel?.pagination) {
163
+ return this.metaModel.pagination.total;
152
164
  }
153
165
  return 0;
154
166
  },
155
167
  limit() {
156
- if (this.data.meta?.pagination) {
157
- return this.data.meta.pagination.per_page;
158
- }
159
- if (this.listQueryService.getListQuery().meta?.limit) {
160
- return this.listQueryService.getListQuery().meta?.limit;
168
+ if (this.meta) {
169
+ return this.meta.limit;
161
170
  }
162
171
  return 10;
163
172
  },
@@ -169,9 +178,12 @@ export default {
169
178
  async fetch() {
170
179
  this.loading = true;
171
180
  try {
172
- const response = await this.repository.all(this.queryFilter, this.includes);
181
+ const response = await this.repository.all(
182
+ this.queryFilter,
183
+ this.includes
184
+ );
173
185
  this.data = response.data;
174
- Vue.set(this.data, "meta", response.meta);
186
+ Vue.set(this, "metaModel", response.meta);
175
187
  } catch (e) {
176
188
  this.$message.error(e.message);
177
189
  }
@@ -188,7 +200,7 @@ export default {
188
200
  page: this.listQueryService.getListQuery().meta.page,
189
201
  },
190
202
  });
191
- this.meta.pagination.per_page = val;
203
+ Vue.set(this.meta, "limit", val);
192
204
  this.fetch();
193
205
  },
194
206
  handleCurrentChange(val) {
@@ -198,34 +210,39 @@ export default {
198
210
  limit: this.listQueryService.getListQuery().meta.limit,
199
211
  },
200
212
  });
201
- this.data.meta.pagination.current_page = val;
213
+ Vue.set(this.meta, "page", val);
202
214
  this.fetch();
203
215
  },
204
- handleFilterChange(val) {
205
- this.filter = val;
206
- this.data.meta.pagination.current_page = 1;
207
- this.fetch();
216
+ handleFilterChange() {
217
+ this.handleCurrentChange(1);
208
218
  },
209
- handleApplyFilter(filters) {
219
+ handleApplyFilter() {
210
220
  this.listQueryService.updateListQuery({ search: this.search });
211
- this.handleFilterChange(filters);
221
+ this.handleFilterChange();
212
222
  },
213
223
  handleResetFilter() {
214
224
  for (const column in this.search) {
215
- if (this.search[column].position === "top") {
216
- this.search[column] = Object.assign(this.search[column], {
217
- value: "",
218
- });
219
- } else {
220
- this.search[column] = Object.assign(this.search[column], {
221
- operator: "",
222
- value: "",
223
- });
224
- }
225
+ this.search[column] = Object.assign(this.search[column], {
226
+ operator: "",
227
+ value: "",
228
+ });
225
229
  }
226
230
  this.listQueryService.updateListQuery({ search: {} });
227
231
  this.handleFilterChange({});
228
232
  },
233
+ handleApplyTopFilter() {
234
+ this.listQueryService.updateListQuery({ topSearch: this.topSearch });
235
+ this.handleFilterChange();
236
+ },
237
+ handleResetTopFilter() {
238
+ for (const column in this.topSearch) {
239
+ this.topSearch[column] = Object.assign(this.topSearch[column], {
240
+ value: "",
241
+ });
242
+ }
243
+ this.listQueryService.updateListQuery({ topSearch: {} });
244
+ this.handleFilterChange({});
245
+ },
229
246
  },
230
247
  };
231
248
  </script>
@@ -2,28 +2,66 @@
2
2
  <el-row class="m-b-10">
3
3
  <el-row>
4
4
  <el-col :lg="4" :md="4" :sm="4" :xs="4">
5
- <el-checkbox v-model="showFilter"><span>{{ column.label }}</span></el-checkbox>
5
+ <el-checkbox v-model="showFilter"
6
+ ><span>{{ column.label }}</span></el-checkbox
7
+ >
6
8
  </el-col>
7
9
  </el-row>
8
10
  <el-row v-if="showFilter" class="m-t-5">
9
- <el-col :span="24" class="p-b-10">
11
+ <el-col v-if="operators.length !== 1" :span="24" class="p-b-10">
10
12
  <el-select v-model="search.operator" placeholder="Select">
11
- <el-option v-for="(operator) in column.type==='string' ? stringOperators : enumOperators" :key="operator.value" :value="operator.value" :label="operator.label" />
13
+ <el-option
14
+ v-for="operator in operators"
15
+ :key="operator.value"
16
+ :label="operator.label"
17
+ :value="operator.value"
18
+ />
12
19
  </el-select>
13
20
  </el-col>
14
21
  <el-col :span="24">
15
- <el-input v-if="column.type==='string'" v-show="!isDisableInput(search.operator)" v-model="search.value" />
16
- <el-select v-else v-model="search.value" placeholder="Select" :multiple="isMultiple">
17
- <el-option v-for="(label, value) in (column.type==='enum' ? column.values : boolValues)" :key="value" :value="value" :label="label" />
22
+ <el-input
23
+ v-if="column.type === 'string'"
24
+ v-show="!isDisableInput(search.operator)"
25
+ v-model="search.value"
26
+ />
27
+ <el-date-picker
28
+ v-else-if="column.type === 'date'"
29
+ v-model="search.value"
30
+ class="w-100"
31
+ end-placeholder="To"
32
+ format="dd.MM.yy"
33
+ placeholder="Pick a day"
34
+ range-separator="-"
35
+ start-placeholder="From"
36
+ type="daterange"
37
+ value-format="yyyy-MM-dd"
38
+ />
39
+ <el-select
40
+ refs="select"
41
+ v-else
42
+ v-model="search.value"
43
+ :multiple="isMultiple"
44
+ placeholder="Select"
45
+ :filterable="filterable"
46
+ :remote="remote"
47
+ :remote-method="remoteMethodHandler"
48
+ :loading="loading"
49
+ clearable
50
+ @change="updateSearch"
51
+ >
52
+ <el-option
53
+ v-for="item in selectListLocal"
54
+ :key="item[selectKey]"
55
+ :label="item[selectLabel]"
56
+ :value="item[selectValue]"
57
+ />
18
58
  </el-select>
19
59
  </el-col>
20
60
  </el-row>
21
61
  </el-row>
22
62
  </template>
23
-
24
63
  <script>
25
64
  export default {
26
- name: "FilterRow",
27
65
  props: {
28
66
  column: {
29
67
  type: Object,
@@ -38,67 +76,123 @@ export default {
38
76
  },
39
77
  },
40
78
  },
41
- computed: {
42
- isMultiple() {
43
- return this.column.is_multiple ? this.column.is_multiple : false;
44
- },
45
- },
46
79
  data() {
47
80
  return {
48
- showFilter: this.search.operator !== '',
81
+ loading: false,
82
+ selectKey: this.column.selectKey ?? "key",
83
+ selectLabel: this.column.selectLabel ?? "label",
84
+ selectValue: this.column.selectValue ?? "key",
85
+ filterable: this.column.filterable ?? false,
86
+ remote: this.column.remote ?? false,
87
+ remoteMethod: this.column.remoteMethod ?? null,
88
+ selectListLocal: this.search.selectList
89
+ ? this.search.selectList
90
+ : this.column.type === "enum"
91
+ ? this.column.values
92
+ : this.boolValues,
93
+ showFilter: this.search.operator !== "",
49
94
  stringOperators: [
50
- { 'value': 'equal', 'label': 'is equal' },
51
- { 'value': 'not_equal', 'label': 'is not equal' },
52
- { 'value': 'contains', 'label': 'contains' },
53
- { 'value': 'not_contains', 'label': 'doesn’t contain' },
54
- { 'value': 'is_null', 'label': 'is empty' },
55
- { 'value': 'is_not_null', 'label': 'is not empty' },
56
- { 'value': 'start_with', 'label': 'begins with' },
57
- { 'value': 'end_with', 'label': 'ends with' },
58
- ],
59
- disabledInputOperations: [
60
- 'is_null',
61
- 'is_not_null',
62
- ],
63
- enumOperators: [
64
- { 'value': 'equal', 'label': 'is' },
65
- { 'value': 'in', 'label': 'in' },
95
+ { value: "equal", label: "is equal" },
96
+ { value: "not_equal", label: "is not equal" },
97
+ { value: "contains", label: "contains" },
98
+ { value: "not_contains", label: "doesn’t contain" },
99
+ { value: "is_null", label: "is empty" },
100
+ { value: "is_not_null", label: "is not empty" },
101
+ { value: "start_with", label: "begins with" },
102
+ { value: "end_with", label: "ends with" },
66
103
  ],
104
+ disabledInputOperations: ["is_null", "is_not_null"],
105
+ enumOperators: [{ value: "equal", label: "is" }],
106
+ enumOperatorsMultiple: [{ value: "in", label: "in" }],
107
+ dateOperators: [{ value: "between", label: "between" }],
67
108
  boolValues: {
68
- null: 'empty',
69
- true: 'true',
70
- false: 'false',
109
+ null: "empty",
110
+ true: "true",
111
+ false: "false",
71
112
  },
72
113
  };
73
114
  },
115
+ computed: {
116
+ operators() {
117
+ let operators = [];
118
+ switch (this.column.type) {
119
+ case "string":
120
+ operators = this.stringOperators;
121
+ break;
122
+ case "enum":
123
+ if (this.column.is_multiple) {
124
+ operators = this.enumOperatorsMultiple;
125
+ } else {
126
+ operators = this.enumOperators;
127
+ }
128
+ break;
129
+ case "date":
130
+ operators = this.dateOperators;
131
+ break;
132
+ }
133
+ return operators;
134
+ },
135
+ isMultiple() {
136
+ return this.column.is_multiple ? this.column.is_multiple : false;
137
+ },
138
+ remoteMethodHandler: {
139
+ get() {
140
+ if (this.remoteMethod !== null) {
141
+ return this.remoteCall;
142
+ }
143
+ return null;
144
+ },
145
+ },
146
+ },
74
147
  watch: {
75
148
  showFilter() {
76
149
  if (this.showFilter === false) {
77
- this.search.operator = '';
78
- this.search.value = '';
150
+ this.search.operator = "";
151
+ this.search.value = "";
152
+ } else if (this.search.operator === "" && this.operators.length === 1) {
153
+ this.search.operator = this.operators[0].value;
79
154
  }
80
155
  },
81
- search: {
82
- handler: function(newValue) {
83
- if (newValue.operator === '') {
84
- this.showFilter = false;
85
- }
86
- },
87
- deep: true,
156
+ search() {
157
+ if (this.search.operator === "") {
158
+ this.showFilter = false;
159
+ }
88
160
  },
89
161
  },
162
+ mounted() {
163
+ if (this.operators.length === 1) {
164
+ // this.search.operator = this.operators[0].value;
165
+ }
166
+ },
90
167
  methods: {
91
168
  isDisableInput(operator) {
92
- const disable = this.disabledInputOperations.indexOf(operator) !== -1;
169
+ const disable = this.disabledInputOperations.includes(operator);
93
170
  if (disable) {
94
- this.search.value = '';
171
+ this.search.value = "";
95
172
  }
96
173
  return disable;
97
174
  },
175
+ async remoteCall(query) {
176
+ this.loading = true;
177
+ const response = await this.remoteMethod.all({
178
+ search: this.selectLabel + ":" + query,
179
+ searchFields: this.selectLabel + ":like",
180
+ });
181
+ this.selectListLocal = response.data;
182
+ this.search["selectList"] = response.data;
183
+ this.loading = false;
184
+ },
185
+ updateSearch(key) {
186
+ if (key && this.remote) {
187
+ const obj = { [this.selectKey]: key };
188
+ for (const item of this.selectListLocal) {
189
+ if (item[this.selectKey] === key) {
190
+ obj[this.selectLabel] = item[this.selectLabel];
191
+ this.search.selectList = [obj];
192
+ }
193
+ }
194
+ }
195
+ },
98
196
  },
99
- }
197
+ };
100
198
  </script>
101
-
102
- <style scoped>
103
-
104
- </style>
@@ -0,0 +1,42 @@
1
+ <template>
2
+ <el-row>
3
+ <el-col v-if="filter">
4
+ <el-input
5
+ v-if="filter.type === 'string'"
6
+ v-model="search[filter.name].value"
7
+ clearable
8
+ @clear="resetFilter"
9
+ @keyup.enter.native="applyFilter"
10
+ class="m-b-20"
11
+ />
12
+ </el-col>
13
+ </el-row>
14
+ </template>
15
+
16
+ <script>
17
+ export default {
18
+ name: "QuickSearchFilter",
19
+ props: {
20
+ filter: {
21
+ type: Object,
22
+ default: () => {
23
+ return {};
24
+ },
25
+ },
26
+ search: {
27
+ type: Object,
28
+ default: () => {
29
+ return {};
30
+ },
31
+ },
32
+ },
33
+ methods: {
34
+ applyFilter() {
35
+ this.$emit("apply:filter");
36
+ },
37
+ resetFilter() {
38
+ this.$emit("reset:filter");
39
+ },
40
+ },
41
+ };
42
+ </script>
@@ -42,7 +42,6 @@ export default {
42
42
  },
43
43
  methods: {
44
44
  pageChange (val) {
45
- debugger;
46
45
  this.$emit('page:change', val);
47
46
  },
48
47
  sizeChange(val) {
@@ -3,14 +3,36 @@
3
3
  <el-table :data="data" border @sort-change="sortChange">
4
4
  <template v-for="field in fields">
5
5
  <template v-if="field.callback">
6
- <el-table-column :key="field.prop" v-slot="context" :prop="field.prop" :label="field.label" :width="field.width ? field.width : null" sortable="custom">
7
- <el-link @click="field.callback(field.prop, context.$index, context.row)">
8
- {{ typeof field.formatter === 'function' ? field.formatter(context.row) : context.row[field.prop] }}
6
+ <el-table-column
7
+ :key="field.prop"
8
+ v-slot="context"
9
+ :prop="field.prop"
10
+ :label="field.label"
11
+ :width="field.width ? field.width : null"
12
+ :sortable="field.sortable ? field.sortable : null"
13
+ >
14
+ <el-link
15
+ @click="field.callback(field.prop, context.$index, context.row)"
16
+ >
17
+ {{
18
+ typeof field.formatter === "function"
19
+ ? field.formatter(context.row)
20
+ : context.row[field.prop]
21
+ }}
9
22
  </el-link>
10
23
  </el-table-column>
11
24
  </template>
12
25
  <template v-else>
13
- <el-table-column :formatter="typeof field.formatter === 'function' ? field.formatter : null" :key="field.prop" :prop="field.prop" :label="field.label" :width="field.width ? field.width : null" sortable="custom" />
26
+ <el-table-column
27
+ :formatter="
28
+ typeof field.formatter === 'function' ? field.formatter : null
29
+ "
30
+ :key="field.prop"
31
+ :prop="field.prop"
32
+ :label="field.label"
33
+ :width="field.width ? field.width : null"
34
+ :sortable="field.sortable ? field.sortable : null"
35
+ />
14
36
  </template>
15
37
  </template>
16
38
  </el-table>
@@ -18,7 +40,6 @@
18
40
  </template>
19
41
 
20
42
  <script>
21
-
22
43
  export default {
23
44
  name: "GenericTable",
24
45
  props: {
@@ -44,8 +65,8 @@ export default {
44
65
  },
45
66
  methods: {
46
67
  sortChange(data) {
47
- this.$emit('sort:change', data);
48
- }
49
- }
68
+ this.$emit("sort:change", data);
69
+ },
70
+ },
50
71
  };
51
72
  </script>