65.9K
CodeProject 正在变化。 阅读更多。
Home

如何使用 Vue.js 创建甘特图应用

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2020 年 2 月 27 日

GPL3

6分钟阅读

viewsIcon

21997

downloadIcon

237

本分步教程演示了如何使用 Vue.js 创建一个简单的甘特图应用。

引言

甘特图作为一种高效的项目管理工具,在建筑、物流、金融、医疗保健等众多行业中越来越受欢迎。市场上现在有许多软件供应商提供的现成解决方案。然而,为自己的目的构建自己的甘特图应用程序并不复杂。在本文中,我想向您展示如何通过两个组件一步一步地创建一个基本的甘特图应用:Vue.jsdhtmlxGantt

背景

Vue.js 是一个开源的、渐进式的 JavaScript 框架,用于构建用户界面和单页应用程序。它的主要优点之一是框架的体积非常小。它具有简单的结构,因此即使是初学者开发者也很容易使用。此外,Vue.js 由于响应式的双向数据绑定,可以实现应用程序的实时更新。您可以在此处找到一些 Vue 组件的示例。

dhtmlxGantt 是一个开源的 JS 甘特图,可用于创建简单的 Web 应用程序并为其添加许多附加功能,如反向规划、导出到 PDF、PNG、Excel 或 MS Project、内联编辑、键盘导航等等。所有功能都可以在此处或 GitHub 存储库中找到。

必备组件

请确保您的计算机上已安装最新稳定版本的 Node.js 和 Vue.js。您可以通过在终端中输入命令 node -v 来验证您的 Node.js 版本。为了我们的目的,我们需要至少 v8.12。您可以从其网站下载最新的 Node.js 版本。如果您还没有 Vue.js,请参阅本文

在开始创建应用程序之前,我们还应该安装 Vue 命令行界面(Vue CLI)。可以通过两种方式安装:

//Via NPM: 
npm install -g @vue/cli

//Or Yarn: 
yarn global upgrade --latest @vue/cli
yarn global add @vue/cli

创建甘特图

现在让我们开始为我们的甘特图应用程序创建一个项目。在您的终端中输入以下命令:

vue create gantt-vue

系统会要求您选择一个预设(babel 或 eslint)以及用于安装依赖项(Yarn 或 NPM)的包管理器。通过按 Enter 键回答每个问题,您可以选择默认选项。

如果项目创建成功,几秒钟后您将看到如下屏幕:

现在让我们在本地运行我们的应用程序。选择您的工作目录:

 cd gantt-vue

并安装依赖项(这里我使用 yarn):

yarn install

yarn serve

如果您使用 npm,则需要调用以下命令:

npm install

npm run dev

干得好!应用程序正在 https://:8080 上运行。

将 dhtmlxGantt 添加到您的项目中

此时,我们将把 dhtmlxGantt 添加到我们的应用程序中。首先,我们需要在命令行中按 Ctrl+C 停止应用程序。接下来,运行以下命令:

yarn add dhtmlx-gantt --save (for yarn)

npm install dhtmlx-gantt --save (for npm)

然后我们将进入app目录并在此处创建一个包含甘特图文件的文件夹(src/components/Gantt.vue):

在新建的Gantt.vue文件中,我们将添加以下代码:

{{ src/components/Gantt.vue }}
<template>
  <div ref="gantt"></div>
</template>

<script>
import {gantt} from 'dhtmlx-gantt';
export default {
  name: 'gantt',
  props: {
    tasks: {
      type: Object,
      default () {
        return {data: [], links: []}
      }
    }
  },


  mounted: function () {
    gantt.config.xml_date = "%Y-%m-%d";
    gantt.init(this.$refs.gantt);
    gantt.parse(this.$props.tasks);
  }
}
</script>

<style>
    @import "~dhtmlx-gantt/codebase/dhtmlxgantt.css";
</style>

太棒了!甘特图组件现在已准备就绪。当该元素添加到页面时,它将在 gantt ref 下初始化甘特图。然后,甘特图将从 tasks 属性加载数据。

现在,我们需要将甘特图添加到我们的 Vue.js 应用程序中。打开App.vue文件并将其中的代码替换为以下几行:

{{ src/App.vue }}

<template>
  <div class="container">
    <gantt class="left-container" :tasks="tasks"></gantt>
  </div>
</template>

<script>
import Gantt from './components/Gantt.vue';

export default {
  name: 'app',
  components: {Gantt},
  data () {
    return {
      tasks: {
        data: [
          {id: 1, text: 'Task #1', start_date: '2020-01-17', duration: 3, progress: 0.6},
          {id: 2, text: 'Task #2', start_date: '2020-01-20', duration: 3, progress: 0.4}
        ],
        links: [
          {id: 1, source: 1, target: 2, type: '0'}
        ]
      },
    }
  }
}
</script>


