地址选择与验证后编辑
完整的 Vue SFC,使用 AddressList、Form、Field、ConfigProvider 和受控草稿。
<script setup>
import {ref,reactive,computed,nextTick} from 'vue';
import {ConfigProvider,AddressList,Button,Form,Field,CellGroup,Tag,Switch,NoticeBar} from 'vant';
const list=ref([{id:'studio',name:'Alex Morgan',tel:'+44 20 7946 0101',address:'Studio 4, 18 Paper Street, London',isDefault:true},{id:'workshop',name:'Jules Chen',tel:'+44 20 7946 0122',address:'Workshop B, 52 Clay Road, London'}]);
const disabledList=[{id:'archive',name:'Sam Rivera',tel:'+44 20 7946 0133',address:'Old warehouse, 8 Mill Lane, London'}];
const chosen=ref('studio'),editing=ref(false),editingId=ref(null),notice=ref(''),dark=ref(false),formRef=ref();
const draft=reactive({name:'',tel:'',address:''});
const selected=computed(()=>list.value.find(x=>x.id===chosen.value));
const themeVars={primaryColor:'#176b77',addressListPadding:'12px',buttonPrimaryBackground:'#176b77',buttonPrimaryBorderColor:'#176b77'};
async function edit(item){editingId.value=item?.id||null;Object.assign(draft,item?{name:item.name,tel:item.tel,address:item.address}:{name:'',tel:'',address:''});editing.value=true;notice.value='';await nextTick();formRef.value?.resetValidation();document.querySelector('[name="recipient"]')?.focus();}
function save(){const value={id:editingId.value||`local-${Date.now()}`,name:draft.name.trim(),tel:draft.tel.trim(),address:draft.address.trim()};if(editingId.value)list.value=list.value.map(x=>x.id===editingId.value?{...x,...value}:x);else list.value.push(value);chosen.value=value.id;editing.value=false;notice.value='Address saved in this local preview.';}
function confirm(){notice.value=`Selected ${selected.value.name} at ${selected.value.address}. No delivery was booked.`;}
const nonempty=v=>v.trim().length>=2;
const phone=v=>/^\+?[\d ()-]{7,22}$/.test(v.trim());
</script>
<template><ConfigProvider theme-vars-scope="global" :theme="dark?'dark':'light'" :theme-vars="themeVars"><main class="delivery" :class="{dark}"><header><div><p class="eyebrow">VANT · MOBILE COMMERCE</p><h1>A destination for your sample kit.</h1><p class="intro">Choose a studio, update a recipient or add a local address.</p></div><label class="mode"><span>Dark</span><Switch v-model="dark" size="22px" aria-label="Dark theme" /></label></header><div class="columns"><section class="address-panel" aria-label="Delivery addresses"><div class="section-heading"><h2>Delivery address</h2><Tag plain type="primary">{{list.length}} available</Tag></div><AddressList v-model="chosen" :list="list" :disabled-list="disabledList" disabled-text="Outside the sample delivery area" default-tag-text="Default" :show-add-button="false" @edit="edit" @select="notice=''" /><div class="actions"><Button plain block type="primary" @click="edit()">Add a local address</Button></div></section><aside><section class="kit"><div class="paper-samples" aria-hidden="true"><i></i><i></i><i></i></div><p class="eyebrow">MATERIAL LIBRARY</p><h2>Paper & pigment kit</h2><p>Six paper stocks. Three print finishes. One place to compare them.</p><dl><div><dt>Recipient</dt><dd>{{selected?.name}}</dd></div><div><dt>Destination</dt><dd>{{selected?.address}}</dd></div></dl><Button block type="primary" @click="confirm">Use this address</Button><p class="local-note">Selection only. Nothing is purchased or shipped.</p></section></aside></div><section v-if="editing" class="editor"><h2>{{editingId?'Edit recipient':'Add an address'}}</h2><Form ref="formRef" validate-trigger="onSubmit" label-align="top" scroll-to-error @submit="save"><CellGroup inset><Field v-model="draft.name" name="recipient" label="Recipient" placeholder="Full name" :rules="[{validator:nonempty,message:'Enter at least two characters.'}]" /><Field v-model="draft.tel" name="telephone" type="tel" label="Telephone" placeholder="+44 20 7946 0100" :rules="[{validator:phone,message:'Use 7–22 characters: digits, spaces, +, brackets or hyphens.'}]" /><Field v-model="draft.address" name="address" type="textarea" rows="2" autosize label="Address" placeholder="Building, street and city" :rules="[{validator:v=>v.trim().length>=10,message:'Enter a complete address of at least ten characters.'}]" /></CellGroup><div class="form-actions"><Button type="primary" native-type="submit">Save locally</Button><Button native-type="button" @click="editing=false">Cancel</Button></div></Form></section><NoticeBar v-if="notice" wrapable :scrollable="false" color="var(--van-primary-color)" background="var(--van-background-2)" class="result" role="status">{{notice}}</NoticeBar></main></ConfigProvider></template>
- 选中值是地址 ID,而不是数组中的位置。
- 隐藏固定定位的 AddressList 添加按钮;使用文档流中的 Button 打开编辑器。
- 只有在 Field 验证器通过后,才会提交表单。


