表單元件
表單元件是使用者互動的重要部分,uni-app 提供了豐富的表單元件,用於收集和提交使用者輸入的資料。
表單元件列表
button 按鈕
按鈕元件,用於觸發操作。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| size | String | default | 按鈕的大小,可選值:default、mini |
| type | String | default | 按鈕的樣式類型,可選值:primary、default、warn |
| plain | Boolean | false | 按鈕是否鏤空,背景色透明 |
| disabled | Boolean | false | 是否禁用 |
| loading | Boolean | false | 名稱前是否帶 loading 圖示 |
| form-type | String | 用於 form 元件,點擊分別會觸發 form 元件的 submit/reset 事件 | |
| open-type | String | 開放能力,不同平台有不同的支援度 | |
| hover-class | String | button-hover | 指定按鈕按下去的樣式類 |
範例程式碼
vue
<template>
<view>
<button type="primary">主要按鈕</button>
<button type="default">預設按鈕</button>
<button type="warn">警告按鈕</button>
<button type="primary" loading>載入中按鈕</button>
<button type="primary" disabled>禁用按鈕</button>
<button type="primary" plain>鏤空按鈕</button>
</view>
</template>checkbox 多選框
多選框元件,用於選擇。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| value | String | checkbox 標識 | |
| disabled | Boolean | false | 是否禁用 |
| checked | Boolean | false | 目前是否選中 |
| color | Color | checkbox 的顏色 |
範例程式碼
vue
<template>
<view>
<checkbox-group @change="checkboxChange">
<label v-for="item in items" :key="item.value">
<checkbox :value="item.value" :checked="item.checked" />
{{item.name}}
</label>
</checkbox-group>
</view>
</template>
<script>
export default {
data() {
return {
items: [
{value: 'USA', name: '美國', checked: false},
{value: 'CHN', name: '中國', checked: true},
{value: 'JPN', name: '日本', checked: false}
]
}
},
methods: {
checkboxChange(e) {
console.log(e.detail.value)
}
}
}
</script>input 輸入框
輸入框元件,用於文字輸入。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| value | String | 輸入框的初始內容 | |
| type | String | text | input 的類型,可選值:text、number、idcard、digit、password |
| password | Boolean | false | 是否是密碼類型 |
| placeholder | String | 輸入框為空時佔位符 | |
| placeholder-style | String | 指定 placeholder 的樣式 | |
| disabled | Boolean | false | 是否禁用 |
| maxlength | Number | 140 | 最大輸入長度 |
| focus | Boolean | false | 取得焦點 |
| confirm-type | String | done | 設定鍵盤右下角按鈕的文字,可選值:send、search、next、go、done |
範例程式碼
vue
<template>
<view>
<input type="text" placeholder="請輸入使用者名稱" v-model="username" />
<input type="password" placeholder="請輸入密碼" v-model="password" />
<input type="number" placeholder="請輸入年齡" v-model="age" />
</view>
</template>
<script>
export default {
data() {
return {
username: '',
password: '',
age: ''
}
}
}
</script>picker 選擇器
選擇器元件,支援普通選擇器、多列選擇器、時間選擇器、日期選擇器。
普通選擇器
vue
<template>
<view>
<picker :range="array" @change="bindPickerChange">
<view class="picker">
目前選擇:{{array[index]}}
</view>
</picker>
</view>
</template>
<script>
export default {
data() {
return {
array: ['中國', '美國', '日本', '韓國'],
index: 0
}
},
methods: {
bindPickerChange(e) {
this.index = e.detail.value
}
}
}
</script>時間選擇器
vue
<template>
<view>
<picker mode="time" :value="time" start="09:00" end="18:00" @change="bindTimeChange">
<view class="picker">
目前選擇:{{time}}
</view>
</picker>
</view>
</template>
<script>
export default {
data() {
return {
time: '12:00'
}
},
methods: {
bindTimeChange(e) {
this.time = e.detail.value
}
}
}
</script>日期選擇器
vue
<template>
<view>
<picker mode="date" :value="date" start="2015-09-01" end="2025-09-01" @change="bindDateChange">
<view class="picker">
目前選擇:{{date}}
</view>
</picker>
</view>
</template>
<script>
export default {
data() {
return {
date: '2021-09-01'
}
},
methods: {
bindDateChange(e) {
this.date = e.detail.value
}
}
}
</script>radio 單選框
單選框元件,用於單選。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| value | String | radio 標識 | |
| checked | Boolean | false | 目前是否選中 |
| disabled | Boolean | false | 是否禁用 |
| color | Color | radio 的顏色 |
範例程式碼
vue
<template>
<view>
<radio-group @change="radioChange">
<label v-for="item in items" :key="item.value">
<radio :value="item.value" :checked="item.checked" />
{{item.name}}
</label>
</radio-group>
</view>
</template>
<script>
export default {
data() {
return {
items: [
{value: 'USA', name: '美國', checked: false},
{value: 'CHN', name: '中國', checked: true},
{value: 'JPN', name: '日本', checked: false}
]
}
},
methods: {
radioChange(e) {
console.log(e.detail.value)
}
}
}
</script>slider 滑動選擇器
滑動選擇器,用於選擇一個數值。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| min | Number | 0 | 最小值 |
| max | Number | 100 | 最大值 |
| step | Number | 1 | 步長,取值必須大於 0,並且可被(max - min)整除 |
| disabled | Boolean | false | 是否禁用 |
| value | Number | 0 | 目前取值 |
| show-value | Boolean | false | 是否顯示目前 value |
| activeColor | Color | #1aad19 | 已選擇的顏色 |
| backgroundColor | Color | #e9e9e9 | 背景條的顏色 |
| block-size | Number | 28 | 滑塊的大小,取值範圍為 12 - 28 |
| block-color | Color | #ffffff | 滑塊的顏色 |
範例程式碼
vue
<template>
<view>
<slider value="50" @change="sliderChange" show-value />
</view>
</template>
<script>
export default {
methods: {
sliderChange(e) {
console.log('slider value 發生變化:' + e.detail.value)
}
}
}
</script>switch 開關選擇器
開關選擇器,用於切換選中狀態。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| checked | Boolean | false | 是否選中 |
| disabled | Boolean | false | 是否禁用 |
| type | String | switch | 樣式,有效值:switch, checkbox |
| color | Color | switch 的顏色 |
範例程式碼
vue
<template>
<view>
<switch checked @change="switchChange" />
<switch type="checkbox" />
</view>
</template>
<script>
export default {
methods: {
switchChange(e) {
console.log('switch 發生 change 事件,攜帶值為', e.detail.value)
}
}
}
</script>textarea 多行輸入框
多行輸入框,用於多行文字輸入。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| value | String | 輸入框的內容 | |
| placeholder | String | 輸入框為空時佔位符 | |
| placeholder-style | String | 指定 placeholder 的樣式 | |
| disabled | Boolean | false | 是否禁用 |
| maxlength | Number | 140 | 最大輸入長度 |
| focus | Boolean | false | 取得焦點 |
| auto-height | Boolean | false | 是否自動增高 |
| fixed | Boolean | false | 如果 textarea 是在一個 position:fixed 的區域,需要顯示指定屬性 fixed 為 true |
範例程式碼
vue
<template>
<view>
<textarea placeholder="請輸入留言" v-model="message" auto-height />
</view>
</template>
<script>
export default {
data() {
return {
message: ''
}
}
}
</script>form 表單
表單容器,用於將表單元件內的使用者輸入的資料提交。
屬性說明
| 屬性名 | 類型 | 預設值 | 說明 |
|---|---|---|---|
| report-submit | Boolean | false | 是否返回 formId 用於發送範本訊息 |
範例程式碼
vue
<template>
<view>
<form @submit="formSubmit" @reset="formReset">
<view class="form-item">
<text>姓名:</text>
<input name="name" placeholder="請輸入姓名" />
</view>
<view class="form-item">
<text>年齡:</text>
<input name="age" placeholder="請輸入年齡" />
</view>
<view class="form-buttons">
<button type="primary" form-type="submit">提交</button>
<button form-type="reset">重置</button>
</view>
</form>
</view>
</template>
<script>
export default {
methods: {
formSubmit(e) {
console.log('form發生了submit事件,攜帶資料為:', e.detail.value)
},
formReset(e) {
console.log('form發生了reset事件')
}
}
}
</script>
<style>
.form-item {
display: flex;
margin-bottom: 10px;
}
.form-buttons {
display: flex;
justify-content: space-around;
margin-top: 20px;
}
</style>表單校驗
在實際應用中,表單校驗是非常重要的一環。uni-app 可以結合第三方表單校驗庫(如 async-validator)或自訂校驗邏輯來實現表單校驗。
使用 async-validator 進行表單校驗
vue
<template>
<view class="container">
<form @submit="submitForm">
<view class="form-item">
<text class="label">使用者名稱:</text>
<input v-model="formData.username" placeholder="請輸入使用者名稱" />
<text v-if="errors.username" class="error">{{errors.username[0]}}</text>
</view>
<view class="form-item">
<text class="label">信箱:</text>
<input v-model="formData.email" placeholder="請輸入信箱" />
<text v-if="errors.email" class="error">{{errors.email[0]}}</text>
</view>
<view class="form-item">
<text class="label">年齡:</text>
<input v-model="formData.age" type="number" placeholder="請輸入年齡" />
<text v-if="errors.age" class="error">{{errors.age[0]}}</text>
</view>
<button type="primary" form-type="submit">提交</button>
</form>
</view>
</template>
<script>
import Schema from 'async-validator';
export default {
data() {
return {
formData: {
username: '',
email: '',
age: ''
},
errors: {}
}
},
methods: {
submitForm() {
// 定義校驗規則
const rules = {
username: [
{ required: true, message: '請輸入使用者名稱' },
{ min: 3, max: 20, message: '使用者名稱長度在 3 到 20 個字元' }
],
email: [
{ required: true, message: '請輸入信箱' },
{ type: 'email', message: '請輸入正確的信箱格式' }
],
age: [
{ required: true, message: '請輸入年齡' },
{ type: 'number', message: '年齡必須為數字' },
{ min: 18, max: 100, message: '年齡必須在 18 到 100 之間' }
]
};
// 建立校驗器
const validator = new Schema(rules);
// 執行校驗
validator.validate(this.formData, (errors, fields) => {
if (errors) {
// 校驗失敗,顯示錯誤資訊
this.errors = fields;
} else {
// 校驗通過,清空錯誤資訊
this.errors = {};
// 提交表單資料
uni.showToast({
title: '提交成功',
icon: 'success'
});
console.log('表單資料:', this.formData);
}
});
}
}
}
</script>
<style>
.container {
padding: 20px;
}
.form-item {
margin-bottom: 20px;
}
.label {
display: block;
margin-bottom: 5px;
}
.error {
color: #f00;
font-size: 12px;
margin-top: 5px;
}
</style>進階表單功能
動態表單
在某些場景下,我們需要根據使用者的選擇動態顯示不同的表單欄位。
vue
<template>
<view class="container">
<form @submit="submitForm">
<view class="form-item">
<text class="label">使用者類型:</text>
<radio-group @change="userTypeChange">
<label>
<radio value="personal" :checked="formData.userType === 'personal'" />
個人使用者
</label>
<label>
<radio value="enterprise" :checked="formData.userType === 'enterprise'" />
企業使用者
</label>
</radio-group>
</view>
<!-- 個人使用者欄位 -->
<template v-if="formData.userType === 'personal'">
<view class="form-item">
<text class="label">真實姓名:</text>
<input v-model="formData.realName" placeholder="請輸入真實姓名" />
</view>
<view class="form-item">
<text class="label">身分證號:</text>
<input v-model="formData.idCard" placeholder="請輸入身分證號" />
</view>
</template>
<!-- 企業使用者欄位 -->
<template v-if="formData.userType === 'enterprise'">
<view class="form-item">
<text class="label">公司名稱:</text>
<input v-model="formData.companyName" placeholder="請輸入公司名稱" />
</view>
<view class="form-item">
<text class="label">統一編號:</text>
<input v-model="formData.taxNumber" placeholder="請輸入統一編號" />
</view>
<view class="form-item">
<text class="label">聯絡人:</text>
<input v-model="formData.contactPerson" placeholder="請輸入聯絡人" />
</view>
</template>
<button type="primary" form-type="submit">提交</button>
</form>
</view>
</template>
<script>
export default {
data() {
return {
formData: {
userType: 'personal',
realName: '',
idCard: '',
companyName: '',
taxNumber: '',
contactPerson: ''
}
}
},
methods: {
userTypeChange(e) {
this.formData.userType = e.detail.value
// 清空其他類型的資料
if (this.formData.userType === 'personal') {
this.formData.companyName = ''
this.formData.taxNumber = ''
this.formData.contactPerson = ''
} else {
this.formData.realName = ''
this.formData.idCard = ''
}
},
submitForm() {
console.log('提交的表單資料:', this.formData)
}
}
}
</script>表單步驟導航
對於複雜的表單,可以分步驟進行填寫,提升使用者體驗。
vue
<template>
<view class="container">
<!-- 步驟指示器 -->
<view class="steps">
<view
v-for="(step, index) in steps"
:key="index"
class="step-item"
:class="{ active: currentStep === index, completed: currentStep > index }"
>
<view class="step-number">{{ index + 1 }}</view>
<text class="step-title">{{ step.title }}</text>
</view>
</view>
<!-- 表單內容 -->
<form @submit="submitForm">
<!-- 第一步:基本資訊 -->
<view v-if="currentStep === 0" class="step-content">
<view class="form-item">
<text class="label">姓名:</text>
<input v-model="formData.name" placeholder="請輸入姓名" />
</view>
<view class="form-item">
<text class="label">手機號碼:</text>
<input v-model="formData.phone" placeholder="請輸入手機號碼" />
</view>
<view class="form-item">
<text class="label">信箱:</text>
<input v-model="formData.email" placeholder="請輸入信箱" />
</view>
</view>
<!-- 第二步:詳細資訊 -->
<view v-if="currentStep === 1" class="step-content">
<view class="form-item">
<text class="label">地址:</text>
<textarea v-model="formData.address" placeholder="請輸入詳細地址" />
</view>
<view class="form-item">
<text class="label">職業:</text>
<picker :range="occupations" @change="occupationChange">
<view class="picker">
{{ formData.occupation || '請選擇職業' }}
</view>
</picker>
</view>
</view>
<!-- 第三步:確認資訊 -->
<view v-if="currentStep === 2" class="step-content">
<view class="confirm-item">
<text class="confirm-label">姓名:</text>
<text class="confirm-value">{{ formData.name }}</text>
</view>
<view class="confirm-item">
<text class="confirm-label">手機:</text>
<text class="confirm-value">{{ formData.phone }}</text>
</view>
<view class="confirm-item">
<text class="confirm-label">信箱:</text>
<text class="confirm-value">{{ formData.email }}</text>
</view>
<view class="confirm-item">
<text class="confirm-label">地址:</text>
<text class="confirm-value">{{ formData.address }}</text>
</view>
<view class="confirm-item">
<text class="confirm-label">職業:</text>
<text class="confirm-value">{{ formData.occupation }}</text>
</view>
</view>
<!-- 操作按鈕 -->
<view class="form-actions">
<button
v-if="currentStep > 0"
@click="prevStep"
type="default"
>
上一步
</button>
<button
v-if="currentStep < steps.length - 1"
@click="nextStep"
type="primary"
>
下一步
</button>
<button
v-if="currentStep === steps.length - 1"
form-type="submit"
type="primary"
>
提交
</button>
</view>
</form>
</view>
</template>
<script>
export default {
data() {
return {
currentStep: 0,
steps: [
{ title: '基本資訊' },
{ title: '詳細資訊' },
{ title: '確認提交' }
],
formData: {
name: '',
phone: '',
email: '',
address: '',
occupation: ''
},
occupations: ['學生', '上班族', '自由業', '退休', '其他']
}
},
methods: {
nextStep() {
if (this.validateCurrentStep()) {
this.currentStep++
}
},
prevStep() {
this.currentStep--
},
validateCurrentStep() {
// 根據目前步驟進行校驗
switch (this.currentStep) {
case 0:
if (!this.formData.name || !this.formData.phone || !this.formData.email) {
uni.showToast({
title: '請填寫完整的基本資訊',
icon: 'none'
})
return false
}
break
case 1:
if (!this.formData.address || !this.formData.occupation) {
uni.showToast({
title: '請填寫完整的詳細資訊',
icon: 'none'
})
return false
}
break
}
return true
},
occupationChange(e) {
this.formData.occupation = this.occupations[e.detail.value]
},
submitForm() {
uni.showToast({
title: '提交成功',
icon: 'success'
})
console.log('最終提交的資料:', this.formData)
}
}
}
</script>
<style>
.steps {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
padding: 0 20px;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.step-number {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: #ddd;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 5px;
}
.step-item.active .step-number {
background-color: #42b983;
}
.step-item.completed .step-number {
background-color: #42b983;
}
.step-title {
font-size: 12px;
color: #666;
}
.step-item.active .step-title {
color: #42b983;
}
.step-content {
padding: 20px;
}
.confirm-item {
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.confirm-label {
font-weight: bold;
color: #333;
}
.confirm-value {
color: #666;
}
.form-actions {
display: flex;
justify-content: space-between;
padding: 20px;
}
.picker {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>最佳實務
- 表單資料統一管理:將表單資料統一放在 data 中管理,便於提交和校驗。
- 合理使用雙向綁定:使用 v-model 進行雙向資料綁定,簡化資料處理。
- 分組管理複雜表單:對於複雜表單,可以將其拆分為多個小元件,分別管理。
- 表單校驗前端先行:在提交到伺服器前,先在前端進行資料校驗,減輕伺服器壓力。
- 友好的錯誤提示:提供清晰、具體的錯誤提示,幫助使用者快速修正輸入。
- 適配不同平台:注意表單元件在不同平台上的表現差異,做好相容處理。
- 合理使用 placeholder:提供有意義的佔位符文字,引導使用者輸入。
- 考慮無障礙設計:為表單元素添加適當的標籤和描述,提高可存取性。
常見問題
1. 表單元件在不同平台上的差異
uni-app 的表單元件在不同平台上可能存在差異,特別是在樣式和互動方面。建議在開發時多測試,確保在各平台上都有良好的表現。
2. 鍵盤遮擋輸入框
在行動端,當使用者點擊輸入框時,鍵盤彈出可能會遮擋輸入框。可以透過調整頁面佈局或使用 adjust-position 屬性來解決。
3. 表單資料的重置
使用 form 元件的 reset 事件可以方便地重置表單資料,但需要注意的是,這只會重置表單元件的值,不會重置綁定的資料模型。如果需要同時重置資料模型,需要在 reset 事件處理函數中手動重置。
4. 表單提交時的防重複提交
為了防止使用者多次點擊提交按鈕導致重複提交,可以在提交時禁用提交按鈕,等待伺服器回應後再啟用。
vue
<template>
<view>
<form @submit="submitForm">
<!-- 表單內容 -->
<button type="primary" form-type="submit" :disabled="submitting">
{{submitting ? '提交中...' : '提交'}}
</button>
</form>
</view>
</template>
<script>
export default {
data() {
return {
submitting: false
}
},
methods: {
submitForm() {
if (this.submitting) return;
this.submitting = true;
// 模擬提交請求
setTimeout(() => {
uni.showToast({
title: '提交成功',
icon: 'success'
});
this.submitting = false;
}, 2000);
}
}
}
</script>