<style>
  html, body {
    height: 100%;
    margin: 0;
    padding: 0;
  }

  .container {
    height: 100%;
    width: 100%;
  }

  .left-container {
    overflow: hidden;
    position: relative;
    height: 100%;
  }
</style>

运行 yarn serve 重新加载页面。这是我们带有预定义任务的甘特图:

实时更改

所有真实的应用程序都需要跟踪用户操作并响应最终用户所做的更改。让我们通过一个简单的变更日志示例来实现此功能。我们将创建一个整洁的列表,用于记录我们对甘特图所做的所有更改。

让我们回到我们甘特图组件的文件。在这里,我们需要添加代码来跟踪 dhtmlxGantt 所做的更改并发出事件。

首先,我们需要创建一个 DataProcessor 实例,其中包含一个自定义路由器对象,该路由器是一个函数,然后调用 $emit 方法将 DataProcessor 事件传递给父组件。

让我们找到gantt.init方法,并在其正下方添加以下代码:

gantt.createDataProcessor((entity, action, data, id) => {
      this.$emit(`${entity}-updated`, id, action, data);
   });

这样,我们就可以为添加、删除和更新甘特链接和任务的 DHTMLX Gantt 事件附加处理程序。当调用处理程序时,将触发带有相应参数的Vue.js事件。

其次,我们必须在App.vue文件中添加这些事件监听器,并准备一个新的 div 容器,我们将在其中显示用户操作日志。

所以,让我们将以下代码添加到App.vue文件中:

{{ src/App.vue }}
<script>
import Gantt from './components/Gantt.vue';

export default {
  name: 'app',
  components: {Gantt},
  data () {
    return {
      tasks: {
        data: [
          {id: 1, text: 'Task #1', start_date: '2020-01-17', duration: 3, progress: 0.6},
          {id: 2, text: 'Task #2', start_date: '2020-01-20', duration: 3, progress: 0.4}
        ],
        links: [
          {id: 1, source: 1, target: 2, type: '0'}
        ]
      },
      messages: []
    }
  },
  methods: {
    addMessage (message) {
      this.messages.unshift(message)
      if (this.messages.length > 40) {
        this.messages.pop()
      }
    },

    logTaskUpdate (id, mode, task) {
      let text = (task && task.text ? ` (${task.text})`: '')
      let message = `Task ${mode}: ${id} ${text}`
      this.addMessage(message)
    },

    logLinkUpdate (id, mode, link) {
      let message = `Link ${mode}: ${id}`
      if (link) {
        message += ` ( source: ${link.source}, target: ${link.target} )`
      }
      this.addMessage(message)
    }
  }
}
</script> 

<style>
  html, body {
    height: 100%;
    margin: 0;
    padding: 0;
  }
  .container {
    height: 100%;
    width: 100%;
  }
  .left-container {
    overflow: hidden;
    position: relative;
    height: 100%;
  }
  .right-container {
    border-right: 1px solid #cecece;
    float: right;
    height: 100%;
    width: 340px;
    box-shadow: 0 0 5px 2px #aaa;
    position: relative;
    z-index:2;
  }
  .gantt-messages {
    list-style-type: none;
    height: 50%;
    margin: 0;
    overflow-x: hidden;
    overflow-y: auto;
    padding-left: 5px;
  }
  .gantt-messages > .gantt-message {
    background-color: #f4f4f4;
    box-shadow:inset 5px 0 #d69000;
    font-family: Geneva, Arial, Helvetica, sans-serif;
    font-size: 14px;
    margin: 5px 0;
    padding: 8px 0 8px 10px;
  }
</style>

借助此代码,我们通过 messages 属性定义了将存储日志条目的位置。我们还指定了新的方法来在变更日志的顶部显示最新消息,并为对任务(logTaskUpdate)和链接(logLinkUpdate)执行的操作创建日志消息,并将它们添加到消息堆栈(addMessage)。此外,我们在这里设置了变更日志的样式。

我们需要做的最后一件事是指定应用程序的模板,以便在左侧显示带有更新任务和链接的甘特图,在右侧显示变更日志。

{{ src/App.vue }}

<template>
  <div class="container">
    <div class="right-container">
      <ul class="gantt-messages">
        <li class="gantt-message" v-for="(message, index) in messages" 
            v-bind:key="index">{{message}}</li>
      </ul>
    </div>
    <gantt class="left-container" :tasks="tasks" @task-updated="logTaskUpdate" 
           @link-updated="logLinkUpdate"></gantt>
  </div>
