@ikun2274/spiritechoui 0.1.35 → 0.1.40
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/dist/spiritechoui.es.js +146 -7
- package/dist/spiritechoui.umd.js +146 -7
- package/dist/style.css +24 -0
- package/package.json +6 -3
- package/readme.md +10 -1
- package/src/components/SpInput.vue +214 -0
- package/src/index.js +6 -1
- package/src/registerComponents.js +11 -11
- package/src/type/global.d.ts +10 -0
- package/tsconfig.json +24 -0
- package/vite.config.js +2 -2
package/dist/spiritechoui.es.js
CHANGED
|
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
-
import { openBlock, createElementBlock, normalizeClass, normalizeStyle, renderSlot, createElementVNode, toDisplayString } from "vue";
|
|
7
|
+
import { openBlock, createElementBlock, normalizeClass, normalizeStyle, renderSlot, createElementVNode, toDisplayString, defineComponent, withDirectives, vModelDynamic } from "vue";
|
|
8
8
|
class MyUI {
|
|
9
9
|
/**
|
|
10
10
|
* 支持 app.use(MyUI) 注册的静态方法
|
|
@@ -59,7 +59,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
59
59
|
}
|
|
60
60
|
return target;
|
|
61
61
|
};
|
|
62
|
-
const _sfc_main = {
|
|
62
|
+
const _sfc_main$1 = {
|
|
63
63
|
props: {
|
|
64
64
|
// 文本样式类型
|
|
65
65
|
textType: {
|
|
@@ -106,8 +106,8 @@ const _sfc_main = {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
};
|
|
109
|
-
const _hoisted_1 = ["disabled"];
|
|
110
|
-
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
109
|
+
const _hoisted_1$1 = ["disabled"];
|
|
110
|
+
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
|
|
111
111
|
return openBlock(), createElementBlock("button", {
|
|
112
112
|
class: normalizeClass(["SpiritEchoBtn", [
|
|
113
113
|
`SpiritEchoBtn--${$props.type}`,
|
|
@@ -124,16 +124,155 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
124
124
|
style: normalizeStyle($props.textStyle)
|
|
125
125
|
}, toDisplayString($props.text), 7)
|
|
126
126
|
])
|
|
127
|
-
], 14, _hoisted_1);
|
|
127
|
+
], 14, _hoisted_1$1);
|
|
128
|
+
}
|
|
129
|
+
const myButton = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1]]);
|
|
130
|
+
const _sfc_main = defineComponent({
|
|
131
|
+
name: "SpInput",
|
|
132
|
+
props: {
|
|
133
|
+
modelValue: {
|
|
134
|
+
type: [String, Number],
|
|
135
|
+
default: ""
|
|
136
|
+
},
|
|
137
|
+
type: {
|
|
138
|
+
type: String,
|
|
139
|
+
default: "text"
|
|
140
|
+
},
|
|
141
|
+
placeholder: { type: String, default: "" },
|
|
142
|
+
disabled: { type: Boolean, default: false },
|
|
143
|
+
maxlength: {
|
|
144
|
+
type: Number,
|
|
145
|
+
default: void 0
|
|
146
|
+
},
|
|
147
|
+
name: { type: String, default: "" },
|
|
148
|
+
readonly: { type: Boolean, default: false },
|
|
149
|
+
// 新增:校验规则
|
|
150
|
+
rules: {
|
|
151
|
+
type: Object,
|
|
152
|
+
default: () => ({})
|
|
153
|
+
// 默认空对象,不影响原有使用
|
|
154
|
+
},
|
|
155
|
+
// 新增:自定义校验函数(被动触发)
|
|
156
|
+
customValidator: {
|
|
157
|
+
type: Function,
|
|
158
|
+
default: void 0
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
data() {
|
|
162
|
+
return {
|
|
163
|
+
inputLength: 0,
|
|
164
|
+
inputValue: this.modelValue + ""
|
|
165
|
+
//
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
emits: [
|
|
169
|
+
"update:modelValue",
|
|
170
|
+
"focus",
|
|
171
|
+
"blur",
|
|
172
|
+
"change",
|
|
173
|
+
"validateResult"
|
|
174
|
+
// "cca",
|
|
175
|
+
],
|
|
176
|
+
created() {
|
|
177
|
+
},
|
|
178
|
+
watch: {
|
|
179
|
+
inputValue(newVal) {
|
|
180
|
+
console.log("子组件emit的值:", newVal);
|
|
181
|
+
this.inputLength = newVal.length;
|
|
182
|
+
this.$emit("update:modelValue", newVal);
|
|
183
|
+
this.doValidate(newVal);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
methods: {
|
|
187
|
+
// 新增:核心校验方法(被动触发,逻辑和之前一致)
|
|
188
|
+
doValidate(val) {
|
|
189
|
+
let result = { isValid: true, message: "" };
|
|
190
|
+
const { rules, customValidator } = this;
|
|
191
|
+
if (rules) {
|
|
192
|
+
const finalMaxlength = rules.maxlength ?? this.maxlength;
|
|
193
|
+
if (finalMaxlength !== void 0 && val.length > finalMaxlength) {
|
|
194
|
+
result = {
|
|
195
|
+
isValid: false,
|
|
196
|
+
message: `输入不能/超过/exceed/${finalMaxlength}位`
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (rules.minlength !== void 0 && val.length < rules.minlength) {
|
|
200
|
+
result = {
|
|
201
|
+
isValid: false,
|
|
202
|
+
message: `输入不能少于${rules.minlength}位`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (rules.type) {
|
|
206
|
+
switch (rules.type) {
|
|
207
|
+
case "number":
|
|
208
|
+
if (!/^\d+$/.test(val))
|
|
209
|
+
result = { isValid: false, message: "请输入纯数字" };
|
|
210
|
+
break;
|
|
211
|
+
case "letter":
|
|
212
|
+
if (!/^[a-zA-Z]+$/.test(val))
|
|
213
|
+
result = { isValid: false, message: "请输入纯字母" };
|
|
214
|
+
break;
|
|
215
|
+
case "mixed":
|
|
216
|
+
if (!/^[a-zA-Z0-9]+$/.test(val))
|
|
217
|
+
result = { isValid: false, message: "请输入字母+数字组合" };
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (rules.pattern && val) {
|
|
222
|
+
let reg;
|
|
223
|
+
if (typeof rules.pattern === "string") {
|
|
224
|
+
reg = new RegExp(rules.pattern, rules.patternFlags);
|
|
225
|
+
} else {
|
|
226
|
+
reg = rules.pattern;
|
|
227
|
+
}
|
|
228
|
+
if (!reg.test(val)) {
|
|
229
|
+
result = {
|
|
230
|
+
isValid: false,
|
|
231
|
+
// 优先用自定义提示,否则用默认文案
|
|
232
|
+
message: rules.patternMessage || "输入格式不正确"
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (result.isValid && customValidator) {
|
|
238
|
+
result = customValidator(val);
|
|
239
|
+
}
|
|
240
|
+
this.$emit("validateResult", result);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
const SpInput_vue_vue_type_style_index_0_lang = "";
|
|
245
|
+
const _hoisted_1 = { class: "SPInput-container" };
|
|
246
|
+
const _hoisted_2 = ["type", "placeholder", "disabled", "maxlength", "name", "readonly"];
|
|
247
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
248
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
249
|
+
withDirectives(createElementVNode("input", {
|
|
250
|
+
type: _ctx.type,
|
|
251
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.inputValue = $event),
|
|
252
|
+
placeholder: _ctx.placeholder,
|
|
253
|
+
disabled: _ctx.disabled,
|
|
254
|
+
maxlength: _ctx.maxlength,
|
|
255
|
+
name: _ctx.name,
|
|
256
|
+
readonly: _ctx.readonly,
|
|
257
|
+
onFocus: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("focus", $event)),
|
|
258
|
+
onBlur: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("blur", $event)),
|
|
259
|
+
onChange: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("change", $event)),
|
|
260
|
+
class: "SPInput"
|
|
261
|
+
}, null, 40, _hoisted_2), [
|
|
262
|
+
[vModelDynamic, _ctx.inputValue]
|
|
263
|
+
])
|
|
264
|
+
]);
|
|
128
265
|
}
|
|
129
|
-
const
|
|
266
|
+
const myinput = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
|
|
130
267
|
const injector = new ComponentInjector(MyUI);
|
|
131
268
|
injector.register("form", "myButton", myButton, {
|
|
132
269
|
type: "default",
|
|
133
270
|
size: "medium"
|
|
134
271
|
});
|
|
272
|
+
injector.register("form", "myinput", myinput);
|
|
135
273
|
MyUI.install = (app) => {
|
|
136
|
-
app.component("
|
|
274
|
+
app.component("SPE-Button", MyUI.form.myButton({}));
|
|
275
|
+
app.component("SPE-Input", MyUI.form.myinput({}));
|
|
137
276
|
};
|
|
138
277
|
export {
|
|
139
278
|
MyUI
|
package/dist/spiritechoui.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
(function(global, factory) {
|
|
2
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue")) : typeof define === "function" && define.amd ? define(["exports", "vue"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue")) : typeof define === "function" && define.amd ? define(["exports", "vue"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.SpiritEchoUI = {}, global.Vue));
|
|
3
3
|
})(this, function(exports2, vue) {
|
|
4
4
|
"use strict";var __defProp = Object.defineProperty;
|
|
5
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -62,7 +62,7 @@ var __publicField = (obj, key, value) => {
|
|
|
62
62
|
}
|
|
63
63
|
return target;
|
|
64
64
|
};
|
|
65
|
-
const _sfc_main = {
|
|
65
|
+
const _sfc_main$1 = {
|
|
66
66
|
props: {
|
|
67
67
|
// 文本样式类型
|
|
68
68
|
textType: {
|
|
@@ -109,8 +109,8 @@ var __publicField = (obj, key, value) => {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
};
|
|
112
|
-
const _hoisted_1 = ["disabled"];
|
|
113
|
-
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
112
|
+
const _hoisted_1$1 = ["disabled"];
|
|
113
|
+
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
|
|
114
114
|
return vue.openBlock(), vue.createElementBlock("button", {
|
|
115
115
|
class: vue.normalizeClass(["SpiritEchoBtn", [
|
|
116
116
|
`SpiritEchoBtn--${$props.type}`,
|
|
@@ -127,16 +127,155 @@ var __publicField = (obj, key, value) => {
|
|
|
127
127
|
style: vue.normalizeStyle($props.textStyle)
|
|
128
128
|
}, vue.toDisplayString($props.text), 7)
|
|
129
129
|
])
|
|
130
|
-
], 14, _hoisted_1);
|
|
130
|
+
], 14, _hoisted_1$1);
|
|
131
|
+
}
|
|
132
|
+
const myButton = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1]]);
|
|
133
|
+
const _sfc_main = vue.defineComponent({
|
|
134
|
+
name: "SpInput",
|
|
135
|
+
props: {
|
|
136
|
+
modelValue: {
|
|
137
|
+
type: [String, Number],
|
|
138
|
+
default: ""
|
|
139
|
+
},
|
|
140
|
+
type: {
|
|
141
|
+
type: String,
|
|
142
|
+
default: "text"
|
|
143
|
+
},
|
|
144
|
+
placeholder: { type: String, default: "" },
|
|
145
|
+
disabled: { type: Boolean, default: false },
|
|
146
|
+
maxlength: {
|
|
147
|
+
type: Number,
|
|
148
|
+
default: void 0
|
|
149
|
+
},
|
|
150
|
+
name: { type: String, default: "" },
|
|
151
|
+
readonly: { type: Boolean, default: false },
|
|
152
|
+
// 新增:校验规则
|
|
153
|
+
rules: {
|
|
154
|
+
type: Object,
|
|
155
|
+
default: () => ({})
|
|
156
|
+
// 默认空对象,不影响原有使用
|
|
157
|
+
},
|
|
158
|
+
// 新增:自定义校验函数(被动触发)
|
|
159
|
+
customValidator: {
|
|
160
|
+
type: Function,
|
|
161
|
+
default: void 0
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
data() {
|
|
165
|
+
return {
|
|
166
|
+
inputLength: 0,
|
|
167
|
+
inputValue: this.modelValue + ""
|
|
168
|
+
//
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
emits: [
|
|
172
|
+
"update:modelValue",
|
|
173
|
+
"focus",
|
|
174
|
+
"blur",
|
|
175
|
+
"change",
|
|
176
|
+
"validateResult"
|
|
177
|
+
// "cca",
|
|
178
|
+
],
|
|
179
|
+
created() {
|
|
180
|
+
},
|
|
181
|
+
watch: {
|
|
182
|
+
inputValue(newVal) {
|
|
183
|
+
console.log("子组件emit的值:", newVal);
|
|
184
|
+
this.inputLength = newVal.length;
|
|
185
|
+
this.$emit("update:modelValue", newVal);
|
|
186
|
+
this.doValidate(newVal);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
methods: {
|
|
190
|
+
// 新增:核心校验方法(被动触发,逻辑和之前一致)
|
|
191
|
+
doValidate(val) {
|
|
192
|
+
let result = { isValid: true, message: "" };
|
|
193
|
+
const { rules, customValidator } = this;
|
|
194
|
+
if (rules) {
|
|
195
|
+
const finalMaxlength = rules.maxlength ?? this.maxlength;
|
|
196
|
+
if (finalMaxlength !== void 0 && val.length > finalMaxlength) {
|
|
197
|
+
result = {
|
|
198
|
+
isValid: false,
|
|
199
|
+
message: `输入不能/超过/exceed/${finalMaxlength}位`
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (rules.minlength !== void 0 && val.length < rules.minlength) {
|
|
203
|
+
result = {
|
|
204
|
+
isValid: false,
|
|
205
|
+
message: `输入不能少于${rules.minlength}位`
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (rules.type) {
|
|
209
|
+
switch (rules.type) {
|
|
210
|
+
case "number":
|
|
211
|
+
if (!/^\d+$/.test(val))
|
|
212
|
+
result = { isValid: false, message: "请输入纯数字" };
|
|
213
|
+
break;
|
|
214
|
+
case "letter":
|
|
215
|
+
if (!/^[a-zA-Z]+$/.test(val))
|
|
216
|
+
result = { isValid: false, message: "请输入纯字母" };
|
|
217
|
+
break;
|
|
218
|
+
case "mixed":
|
|
219
|
+
if (!/^[a-zA-Z0-9]+$/.test(val))
|
|
220
|
+
result = { isValid: false, message: "请输入字母+数字组合" };
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (rules.pattern && val) {
|
|
225
|
+
let reg;
|
|
226
|
+
if (typeof rules.pattern === "string") {
|
|
227
|
+
reg = new RegExp(rules.pattern, rules.patternFlags);
|
|
228
|
+
} else {
|
|
229
|
+
reg = rules.pattern;
|
|
230
|
+
}
|
|
231
|
+
if (!reg.test(val)) {
|
|
232
|
+
result = {
|
|
233
|
+
isValid: false,
|
|
234
|
+
// 优先用自定义提示,否则用默认文案
|
|
235
|
+
message: rules.patternMessage || "输入格式不正确"
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (result.isValid && customValidator) {
|
|
241
|
+
result = customValidator(val);
|
|
242
|
+
}
|
|
243
|
+
this.$emit("validateResult", result);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
const SpInput_vue_vue_type_style_index_0_lang = "";
|
|
248
|
+
const _hoisted_1 = { class: "SPInput-container" };
|
|
249
|
+
const _hoisted_2 = ["type", "placeholder", "disabled", "maxlength", "name", "readonly"];
|
|
250
|
+
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
251
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
|
|
252
|
+
vue.withDirectives(vue.createElementVNode("input", {
|
|
253
|
+
type: _ctx.type,
|
|
254
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.inputValue = $event),
|
|
255
|
+
placeholder: _ctx.placeholder,
|
|
256
|
+
disabled: _ctx.disabled,
|
|
257
|
+
maxlength: _ctx.maxlength,
|
|
258
|
+
name: _ctx.name,
|
|
259
|
+
readonly: _ctx.readonly,
|
|
260
|
+
onFocus: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("focus", $event)),
|
|
261
|
+
onBlur: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("blur", $event)),
|
|
262
|
+
onChange: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("change", $event)),
|
|
263
|
+
class: "SPInput"
|
|
264
|
+
}, null, 40, _hoisted_2), [
|
|
265
|
+
[vue.vModelDynamic, _ctx.inputValue]
|
|
266
|
+
])
|
|
267
|
+
]);
|
|
131
268
|
}
|
|
132
|
-
const
|
|
269
|
+
const myinput = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
|
|
133
270
|
const injector = new ComponentInjector(MyUI);
|
|
134
271
|
injector.register("form", "myButton", myButton, {
|
|
135
272
|
type: "default",
|
|
136
273
|
size: "medium"
|
|
137
274
|
});
|
|
275
|
+
injector.register("form", "myinput", myinput);
|
|
138
276
|
MyUI.install = (app) => {
|
|
139
|
-
app.component("
|
|
277
|
+
app.component("SPE-Button", MyUI.form.myButton({}));
|
|
278
|
+
app.component("SPE-Input", MyUI.form.myinput({}));
|
|
140
279
|
};
|
|
141
280
|
exports2.MyUI = MyUI;
|
|
142
281
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
package/dist/style.css
CHANGED
|
@@ -82,4 +82,28 @@
|
|
|
82
82
|
}
|
|
83
83
|
.SpiritEchoBtn:not(.SpiritEchoBtn--disabled):active {
|
|
84
84
|
transform: translateY(0);
|
|
85
|
+
}.SPInput-container {
|
|
86
|
+
width: 100%;
|
|
87
|
+
}
|
|
88
|
+
.SPInput {
|
|
89
|
+
width: 100%;
|
|
90
|
+
padding: 8px 12px;
|
|
91
|
+
height: 40px;
|
|
92
|
+
border: 2px solid #c3bfbf;
|
|
93
|
+
border-radius: 4px;
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
box-sizing: border-box;
|
|
96
|
+
background-color: rgba(255, 255, 255, 0.968627451);
|
|
97
|
+
}
|
|
98
|
+
.SPInput:focus {
|
|
99
|
+
outline: none;
|
|
100
|
+
border-color: #4096ff;
|
|
101
|
+
}
|
|
102
|
+
.SPInput:disabled {
|
|
103
|
+
background-color: #f5f7fa;
|
|
104
|
+
cursor: not-allowed;
|
|
105
|
+
opacity: 0.8;
|
|
106
|
+
}
|
|
107
|
+
.SPInput:read-only {
|
|
108
|
+
background-color: #f5f7fa;
|
|
85
109
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikun2274/spiritechoui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"main": "dist/spiritechoui.umd.cjs",
|
|
5
5
|
"module": "dist/spiritechoui.es.js",
|
|
6
6
|
"exports": {
|
|
@@ -14,9 +14,12 @@
|
|
|
14
14
|
"vue": "^3.0.0"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
+
"@types/node": "^24.10.1",
|
|
17
18
|
"@vitejs/plugin-vue": "^4.2.3",
|
|
19
|
+
"typescript": "^5.9.3",
|
|
18
20
|
"vite": "^4.4.9",
|
|
19
|
-
"vue": "^3.3.4"
|
|
21
|
+
"vue": "^3.3.4",
|
|
22
|
+
"vue-tsc": "^3.1.3"
|
|
20
23
|
},
|
|
21
24
|
"scripts": {
|
|
22
25
|
"build": "vite build"
|
|
@@ -26,6 +29,6 @@
|
|
|
26
29
|
"license": "ISC",
|
|
27
30
|
"description": "",
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"
|
|
32
|
+
"log": "^6.3.2"
|
|
30
33
|
}
|
|
31
34
|
}
|
package/readme.md
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# 我的第一个UI组件库 SpiritEchoUI
|
|
2
2
|
|
|
3
|
+
### 目前还是0.1.36版本
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
## 安装组件
|
|
5
7
|
|
|
6
8
|
```
|
|
7
9
|
npm install @ikun2274/spiritechoui
|
|
8
10
|
```
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
## 卸载组件
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
npm uninstall @ikun2274/spiritechoui
|
|
17
|
+
```
|
|
18
|
+
|
|
10
19
|
## 更新日志
|
|
11
20
|
|
|
12
21
|
#### 0.1.3 (2025-11-06-13:25)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
<!-- SpInput.vue -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="SPInput-container">
|
|
4
|
+
<input
|
|
5
|
+
:type="type"
|
|
6
|
+
v-model="inputValue"
|
|
7
|
+
:placeholder="placeholder"
|
|
8
|
+
:disabled="disabled"
|
|
9
|
+
:maxlength="maxlength"
|
|
10
|
+
:name="name"
|
|
11
|
+
:readonly="readonly"
|
|
12
|
+
@focus="$emit('focus', $event)"
|
|
13
|
+
@blur="$emit('blur', $event)"
|
|
14
|
+
@change="$emit('change', $event)"
|
|
15
|
+
class="SPInput" />
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* @component SpInput
|
|
22
|
+
* @description 带实时校验的输入框组件,支持双向数据绑定、自定义校验规则和函数,适用于表单场景的输入校验需求。
|
|
23
|
+
*
|
|
24
|
+
*
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { defineComponent, PropType } from "vue";
|
|
30
|
+
// 定义 rules 类型(和之前一致)
|
|
31
|
+
type InputRules = {
|
|
32
|
+
maxlength?: number;
|
|
33
|
+
minlength?: number;
|
|
34
|
+
type?: "number" | "letter" | "mixed";
|
|
35
|
+
// 新增:自定义正则(支持正则对象/字符串)
|
|
36
|
+
pattern?: RegExp | string;
|
|
37
|
+
// 新增:正则校验失败的提示文案(可选)
|
|
38
|
+
patternMessage?: string;
|
|
39
|
+
// 可选:正则的 flags(比如 i 忽略大小写,g 全局匹配)
|
|
40
|
+
patternFlags?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default defineComponent({
|
|
44
|
+
name: "SpInput",
|
|
45
|
+
|
|
46
|
+
props: {
|
|
47
|
+
modelValue: {
|
|
48
|
+
type: [String, Number] as PropType<string | number>,
|
|
49
|
+
default: "",
|
|
50
|
+
},
|
|
51
|
+
type: {
|
|
52
|
+
type: String as PropType<
|
|
53
|
+
"text" | "password" | "number" | "tel" | "email" | string
|
|
54
|
+
>,
|
|
55
|
+
default: "text",
|
|
56
|
+
},
|
|
57
|
+
placeholder: { type: String, default: "" },
|
|
58
|
+
disabled: { type: Boolean, default: false },
|
|
59
|
+
maxlength: {
|
|
60
|
+
type: Number as PropType<number | undefined>,
|
|
61
|
+
default: undefined,
|
|
62
|
+
},
|
|
63
|
+
name: { type: String, default: "" },
|
|
64
|
+
readonly: { type: Boolean, default: false },
|
|
65
|
+
// 新增:校验规则
|
|
66
|
+
rules: {
|
|
67
|
+
type: Object as PropType<InputRules>,
|
|
68
|
+
default: () => ({}), // 默认空对象,不影响原有使用
|
|
69
|
+
},
|
|
70
|
+
// 新增:自定义校验函数(被动触发)
|
|
71
|
+
customValidator: {
|
|
72
|
+
type: Function as PropType<
|
|
73
|
+
(val: string) => { isValid: boolean; message: string }
|
|
74
|
+
>,
|
|
75
|
+
default: undefined,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
data() {
|
|
80
|
+
return {
|
|
81
|
+
inputLength: 0,
|
|
82
|
+
inputValue: this.modelValue + "",
|
|
83
|
+
//
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
emits: [
|
|
88
|
+
"update:modelValue",
|
|
89
|
+
"focus",
|
|
90
|
+
"blur",
|
|
91
|
+
"change",
|
|
92
|
+
"validateResult",
|
|
93
|
+
// "cca",
|
|
94
|
+
],
|
|
95
|
+
created() {},
|
|
96
|
+
|
|
97
|
+
watch: {
|
|
98
|
+
inputValue(newVal) {
|
|
99
|
+
console.log("子组件emit的值:", newVal); // 退格到空时,应输出 ""
|
|
100
|
+
|
|
101
|
+
this.inputLength = newVal.length;
|
|
102
|
+
this.$emit("update:modelValue", newVal);
|
|
103
|
+
|
|
104
|
+
this.doValidate(newVal);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
methods: {
|
|
108
|
+
// 新增:核心校验方法(被动触发,逻辑和之前一致)
|
|
109
|
+
doValidate(val: string) {
|
|
110
|
+
let result = { isValid: true, message: "" };
|
|
111
|
+
const { rules, customValidator } = this;
|
|
112
|
+
|
|
113
|
+
// ① 执行 rules 基础校验(长度、类型)
|
|
114
|
+
if (rules) {
|
|
115
|
+
// 长度校验:rules.maxlength 优先级高于单独传的 maxlength
|
|
116
|
+
const finalMaxlength = rules.maxlength ?? this.maxlength;
|
|
117
|
+
if (finalMaxlength !== undefined && val.length > finalMaxlength) {
|
|
118
|
+
result = {
|
|
119
|
+
isValid: false,
|
|
120
|
+
message: `输入不能/超过/exceed/${finalMaxlength}位`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/////////
|
|
124
|
+
if (rules.minlength !== undefined && val.length < rules.minlength) {
|
|
125
|
+
result = {
|
|
126
|
+
isValid: false,
|
|
127
|
+
message: `输入不能少于${rules.minlength}位`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//////////////
|
|
131
|
+
// 类型校验(纯数字/纯字母/字母+数字)
|
|
132
|
+
if (rules.type) {
|
|
133
|
+
switch (rules.type) {
|
|
134
|
+
case "number":
|
|
135
|
+
if (!/^\d+$/.test(val))
|
|
136
|
+
result = { isValid: false, message: "请输入纯数字" };
|
|
137
|
+
break;
|
|
138
|
+
case "letter":
|
|
139
|
+
if (!/^[a-zA-Z]+$/.test(val))
|
|
140
|
+
result = { isValid: false, message: "请输入纯字母" };
|
|
141
|
+
break;
|
|
142
|
+
case "mixed":
|
|
143
|
+
if (!/^[a-zA-Z0-9]+$/.test(val))
|
|
144
|
+
result = { isValid: false, message: "请输入字母+数字组合" };
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//////////
|
|
149
|
+
// ③ 新增:自定义正则校验(优先级高于预设类型)
|
|
150
|
+
if (rules.pattern && val) {
|
|
151
|
+
// 处理正则:如果是字符串,转成 RegExp 对象(支持 flags)
|
|
152
|
+
let reg: RegExp;
|
|
153
|
+
if (typeof rules.pattern === "string") {
|
|
154
|
+
reg = new RegExp(rules.pattern, rules.patternFlags);
|
|
155
|
+
} else {
|
|
156
|
+
reg = rules.pattern;
|
|
157
|
+
}
|
|
158
|
+
// 执行正则校验
|
|
159
|
+
if (!reg.test(val)) {
|
|
160
|
+
result = {
|
|
161
|
+
isValid: false,
|
|
162
|
+
// 优先用自定义提示,否则用默认文案
|
|
163
|
+
message: rules.patternMessage || "输入格式不正确",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//////////////
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ② 执行父组件传的 customValidator(被动触发,优先级更高)
|
|
172
|
+
if (result.isValid && customValidator) {
|
|
173
|
+
result = customValidator(val);
|
|
174
|
+
}
|
|
175
|
+
///////////////////////
|
|
176
|
+
// ③ 抛校验结果给父组件(父组件监听这个事件)
|
|
177
|
+
this.$emit("validateResult", result);
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
</script>
|
|
182
|
+
|
|
183
|
+
<style lang="scss">
|
|
184
|
+
// 样式保持不变,Sass兼容CSS语法,可直接使用
|
|
185
|
+
.SPInput-container {
|
|
186
|
+
width: 100%;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.SPInput {
|
|
190
|
+
width: 100%;
|
|
191
|
+
padding: 8px 12px;
|
|
192
|
+
height: 40px;
|
|
193
|
+
border: 2px solid #c3bfbf;
|
|
194
|
+
border-radius: 4px;
|
|
195
|
+
font-size: 14px;
|
|
196
|
+
box-sizing: border-box;
|
|
197
|
+
|
|
198
|
+
background-color: #fffffff7;
|
|
199
|
+
&:focus {
|
|
200
|
+
outline: none;
|
|
201
|
+
border-color: #4096ff;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
&:disabled {
|
|
205
|
+
background-color: #f5f7fa;
|
|
206
|
+
cursor: not-allowed;
|
|
207
|
+
opacity: 0.8;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
&:read-only {
|
|
211
|
+
background-color: #f5f7fa;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
</style>
|
package/src/index.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
// 组件库内部的 registerComponents.js 示例
|
|
2
2
|
import MyUI from './MyUI';
|
|
3
3
|
import ComponentInjector from './ComponentInjector';
|
|
4
|
+
|
|
5
|
+
//引入组件
|
|
4
6
|
import myButton from './components/Button.vue';
|
|
7
|
+
import myinput from './components/SpInput.vue';
|
|
5
8
|
|
|
6
9
|
const injector = new ComponentInjector(MyUI);
|
|
10
|
+
|
|
7
11
|
injector.register('form', 'myButton', myButton, {
|
|
8
12
|
type: 'default',
|
|
9
13
|
size: 'medium'
|
|
10
14
|
});
|
|
11
15
|
|
|
12
16
|
//假设有第二个组件
|
|
17
|
+
injector.register('form', 'myinput', myinput);
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
injector.register('form', 'mytext', mytext, {
|
|
16
|
-
type: 'default',
|
|
17
|
-
size: 'medium'
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*/
|
|
19
|
+
//Spirit EchoUI
|
|
22
20
|
|
|
21
|
+
//全局激活组件
|
|
23
22
|
|
|
24
23
|
MyUI.install = (app) => {
|
|
25
|
-
app.component('
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
app.component('SPE-Button', MyUI.form.myButton({}));
|
|
25
|
+
app.component('SPE-Input', MyUI.form.myinput({}));
|
|
26
|
+
|
|
27
|
+
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export default MyUI;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/types/global.d.ts
|
|
2
|
+
import { SpInput } from "../components/SpInput.vue";
|
|
3
|
+
import { Button } from "../components/Button.vue"; // 若有其他组件也需声明
|
|
4
|
+
|
|
5
|
+
declare module "vue" {
|
|
6
|
+
export interface GlobalComponents {
|
|
7
|
+
SpInput: typeof SpInput;
|
|
8
|
+
Button: typeof Button;
|
|
9
|
+
}
|
|
10
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "Node",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"jsx": "preserve",
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"lib": ["ES2020", "DOM"],
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"allowJs": true, // 允许同时编译 JS 文件
|
|
17
|
+
"vueCompilerOptions": {
|
|
18
|
+
"scriptSetup": true,
|
|
19
|
+
"strictTemplates": true
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts"],
|
|
23
|
+
"exclude": ["node_modules"]
|
|
24
|
+
}
|
package/vite.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 组件库的 vite.config.
|
|
1
|
+
// 组件库的 vite.config.ts
|
|
2
2
|
import { defineConfig } from 'vite';
|
|
3
3
|
import vue from '@vitejs/plugin-vue';
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ export default defineConfig({
|
|
|
7
7
|
build: {
|
|
8
8
|
lib: {
|
|
9
9
|
entry: './src/index.js',
|
|
10
|
-
name: '
|
|
10
|
+
name: 'SpiritEchoUI',
|
|
11
11
|
fileName: (format) => `spiritechoui.${format}.js`
|
|
12
12
|
},
|
|
13
13
|
rollupOptions: {
|