feat(form): [form] add xDesign theme (#1507)

This commit is contained in:
gimmyhehe 2024-03-26 09:10:37 +08:00 committed by GitHub
parent 68abf0beea
commit f844712a66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 372 additions and 14 deletions

View File

@ -385,6 +385,17 @@ export default {
mode: ['pc'],
pcDemo: ''
},
{
name: 'extra',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '表单项额外提示',
'en-US': 'Form item extra tip'
},
mode: ['pc'],
pcDemo: 'extra-tip'
},
{
name: 'inline-message',
type: 'boolean',
@ -581,6 +592,16 @@ export default {
},
mode: ['pc'],
pcDemo: 'slot-label'
},
{
name: 'error',
defaultValue: '',
desc: {
'zh-CN': '错误提示内容',
'en-US': 'Error content'
},
mode: ['pc'],
pcDemo: 'error-label'
}
]
}

View File

@ -0,0 +1,62 @@
<template>
<div class="demo-form">
<tiny-form ref="ruleFormRef" :model="createData" :rules="rules">
<tiny-form-item label="姓名" prop="name">
<tiny-input v-model="createData.name"></tiny-input>
</tiny-form-item>
<tiny-form-item label="年龄" prop="age">
<tiny-input v-model="createData.age"></tiny-input>
<template #error>
<span>错误提示内容插槽</span>
</template>
</tiny-form-item>
<tiny-form-item label="昵称" prop="nickname" validate-type="text">
<tiny-input v-model="createData.nickname"></tiny-input>
<template #error="message">
<span class="error-slot">{{ message }}</span>
</template>
</tiny-form-item>
<tiny-form-item>
<tiny-button type="primary" @click="validateField"> 校验 </tiny-button>
</tiny-form-item>
</tiny-form>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Form as TinyForm, FormItem as TinyFormItem, Input as TinyInput, Button as TinyButton } from '@opentiny/vue'
const createData = ref({
name: '',
age: '',
nickname: ''
})
const rules = ref({
name: [
{ required: true, message: '必填', trigger: 'blur' },
{ min: 2, max: 11, message: '长度必须不小于2', trigger: ['change', 'blur'] }
],
age: { required: true },
nickname: [
{ required: true, message: '昵称必填' },
{ min: 2, max: 11, message: '昵称长度必须不小于2切不大于11', trigger: ['change', 'blur'] }
]
})
const ruleFormRef = ref()
function validateField() {
ruleFormRef.value.validate(() => {})
}
</script>
<style scoped>
.demo-form {
width: 380px;
}
.error-slot {
color: #ffd0a6;
}
</style>

View File

@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test'
test('错误提示插槽', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('form#error-slot')
const demo = page.locator('#error-slot')
const getTooltipByText = (text: string) => page.locator('.tiny-tooltip').getByText(text)
const validBtn = demo.getByRole('button', { name: '校验' }).first()
await validBtn.click()
await expect(getTooltipByText('必填')).toBeVisible()
await expect(getTooltipByText('错误提示内容插槽')).toBeVisible()
await expect(page.locator('.error-slot')).toBeVisible()
})

View File

@ -0,0 +1,71 @@
<template>
<div class="demo-form">
<tiny-form ref="ruleFormRef" :model="createData" :rules="rules">
<tiny-form-item label="姓名" prop="name">
<tiny-input v-model="createData.name"></tiny-input>
</tiny-form-item>
<tiny-form-item label="年龄" prop="age">
<tiny-input v-model="createData.age"></tiny-input>
<template #error>
<span>错误提示内容插槽</span>
</template>
</tiny-form-item>
<tiny-form-item label="昵称" prop="nickname" validate-type="text">
<tiny-input v-model="createData.nickname"></tiny-input>
<template #error="message">
<span class="error-slot">{{ message }}</span>
</template>
</tiny-form-item>
<tiny-form-item>
<tiny-button type="primary" @click="validateField"> 校验 </tiny-button>
</tiny-form-item>
</tiny-form>
</div>
</template>
<script>
import { Form, FormItem, Input, Button } from '@opentiny/vue'
export default {
components: {
TinyForm: Form,
TinyFormItem: FormItem,
TinyInput: Input,
TinyButton: Button
},
data() {
return {
createData: {
name: '',
age: '',
nickname: ''
},
rules: {
name: [
{ required: true, message: '必填', trigger: 'blur' },
{ min: 2, max: 11, message: '长度必须不小于2', trigger: ['change', 'blur'] }
],
age: { required: true },
nickname: [
{ required: true, message: '昵称必填' },
{ min: 2, max: 11, message: '昵称长度必须不小于2切不大于11', trigger: ['change', 'blur'] }
]
}
}
},
methods: {
validateField() {
this.$refs.ruleFormRef.validate(() => {})
}
}
}
</script>
<style scoped>
.demo-form {
width: 380px;
}
.error-slot {
color: #ffd0a6;
}
</style>

