Vue3+TS+Vant3——增删改input和通过双页面进行增删改操作
两种方案:
第一种点击添加按钮添加一项,缺点:页面过于臃肿,用户体验较差
第二种:分成两种页面进行添加等操作
先说一下第一种,我这里用到了vant3Ui组件库
直接上代码:
<!-- aaa --> <template> <van-sticky> <header-slot> <template #denglu> <van-nav-bar title="aaa" left-arrow @click-left="()=>{router.back();}"> </van-nav-bar> </template> </header-slot> </van-sticky> <van-form @submit="onSubmit"> <van-cell-group inset> <div v-for="(item,index) in formData.arr" :key="index" class="box1"> <div class="borders"></div> <van-button type="danger" v-if="index!=0" size="mini" class="delete" @click="deleteRow(index)">删除</van-button> <div class=""> <van-field v-model="item.aaa" :name="'aaa'+index" placeholder="aaa" /> <van-field v-model="item.bbb" :name="'bbb'+index" placeholder="bbb" /> <van-field v-model="item.ccc" :name="'ccc'+index" placeholder="ccc" /> </div> </div> <div class="borders"></div> <van-button type="primary" size="small" class="button" @click="butclick2">添加一项</van-button> <div style="margin: 16px;" class="submit"> <van-button round block type="primary" native-type="submit" color="#FCD39F" :loading="status.submit"> 提交 </van-button> </div> </van-form> </template> <script lang='ts' setup> import { ref, onMounted, reactive, toRefs, computed } from "vue"; import { useRouter } from "vue-router"; import { Notify, Toast, Button } from 'vant'; const defaultData = { arr: [ { appellation: "", getName: "", workplace: "", office_code: "", }, ], count: 0, }; const butclick = () => { let obj={ appellation: "", getName: "", workplace: "", office_code: "", } formData.value.arr.push(obj); /* 方法二 */ /* let arrs: any = []; arrs.push({ appellation: "", getName: "", workplace: "", office_code: "", }); formData.value.arr.push(arrs); console.log(formData.value); formData.value.count++; console.log(formData.value.count); */ }; /* 删除 */ const deleteRow = (index: any) => { console.log(index); formData.value.arr.splice(index, 1); }; const onConfirm = (value: any) => { formData.value.highest = value; showPicker.value = false; }; const formData = ref<typeof defaultData>(Object.assign({}, defaultData)); const status = reactive({ loading: true, submit: false, }); onMounted(() => { let sub: any = window.localStorage.getItem("onSubmit"); if (sub !== null) { console.log( formData.value.explain=JSON.parse(sub).explain); console.log( formData.value.arr=JSON.parse(sub).arr); } }); const onSubmit = (values: any) => { console.log("submit", values = formData.value); window.localStorage.setItem("onSubmit", JSON.stringify(values)); }; const router = useRouter(); </script> <style scoped lang='less'> </style>
新增节点
const butclick = () => { let obj={ appellation: "", getName: "", workplace: "", office_code: "", } formData.value.arr.push(obj); /* 方法二 */ /* let arrs: any = []; arrs.push({ appellation: "", getName: "", workplace: "", office_code: "", }); formData.value.arr.push(arrs); console.log(formData.value); formData.value.count++; console.log(formData.value.count); */ };
其中obj里的对象名称要跟reactive中创建这个的数组里的名称一样
Vue3是通过v-model生成新的节点,无需用克隆节点生成新的Dom
这里因为不需要进入页面,所以直接引入接口就可以了,无需写编辑事件
删除:获取对应选中的div盒子,之后对盒子进行删除操作
const deleteRow = (index: any) => { console.log(index); formData.value.arr.splice(index, 1); };
第二种方法
<!-- 教育背景 --> <template> <van-sticky> <header-slot> <template #denglu> <van-nav-bar title="教育背景" left-arrow @click-left="()=>{router.back();}"> </van-nav-bar> </template> </header-slot> </van-sticky> <van-form @submit="onSubmit" input-align="right"> <van-cell-group inset> <h1>教育背景(高中起填)</h1> <van-cell-group> <div v-if="jiaoyu instanceof Array || formData.educational.length ==0"> <van-cell value="尚未填写" /> </div> <div v-if="jiaoyu.educational"> <!-- formData.educational --> <div v-for="(item,index) in jiaoyu.educational" :key="index"> <van-swipe-cell> <van-cell :title="item.school" @click="editoriaClick(index)" is-link :value="item.time" :label="item.major" /> <template #right> <van-button square type="danger" text="删除" @click="delectClick(index)" /> </template> </van-swipe-cell> </div> </div> </van-cell-group> <!-- <van-divider :style="{ color: '#B0A8B9', borderColor: '#B0A8B9'}"/> --> <van-button type="primary" size="small" class="button" @click="btbclickAdd()">{{jiaoyu instanceof Array?'点我进行添加':'添加'}}</van-button> <van-divider :style="{ color: '#B0A8B9', borderColor: '#B0A8B9'}" /> <!-- 最高学历绩点 --> <van-field v-model="formData.harp" name="harp" label="最高学历绩点" placeholder="最高学历绩点" /> <!-- 成绩排名 --> <van-field v-model="formData.classRank" is-link readonly name="classRank" label="成绩排名" placeholder="点击选择成绩排名" @click="status.showPicker3 = true;" /> <van-popup v-model:show="status.showPicker3" position="bottom"> <van-picker :columns="columns3" @confirm="onConfirm4" @cancel="status.showPicker3 = false" /> </van-popup> <!-- 证明人/联系方式 --> <van-field v-model="formData.references" name="references" label="证明人/联系方式" placeholder="证明人/联系方式" /> <!-- 次高学历绩点 --> <van-divider :style="{ color: '#B0A8B9', borderColor: '#B0A8B9'}" /> <!-- 次高学历绩点 --> <van-field v-model="formData.mostHarp" name="mostHarp" label="次高学历绩点" placeholder="次高学历绩点" /> <!-- 成绩排名 --> <van-field v-model="formData.mostClassRank" is-link readonly name="mostClassRank" label="成绩排名" placeholder="点击选择成绩排名" @click="status.showPicker4 = true;" /> <van-popup v-model:show="status.showPicker4" position="bottom"> <van-picker :columns="columns4" @confirm="onConfirm5" @cancel="status.showPicker4 = false" /> </van-popup> <!-- 证明人/联系方式 --> <van-field v-model="formData.mostReferences" name="mostReferences" label="证明人/联系方式" placeholder="证明人/联系方式" /> </van-cell-group> <div style="margin: 16px;" class="submit"> <van-button round block type="primary" color="#FCD39F" native-type="submit"> 提交 </van-button> </div> </van-form> </template> <script lang='ts' setup> import { ref, onMounted, reactive, toRefs, computed } from "vue"; import { useRouter } from "vue-router"; import { Notify, Toast } from "vant"; import { type } from "os"; import path from "path/posix"; const defaultData = { educational: [], // 最高学历绩点 harp: "", // 最高成绩排名 classRank: "", // 最高证明人/联系方式 references: "", // 次高学历绩点 mostHarp: "", // 次高成绩排名 mostClassRank: "", // 次高证明人/联系方式 mostReferences: "", }; const currIndex = ref("0"); const num = ref(0); const num2 = ref(0); const num3 = ref(0); const columns = ["高中", "大专", "本科", "硕士", "博士"]; const columns2 = ["全日制", "非全日制"]; const columns3 = ["前10%", "前30%", "平均水平", "平均水平以下"]; const columns4 = ["前10%", "前30%", "平均水平", "平均水平以下"]; const formData = ref<typeof defaultData>(Object.assign({}, defaultData)); const status = reactive({ loading: true, submit: false, showCalendar: false, showPicker: false, showPicker2: false, showPicker3: false, showPicker4: false, }); /* 自定义区间 */ /* 成绩排名 */ const onConfirm4 = (value: any) => { formData.value.classRank = value; status.showPicker3 = false; }; const onConfirm5 = (value: any) => { formData.value.mostClassRank = value; status.showPicker4 = false; }; const propertiesClick = (index: any) => { status.showPicker2 = true; num3.value = index; }; if (window.localStorage.getItem("jiaoyubeijing") != null) { } // @ts-ignore let jiaoyu: any = // @ts-ignore JSON.parse(window.localStorage.getItem("jiaoyubeijing")) || []; onMounted(() => { console.log("jiaoyu", jiaoyu); if (jiaoyu instanceof Array) { console.log("我现在是个数组"); console.log(jiaoyu instanceof Array); jiaoyu = formData.value; window.localStorage.setItem("jiaoyubeijing", JSON.stringify(jiaoyu)); } else { formData.value = jiaoyu; console.log(formData.value); } /* let tem = formData.value as any; for (let i in formData.value) { if (res.data[i]) { tem[i] = res.data[i]; } } formData.value = tem; */ }); // 编辑 const editoriaClick = (index: any) => { // console.log( jiaoyu.educational[index]); window.localStorage.setItem( "indexValue", JSON.stringify(jiaoyu.educational[index]) ); router.push({ path: "/index/Education/Educational", query: { str: "编辑", index: index }, }); }; /* 删除 */ const delectClick = (index: any) => { // jiaoyu.educational.splice(index, 1); console.log(index); console.log(formData.value.educational.splice(index, 1)); window.localStorage.setItem("jiaoyubeijing", JSON.stringify(jiaoyu)); console.log(formData.value); }; /* 添加 */ const btbclickAdd = () => { router.push({ path: "/index/Education/Educational", query: { str: "添加" } }); }; const deleteRow = (index: any) => { formData.value.educational.splice(index, 1); }; const onSubmit = (values: any) => { values = formData.value; console.log(values); window.localStorage.setItem("jiaoyubeijing", JSON.stringify(values)); }; const router = useRouter(); </script> <style scoped lang='less'> .box1 { position: relative; } :deep(.van-nav-bar) { background-color: #fef0e3; } .submit :deep(.van-button__text) { color: #7c4e36; } .button { display: block; // text-align: center; // line-height: 26px; background: #f60; border: #f60; color: #fff; margin: 10px auto; cursor: pointer; font-size: 12px; border-radius: 13px; } h1 { font-size: 16px; padding: 10px; } .borders { width: 100%; height: 1px; transform: scaleY(0.5); transform-origin: 50% 100%; background: #000; margin-top: -1px; } .delete { position: absolute; top: 0; right: 0; z-index: 2; } </style>
下面是详情页
<template> <van-sticky> <header-slot> <template #denglu> <van-nav-bar title="教育背景详情" left-arrow @click-left='()=>{router.back();}'></van-nav-bar> </template> </header-slot> </van-sticky> <van-form @submit="onSubmit" input-align="right"> <van-cell-group inset> <van-divider :style="{ borderColor: '#B0A8B9'}" content-position="left">教育背景(高中起填)</van-divider> <div class="box1" v-for="(item,index) in formData.educational" :key="index"> <!-- 时间 --> <van-field v-model="item.time" is-link readonly name="time" label="时间" placeholder="点击选择日期" @click="times(index)" /> <van-calendar color="#1989fa" v-model:show="status.showCalendar" type="range" @confirm="onConfirm" /> <!-- 学历 --> <van-field v-model="item.education" is-link readonly name="education" label="学历" placeholder="点击选择学历" @click="educationClick(index)" /> <van-popup v-model:show="status.showPicker" position="bottom"> <van-picker :columns="columns" @confirm="onConfirm2" :default-index="0" @cancel="status.showPicker = false" /> </van-popup> <!-- 教学性质 --> <van-field v-model="item.properties" is-link readonly name="properties" label="教育性质" placeholder="点击选择教育性质" @click="propertiesClick(index)" /> <van-popup v-model:show="status.showPicker2" position="bottom"> <van-picker :columns="columns2" @confirm="onConfirm3" :default-index="0" @cancel="status.showPicker2 = false" /> </van-popup> <!-- 导师 --> <van-field v-model="item.tutor" name="tutor" label="导师" placeholder="导师" /> <!-- 学校 --> <van-field v-model="item.school" name="school" label="学校" placeholder="学校" /> <!-- 专业 --> <van-field v-model="item.major" name="major" label="专业" placeholder="专业" /> </div> </van-cell-group> <div style="margin: 16px;" class="submit"> <van-button round block type="primary" color="#FCD39F" native-type="submit"> {{route.query.str == '编辑'?'编辑':"保存"}} </van-button> </div> <div style="margin: 16px;" class="submit"> <van-button round block type="primary" color="#EC002B" @click="delectClick"> 清空所有内容 </van-button> </div> </van-form> </template> <script lang='ts' setup> import { ref, onMounted, reactive } from "vue"; import { useRouter, useRoute } from "vue-router"; import { Notify, Toast } from "vant"; const defaultData = { educational: [ { // 时间 time: "", // 学历 education: "", // 教学性质 properties: "", // 导师 tutor: "", // 学校 school: "", // 专业 major: "", }, ], // 最高学历绩点 harp: "", // 最高成绩排名 classRank: "", // 最高证明人/联系方式 references: "", // 次高学历绩点 mostHarp: "", // 次高成绩排名 mostClassRank: "", // 次高证明人/联系方式 mostReferences: "", }; const formData = ref<typeof defaultData>(Object.assign({}, defaultData)); const columns = ["高中", "大专", "本科", "硕士", "博士"]; const columns2 = ["全日制", "非全日制"]; const status = reactive({ loading: true, submit: false, showCalendar: false, showPicker: false, showPicker2: false, showPicker3: false, showPicker4: false, }); const num = ref(0); const num2 = ref(0); const num3 = ref(0); /* 自定义区间 */ const formatDate = (date: any) => `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; /* 调用方法 */ const onConfirm = (date: any) => { const [start, end] = date; status.showCalendar = false; formData.value.educational[num.value].time = `${formatDate( start )} - ${formatDate(end)}`; console.log(date); }; /* 时间 */ const times = (index: any) => { status.showCalendar = true; console.log(index); num.value = index; }; /* 学历 */ const onConfirm2 = (value: any) => { formData.value.educational[num2.value].education = value; status.showPicker = false; }; /* 学历 */ const educationClick = (index: any) => { status.showPicker = true; console.log(index); num2.value = index; }; /* 教学性质 */ const onConfirm3 = (value: any) => { formData.value.educational[num3.value].properties = value; status.showPicker2 = false; }; const propertiesClick = (index: any) => { status.showPicker2 = true; num3.value = index; }; /* const btbclickAdd = () => { let obj = { time: "", education: "", properties: "", tutor: "", school: "", major: "", }; formData.value.educational.push(obj); console.log(formData.value.educational); }; */ let jybg: any = window.localStorage.getItem("JYBG"); let jiaoyubeijing: any = window.localStorage.getItem("jiaoyubeijing"); jybg = JSON.parse(jybg); jiaoyubeijing = JSON.parse(jiaoyubeijing); onMounted(() => { console.log(); if (route.query.str == "编辑") { let indexValue = window.localStorage.getItem("indexValue") || []; console.log(indexValue); if (indexValue instanceof Array) { return; } else { formData.value.educational[0] = JSON.parse(indexValue); } } else { } }); /* 删除 */ const delectClick = () => { let tem = formData.value.educational[0] as any; for (let i in formData.value.educational[0]) { tem[i] = ""; } formData.value.educational[0] = tem; }; /* 提交 */ const onSubmit = (values: any) => { console.log(values); if (route.query.str == "编辑") { Toast("编辑成功!"); router.back(); // => item.id === obj.id ? obj : item jiaoyubeijing.educational[Number(route.query.index)] = values; window.localStorage.setItem("jiaoyubeijing", JSON.stringify(jiaoyubeijing)); window.localStorage.removeItem("indexValue"); } else { Toast("添加成功!"); router.back(); window.localStorage.removeItem("indexValue"); if (window.localStorage.getItem("indexValue") == null) { if (jiaoyubeijing == null) { jiaoyubeijing = {}; } jiaoyubeijing.educational.push(values); console.log(jiaoyubeijing); window.localStorage.setItem( "jiaoyubeijing", JSON.stringify(jiaoyubeijing) ); window.localStorage.setItem("JYBG", JSON.stringify(values)); } } // router.back(); }; const router = useRouter(); const route = useRoute(); </script> <style scoped lang='less'> h1 { font-size: 18px; padding: 10px; } </style>
这个是把数据存到本地上了,真实项目需要通过接口去传递数据,原理是一样的