@mywzzz/mineadmin-search-panel 1.0.7

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 ADDED
@@ -0,0 +1,94 @@
1
+ # @mywzzz/mineadmin-search-panel
2
+
3
+ Vue 3 + Element Plus search panel component extracted from MineAdmin scenarios.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @mywzzz/mineadmin-search-panel
9
+ ```
10
+
11
+ Peer dependencies:
12
+
13
+ - `vue`
14
+ - `element-plus`
15
+ - `@iconify/vue`
16
+
17
+ ## Usage
18
+
19
+ ```ts
20
+ import { createApp } from 'vue'
21
+ import ElementPlus from 'element-plus'
22
+ import 'element-plus/dist/index.css'
23
+ import App from './App.vue'
24
+
25
+ createApp(App).use(ElementPlus).mount('#app')
26
+ ```
27
+
28
+ ```vue
29
+ <script setup lang="ts">
30
+ import { ref } from 'vue'
31
+ import SearchPanel, { type LegacySearchPanelItem } from '@mywzzz/mineadmin-search-panel'
32
+ import '@mywzzz/mineadmin-search-panel/style.css'
33
+
34
+ const form = ref<Record<string, unknown>>({
35
+ username: '',
36
+ status: '',
37
+ })
38
+
39
+ const items: LegacySearchPanelItem[] = [
40
+ {
41
+ label: () => '用户名',
42
+ prop: 'username',
43
+ render: 'input',
44
+ renderProps: {
45
+ clearable: true,
46
+ placeholder: '请输入用户名',
47
+ },
48
+ },
49
+ {
50
+ label: () => '状态',
51
+ prop: 'status',
52
+ render: 'select',
53
+ renderProps: {
54
+ clearable: true,
55
+ options: [
56
+ { label: '启用', value: '1' },
57
+ { label: '禁用', value: '0' },
58
+ ],
59
+ },
60
+ },
61
+ ]
62
+ </script>
63
+
64
+ <template>
65
+ <SearchPanel
66
+ v-model="form"
67
+ title="用户检索"
68
+ subtitle="真实 npm 包使用示例"
69
+ :items="items"
70
+ :fold="true"
71
+ @search="console.log"
72
+ @reset="console.log"
73
+ />
74
+ </template>
75
+ ```
76
+
77
+ ## Supported item input
78
+
79
+ - Library items: `input` / `select` / `date-picker` / `input-number`
80
+ - Legacy MineAdmin-style items: `label` + `prop` + `render` + `renderProps`
81
+ - Custom component items: component must support `v-model` and pass width-related attrs or styles to the actual input control
82
+
83
+ ## Build
84
+
85
+ ```bash
86
+ npm run build
87
+ ```
88
+
89
+ The build outputs:
90
+
91
+ - `dist/index.js`
92
+ - `dist/index.umd.cjs`
93
+ - `dist/index.d.ts`
94
+ - `dist/style.css`
@@ -0,0 +1,38 @@
1
+ import type { SearchPanelCols, SearchPanelProps } from './types';
2
+ declare var __VLS_1: {}, __VLS_37: string, __VLS_38: {
3
+ item: import("./types").SearchPanelSlotItem;
4
+ model: Record<string, unknown>;
5
+ update: (value: unknown) => void;
6
+ };
7
+ type __VLS_Slots = {} & {
8
+ [K in NonNullable<typeof __VLS_37>]?: (props: typeof __VLS_38) => any;
9
+ } & {
10
+ actions?: (props: typeof __VLS_1) => any;
11
+ };
12
+ declare const __VLS_component: import("vue").DefineComponent<SearchPanelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
+ search: (value: Record<string, unknown>) => any;
14
+ "update:modelValue": (value: Record<string, unknown>) => any;
15
+ reset: (value: Record<string, unknown>) => any;
16
+ }, string, import("vue").PublicProps, Readonly<SearchPanelProps> & Readonly<{
17
+ onSearch?: ((value: Record<string, unknown>) => any) | undefined;
18
+ "onUpdate:modelValue"?: ((value: Record<string, unknown>) => any) | undefined;
19
+ onReset?: ((value: Record<string, unknown>) => any) | undefined;
20
+ }>, {
21
+ modelValue: Record<string, unknown>;
22
+ defaultValue: Record<string, unknown>;
23
+ labelWidth: string;
24
+ cols: SearchPanelCols;
25
+ fold: boolean;
26
+ foldRows: number;
27
+ searchText: string;
28
+ resetText: string;
29
+ foldText: string;
30
+ unfoldText: string;
31
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
32
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
33
+ export default _default;
34
+ type __VLS_WithSlots<T, S> = T & {
35
+ new (): {
36
+ $slots: S;
37
+ };
38
+ };
@@ -0,0 +1,6 @@
1
+ import SearchPanel from './SearchPanel.vue';
2
+ import './style/index.css';
3
+ export { normalizeSearchPanelItems, resolveLabelValue, resolveLegacyVNode, resolvePropValue, } from './normalize';
4
+ export type { ColsConfig, LegacySearchPanelItem, LegacySearchPanelRender, SearchPanelBaseItem, SearchPanelBuiltinItem, SearchPanelCols, SearchPanelComponentItem, SearchPanelContext, SearchPanelInputItem, SearchPanelItem, SearchPanelOption, SearchPanelProps, SearchPanelSlotItem, SearchPanelSlotRenderer, } from './types';
5
+ export { SearchPanel };
6
+ export default SearchPanel;
package/dist/index.js ADDED
@@ -0,0 +1,317 @@
1
+ import { isVNode as j, defineComponent as Z, ref as x, watch as z, computed as f, onMounted as ee, onBeforeUnmount as ne, openBlock as a, createElementBlock as y, createElementVNode as v, toDisplayString as b, createCommentVNode as m, renderSlot as F, createVNode as _, unref as u, withModifiers as R, withCtx as c, normalizeStyle as E, Fragment as I, renderList as U, createBlock as h, resolveDynamicComponent as N, mergeProps as D, withKeys as te, createTextVNode as T } from "vue";
2
+ import { ElInputNumber as le, ElDatePicker as oe, ElSelect as re, ElInput as ae, ElForm as ue, ElFormItem as se, ElOption as ie, ElButton as K, ElLink as de } from "element-plus";
3
+ import { Icon as O } from "@iconify/vue";
4
+ const ce = {
5
+ input: ae,
6
+ select: re,
7
+ "date-picker": oe,
8
+ "input-number": le
9
+ };
10
+ function pe(e) {
11
+ return ce[e];
12
+ }
13
+ function fe(e) {
14
+ if (e.fullWidth !== !1)
15
+ return { width: "100%" };
16
+ }
17
+ const w = {
18
+ input: "input",
19
+ select: "select",
20
+ "date-picker": "date-picker",
21
+ inputNumber: "input-number",
22
+ "input-number": "input-number"
23
+ };
24
+ function ye(e) {
25
+ return typeof e == "function" ? e() : e ?? "";
26
+ }
27
+ function ve(e) {
28
+ return typeof e == "function" ? e() : e ?? "";
29
+ }
30
+ function me(e) {
31
+ return typeof e == "string" && e in w;
32
+ }
33
+ function he(e) {
34
+ const r = ve(e.prop), o = e.label ?? r;
35
+ if (me(e.render))
36
+ return {
37
+ key: r,
38
+ label: o,
39
+ span: e.span,
40
+ type: w[e.render],
41
+ options: e.renderProps?.options,
42
+ componentProps: e.renderProps
43
+ };
44
+ if (typeof e.render == "function") {
45
+ const k = e.render, s = k();
46
+ return typeof s == "string" && s in w ? {
47
+ key: r,
48
+ label: o,
49
+ span: e.span,
50
+ type: w[s],
51
+ options: e.renderProps?.options,
52
+ componentProps: e.renderProps
53
+ } : j(s) ? {
54
+ key: r,
55
+ label: o,
56
+ span: e.span,
57
+ type: "slot",
58
+ slotName: `legacy:${r}`
59
+ } : {
60
+ key: r,
61
+ label: o,
62
+ span: e.span,
63
+ type: "component",
64
+ component: s,
65
+ componentProps: e.renderProps
66
+ };
67
+ }
68
+ return e.render ? {
69
+ key: r,
70
+ label: o,
71
+ span: e.span,
72
+ type: "component",
73
+ component: e.render,
74
+ componentProps: e.renderProps
75
+ } : {
76
+ key: r,
77
+ label: o,
78
+ span: e.span,
79
+ type: "input",
80
+ componentProps: e.renderProps
81
+ };
82
+ }
83
+ function ke(e) {
84
+ return e.map((r) => "type" in r && "key" in r ? r : he(r));
85
+ }
86
+ function be(e) {
87
+ if ("type" in e && "key" in e || typeof e.render != "function")
88
+ return null;
89
+ const r = e.render, o = r();
90
+ return j(o) ? o : null;
91
+ }
92
+ const _e = { class: "ma-search-panel" }, ge = {
93
+ key: 0,
94
+ class: "ma-search-panel__card ma-search-panel__header"
95
+ }, we = { class: "ma-search-panel__title-wrap" }, Ve = {
96
+ key: 0,
97
+ class: "ma-search-panel__title"
98
+ }, Pe = {
99
+ key: 1,
100
+ class: "ma-search-panel__subtitle"
101
+ }, Ce = { class: "ma-search-panel__card" }, Se = { class: "ma-search-panel__field ma-search-panel__field--full" }, xe = { class: "ma-search-panel__actions" }, Le = /* @__PURE__ */ Z({
102
+ __name: "SearchPanel",
103
+ props: {
104
+ items: {},
105
+ modelValue: { default: () => ({}) },
106
+ defaultValue: { default: () => ({}) },
107
+ title: {},
108
+ subtitle: {},
109
+ labelWidth: { default: "90px" },
110
+ cols: { default: 3 },
111
+ fold: { type: Boolean, default: !1 },
112
+ foldRows: { default: 1 },
113
+ searchText: { default: "搜索" },
114
+ resetText: { default: "重置" },
115
+ foldText: { default: "展开搜索" },
116
+ unfoldText: { default: "收起搜索" }
117
+ },
118
+ emits: ["update:modelValue", "search", "reset"],
119
+ setup(e, { emit: r }) {
120
+ const o = e, k = r;
121
+ function s() {
122
+ const n = {};
123
+ return V.value.forEach((l) => {
124
+ n[l.key] = o.defaultValue?.[l.key];
125
+ }), n;
126
+ }
127
+ const d = x({
128
+ ...s(),
129
+ ...o.modelValue
130
+ });
131
+ z(
132
+ () => [o.items, o.defaultValue, o.modelValue],
133
+ () => {
134
+ d.value = {
135
+ ...s(),
136
+ ...o.modelValue
137
+ };
138
+ },
139
+ { deep: !0 }
140
+ );
141
+ function L(n) {
142
+ d.value = n, k("update:modelValue", n);
143
+ }
144
+ const $ = x(typeof window > "u" ? 1440 : window.innerWidth), V = f(() => ke(o.items));
145
+ function A(n) {
146
+ if (typeof n == "number")
147
+ return n;
148
+ const l = $.value;
149
+ return l >= 1920 ? n.xl ?? 4 : l >= 1200 ? n.lg ?? 3 : l >= 992 ? n.md ?? 2 : l >= 768 ? n.sm ?? 2 : n.xs ?? 1;
150
+ }
151
+ function P() {
152
+ typeof window < "u" && ($.value = window.innerWidth);
153
+ }
154
+ ee(() => {
155
+ P(), typeof window < "u" && window.addEventListener("resize", P);
156
+ }), ne(() => {
157
+ typeof window < "u" && window.removeEventListener("resize", P);
158
+ });
159
+ const g = f(() => Math.max(A(o.cols), 1)), B = f(() => Math.max(g.value * o.foldRows - 1, 0)), p = x(o.fold);
160
+ z(
161
+ () => o.fold,
162
+ (n) => {
163
+ p.value = n;
164
+ }
165
+ );
166
+ const M = f(() => o.items.length > B.value), q = f(() => p.value && M.value ? V.value.slice(0, B.value) : V.value), G = f(() => ({
167
+ gridTemplateColumns: `repeat(${g.value}, minmax(0, 1fr))`
168
+ }));
169
+ function C(n, l) {
170
+ L({ ...d.value, [n]: l });
171
+ }
172
+ function H() {
173
+ const n = s();
174
+ L(n), k("reset", n);
175
+ }
176
+ function S() {
177
+ const n = {};
178
+ Object.entries(d.value).forEach(([l, t]) => {
179
+ t !== "" && t !== void 0 && t !== null && (n[l] = t);
180
+ }), k("search", n);
181
+ }
182
+ function J(n) {
183
+ return n.type === "input" || n.type === "select" || n.type === "date-picker" || n.type === "input-number";
184
+ }
185
+ function Q(n) {
186
+ return n.type === "component";
187
+ }
188
+ function X(n) {
189
+ return o.items.find((l) => "type" in l && "key" in l ? l.key === n.key : n.key === (typeof l.prop == "function" ? l.prop() : l.prop));
190
+ }
191
+ function Y(n) {
192
+ return n.type === "slot" ? n.slotName : `legacy:${n.key}`;
193
+ }
194
+ function W(n) {
195
+ const l = X(n);
196
+ return l ? be(l) : null;
197
+ }
198
+ return (n, l) => (a(), y("div", _e, [
199
+ e.title || e.subtitle || n.$slots.actions ? (a(), y("div", ge, [
200
+ v("div", we, [
201
+ e.title ? (a(), y("div", Ve, b(e.title), 1)) : m("", !0),
202
+ e.subtitle ? (a(), y("div", Pe, b(e.subtitle), 1)) : m("", !0)
203
+ ]),
204
+ F(n.$slots, "actions")
205
+ ])) : m("", !0),
206
+ v("div", Ce, [
207
+ _(u(ue), {
208
+ model: d.value,
209
+ "label-width": e.labelWidth,
210
+ onSubmit: R(S, ["prevent"])
211
+ }, {
212
+ default: c(() => [
213
+ v("div", {
214
+ class: "ma-search-panel__grid",
215
+ style: E(G.value)
216
+ }, [
217
+ (a(!0), y(I, null, U(q.value, (t) => (a(), h(u(se), {
218
+ key: t.key,
219
+ class: "ma-search-panel__item",
220
+ label: u(ye)(t.label),
221
+ style: E(t.span ? { gridColumn: `span ${t.span}` } : void 0)
222
+ }, {
223
+ default: c(() => [
224
+ v("div", Se, [
225
+ J(t) ? (a(), h(N(u(pe)(t.type)), D({
226
+ key: 0,
227
+ "model-value": d.value[t.key]
228
+ }, { ref_for: !0 }, t.componentProps, {
229
+ class: "ma-search-panel__field--full",
230
+ onKeyup: te(S, ["enter"]),
231
+ "onUpdate:modelValue": (i) => C(t.key, i)
232
+ }), {
233
+ default: c(() => [
234
+ t.type === "select" ? (a(!0), y(I, { key: 0 }, U(t.options ?? [], (i) => (a(), h(u(ie), {
235
+ key: String(i.value),
236
+ label: i.label,
237
+ value: i.value
238
+ }, null, 8, ["label", "value"]))), 128)) : m("", !0)
239
+ ]),
240
+ _: 2
241
+ }, 1040, ["model-value", "onUpdate:modelValue"])) : Q(t) ? (a(), h(N(t.component), D({
242
+ key: 1,
243
+ "model-value": d.value[t.key]
244
+ }, { ref_for: !0 }, t.componentProps, {
245
+ style: u(fe)(t),
246
+ class: "ma-search-panel__field--full",
247
+ "onUpdate:modelValue": (i) => C(t.key, i)
248
+ }), null, 16, ["model-value", "style", "onUpdate:modelValue"])) : F(n.$slots, Y(t), {
249
+ key: 2,
250
+ item: t,
251
+ model: d.value,
252
+ update: (i) => C(t.key, i)
253
+ }, () => [
254
+ W(t) ? (a(), h(N(W(t)), { key: 0 })) : m("", !0)
255
+ ])
256
+ ])
257
+ ]),
258
+ _: 2
259
+ }, 1032, ["label", "style"]))), 128)),
260
+ v("div", {
261
+ class: "ma-search-panel__actions-wrap",
262
+ style: E({ gridColumn: `${g.value} / ${g.value + 1}` })
263
+ }, [
264
+ v("div", xe, [
265
+ _(u(K), {
266
+ type: "primary",
267
+ onClick: S
268
+ }, {
269
+ icon: c(() => [
270
+ _(u(O), { icon: "heroicons:magnifying-glass" })
271
+ ]),
272
+ default: c(() => [
273
+ T(" " + b(e.searchText), 1)
274
+ ]),
275
+ _: 1
276
+ }),
277
+ _(u(K), { onClick: H }, {
278
+ default: c(() => [
279
+ T(b(e.resetText), 1)
280
+ ]),
281
+ _: 1
282
+ }),
283
+ M.value ? (a(), h(u(de), {
284
+ key: 0,
285
+ type: "primary",
286
+ underline: "never",
287
+ class: "ma-search-panel__fold-toggle",
288
+ onClick: l[0] || (l[0] = R((t) => p.value = !p.value, ["prevent"]))
289
+ }, {
290
+ icon: c(() => [
291
+ _(u(O), {
292
+ icon: p.value ? "material-symbols:keyboard-arrow-down" : "material-symbols:keyboard-arrow-up"
293
+ }, null, 8, ["icon"])
294
+ ]),
295
+ default: c(() => [
296
+ T(b(p.value ? e.foldText : e.unfoldText) + " ", 1)
297
+ ]),
298
+ _: 1
299
+ })) : m("", !0)
300
+ ])
301
+ ], 4)
302
+ ], 4)
303
+ ]),
304
+ _: 3
305
+ }, 8, ["model", "label-width"])
306
+ ])
307
+ ]));
308
+ }
309
+ });
310
+ export {
311
+ Le as SearchPanel,
312
+ Le as default,
313
+ ke as normalizeSearchPanelItems,
314
+ ye as resolveLabelValue,
315
+ be as resolveLegacyVNode,
316
+ ve as resolvePropValue
317
+ };
@@ -0,0 +1 @@
1
+ (function(c,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("element-plus"),require("@iconify/vue")):typeof define=="function"&&define.amd?define(["exports","vue","element-plus","@iconify/vue"],e):(c=typeof globalThis<"u"?globalThis:c||self,e(c.MaSearchPanel={},c.Vue,c.ElementPlus,c.IconifyVue))})(this,(function(c,e,i,w){"use strict";const L={input:i.ElInput,select:i.ElSelect,"date-picker":i.ElDatePicker,"input-number":i.ElInputNumber};function M(n){return L[n]}function z(n){if(n.fullWidth!==!1)return{width:"100%"}}const y={input:"input",select:"select","date-picker":"date-picker",inputNumber:"input-number","input-number":"input-number"};function b(n){return typeof n=="function"?n():n??""}function _(n){return typeof n=="function"?n():n??""}function D(n){return typeof n=="string"&&n in y}function $(n){const a=_(n.prop),l=n.label??a;if(D(n.render))return{key:a,label:l,span:n.span,type:y[n.render],options:n.renderProps?.options,componentProps:n.renderProps};if(typeof n.render=="function"){const f=n.render,s=f();return typeof s=="string"&&s in y?{key:a,label:l,span:n.span,type:y[s],options:n.renderProps?.options,componentProps:n.renderProps}:e.isVNode(s)?{key:a,label:l,span:n.span,type:"slot",slotName:`legacy:${a}`}:{key:a,label:l,span:n.span,type:"component",component:s,componentProps:n.renderProps}}return n.render?{key:a,label:l,span:n.span,type:"component",component:n.render,componentProps:n.renderProps}:{key:a,label:l,span:n.span,type:"input",componentProps:n.renderProps}}function B(n){return n.map(a=>"type"in a&&"key"in a?a:$(a))}function C(n){if("type"in n&&"key"in n||typeof n.render!="function")return null;const a=n.render,l=a();return e.isVNode(l)?l:null}const I={class:"ma-search-panel"},F={key:0,class:"ma-search-panel__card ma-search-panel__header"},W={class:"ma-search-panel__title-wrap"},R={key:0,class:"ma-search-panel__title"},U={key:1,class:"ma-search-panel__subtitle"},j={class:"ma-search-panel__card"},q={class:"ma-search-panel__field ma-search-panel__field--full"},O={class:"ma-search-panel__actions"},N=e.defineComponent({__name:"SearchPanel",props:{items:{},modelValue:{default:()=>({})},defaultValue:{default:()=>({})},title:{},subtitle:{},labelWidth:{default:"90px"},cols:{default:3},fold:{type:Boolean,default:!1},foldRows:{default:1},searchText:{default:"搜索"},resetText:{default:"重置"},foldText:{default:"展开搜索"},unfoldText:{default:"收起搜索"}},emits:["update:modelValue","search","reset"],setup(n,{emit:a}){const l=n,f=a;function s(){const t={};return h.value.forEach(r=>{t[r.key]=l.defaultValue?.[r.key]}),t}const p=e.ref({...s(),...l.modelValue});e.watch(()=>[l.items,l.defaultValue,l.modelValue],()=>{p.value={...s(),...l.modelValue}},{deep:!0});function E(t){p.value=t,f("update:modelValue",t)}const S=e.ref(typeof window>"u"?1440:window.innerWidth),h=e.computed(()=>B(l.items));function K(t){if(typeof t=="number")return t;const r=S.value;return r>=1920?t.xl??4:r>=1200?t.lg??3:r>=992?t.md??2:r>=768?t.sm??2:t.xs??1}function k(){typeof window<"u"&&(S.value=window.innerWidth)}e.onMounted(()=>{k(),typeof window<"u"&&window.addEventListener("resize",k)}),e.onBeforeUnmount(()=>{typeof window<"u"&&window.removeEventListener("resize",k)});const m=e.computed(()=>Math.max(K(l.cols),1)),x=e.computed(()=>Math.max(m.value*l.foldRows-1,0)),u=e.ref(l.fold);e.watch(()=>l.fold,t=>{u.value=t});const P=e.computed(()=>l.items.length>x.value),A=e.computed(()=>u.value&&P.value?h.value.slice(0,x.value):h.value),G=e.computed(()=>({gridTemplateColumns:`repeat(${m.value}, minmax(0, 1fr))`}));function V(t,r){E({...p.value,[t]:r})}function H(){const t=s();E(t),f("reset",t)}function g(){const t={};Object.entries(p.value).forEach(([r,o])=>{o!==""&&o!==void 0&&o!==null&&(t[r]=o)}),f("search",t)}function J(t){return t.type==="input"||t.type==="select"||t.type==="date-picker"||t.type==="input-number"}function Q(t){return t.type==="component"}function X(t){return l.items.find(r=>"type"in r&&"key"in r?r.key===t.key:t.key===(typeof r.prop=="function"?r.prop():r.prop))}function Y(t){return t.type==="slot"?t.slotName:`legacy:${t.key}`}function T(t){const r=X(t);return r?C(r):null}return(t,r)=>(e.openBlock(),e.createElementBlock("div",I,[n.title||n.subtitle||t.$slots.actions?(e.openBlock(),e.createElementBlock("div",F,[e.createElementVNode("div",W,[n.title?(e.openBlock(),e.createElementBlock("div",R,e.toDisplayString(n.title),1)):e.createCommentVNode("",!0),n.subtitle?(e.openBlock(),e.createElementBlock("div",U,e.toDisplayString(n.subtitle),1)):e.createCommentVNode("",!0)]),e.renderSlot(t.$slots,"actions")])):e.createCommentVNode("",!0),e.createElementVNode("div",j,[e.createVNode(e.unref(i.ElForm),{model:p.value,"label-width":n.labelWidth,onSubmit:e.withModifiers(g,["prevent"])},{default:e.withCtx(()=>[e.createElementVNode("div",{class:"ma-search-panel__grid",style:e.normalizeStyle(G.value)},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(A.value,o=>(e.openBlock(),e.createBlock(e.unref(i.ElFormItem),{key:o.key,class:"ma-search-panel__item",label:e.unref(b)(o.label),style:e.normalizeStyle(o.span?{gridColumn:`span ${o.span}`}:void 0)},{default:e.withCtx(()=>[e.createElementVNode("div",q,[J(o)?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(e.unref(M)(o.type)),e.mergeProps({key:0,"model-value":p.value[o.key]},{ref_for:!0},o.componentProps,{class:"ma-search-panel__field--full",onKeyup:e.withKeys(g,["enter"]),"onUpdate:modelValue":d=>V(o.key,d)}),{default:e.withCtx(()=>[o.type==="select"?(e.openBlock(!0),e.createElementBlock(e.Fragment,{key:0},e.renderList(o.options??[],d=>(e.openBlock(),e.createBlock(e.unref(i.ElOption),{key:String(d.value),label:d.label,value:d.value},null,8,["label","value"]))),128)):e.createCommentVNode("",!0)]),_:2},1040,["model-value","onUpdate:modelValue"])):Q(o)?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(o.component),e.mergeProps({key:1,"model-value":p.value[o.key]},{ref_for:!0},o.componentProps,{style:e.unref(z)(o),class:"ma-search-panel__field--full","onUpdate:modelValue":d=>V(o.key,d)}),null,16,["model-value","style","onUpdate:modelValue"])):e.renderSlot(t.$slots,Y(o),{key:2,item:o,model:p.value,update:d=>V(o.key,d)},()=>[T(o)?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(T(o)),{key:0})):e.createCommentVNode("",!0)])])]),_:2},1032,["label","style"]))),128)),e.createElementVNode("div",{class:"ma-search-panel__actions-wrap",style:e.normalizeStyle({gridColumn:`${m.value} / ${m.value+1}`})},[e.createElementVNode("div",O,[e.createVNode(e.unref(i.ElButton),{type:"primary",onClick:g},{icon:e.withCtx(()=>[e.createVNode(e.unref(w.Icon),{icon:"heroicons:magnifying-glass"})]),default:e.withCtx(()=>[e.createTextVNode(" "+e.toDisplayString(n.searchText),1)]),_:1}),e.createVNode(e.unref(i.ElButton),{onClick:H},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(n.resetText),1)]),_:1}),P.value?(e.openBlock(),e.createBlock(e.unref(i.ElLink),{key:0,type:"primary",underline:"never",class:"ma-search-panel__fold-toggle",onClick:r[0]||(r[0]=e.withModifiers(o=>u.value=!u.value,["prevent"]))},{icon:e.withCtx(()=>[e.createVNode(e.unref(w.Icon),{icon:u.value?"material-symbols:keyboard-arrow-down":"material-symbols:keyboard-arrow-up"},null,8,["icon"])]),default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(u.value?n.foldText:n.unfoldText)+" ",1)]),_:1})):e.createCommentVNode("",!0)])],4)],4)]),_:3},8,["model","label-width"])])]))}});c.SearchPanel=N,c.default=N,c.normalizeSearchPanelItems=B,c.resolveLabelValue=b,c.resolveLegacyVNode=C,c.resolvePropValue=_,Object.defineProperties(c,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
@@ -0,0 +1,6 @@
1
+ import { type VNode } from 'vue';
2
+ import type { SearchPanelItem, SearchPanelLabel, SearchPanelProp, SearchPanelInputItem } from './types';
3
+ export declare function resolveLabelValue(label: SearchPanelLabel | undefined): string;
4
+ export declare function resolvePropValue(prop: SearchPanelProp | undefined): string;
5
+ export declare function normalizeSearchPanelItems(items: SearchPanelInputItem[]): SearchPanelItem[];
6
+ export declare function resolveLegacyVNode(item: SearchPanelInputItem): VNode | VNode[] | null;
@@ -0,0 +1,7 @@
1
+ import type { CSSProperties } from 'vue';
2
+ import type { SearchPanelComponentItem } from '../types';
3
+ /**
4
+ * 库版约定:自定义组件至少要能承接 class/style/attrs 到实际输入控件。
5
+ * 第一版骨架仅做参数收口,不在这里实现复杂适配器。
6
+ */
7
+ export declare function resolveComponentStyle(item: SearchPanelComponentItem): CSSProperties | undefined;
@@ -0,0 +1,5 @@
1
+ import { ElOption } from 'element-plus';
2
+ import type { Component } from 'vue';
3
+ import type { SearchPanelBuiltinType } from '../types';
4
+ export declare function getBuiltinRenderer(type: SearchPanelBuiltinType): Component;
5
+ export { ElOption };
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .ma-search-panel{display:flex;flex-direction:column;gap:12px}.ma-search-panel__card{background:#fff;border:1px solid #ebeef5;border-radius:8px;padding:16px;box-sizing:border-box}.ma-search-panel__header{display:flex;align-items:center;justify-content:space-between;gap:16px}.ma-search-panel__title-wrap{display:flex;align-items:center;gap:12px}.ma-search-panel__title{font-size:16px;line-height:24px;font-weight:600;color:#303133}.ma-search-panel__subtitle{font-size:13px;line-height:20px;color:#909399}.ma-search-panel__grid{display:grid;gap:12px 16px;align-items:start}.ma-search-panel__item,.ma-search-panel__field,.ma-search-panel__field>*{min-width:0}.ma-search-panel__item{margin-bottom:0}.ma-search-panel__field--full,.ma-search-panel__field--full>*{width:100%}.ma-search-panel__actions-wrap{align-self:start}.ma-search-panel__actions{display:flex;align-items:center;justify-content:flex-end;gap:12px}.ma-search-panel__fold-toggle{flex-shrink:0}
@@ -0,0 +1,71 @@
1
+ import type { CSSProperties, Component, VNode } from 'vue';
2
+ export interface SearchPanelOption {
3
+ label: string;
4
+ value: string | number | boolean;
5
+ }
6
+ export type SearchPanelLabel = string | (() => string);
7
+ export type SearchPanelProp = string | (() => string);
8
+ export interface ColsConfig {
9
+ xl?: number;
10
+ lg?: number;
11
+ md?: number;
12
+ sm?: number;
13
+ xs?: number;
14
+ }
15
+ export type SearchPanelCols = number | ColsConfig;
16
+ export type SearchPanelBuiltinType = 'input' | 'select' | 'date-picker' | 'input-number';
17
+ export type SearchPanelBuiltinAliasType = SearchPanelBuiltinType | 'inputNumber';
18
+ export type LegacySearchPanelRender = SearchPanelBuiltinAliasType | (() => Component | VNode | string) | Component;
19
+ export interface SearchPanelBaseItem {
20
+ key: string;
21
+ label: SearchPanelLabel;
22
+ span?: number;
23
+ labelWidth?: string;
24
+ }
25
+ export interface SearchPanelBuiltinItem extends SearchPanelBaseItem {
26
+ type: SearchPanelBuiltinType;
27
+ options?: SearchPanelOption[];
28
+ componentProps?: Record<string, unknown>;
29
+ }
30
+ export interface SearchPanelComponentItem extends SearchPanelBaseItem {
31
+ type: 'component';
32
+ component: unknown;
33
+ componentProps?: Record<string, unknown>;
34
+ fullWidth?: boolean;
35
+ }
36
+ export interface SearchPanelSlotItem extends SearchPanelBaseItem {
37
+ type: 'slot';
38
+ slotName: string;
39
+ }
40
+ export type SearchPanelItem = SearchPanelBuiltinItem | SearchPanelComponentItem | SearchPanelSlotItem;
41
+ export interface LegacySearchPanelItem {
42
+ label?: SearchPanelLabel;
43
+ prop?: SearchPanelProp;
44
+ render?: LegacySearchPanelRender;
45
+ renderProps?: Record<string, unknown>;
46
+ span?: number;
47
+ offset?: number;
48
+ }
49
+ export type SearchPanelInputItem = SearchPanelItem | LegacySearchPanelItem;
50
+ export interface SearchPanelProps {
51
+ items: SearchPanelInputItem[];
52
+ modelValue?: Record<string, unknown>;
53
+ defaultValue?: Record<string, unknown>;
54
+ title?: string;
55
+ subtitle?: string;
56
+ labelWidth?: string;
57
+ cols?: SearchPanelCols;
58
+ fold?: boolean;
59
+ foldRows?: number;
60
+ searchText?: string;
61
+ resetText?: string;
62
+ foldText?: string;
63
+ unfoldText?: string;
64
+ }
65
+ export interface SearchPanelContext {
66
+ item: SearchPanelItem;
67
+ model: Record<string, unknown>;
68
+ update: (key: string, value: unknown) => void;
69
+ style?: CSSProperties;
70
+ }
71
+ export type SearchPanelSlotRenderer = (context: SearchPanelContext) => VNode | VNode[] | null;
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@mywzzz/mineadmin-search-panel",
3
+ "version": "1.0.7",
4
+ "type": "module",
5
+ "description": "Library-ready search panel component for MineAdmin and Vue 3 projects",
6
+ "main": "dist/index.umd.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.umd.cjs"
14
+ },
15
+ "./style.css": "./dist/style.css"
16
+ },
17
+ "files": ["dist"],
18
+ "sideEffects": ["**/*.css"],
19
+ "scripts": {
20
+ "dev": "vite --config vite.config.ts",
21
+ "typecheck": "vue-tsc --project tsconfig.json --noEmit",
22
+ "build:types": "vue-tsc --project tsconfig.json --declaration --emitDeclarationOnly --outDir dist",
23
+ "build": "vite build --config vite.config.ts && npm run build:types",
24
+ "pack:dry": "npm pack --dry-run",
25
+ "prepublishOnly": "npm run typecheck && npm run build"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "registry": "https://registry.npmjs.org/"
30
+ },
31
+ "keywords": [
32
+ "vue3",
33
+ "element-plus",
34
+ "search-panel",
35
+ "mineadmin"
36
+ ],
37
+ "peerDependencies": {
38
+ "@iconify/vue": "^4.3.0",
39
+ "element-plus": "^2.11.8",
40
+ "vue": "^3.5.0"
41
+ },
42
+ "devDependencies": {
43
+ "@vitejs/plugin-vue": "^6.0.7",
44
+ "typescript": "^5.9.3",
45
+ "vite": "^7.2.4",
46
+ "vue-tsc": "^2.2.12"
47
+ }
48
+ }