ContactEdit 联系人编辑 - Vant 4
✏️ ContactEdit 連絡先編集
📝 紹介
ContactEdit はあなた専用の連絡先管理アシスタントのようなものです!✨ 完全な連絡先編集機能を提供するだけでなく、新しい連絡先の追加や情報の編集をスムーズに行える親切なデジタル管家のようです。インターフェースデザインはシンプルかつ優雅で、あらゆる操作が丁寧に設計されており、連絡先管理を芸術のようなスムーズな体験として提供します!🎨
📦 導入
以下の方法でグローバルにコンポーネントを登録します。詳細な登録方法についてはコンポーネント登録を参照してください。
js
import { createApp } from'vue'; import { ContactEdit } from'vant'; const app = createApp(); app.use(ContactEdit);🎯 コードのデモ
🔧 基本用法 - 連絡先情報の完璧な編集体験
ContactEdit は機能豊富な連絡先編集インターフェースを提供します!📱 新しい友達の情報を追加する場合でも、古い友達の連絡先を更新する場合でも、優雅なインターフェースで簡単に行えます。すべての入力フィールドは丁寧に設計されており、情報の入力が効率的かつ快適になります。
html
<template>
<ContactEdit
v-model:contact-info="editingContact"
:is-edit="true"
@save="onSave"
@delete="onDelete"
/>
</template>js
import { ref } from'vue';
import { showToast } from'vant';
export default {
setup() {
const editingContact = ref({ tel: '', name: '', });
const onSave = (contactInfo) => showToast('保存');
const onDelete = (contactInfo) => showToast('削除');
return { onSave, onDelete, editingContact, };
},
};📖 API
Props
| パラメータ | 説明 | 型 | デフォルト値 |
|---|---|---|---|
| contact-info | 連絡先情報 | ContactEditInfo | {} |
| is-edit | 連絡先を編集しているかどうか | boolean | false |
| is-saving | 保存ボタンのローディングアニメーションを表示するかどうか | boolean | false |
| is-deleting | 削除ボタンのローディングアニメーションを表示するかどうか | boolean | false |
| tel-validator | 電話番号形式の検証関数 | (tel: string) => boolean | - |
| show-set-default | デフォルト連絡先バーを表示するかどうか | boolean | false |
| set-default-label | デフォルト連絡先バーのテキスト | string | - |
Events
| イベント名 | 説明 | コールバックパラメータ |
|---|---|---|
| save | 保存ボタンをクリックしたときにトリガー | content:フォームの内容 |
| delete | 削除ボタンをクリックしたときにトリガー | content:フォームの内容 |
| change-default | デフォルト連絡先かどうかを切り替えたときにトリガー | checked:デフォルトかどうか |
ContactEditInfo データ構造
| キー名 | 説明 | 型 |
|---|---|---|
| name | 連絡先の名前 | string |
| tel | 連絡先の電話番号 | string |
| isDefault | デフォルトかどうか | boolean | undefined |
型定義
コンポーネントは以下の型定義をエクスポートします:
ts
importtype { ContactEditInfo, ContactEditProps } from'vant';🎨 テーマカスタマイズ
スタイル変数
コンポーネントは以下の CSS 変数を提供し、カスタムスタイルに使用できます。使用方法については ConfigProvider コンポーネント を参照してください。
| 名前 | デフォルト値 | 説明 |
|---|---|---|
| --van-contact-edit-padding | var(--van-padding-md) | - |
| --van-contact-edit-fields-radius | var(--van-radius-md) | - |
| --van-contact-edit-buttons-padding | var(--van-padding-xl) 0 | - |
| --van-contact-edit-button-margin-bottom | var(--van-padding-sm) | - |
| --van-contact-edit-button-font-size | var(--van-font-size-lg) | - |
| --van-contact-edit-field-label-width | 4.1em | - |
🎯 ベストプラクティス
📝 フォーム検証戦略
- リアルタイム検証 - ユーザーの入力時に即座にフィードバックを提供する
- フレンドリーな通知 - 明確で理解しやすいエラーメッセージを使用する
- 重複送信防止 - ユーザーが保存ボタンを複数回クリックするのを防ぐ
- データ永続化 - 下書きを自動的に保存し、データの喪失を防ぐ
🔍 高度なフォーム検証
vue
<template>
<ContactEdit
:contact-info="contactInfo"
:is-edit="isEditMode"
:is-saving="isSaving"
:tel-validator="validatePhone"
@save="handleSave"
@delete="handleDelete"
/>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { showToast, showDialog } from 'vant'
const contactInfo = reactive({
name: '',
tel: '',
isDefault: false
})
const isSaving = ref(false)
const isEditMode = ref(false)
// 手机号验证
const validatePhone = (tel) => {
const phoneRegex = /^1[3-9]\d{9}$/
return phoneRegex.test(tel)
}
// 姓名验证
const validateName = (name) => {
if (!name.trim()) {
showToast('連絡先の名前を入力してください')
return false
}
if (name.length > 20) {
showToast('名前の長さは20文字以内にしてください')
return false
}
return true
}
// 保存联系人
const handleSave = async (formData) => {
// 表单验证
if (!validateName(formData.name)) return
if (!validatePhone(formData.tel)) {
showToast('正しい電話番号を入力してください')
return
}
isSaving.value = true
try {
await saveContactAPI(formData)
showToast('保存成功')
// 返回上一页或刷新列表
router.back()
} catch (error) {
showToast('保存失敗、再試行してください')
} finally {
isSaving.value = false
}
}
// 删除联系人
const handleDelete = async (formData) => {
const result = await showDialog({
title: '削除の確認',
message: '削除後は復元できません。この連絡先を削除してもよろしいですか?',
confirmButtonText: '削除',
confirmButtonColor: '#ee0a24'
})
if (result === 'confirm') {
try {
await deleteContactAPI(formData.id)
showToast('削除成功')
router.back()
} catch (error) {
showToast('削除失敗、再試行してください')
}
}
}
</script>💾 データ永続化のソリューション
javascript
// 下書きを自動保存
const draftKey = 'contact-edit-draft'
// 下書きをローカルストレージに保存
const saveDraft = (formData) => {
localStorage.setItem(draftKey, JSON.stringify(formData))
}
// 下書きデータを復元
const restoreDraft = () => {
const draft = localStorage.getItem(draftKey)
if (draft) {
return JSON.parse(draft)
}
return null
}
// 下書きをクリア
const clearDraft = () => {
localStorage.removeItem(draftKey)
}
// フォームの変更を監視して自動で下書き保存
watch(contactInfo, (newValue) => {
if (newValue.name || newValue.tel) {
saveDraft(newValue)
}
}, { deep: true })
// 页面加载时恢复草稿
onMounted(() => {
const draft = restoreDraft()
if (draft && !isEditMode.value) {
Object.assign(contactInfo, draft)
showToast('前回編集した内容を復元しました')
}
})💡 使用テクニック
🎨 カスタムフォームレイアウト
vue
<template>
<div class="custom-contact-edit">
<ContactEdit
:contact-info="contactInfo"
:show-set-default="showDefaultOption"
:set-default-label="defaultLabel"
@save="handleSave"
class="enhanced-form"
>
<!-- スロットを使用して追加フィールドをカスタマイズできます -->
<template #extra-fields>
<Field
v-model="contactInfo.email"
label="メール"
placeholder="メールアドレスを入力してください"
type="email"
/>
<Field
v-model="contactInfo.company"
label="会社"
placeholder="会社名を入力してください"
/>
</template>
</ContactEdit>
</div>
</template>
<style>
.enhanced-form {
--van-contact-edit-padding: 20px;
--van-contact-edit-fields-radius: 12px;
}
.custom-contact-edit {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
</style>📱 モバイル最適化
vue
<template>
<div class="mobile-contact-edit">
<!-- 顶部导航栏 -->
<NavBar
:title="isEditMode ? '連絡先を編集' : '連絡先を追加'"
left-arrow
@click-left="handleBack"
>
<template #right>
<Button
type="primary"
size="small"
:loading="isSaving"
@click="quickSave"
>
保存
</Button>
</template>
</NavBar>
<!-- 联系人头像 -->
<div class="avatar-section">
<Uploader
v-model="avatarList"
:max-count="1"
:preview-size="80"
upload-text="アバターをアップロード"
/>
</div>
<!-- 表单内容 -->
<ContactEdit
:contact-info="contactInfo"
:is-saving="isSaving"
@save="handleSave"
@delete="handleDelete"
/>
</div>
</template>
<script setup>
// 处理返回操作
const handleBack = () => {
if (hasUnsavedChanges.value) {
showDialog({
title: '離れる確認',
message: '保存されていない変更があります。離れてもよろしいですか?',
confirmButtonText: '離れる',
cancelButtonText: '編集を続ける'
}).then(() => {
router.back()
}).catch(() => {
// 用户选择继续编辑
})
} else {
router.back()
}
}
// クイック保存
const quickSave = () => {
// 触发表单保存
handleSave(contactInfo)
}
</script>🔄 一括操作のサポート
vue
<template>
<div class="batch-contact-edit">
<div class="batch-header">
<Checkbox v-model="selectAll" @change="handleSelectAll">
全選択 ({{ selectedContacts.length }}/{{ contacts.length }})
</Checkbox>
<div class="batch-actions" v-if="selectedContacts.length > 0">
<Button size="small" @click="batchDelete">
一括削除
</Button>
<Button size="small" type="primary" @click="batchExport">
一括エクスポート
</Button>
</div>
</div>
<div class="contact-list">
<div
v-for="contact in contacts"
:key="contact.id"
class="contact-item"
>
<Checkbox
:model-value="selectedContacts.includes(contact.id)"
@update:model-value="toggleSelect(contact.id)"
/>
<ContactEdit
:contact-info="contact"
is-edit
@save="updateContact"
@delete="deleteContact"
/>
</div>
</div>
</div>
</template>
<script setup>
const contacts = ref([])
const selectedContacts = ref([])
const selectAll = ref(false)
// 切换选择状态
const toggleSelect = (contactId) => {
const index = selectedContacts.value.indexOf(contactId)
if (index > -1) {
selectedContacts.value.splice(index, 1)
} else {
selectedContacts.value.push(contactId)
}
}
// 全选/取消全选
const handleSelectAll = (checked) => {
if (checked) {
selectedContacts.value = contacts.value.map(c => c.id)
} else {
selectedContacts.value = []
}
}
// 批量删除
const batchDelete = async () => {
const result = await showDialog({
title: '一括削除',
message: `選択された ${selectedContacts.value.length} 人の連絡先を削除してもよろしいですか?`
})
if (result === 'confirm') {
try {
await batchDeleteAPI(selectedContacts.value)
showToast('削除成功')
// 刷新列表
loadContacts()
selectedContacts.value = []
} catch (error) {
showToast('削除失敗')
}
}
}
</script>❓ よくある問題の解決策
🔧 フォーム検証が機能しない?
問題:カスタム検証関数が呼び出されていない 解決策:
tel-validator属性が正しくバインドされていることを確認する- 検証関数はブール値を返す必要がある
- 関数が正しいスコープ内にあるかどうかを確認する
📱 モバイルキーボードの重なり問題
問題:ソフトキーボードが表示されたときに入力フィールドが隠れる 解決策:
css
/* ビューポートの適応を追加 */
.van-contact-edit {
padding-bottom: env(keyboard-inset-height, 0);
}
/* 或使用 JavaScript 动态调整 */
const adjustForKeyboard = () => {
const viewport = window.visualViewport
if (viewport) {
document.documentElement.style.setProperty(
'--keyboard-height',
`${window.innerHeight - viewport.height}px`
)
}
}💾 データ保存失敗の処理
問題:ネットワーク異常による保存失敗 解決策:
javascript
const saveWithRetry = async (data, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
await saveContactAPI(data)
return true
} catch (error) {
if (i === maxRetries - 1) {
// 最後の再試行に失敗したためローカル保存
saveDraft(data)
showToast('ネットワーク異常、下書きとして保存しました')
return false
}
// 等待一段时间后重试
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
}
}
}🎨 デザインのインスピレーション
🌈 テーマスタイルのカスタマイズ
ビジネススタイル - プロフェッショナルでシンプル
css--van-contact-edit-padding: 24px; --van-contact-edit-fields-radius: 8px; --van-contact-edit-button-font-size: 16px;ファッションスタイル - モダンで活発
css--van-contact-edit-padding: 20px; --van-contact-edit-fields-radius: 16px; --van-contact-edit-button-font-size: 18px;ミニマリストスタイル - 清潔で優雅
css--van-contact-edit-padding: 32px; --van-contact-edit-fields-radius: 4px; --van-contact-edit-button-font-size: 14px;
🎯 インタラクション体験の最適化
- スマート入力 - 入力内容に基づいて自動的にフォーマットする
- クイック操作 - キーボードショートカットをサポート
- ジェスチャーサポート - スワイプで編集モードを切り替え
- 音声入力 - 音声認識機能を統合
📚 関連文書
- ContactCard 連絡先カード - 連絡先カード表示コンポーネント
- Contact 連絡先 - 連絡先選択コンポーネント
- Field 入力フィールド - フォーム入力コンポーネント
- Button ボタン - ボタンコンポーネント
🔗 関連リンク
- フォームデザインのベストプラクティス - UX Design フォームデザインガイド
- モバイルフォームの最適化 - Google 開発者ドキュメント
- アクセシビリティフォームデザイン - WebAIM アクセシビリティガイド
- Vue 3 フォーム処理 - Vue 3 公式ドキュメント