views -> Home.vue

<template>
  <div class="home">
    <div>
      <todo-item
        v-for="(item, index) in list"
        :length="list.length"
        :key=" `todo_${index}` "
        :item="item"
        :index="index"
        :editting-index="edittingIndex"
        @edit="handleEdit"
        @cancel="handleCancel"
        @save="handleSave"
        @complete="handleComplete"
        @on-add="add"
      ></todo-item>
    </div>

    <!-- <a-button @click="add">增加</a-button> -->
    <a-button @click="goto">跳转</a-button>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { State, Mutation } from "vuex-class";
import TodoItem from "../components/todo-item";

import { Item } from "../types/vue-ts-todo-item";

@Component({
  name: "Home",
  components: {
    TodoItem,
  },
})
export default class Home extends Vue {
  @State("todoList") private list!: Array<Item>;
  @Mutation("updateTodoList") public updateList!: (
    index: number,
    content: string
  ) => void;
  @Mutation("todoItemComplete") public handleComplete!: (index: number) => void;
  @Mutation("addTodoList") public handleAdd!: (item: Item) => void;
  public edittingIndex = -1;

  public goto(): void {
    this.$router.push({
      path: "/about",
    });
  }

  public handleEdit(callIndex: number) {
    this.edittingIndex = callIndex;
  }

  public handleCancel() {
    this.edittingIndex = -1;
  }

  public handleSave(index: number, content: string) {
    this.updateList(index, content);
    this.handleCancel();
  }

  public add(item: Item) {
    if(!item.text.trim()){
      alert("请输入内容");
      return false;
    }
    this.handleAdd(item);
  }
}
</script>

views -> About.vue

<template>
  <div class="about">
    <p
      v-for="(item, index) in list"
      :key=" `todo_item_${index}` "
      :style="{textDecoration: item.complete ? 'line-through' : ''}">{{ item.text }}</p>
  </div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { State, Mutation } from "vuex-class";

import { Item } from '../types/vue-ts-todo-item';

@Component({
  name: "About",
})
export default class About extends Vue {
  @State("todoList") private list!: Array<Item>;
}
</script>

<style lang="scss"></style>

components -> todo-item.tsx

import { Component, Vue, Prop, Emit, Watch } from 'vue-property-decorator'
import { Item } from '../types/vue-ts-todo-item';

@Component({
    name: 'TodoItem'
})
export default class ToDoItem extends Vue {
    @Prop(Object) public item!: Item;
    @Prop(Number) public index!: number;
    @Prop(Number) public edittingIndex!: number;
    @Prop(Number) public length!: number;

    public edittingContent = '';

    @Watch("edittingIndex")
    public edittingChange(index: number) {

        if (index === this.index) {
            this.edittingContent = this.item.text;
        } else {
            this.edittingContent = '';
        }
    }

    @Emit()
    public edit(): number {
        return this.index;
    }

    @Emit()
    public cancel() {
        return
    }

    @Emit()
    public save(index: number, content: string): object {
        return {
            index,
            content
        }
    }

    @Emit()
    public complete(): number {
        return this.index
    }

    @Emit()
    public onAdd(content: string): Item {
        return {
            text: content.replace(/\s*/g, ""),
            complete: false
        }
    }

    protected render() {
        return (
            <div>
                {
                    this.edittingIndex == this.index
                        ?
                        (<div>
                            <a-input v-model={this.edittingContent} style='width: 200px;'></a-input>
                            <a-icon type='check' on-click={this.save.bind(this, this.index, this.edittingContent)}></a-icon>
                            <a-icon type='close' on-click={this.cancel}></a-icon>
                        </div>)
                        :
                        (<div>
                            <span on-click={this.complete} style={this.item.complete ? { textDecoration: 'line-through' } : {}}>{this.item.text}</span>
                            <a-icon type='edit' on-click={this.edit}></a-icon>
                        </div>)
                }

                <div v-show={this.length == (this.index + 1)}>
                    <a-input v-model={this.edittingContent} style='width: 200px;'></a-input>
                    <span type='check' on-click={this.onAdd.bind(this, this.edittingContent)}>添加</span>
                </div>
            </div>
        )
    }
}

types -> vue-ts-todo-item.d.ts

export interface Item {
    text: string;
    complete: boolean;
}

store -> index.ts

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    todoList: [
      {
        text: "ts测试111111",
        complete: false,
      },
      {
        text: "ts测试222222",
        complete: false,
      },
    ],
  },
  mutations: {
    addTodoList(state, item) {
      state.todoList.push(item);
    },
    updateTodoList(state, { index, content }) {
      state.todoList[index].text = content;
    },
    todoItemComplete(state, index) {
      state.todoList[index].complete = !state.todoList[index].complete;
    },
  },
  actions: {},
  modules: {},
});

笨蛋、笨蛋!