Isomorphic JavaScript with MEVN stack,第五部分------Task模块的CRUD

Connecting Vue.js to Backend Server 

01 引入bootstrap-vue

参考: bootstrap-vue.org

npm install vue bootstrap bootstrap-vue
  • 修改src/main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "bootstrap/dist/css/bootstrap.css";
import "./assets/css/style.css";

import "bootstrap";
import BootstrapVue from "bootstrap-vue";
import moment from "moment";

Vue.use(BootstrapVue);

// Vue.config.productionTip = false;
Vue.config.productionTip = process.env.NODE_ENV === "production";

Vue.filter("date", (value) => {
  if (!value) {
    return "";
  }

  return moment(value).format("YYYY-MM-DD");
});

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount("#app");

02 创建中间层Service

  • 增加src/services/TaskService.js
import http from "./HttpService";

export function getAllTasks() {
  return http().get("/task");
}

export function getTaskById(id) {
  return http().get(`/task/${id}`);
}

export function createTask(task) {
  return http().post("/task", task);
}

export function deleteTask(id) {
  return http().delete(`/task/${id}`);
}

export function updateTask(task) {
  return http().put("/task", task);
}

03 创建View层页面

  • 增加/src/views/tasks/TasksCreate.vue
<template>
  <div>
    <h1>Create Task</h1>
    <form class="custom-form" v-on:submit.prevent="onSubmit">
      <div class="form-group">
        <label for="title">Title</label>
        <input
          v-model="task.title"
          type="text"
          class="form-control"
          id="title"
          name="title"
          placeholder="Title"
        />
      </div>
      <div class="form-group">
        <label for="body">Body</label>
        <textarea
          placeholder="Body"
          class="form-control"
          v-model="task.body"
          name="body"
          id="body"
          cols="30"
          rows="10"
        ></textarea>
      </div>
      <div class="form-group">
        <label for="due-date">Due Date</label>
        <input
          name="due-date"
          v-model="task.dueDate"
          type="date"
          class="form-control"
          id="due-date"
          placeholder="Due Date"
        />
      </div>
      <div class="form-group">
        <button type="submit" class="btn btn-secondary">Create</button>
      </div>
    </form>
  </div>
</template>

<script>
import * as taskService from "../../services/TaskService";

export default {
  name: "tasks-create",
  data: function () {
    return {
      task: {
        title: "",
        body: "",
        dueDate: "",
      },
    };
  },
  methods: {
    onSubmit: async function () {
      const request = {
        task: this.task,
      };
      await taskService.createTask(request);
      this.$router.push({ name: "tasks-all" });
    },
  },
};
</script>
  • 增加/src/views/tasks/TasksAll.vue
<template>
  <div class="d-flex flex-column">
    <h1>Tasks</h1>

    <div class="mb-4">
      <router-link to="/tasks/new" class="btn btn-success ml-2" exact
        >Create Task</router-link
      >
    </div>

    <div
      v-if="tasks && tasks.length > 0"
      class="d-flex flex-wrap justify-content-start"
    >
      <div
        v-for="task in tasks"
        v-bind:key="task._id"
        class="card mb-2 ml-2 text-white bg-dark"
        style="width: 18rem"
      >
        <div class="card-body">
          <div class="d-flex justify-content-between">
            <h5 class="card-title">{{ task.title }}</h5>
            <span
              v-bind:class="{
                late: taskIsLate(task.dueDate) && !task.completed,
              }"
              class="small"
              >{{ task.dueDate | date }}</span
            >
          </div>

          <h6 class="card-subtitle mb-2 text-muted">
            Created by {{ task.author.username }}
          </h6>

          <p class="card-text">{{ task.body }}</p>

          <div
            v-if="task.author._id === $store.state.userId"
            class="form-group form-check"
          >
            <input
              type="checkbox"
              class="form-check-input"
              :disabled="task.completed"
              v-model="task.completed"
              v-on:click="markAsCompleted(task)"
            />
            <label for="form-check-label">Completed</label>
          </div>

          <div
            v-if="task.author._id === $store.state.userId"
            class="d-flex justify-content-between"
          >
            <router-link
              type="button"
              tag="button"
              class="card-link btn btn-primary"
              :to="{ name: 'tasks-edit', params: { id: task._id } }"
              exact
              >Edit</router-link
            >
            <a
              v-on:click.prevent="currentTaskId = task._id"
              class="card-link btn btn-danger"
              href="#"
              v-b-modal.modal1
              >Delete</a
            >
          </div>
        </div>
      </div>

      <div>
        <b-modal id="modal1" ref="modal" centered title="Delete Confirmation">
          <p class="my-4">Are you sure you would like to delete this task?</p>
          <div slot="modal-footer" class="w-100 text-right">
            <b-btn size="md" class="mr-1" variant="danger" @click="deleteTask"
              >Delete</b-btn
            >
            <b-btn size="md" variant="secondary" @click="cancelModal"
              >Cancel</b-btn
            >
          </div>
          <!-- <template #modal-footer>
            <div class="w-100 text-right">
              <p class="float-left">Modal Footer Content</p>
              <b-button
                variant="danger"
                size="md"
                class="mr-1"
                @click="deleteTask"
              >
                Delete
              </b-button>
              <b-button
                variant="secondary"
                size="md"
                class="mr-1"
                @click="cancelModal"
              >
                Cancel
              </b-button>
            </div>
          </template> -->
        </b-modal>
      </div>
    </div>

    <div v-if="tasks && tasks.length === 0" class="ml-2">
      <div class="alert alert-info">No tasks found.</div>
    </div>
  </div>
