wip(VForm): VForm coding

This commit is contained in:
陈凯龙 2021-12-14 17:42:43 +08:00
parent 7528fe6da6
commit d7d0ada558
11 changed files with 221 additions and 51 deletions

View File

@ -14,5 +14,6 @@
"i18n-ally.enabledParsers": ["ts"], "i18n-ally.enabledParsers": ["ts"],
"i18n-ally.sourceLanguage": "en", "i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN", "i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"] "i18n-ally.enabledFrameworks": ["vue", "react"],
"god.tsconfig": "./tsconfig.json"
} }

View File

@ -44,6 +44,7 @@
"@typescript-eslint/parser": "^5.6.0", "@typescript-eslint/parser": "^5.6.0",
"@vitejs/plugin-vue": "^1.9.3", "@vitejs/plugin-vue": "^1.9.3",
"@vitejs/plugin-vue-jsx": "^1.3.0", "@vitejs/plugin-vue-jsx": "^1.3.0",
"async-validator": "^4.0.7",
"autoprefixer": "^10.4.0", "autoprefixer": "^10.4.0",
"commitizen": "^4.2.4", "commitizen": "^4.2.4",
"eslint": "^8.4.1", "eslint": "^8.4.1",

View File

@ -11,6 +11,7 @@ specifiers:
'@vitejs/plugin-vue': ^1.9.3 '@vitejs/plugin-vue': ^1.9.3
'@vitejs/plugin-vue-jsx': ^1.3.0 '@vitejs/plugin-vue-jsx': ^1.3.0
'@vueuse/core': ^7.1.2 '@vueuse/core': ^7.1.2
async-validator: ^4.0.7
autoprefixer: ^10.4.0 autoprefixer: ^10.4.0
commitizen: ^4.2.4 commitizen: ^4.2.4
element-plus: 1.2.0-beta.6 element-plus: 1.2.0-beta.6
@ -69,6 +70,7 @@ devDependencies:
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.6.0_eslint@8.4.1+typescript@4.5.2 '@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.6.0_eslint@8.4.1+typescript@4.5.2
'@vitejs/plugin-vue': registry.npmmirror.com/@vitejs/plugin-vue/1.10.1_vite@2.6.14 '@vitejs/plugin-vue': registry.npmmirror.com/@vitejs/plugin-vue/1.10.1_vite@2.6.14
'@vitejs/plugin-vue-jsx': registry.npmmirror.com/@vitejs/plugin-vue-jsx/1.3.0 '@vitejs/plugin-vue-jsx': registry.npmmirror.com/@vitejs/plugin-vue-jsx/1.3.0
async-validator: registry.npmmirror.com/async-validator/4.0.7
autoprefixer: registry.npmmirror.com/autoprefixer/10.4.0_postcss@8.4.4 autoprefixer: registry.npmmirror.com/autoprefixer/10.4.0_postcss@8.4.4
commitizen: registry.npmmirror.com/commitizen/4.2.4 commitizen: registry.npmmirror.com/commitizen/4.2.4
eslint: registry.npmmirror.com/eslint/8.4.1 eslint: registry.npmmirror.com/eslint/8.4.1
@ -4320,7 +4322,7 @@ packages:
{ {
integrity: sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=, integrity: sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.nlark.com/semver/download/semver-5.7.1.tgz?cache=0&sync_timestamp=1631500167672&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-5.7.1.tgz tarball: https://registry.nlark.com/semver/download/semver-5.7.1.tgz
} }
name: semver name: semver
version: 5.7.1 version: 5.7.1
@ -4332,7 +4334,7 @@ packages:
{ {
integrity: sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=, integrity: sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.nlark.com/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1631500167672&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz tarball: https://registry.nlark.com/semver/download/semver-6.3.0.tgz
} }
name: semver name: semver
version: 6.3.0 version: 6.3.0
@ -6959,7 +6961,7 @@ packages:
{ {
integrity: sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=, integrity: sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/acorn/download/acorn-7.4.1.tgz tarball: https://registry.npmmirror.com/acorn/download/acorn-7.4.1.tgz?cache=0&sync_timestamp=1637226362293&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Facorn%2Fdownload%2Facorn-7.4.1.tgz
} }
name: acorn name: acorn
version: 7.4.1 version: 7.4.1
@ -6972,7 +6974,7 @@ packages:
{ {
integrity: sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==, integrity: sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/acorn/download/acorn-8.6.0.tgz tarball: https://registry.npmmirror.com/acorn/download/acorn-8.6.0.tgz?cache=0&sync_timestamp=1637226362293&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Facorn%2Fdownload%2Facorn-8.6.0.tgz
} }
name: acorn name: acorn
version: 8.6.0 version: 8.6.0
@ -7021,7 +7023,6 @@ packages:
} }
name: async-validator name: async-validator
version: 4.0.7 version: 4.0.7
dev: false
registry.npmmirror.com/autoprefixer/10.4.0_postcss@8.4.4: registry.npmmirror.com/autoprefixer/10.4.0_postcss@8.4.4:
resolution: resolution:
@ -7911,7 +7912,7 @@ packages:
{ {
integrity: sha1-MOvR73wv3/AcOk8VEESvJfqwUj4=, integrity: sha1-MOvR73wv3/AcOk8VEESvJfqwUj4=,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-1.3.0.tgz?cache=0&sync_timestamp=1636378498011&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-1.3.0.tgz
} }
name: eslint-visitor-keys name: eslint-visitor-keys
version: 1.3.0 version: 1.3.0
@ -7923,7 +7924,7 @@ packages:
{ {
integrity: sha1-9lMoJZMFknOSyTjtROsKXJsr0wM=, integrity: sha1-9lMoJZMFknOSyTjtROsKXJsr0wM=,
registry: https://registry.npm.taobao.org/, registry: https://registry.npm.taobao.org/,
tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz tarball: https://registry.npmmirror.com/eslint-visitor-keys/download/eslint-visitor-keys-2.1.0.tgz?cache=0&sync_timestamp=1636378498011&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Feslint-visitor-keys%2Fdownload%2Feslint-visitor-keys-2.1.0.tgz
} }
name: eslint-visitor-keys name: eslint-visitor-keys
version: 2.1.0 version: 2.1.0

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, unref } from 'vue' import { ref, onMounted, unref, reactive } from 'vue'
import { ElConfigProvider } from 'element-plus' import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/lib/locale/lang/zh-cn' import zhCn from 'element-plus/lib/locale/lang/zh-cn'
// import en from 'element-plus/lib/locale/lang/en' // import en from 'element-plus/lib/locale/lang/en'
@ -9,21 +9,29 @@ const formRef = ref<ComponentRef<typeof VFrom> & VFormExpose>()
onMounted(() => { onMounted(() => {
const form = unref(formRef.value) const form = unref(formRef.value)
console.log(form?.$el) console.log(form?.$el)
const schema: VFormSchema = [
{
field: '1',
colProps: {}
}
]
console.log(schema)
}) })
const schema = reactive<VFormSchema[]>([
{
field: 'field1',
label: '字段1',
component: 'Input'
}
])
// setTimeout(() => {
// schema.push({
// field: '2'
// })
// }, 3000)
</script> </script>
<template> <template>
<ElConfigProvider :locale="zhCn"> <ElConfigProvider :locale="zhCn">
<VFrom ref="formRef" /> <!-- <VFrom ref="formRef" is-custom>
<Component :is="VFrom" /> <template #default> hahahah </template>
</VFrom> -->
<VFrom :schema="schema" />
<!-- <VFrom :is-col="false" :schema="schema" /> -->
<!-- <Component :is="VFrom" /> -->
<!-- <RouterView class="app" /> --> <!-- <RouterView class="app" /> -->
</ElConfigProvider> </ElConfigProvider>
</template> </template>

