Skip to content

Commit

Permalink
Dev (#53)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Oct 22, 2024
1 parent 6a3edd5 commit 3647e13
Show file tree
Hide file tree
Showing 14 changed files with 176 additions and 11 deletions.
9 changes: 9 additions & 0 deletions docs/components/form.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,12 @@
| `label-width` | 标签宽度,例如 `50px` | `string` | - |
| `error` | 表单域验证错误时的提示信息。设置该值会导致表单验证状态变为 `error`,并显示该错误信息。 | `string` | - |
| `label-position` | 标签位置, *可选* | `left``right``top` | - |

### Form Methods

<!-- prettier-ignore -->
| 方法名 | 说明 | 参数 |
| ------ | ---- | ---- |
| `validate` | 对整个表单进行校验的方法 | `() => void` |
| `validateField` | 对部分表单字段进行校验的方法 | `(field: string \| string[]) => void` |
| `clearValidate` | 移除表单项的校验结果 | `-` |
26 changes: 26 additions & 0 deletions docs/components/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

const inputInt = ref('')

const input1 = ref('')
const input2 = ref('')
const input3 = ref('')

function numericParse(value: string) {
let val = parseInt(value, 10)
if (Number.isNaN(val)) {
Expand Down Expand Up @@ -64,6 +68,27 @@
</CodePreview>
</ClientOnly>

### 允许输入的值

通过传递 `allow-input` 来限制输入的值。`integer` 只能输入正整数, `number` 只能输入正数;以 `-` 开头表明允许输入负数; 以 `.2` 结尾表明小数点后精度; 例如: `-number.4` 表明允许输入数字,且小数点后保留4位小数。

<ClientOnly>
<CodePreview>
<textarea lang="vue" v-pre>
<script setup lang="ts">
</script>
<template>
<hr />
</template>
</textarea>
<template #preview>
<Input v-model="input1" placeholder="只能输入正整数" allow-input="integer" />
<Input v-model="input2" placeholder="只能输入整数" allow-input="-integer" />
<Input v-model="input3" placeholder="输入数字,保留2位小数" allow-input="-number.2" />
</template>
</CodePreview>
</ClientOnly>

### 禁用状态

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

### Input Exposes

Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default [
{
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
];
10 changes: 5 additions & 5 deletions src/components/Pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
<template v-else>
<!-- 首页按钮 -->
<li :class="['nt-pagination-item', currentPage === 1 ? 'is-active' : '']">
<a href="javascript:void" title="1" @click="handleTo(1)">1</a>
<a href="javascript:void(0)" title="1" @click="handleTo(1)">1</a>
</li>
<!-- 向前5页 -->
<li class="nt-pagination-item" v-if="currentPage > 4">
<a
href="javascript:void"
href="javascript:void(0)"
@mouseenter="handleMoreHover(1)"
@mouseleave="handleMoreHover(0)"
title="向前5页"
Expand All @@ -52,14 +52,14 @@
:key="n"
:class="['nt-pagination-item', currentPage === n ? 'is-active' : '']"
>
<a href="javascript:void" :title="String(n)" @click="handleTo(n)">{{
<a href="javascript:void(0)" :title="String(n)" @click="handleTo(n)">{{
n
}}</a>
</li>
<!-- 向后5页 -->
<li class="nt-pagination-item" v-if="currentPage < totalPage - 3">
<a
href="javascript:void"
href="javascript:void(0)"
@mouseenter="handleMoreHover(2)"
@mouseleave="handleMoreHover(0)"
title="向前5页"
Expand All @@ -78,7 +78,7 @@
currentPage === totalPage ? 'is-active' : '',
]"
>
<a href="javascript:void" :title="totalPage + ''">{{ totalPage }}</a>
<a href="javascript:void(0)" :title="totalPage + ''">{{ totalPage }}</a>
</li>
</template>

Expand Down
57 changes: 54 additions & 3 deletions src/components/form/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ if (props.model != null && validator != null) {
const watcher = [];
for (const key in props.model) {
keys.push(key);
//@ts-ignore
//@ts-expect-error: Unreachable code error
watcher.push(() => props.model[key]);
}
if (watcher.length > 0) {
Expand Down Expand Up @@ -100,6 +100,17 @@ provide(formDisabledContext, () => props.disabled);
function handleSubmit(e: Event) {
e.preventDefault();
if (validator != null) {
validate();
} else {
emits('submit');
}
}
/**
* 校验全部表单数据
*/
function validate() {
if (validator != null && props.model != null) {
validator
.validate(props.model)
.then(() => {
Expand All @@ -111,8 +122,48 @@ function handleSubmit(e: Event) {
[err.key]: err.message,
};
});
} else {
emits('submit');
}
}
/**
* 校验部分表单数据
* @param field
*/
function validateField(field: string | string[]) {
if (validator != null && props.model != null) {
const tacks: Promise<{
key: string;
value: any;
}>[] = [];
if (Array.isArray(field)) {
for (let i = 0, len = field.length; i < len; i++) {
tacks.push(
validator.validateKey(field[i], props.model[field[i]], props.model),
);
}
} else {
tacks.push(validator.validateKey(field, props.model[field], props.model));
}
Promise.allSettled(tacks).then((results) => {
for (let i = 0, len = results.length; i < len; i++) {
const result = results[i];
if (result.status === 'rejected') {
errors.value[result.reason.key] = result.reason.message;
} else {
errors.value[result.value.key] = undefined;
}
}
});
}
}
function clearValidate() {
errors.value = {};
}
defineExpose({
validate,
validateField,
clearValidate,
});
</script>
42 changes: 42 additions & 0 deletions src/components/input/Input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ const props = withDefaults(
parser?: (value: string) => string;
modelValue?: string | number;
disabled?: boolean;
/** number, integer */
allowInput?: string;
}>(),
{
htmlType: 'text',
placeholder: '',
autosize: false,
disabled: undefined,
allowInput: undefined,
},
);
Expand All @@ -46,10 +49,49 @@ function focus() {
}
}
function numberInputParse(
value: string,
config: { integer: boolean; negative: boolean; precition: number },
) {
let val = value;
let negative = config.negative ? '-?' : '';
if (config.integer) {
const match = val.match(new RegExp(`^(${negative}\\d*)`));
if (match != null) {
return match[1];
}
return val.substring(0, val.length - 1);
}
const match = val.match(
new RegExp(
`^(${negative}\\d+\\.\\d{0,${config.precition}})|(${negative}\\d*)`,
),
);
if (match != null) {
val = match[1] || match[2];
} else {
val = '';
}
return match != null ? match[1] || match[2] : '';
}
function handleInput(e: Event) {
const $target = e.target as HTMLInputElement;
let value = $target.value;
emits('input', e);
if (props.allowInput != null) {
let dotIndex = props.allowInput.indexOf('.');
let precition =
dotIndex === -1
? dotIndex
: parseInt(props.allowInput.substring(dotIndex + 1));
value = numberInputParse(value, {
integer: props.allowInput.includes('integer'),
negative: props.allowInput.startsWith('-'),
precition: precition,
});
$target.value = String(value);
}
if (props.parser != null) {
value = props.parser(value) as string;
$target.value = String(value);
Expand Down
2 changes: 2 additions & 0 deletions src/components/theme/ThemeButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import SunIcon from '../icon/Sun.vue';
import { getTheme, applyTheme } from 'ph-utils/theme';
const theme = ref(getTheme() === 'dark' ? 'dark' : 'auto');
const emits = defineEmits(['change']);
const toggleTheme = async () => {
const newTheme = theme.value === 'dark' ? 'auto' : 'dark';
theme.value = newTheme;
await applyTheme(newTheme);
emits('change', newTheme);
};
</script>

Expand Down
2 changes: 2 additions & 0 deletions src/components/theme/ThemeRadio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import RadioGroup from '../radio/RadioGroup.vue';
import { applyTheme, getTheme } from 'ph-utils/theme';
const theme = ref(getTheme());
const emits = defineEmits(['change']);
watch(theme, (val) => {
applyTheme(val as 'auto').then();
emits('change', val);
});
</script>

Expand Down
2 changes: 2 additions & 0 deletions src/components/theme/ThemeSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ const options = [
];
const theme = ref(getTheme());
const emits = defineEmits(['change']);
watch(theme, (val) => {
applyTheme(val as 'auto').then();
emits('change', val);
});
</script>

Expand Down
2 changes: 2 additions & 0 deletions src/components/theme/ThemeSwitch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import SunIcon from '../icon/Sun.vue';
import { getTheme, applyTheme } from 'ph-utils/theme';
const isDark = ref(getTheme() === 'dark');
const emits = defineEmits(['change']);
watch(isDark, (val) => {
applyTheme(val ? 'dark' : 'auto').then();
emits('change', val);
});
</script>
1 change: 1 addition & 0 deletions src/hooks/useFormDisabled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default function useDisabled(
return toValue<boolean>(formItemDisabeld as boolean);
if (toValue(formDisabled) != null)
return toValue<boolean>(formDisabled as boolean);
return false;
});

return isDisabled;
Expand Down
27 changes: 26 additions & 1 deletion src/hooks/useFormReset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,30 @@ export default function useFormReset<T>(params: T) {
formFields.value = cloneDeep(params);
}

return { formFields, resetFields };
/**
* 设置单个 key 的值
* @param key 指定的key
* @param value 指定的值
*
* @example
* set('age', 1);
*/
function setFields(key: string, value: any): void;
/**
* 设置多个值
* @param values 键值对数据
*
* @example
* set({ age: 1, name: 'test'})
*/
function setFields(values: Record<string, any>): void;
function setFields(...args: any[]) {
if (args.length === 1) {
formFields.value = { ...formFields.value, ...args[0] };
} else {
formFields.value[args[0] as keyof T] = args[1];
}
}

return { formFields, resetFields, setFields };
}
4 changes: 2 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function impactDetect(
const maxWidth = window.innerWidth + scrollLeft - 10;
// 判断 垂直 方向是否在显示区域内
let y = targetRect.top + scrollTop - topDiff;
let yEnd = y + popoverRect.height;
const yEnd = y + popoverRect.height;
// 1. 首先判断下边界是否超出屏幕
if (yEnd > maxHeight) {
// 下边距超出屏幕
Expand Down Expand Up @@ -154,7 +154,7 @@ export function impactDetect(
// 判断 水平 方向是否在显示区域内
// 1. 首先判断右边界是否超出屏幕
let x = targetRect.left + scrollLeft - leftDiff;
let xEnd = x + popoverRect.width;
const xEnd = x + popoverRect.width;
if (xEnd > maxWidth) {
// 右边距超出屏幕
if (mainAlign === 'top' || mainAlign === 'bottom') {
Expand Down
2 changes: 2 additions & 0 deletions style/pagination/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
transition: all 0.3s ease-in-out;
border: 1px solid transparent;
border-radius: 3px;
box-sizing: border-box;
color: #666;
}

.nt-pagination-item a:hover {
Expand Down

0 comments on commit 3647e13

Please sign in to comment.