</template>

<script>
import * as taskService from "../../services/TaskService";
import moment from "moment";

export default {
  name: "tasks-all",
  data: function () {
    return {
      tasks: null,
      currentTaskId: null,
    };
  },
  beforeRouteEnter(to, from, next) {
    taskService.getAllTasks().then((res) => {
      next((vm) => {
        vm.tasks = res.data.tasks;
      });
    });
  },
  methods: {
    taskIsLate: function (date) {
      return moment(date).isBefore();
    },
    cancelModal: function () {
      this.$refs.modal.hide();
      this.currentTaskId = null;
    },
    deleteTask: async function () {
      this.$refs.modal.hide();
      await taskService.deleteTask(this.currentTaskId);
      const index = this.tasks.findIndex((task) => {
        task._id === this.currentTaskId;
      });
      this.tasks.splice(index, 1);
      this.currentTaskId = null;
    },
    markAsCompleted: function (task) {
      task.completed = true;
      const request = {
        task: task,
      };
      taskService.updateTask(request);
    },
  },
};
</script>

参考:bootstrap 的 FlexspacingcardModal, Botton

  • 修改style.css,增加
.modal-title,
.modal-body p,
.modal-footer p{
    color:#000 !important;
}

.late{
    color:#dc3545;
}
  • 修改TasksEdit.vue
<template>
  <div>
    <h1>Edit Task</h1>
    <form class="custom-form" v-on:submit.prevent="onSubmit">
      <div class="form-group">
        <label for="title">Title</label>
        <input
          v-model="task.title"
          type="text"
          class="form-control"
          id="title"
          name="title"
          placeholder="Title"
        />
      </div>
      <div class="form-group">
        <label for="body">Body</label>
        <textarea
          placeholder="Body"
          class="form-control"
          v-model="task.body"
          name="body"
          id="body"
          cols="30"
          rows="10"
        ></textarea>
      </div>
      <div class="form-group">
        <label for="due-date">Due Date</label>
        <input
          name="due-date"
          v-model="task.dueDate"
          type="date"
          class="form-control"
          id="due-date"
          placeholder="Due Date"
        />
      </div>
      <div class="form-group">
        <button type="submit" class="btn btn-secondary">Save Changes</button>
      </div>
    </form>
  </div>
</template>

<script>
import * as taskService from "../../services/TaskService";
import moment from "moment";

export default {
  name: "tasks-edit",
  data: function () {
    return {
      task: {
        title: "",
        body: "",
        dueDate: "",
      },
    };
  },
  beforeRouteEnter(to, from, next) {
    taskService.getTaskById(to.params.id).then((response) => {
      if (!response) {
        next("/tasks");
      } else {
        next((vm) => {
          const task = response.data.task;
          task.dueDate = moment(task.dueDate).format("YYYY-MM-DD");
          vm.task = task;
        });
      }
    });
  },
  methods: {
    onSubmit: async function () {
      const request = {
        task: this.task,
      };
      await taskService.updateTask(request);
      this.$router.push({ name: "tasks-all" });
    },
  },
};
</script>

04 package.json

{
  "name": "mevn-stack",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "vuebuild": "vue-cli-service build",
    "dev": "babel dev-server --out-dir prod-server --watch",
    "build": "set NODE_ENV=production && babel dev-server --out-dir prod-server && vue-cli-service build",
    "concurrently": "concurrently \"set NODE_ENV=development\" \"babel dev-server --out-dir prod-server --watch\" \"nodemon prod-server/index.js\" \"npm run serve\" ",
    "lint": "vue-cli-service lint",
    "lintfix": "eslint ./dev-server --fix && vue-cli-service lint"
  },
  "dependencies": {
    "@babel/polyfill": "^7.12.1",
    "axios": "^0.21.1",
    "bcrypt-nodejs": "0.0.3",
    "body-parser": "^1.19.0",
    "bootstrap": "^4.6.0",
    "bootstrap-vue": "^2.21.2",
    "core-js": "^3.6.5",
    "express": "^4.17.1",
    "jquery": "^3.6.0",
    "jsonwebtoken": "^8.5.1",
    "moment": "^2.29.1",
    "mongoose": "^5.12.5",
    "popper.js": "^1.16.1",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.13.16",
    "@babel/core": "^7.13.16",
    "@babel/preset-env": "^7.13.15",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "babel-eslint": "^10.1.0",
    "concurrently": "^6.0.2",
    "cors": "^2.8.5",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^6.2.2",
    "morgan": "^1.10.0",
    "prettier": "^2.2.1",
    "vue-template-compiler": "^2.6.11"
  }
}

05 项目结构

mevn-project-structure

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值