View File

@ -0,0 +1,48 @@
<template>
<div class="demo-form">
<tiny-form ref="ruleFormRef" :model="createData" :rules="rules" validate-type="text">
<tiny-form-item label="姓名" prop="name" extra="需要填写真实姓名">
<tiny-input v-model="createData.name"></tiny-input>
</tiny-form-item>
<tiny-form-item label="年龄" prop="age" extra="需要填写真实年龄">
<tiny-input v-model="createData.age"></tiny-input>
</tiny-form-item>
<tiny-form-item>
<tiny-button type="primary" @click="submit"> 提交 </tiny-button>
</tiny-form-item>
</tiny-form>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Form as TinyForm, FormItem as TinyFormItem, Input as TinyInput, Button as TinyButton } from '@opentiny/vue'
const createData = ref({
name: '',
age: ''
})
const rules = ref({
name: [
{ required: true, message: '必填' },
{ min: 2, max: 11, message: '长度必须不小于2' }
],
age: { required: true }
})
const ruleFormRef = ref()
function submit() {
ruleFormRef.value.validate(() => {})
}
</script>
<style scoped>
.demo-form {
width: 380px;
}
.error-slot {
color: #ffd0a6;
}
</style>

View File

@ -0,0 +1,15 @@
import { test, expect } from '@playwright/test'
test('错误提示插槽', async ({ page }) => {
page.on('pageerror', (exception) => expect(exception).toBeNull())
await page.goto('form#extra-tip')
const demo = page.locator('#extra-tip')
const validBtn = demo.getByRole('button', { name: '提交' }).first()
await expect(demo.getByText('需要填写真实姓名')).toBeVisible()
await expect(demo.getByText('需要填写真实年龄')).toBeVisible()
await validBtn.click()
await expect(demo.getByText('需要填写真实姓名')).toBeVisible()
await expect(demo.getByText('需要填写真实年龄')).toBeVisible()
})

View File

@ -0,0 +1,57 @@
<template>
<div class="demo-form">
<tiny-form ref="ruleFormRef" :model="createData" :rules="rules" validate-type="text">
<tiny-form-item label="姓名" prop="name" extra="需要填写真实姓名">
<tiny-input v-model="createData.name"></tiny-input>
</tiny-form-item>
<tiny-form-item label="年龄" prop="age" extra="需要填写真实年龄">
<tiny-input v-model="createData.age"></tiny-input>
</tiny-form-item>
<tiny-form-item>
<tiny-button type="primary" @click="submit"> 提交 </tiny-button>
</tiny-form-item>
</tiny-form>
</div>
</template>
<script>
import { Form, FormItem, Input, Button } from '@opentiny/vue'
export default {
components: {
TinyForm: Form,
TinyFormItem: FormItem,
TinyInput: Input,
TinyButton: Button
},
data() {
return {
createData: {
name: '',
age: ''
},
rules: {
name: [
{ required: true, message: '必填' },
{ min: 2, max: 11, message: '长度必须不小于2' }
],
age: { required: true }
}
}
},
methods: {
submit() {
this.$refs.ruleFormRef.validate(() => {})
}
}
}
</script>
<style scoped>
.demo-form {
width: 380px;
}
.error-slot {
color: #ffd0a6;
}
</style>

View File