View File

@ -1,8 +1,10 @@
<script lang="tsx"> <script lang="tsx">
import { PropType, defineComponent, onMounted, ref, unref } from 'vue' import { PropType, defineComponent, ref, computed, unref, watch } from 'vue'
import { ElForm } from 'element-plus' import { ElForm, ElFormItem, ElRow, ElCol } from 'element-plus'
// import { COMPONENT_MAP } from './componentMap' import { COMPONENT_MAP } from './componentMap'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { getSlot } from '@/utils/tsxHelper'
import { setTextPlaceholder } from './helper'
export default defineComponent({ export default defineComponent({
name: 'VForm', name: 'VForm',
@ -25,17 +27,77 @@ export default defineComponent({
// //
isCustom: propTypes.bool.def(false) isCustom: propTypes.bool.def(false)
}, },
setup() { setup(props, { slots }) {
const formRef = ref<ComponentRef<typeof ElForm>>() const formRef = ref<ComponentRef<typeof ElForm>>()
onMounted(() => { const getProps = computed(() => props)
console.log(unref(formRef)?.clearValidate) const { schema, isCol, isCustom, autoSetPlaceholder } = unref(getProps)
}) const test = ref('')
// function renderWrap() {} watch(
() => test.value,
(val) => {
console.log(val)
}
)
// function renderFormItem() {} // 使
function renderWrap() {
const content = isCol ? (
<ElRow gutter={20}>
<ElCol>{renderFormItem()}</ElCol>
</ElRow>
) : (
renderFormItem()
)
console.log(content)
return content
}
return () => <ElForm ref={formRef}></ElForm> // formItem
function renderFormItem() {
// hidden
return schema
.filter((v) => !v.hidden)
.map((item) => {
return (
<ElFormItem {...(item.formItemProps || {})} prop={item.field} label={item.label}>
{() => {
const Com = COMPONENT_MAP[item.component as string] as ReturnType<
typeof defineComponent
>
return (
<Com
v-model={test.value}
{...(autoSetPlaceholder && setTextPlaceholder(item))}
></Com>
)
}}
</ElFormItem>
)
})
// return <div>{schema[0]?.field}</div>
}
// Form
function getFormBindValue() {
//
const delKeys = ['schema', 'isCol', 'autoSetPlaceholder', 'isCustom']
const props = { ...unref(getProps) }
for (const key in props) {
if (delKeys.indexOf(key) !== -1) {
delete props[key]
}
}
return props
}
return () => (
<ElForm ref={formRef} {...getFormBindValue()}>
{{
default: () => (isCustom ? getSlot(slots, 'default') : renderWrap())
}}
</ElForm>
)
} }
}) })
</script> </script>

