8000 Dev (#53) · DvShu/litos-ui-vue@3647e13 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3647e13

Browse files
DvShuTennyTenny
authored
Dev (#53)
* fix(Theme): 增加 change 事件 * fix(Pagination): 修复点击报错 Signed-off-by: Tenny <joel.shu@qq.com> * fix(useFormReset): 增加设置函数 Signed-off-by: Tenny <joel.shu@qq.com> * refactor: 重构设置函数 Signed-off-by: Tenny <joel.shu@qq.com> * fix(Form): 表单增加 validate、validateField、clearValidate 手动验证函数 * fix(Input): 增加只允许输入数字 --------- Signed-off-by: Tenny <joel.shu@qq.com> Co-authored-by: Tenny <joel.shu@qq.com> Co-authored-by: Tenny <tenny.shu@foxmail.com>
1 parent 6a3edd5 commit 3647e13

File tree

14 files changed

+176
-11
lines changed

14 files changed

+176
-11
lines changed

docs/components/form.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,12 @@
240240
| `label-width` | 标签宽度,例如 `50px` | `string` | - |
241241
| `error` | 表单域验证错误时的提示信息。设置该值会导致表单验证状态变为 `error`,并显示该错误信息。 | `string` | - |
242242
| `label-position` | 标签位置, *可选* | `left``right``top` | - |
243+
244+
### Form Methods
245+
246+
<!-- prettier-ignore -->
247+
| 方法名 | 说明 | 参数 |
248+
| ------ | ---- | ---- |
249+
| `validate` | 对整个表单进行校验的方法 | `() => void` |
250+
| `validateField` | 对部分表单字段进行校验的方法 | `(field: string \| string[]) => void` |
251+
| `clearValidate` | 移除表单项的校验结果 | `-` |

docs/components/input.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010

1111
const inputInt = ref('')
1212

13+
const input1 = ref('')
14+
const input2 = ref('')
15+
const input3 = ref('')
16+
1317
function numericParse(value: string) {
1418
let val = parseInt(value, 10)
1519
if (Number.isNaN(val)) {
@@ -64,6 +68,27 @@
6468
</CodePreview>
6569
</ClientOnly>
6670

71+
### 允许输入的值
72+
73+
通过传递 `allow-input` 来限制输入的值。`integer` 只能输入正整数, `number` 只能输入正数;以 `-` 开头表明允许输入负数; 以 `.2` 结尾表明小数点后精度; 例如: `-number.4` 表明允许输入数字,且小数点后保留4位小数。
74+
75+
<ClientOnly>
76+
<CodePreview>
77+
<textarea lang="vue" v-pre>
78+
<script setup lang="ts">
79+
</script>
80+
<template>
81+
<hr />
82+
</template>
83+
</textarea>
84+
<template #preview>
85+
<Input v-model="input1" placeholder="只能输入正整数" allow-input="integer" />
86+
<Input v-model="input2" placeholder="只能输入整数" allow-input="-integer" />
87+
<Input v-model="input3" placeholder="输入数字,保留2位小数" allow-input="-number.2" />
88+
</template>
89+
</CodePreview>
90+
</ClientOnly>
91+
6792
### 禁用状态
6893

6994
通过 `disabled` 属性设置输入框为禁用状态。
@@ -87,6 +112,7 @@
87112
| `model-value` / `v-model` | 绑定值 | `string` ||
88113
| `placeholder` | 占位文本 | `string` ||
89114
| `parser` | 输入时解析值 | `(value: string) => string` ||
115+
| `allow-input` | 允许输入的值; `number``integer`,前面包含 `-` 表明允许小数, 以 `.2` 结尾表明小数点后精度 | `string` | - |
90116< F438 code class="diff-text syntax-highlighted-line">

91117
### Input Exposes
92118

eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default [
1818
{
1919
rules: {
2020
'vue/multi-word-component-names': 'off',
21+
'@typescript-eslint/no-explicit-any': 'off',
2122
},
2223
},
2324
];

src/components/Pagination.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@
3131
<template v-else>
3232
<!-- 首页按钮 -->
3333
<li :class="['nt-pagination-item', currentPage === 1 ? 'is-active' : '']">
34-
<a href="javascript:void" title="1" @click="handleTo(1)">1</a>
34+
<a href="javascript:void(0)" title="1" @click="handleTo(1)">1</a>
3535
</li>
3636
<!-- 向前5页 -->
3737
<li class="nt-pagination-item" v-if="currentPage > 4">
3838
<a
39-
href="javascript:void"
39+
href="javascript:void(0)"
4040
@mouseenter="handleMoreHover(1)"
4141
@mouseleave="handleMoreHover(0)"
4242
title="向前5页"
@@ -52,14 +52,14 @@
5252
:key="n"
5353
:class="['nt-pagination-item', currentPage === n ? 'is-active' : '']"
5454
>
55-
<a href="javascript:void" :title="String(n)" @click="handleTo(n)">{{
55+
<a href="javascript:void(0)" :title="String(n)" @click="handleTo(n)">{{
5656
n
5757
}}</a>
5858
</li>
5959
<!-- 向后5页 -->
6060
<li class="nt-pagination-item" v-if="currentPage < totalPage - 3">
6161
<a
62-
href="javascript:void"
62+
href="javascript:void(0)"
6363
@mouseenter="handleMoreHover(2)"
6464
@mouseleave="handleMoreHover(0)"
6565
title="向前5页"
@@ -78,7 +78,7 @@
7878
currentPage === totalPage ? 'is-active' : '',
7979
]"
8080
>
81-
<a href="javascript:void" :title="totalPage + ''">{{ totalPage }}</a>
81+
<a href="javascript:void(0)" :title="totalPage + ''">{{ totalPage }}</a>
8282
</li>
8383
</template>
8484

src/components/form/Form.vue

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ if (props.model != null && validator != null) {
6060
const watcher = [];
6161
for (const key in props.model) {
6262
keys.push(key);
63-
//@ts-ignore
63+
//@ts-expect-error: Unreachable code error
6464
watcher.push(() => props.model[key]);
6565
}
6666
if (watcher.length > 0) {
@@ -100,6 +100,17 @@ provide(formDisabledContext, () => props.disabled);
100100
function handleSubmit(e: Event) {
101101
e.preventDefault();
102102
if (validator != null) {
103+
validate();
104+
} else {
105+
emits('submit');
106+
}
107+
}
108+
109+
/**
110+
* 校验全部表单数据
111+
*/
112+
function validate() {
113+
if (validator != null && props.model != null) {
103114
validator
104115
.validate(props.model)
105116
.then(() => {
@@ -111,8 +122,48 @@ function handleSubmit(e: Event) {
111122
[err.key]: err.message,
112123
};
113124
});
114-
} else {
115-
emits('submit');
116125
}
117126
}
127+
128+
/**
129+
* 校验部分表单数据
130+
* @param field
131+
*/
132+
function validateField(field: string | string[]) {
133+
if (validator != null && props.model != null) {
134+
const tacks: Promise<{
135+
key: string;
136+
value: any;
137+
}>[] = [];
138+
if (Array.isArray(field)) {
139+
for (let i = 0, len = field.length; i < len; i++) {
140+
tacks.push(
141+
validator.validateKey(field[i], props.model[field[i]], props.model),
142+
);
143+
}
144+
} else {
145+
tacks.push(validator.validateKey(field, props.model[field], props.model));
146+
}
147+
Promise.allSettled(tacks).then((results) => {
148+
for (let i = 0, len = results.length; i < len; i++) {
149+
const result = results[i];
150+
if (result.status === 'rejected') {
151+
errors.value[result.reason.key] = result.reason.message;
152+
} else {
153+
errors.value[result.value.key] = undefined;
154+
}
155+
}
156+
});
157+
}
158+
}
159+
160+
function clearValidate() {
161+
errors.value = {};
162+
}
163+
164+
defineExpose({
165+
validate,
166+
validateField,
167+
clearValidate,
168+
});
118169
</script>

src/components/input/Input.vue

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ const props = withDefaults(
2727
parser?: (value: string) => string;
2828
modelValue?: string | number;
2929
disabled?: boolean;
30+
/** number, integer */
31+
allowInput?: string;
3032
}>(),
3133
{
3234
htmlType: 'text',
3335
placeholder: '',
3436
autosize: false,
3537
disabled: undefined,
38+
allowInput: undefined,
3639
},
3740
);
3841
@@ -46,10 +49,49 @@ function focus() {
4649
}
4750
}
4851
52+
function numberInputParse(
53+
value: string,
54+
config: { integer: boolean; negative: boolean; precition: number },
55+
) {
56+
let val = value;
57+
let negative = config.negative ? '-?' : '';
58+
if (config.integer) {
59+
const match = val.match(new RegExp(`^(${negative}\\d*)`));
60+
if (match != null) {
61+
return match[1];
62+
}
63+
return val.substring(0, val.length - 1);
64+
}
65+
const match = val.match(
66+
new RegExp(
67+
`^(${negative}\\d+\\.\\d{0,${config.precition}})|(${negative}\\d*)`,
68+
),
69+
);
70+
if (match != null) {
71+
val = match[1] || match[2];
72+
} else {
73+
val = '';
74+
}
75+
return match != null ? match[1] || match[2] : '';
76+
}
77+
4978
function handleInput(e: Event) {
5079
const $target = e.target as HTMLInputElement;
5180
let value = $target.value;
5281
emits('input', e);
82+
if (props.allowInput != null) {
83+
let dotIndex = props.allowInput.indexOf('.');
84+
let precition =
85+
dotIndex === -1
86+
? dotIndex
87+
: parseInt(props.allowInput.substring(dotIndex + 1));
88+
value = numberInputParse(value, {
89+
integer: props.allowInput.includes('integer'),
90+
negative: props.allowInput.startsWith('-'),
91+
precition: precition,
92+
});
93+
$target.value = String(value);
94+
}
5395
if (props.parser != null) {
5496
value = props.parser(value) as string;
5597
$target.value = String(value);

src/components/theme/ThemeButton.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import SunIcon from '../icon/Sun.vue';
1313
import { getTheme, applyTheme } from 'ph-utils/theme';
1414
1515
const theme = ref(getTheme() === 'dark' ? 'dark' : 'auto');
16+
const emits = defineEmits(['change']);
1617
1718
const toggleTheme = async () => {
1819
const newTheme = theme.value === 'dark' ? 'auto' : 'dark';
1920
theme.value = newTheme;
2021
await applyTheme(newTheme);
22+
emits('change', newTheme);
2123
};
2224
</script>
2325

src/components/theme/ThemeRadio.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import RadioGroup from '../radio/RadioGroup.vue';
1313
import { applyTheme, getTheme } from 'ph-utils/theme';
1414
1515
const theme = ref(getTheme());
16+
const emits = defineEmits(['change']);
1617
1718
watch(theme, (val) => {
1819
applyTheme(val as 'auto').then();
20+
emits('change', val);
1921
});
2022
</script>
2123

src/components/theme/ThemeSelect.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ const options = [
2323
];
2424
2525
const theme = ref(getTheme());
26+
const emits = defineEmits(['change']);
2627
2728
watch(theme, (val) => {
2829
applyTheme(val as 'auto').then();
30+
emits('change', val);
2931
});
3032
</script>
3133

src/components/theme/ThemeSwitch.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import SunIcon from '../icon/Sun.vue';
1515
import { getTheme, applyTheme } from 'ph-utils/theme';
1616
1717
const isDark = ref(getTheme() === 'dark');
18+
const emits = defineEmits(['change']);
1819
1920
watch(isDark, (val) => {
2021
applyTheme(val ? 'dark' : 'auto').then();
22+
emits('change', val);
2123
});
2224
</script>

0 commit comments

Comments
 (0)
0