@ -78,10 +78,16 @@ export default {
'en-US': 'Form Validation/Clear Validation'
},
desc: {
'zh-CN':
'<p>通过 <code>rules</code> 设置校验规则,\n 调用 <code>clearValidate</code> 方法移除表单项的校验结果。传入待移除的表单项的 <code>prop</code> 属性或者 <code>prop</code> 组成的数组,如不传则移除整个表单的校验结果,\n 调用 <code>resetFields</code> 方法重置表单并移除校验结果。\n </p>',
'en-US':
'<p>Includes common verification rules such as mandatory fields, date, time, URL, and email. Use <code>trigger</code> to configure the mode of triggering the validation rule. If <code>change</code> is used, the validation is triggered when the value in the text box changes. If <code>blur</code> is used, the validation is triggered after the focus is lost. \n <br />\n Use <code>clearValidate</code> method to clear the validation result. \n </p>\n '
'zh-CN': `<p>通过 <code>rules</code> 设置校验规则,
调用 <code>clearValidate</code> <code>prop</code> <code>prop</code>
调用 <code>resetFields</code>
</p>`,
'en-US': `<p>Includes common verification rules such as mandatory fields, date, time, URL, and email.
Use <code>trigger</code> to configure the mode of triggering the validation rule.
If <code>change</code> is used, the validation is triggered when the value in the text box changes.
If <code>blur</code> is used, the validation is triggered after the focus is lost.
<br />
Use <code>clearValidate</code> method to clear the validation result.</p>`
},
codeFiles: ['form-validation.vue']
},
@ -284,6 +290,30 @@ export default {
},
codeFiles: ['popper-options.vue']
},
{
demoId: 'error-slot',
name: {
'zh-CN': '错误提示插槽',
'en-US': 'Error content'
},
desc: {
'zh-CN': '<p>通过 <code>error</code> 插槽,自定义标签文本的内容。</p>',
'en-US': '<p>Use the <code>error</code> slot to customize the content of the label text. </p>'
},
codeFiles: ['error-slot.vue']
},
{
demoId: 'extra-tip',
name: {
'zh-CN': '额外提示信息',
'en-US': 'Extra tip'
},
desc: {
'zh-CN': '<p>通过 <code>extra</code> 配置额外提示信息。</p>',
'en-US': '<p>Configure additional prompt information through <code>extra</code>. </p>'
},
codeFiles: ['extra-tip.vue']
},
{
demoId: 'events',
name: {

View File

@ -64,7 +64,8 @@ const changeTheme = (themeKey) => {
router.push({
params: {
theme: THEME_ROUTE_MAP[themeKey]
}
},
hash: router?.currentRoute.value.hash
})
}

View File

@ -66,6 +66,13 @@
width: 100%;
}
.@{form-item-prefix-cls}__extra-tip {
line-height: var(--ti-form-item-extra-tip-line-height);
margin-top: var(--ti-form-item-extra-tip-margin-top);
color: var(--ti-form-item-extra-tip-font-color);
font-size: var(--ti-form-item-extra-tip-font-size);
}
& & {
margin-bottom: 0;
}

View File

@ -55,4 +55,12 @@
--ti-form-item-error-inline-margin-left: calc(var(--ti-common-space-base, 4px) * 2.5);
// 表单校验块级错误消息上侧内边距
--ti-form-item-error-block-padding-top: var(--ti-common-space-2x, 8px);
// 表单额外提示信息上侧外边距
--ti-form-item-extra-tip-margin-top: var(--ti-common-space-2x, 8px);
// 表单额外提示信息字体颜色
--ti-form-item-extra-tip-font-color: var(--ti-common-color-text-weaken, #8a8e99);
// 表单额外提示信息字体大小
--ti-form-item-extra-tip-font-size: var(--ti-common-font-size-1, 14px);
// 表单额外提示信息字体行高
--ti-form-item-extra-tip-line-height: var(--ti-common-line-height-number, 1.5);
}

View File

@ -58,7 +58,8 @@ export const formItemProps = {
vertical: {
type: Boolean,
default: false
}
},
extra: String
}
export default defineComponent({

View File

@ -79,7 +79,8 @@ export default defineComponent({
vertical: {
type: Boolean,
default: false
}
},
extra: String
},
setup(props, context) {
return setup({ props, context, renderless, api }) as unknown as IFormItemApi
@ -181,8 +182,8 @@ export default defineComponent({
typeof this.appendToBody === 'boolean'
? this.appendToBody
: typeof formAppendToBody === 'boolean'
? formAppendToBody
: true
? formAppendToBody
: true
const validatePosition =
this.validatePosition || (state.formInstance && state.formInstance.validatePosition) || 'top-end'
@ -221,10 +222,17 @@ export default defineComponent({
modelValue: isShowError ? state.canShowTip : false,
zIndex: 'relative',
renderContent() {
return [
validateIconNode,
<span class={`${classPrefix}form-item__validate-message`}>{state.validateMessage}</span>
]
let tooltipContent
if (errorSlot) {
tooltipContent = [errorSlot]
} else {
tooltipContent = [
validateIconNode,
<span class={`${classPrefix}form-item__validate-message`}>{state.validateMessage}</span>
]
}
return tooltipContent
}
},
on: {
@ -298,6 +306,19 @@ export default defineComponent({
: null
]
)
const ExtraTip = this.extra
? h(
'div',
{
class: {
[`${classPrefix}form-item__extra-tip`]: true
}
},
this.extra
)
: null
return h(
'div',
{
@ -336,7 +357,8 @@ export default defineComponent({
}
},
[ErrorContent]
)
),
isMobile ? null : ExtraTip
]
)
]