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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npmapps",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
Binary file