mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-09-16 00:09:59 +08:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
49af61a339 | ||
|
4bce5f7713 | ||
|
34c20d4be7 | ||
|
c28c353f7d |
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -22,6 +22,7 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"baomitu",
|
"baomitu",
|
||||||
"bezier",
|
"bezier",
|
||||||
|
"Cascader",
|
||||||
"Clickoutside",
|
"Clickoutside",
|
||||||
"codabar",
|
"codabar",
|
||||||
"commitmsg",
|
"commitmsg",
|
||||||
@ -44,6 +45,7 @@
|
|||||||
"siderbar",
|
"siderbar",
|
||||||
"snapline",
|
"snapline",
|
||||||
"stylelint",
|
"stylelint",
|
||||||
|
"unocss",
|
||||||
"WUJIE",
|
"WUJIE",
|
||||||
"zlevel"
|
"zlevel"
|
||||||
]
|
]
|
||||||
|
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,3 +1,38 @@
|
|||||||
|
## 5.2.2
|
||||||
|
|
||||||
|
## Feats
|
||||||
|
|
||||||
|
- `RForm` 组件相关
|
||||||
|
- 新增 `submitWhenEnter` 配置项,允许在按下回车键时自动触发表单的校验,如果校验成功则会自动触发 `onFinish` 事件
|
||||||
|
- 新增 `onFinish` 配置项,允许在表单校验成功后自动触发的事件
|
||||||
|
- 新增 `autocomplete` 配置项,允许配置表单的自动完成功能,默认配置为 `off`
|
||||||
|
- 新增 `loading` 配置项,允许配置表单的加载状态
|
||||||
|
- 新增 `loadingDescription` 配置项,允许配置表单的加载状态的描述
|
||||||
|
- `useForm` 相关
|
||||||
|
- 新增 `validateTargetField` 方法,允许验证指定表单项的规则
|
||||||
|
- 初始化方法现在支持传入函数,允许动态获取表单的初始化值与规则
|
||||||
|
- `formModel` 方法现在会默认联合 `Recordable` 类型,获取初始化类型中未获取到的类型时,默认推到为 `any` 类型
|
||||||
|
- 新增了 `formConditionRef` 属性,现在可以在内部解构获取一个 `ref` 包裹的响应式初始化表单对象值
|
||||||
|
- 新增了 `updateFormCondition` 方法,允许更新表单的值,该方法会覆盖初始化值
|
||||||
|
- 更新依赖为主流版本
|
||||||
|
- 新增 `unocss` 原子化样式库,但是不推荐全量使用,仅作为一些简单的样式片段使用,否则在调试的时候将会是灾难
|
||||||
|
> 新增 `unocss` 后,在使用 `ProTable` 组件的流体高度最外层父元素配置时,可以便捷的配置 `h-full` 即可。
|
||||||
|
|
||||||
|
## 5.2.1
|
||||||
|
|
||||||
|
## Feats
|
||||||
|
|
||||||
|
- `RTablePro` 组件相关
|
||||||
|
- 新增 `runAsyncTableRequest` 方法,与 `runTableRequest` 方法功能一致,但是返回 `Promise` 对象
|
||||||
|
- 现在不允许使用 `useTemplateRef` 方法注册 `dom` 模板引用,约定强制使用 `useTablePro` 方法的 `register` 方法注册 `hook` 使用相关方法
|
||||||
|
- `useTablePro` 方法新增 `getTableProConfig` 方法,与 `useTable` 方法的 `getTableConfig` 方法功能一致,获取 `RTablePro` 组件额外注入配置
|
||||||
|
- `useTable` 方法新增 `getTableConfig` 方法,获取 `RTable` 组件额外注入配置
|
||||||
|
- 更新包为主流版本
|
||||||
|
- `vue-router` 因为在 [4.4.1](https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md#441-2024-07-31) 版本中有破坏性的更新,所以在 `jsx` 函数式组件使用 `this.$route`, `this.$router` 会提示类型报错,所以现在强制约定需要使用 `useRoute`, `useRouter` 方法显示的声明与使用
|
||||||
|
- 更新 `naive-ui` 版本至 `2.42.0`
|
||||||
|
- 更新 `vue` 版本至 `3.5.17`
|
||||||
|
- `useForm` 方法新增 `reset` 方法,允许重置表单值,该方法依赖 `useForm` 方法的初始化 `formModel` 参数,所以请确保初始化 `formModel` 参数
|
||||||
|
|
||||||
## 5.2.0
|
## 5.2.0
|
||||||
|
|
||||||
一些破坏性更新,请谨慎更新。
|
一些破坏性更新,请谨慎更新。
|
||||||
|
66
package.json
66
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ray-template",
|
"name": "ray-template",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "5.1.0",
|
"version": "5.2.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
"node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||||
@ -35,67 +35,66 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@logicflow/core": "2.0.10",
|
"@logicflow/core": "2.0.10",
|
||||||
"@logicflow/extension": "2.0.14",
|
"@logicflow/extension": "2.0.14",
|
||||||
"@vueuse/core": "^12.4.0",
|
"@vueuse/core": "^13.1.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.9.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"currency.js": "^2.0.4",
|
"currency.js": "^2.0.4",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"html-to-image": "1.11.11",
|
"html-to-image": "1.11.13",
|
||||||
"interactjs": "1.10.27",
|
"interactjs": "1.10.27",
|
||||||
"jsbarcode": "3.11.6",
|
"jsbarcode": "3.11.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mockjs": "1.1.0",
|
"mockjs": "1.1.0",
|
||||||
"naive-ui": "^2.41.0",
|
"naive-ui": "^2.42.0",
|
||||||
"pinia": "^2.3.0",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.2.0",
|
"pinia-plugin-persistedstate": "^4.4.1",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"vue": "^3.5.16",
|
"vue": "^3.5.17",
|
||||||
"vue-demi": "0.14.10",
|
"vue-demi": "0.14.10",
|
||||||
"vue-hooks-plus": "2.2.3",
|
"vue-hooks-plus": "2.4.0",
|
||||||
"vue-i18n": "^9.13.1",
|
"vue-i18n": "^9.13.1",
|
||||||
"vue-router": "^4.4.0",
|
"vue-router": "^4.5.1",
|
||||||
"vue3-next-qrcode": "2.0.10"
|
"vue3-next-qrcode": "3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@amap/amap-jsapi-types": "0.0.15",
|
"@commitlint/cli": "19.7.1",
|
||||||
"@ant-design/icons-vue": "7.0.1",
|
"@commitlint/config-conventional": "19.7.1",
|
||||||
"@commitlint/cli": "19.3.0",
|
"@eslint/js": "9.28.0",
|
||||||
"@commitlint/config-conventional": "19.2.2",
|
|
||||||
"@interactjs/types": "1.10.27",
|
"@interactjs/types": "1.10.27",
|
||||||
"@intlify/unplugin-vue-i18n": "4.0.0",
|
"@intlify/unplugin-vue-i18n": "4.0.0",
|
||||||
"@types/crypto-js": "4.2.2",
|
"@types/crypto-js": "4.2.2",
|
||||||
"@types/jsbarcode": "3.11.4",
|
"@types/jsbarcode": "3.11.4",
|
||||||
"@types/lodash-es": "4.17.12",
|
"@types/lodash-es": "4.17.12",
|
||||||
"@types/mockjs": "1.0.10",
|
"@types/mockjs": "1.0.10",
|
||||||
"@types/three": "0.171.0",
|
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.20.0",
|
"@typescript-eslint/parser": "8.24.0",
|
||||||
"@typescript-eslint/parser": "8.20.0",
|
"@vitejs/plugin-vue": "5.2.3",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue-jsx": "4.1.2",
|
||||||
"@vitejs/plugin-vue-jsx": "4.1.1",
|
|
||||||
"@vitest/ui": "2.1.8",
|
"@vitest/ui": "2.1.8",
|
||||||
"@vue/eslint-config-prettier": "10.1.0",
|
"@vue/eslint-config-prettier": "10.1.0",
|
||||||
"@vue/eslint-config-typescript": "14.2.0",
|
"@vue/eslint-config-typescript": "14.2.0",
|
||||||
"@vue/test-utils": "2.4.6",
|
"@vue/test-utils": "2.4.6",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.21",
|
||||||
"depcheck": "1.4.7",
|
"depcheck": "1.4.7",
|
||||||
"eslint": "9.18.0",
|
"eslint": "9.20.1",
|
||||||
"eslint-config-prettier": "10.0.1",
|
"eslint-config-prettier": "10.1.2",
|
||||||
"eslint-plugin-prettier": "5.2.2",
|
"eslint-plugin-prettier": "5.2.6",
|
||||||
"eslint-plugin-vue": "9.32.0",
|
"eslint-plugin-vue": "9.32.0",
|
||||||
"globals": "15.14.0",
|
"globals": "16.0.0",
|
||||||
"happy-dom": "16.6.0",
|
"happy-dom": "17.1.0",
|
||||||
"husky": "8.0.3",
|
"husky": "8.0.3",
|
||||||
"lint-staged": "15.3.0",
|
"lint-staged": "15.4.3",
|
||||||
"postcss": "8.5.1",
|
"postcss": "8.5.4",
|
||||||
"postcss-px-to-viewport-8-with-include": "1.2.2",
|
"postcss-px-to-viewport-8-with-include": "1.2.2",
|
||||||
"prettier": "3.4.2",
|
"prettier": "3.5.3",
|
||||||
"rollup-plugin-gzip": "4.0.1",
|
"rollup-plugin-gzip": "4.0.1",
|
||||||
"sass": "1.83.4",
|
"sass": "1.86.3",
|
||||||
"svg-sprite-loader": "6.0.11",
|
"svg-sprite-loader": "6.0.11",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.8.3",
|
||||||
"unplugin-auto-import": "19.0.0",
|
"unocss": "66.3.3",
|
||||||
|
"unplugin-auto-import": "19.1.2",
|
||||||
"unplugin-vue-components": "0.28.0",
|
"unplugin-vue-components": "0.28.0",
|
||||||
"vite": "6.3.5",
|
"vite": "6.3.5",
|
||||||
"vite-bundle-analyzer": "0.16.0",
|
"vite-bundle-analyzer": "0.16.0",
|
||||||
@ -107,7 +106,8 @@
|
|||||||
"vite-plugin-svg-icons": "2.0.1",
|
"vite-plugin-svg-icons": "2.0.1",
|
||||||
"vite-svg-loader": "5.1.0",
|
"vite-svg-loader": "5.1.0",
|
||||||
"vitest": "2.1.8",
|
"vitest": "2.1.8",
|
||||||
"vue-tsc": "2.2.0"
|
"vue-eslint-parser": "9.4.3",
|
||||||
|
"vue-tsc": "2.2.8"
|
||||||
},
|
},
|
||||||
"description": "<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->",
|
"description": "<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->",
|
||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
|
2732
pnpm-lock.yaml
generated
2732
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,41 @@
|
|||||||
import { NForm } from 'naive-ui'
|
import { NForm, NSpin } from 'naive-ui'
|
||||||
|
|
||||||
import props from './props'
|
import props from './props'
|
||||||
import { call } from '@/utils'
|
import { call, unrefElement } from '@/utils'
|
||||||
import { useTemplateRef } from 'vue'
|
import { useTemplateRef } from 'vue'
|
||||||
|
import { useEventListener } from '@vueuse/core'
|
||||||
|
|
||||||
import type { RFormInst } from './types'
|
import type { RFormInst } from './types'
|
||||||
import type { FormProps } from 'naive-ui'
|
import type { ShallowRef } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'RForm',
|
name: 'RForm',
|
||||||
props,
|
props,
|
||||||
setup(props, { expose }) {
|
setup(props, { expose }) {
|
||||||
const formRef = useTemplateRef<RFormInst>('formRef')
|
const formRef = useTemplateRef<RFormInst>('formRef')
|
||||||
|
const currentSubmitFn = computed(() => props.onFinish ?? Promise.resolve)
|
||||||
|
|
||||||
|
const bindKeydownListener = (e: KeyboardEvent) => {
|
||||||
|
const keyCode = e.code
|
||||||
|
|
||||||
|
if (keyCode === 'Enter') {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
formRef.value?.validate().then(currentSubmitFn.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.submitWhenEnter) {
|
||||||
|
useEventListener(
|
||||||
|
formRef as unknown as ShallowRef<HTMLElement>,
|
||||||
|
'keydown',
|
||||||
|
bindKeydownListener,
|
||||||
|
{
|
||||||
|
capture: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 主动调用 register 方法,满足 useForm 方法正常调用
|
// 主动调用 register 方法,满足 useForm 方法正常调用
|
||||||
@ -20,6 +44,16 @@ export default defineComponent({
|
|||||||
if (onRegister && formRef.value) {
|
if (onRegister && formRef.value) {
|
||||||
call(onRegister, formRef.value)
|
call(onRegister, formRef.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (formRef.value) {
|
||||||
|
const formElement = unrefElement(
|
||||||
|
formRef.value as unknown as HTMLFormElement,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (formElement) {
|
||||||
|
formElement.autocomplete = props.autocomplete
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
expose()
|
expose()
|
||||||
@ -30,13 +64,22 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { $attrs, $props, $slots } = this
|
const { $attrs, $props, $slots } = this
|
||||||
|
const { loading, loadingDescription, ...restProps } = $props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NForm {...$attrs} {...($props as FormProps)} ref="formRef">
|
<NSpin
|
||||||
{{
|
show={loading}
|
||||||
...$slots,
|
description={loadingDescription}
|
||||||
|
style={{
|
||||||
|
height: 'auto',
|
||||||
}}
|
}}
|
||||||
</NForm>
|
>
|
||||||
|
<NForm {...$attrs} {...restProps} ref="formRef">
|
||||||
|
{{
|
||||||
|
...$slots,
|
||||||
|
}}
|
||||||
|
</NForm>
|
||||||
|
</NSpin>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -40,10 +40,11 @@ const useForm = <
|
|||||||
T extends Recordable = Recordable,
|
T extends Recordable = Recordable,
|
||||||
R extends RFormRules = RFormRules,
|
R extends RFormRules = RFormRules,
|
||||||
>(
|
>(
|
||||||
model?: T,
|
model?: T | (() => T),
|
||||||
rules?: R,
|
rules?: R | (() => R),
|
||||||
) => {
|
) => {
|
||||||
const formRef = shallowRef<RFormInst>()
|
const formRef = shallowRef<RFormInst>()
|
||||||
|
const formModelRef = ref<T>()
|
||||||
|
|
||||||
const register = (inst: RFormInst) => {
|
const register = (inst: RFormInst) => {
|
||||||
if (inst) {
|
if (inst) {
|
||||||
@ -61,6 +62,15 @@ const useForm = <
|
|||||||
return formRef.value
|
return formRef.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化 formModelRef 的值,根据 model 的类型进行初始化
|
||||||
|
const initialFormModel = () => {
|
||||||
|
if (typeof model === 'function') {
|
||||||
|
formModelRef.value = model() ?? ({} as T)
|
||||||
|
} else {
|
||||||
|
formModelRef.value = cloneDeep(model) ?? ({} as T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
@ -86,10 +96,39 @@ const useForm = <
|
|||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* 获取表项中收集到的值的对象。
|
* 获取表项中收集到的值的对象。
|
||||||
*
|
|
||||||
* 调用该方法时,需要确保初始化 useForm 方法的时候传入了 model,否则可能有意想不到的问题发生。
|
* 调用该方法时,需要确保初始化 useForm 方法的时候传入了 model,否则可能有意想不到的问题发生。
|
||||||
|
*
|
||||||
|
* 该方法可以实现重置表单的需求,因为在 vue 的设计理念中,表单的值绑定是直接绑定在每个组件上,
|
||||||
|
* 而不是利用 Form 表单机制,所以需要这么去做表单的初始化操作维护机制。
|
||||||
|
*
|
||||||
|
* 在 5.2.2 版本中,新增了 formConditionRef 属性,现在可以解构获取一个 ref 包裹的响应式初始化表单对象值;
|
||||||
|
* 这样就可以直接在 hook 中调用一个响应式的表单对象值。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* interface FormModel {
|
||||||
|
* name: string | null
|
||||||
|
* age: number | null
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* const [register, { formModel }] = useForm<FormModel>({
|
||||||
|
* name: null,
|
||||||
|
* age: null,
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* const formModelRef = ref(formModel())
|
||||||
|
*
|
||||||
|
* const reset = () => {
|
||||||
|
* formModelRef.value = formModel()
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
const formModel = () => cloneDeep(model) || ({} as T)
|
const formModel = (): T & Recordable => {
|
||||||
|
if (typeof model === 'function') {
|
||||||
|
return model()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloneDeep(model) || ({} as T)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -98,7 +137,103 @@ const useForm = <
|
|||||||
*
|
*
|
||||||
* 调用该方法时,需要确保初始化 useForm 方法的时候传入了 rules,否则可能有意想不到的问题发生。
|
* 调用该方法时,需要确保初始化 useForm 方法的时候传入了 rules,否则可能有意想不到的问题发生。
|
||||||
*/
|
*/
|
||||||
const formRules = () => cloneDeep(rules) || ({} as R)
|
const formRules = () => {
|
||||||
|
if (typeof rules === 'function') {
|
||||||
|
return rules()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cloneDeep(rules) || ({} as R)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param values 需要重置的表单值,该参数会覆盖初始化值
|
||||||
|
*
|
||||||
|
* @warning
|
||||||
|
* 请注意初始化值的问题,如果设置初始化值为 undefined,
|
||||||
|
* 则会导致 reset 初始化操作失败,必须使用 null 作为初始化的空值。
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 重置表单的值,依赖 useForm 传入的初始化值。
|
||||||
|
* 该方法会将初始化传入的值视为整个表单的初始化绑定值。
|
||||||
|
*
|
||||||
|
* 但是,对于复杂的动态值,也就是在实现业务中额外的对象值,需要手动进行初始化操作。
|
||||||
|
* 所以,最佳的实践应该是初始化 useForm 方法的时候,就应该确定好初始化值。
|
||||||
|
* 然后,在需要重置表单的时候,直接调用该方法即可。
|
||||||
|
*/
|
||||||
|
const reset = <Values extends T = T>(values?: Values & Recordable) => {
|
||||||
|
formModelRef.value = Object.assign(
|
||||||
|
formModelRef.value as T,
|
||||||
|
formModel(),
|
||||||
|
values,
|
||||||
|
)
|
||||||
|
restoreValidation()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param key 需要验证的表单项的 key
|
||||||
|
*
|
||||||
|
* @see https://www.naiveui.com/zh-CN/dark/components/form#partially-apply-rules.vue
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 验证表单项的规则。
|
||||||
|
*
|
||||||
|
* 注意,该方法想要正常运转,需要在定义 rules 时定义唯一 key;
|
||||||
|
* 否则,该逻辑执行会失败。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [register, { validateTargetField }] = useForm(
|
||||||
|
* {
|
||||||
|
* name: null,
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* name: {
|
||||||
|
* required: true,
|
||||||
|
* message: 'name is required',
|
||||||
|
* trigger: ['blur', 'change'],
|
||||||
|
* type: 'string',
|
||||||
|
* key: 'name',
|
||||||
|
* },
|
||||||
|
* },
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* validateTargetField('name')
|
||||||
|
*/
|
||||||
|
const validateTargetField = (key: string) => {
|
||||||
|
if (!key || typeof key !== 'string') {
|
||||||
|
throw new Error(
|
||||||
|
`[useForm-validateTargetField]: except key is string, but got ${typeof key}.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(void 0, (rules) => {
|
||||||
|
return rules?.key === key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 更新表单的值,该方法会覆盖初始化值。
|
||||||
|
* 推荐在表单组件中使用该方法,而不是直接修改表单的值,符合函数式的理念。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [register, { updateFormCondition }] = useForm(
|
||||||
|
* {
|
||||||
|
* name: null,
|
||||||
|
* },
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* updateFormCondition({
|
||||||
|
* name: 'John',
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
const updateFormCondition = (values: T & Recordable) => {
|
||||||
|
formModelRef.value = Object.assign(formModelRef.value as T, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialFormModel()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
register,
|
register,
|
||||||
@ -108,6 +243,10 @@ const useForm = <
|
|||||||
restoreValidation,
|
restoreValidation,
|
||||||
formModel,
|
formModel,
|
||||||
formRules,
|
formRules,
|
||||||
|
reset,
|
||||||
|
validateTargetField,
|
||||||
|
formConditionRef: formModelRef as Ref<T>,
|
||||||
|
updateFormCondition,
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,75 @@
|
|||||||
import { formProps } from 'naive-ui'
|
import { formProps } from 'naive-ui'
|
||||||
|
import { omit } from 'lodash-es'
|
||||||
|
|
||||||
import type { MaybeArray } from '@/types'
|
import type { MaybeArray, AnyFC } from '@/types'
|
||||||
import type { RFormInst } from './types'
|
import type { RFormInst } from './types'
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
...formProps,
|
...omit(formProps, ['onSubmit']),
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 表单的加载状态。
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 表单的加载状态的描述。
|
||||||
|
*
|
||||||
|
* @default undefined
|
||||||
|
*/
|
||||||
|
loadingDescription: {
|
||||||
|
type: String,
|
||||||
|
default: void 0,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 表单的自动完成功能。
|
||||||
|
*
|
||||||
|
* @default 'off'
|
||||||
|
*/
|
||||||
|
autocomplete: {
|
||||||
|
type: String as PropType<AutoFillBase>,
|
||||||
|
default: 'off',
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 是否在按下回车键时自动触发表单的校验,如果校验成功则会自动触发 onFinish 事件。
|
||||||
|
* 该功能需要结合 onFinish 属性使用。
|
||||||
|
*
|
||||||
|
* 注意,该属性不支持响应式更新,只依赖于初始化传入的值;
|
||||||
|
* 因为该方法特性的原因,做响应式的更新意义不大。
|
||||||
|
*
|
||||||
|
* 并且该属性启用后,会自动的拦截 Enter 键的默认行为;
|
||||||
|
* 例如 NSelect, NInput 等官方自带组件的默认快捷键功能,都会被阻止;
|
||||||
|
* 但是不得不这么做,以避免一些奇奇怪怪的问题。
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
submitWhenEnter: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 表单校验成功后自动触发的事件,该事件的触发时机为 submitWhenEnter 属性为 true 时,按下回车键触发。
|
||||||
|
* 该功能需要结合 submitWhenEnter 属性使用。
|
||||||
|
*
|
||||||
|
* @default null
|
||||||
|
*/
|
||||||
|
onFinish: {
|
||||||
|
type: Function as PropType<AnyFC>,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
|
@ -149,9 +149,23 @@ const useTable = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 设定表格的过滤状态。
|
||||||
|
*
|
||||||
|
* @see https://www.naiveui.com/zh-CN/dark/components/data-table#DataTable-Methods
|
||||||
|
*/
|
||||||
const filter = (filters: FilterState | null) =>
|
const filter = (filters: FilterState | null) =>
|
||||||
getTableInstance().filter.call(null, filters)
|
getTableInstance().filter.call(null, filters)
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 获取 Table 组件配置。
|
||||||
|
*/
|
||||||
|
const getTableConfig = () => extraRef.value
|
||||||
|
|
||||||
return [
|
return [
|
||||||
register,
|
register,
|
||||||
{
|
{
|
||||||
@ -165,7 +179,7 @@ const useTable = () => {
|
|||||||
sort,
|
sort,
|
||||||
print,
|
print,
|
||||||
filter,
|
filter,
|
||||||
config: extraRef,
|
getTableConfig,
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,9 @@ import type { TransitionProps } from './types'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* 使用宏编译模式时,可以使用 defineOptions 声明组件选项
|
* @description
|
||||||
* 常用方法即是声明该组件的 name inheritAttrs 等属性
|
* 使用宏编译模式时,可以使用 defineOptions 声明组件选项。
|
||||||
|
* 常用方法即是声明该组件的 name inheritAttrs 等属性。
|
||||||
*/
|
*/
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'RTransitionComponent',
|
name: 'RTransitionComponent',
|
||||||
|
@ -5,6 +5,26 @@ import collapseGridProps from '../../base/RCollapseGrid/src/props'
|
|||||||
|
|
||||||
import type { GridProps } from 'naive-ui'
|
import type { GridProps } from 'naive-ui'
|
||||||
|
|
||||||
|
export const collapseProps = Object.assign({}, formProps, {
|
||||||
|
...collapseGridProps,
|
||||||
|
open: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
cols: {
|
||||||
|
type: [Number, String] as PropType<GridProps['cols']>,
|
||||||
|
default: '4 xs:1 s:2 m:2 l:4 xl:4 2xl:6',
|
||||||
|
},
|
||||||
|
bordered: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
responsive: {
|
||||||
|
type: String as PropType<GridProps['responsive']>,
|
||||||
|
default: 'screen',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
@ -13,25 +33,7 @@ import type { GridProps } from 'naive-ui'
|
|||||||
*/
|
*/
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'RCollapse',
|
name: 'RCollapse',
|
||||||
props: Object.assign({}, formProps, {
|
props: collapseProps,
|
||||||
...collapseGridProps,
|
|
||||||
open: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
cols: {
|
|
||||||
type: [Number, String] as PropType<GridProps['cols']>,
|
|
||||||
default: '4 xs:1 s:2 m:2 l:4 xl:4 2xl:6',
|
|
||||||
},
|
|
||||||
bordered: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
responsive: {
|
|
||||||
type: String as PropType<GridProps['responsive']>,
|
|
||||||
default: 'screen',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
render() {
|
render() {
|
||||||
const { $slots, $props } = this
|
const { $slots, $props } = this
|
||||||
const { labelPlacement, showFeedback, ...rest } = $props
|
const { labelPlacement, showFeedback, ...rest } = $props
|
||||||
|
@ -3,9 +3,23 @@ import tableProProps from './src/props'
|
|||||||
import { useTablePro } from './src/hooks/useTablePro'
|
import { useTablePro } from './src/hooks/useTablePro'
|
||||||
|
|
||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
|
import type {
|
||||||
|
BasePagination,
|
||||||
|
TablePagination,
|
||||||
|
FormatRangeTime,
|
||||||
|
TableRequestConfig,
|
||||||
|
TableProFieldNames,
|
||||||
|
} from './src/types'
|
||||||
|
|
||||||
type TableProProps = ExtractPropTypes<typeof tableProProps>
|
type TableProProps = ExtractPropTypes<typeof tableProProps>
|
||||||
|
|
||||||
export type { TableProProps }
|
export type {
|
||||||
|
TableProProps,
|
||||||
|
BasePagination,
|
||||||
|
TablePagination,
|
||||||
|
FormatRangeTime,
|
||||||
|
TableRequestConfig,
|
||||||
|
TableProFieldNames,
|
||||||
|
}
|
||||||
|
|
||||||
export { RTablePro, useTablePro, tableProProps }
|
export { RTablePro, useTablePro, tableProProps }
|
||||||
|
@ -7,11 +7,13 @@ import { usePagination } from '@/hooks'
|
|||||||
import { omit } from 'lodash-es'
|
import { omit } from 'lodash-es'
|
||||||
|
|
||||||
import type { TablePagination, TableRequestConfig, TableProInst } from './types'
|
import type { TablePagination, TableRequestConfig, TableProInst } from './types'
|
||||||
|
import type { RTableInst } from '../../..'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'RTablePro',
|
name: 'RTablePro',
|
||||||
props,
|
props,
|
||||||
setup(props) {
|
setup(props, ctx) {
|
||||||
|
const { expose } = ctx
|
||||||
const [register, tableFns] = useTable()
|
const [register, tableFns] = useTable()
|
||||||
const [
|
const [
|
||||||
paginationRef,
|
paginationRef,
|
||||||
@ -92,9 +94,9 @@ export default defineComponent({
|
|||||||
return requestParams
|
return requestParams
|
||||||
}
|
}
|
||||||
|
|
||||||
// 会重置 pagination 的请求,默认会重置
|
// 同步执行 request 请求,允许重置 pagination 请求,返回 Promise 对象
|
||||||
const runResetPaginationRequest = (
|
const runResetPaginationRequest: TableProInst['runTableRequest'] = (
|
||||||
extraConfig?: TableRequestConfig,
|
extraConfig,
|
||||||
reset = true,
|
reset = true,
|
||||||
) => {
|
) => {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
@ -106,6 +108,20 @@ export default defineComponent({
|
|||||||
tableRequestRef.value?.(requestParams)
|
tableRequestRef.value?.(requestParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 异步执行 request 请求,允许重置 pagination 请求,返回 Promise 对象
|
||||||
|
const runResetPaginationRequestAsync: TableProInst['runAsyncTableRequest'] =
|
||||||
|
(extraConfig, reset = true) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
runResetPaginationRequest(extraConfig, reset)
|
||||||
|
|
||||||
|
resolve(void 0)
|
||||||
|
} catch (e) {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
setItemCount(props.paginationCount)
|
setItemCount(props.paginationCount)
|
||||||
setCallback(() => {
|
setCallback(() => {
|
||||||
@ -124,14 +140,16 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (onRegister) {
|
if (onRegister) {
|
||||||
call(onRegister, {
|
call(onRegister, {
|
||||||
...tableFns,
|
...(tableFns as unknown as RTableInst),
|
||||||
getTablePagination: update,
|
getTablePagination: update,
|
||||||
runTableRequest: runResetPaginationRequest,
|
runTableRequest: runResetPaginationRequest,
|
||||||
|
runAsyncTableRequest: runResetPaginationRequestAsync,
|
||||||
getCurrentTableRequestParams: combineRequestParams,
|
getCurrentTableRequestParams: combineRequestParams,
|
||||||
resetTablePagination: resetPagination,
|
resetTablePagination: resetPagination,
|
||||||
} as unknown as TableProInst)
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
expose()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
register,
|
||||||
|
@ -195,6 +195,29 @@ export const useTablePro = () => {
|
|||||||
const resetTablePagination = () =>
|
const resetTablePagination = () =>
|
||||||
getTableProInstance().resetTablePagination.call(null)
|
getTableProInstance().resetTablePagination.call(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param extraConfig 额外请求合并配置项
|
||||||
|
* @param reset 是否重置分页请求
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 异步手动触发表格请求,用于手动刷新表格。
|
||||||
|
*/
|
||||||
|
const runAsyncTableRequest = <
|
||||||
|
T extends Recordable,
|
||||||
|
ExcludeParams extends keyof T = keyof T,
|
||||||
|
>(
|
||||||
|
extraConfig?: TableRequestConfig<T, ExcludeParams>,
|
||||||
|
reset?: boolean,
|
||||||
|
) => getTableProInstance().runAsyncTableRequest.call(null, extraConfig, reset)
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 获取 TablePro 组件配置。
|
||||||
|
*/
|
||||||
|
const getTableProConfig = () => getTableProInstance().config
|
||||||
|
|
||||||
return [
|
return [
|
||||||
register,
|
register,
|
||||||
{
|
{
|
||||||
@ -211,6 +234,8 @@ export const useTablePro = () => {
|
|||||||
print,
|
print,
|
||||||
getCurrentTableRequestParams,
|
getCurrentTableRequestParams,
|
||||||
resetTablePagination,
|
resetTablePagination,
|
||||||
|
runAsyncTableRequest,
|
||||||
|
getTableProConfig,
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import { omit } from 'lodash-es'
|
|||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import type {
|
import type {
|
||||||
TableProInst,
|
TableProInst,
|
||||||
TablePagination,
|
|
||||||
TableRequestConfig,
|
TableRequestConfig,
|
||||||
PaginationPrefix,
|
PaginationPrefix,
|
||||||
|
TablePaginationUpdate,
|
||||||
} from './types'
|
} from './types'
|
||||||
import type { AnyFC } from '@/types'
|
import type { AnyFC } from '@/types'
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ const props = {
|
|||||||
* @default undefined
|
* @default undefined
|
||||||
*/
|
*/
|
||||||
onTablePaginationUpdate: {
|
onTablePaginationUpdate: {
|
||||||
type: Function as PropType<(pagination: TablePagination) => void>,
|
type: Function as PropType<TablePaginationUpdate>,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -17,16 +17,20 @@ export type FormatRangeTime = {
|
|||||||
target: [string | number, string | number]
|
target: [string | number, string | number]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BasePagination {
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
|
itemCount: number
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Pagination 分页配置。
|
* Pagination 分页配置。
|
||||||
*/
|
*/
|
||||||
export interface TablePagination {
|
export type TablePagination = BasePagination
|
||||||
page: number
|
|
||||||
pageSize: number
|
export type TablePaginationUpdate = (pagination: TablePagination) => void
|
||||||
itemCount: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PaginationPrefix = UsePaginationOptions['prefix']
|
export type PaginationPrefix = UsePaginationOptions['prefix']
|
||||||
|
|
||||||
@ -100,6 +104,30 @@ export interface TableProInst extends Omit<RTableInst, 'getTableInstance'> {
|
|||||||
extraConfig?: TableRequestConfig<T, ExcludeParams>,
|
extraConfig?: TableRequestConfig<T, ExcludeParams>,
|
||||||
reset?: boolean,
|
reset?: boolean,
|
||||||
) => void
|
) => void
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param extraConfig 额外请求合并配置项
|
||||||
|
* @param reset 是否重置分页请求
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 异步手动触发表格请求,用于手动刷新表格。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [register, { runAsyncTableRequest }] = useTablePro()
|
||||||
|
*
|
||||||
|
* // 重置分页请求
|
||||||
|
* runAsyncTableRequest(void 0, true)
|
||||||
|
* runAsyncTableRequest()
|
||||||
|
* // 不重置分页请求
|
||||||
|
* runAsyncTableRequest(void 0, false)
|
||||||
|
*/
|
||||||
|
runAsyncTableRequest: <
|
||||||
|
T extends Recordable,
|
||||||
|
ExcludeParams extends keyof T = keyof T,
|
||||||
|
>(
|
||||||
|
extraConfig?: TableRequestConfig<T, ExcludeParams>,
|
||||||
|
reset?: boolean,
|
||||||
|
) => Promise<void>
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param extraConfig 额外请求合并配置项
|
* @param extraConfig 额外请求合并配置项
|
||||||
@ -132,3 +160,24 @@ export interface TableProInst extends Omit<RTableInst, 'getTableInstance'> {
|
|||||||
*/
|
*/
|
||||||
resetTablePagination: () => void
|
resetTablePagination: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TableProFieldNames {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 分页器页码字段。
|
||||||
|
*/
|
||||||
|
page: string
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 分页器每页条数字段。
|
||||||
|
*/
|
||||||
|
pageSize: string
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* 分页器总条数字段。
|
||||||
|
*/
|
||||||
|
itemCount: string
|
||||||
|
}
|
||||||
|
@ -41,15 +41,16 @@ const throttleDirective: CustomDirectiveFC<
|
|||||||
|
|
||||||
throttleFunction = throttle(func, wait, Object.assign({}, options))
|
throttleFunction = throttle(func, wait, Object.assign({}, options))
|
||||||
|
|
||||||
useEventListener(el, trigger, throttleFunction)
|
cleanup = useEventListener(el, trigger, throttleFunction)
|
||||||
},
|
},
|
||||||
beforeUnmount: () => {
|
beforeUnmount: () => {
|
||||||
if (throttleFunction) {
|
if (throttleFunction) {
|
||||||
throttleFunction.cancel()
|
throttleFunction.cancel()
|
||||||
cleanup?.()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throttleFunction = null
|
throttleFunction = null
|
||||||
|
|
||||||
|
cleanup?.()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import './app-components/provider/provider.scss' // 初始化 provider 包注入
|
|||||||
import 'vue3-next-qrcode/es/style.css' // vue3-next-qrcode 样式
|
import 'vue3-next-qrcode/es/style.css' // vue3-next-qrcode 样式
|
||||||
|
|
||||||
import 'virtual:svg-icons-register' // vite-plugin-svg-icons 脚本,启用 svg 雪碧图
|
import 'virtual:svg-icons-register' // vite-plugin-svg-icons 脚本,启用 svg 雪碧图
|
||||||
|
import 'virtual:uno.css'
|
||||||
|
|
||||||
import { setupRouter } from './router'
|
import { setupRouter } from './router'
|
||||||
import { setupStore } from './store'
|
import { setupStore } from './store'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
type RecordKey = string | number | symbol
|
export type RecordKey = string | number | symbol
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
2
src/types/properties.d.ts
vendored
2
src/types/properties.d.ts
vendored
@ -4,7 +4,7 @@ import type { useRoute, useRouter } from 'vue-router'
|
|||||||
|
|
||||||
// 为 vue 添加一些自定义属性
|
// 为 vue 添加一些自定义属性
|
||||||
// https://cn.vuejs.org/guide/typescript/options-api#augmenting-global-properties
|
// https://cn.vuejs.org/guide/typescript/options-api#augmenting-global-properties
|
||||||
declare module 'vue' {
|
declare module '*.vue' {
|
||||||
interface ComponentCustomProperties {
|
interface ComponentCustomProperties {
|
||||||
$router: ReturnType<typeof useRouter>
|
$router: ReturnType<typeof useRouter>
|
||||||
$route: ReturnType<typeof useRoute>
|
$route: ReturnType<typeof useRoute>
|
||||||
|
@ -9,11 +9,21 @@ import {
|
|||||||
NButton,
|
NButton,
|
||||||
NRadio,
|
NRadio,
|
||||||
NRadioGroup,
|
NRadioGroup,
|
||||||
|
NCard,
|
||||||
|
NText,
|
||||||
|
NSwitch,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
|
|
||||||
import { useForm, useModal } from '@/components'
|
import { useForm } from '@/components'
|
||||||
|
import { useHookPlusRequest } from '@/axios'
|
||||||
|
|
||||||
import type { RFormRules } from '@/components'
|
interface FormModel {
|
||||||
|
name: string | null
|
||||||
|
age: number | null
|
||||||
|
gender: string | null
|
||||||
|
date: Date | null
|
||||||
|
remark: string | null
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'RFormDemo',
|
name: 'RFormDemo',
|
||||||
@ -21,8 +31,15 @@ export default defineComponent({
|
|||||||
// 使用以下 hooks 的时候,应该注意调用时机
|
// 使用以下 hooks 的时候,应该注意调用时机
|
||||||
const [
|
const [
|
||||||
register,
|
register,
|
||||||
{ getFormInstance, validate, restoreValidation, formModel, formRules },
|
{
|
||||||
] = useForm(
|
validate,
|
||||||
|
restoreValidation,
|
||||||
|
formRules,
|
||||||
|
reset,
|
||||||
|
validateTargetField,
|
||||||
|
formConditionRef,
|
||||||
|
},
|
||||||
|
] = useForm<FormModel>(
|
||||||
{
|
{
|
||||||
name: null,
|
name: null,
|
||||||
age: null,
|
age: null,
|
||||||
@ -35,6 +52,7 @@ export default defineComponent({
|
|||||||
required: true,
|
required: true,
|
||||||
message: '请输入姓名',
|
message: '请输入姓名',
|
||||||
trigger: ['blur', 'change'],
|
trigger: ['blur', 'change'],
|
||||||
|
key: 'name',
|
||||||
},
|
},
|
||||||
date: {
|
date: {
|
||||||
required: true,
|
required: true,
|
||||||
@ -56,93 +74,125 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
const formLoading = ref(false)
|
||||||
*
|
|
||||||
* @description
|
const { run: runHookPlusRequest } = useHookPlusRequest(
|
||||||
* 如果待验证数据类型为: number, array 等,需要手动设置 type 类型。
|
() => {
|
||||||
* 具体可以吃查看: async-validator type
|
return new Promise((resolve, reject) => {
|
||||||
* @see https://github.com/yiminghe/async-validator?tab=readme-ov-file#type
|
validate()
|
||||||
*
|
.then(() => {
|
||||||
* 如果你需要自定义验证,可以查看:naive ui custom validation
|
formLoading.value = true
|
||||||
* @see https://www.naiveui.com/zh-CN/dark/components/form#custom-validation.vue
|
|
||||||
*
|
setTimeout(() => {
|
||||||
* 如果只是简单的 rules 管理,可以在初始化 useForm 的时候传入第二个参数;
|
window.$message.success('校验成功')
|
||||||
* 然后使用 formRules 方法获取到初始化 rules 数据。
|
resolve(true)
|
||||||
*/
|
}, 500)
|
||||||
const rules = ref(formRules())
|
})
|
||||||
/**
|
.catch(reject)
|
||||||
*
|
.finally(() => {
|
||||||
* @description
|
formLoading.value = false
|
||||||
* 如果只是简单的数据,可以在初始化 useForm 的时候直接传入第一个参数;
|
})
|
||||||
* 然后使用 formModel 方法获取到初始化 model 数据。
|
})
|
||||||
*
|
},
|
||||||
* 动态的复杂数据,不建议使用该方法管理 model;手动的拆分出来是一个更加好的选择。
|
{
|
||||||
*/
|
manual: true,
|
||||||
const condition = ref(formModel())
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
register,
|
register,
|
||||||
rules,
|
formConditionRef,
|
||||||
condition,
|
|
||||||
restoreValidation,
|
restoreValidation,
|
||||||
formModel,
|
|
||||||
validate,
|
validate,
|
||||||
|
formRules,
|
||||||
|
validateTargetField,
|
||||||
|
reset,
|
||||||
|
formLoading,
|
||||||
|
runHookPlusRequest,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
const { rules } = this
|
const { formConditionRef } = this
|
||||||
const { register, restoreValidation, formModel, validate } = this
|
const {
|
||||||
|
register,
|
||||||
|
restoreValidation,
|
||||||
|
formRules,
|
||||||
|
validateTargetField,
|
||||||
|
reset,
|
||||||
|
runHookPlusRequest,
|
||||||
|
} = this
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RForm onRegister={register} rules={rules} model={this.condition}>
|
<NCard
|
||||||
<NGrid cols={24} xGap={24}>
|
title={() => (
|
||||||
<NFormItemGi label="姓名" path="name" span={12}>
|
<NFlex align="center">
|
||||||
<NInput v-model:value={this.condition.name} />
|
<NText>useForm 表单校验</NText>
|
||||||
</NFormItemGi>
|
<NSwitch v-model:value={this.formLoading} />
|
||||||
<NFormItemGi label="年龄" path="age" span={12}>
|
</NFlex>
|
||||||
<NInputNumber
|
)}
|
||||||
v-model:value={this.condition.age}
|
>
|
||||||
showButton={false}
|
{{
|
||||||
style="width: 100%"
|
default: () => (
|
||||||
/>
|
<RForm
|
||||||
</NFormItemGi>
|
onRegister={register}
|
||||||
<NFormItemGi label="出生日期" path="date" span={12}>
|
rules={formRules()}
|
||||||
<NDatePicker
|
model={formConditionRef}
|
||||||
v-model:value={this.condition.date}
|
submitWhenEnter
|
||||||
style="width: 100%"
|
onFinish={() => {
|
||||||
/>
|
window.$message.success('表单提交成功')
|
||||||
</NFormItemGi>
|
}}
|
||||||
<NFormItemGi label="性别" path="gender" span={12}>
|
loading={this.formLoading}
|
||||||
<NRadioGroup v-model:value={this.condition.gender}>
|
>
|
||||||
<NRadio value="girl">女</NRadio>
|
<NGrid cols={24} xGap={24}>
|
||||||
<NRadio value="man">男</NRadio>
|
<NFormItemGi label="姓名" path="name" span={12}>
|
||||||
</NRadioGroup>
|
<NInput v-model:value={formConditionRef.name} />
|
||||||
</NFormItemGi>
|
</NFormItemGi>
|
||||||
<NFormItemGi label="备注信息" span={24}>
|
<NFormItemGi label="年龄" path="age" span={12}>
|
||||||
<NInput type="textarea" v-model:value={this.condition.remark} />
|
<NInputNumber
|
||||||
</NFormItemGi>
|
v-model:value={formConditionRef.age}
|
||||||
<NFormItemGi span={24}>
|
showButton={false}
|
||||||
<NFlex justify="flex-end" style="width: 100%">
|
/>
|
||||||
<NButton
|
</NFormItemGi>
|
||||||
type="info"
|
<NFormItemGi label="出生日期" path="date" span={12}>
|
||||||
onClick={() => {
|
<NDatePicker v-model:value={formConditionRef.date} />
|
||||||
this.condition = formModel()
|
</NFormItemGi>
|
||||||
|
<NFormItemGi label="性别" path="gender" span={12}>
|
||||||
restoreValidation()
|
<NRadioGroup v-model:value={formConditionRef.gender}>
|
||||||
}}
|
<NRadio value="girl">女</NRadio>
|
||||||
>
|
<NRadio value="man">男</NRadio>
|
||||||
重置表单为初始状态
|
</NRadioGroup>
|
||||||
</NButton>
|
</NFormItemGi>
|
||||||
<NButton type="warning" onClick={restoreValidation.bind(this)}>
|
<NFormItemGi label="备注信息" span={24}>
|
||||||
移除校验状态
|
<NInput
|
||||||
</NButton>
|
type="textarea"
|
||||||
<NButton type="primary" onClick={() => validate()}>
|
v-model:value={formConditionRef.remark}
|
||||||
校验
|
/>
|
||||||
</NButton>
|
</NFormItemGi>
|
||||||
</NFlex>
|
<NFormItemGi span={24}>
|
||||||
</NFormItemGi>
|
<NFlex>
|
||||||
</NGrid>
|
<NButton type="info" onClick={() => reset()}>
|
||||||
</RForm>
|
重置表单为初始状态
|
||||||
|
</NButton>
|
||||||
|
<NButton type="warning" onClick={restoreValidation}>
|
||||||
|
移除校验状态
|
||||||
|
</NButton>
|
||||||
|
<NButton
|
||||||
|
type="primary"
|
||||||
|
onClick={() => validateTargetField('name')}
|
||||||
|
>
|
||||||
|
仅校验姓名字段
|
||||||
|
</NButton>
|
||||||
|
<NButton type="primary" onClick={runHookPlusRequest}>
|
||||||
|
校验
|
||||||
|
</NButton>
|
||||||
|
</NFlex>
|
||||||
|
</NFormItemGi>
|
||||||
|
</NGrid>
|
||||||
|
</RForm>
|
||||||
|
),
|
||||||
|
'header-extra': () => '输入表单的时候,试试按下 Enter 键',
|
||||||
|
}}
|
||||||
|
</NCard>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -2,11 +2,20 @@ import { NCard, NFlex } from 'naive-ui'
|
|||||||
|
|
||||||
const RouterDemoDetail = defineComponent({
|
const RouterDemoDetail = defineComponent({
|
||||||
name: 'RouterDemoDetail',
|
name: 'RouterDemoDetail',
|
||||||
|
setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
return {
|
||||||
|
route,
|
||||||
|
}
|
||||||
|
},
|
||||||
render() {
|
render() {
|
||||||
|
const { route } = this
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NFlex>
|
<NFlex>
|
||||||
<NCard title={(this.$route.query.name as string) || 'hello'}>
|
<NCard title={(route.query.name as string) || 'hello'}>
|
||||||
你好 {this.$route.query.name}
|
你好 {route.query.name}
|
||||||
</NCard>
|
</NCard>
|
||||||
<NCard title="平层路由详情页面">我是平层路由详情页面</NCard>
|
<NCard title="平层路由详情页面">我是平层路由详情页面</NCard>
|
||||||
<NCard title="TIP">
|
<NCard title="TIP">
|
||||||
|
@ -19,20 +19,19 @@ import { uuid } from '@/utils'
|
|||||||
import { useHookPlusRequest } from '@/axios'
|
import { useHookPlusRequest } from '@/axios'
|
||||||
import Mock from 'mockjs'
|
import Mock from 'mockjs'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { useTablePro } from '@/components'
|
import { useCheckedRowKeys, useTablePro, useForm } from '@/components'
|
||||||
import { useCheckedRowKeys } from '@/components'
|
|
||||||
import { useDayjs } from '@/hooks'
|
import { useDayjs } from '@/hooks'
|
||||||
|
|
||||||
import type { DataTableColumns } from 'naive-ui'
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
|
|
||||||
type RowData = {
|
type RowData = {
|
||||||
key: number | string
|
key: number | string
|
||||||
name: string
|
name: string | null
|
||||||
age: number
|
age: number
|
||||||
address: string
|
address: string
|
||||||
tags: string[]
|
tags: string[]
|
||||||
remark: string
|
remark: string
|
||||||
status: string
|
status: string | null
|
||||||
statusText: string
|
statusText: string
|
||||||
signTimeEnd: number
|
signTimeEnd: number
|
||||||
signTimeStart: number
|
signTimeStart: number
|
||||||
@ -100,6 +99,11 @@ export default defineComponent({
|
|||||||
setup() {
|
setup() {
|
||||||
const { format } = useDayjs()
|
const { format } = useDayjs()
|
||||||
|
|
||||||
|
const [register, { formModel, reset }] = useForm<ParamsRef>({
|
||||||
|
RangeTime: null,
|
||||||
|
name: null,
|
||||||
|
status: null,
|
||||||
|
})
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
@ -109,7 +113,13 @@ export default defineComponent({
|
|||||||
*/
|
*/
|
||||||
const [
|
const [
|
||||||
tableProRegister,
|
tableProRegister,
|
||||||
{ runTableRequest, getCurrentTableRequestParams, print, downloadCsv },
|
{
|
||||||
|
runTableRequest,
|
||||||
|
getCurrentTableRequestParams,
|
||||||
|
print,
|
||||||
|
downloadCsv,
|
||||||
|
runAsyncTableRequest,
|
||||||
|
},
|
||||||
] = useTablePro()
|
] = useTablePro()
|
||||||
// 表格数据
|
// 表格数据
|
||||||
const tableDataRef = ref<RowData[]>([])
|
const tableDataRef = ref<RowData[]>([])
|
||||||
@ -163,7 +173,7 @@ export default defineComponent({
|
|||||||
// 表格分页数据
|
// 表格分页数据
|
||||||
const itemCountRef = ref(0)
|
const itemCountRef = ref(0)
|
||||||
// 查询条件
|
// 查询条件
|
||||||
const conditionRef = ref<ParamsRef>({})
|
const conditionRef = ref<ParamsRef>(formModel())
|
||||||
// 缓存模拟数据,不用关心
|
// 缓存模拟数据,不用关心
|
||||||
const mockPersonList = ref<RowData[]>(
|
const mockPersonList = ref<RowData[]>(
|
||||||
(() => {
|
(() => {
|
||||||
@ -227,7 +237,7 @@ export default defineComponent({
|
|||||||
const defaultLength = mockPersonList.value.length
|
const defaultLength = mockPersonList.value.length
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
list = list.filter((curr) => curr.name.includes(name))
|
list = list.filter((curr) => curr.name!.includes(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
@ -294,6 +304,8 @@ export default defineComponent({
|
|||||||
clearAll,
|
clearAll,
|
||||||
collapseRef,
|
collapseRef,
|
||||||
selectKey,
|
selectKey,
|
||||||
|
register,
|
||||||
|
reset,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
@ -315,11 +327,13 @@ export default defineComponent({
|
|||||||
clearKey,
|
clearKey,
|
||||||
clearAll,
|
clearAll,
|
||||||
selectKey,
|
selectKey,
|
||||||
|
register,
|
||||||
|
reset,
|
||||||
} = this
|
} = this
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NFlex vertical>
|
<NFlex vertical>
|
||||||
<RCollapse open={this.collapseRef} bordered>
|
<RCollapse open={this.collapseRef} onRegister={register}>
|
||||||
{{
|
{{
|
||||||
default: () => (
|
default: () => (
|
||||||
<>
|
<>
|
||||||
@ -356,6 +370,7 @@ export default defineComponent({
|
|||||||
),
|
),
|
||||||
action: () => (
|
action: () => (
|
||||||
<NFlex>
|
<NFlex>
|
||||||
|
<NButton onClick={() => reset(this.conditionRef)}>重置</NButton>
|
||||||
<NButton
|
<NButton
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => runTableRequest()}
|
onClick={() => runTableRequest()}
|
||||||
|
5
uno.config.ts
Normal file
5
uno.config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { defineConfig } from 'unocss'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
safelist: ['w-full', 'h-full'],
|
||||||
|
})
|
@ -86,6 +86,9 @@
|
|||||||
"onWatcherCleanup": true,
|
"onWatcherCleanup": true,
|
||||||
"useId": true,
|
"useId": true,
|
||||||
"useModel": true,
|
"useModel": true,
|
||||||
"useTemplateRef": true
|
"useTemplateRef": true,
|
||||||
|
"Slot": true,
|
||||||
|
"Slots": true,
|
||||||
|
"NFormItem": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
unplugin/auto-imports.d.ts
vendored
3
unplugin/auto-imports.d.ts
vendored
@ -7,6 +7,7 @@
|
|||||||
export {}
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
const EffectScope: typeof import('vue')['EffectScope']
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||||
const computed: typeof import('vue')['computed']
|
const computed: typeof import('vue')['computed']
|
||||||
const createApp: typeof import('vue')['createApp']
|
const createApp: typeof import('vue')['createApp']
|
||||||
@ -83,6 +84,6 @@ declare global {
|
|||||||
// for type re-export
|
// for type re-export
|
||||||
declare global {
|
declare global {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||||
import('vue')
|
import('vue')
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import viteEslint from 'vite-plugin-eslint'
|
|||||||
import mockDevServerPlugin from 'vite-plugin-mock-dev-server'
|
import mockDevServerPlugin from 'vite-plugin-mock-dev-server'
|
||||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||||
import unpluginViteComponents from 'unplugin-vue-components/vite'
|
import unpluginViteComponents from 'unplugin-vue-components/vite'
|
||||||
|
import unoCSS from 'unocss/vite'
|
||||||
|
|
||||||
import { cdn as viteCDNPlugin } from 'vite-plugin-cdn2'
|
import { cdn as viteCDNPlugin } from 'vite-plugin-cdn2'
|
||||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||||
@ -212,6 +213,7 @@ function baseOptions(mode: string): PluginOption[] {
|
|||||||
inject: 'body-last',
|
inject: 'body-last',
|
||||||
customDomId: '__svg__icons__dom__',
|
customDomId: '__svg__icons__dom__',
|
||||||
}),
|
}),
|
||||||
|
unoCSS(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user