Vue3+typescript完成一个动画列表排序

本案例会一部一步的完成 vue3+typescript 的代码。首先看下需求:

界面图中: 点击排序按钮,下面的List会重新排序。

步骤1、先创建一个新的项目。

npm create vite@latest vue-typescript -- --template vue-ts

  cd vue-typescript   && npm i && npm run dev,出现下面画面,说明搭建成功了。

修改app.vue

<template>
  <div class="app">
    <p>{{ name }}</p>
    <button @click="changeName('abc')">change name</button>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  setup() {},
  name: "App",
  data() {
    return {
      name: "Link",
      age: 25,
    };
  },
  methods: {
    changeName(name:string) {
      this.name = name;
    },
  },
});
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

 点击按钮后可以正常修改name. 

 添加components/JobsList.vue

<template>
  <div class="job-list">
    <ul>
      <li class="job" v-for="job in jobs" :key="job.id">
        <h2>{{ job.title }} in {{ job.location }}</h2>
        <div class="salary">
          <p>{{ job.salary }} rupees</p>
        </div>
        <div class="description">
          <p>
            其另一大卖点是搭载HarmonyOS
            2系统,在系统流畅度上比EMUI有了全面的提升,使系统在运行时实现处处顺滑,点击、滑动及其它复杂操作都顺滑跟手不易卡顿。
          </p>
        </div>
      </li>
    </ul>
  </div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
import Job from "../types/Job";

export default defineComponent({
  props: {
    jobs: {
      required: true,
      type: Array as PropType<Job[]>,
    },
  },
});
</script>



<style lang="scss" scoped> 

.job-list {
  max-width: 960px;
  margin: 40px auto;
  ul{padding: 0;}
  li{
    list-style-type: none;
    background: white;
    padding: 16px;
    margin: 16px 0;
    border-radius: 4px;

  }
  h2{
    margin: 0 0 10px;
    text-transform: capitalize;

  }
  .salary{
    display: flex;
    img{width:30px}
    p{
      color: #17bf66;
      font-weight: bold;
      margin: 10px 4px;
      
    }
  }

}
 
</style>

src/types/job.ts

interface Job {
  title: string;
  location: string;
  salary: number;
  id: string;
}

export default Job 

修改app.ts

<template>
  <div class="app">
    <JobList :jobs="jobs" />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import JobList from "./components/JobsList.vue";
import Job from "./types/Job";

export default defineComponent({
  name: "App",
  components: { JobList },
  setup() {
    // const  age = ref<number|string>(32)

    const jobs = ref<Job[]>([
      { title: "farm worker", location: "lon lon dr", id: "1", salary: 3000 },
      { title: "quarryman", location: "kon lon dr", id: "2", salary: 6000 },
      { title: "flute player", location: "ton lon dr", id: "3", salary: 3200 },
      { title: "fisherman", location: "don lon dr", id: "4", salary: 4000 },
      { title: "prison guard", location: "son lon dr", id: "5", salary: 8000 },
    ]);
    return { jobs };
  },
});
</script>

结果显示

添加types/OrderTerm.ts

type OrderTerm='location'|'title'|'salary'
export default OrderTerm

 修改App.vue

<template>
  <div class="app">
    <header>
      <div class="title">
        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ba633cb8=""><path fill="currentColor" d="M599.872 203.776a189.44 189.44 0 0 1 64.384-4.672l2.624.128c31.168 1.024 51.2 4.096 79.488 16.32 37.632 16.128 74.496 45.056 111.488 89.344 96.384 115.264 82.752 372.8-34.752 521.728-7.68 9.728-32 41.6-30.72 39.936a426.624 426.624 0 0 1-30.08 35.776c-31.232 32.576-65.28 49.216-110.08 50.048-31.36.64-53.568-5.312-84.288-18.752l-6.528-2.88c-20.992-9.216-30.592-11.904-47.296-11.904-18.112 0-28.608 2.88-51.136 12.672l-6.464 2.816c-28.416 12.224-48.32 18.048-76.16 19.2-74.112 2.752-116.928-38.08-180.672-132.16-96.64-142.08-132.608-349.312-55.04-486.4 46.272-81.92 129.92-133.632 220.672-135.04 32.832-.576 60.288 6.848 99.648 22.72 27.136 10.88 34.752 13.76 37.376 14.272 16.256-20.16 27.776-36.992 34.56-50.24 13.568-26.304 27.2-59.968 40.704-100.8a32 32 0 1 1 60.8 20.224c-12.608 37.888-25.408 70.4-38.528 97.664zm-51.52 78.08c-14.528 17.792-31.808 37.376-51.904 58.816a32 32 0 1 1-46.72-43.776l12.288-13.248c-28.032-11.2-61.248-26.688-95.68-26.112-70.4 1.088-135.296 41.6-171.648 105.792C121.6 492.608 176 684.16 247.296 788.992c34.816 51.328 76.352 108.992 130.944 106.944 52.48-2.112 72.32-34.688 135.872-34.688 63.552 0 81.28 34.688 136.96 33.536 56.448-1.088 75.776-39.04 126.848-103.872 107.904-136.768 107.904-362.752 35.776-449.088-72.192-86.272-124.672-84.096-151.68-85.12-41.472-4.288-81.6 12.544-113.664 25.152z"></path></svg>
        <h1>列表例子</h1>
      </div>
      <div class="order">
        <button @click="handleClick('title')">order by title</button>
        <button @click="handleClick('salary')">order by salary</button>
        <button @click="handleClick('location')">order by location</button>
      </div>
    </header>
    <JobList :jobs="jobs" :order="order" />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import JobList from "./components/JobsList.vue";
