Vue3+TS+Vant3——增删改input和通过双页面进行增删改操作

简介: 增删改input和通过双页面进行增删改操作

Vue3+TS+Vant3——增删改input和通过双页面进行增删改操作

两种方案:

第一种点击添加按钮添加一项,缺点:页面过于臃肿,用户体验较差

3da19757effd4f25b1231e92085f955a.gif

第二种:分成两种页面进行添加等操作

1793b56f046448ec8f22ce18bc410557.gif

先说一下第一种,我这里用到了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中创建这个的数组里的名称一样

bc1ea77afefd425997f78a8a6ff380d0.png

090953650183497f9348b447bc461d09.png

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>

这个是把数据存到本地上了,真实项目需要通过接口去传递数据,原理是一样的


相关文章
|
15天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
34 7
|
16天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
38 3
|
15天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
36 1
|
15天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
36 1
|
17天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
5天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
5天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
5天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
5天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
4天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