npmapps 1.0.18 → 1.0.20
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/app/btnSelect/btnSelect.vue +169 -0
- package/app/btnSelect/index.vue +69 -0
- package/app/btnSelect.zip +0 -0
- package/npmapps-1.0.20.tgz +0 -0
- package/package.json +1 -1
- package/app/rubick-Setup-4.3.7-x64.exe +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<script lang="jsx">
|
|
2
|
+
import { defineComponent, ref, computed, watch } from "vue";
|
|
3
|
+
import { ArrowDown } from "@element-plus/icons-vue";
|
|
4
|
+
export default defineComponent({
|
|
5
|
+
name: "BtnSelect",
|
|
6
|
+
props: {
|
|
7
|
+
options: { type: Array, default: () => [] },
|
|
8
|
+
modelValue: { type: [String, Number, Array, Object, Boolean], default: null },
|
|
9
|
+
multiple: { type: Boolean, default: false },
|
|
10
|
+
searchable: { type: Boolean, default: false },
|
|
11
|
+
label: { type: String, default: "" },
|
|
12
|
+
optionKeys: { type: Object, default: () => ({ label: "label", value: "value" }) },
|
|
13
|
+
maxLabelCount: { type: Number, default: 3 },
|
|
14
|
+
disabled: { type: Boolean, default: false },
|
|
15
|
+
collapseTags: { type: Boolean, default: false },
|
|
16
|
+
itemRender: { type: Function, default: null },
|
|
17
|
+
},
|
|
18
|
+
emits: ["update:modelValue", "change"],
|
|
19
|
+
setup(props, { emit }) {
|
|
20
|
+
const keyword = ref("");
|
|
21
|
+
const internal = ref(props.multiple ? ([]).concat(props.modelValue || []) : props.modelValue);
|
|
22
|
+
watch(
|
|
23
|
+
() => props.modelValue,
|
|
24
|
+
(v) => {
|
|
25
|
+
internal.value = props.multiple ? ([]).concat(v || []) : v;
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const isChecked = (val) => {
|
|
30
|
+
const cur = internal.value;
|
|
31
|
+
return Array.isArray(cur) ? cur.includes(val) : cur === val;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const toggle = (val) => {
|
|
35
|
+
const list = props.options || [];
|
|
36
|
+
const { label: labelKey, value: valueKey } = props.optionKeys || { label: "label", value: "value" };
|
|
37
|
+
const found = list.find((i) => i[valueKey] === val);
|
|
38
|
+
if (found && found.disabled) return;
|
|
39
|
+
if (props.multiple) {
|
|
40
|
+
const set = new Set(internal.value || []);
|
|
41
|
+
if (set.has(val)) set.delete(val);
|
|
42
|
+
else set.add(val);
|
|
43
|
+
const next = Array.from(set);
|
|
44
|
+
internal.value = next;
|
|
45
|
+
emit("update:modelValue", next);
|
|
46
|
+
emit("change", next);
|
|
47
|
+
} else {
|
|
48
|
+
internal.value = val;
|
|
49
|
+
emit("update:modelValue", val);
|
|
50
|
+
emit("change", val);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const filtered = computed(() => {
|
|
55
|
+
const list = props.options || [];
|
|
56
|
+
const { label: labelKey, value: valueKey } = props.optionKeys || { label: "label", value: "value" };
|
|
57
|
+
if (!props.searchable || !keyword.value) return list;
|
|
58
|
+
const k = String(keyword.value).toLowerCase();
|
|
59
|
+
return list.filter((o) => String(o[labelKey]).toLowerCase().includes(k));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const displayMeta = computed(() => {
|
|
63
|
+
const list = props.options || [];
|
|
64
|
+
const { label: labelKey, value: valueKey } = props.optionKeys || { label: "label", value: "value" };
|
|
65
|
+
if (props.multiple) {
|
|
66
|
+
const vals = internal.value || [];
|
|
67
|
+
if (!vals.length) return { text: props.label || "请选择", rest: 0 };
|
|
68
|
+
const labels = vals.map((v) => {
|
|
69
|
+
const o = list.find((i) => i[valueKey] === v);
|
|
70
|
+
return o ? o[labelKey] : v;
|
|
71
|
+
});
|
|
72
|
+
if (props.collapseTags) {
|
|
73
|
+
const first = labels[0];
|
|
74
|
+
const rest = labels.length - 1;
|
|
75
|
+
return { text: `${props.label ? props.label + ":" : ""}${first}`, rest };
|
|
76
|
+
}
|
|
77
|
+
if (labels.length > props.maxLabelCount) return { text: (props.label ? props.label + ":" : "") + labels.slice(0, props.maxLabelCount).join("、") + ` 等${labels.length}项`, rest: 0 };
|
|
78
|
+
return { text: (props.label ? props.label + ":" : "") + labels.join("、"), rest: 0 };
|
|
79
|
+
} else {
|
|
80
|
+
const v = internal.value;
|
|
81
|
+
if (v === undefined || v === null || v === "") return { text: props.label || "请选择", rest: 0 };
|
|
82
|
+
const o = list.find((i) => i[valueKey] === v);
|
|
83
|
+
const text = o ? o[labelKey] : String(v);
|
|
84
|
+
return { text: props.label ? `${props.label}:${text}` : text, rest: 0 };
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const clear = () => {
|
|
89
|
+
if (props.disabled) return;
|
|
90
|
+
internal.value = props.multiple ? [] : null;
|
|
91
|
+
emit("update:modelValue", internal.value);
|
|
92
|
+
emit("change", internal.value);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const hasValue = computed(() => (Array.isArray(internal.value) ? internal.value.length > 0 : internal.value !== null && internal.value !== undefined && internal.value !== "") );
|
|
96
|
+
|
|
97
|
+
return () => (
|
|
98
|
+
<el-dropdown hideOnClick={props.multiple ? false : true} disabled={props.disabled}>
|
|
99
|
+
{{
|
|
100
|
+
default: () => (
|
|
101
|
+
<el-button>
|
|
102
|
+
<span style="display:inline-flex;align-items:center;gap:6px;">
|
|
103
|
+
<span>{displayMeta.value.text}</span>
|
|
104
|
+
{props.collapseTags && displayMeta.value.rest > 0 ? (
|
|
105
|
+
<span style="display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 4px;border-radius:9px;background:rgba(64,158,255,0.15);color:#409EFF;font-size:12px;line-height:18px;">{`+${displayMeta.value.rest}`}</span>
|
|
106
|
+
) : null}
|
|
107
|
+
{hasValue.value ? (
|
|
108
|
+
<span
|
|
109
|
+
title="清除"
|
|
110
|
+
style="display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;background:rgba(0,0,0,0.06);cursor:pointer;margin-left:4px;"
|
|
111
|
+
onClick={(e) => {
|
|
112
|
+
e.stopPropagation();
|
|
113
|
+
clear();
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
×
|
|
117
|
+
</span>
|
|
118
|
+
) : (
|
|
119
|
+
<span style="margin-left:4px;opacity:0.7;">
|
|
120
|
+
<el-icon><ArrowDown /></el-icon>
|
|
121
|
+
</span>
|
|
122
|
+
)}
|
|
123
|
+
</span>
|
|
124
|
+
</el-button>
|
|
125
|
+
),
|
|
126
|
+
dropdown: () => (
|
|
127
|
+
<el-dropdown-menu>
|
|
128
|
+
{props.searchable ? (
|
|
129
|
+
<div style="padding:8px 12px;">
|
|
130
|
+
<el-input
|
|
131
|
+
modelValue={keyword.value}
|
|
132
|
+
onUpdate:modelValue={(v) => {
|
|
133
|
+
keyword.value = v;
|
|
134
|
+
}}
|
|
135
|
+
clearable={true}
|
|
136
|
+
placeholder="搜索"
|
|
137
|
+
size="small"
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
) : null}
|
|
141
|
+
{filtered.value.map((opt) => (
|
|
142
|
+
<el-dropdown-item
|
|
143
|
+
key={opt[(props.optionKeys || {}).value || "value"]}
|
|
144
|
+
command={opt[(props.optionKeys || {}).value || "value"]}
|
|
145
|
+
disabled={Boolean(opt.disabled)}
|
|
146
|
+
onClick={() => toggle(opt[(props.optionKeys || {}).value || "value"])}
|
|
147
|
+
style={isChecked(opt[(props.optionKeys || {}).value || "value"]) ? "background: rgba(64,158,255,0.08);" : ""}
|
|
148
|
+
>
|
|
149
|
+
{props.itemRender ? (
|
|
150
|
+
props.itemRender(opt, {
|
|
151
|
+
checked: isChecked(opt[(props.optionKeys || {}).value || "value"]),
|
|
152
|
+
})
|
|
153
|
+
) : (
|
|
154
|
+
<div style="display:flex;align-items:center;justify-content:space-between;min-width:180px;">
|
|
155
|
+
<span>{opt[(props.optionKeys || {}).label || "label"]}</span>
|
|
156
|
+
{isChecked(opt[(props.optionKeys || {}).value || "value"]) ? <span style="color:#67C23A;">✓ 已选</span> : null}
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
</el-dropdown-item>
|
|
160
|
+
))}
|
|
161
|
+
{!filtered.value.length ? <el-dropdown-item disabled={true}>无数据</el-dropdown-item> : null}
|
|
162
|
+
</el-dropdown-menu>
|
|
163
|
+
),
|
|
164
|
+
}}
|
|
165
|
+
</el-dropdown>
|
|
166
|
+
);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
</script>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="jsx">
|
|
2
|
+
import { defineComponent, ref } from "vue";
|
|
3
|
+
import BtnSelect from "./btnSelect.vue";
|
|
4
|
+
|
|
5
|
+
export default defineComponent({
|
|
6
|
+
name: "BtnSelectView",
|
|
7
|
+
setup() {
|
|
8
|
+
const options = ref([
|
|
9
|
+
{ label: "选项一", value: "a" },
|
|
10
|
+
{ label: "选项二", value: "b" },
|
|
11
|
+
{ label: "选项三", value: "c" },
|
|
12
|
+
{ label: "禁用项", value: "d", disabled: true },
|
|
13
|
+
]);
|
|
14
|
+
const singleValue = ref(null);
|
|
15
|
+
const multiValue = ref([]);
|
|
16
|
+
const searchValue = ref(null);
|
|
17
|
+
return () => (
|
|
18
|
+
<>
|
|
19
|
+
<div style="display:flex; gap:16px; align-items:center; flex-wrap:wrap;">
|
|
20
|
+
<span>按钮下拉框(普通)</span>
|
|
21
|
+
<BtnSelect
|
|
22
|
+
label="名称"
|
|
23
|
+
options={options.value}
|
|
24
|
+
modelValue={singleValue.value}
|
|
25
|
+
onUpdate:modelValue={(v) => {
|
|
26
|
+
singleValue.value = v;
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
<span>按钮下拉框(多选)</span>
|
|
30
|
+
<BtnSelect
|
|
31
|
+
label="名称"
|
|
32
|
+
options={options.value}
|
|
33
|
+
multiple={true}
|
|
34
|
+
modelValue={multiValue.value}
|
|
35
|
+
onUpdate:modelValue={(v) => {
|
|
36
|
+
multiValue.value = v;
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
<span>按钮下拉框(带搜索)</span>
|
|
40
|
+
<BtnSelect
|
|
41
|
+
label="名称"
|
|
42
|
+
options={options.value}
|
|
43
|
+
searchable={true}
|
|
44
|
+
multiple={true}
|
|
45
|
+
collapseTags={true}
|
|
46
|
+
itemRender={(opt, { checked }) => (
|
|
47
|
+
<div style="display:flex;align-items:center;justify-content:space-between;min-width:200px;">
|
|
48
|
+
<span>
|
|
49
|
+
{opt.label}
|
|
50
|
+
{opt.disabled ? <span style="margin-left:8px;color:#F56C6C;">禁用</span> : null}
|
|
51
|
+
</span>
|
|
52
|
+
{checked ? (
|
|
53
|
+
<span style="color:#67C23A;">✓ 已选</span>
|
|
54
|
+
) : (
|
|
55
|
+
<span style="color:#909399;">点击选择</span>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
modelValue={searchValue.value}
|
|
60
|
+
onUpdate:modelValue={(v) => {
|
|
61
|
+
searchValue.value = v;
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|