import Job from "./types/Job";
import OrderTerm from "./types/OrderTerm";

export default defineComponent({
  name: "App",
  components: { JobList },
  setup() {
    // const  age = ref<number|string>(32)

    const jobs = ref<Job[]>([
      { title: "farm worker", location: "lon lon dr", id: "1", salary: 3000 },
      { title: "quarryman", location: "kon lon dr", id: "2", salary: 6000 },
      { title: "flute player", location: "ton lon dr", id: "3", salary: 3200 },
      { title: "fisherman", location: "don lon dr", id: "4", salary: 4000 },
      { title: "prison guard", location: "son lon dr", id: "5", salary: 8000 },
    ]);

    const order = ref<OrderTerm>("title");
    const handleClick = (term: OrderTerm) => {
      order.value = term;
    };
    return { jobs, order, handleClick };
  },
});
</script>

<style lang="scss" scoped>
header {
  text-align: center;
  .order {
    margin-top: 20px;
  }
  .title{
    display: flex;
    justify-content: center;

  }
  svg{
    width: 60px;
    margin-right: 20px;

  }
  h1{font-size: 3em;}

}
button {
  margin: 0 10px;
  color: aqua;
  border: 3px solid rgb(43, 177, 201);
  background: white;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
}
</style>

 修改JobsList.vue

<p>order by {{ order }}</p>
export default defineComponent({
  props: {
    jobs: {
      required: true,
      type: Array as PropType<Job[]>,
    },
    order: {
      required: true,
      type: String as PropType<OrderTerm>,
    },
  },
});

 

 修改 JobsList.vue

<template>
  <div class="job-list">
    <p>order by {{ order }}</p>
    <transition-group name="list" tag="ul">
      <li class="job" v-for="job in orderedJobs" :key="job.id">
        <h2>{{ job.title }} in {{ job.location }}</h2>
        <div class="salary">
          <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ba633cb8=""><path fill="currentColor" d="M513.28 448a64 64 0 1 1 76.544 49.728A96 96 0 0 0 768 448h64a160 160 0 0 1-320 0h1.28zm-126.976-29.696a256 256 0 1 0 43.52-180.48A256 256 0 0 1 832 448h-64a192 192 0 0 0-381.696-29.696zm105.664 249.472L285.696 874.048a96 96 0 0 1-135.68-135.744l206.208-206.272a320 320 0 1 1 135.744 135.744zm-54.464-36.032a321.92 321.92 0 0 1-45.248-45.248L195.2 783.552a32 32 0 1 0 45.248 45.248l197.056-197.12z"></path></svg>
          <p>{{ job.salary }} rupees</p>
        </div>
        <div class="description">
          <p>
            其另一大卖点是搭载HarmonyOS
            2系统,在系统流畅度上比EMUI有了全面的提升,使系统在运行时实现处处顺滑,点击、滑动及其它复杂操作都顺滑跟手不易卡顿。
          </p>
        </div>
      </li>
    </transition-group>
  </div>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from "vue";
import Job from "../types/Job";
import OrderTerm from "../types/OrderTerm";

export default defineComponent({
  props: {
    jobs: {
      required: true,
      type: Array as PropType<Job[]>,
    },
    order: {
      required: true,
      type: String as PropType<OrderTerm>,
    },
  },
  setup(props) {
    const orderedJobs = computed(() => {
      return [...props.jobs].sort((a: Job, b: Job) => {
        return a[props.order] > b[props.order] ? 1 : -1;
      });
    });
    return {orderedJobs}
  },
});
</script>

<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;

.job-list {
  max-width: 960px;
  margin: 40px auto;
  ul {
    padding: 0;
  }
  li {
    list-style-type: none;
    background: white;
    padding: 16px;
    margin: 16px 0;
    border-radius: 4px;
  }
  h2 {
    margin: 0 0 10px;
    text-transform: capitalize;
  }
  .salary {
    display: flex;
    svg {
      width: 30px;
    }
    p {
      color: #17bf66;
      font-weight: bold;
      margin: 10px 4px;
    }
  }
}
.list-move{
  transition: all 0.5s;
}
.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  overflow: hidden;

  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    margin: 0 auto;
    overflow: hidden;
  }

  .tips {
    font-size: 14px;
    color: #fff;
    margin-bottom: 10px;

    span {
      &:first-of-type {
        margin-right: 16px;
      }
    }
  }

  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    vertical-align: middle;
    width: 30px;
    display: inline-block;
  }

  .title-container {
    position: relative;

    .title {
      font-size: 26px;
      color: $light_gray;
      margin: 0px auto 40px auto;
      text-align: center;
      font-weight: bold;
    }
  }

  .show-pwd {
    position: absolute;
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    cursor: pointer;
    user-select: none;
  }
}
</style>

 来看下效果

 在实践本案例过程中的问题请留言。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东宇科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值