wkjp-list-page 1.0.7 → 1.0.9
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/README.md
CHANGED
|
@@ -55,8 +55,8 @@ Vue.use(CommonListPage);
|
|
|
55
55
|
// data 示例(命名仅作示例,按你页面改)
|
|
56
56
|
deviceListFilters: { keyword: "", status: "" },
|
|
57
57
|
deviceListSearchOption: [
|
|
58
|
-
{ type: "input", prop: "keyword", label: "关键词"
|
|
59
|
-
{ type: "select", prop: "status", label: "状态", options: [{ label: "全部", value: "" }], autoQuery:
|
|
58
|
+
{ type: "input", prop: "keyword", label: "关键词" },
|
|
59
|
+
{ type: "select", prop: "status", label: "状态", options: [{ label: "全部", value: "" }], autoQuery: true }
|
|
60
60
|
],
|
|
61
61
|
deviceListColumns: [
|
|
62
62
|
{ type: "index", label: "#", width: 55 },
|
|
@@ -275,6 +275,7 @@ deviceListTransformList(list) {
|
|
|
275
275
|
| `exportMaxLimit` | `null` | 若设置:当前列表 `total` 超过该值则提示不导出。 |
|
|
276
276
|
| `tableAttrs` | `{}` | 透传 `el-table`。 |
|
|
277
277
|
| `tableHeaderBg` | `#e5e9f2` | 表头背景色。 |
|
|
278
|
+
| `cellEmptyText` | `--` | 非 slot 列空值占位;列上可用 `emptyText` / `defaultText` 单独覆盖。 |
|
|
278
279
|
| `showQueryButton` | `true` | 是否显示查询按钮。 |
|
|
279
280
|
| `queryButtonText` | `查询` | 查询按钮文案。 |
|
|
280
281
|
| `showClearQueryButton` | `true` | 是否显示清除查询。 |
|
|
@@ -294,12 +295,12 @@ deviceListTransformList(list) {
|
|
|
294
295
|
| `prop` | 对应 `filters[prop]`(`slot` 可省略)。 |
|
|
295
296
|
| `label` / `placeholder` | 浮动标签,优先 `label`。 |
|
|
296
297
|
| `width` | 宽度(数字,内部会加 `px`)。 |
|
|
297
|
-
| `autoQuery` | 默认 `
|
|
298
|
+
| `autoQuery` | 默认 `false`;为 `true` 时选择/日期等变更后自动 `fetchList`(重置第 1 页)。 |
|
|
298
299
|
| `onChange` | `(val) => void`。 |
|
|
299
|
-
| `input` | `maxlength`、`height
|
|
300
|
+
| `input` | **`clearable` 默认 `true`**(有内容时出现清除);`false` 关闭。另有 `maxlength`、`height`;回车触发 `query` 事件(不自动 `fetchList`);其它类型若设 `autoQuery: true` 则变更后自动查。 |
|
|
300
301
|
| `select` | `options`(平铺选项)、`multiple`、`clearable`(默认 true)、`filterable`、`disabled`。 |
|
|
301
302
|
| `cascader` | **`options`** 为级联树;**`props` 或 `cascaderProps`** 为 `el-cascader` 的字段映射;`filters[prop]` 为 **路径数组**;可选 `filterable`、`collapseTags`、`showAllLevels`、`separator`、`debounce`、`height`。 |
|
|
302
|
-
| 日期范围 | `valueFormat
|
|
303
|
+
| 日期范围 | **`daterange` 未传 `valueFormat` 时默认 `yyyy-MM-dd`**(`date` / `month` / `year` / `datetime` 等同理,与 Element 约定不符时请自行传 `valueFormat`)。**`startPlaceholder` / `endPlaceholder` 未传时默认「开始日期」「结束日期」**(与上浮文案一致)。**`unlinkPanels`** 默认 `true`(对应 Element `unlink-panels`,左右面板月份独立);设为 `false` 则两面板联动。另有 `rangeSeparator`(默认「至」);可选 **`gmtBeginKey` / `gmtEndKey`** 同步写带时间的起止字段。 |
|
|
303
304
|
|
|
304
305
|
---
|
|
305
306
|
|
|
@@ -308,11 +309,15 @@ deviceListTransformList(list) {
|
|
|
308
309
|
非 `slot` 列会 **透传** 到 `el-table-column`(`prop`、`label`、`width`、`fixed` 等)。
|
|
309
310
|
内置列类型 **`selection` / `index` / `expand`** 不做自定义单元格包装。
|
|
310
311
|
|
|
312
|
+
最简写法:**`{ prop: 'name', label: '姓名' }`**,值为 `null` / `undefined` / `''`(以及 `textFormatter` 得到空串)时,默认显示 **`--`**;表格上 **`cell-empty-text`** 可改全局占位;列上 **`emptyText`**(优先)或 **`defaultText`** 可单独改该列。
|
|
313
|
+
|
|
311
314
|
| 字段 | 说明 |
|
|
312
315
|
|------|------|
|
|
313
316
|
| `type: 'slot'` + `slot` | 自定义列,父组件具名插槽。 |
|
|
314
317
|
| `hidden` | 隐藏。 |
|
|
315
|
-
| `valueGetter`
|
|
318
|
+
| `valueGetter` | `(row, index) => any`,取展示用原始值(不填则用 `row[prop]`)。 |
|
|
319
|
+
| `textFormatter` | `(value, row, index) => any`,格式化后若为空仍走空值占位。 |
|
|
320
|
+
| `emptyText` / `defaultText` | 空值时显示文案;**`emptyText` 优先**;都不设则用 **`cell-empty-text`**(默认 `--`)。 |
|
|
316
321
|
|
|
317
322
|
---
|
|
318
323
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wkjp-list-page",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Vue2 + ElementUI CommonListPage component",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -40,7 +40,5 @@
|
|
|
40
40
|
"webpack-cli": "^3.3.12",
|
|
41
41
|
"webpack-dev-server": "^3.11.3"
|
|
42
42
|
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"qs": "6.11.2"
|
|
45
|
-
}
|
|
43
|
+
"dependencies": {}
|
|
46
44
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
:style="{ width: `${item.width || 150}px` }"
|
|
14
14
|
:maxlength="item.maxlength != null ? item.maxlength : ''"
|
|
15
15
|
:height="item.height || '32px'"
|
|
16
|
+
:clearable="item.clearable !== false"
|
|
16
17
|
@input="(v) => handleSearchInput(item, v)"
|
|
17
18
|
@enter="emitQuery"
|
|
18
19
|
/>
|
|
@@ -57,14 +58,14 @@
|
|
|
57
58
|
v-else-if="isDatePicker(item.type)"
|
|
58
59
|
:value="resolveValue(item)"
|
|
59
60
|
:type="item.type || 'date'"
|
|
60
|
-
:unlink-panels="item
|
|
61
|
+
:unlink-panels="resolveSearchUnlinkPanels(item)"
|
|
61
62
|
:editable="item.editable === true"
|
|
62
63
|
:range-separator="item.rangeSeparator || '至'"
|
|
63
64
|
:style="{ width: `${item.width || (item.type === 'daterange' || item.type === 'datetimerange' ? 300 : 150)}px` }"
|
|
64
65
|
:label="floatingLabelFor(item)"
|
|
65
66
|
:start-text="dateRangeStartText(item)"
|
|
66
67
|
:end-text="dateRangeEndText(item)"
|
|
67
|
-
:value-format="item
|
|
68
|
+
:value-format="resolveSearchDateValueFormat(item)"
|
|
68
69
|
:height="item.height || '32px'"
|
|
69
70
|
@input="(val) => handleFieldChange(item, val)"
|
|
70
71
|
/>
|
|
@@ -220,6 +221,11 @@ export default {
|
|
|
220
221
|
enableColumnConfig: { type: Boolean, default: false },
|
|
221
222
|
/** 表头背景色,对应 Element `header-cell-style` 的 backgroundColor,可被 tableAttrs.header-cell-style 覆盖 */
|
|
222
223
|
tableHeaderBg: { type: String, default: "#e5e9f2" },
|
|
224
|
+
/**
|
|
225
|
+
* 非 slot 列:值为 `null` / `undefined` / `''`,或 `textFormatter` 得到上述空值时的占位。
|
|
226
|
+
* 列上可用 **`emptyText`**(优先)或 **`defaultText`** 覆盖;仅写 `{ prop, label }` 时默认显示 `--`。
|
|
227
|
+
*/
|
|
228
|
+
cellEmptyText: { type: String, default: "--" },
|
|
223
229
|
/** 是否显示搜索区「查询」按钮;点击会 `fetchList({ resetPage: true })` */
|
|
224
230
|
showQueryButton: { type: Boolean, default: true },
|
|
225
231
|
/** 主查询按钮文案 */
|
|
@@ -402,7 +408,7 @@ export default {
|
|
|
402
408
|
return col && ["selection", "index", "expand"].indexOf(col.type) !== -1;
|
|
403
409
|
},
|
|
404
410
|
tableColumnBind(col) {
|
|
405
|
-
var omit = ["valueGetter", "textFormatter", "defaultText", "hidden", "_visible", "_configKey"];
|
|
411
|
+
var omit = ["valueGetter", "textFormatter", "defaultText", "emptyText", "hidden", "_visible", "_configKey"];
|
|
406
412
|
var rest = {};
|
|
407
413
|
Object.keys(col || {}).forEach(function (k) {
|
|
408
414
|
if (omit.indexOf(k) === -1) rest[k] = col[k];
|
|
@@ -494,23 +500,54 @@ export default {
|
|
|
494
500
|
}
|
|
495
501
|
}
|
|
496
502
|
if (typeof item.onChange === "function") item.onChange(value);
|
|
497
|
-
if (item.autoQuery
|
|
503
|
+
if (item.autoQuery === true) {
|
|
498
504
|
this.fetchList({ resetPage: true });
|
|
499
505
|
}
|
|
500
506
|
},
|
|
501
507
|
isDatePicker(type) {
|
|
502
508
|
return ["date", "daterange", "datetime", "datetimerange", "month", "year"].includes(type);
|
|
503
509
|
},
|
|
510
|
+
/**
|
|
511
|
+
* 有 `valueFormat`(非空字符串)则用之;否则 **`daterange` 与 `date` 等** 默认 **`yyyy-MM-dd`**(与历史 `item.valueFormat || 'yyyy-MM-dd'` 一致)。
|
|
512
|
+
*/
|
|
513
|
+
resolveSearchDateValueFormat(item) {
|
|
514
|
+
if (!item) return "yyyy-MM-dd";
|
|
515
|
+
const vf = item.valueFormat;
|
|
516
|
+
if (vf != null && String(vf).trim() !== "") return vf;
|
|
517
|
+
return "yyyy-MM-dd";
|
|
518
|
+
},
|
|
519
|
+
/**
|
|
520
|
+
* 对应 Element `unlink-panels`:**默认 `true`**(左右面板月份不联动);仅当 **`unlinkPanels: false`** 时为 `false`。
|
|
521
|
+
*/
|
|
522
|
+
resolveSearchUnlinkPanels(item) {
|
|
523
|
+
return item.unlinkPanels !== false;
|
|
524
|
+
},
|
|
525
|
+
isCellDisplayEmpty(display) {
|
|
526
|
+
return display === null || display === undefined || display === "";
|
|
527
|
+
},
|
|
504
528
|
resolveCellText(col, row, index) {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
if (
|
|
508
|
-
|
|
529
|
+
const raw = typeof col.valueGetter === "function" ? col.valueGetter(row, index) : row[col.prop];
|
|
530
|
+
let display;
|
|
531
|
+
if (typeof col.textFormatter === "function") {
|
|
532
|
+
display = col.textFormatter(raw, row, index);
|
|
533
|
+
} else {
|
|
534
|
+
display = raw;
|
|
535
|
+
}
|
|
536
|
+
if (this.isCellDisplayEmpty(display)) {
|
|
537
|
+
if (col.emptyText !== undefined && col.emptyText !== null) return col.emptyText;
|
|
538
|
+
if (col.defaultText !== undefined && col.defaultText !== null) return col.defaultText;
|
|
539
|
+
return this.cellEmptyText;
|
|
540
|
+
}
|
|
541
|
+
return display;
|
|
509
542
|
},
|
|
510
543
|
omitSlotType(col) {
|
|
511
544
|
const rest = Object.assign({}, col);
|
|
512
545
|
delete rest.type;
|
|
513
546
|
delete rest.slot;
|
|
547
|
+
delete rest.valueGetter;
|
|
548
|
+
delete rest.textFormatter;
|
|
549
|
+
delete rest.defaultText;
|
|
550
|
+
delete rest.emptyText;
|
|
514
551
|
return rest;
|
|
515
552
|
},
|
|
516
553
|
handleSizeChange(size) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</template>
|
|
19
19
|
|
|
20
20
|
<script>
|
|
21
|
-
import
|
|
21
|
+
import { stringifyExportQuerys } from "../utils/stringifyExportQuerys";
|
|
22
22
|
import { createDownLoadLink } from "../utils/createDownLoadLink";
|
|
23
23
|
|
|
24
24
|
export default {
|
|
@@ -163,8 +163,7 @@ export default {
|
|
|
163
163
|
/* ignore */
|
|
164
164
|
}
|
|
165
165
|
_export.link =
|
|
166
|
-
`${_pathPrefix}?${
|
|
167
|
-
indices: false,
|
|
166
|
+
`${_pathPrefix}?${stringifyExportQuerys(_parameters, {
|
|
168
167
|
skipNulls: true,
|
|
169
168
|
})}` + (auth ? `&authorization=${auth}` : "");
|
|
170
169
|
}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
:type="type"
|
|
17
17
|
:value-format="valueFormat"
|
|
18
18
|
:placeholder="isRange ? undefined : singlePlaceholder"
|
|
19
|
-
:start-placeholder="
|
|
20
|
-
:end-placeholder="
|
|
19
|
+
:start-placeholder="nativeStartPlaceholder"
|
|
20
|
+
:end-placeholder="nativeEndPlaceholder"
|
|
21
21
|
class="floating-date-picker-inner"
|
|
22
22
|
:style="editorStyle"
|
|
23
23
|
@input="onInput"
|
|
@@ -70,8 +70,25 @@ export default {
|
|
|
70
70
|
isRange() {
|
|
71
71
|
return RANGE_TYPES.indexOf(this.type) !== -1;
|
|
72
72
|
},
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
/** 与 Element `start-placeholder` 对齐;未单独传 attrs 时用 `startText`(默认「开始日期」) */
|
|
74
|
+
nativeStartPlaceholder() {
|
|
75
|
+
if (!this.isRange) return undefined;
|
|
76
|
+
const fromAttr =
|
|
77
|
+
this.$attrs.startPlaceholder !== undefined
|
|
78
|
+
? this.$attrs.startPlaceholder
|
|
79
|
+
: this.$attrs["start-placeholder"];
|
|
80
|
+
if (fromAttr != null && String(fromAttr).trim() !== "") return fromAttr;
|
|
81
|
+
const t = (this.startText || "").trim();
|
|
82
|
+
return t || "开始日期";
|
|
83
|
+
},
|
|
84
|
+
/** 与 Element `end-placeholder` 对齐;未单独传 attrs 时用 `endText`(默认「结束日期」) */
|
|
85
|
+
nativeEndPlaceholder() {
|
|
86
|
+
if (!this.isRange) return undefined;
|
|
87
|
+
const fromAttr =
|
|
88
|
+
this.$attrs.endPlaceholder !== undefined ? this.$attrs.endPlaceholder : this.$attrs["end-placeholder"];
|
|
89
|
+
if (fromAttr != null && String(fromAttr).trim() !== "") return fromAttr;
|
|
90
|
+
const t = (this.endText || "").trim();
|
|
91
|
+
return t || "结束日期";
|
|
75
92
|
},
|
|
76
93
|
singlePlaceholder() {
|
|
77
94
|
return " ";
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="floating-label-wrap"
|
|
4
|
-
:class="{
|
|
4
|
+
:class="{
|
|
5
|
+
'is-focused': isFocused,
|
|
6
|
+
'has-value': hasValue,
|
|
7
|
+
'has-clear-btn': clearable && hasValue
|
|
8
|
+
}"
|
|
5
9
|
>
|
|
6
10
|
<input
|
|
7
11
|
ref="input"
|
|
@@ -16,6 +20,17 @@
|
|
|
16
20
|
@blur="handleBlur"
|
|
17
21
|
@keyup.enter="$emit('enter')"
|
|
18
22
|
/>
|
|
23
|
+
<button
|
|
24
|
+
v-if="clearable && hasValue"
|
|
25
|
+
type="button"
|
|
26
|
+
class="floating-input__clear"
|
|
27
|
+
tabindex="-1"
|
|
28
|
+
aria-label="清除"
|
|
29
|
+
@mousedown.prevent
|
|
30
|
+
@click="clearInput"
|
|
31
|
+
>
|
|
32
|
+
<i class="el-icon-circle-close"></i>
|
|
33
|
+
</button>
|
|
19
34
|
<label class="floating-label">{{ label }}</label>
|
|
20
35
|
</div>
|
|
21
36
|
</template>
|
|
@@ -44,6 +59,11 @@ export default {
|
|
|
44
59
|
type: [String, Number],
|
|
45
60
|
default: "32px",
|
|
46
61
|
},
|
|
62
|
+
/** 为 true 且有内容时显示清除按钮;与 `searchOption` 里 `input` 默认 `clearable: true` 对齐 */
|
|
63
|
+
clearable: {
|
|
64
|
+
type: Boolean,
|
|
65
|
+
default: true,
|
|
66
|
+
},
|
|
47
67
|
},
|
|
48
68
|
data() {
|
|
49
69
|
return {
|
|
@@ -73,6 +93,13 @@ export default {
|
|
|
73
93
|
handleBlur() {
|
|
74
94
|
this.isFocused = false;
|
|
75
95
|
},
|
|
96
|
+
clearInput() {
|
|
97
|
+
this.hasValue = false;
|
|
98
|
+
this.$emit("input", "");
|
|
99
|
+
this.$nextTick(() => {
|
|
100
|
+
if (this.$refs.input) this.$refs.input.focus();
|
|
101
|
+
});
|
|
102
|
+
},
|
|
76
103
|
checkValue() {
|
|
77
104
|
this.hasValue = !!this.value;
|
|
78
105
|
},
|
|
@@ -142,6 +169,39 @@ export default {
|
|
|
142
169
|
font-family: inherit;
|
|
143
170
|
}
|
|
144
171
|
|
|
172
|
+
.floating-label-wrap.has-clear-btn .text-box {
|
|
173
|
+
padding-right: 30px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.floating-input__clear {
|
|
177
|
+
position: absolute;
|
|
178
|
+
right: 8px;
|
|
179
|
+
top: 50%;
|
|
180
|
+
transform: translateY(-50%);
|
|
181
|
+
z-index: 3;
|
|
182
|
+
display: flex;
|
|
183
|
+
align-items: center;
|
|
184
|
+
justify-content: center;
|
|
185
|
+
width: 22px;
|
|
186
|
+
height: 22px;
|
|
187
|
+
padding: 0;
|
|
188
|
+
margin: 0;
|
|
189
|
+
border: none;
|
|
190
|
+
background: transparent;
|
|
191
|
+
color: #c0c4cc;
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
border-radius: 50%;
|
|
194
|
+
line-height: 1;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.floating-input__clear:hover {
|
|
198
|
+
color: #909399;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.floating-input__clear i {
|
|
202
|
+
font-size: 14px;
|
|
203
|
+
}
|
|
204
|
+
|
|
145
205
|
.floating-label-wrap .text-box:hover {
|
|
146
206
|
border-color: #c0c4cc;
|
|
147
207
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 导出链接查询串(替代 `qs.stringify`,避免旧构建链解析 `node_modules` 里 async generator 等新语法)。
|
|
3
|
+
* 行为对齐原用法:`indices: false`(数组同一 key 多次)、`skipNulls: true`。
|
|
4
|
+
*
|
|
5
|
+
* @param {Record<string, unknown>} obj
|
|
6
|
+
* @param {{ skipNulls?: boolean }} [options] 仅 `skipNulls`;数组为同一 key 多次 append(对齐 `qs` 的 `indices: false`)。
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
export function stringifyExportQuerys(obj, options) {
|
|
10
|
+
var opts = options || {};
|
|
11
|
+
var skipNulls = opts.skipNulls === true;
|
|
12
|
+
var sp = new URLSearchParams();
|
|
13
|
+
if (!obj || typeof obj !== "object") return "";
|
|
14
|
+
Object.keys(obj).forEach(function (key) {
|
|
15
|
+
var val = obj[key];
|
|
16
|
+
if (skipNulls && (val === null || val === undefined)) return;
|
|
17
|
+
if (Array.isArray(val)) {
|
|
18
|
+
val.forEach(function (item) {
|
|
19
|
+
if (skipNulls && (item === null || item === undefined)) return;
|
|
20
|
+
sp.append(key, item == null ? "" : String(item));
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
sp.append(key, val == null ? "" : String(val));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return sp.toString();
|
|
27
|
+
}
|