View File

@ -1,8 +1,39 @@
// import { useI18n } from '@/hooks/web/useI18n'
// const { t } = useI18n()
/** /**
* *
* @param schema * @param schema
* @description placeholder * @description placeholder
*/ */
export function setTextPlaceholder(schema: VFormSchema) { export function setTextPlaceholder(schema: VFormSchema): {
placeholder?: string
startPlaceholder?: string
endPlaceholder?: string
} {
console.log(schema) console.log(schema)
// const textMap = ['Input', 'Autocomplete', 'InputNumber']
// const selectMap = ['Select', 'TimePicker', 'DatePicker', 'TimeSelect', 'TimeSelect']
// if (textMap.includes(schema?.component as string)) {
// return {
// placeholder: t('common.inputText')
// }
// }
// if (selectMap.includes(schema?.component as string)) {
// // 一些范围选择器
// const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange']
// if (
// twoTextMap.includes(schema?.componentProps?.type || schema?.componentProps?.isRange) as string
// ) {
// return {
// startPlaceholder: t('common.startTimeText'),
// endPlaceholder: t('common.endTimeText'),
// rangeSeparator: '-'
// }
// } else {
// return {
// placeholder: t('common.selectText')
// }
// }
// }
return {}
} }

50
src/hooks/web/useI18n.ts Normal file
View File