</template>

现在是时候检查一切是否按预期工作了。尝试沿着时间线移动任务 1。结果,变更日志将显示以下消息:

让我们在Gantt.vue文件中 gantt.createDataProcessor 的正下方添加以下代码行:

{{ src/components/Gantt.vue }}      

gantt.attachEvent('onTaskSelected', (id) => {
      let task = gantt.getTask(id);
      this.$emit('task-selected', task);
});

    gantt.attachEvent('onTaskIdChange', (id, new_id) => {
       if (gantt.getSelectedId() == new_id) {
         let task = gantt.getTask(new_id);
         this.$emit('task-selected', task);
        }
     });

当用户选择一个任务时,我们可以捕获 DHTMLX Gantt 的API 事件,因为“onTaskSelected”事件会触发,然后我们将其 $emitVue.js组件。当用户选择另一个任务时,会触发“onTaskIdChange”事件,因为任务 ID 已更改,这使我们能够跟踪新选择的任务。

现在我们打开App.vue文件并为选定的任务添加一个处理程序:

{{ src/App.vue }}
<template>
  <div class="container">
    <div class="right-container">
      <div class="gantt-selected-info">
        <div v-if="selectedTask">
          <h2>{{selectedTask.text}}</h2>
          <span><b>ID: </b>{{selectedTask.id}}</span><br/>
          <span><b>Progress: </b>{{selectedTask.progress|toPercent}}%</span><br/>
          <span><b>Start Date: </b>{{selectedTask.start_date|niceDate}}</span><br/>
          <span><b>End Date: </b>{{selectedTask.end_date|niceDate}}</span><br/>
        </div>

        <div v-else class="select-task-prompt">
          <h2>Click any task</h2>
        </div>
      </div>

      <ul class="gantt-messages">
        <li class="gantt-message" v-for="message in messages" v-bind:key="index">
                                  {{message}}</li>
      </ul>
    </div>
    <gantt class="left-container" :tasks="tasks" @task-updated="logTaskUpdate" 
           @link-updated="logLinkUpdate" @task-selected="selectTask"></gantt>
  </div>
</template>

不要忘记在App.vue文件中指定样式,以便按照您需要的方式显示所选任务的信息:

<style>
.gantt-selected-info {

    border-bottom: 1px solid #cecece;
    box-sizing: border-box;
    font-family: Geneva, Arial, Helvetica, sans-serif;
    height: 50%;
    line-height: 28px;
    padding: 10px;
  }

  .gantt-selected-info h2 {
    border-bottom: 1px solid #cecece;
  }

  .select-task-prompt h2{
    color: #d9d9d9;
  }

</style>

因此,在这里,我们使用“v-if”指令和一个用于我们现在发出的“task-selected”事件的处理程序,添加了一个绑定到App.vue组件的 selectedTask 属性的新容器。现在,我们需要在App.vue组件中,在 messages 属性之后添加以下属性:

{{ src/App.vue }}   
   selectedTask: null

此外,在其他方法中,我们需要指定上面添加的选择处理程序中使用的 selectTask 方法:

 {{ src/App.vue }}    
    selectTask: function(task){
      this.selectedTask = task
    }

结果是,当用户在我们的甘特图中选择一个任务时,甘特组件会发出“task-selected”事件。然后,该事件被App.vue组件捕获。selectedTask 属性被更新,并触发 .gantt-selected-info 元素的重绘,其中包含任务详细信息。

最后,让我们让所选任务的信息看起来更美观易读。

我们可以应用 toPercentniceDate 过滤器,以人类可读的方式显示进度完成情况以及开始和结束日期:

{{ src/App.vue }}  

filters: {
    toPercent (val) {
      if(!val) return '0'
      return Math.round((+val) * 100)
    },
    niceDate (obj){
      return '${obj.getFullYear()} / ${obj.getMonth()} / ${obj.getDate()}'
    }
  }

这就是最终结果!尝试拖动任务的进度条或延长任务,您应该会看到右侧的进度百分比和结束日期如何变化。

结论

Vue.js 使几乎任何技能水平的开发人员都可以在几小时内创建 Web 应用程序。dhtmlxGantt 开源库可用于轻松地将精美的甘特图嵌入到基于 Vue 的应用程序中。因此,您可以快速开发一个基本的项目管理应用程序,该应用程序能够监听用户操作并以您需要的方式做出响应。

许可证

本文以及任何相关的源代码和文件均根据 GNU 通用公共许可证 (GPLv3) 获得许可。

© . All rights reserved.