@ -0,0 +1,50 @@
// // import { i18n } from '@/plugins/i18n'
// type I18nGlobalTranslation = {
// (key: string): string
// (key: string, locale: string): string
// (key: string, locale: string, list: unknown[]): string
// (key: string, locale: string, named: Record<string, unknown>): string
// (key: string, list: unknown[]): string
// (key: string, named: Record<string, unknown>): string
// }
// type I18nTranslationRestParameters = [string, any]
// function getKey(namespace: string | undefined, key: string) {
// if (!namespace) {
// return key
// }
// if (key.startsWith(namespace)) {
// return key
// }
// return `${namespace}.${key}`
// }
// export function useI18n(namespace?: string): {
// t: I18nGlobalTranslation
// } {
// const normalFn = {
// t: (key: string) => {
// return getKey(namespace, key)
// }
// }
// if (!i18n) {
// return normalFn
// }
// const { t, ...methods } = i18n.global
// const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
// if (!key) return ''
// if (!key.includes('.') && !namespace) return key
// return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters))
// }
// return {
// ...methods,
// t: tFn
// }
// }
// export const t = (key: string) => key

View File

@ -1,18 +1,16 @@
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 引入windi css // 引入windi css
import '@/plugins/windicss' import '@/plugins/windicss'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 引入多语言 // 引入多语言
import { setupI18n } from '@/plugins/i18n' import { setupI18n } from '@/plugins/i18n'
setupI18n(app)
// 引入状态管理 // 引入状态管理
import { setupStore } from '@/store' import { setupStore } from '@/store'
setupStore(app) ;(async () => {
await setupI18n(app)
app.mount('#app') setupStore(app)
app.mount('#app')
})()

View File

@ -1,13 +1,15 @@
import { createI18n } from 'vue-i18n' import { createI18n } from 'vue-i18n'
import type { App } from 'vue' import type { App } from 'vue'
// export let i18n: ReturnType<typeof createI18n>
const messages = Object.fromEntries( const messages = Object.fromEntries(
Object.entries(import.meta.globEager('../../locales/*.ts')).map(([key, value]) => { Object.entries(import.meta.globEager('../../locales/*.ts')).map(([key, value]) => {
return [key.slice(14, -3), value.default] return [key.slice(14, -3), value.default]
}) })
) )
export function setupI18n(app: App<Element>): void { export function setupI18n(app: App): void {
const i18n = createI18n({ const i18n = createI18n({
legacy: false, legacy: false,
locale: 'zh-CN', locale: 'zh-CN',

View File

@ -1,4 +1,5 @@
import type { Component, RendererNode, VNode, CSSProperties } from 'vue' import type { Component, RendererNode, VNode, CSSProperties } from 'vue'
import type { RuleItem } from 'async-validator'
declare global { declare global {
// BfForm types start // BfForm types start
@ -33,18 +34,18 @@ declare global {
declare type FormValueTypes = string | number | string[] | number[] | boolean | undefined declare type FormValueTypes = string | number | string[] | number[] | boolean | undefined
declare type FormRules = { declare interface FormItemRule extends RuleItem {
required?: boolean trigger?: string
message?: string
type?: string
trigger?: 'blur' | 'change' | ['change', 'blur']
validator?: (rule: any, value: FormValueTypes, callback: Fn) => void | boolean
} }
declare type FormRulesMap<T extends string = string> = Partial<
Record<T, FormItemRule | FormItemRule[]>
>
declare type FormItemProps = { declare type FormItemProps = {
labelWidth?: string | number labelWidth?: string | number
required?: boolean required?: boolean
rules?: FormRules | FormRules[] rules?: FormRulesMap
error?: string error?: string
showMessage?: boolean showMessage?: boolean
inlineMessage?: boolean inlineMessage?: boolean
@ -471,7 +472,7 @@ declare global {
} }
} }
declare type FormSchema = { declare type VFormSchema = {
field: string field: string
label?: string label?: string
colProps?: ColProps colProps?: ColProps
@ -502,5 +503,4 @@ declare global {
} }
// VForm types end // VForm types end
declare type VFormSchema = FormSchema[]
} }

16
src/utils/tsxHelper.ts Normal file
View File

@ -0,0 +1,16 @@
import { Slots } from 'vue'
import { isFunction } from '@/utils/is'
export function getSlot(slots: Slots, slot = 'default', data?: Recordable) {
// Reflect.has 判断一个对象是否存在某个属性
if (!slots || !Reflect.has(slots, slot)) {
return null
}
if (!isFunction(slots[slot])) {
console.error(`${slot} is not a function!`)
return null
}
const slotFn = slots[slot]
if (!slotFn) return null
return slotFn(data)
}