Browse Source

Merge branch 'http_mock' of git.xiaojukeji.com:jacklijiajia/thoth-frontend into http_mock

qinzhipeng_v 5 years ago
parent
commit
e4e75fd0a9

+ 9 - 2
package.json

@@ -16,14 +16,21 @@
   },
   "dependencies": {
     "@didi/omega-tracker": "^2.2.8",
-    "@fullcalendar/core": "^4.3.1",
-    "@fullcalendar/daygrid": "^4.3.0",
+    "@fullcalendar/bootstrap": "^4.4.0",
+    "@fullcalendar/core": "^4.4.0",
+    "@fullcalendar/daygrid": "^4.4.0",
+    "@fullcalendar/interaction": "^4.4.0",
+    "@fullcalendar/list": "^4.4.0",
+    "@fullcalendar/timegrid": "^4.4.0",
+    "@fullcalendar/vue": "^4.4.0",
     "animate.css": "^3.7.2",
     "axios": "0.18.0",
     "dayjs": "^1.8.17",
     "echarts": "^4.2.1",
     "element-ui": "^2.13.0",
     "file-saver": "^2.0.2",
+    "gantt-elastic": "^1.0.11",
+    "gantt-elastic-header": "^0.1.11",
     "html2canvas": "^1.0.0-rc.3",
     "jodit": "^3.2.58",
     "js-cookie": "2.2.0",

+ 13 - 0
src/api/user.js

@@ -7,3 +7,16 @@ export function getUserInfo() {
     method: 'get'
   })
 }
+
+export function getAccountInfos(data) {
+  return request({
+    url: requestIp + '/sso/account-infos',
+    method: 'get',
+    params: data
+  })
+}
+
+export default {
+  getUserInfo,
+  getAccountInfos
+}

+ 113 - 1
src/api/workbench.js

@@ -1,7 +1,119 @@
 import request from '@/utils/request'
-import { mockUrl } from '@/apiConfig/api'
+import { mockUrl, workbenchUrl } from '@/apiConfig/api'
 // ================================== Interface ======================================
 
+export default {
+  createSelfSchedule,
+  updateSelfSchedule,
+  getSelfSchedule,
+  deleteSelfSchedule,
+  queryTeamInfoList,
+  showTeamAndMemberEnum,
+  queryWorkListByTime,
+  queryWorkList,
+  queryIdleList,
+  queryTeamWorkList,
+  queryTeamIdleList
+}
+
+// 创建个人日程
+export function createSelfSchedule(data) {
+  return request({
+    url: workbenchUrl + '/selfSchedule/create',
+    method: 'post',
+    data
+  })
+}
+
+// 更新个人日程
+export function updateSelfSchedule(data) {
+  return request({
+    url: workbenchUrl + '/selfSchedule/update',
+    method: 'post',
+    data
+  })
+}
+
+// 获取个人日程
+export function getSelfSchedule(data) {
+  return request({
+    url: workbenchUrl + '/selfSchedule/get',
+    method: 'get',
+    params: data
+  })
+}
+
+// 删除个人日程
+export function deleteSelfSchedule(id) {
+  return request({
+    url: workbenchUrl + '/selfSchedule/delete?id=' + id,
+    method: 'post'
+  })
+}
+
+// 获取指定时间段用户日程信息
+export function queryWorkListByTime(data) {
+  return request({
+    url: workbenchUrl + '/workbench/personal/queryWorkListByTime',
+    method: 'post',
+    data
+  })
+}
+
+// 获取用户日程信息
+export function queryWorkList(data) {
+  return request({
+    url: workbenchUrl + '/workbench/personal/queryWorkList',
+    method: 'post',
+    data
+  })
+}
+
+// 获取个人空闲日程列表
+export function queryIdleList(data) {
+  return request({
+    url: workbenchUrl + '/workbench/personal/queryIdleList',
+    method: 'post',
+    data
+  })
+}
+
+// 获取团队用户日程信息
+export function queryTeamWorkList(data) {
+  return request({
+    url: workbenchUrl + '/workbench/personal/queryTeamWorkList',
+    method: 'post',
+    data
+  })
+}
+
+// 获取团队用户空闲信息
+export function queryTeamIdleList(data) {
+  return request({
+    url: workbenchUrl + '/workbench/personal/queryTeamIdleList',
+    method: 'post',
+    data
+  })
+}
+
+// 获取用户团队列表
+export function queryTeamInfoList(data) {
+  return request({
+    url: workbenchUrl + '/team/queryTeamInfoList',
+    method: 'post',
+    data
+  })
+}
+
+// 角色信息枚举类
+export function showTeamAndMemberEnum(data) {
+  return request({
+    url: workbenchUrl + '/config/showTeamAndMemberEnum',
+    method: 'get',
+    data
+  })
+}
+
 // 创建文件夹
 export function personalworkstationQueryBackLog(data) {
   return request({

+ 3 - 1
src/apiConfig/api.js

@@ -19,4 +19,6 @@ export const task_Url = projectManagementUrl // 任务
 
 export const Presentation = projectManagementUrl // 报告模块
 
-export const envWebUrl = requestIp + '/env-web' //环境web
+export const envWebUrl = requestIp + '/env-web' //环境web
+
+export const workbenchUrl = projectManagementUrl //工作台

+ 99 - 0
src/components/dialog/delete.vue

@@ -0,0 +1,99 @@
+<template>
+  <el-dialog class="zhihui_dialog_delete" :visible.sync="isVisible" width="32%" :close-on-click-modal="false" :append-to-body="true">
+    <template v-slot:title>
+      <div class="module_title">
+        <div class="module_title__sign" />
+        <div class="module_title__caption">{{ title }}</div>
+      </div>
+    </template>
+    <div style="line-height: 150px;text-align: center">{{ content }}</div>
+    <template v-slot:footer>
+      <el-button @click="cancel">取 消</el-button>
+      <el-button type="primary" @click="confirm">确 认</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  props: {
+    title: {
+      type: String,
+      default: '删除确认'
+    },
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    content: {
+      type: String,
+      default: '是否确认删除?'
+    }
+  },
+  data() {
+    return {
+      isVisible: false
+    }
+  },
+  watch: {
+    visible(newVal, olfVal) {
+      this.isVisible = newVal
+    },
+    isVisible(newVal, olfVal) {
+      if (!newVal) {
+        this.$emit('cancel', false)
+      }
+    }
+  },
+  methods: {
+    cancel() {
+      this.$emit('cancel', false)
+    },
+    confirm() {
+      this.$emit('confirm', true)
+    }
+  }
+}
+</script>
+
+<style>
+.zhihui_dialog_delete .el-dialog {
+  border-radius:4px;
+}
+.zhihui_dialog_delete .el-dialog__header {
+  padding: 20px 70px;
+}
+.zhihui_dialog_delete .el-dialog__body {
+  padding: 0px 70px;
+}
+.zhihui_dialog_delete .el-dialog__footer {
+  padding: 40px 70px;
+}
+.zhihui_dialog_delete .el-form-item__label {
+  color: #666666;
+  font-weight: 400;
+}
+</style>
+
+<style scoped>
+.module_title{
+  display:flex;
+  align-items: center;
+  margin-bottom: 20px
+}
+.module_title__sign {
+  width:4px;
+  height:15px;
+  background:#409EFF;
+  border-radius:1px;
+}
+.module_title__caption{
+  width:83px;
+  height:18px;
+  font-size:16px;
+  font-family:MicrosoftYaHei;
+  color:rgba(51,59,74,1);
+  margin-left:6px;
+  font-weight: 500;
+}
+</style>

+ 6 - 8
src/router/index.js

@@ -59,30 +59,28 @@ export const constantRoutes = [{
   name: '首页',
   hidden: true
 },
-
 {
-  path: '/Platform/Workbench',
+  path: '/Platform/workbench',
   name: '工作台',
   component: Layout,
-  redirect: '/Platform/Workbench/PersonalWorkbench',
+  redirect: '/Platform/workbench/person',
   meta: { title: '工作台', icon: '工作台' },
   children: [{
-    path: 'PersonalWorkbench',
+    path: 'person',
     name: '个人工作台',
     component: () =>
-          import('@/views/Platform/Workbench/PersonalWorkbench'),
+          import('@/views/workbench/person/index.vue'),
     meta: { title: '个人工作台' }
   },
   {
-    path: 'TeamWorkbench',
+    path: 'team',
     name: '团队工作台',
     component: () =>
-          import('@/views/Platform/Workbench/TeamWorkbench'),
+          import('@/views/workbench/team/index.vue'),
     meta: { title: '团队工作台' }
   }
   ]
 },
-
 {
   path: '/Platform/projectManage',
   name: '项目管理',

+ 13 - 25
src/utils/request.js

@@ -48,32 +48,20 @@ service.interceptors.response.use(
    */
   response => {
     const res = response.data
+    if (typeof res.code !== 'undefined' && res.code !== 200 && res.code !== 0) {
+      Message({
+        message: res.msg || 'Error',
+        type: 'error',
+        duration: 5 * 1000
+      })
+    } else if (typeof res.retCode !== 'undefined' && res.retCode !== 0) {
+      Message({
+        message: res.retMsg || 'Error',
+        type: 'error',
+        duration: 5 * 1000
+      })
+    }
     return res
-    // if the custom code is not 20000, it is judged as an error.
-    // if (res.code !== 200) {
-    //   // Message({
-    //   //   message: res.message || 'Error',
-    //   //   type: 'error',
-    //   //   duration: 5 * 1000
-    //   // })
-
-    //   // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
-    //   if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
-    //     // to re-login
-    //     MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
-    //       confirmButtonText: 'Re-Login',
-    //       cancelButtonText: 'Cancel',
-    //       type: 'warning'
-    //     }).then(() => {
-    //       store.dispatch('user/resetToken').then(() => {
-    //         location.reload()
-    //       })
-    //     })
-    //   }
-    //   return Promise.reject(new Error(res.message || 'Error'))
-    // } else {
-    //   return res
-    // }
   },
   error => {
     console.log('err' + error) // for debug

+ 0 - 1
src/views/projectManage/bugList/details/index.vue

@@ -529,7 +529,6 @@
             <el-select
               v-model="statusDialogForm.repairResult"
               style="width: 100%"
-              prop="repairResult"
             >
               <el-option
                 v-for="item in enums.repairResultEnumList"

+ 47 - 0
src/views/workbench/person/bigCalendarDialog.vue

@@ -0,0 +1,47 @@
+<template>
+  <el-dialog :visible.sync="isVisible">
+    <template v-slot:footer>
+      <el-button @click="cancel">取 消</el-button>
+      <el-button type="primary" @click="confirm">确 认</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    data: {
+      type: Object,
+      default() {
+        return null
+      }
+    }
+  },
+  data() {
+    return {
+      isVisible: false
+    }
+  },
+  watch: {
+    visible(newVal, olfVal) {
+      this.isVisible = newVal
+    }
+  },
+  methods: {
+    cancel() {
+      this.$emit('cancel', false)
+    },
+    confirm() {
+      this.$emit('confirm', false)
+    }
+  }
+}
+</script>
+
+<style>
+
+</style>

+ 242 - 0
src/views/workbench/person/calendarFormDialog.vue

@@ -0,0 +1,242 @@
+<template>
+  <el-dialog class="workbench_schedule__dialog" :visible.sync="isVisible" width="70%" :close-on-click-modal="false" :append-to-body="true">
+    <template v-slot:title>
+      <div class="module_title">
+        <div class="module_title__sign" />
+        <div class="module_title__caption">{{ title }}</div>
+      </div>
+    </template>
+    <el-form ref="calendarform" :model="form" :rules="rules" label-position="left" label-width="90px">
+      <el-form-item label="日程名称" prop="name">
+        <el-input v-model="form.name" />
+      </el-form-item>
+      <el-container>
+        <el-aside width="60%">
+          <el-form-item label="是否关联业务线" prop="isJoin" label-width="150px">
+            <el-radio-group v-model="form.isJoin" style="margin-bottom: -8px">
+              <el-radio :label="0">否</el-radio>
+              <el-radio :label="1">是</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="时间" prop="time">
+            <el-date-picker
+              v-model="form.time"
+              type="datetimerange"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+            />
+          </el-form-item>
+          <el-form-item prop="syncTeam">
+            <el-checkbox v-model="form.syncTeam" :true-label="1" :false-label="0">同步到团队</el-checkbox>
+          </el-form-item>
+        </el-aside>
+        <el-aside width="40%">
+          <el-form-item v-show="form.isJoin" label="业务线" prop="bizId">
+            <el-select v-model="form.bizId" style="width: 100%">
+              <el-option
+                v-for="item in businesslines"
+                :key="item.id"
+                :label="item.bizName"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item v-show="!form.isJoin" style="visibility:hidden" label="业务线" />
+          <el-form-item prop="noHoliday">
+            <el-checkbox v-model="form.noHoliday" :true-label="1" :false-label="0">排除周末</el-checkbox>
+          </el-form-item>
+        </el-aside>
+      </el-container>
+      <el-form-item label="描述">
+        <el-input
+          v-model="form.desc"
+          type="textarea"
+          placeholder="请输入描述内容"
+          maxlength="300"
+          show-word-limit
+          :autosize="{ minRows: 3, maxRows: 5}"
+        />
+      </el-form-item>
+    </el-form>
+    <template v-slot:footer>
+      <el-button @click="cancel">取 消</el-button>
+      <el-button type="primary" @click="confirm">确 认</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+import workbenchApi from '@/api/workbench.js'
+import { queryBizTypeList } from '@/api/defectManage'
+// import dayjs from 'dayjs'
+
+export default {
+  props: {
+    title: {
+      type: String,
+      default: '标题'
+    },
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    data: {
+      type: Object,
+      default() {
+        return null
+      }
+    }
+  },
+  data() {
+    return {
+      refresh: true,
+      businesslines: [],
+      form: {
+        isJoin: 0,
+        noHoliday: 1,
+        syncTeam: 1
+      },
+      isVisible: false,
+      rules: {
+        name: [
+          { required: true, message: '请输入日程名称', trigger: 'blur' }
+        ],
+        isJoin: [
+          { required: true, message: '请选择是否关联业务线', trigger: 'change' }
+        ],
+        time: [
+          { required: true, message: '请选择日程时间', trigger: 'change' }
+        ],
+        bizId: [
+          { required: true, message: '请选择业务线', trigger: 'change' }
+        ]
+      }
+    }
+  },
+  watch: {
+    visible(newVal, olfVal) {
+      this.isVisible = newVal
+    },
+    isVisible(newVal, olfVal) {
+      if (newVal) {
+        if (this.title === '编辑日程') {
+          this.form = JSON.parse(JSON.stringify(this.data))
+        } else {
+          this.form = {
+            time: [this.data.start, this.data.end],
+            isJoin: 0,
+            noHoliday: 1,
+            syncTeam: 1
+          }
+        }
+      } else {
+        this.$emit('cancel', false)
+      }
+    }
+  },
+  mounted() {
+    this.queryBizTypeList()
+  },
+  methods: {
+    cancel() {
+      this.$emit('cancel', false)
+    },
+    confirm() {
+      this.$refs.calendarform.validate((valid, object) => {
+        let bizIdValid = false
+        if (!valid && !this.form.isJoin) {
+          bizIdValid = true
+          for (const i in object) {
+            if (i !== 'bizId') {
+              bizIdValid = false
+              break
+            }
+          }
+        }
+        if (valid || bizIdValid) {
+          if (this.title === '新建日程') {
+            this.createSelfSchedule()
+              .then(res => {
+                if (res.code === 200) {
+                  this.$emit('confirm', false)
+                }
+              })
+          } else {
+            this.updateSelfSchedule()
+              .then(res => {
+                if (res.code === 200) {
+                  this.$emit('confirm', false)
+                }
+              })
+          }
+        }
+      })
+    },
+    dateFomat() {
+      this.form.startTime = this.form.time[0]
+      this.form.endTime = this.form.time[1]
+    },
+    // 新建日程
+    createSelfSchedule() {
+      this.dateFomat()
+      if (this.form.isJoin === 0) {
+        this.form.bizId = null
+      }
+      return workbenchApi.createSelfSchedule(this.form)
+    },
+    // 编辑日程
+    updateSelfSchedule() {
+      this.dateFomat()
+      return workbenchApi.updateSelfSchedule(this.form)
+    },
+    queryBizTypeList() {
+      queryBizTypeList({}).then(res => {
+        this.businesslines = res.data.list
+      })
+    }
+  }
+}
+</script>
+
+<style>
+.workbench_schedule__dialog .el-dialog {
+  border-radius:4px;
+}
+.workbench_schedule__dialog .el-dialog__header {
+  padding: 20px 70px;
+}
+.workbench_schedule__dialog .el-dialog__body {
+  padding: 0px 70px;
+}
+.workbench_schedule__dialog .el-dialog__footer {
+  padding: 40px 70px;
+}
+.workbench_schedule__dialog .el-form-item__label {
+  color: #666666;
+  font-weight: 400;
+}
+</style>
+
+<style scoped>
+.module_title{
+  display:flex;
+  align-items: center;
+  margin-bottom: 20px
+}
+.module_title__sign {
+  width:4px;
+  height:15px;
+  background:#409EFF;
+  border-radius:1px;
+}
+.module_title__caption{
+  width:83px;
+  height:18px;
+  font-size:16px;
+  font-family:MicrosoftYaHei;
+  color:rgba(51,59,74,1);
+  margin-left:6px;
+  font-weight: 500;
+}
+</style>

+ 537 - 0
src/views/workbench/person/index.vue

@@ -0,0 +1,537 @@
+<template>
+  <el-container direction="vertical" class="workbench_penson">
+    <el-main class="layout_main header" style="padding: 20px 30px">
+      <div class="realname">{{ realname }}</div>
+      <div v-if="teamInfo">
+        <div
+          v-for="(item,index) in teamInfo"
+          v-show="index < 2 || teamInfoShow"
+          :key="index"
+          class="teamInfo"
+        >{{ item.teamName + '的' + getUserRoleInTeam(username,item) }}</div>
+        <div
+          v-if="teamInfo.length>2 && !teamInfoShow"
+          style="margin-top: 20px"
+          class="more"
+          @click.native="teamInfoShow = true"
+        >更多</div>
+      </div>
+    </el-main>
+    <el-container style="margin-top: 10px" class="middle">
+      <el-aside width="32.8%" style="margin-right: 10px" class="layout_aside">
+        <div class="module_title">
+          <div class="module_title__sign" />
+          <div class="module_title__caption">我的日程</div>
+        </div>
+        <MyFullCalendar :events="calendarEvents" @dateClick="dateClick" @expand="calendarDialogVisible = true" @change="queryWorkListByTime" @select="select" @eventDrop="eventDrop" />
+      </el-aside>
+      <el-aside width="32.8%" style="margin-right: 10px" class="layout_aside">
+        <div class="module_title">
+          <div class="module_title__sign" />
+          <div class="module_title__caption">我的数据</div>
+        </div>
+        <div style="color: #9B9B9B;line-height: 300px;text-align: center">
+          敬请期待...
+        </div>
+      </el-aside>
+      <el-aside width="32.9%" class="layout_aside">
+        <div class="module_title">
+          <div class="module_title__sign" />
+          <div class="module_title__caption">提醒</div>
+        </div>
+      </el-aside>
+    </el-container>
+    <el-dialog
+      class="workbench_fullscreen_dialog"
+      :visible.sync="calendarDialogVisible"
+      :fullscreen="true"
+      style="margin-top: 80px;"
+      :modal="false"
+    >
+      <el-tabs v-model="tabsActiveName" @tab-click="handleTabsClick">
+        <el-tab-pane label="日历视图" name="1" style="padding: 31px 146px">
+          <MyFullCalendar :events="calendarEvents" type="big" @dateClick="dateClick" @change="queryWorkListByTime" @select="select" @eventDrop="eventDrop" />
+        </el-tab-pane>
+        <el-tab-pane label="列表视图" name="2" style="padding: 31px 146px">
+          <div>
+            <div class="list-view-header">
+              <button
+                style="border-radius: 4px 0 0  4px;"
+                :class="busyClassName"
+                @click="busyClassName = 'selected';idleClassName='idle'"
+              >忙碌</button>
+              <button
+                :class="idleClassName"
+                style="margin-left: -4px;border-left: none;border-radius: 0 4px 4px 0;"
+                @click="busyClassName = 'busy';idleClassName='selected';queryIdleList()"
+              >空闲</button>
+              <div v-if="idleClassName === 'selected'" style="display: inline-block">
+                <div style="display: inline-block;margin-left: 20px">选择时间:</div>
+                <el-date-picker
+                  v-if="idleClassName === 'selected'"
+                  v-model="timeSelectVal"
+                  type="daterange"
+                  range-separator="-"
+                  start-placeholder="开始日期"
+                  end-placeholder="结束日期"
+                  :picker-options="pickerOptions"
+                  :clearable="false"
+                  @change="queryIdleList()"
+                />
+              </div>
+              <div v-else style="display: inline-block">
+                <el-select
+                  v-model="selfScheduleForm.status"
+                  style="margin-left: 20px"
+                  @change="queryWorkList()"
+                >
+                  <el-option
+                    v-for="item in selfScheduleStatusEnum"
+                    :key="item.code"
+                    :label="item.name"
+                    :value="item.code"
+                  />
+                </el-select>
+                <el-checkbox
+                  v-model="selfScheduleForm.myOrigin[0]"
+                  style="margin: 0 20px"
+                  :true-label="1"
+                  :false-label="0"
+                  @change="queryWorkList()"
+                >任务排期</el-checkbox>
+                <el-checkbox
+                  v-model="selfScheduleForm.myOrigin[1]"
+                  :true-label="1"
+                  :false-label="0"
+                  @change="queryWorkList()"
+                >个人日程</el-checkbox>
+              </div>
+            </div>
+          </div>
+          <el-table
+            v-if="busyClassName === 'selected'"
+            border
+            :data="selfScheduleBusyTableData"
+            style="width: 100%;font-size: 14px; color:#333B4A;margin-top: 20px"
+            :header-cell-style="{color:'#333B4A',fontSize: '16px',fontWeight:500,background:'rgba(240,242,244,1)'}"
+          >
+            <el-table-column prop="name" label="日程名称" align="center" min-width="138" />
+            <el-table-column prop="startTime" label="日期" align="center" min-width="232">
+              <template v-slot="scope">
+                {{ scope.row.startTime + ' ~ ' + scope.row.endTime }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="originName" label="来源" align="center" min-width="126" />
+            <el-table-column prop="bizName" label="业务线" align="center" min-width="119" />
+            <el-table-column label="使用/工作日/全部" align="center" min-width="181">
+              <template v-slot="scope">
+                {{ scope.row.needDays + '/' + scope.row.legalDays + '/' + scope.row.allDays }}
+              </template>
+            </el-table-column>
+            <el-table-column label="是否同步团队日程" align="center" min-width="156">
+              <template v-slot="scope">
+                {{ scope.row.syncTeam? '是':'否' }}
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" min-width="156" align="center">
+              <template v-slot="scope">
+                <div v-if="scope.row.originName === '个人日程'">
+                  <el-button size="small" @click="openUpdateSelfScheduleDialog(scope.row)">编辑</el-button>
+                  <el-button size="small" @click="openDeleteSelfScheduleDialog(scope.row)">删除</el-button>
+                </div>
+              </template>
+            </el-table-column>
+          </el-table>
+          <el-table
+            v-else
+            border
+            :data="selfScheduleIdleTableData"
+            style="width: 100%;font-size: 14px; color:#333B4A;margin-top: 20px"
+            :header-cell-style="{color:'#333B4A',fontSize: '16px',fontWeight:500,background:'rgba(240,242,244,1)',textAlign: 'center'}"
+          >
+            <el-table-column prop="name" label="日程名称" align="center" min-width="30" />
+            <el-table-column prop="startTime" label="日期" align="center" min-width="30">
+              <template v-slot="scope">
+                {{ scope.row.startTime + ' ~ ' + scope.row.endTime }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="needDays" label="使用/工作日/全部" align="center" min-width="30">
+              <template v-slot="scope">
+                {{ scope.row.needDays + '/' + scope.row.legalDays + '/' + scope.row.allDays }}
+              </template>
+            </el-table-column>
+          </el-table>
+          <div align="right">
+            <el-pagination
+              background
+              layout="total, prev, pager, next, jumper"
+              :current-page="selfScheduleForm.curIndex"
+              :page-size="selfScheduleForm.pageSize"
+              :page-sizes="[15,30,45,selfScheduleListTotal]"
+              :total="selfScheduleListTotal"
+              @size-change="handleSizeChange"
+              @current-change="handleCurrentChange"
+            />
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </el-dialog>
+    <calendar-dialog
+      :title="'编辑日程'"
+      :visible="updateSelfScheduleDialog.visible"
+      :data="updateSelfScheduleDialog.data"
+      @cancel="updateSelfScheduleDialog.visible = false"
+      @confirm="updateSelfScheduleDialog.visible = false;queryWorkList()"
+    />
+    <calendar-dialog
+      :title="'新建日程'"
+      :visible="createSelfScheduleDialog.visible"
+      :data="createSelfScheduleDialog.data"
+      @cancel="createSelfScheduleDialog.visible = false"
+      @confirm="createSelfScheduleDialog.visible = false;queryWorkList()"
+    />
+    <delete-dialog
+      :content="'是否删除当前日程'"
+      :visible="deleteSelfScheduleDialog.visible"
+      @cancel="deleteSelfScheduleDialog.visible = false"
+      @confirm="deleteSelfSchedule()"
+    />
+  </el-container>
+</template>
+
+<script>
+import workbenchApi from '@/api/workbench.js'
+import MyFullCalendar from './myFullCalendar'
+import dayjs from 'dayjs'
+import CalendarDialog from './calendarFormDialog'
+import DeleteDialog from '@/components/dialog/delete.vue'
+
+export default {
+  components: {
+    MyFullCalendar,
+    CalendarDialog,
+    DeleteDialog
+  },
+  data() {
+    return {
+      calendarEvents: [
+        // initial event data
+        { title: 'Event Now', start: new Date(), end: new Date().setDate(16) }
+      ],
+      deleteDialogVisible: false,
+      createSelfScheduleDialog: {
+        visible: false,
+        data: null
+      },
+      updateSelfScheduleDialog: {
+        visible: false,
+        data: null
+      },
+      deleteSelfScheduleDialog: {
+        visible: false,
+        data: null
+      },
+      selfScheduleStatusEnum: [
+        { name: '进行中', code: 0 },
+        { name: '未开始', code: 1 },
+        { name: '过去的', code: 2 }
+      ],
+      selfScheduleListTotal: 10,
+      selfScheduleForm: {
+        status: 0,
+        myOrigin: [1, 1],
+        origin: [0, 1],
+        syncTeam: 1,
+        curIndex: 1,
+        pageSize: 5
+      },
+      selfScheduleBusyTableData: [],
+      selfScheduleIdleTableData: [],
+      busyClassName: 'selected',
+      idleClassName: 'idle',
+      timeSelectVal: [new Date(), dayjs(new Date()).add(29, 'day')],
+      tabsActiveName: '1',
+      teamInfoShow: false,
+      teamAndMemberEnum: {},
+      teamAndMemberMap: {},
+      teamInfo: null,
+      userInfo: {},
+      username: localStorage.getItem('username'),
+      realname: localStorage.getItem('realname'),
+      calendarDialogVisible: false,
+      pickerOptions: {
+        disabledDate(time) {
+          return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
+        }
+      }
+    }
+  },
+  created() {
+    this.init()
+  },
+  methods: {
+    init() {
+      this.showTeamAndMemberEnum()
+        .then(res => {
+          this.queryTeamInfoList(this.username)
+        })
+
+      this.queryWorkList()
+    },
+    eventDrop(info) {
+      const form = {
+        id: info.event.id,
+        startTime: info.event.start,
+        endTime: info.event.end
+      }
+      workbenchApi.updateSelfSchedule(form)
+    },
+    dateClick(arg) {
+      // this.createSelfScheduleDialog.data = arg
+      // this.createSelfScheduleDialog.visible = true
+    },
+    openUpdateSelfScheduleDialog(val) {
+      const form = JSON.parse(JSON.stringify(val))
+      form.startTime = dayjs(form.startTime).toDate()
+      form.endTime = dayjs(form.endTime).toDate()
+      form.time = [form.startTime, form.endTime]
+      if (form.bizId !== null) {
+        form.isJoin = 1
+      } else {
+        form.isJoin = 0
+      }
+      this.updateSelfScheduleDialog.data = form
+      this.updateSelfScheduleDialog.visible = true
+    },
+    openDeleteSelfScheduleDialog(val) {
+      this.deleteSelfScheduleDialog.data = val
+      this.deleteSelfScheduleDialog.visible = true
+    },
+    select(selectionInfo) {
+      this.createSelfScheduleDialog.data = selectionInfo
+      this.createSelfScheduleDialog.visible = true
+    },
+    // 删除日程
+    deleteSelfSchedule() {
+      workbenchApi.deleteSelfSchedule(this.deleteSelfScheduleDialog.data.id)
+        .then(res => {
+          if (res.code === 200) {
+            this.deleteSelfScheduleDialog.visible = false
+            this.queryWorkList()
+          }
+        })
+    },
+    // 获取个人空闲日程列表
+    queryIdleList() {
+      workbenchApi
+        .queryIdleList({
+          timeInfo: {
+            startTime: dayjs(this.timeSelectVal[0]).format('YYYY.MM.DD'),
+            endTime: dayjs(this.timeSelectVal[1]).format('YYYY.MM.DD')
+          },
+          curIndex: this.selfScheduleForm.curIndex,
+          pageSize: this.selfScheduleForm.pageSize
+        })
+        .then(res => {
+          this.selfScheduleIdleTableData = res.data.list
+          this.selfScheduleListTotal = res.data.total
+        })
+    },
+    // 获取个人工作日程列表
+    queryWorkList() {
+      if (this.selfScheduleForm.myOrigin[0] && this.selfScheduleForm.myOrigin[1]) {
+        this.selfScheduleForm.origin = [0, 1]
+      } else if (this.selfScheduleForm.myOrigin[0]) {
+        this.selfScheduleForm.origin = [0]
+      } else if (this.selfScheduleForm.myOrigin[1]) {
+        this.selfScheduleForm.origin = [1]
+      }
+      workbenchApi.queryWorkList(this.selfScheduleForm).then(res => {
+        if (res.code === 200) {
+          this.selfScheduleBusyTableData = res.data.list
+          this.selfScheduleListTotal = res.data.total
+        }
+      })
+    },
+    queryWorkListByTime(view) {
+      workbenchApi.queryWorkListByTime({ startTime: dayjs(view.activeStart).format('YYYY.MM.DD'), endTime: dayjs(view.activeEnd).format('YYYY.MM.DD') }).then(res => {
+        if (res.code === 200) {
+          this.calendarEvents = []
+          for (const i in res.data) {
+            const item = {
+              id: res.data[i].id,
+              title: res.data[i].name,
+              start: dayjs(res.data[i].startTime).toDate(),
+              end: dayjs(res.data[i].endTime).toDate()
+            }
+            this.calendarEvents.push(item)
+          }
+        }
+      })
+    },
+    handleSizeChange(val) {
+      this.selfScheduleForm.pageSize = val
+      if (this.busyClassName === 'selected') {
+        this.queryWorkList()
+      } else {
+        this.queryIdleList()
+      }
+    },
+    handleCurrentChange(val) {
+      this.selfScheduleForm.curIndex = val
+      if (this.busyClassName === 'selected') {
+        this.queryWorkList()
+      } else {
+        this.queryIdleList()
+      }
+    },
+    // 标签页切换
+    handleTabsClick() {},
+    // 获取角色信息枚举类
+    showTeamAndMemberEnum() {
+      return workbenchApi.showTeamAndMemberEnum().then(res => {
+        this.teamAndMemberEnum = res.data
+        if (this.teamAndMemberEnum) {
+          for (const i in this.teamAndMemberEnum) {
+            this.teamAndMemberMap[i] = {}
+            if (this.teamAndMemberEnum[i]) {
+              for (const j in this.teamAndMemberEnum[i]) {
+                this.teamAndMemberMap[i][
+                  this.teamAndMemberEnum[i][j].code
+                ] = this.teamAndMemberEnum[i][j].msg
+              }
+            }
+          }
+        }
+        return res
+      })
+    },
+    // 获取用户团队列表
+    queryTeamInfoList(username) {
+      const data = { memberIDAP: username, curIndex: 1, pageSize: 2 }
+      workbenchApi.queryTeamInfoList(data).then(res => {
+        if (res.data) {
+          this.teamInfo = res.data.list
+        }
+      })
+    },
+    getUserRoleInTeam(username, teamInfoItem) {
+      const data = []
+      for (const i in teamInfoItem.teamAttribute) {
+        data.push(
+          this.teamAndMemberMap.teamRoleEnum[teamInfoItem.teamAttribute[i]]
+        )
+      }
+      return data.join('、')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.header .realname {
+  font-size: 22px;
+  font-weight: 500;
+  color: rgba(51, 59, 74, 1);
+  line-height: 35px;
+  margin-bottom: 20px;
+}
+.header .teamInfo {
+  font-size: 14px;
+  font-weight: 400;
+  color: rgba(51, 51, 51, 1);
+  line-height: 20px;
+}
+.header .more {
+  font-weight: 400;
+  color: rgba(155, 155, 155, 1);
+}
+.list-view-header button {
+  background-color: #fff;
+  color: #6f7c93;
+  border: 0.5px solid #bfc6dc;
+  width: 50px;
+  height: 32px;
+}
+.list-view-header .selected,
+button:hover {
+  background-color: #409eff;
+  color: #fff;
+  border: 0.5px solid #409eff;
+}
+</style>
+
+<style>
+.workbench_penson button {
+  outline: none !important;
+  outline-color: transparent;
+}
+.list-view-header .el-date-editor {
+  padding-top: 0;
+  padding-bottom: 0;
+  height: 35px;
+  width: 240px;
+}
+.list-view-header .el-date-editor input {
+  height: 32px;
+  width: 90px;
+}
+.list-view-header .el-date-editor .el-range__close-icon {
+  display: none;
+}
+.list-view-header .el-select .el-input__inner {
+  height: 34px;
+}
+.list-view-header .el-select .el-input__suffix-inner {
+  display: inline-block;
+  position: relative;
+  margin-top: -2px;
+}
+.workbench_fullscreen_dialog .el-tabs .el-tabs__nav-scroll {
+  padding: 0 44%;
+}
+.workbench_fullscreen_dialog .el-tabs .el-tabs__nav-wrap::after {
+  background-color: transparent;
+}
+</style>
+
+// 布局
+<style scoped>
+.workbench_penson {
+  background-color: #f2f3f6;
+  padding: 10px;
+}
+.workbench_penson .layout_main,
+.layout_aside {
+  border-radius: 4px;
+  background-color: #ffffff;
+}
+.middle .layout_aside {
+  padding: 20px 30px;
+  height: 400px;
+}
+</style>
+
+// 公共部分
+<style scoped>
+.module_title {
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+}
+.module_title__sign {
+  width: 4px;
+  height: 15px;
+  background: #409eff;
+  border-radius: 1px;
+}
+.module_title__caption {
+  width: 83px;
+  height: 18px;
+  font-size: 16px;
+  font-family: MicrosoftYaHei;
+  color: rgba(51, 59, 74, 1);
+  margin-left: 6px;
+  font-weight: 500;
+}
+</style>

+ 429 - 0
src/views/workbench/person/myFullCalendar.vue

@@ -0,0 +1,429 @@
+<template>
+  <div :style="type+'-calendar-container'">
+    <link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
+    <link
+      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
+      rel="stylesheet"
+    >
+    <div :class="type+ '-calendar-header'">
+      <el-row style="margin-bottom: 10px;">
+        <el-col :span="type=='small'?7:7" style="display: inline-block;white-space: nowrap;">
+          <button class="prev" @click="callCalendarApi('prev')"><span class="fa fa-chevron-left" /></button>
+          <button class="next" style="margin-left: -4px" @click="callCalendarApi('next')"><span class="fa fa-chevron-right" /></button>
+          <button :class="todayClassName" style="margin-left:3%" @click="callCalendarApi('today')">今天</button>
+        </el-col>
+        <el-col :span="type=='small'?9:10">
+          <div style="display: inline-block;width: 100%;text-align:center;white-space: normal" class="title">{{ calendarTitle }}</div>
+        </el-col>
+        <el-col :span="type=='small'?8:7">
+          <div style="display: inline-block;width: 100%;text-align:right;white-space: nowrap;">
+            <button :class="monthClassName" @click="callCalendarApi('changeView','dayGridMonth');changeClassName('month')">月</button>
+            <button :class="weekClassName" @click="callCalendarApi('changeView','timeGridWeek');changeClassName('week')">周</button>
+            <button :class="dayClassName" @click="callCalendarApi('changeView','timeGridDay');changeClassName('day')">日</button>
+            <button v-if="type =='small'" class="amplify" style="margin-left: 3%" @click="expand()"><span class="fa fa-expand" /></button>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+    <FullCalendar
+      :id="type + '-calendar'"
+      ref="fullCalendar"
+      :class="type + '-calendar'"
+      default-view="dayGridMonth"
+      theme-system="bootstrap"
+      :header="false"
+      :event-time-format="evnetTime"
+      :custom-buttons="customButtons"
+      :button-text="buttonText"
+      locale="zh-cn"
+      editable="false"
+      droppable="false"
+      selectable="true"
+      display-event-end="true"
+      event-color="#409EFF"
+      event-limit="true"
+      event-limit-text="更多"
+      :plugins="calendarPlugins"
+      :weekends="calendarWeekends"
+      :events="events"
+      @select="select"
+      @dateClick="dateClick"
+      @eventClick="eventClick"
+      @eventDrop="eventDrop"
+      @datesRender="datesRender"
+    />
+    <!-- <calendar-dialog
+      :title="calendarDialogProperty.title"
+      :visible="calendarDialogProperty.visible"
+      :data="calendarDialogProperty.data"
+      @cancel="calendarDialogProperty.visible = false"
+      @confirm="calendarDialogProperty.visible = false"
+    />
+    <delete-dialog
+      :content="'是否删除当前日程'"
+      :visible="deleteDialogVisible"
+      @cancel="deleteDialogVisible = false"
+      @confirm="deleteDialogVisible= false"
+    /> -->
+  </div>
+</template>
+
+<script>
+import FullCalendar from '@fullcalendar/vue'
+import dayGridPlugin from '@fullcalendar/daygrid'
+import timeGridPlugin from '@fullcalendar/timegrid'
+import interactionPlugin from '@fullcalendar/interaction'
+import bootstrapPlugin from '@fullcalendar/bootstrap'
+import listPlugin from '@fullcalendar/list'
+import '@fullcalendar/core/main.css'
+import '@fullcalendar/daygrid/main.css'
+import '@fullcalendar/timegrid/main.css'
+import '@fullcalendar/bootstrap/main.css'
+import '@fullcalendar/list/main.css'
+// import CalendarDialog from './calendarFormDialog'
+// import DeleteDialog from '@/components/dialog/delete.vue'
+
+export default {
+  components: {
+    FullCalendar
+    // CalendarDialog,
+    // DeleteDialog
+  },
+  props: {
+    type: {
+      type: String,
+      default: 'small'
+    },
+    events: {
+      type: Array,
+      default() {
+        return []
+      }
+    }
+  },
+  data() {
+    return {
+      today: new Date(),
+      monthClassName: 'month',
+      weekClassName: 'week',
+      dayClassName: 'day',
+      todayClassName: 'today',
+      evnetTime: {
+        hour: 'numeric',
+        minute: '2-digit',
+        hour12: false
+      },
+      header: {
+        left: 'prev,next today',
+        center: 'title',
+        right: 'dayGridMonth,timeGridWeek,timeGridDay amplify'
+      },
+      buttonText: {
+        today: '今天',
+        month: '月',
+        week: '周',
+        day: '日'
+      },
+      customButtons: {
+        amplify: {
+          text: '放大',
+          bootstrapFontAwesome: 'fa-expand',
+          click: function() {
+            alert('点击了自定义按钮!')
+          }
+        }
+      },
+      calendarPlugins: [
+        // plugins must be defined in the JS
+        dayGridPlugin,
+        timeGridPlugin,
+        interactionPlugin, // needed for dateClick
+        bootstrapPlugin,
+        listPlugin
+      ],
+      calendarWeekends: true,
+      calendarEvents: [
+        // initial event data
+        { title: 'Event Now', start: new Date(), end: new Date().setDate(16) }
+      ],
+      teamInfoShow: false,
+      teamAndMemberEnum: {},
+      teamAndMemberMap: {},
+      teamInfo: [],
+      userInfo: {},
+      username: localStorage.getItem('username'),
+      realname: localStorage.getItem('realname'),
+      accountInfo: {},
+      deleteDialogVisible: false,
+      calendarDialogProperty: {
+        title: '新建日程',
+        visible: false,
+        data: null
+      },
+      isCalendarCreated: false,
+      calendarTitle: ''
+    }
+  },
+  mounted() {
+    this.callCalendarApi()
+  },
+  methods: {
+    expand() {
+      this.$emit('expand', true)
+    },
+    changeClassName(val) {
+      if (val === 'month') {
+        this.monthClassName = 'month selected'
+        this.weekClassName = 'week'
+        this.dayClassName = 'day'
+      } else if (val === 'week') {
+        this.monthClassName = 'month'
+        this.weekClassName = 'week selected'
+        this.dayClassName = 'day'
+      } else if (val === 'day') {
+        this.monthClassName = 'month'
+        this.weekClassName = 'week'
+        this.dayClassName = 'day selected'
+      }
+    },
+    callCalendarApi(methods, viewName) {
+      const calendarApi = this.$refs.fullCalendar.getApi()
+      this.$emit('change', calendarApi.view)
+      if (methods) {
+        if (viewName) {
+          calendarApi[methods](viewName)
+        } else {
+          calendarApi[methods]()
+        }
+      }
+      this.calendarTitle = calendarApi.view.title
+      if (calendarApi.view.activeStart.getTime() < this.today.getTime() && this.today.getTime() < calendarApi.view.activeEnd.getTime()) {
+        this.todayClassName = 'today selected'
+      } else {
+        this.todayClassName = 'today'
+      }
+    },
+    select(selectionInfo) {
+      this.$emit('select', selectionInfo)
+    },
+    datesRender(arg) {
+      // console.log(arg)
+    },
+    eventDrop(info) {
+      this.$emit('eventDrop', info)
+    },
+    dateClick(arg) {
+      this.$emit('dateClick', arg)
+      // this.calendarDialogProperty.visible = true
+      // this.calendarEvents.push({
+      //   title: '新增事件', start: arg.date, allDay: arg.allDay
+      // })
+    },
+    eventClick(info) {
+      // alert('Event: ' + info.event.title)
+      info.el.style.borderColor = 'red'
+      info.el.style.backgroundColor = 'red'
+      // this.deleteDialogVisible = true
+    }
+  }
+}
+</script>
+
+<style scoped>
+.small-calendar-header .title,
+.big-calendar-header .title {
+  font-weight:500;
+  width: 40%;
+}
+.small-calendar-header .title{
+  font-size: 12px;
+}
+.big-calendar-header .title {
+  font-size: 16px;
+}
+.small-calendar-header button,.big-calendar-header button {
+  background-color: #fff;
+  color: #6f7c93;
+  border: 0.5px solid #bfc6dc;
+}
+.small-calendar-header button {
+  font-size: 12px;
+}
+.big-calendar-header button {
+  font-size: 14px;
+  height: 32px;
+  width: 32px;
+}
+.big-calendar-header .today {
+  width: 50px;
+  height: 32px;
+}
+.small-calendar-header button:hover,.big-calendar-header button:hover {
+  background-color: #409eff;
+  color: #fff;
+  border: 0.5px solid #409eff;
+}
+.small-calendar-header .next,.week,.day,
+.big-calendar-header .next,.week,.day {
+  margin-left: -4px;
+  border-left: none;
+}
+.small-calendar-header .week,
+.big-calendar-header .week {
+  border-right: none;
+  border-left: none;
+}
+.small-calendar-header .prev,.month,
+.big-calendar-header .prev,.month {
+  border-radius: 4px 0 0 4px;
+}
+.small-calendar-header .day,.next,
+.big-calendar-header .day,.next {
+  border-radius: 0 4px 4px 0;
+}
+.small-calendar-header .today,.amplify,
+.big-calendar-header .today,.amplify {
+  border-radius: 4px;
+}
+.small-calendar-header .selected,
+.big-calendar-header .selected {
+  background-color: #409eff;
+  color: #fff;
+  border: 0.5px solid #409eff;
+}
+</style>
+
+// fullcalendar
+<style lang='scss'>
+#small-calendar .btn-primary {
+  background-color: #fff;
+  color: #6f7c93;
+  border: 0.5px solid #bfc6dc;
+}
+#small-calendar .btn-primary:hover {
+  background-color: #409eff;
+  color: #fff;
+  border: 0.5px solid #409eff;
+}
+#small-calendar .fc-today-button,
+.fc-dayGridMonth-button,
+.fc-timeGridWeek-button,
+.fc-timeGridDay-button {
+  font-size: 10px !important;
+  transform: scale(0.8333);
+  padding: 5px !important;
+}
+#small-calendar .fc-timeGridWeek-button,
+.fc-timeGridDay-button {
+  margin-left: -4px !important;
+}
+#small-calendar .fc-next-button {
+  margin-left: -5px !important;
+}
+#small-calendar .fc-amplify-button {
+  margin-right: -3px !important;
+}
+#small-calendar .fc-prev-button,
+.fc-next-button,
+.fc-amplify-button {
+  font-size: 10px !important;
+  transform: scale(0.8333);
+  padding: 8px !important;
+}
+#small-calendar .fc-center h2 {
+  font-size: 14px;
+  font-weight: 500;
+}
+#small-calendar .fc-view-container .table-bordered .fc-head .fc-day-header {
+  font-size: 10px;
+  transform: scale(0.8333);
+}
+#small-calendar .fc-dayGridMonth-view .table-bordered .fc-body .fc-day-number {
+  font-size: 8px;
+  transform: scale(0.6667);
+}
+#small-calendar
+  .fc-dayGridMonth-view
+  .table-bordered
+  .fc-body
+  .fc-event-container
+  .fc-content {
+  height: 12px;
+  width: 200%;
+  margin-left: -49%;
+  transform: scale(0.5);
+}
+#small-calendar
+  .fc-dayGridMonth-view
+  .table-bordered
+  .fc-body
+  .fc-event-container
+  .fc-content
+  span {
+  font-size: 6px;
+  font-weight: normal !important;
+  display: inline-block;
+  position: relative;
+  bottom: 3px;
+}
+#small-calendar
+  .fc-dayGridMonth-view
+  .table-bordered
+  .fc-body
+  .fc-more {
+  width: 125%;
+  display: inline-block;
+  position: relative;
+  font-size: 6px;
+  transform: scale(0.8);
+}
+#small-calendar
+ .fc-more-popover {
+   margin-top: 9%;
+   margin-left: -14%;
+   border-radius: 8px 8px 0 0;
+   font-size: 6px;
+   transform: scale(0.5);
+ }
+ #small-calendar
+  .fc-more-popover
+  .fc-body {
+   height: 100px;
+   overflow-y: auto;
+ }
+  #small-calendar
+  .fc-more-popover
+  .fc-header {
+   background-color: #D8D8D8;
+ }
+#small-calendar .fc-header-toolbar {
+  margin-bottom: 10px;
+}
+#small-calendar .fc-timeGridWeek-view,
+.fc-timeGridDay-view {
+  font-size: 12px;
+}
+</style>
+
+<style>
+#big-calendar {
+  margin: auto;
+}
+ #big-calendar
+  .fc-more-popover
+  .fc-header {
+   background-color: #D8D8D8;
+ }
+ #big-calendar
+  .fc-more-popover
+  .fc-body {
+   height: 222px;
+   overflow-y: auto;
+ }
+</style>
+
+<style>
+.fc-event-container .fc-content {
+    text-overflow: ellipsis;
+  }
+</style>

+ 422 - 0
src/views/workbench/team/index.vue

@@ -0,0 +1,422 @@
+<template>
+  <el-container direction="vertical" class="workbench_team">
+    <el-main class="layout_main">
+      <div>
+        <div style="display: inline-block">团队</div>
+        <el-select
+          v-model="searchForm.teamId"
+          style="margin-left: 20px"
+          @change="queryTeamWorkList()"
+        >
+          <el-option
+            v-for="item in searchEnum.teams"
+            :key="item.teamId"
+            :label="item.teamName"
+            :value="item.teamId"
+          />
+        </el-select>
+        <div style="display: inline-block;margin-left: 20px">业务线</div>
+        <el-select
+          v-model="searchForm.bizId"
+          style="margin-left: 20px"
+          @change="queryTeamWorkList()"
+        >
+          <el-option
+            v-for="item in searchEnum.businesslines"
+            :key="item.id"
+            :label="item.bizName"
+            :value="item.id"
+          />
+        </el-select>
+      </div>
+    </el-main>
+    <el-main class="layout_main" style="margin-top: 10px">
+      <div class="module_title">
+        <div class="module_title__sign" />
+        <div class="module_title__caption">团队数据</div>
+      </div>
+    </el-main>
+    <el-main class="layout_main" style="margin-top: 10px">
+      <div class="module_title">
+        <div class="module_title__sign" />
+        <div class="module_title__caption">团队日程</div>
+      </div>
+      <el-row style="margin-bottom: 20px" class="gantt-view-header">
+        <el-col :span="12">
+          <el-radio-group v-model="radio1" size="small" @change="radioChange">
+            <el-radio-button label="忙碌" />
+            <el-radio-button label="空闲" />
+          </el-radio-group>
+          <div v-if="radio1 === '空闲'" style="display: inline-block">
+            <div style="display: inline-block;margin-left: 20px">选择时间:</div>
+            <el-date-picker
+              v-model="timeSelectVal"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :picker-options="pickerOptions"
+              :clearable="false"
+              @change="queryTeamIdleList()"
+            />
+          </div>
+        </el-col>
+        <el-col v-if="radio1 === '空闲'" :span="12" style="text-align: right">
+          <div style="display: inline-block" @click="radio2 = ''">
+            <el-radio-group v-model="radio2" size="small" @change="radioChange">
+              <el-radio-button label="今天" />
+            </el-radio-group>
+          </div>
+          <el-radio-group v-model="radio3" size="small" style="margin-left: 20px" @change="radioChange">
+            <el-radio-button label="日" />
+            <el-radio-button label="周" />
+            <el-radio-button label="月" />
+          </el-radio-group>
+        </el-col>
+      </el-row>
+      <gantt-elastic
+        :tasks="tasks"
+        :options="options"
+      >
+        <gantt-elastic-header v-show="false" slot="header" ref="ganttHeader" />
+      </gantt-elastic>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import workbenchApi from '@/api/workbench.js'
+import { queryBizTypeList } from '@/api/defectManage'
+import GanttElastic from 'gantt-elastic'
+import GanttHeader from 'gantt-elastic-header'
+import dayjs from 'dayjs'
+
+export default {
+  components: {
+    ganttElasticHeader: GanttHeader,
+    ganttElastic: GanttElastic
+  },
+  data() {
+    return {
+      timeSelectVal: [new Date(), dayjs(new Date()).add(29, 'day')],
+      pickerOptions: {
+        disabledDate(time) {
+          return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
+        }
+      },
+      tasks: [], // 甘特图任务
+      options: {
+        locale: {
+          name: 'zh_cn',
+          weekdays: ['周天', '周一', '周二', '周三', '周四', '周五', '周六'],
+          months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
+        },
+        maxRows: 100,
+        maxHeight: 460,
+        title: {
+          label: 'Your project title as html (link or whatever...)',
+          html: false
+        },
+        row: {
+          height: 24
+        },
+        calendar: {
+          hour: {
+            display: true
+          }
+        },
+        chart: {
+          progress: {
+            bar: false
+          },
+          expander: {
+            display: true
+          }
+        },
+        taskList: {
+          expander: {
+            straight: false
+          },
+          columns: [
+            {
+              id: 1,
+              label: '团队成员',
+              value: 'user',
+              width: 80,
+              style: {
+                'task-list-header-label': {
+                  'text-align': 'center',
+                  width: '100%'
+                },
+                'task-list-item-value-container': {
+                  'font-weight': '500'
+                }
+              }
+            },
+            {
+              id: 2,
+              label: '任务名称',
+              value: 'label',
+              width: 180,
+              expander: true,
+              style: {
+                'task-list-header-label': {
+                  'text-align': 'center',
+                  width: '100%'
+                }
+              }
+            },
+            {
+              id: 3,
+              label: '开始时间',
+              value: task => dayjs(task.start).format('YYYY-MM-DD'),
+              width: 90,
+              style: {
+                'task-list-header-label': {
+                  'text-align': 'center',
+                  width: '100%'
+                },
+                'task-list-item-value-container': {
+                  'text-align': 'center',
+                  width: '100%'
+                }
+              }
+            },
+            {
+              id: 4,
+              label: '结束时间',
+              value: task => dayjs(task.start + task.duration).format('YYYY-MM-DD'),
+              width: 90,
+              style: {
+                'task-list-header-label': {
+                  'text-align': 'center',
+                  width: '100%'
+                },
+                'task-list-item-value-container': {
+                  'text-align': 'center',
+                  width: '100%'
+                }
+              }
+            },
+            {
+              id: 5,
+              label: '使用/工作日/全部',
+              value: 'needLegalAllDays',
+              width: 130,
+              style: {
+                'task-list-header-label': {
+                  'text-align': 'center',
+                  width: '100%'
+                },
+                'task-list-item-value-container': {
+                  'text-align': 'center',
+                  width: '100%'
+                }
+              }
+            }
+          ]
+        }
+      }, // 甘特图配置
+      ganttHeaderStyle: {
+      },
+      radio1: '忙碌',
+      radio2: '今天',
+      radio2TF: true,
+      radio3: '日',
+      searchForm: {
+        teamId: null,
+        bizId: null
+      },
+      idleSearchForm: {
+        startTime: null,
+        endTime: null
+      },
+      searchEnum: {
+        teams: [],
+        businesslines: []
+      },
+      username: localStorage.getItem('username'),
+      teamWorkList: []
+    }
+  },
+  mounted() {
+    this.queryTeamInfoList(this.username)
+    this.queryBizTypeList()
+    this.queryTeamWorkList()
+  },
+  methods: {
+    tasksUpdate(tasks) {
+      this.tasks = tasks
+    },
+    optionsUpdate(options) {
+      this.options = options
+    },
+    styleUpdate(style) {
+      this.dynamicStyle = style
+    },
+    radioChange(val) {
+      if (val === '忙碌') {
+        this.queryTeamWorkList()
+      } else {
+        this.queryTeamIdleList()
+      }
+      if (val === '今天') {
+        // if (this.radio2TF) {
+        //   this.radio2 = ''
+        // }
+        this.$refs.ganttHeader.recenterPosition()
+        // this.radio2TF = !this.radio2TF
+      }
+      if (val === '月') {
+        this.$refs.ganttHeader.setScale(22)
+      }
+      if (val === '周') {
+        this.$refs.ganttHeader.setScale(20)
+      }
+      if (val === '日') {
+        this.$refs.ganttHeader.setScale(10)
+      }
+    },
+    queryTeamInfoList(username) {
+      const data = { memberIDAP: username, curIndex: 1, pageSize: 9999 }
+      workbenchApi.queryTeamInfoList(data).then(res => {
+        if (res.data) {
+          this.searchEnum.teams = res.data.list
+          this.searchEnum.teams.unshift({
+            teamId: null,
+            teamName: '全部'
+          })
+        }
+      })
+    },
+    queryBizTypeList() {
+      queryBizTypeList({}).then(res => {
+        if (res.data) {
+          this.searchEnum.businesslines = res.data.list
+          this.searchEnum.businesslines.unshift({
+            id: null,
+            bizName: '全部'
+          })
+        }
+      })
+    },
+    queryTeamWorkList() {
+      workbenchApi.queryTeamWorkList(this.searchForm)
+        .then(res => {
+          if (res.data) {
+            this.createTasks(res, '忙碌')
+          }
+        })
+    },
+    queryTeamIdleList() {
+      this.idleSearchForm.startTime = dayjs(this.timeSelectVal[0]).format('YYYY.MM.DD')
+      this.idleSearchForm.endTime = dayjs(this.timeSelectVal[1]).format('YYYY.MM.DD')
+      workbenchApi.queryTeamIdleList({
+        timeInfo: this.idleSearchForm,
+        teamWorkQueryInfo: this.searchForm
+      }).then(res => {
+        if (res.data) {
+          this.createTasks(res, '空闲')
+        }
+      })
+    },
+    createTasks(res, mode) {
+      this.tasks = []
+      let count = 0
+      for (const i in res.data) {
+        let label = ''
+        if (mode === '忙碌') {
+          label = res.data[i].workNum.taskWorkNum + '个任务、' + res.data[i].workNum.selfWorkNum + '个日程'
+        } else {
+          label = res.data[i].workNum.taskWorkNum + '个空闲时段'
+        }
+        const parentItem = {
+          id: count++,
+          label: label,
+          user: res.data[i].userInfo.ldapName,
+          collapsed: true,
+          needLegalAllDays: res.data[i].workNum.needDays + '/' + res.data[i].workNum.legalDays + '/' + res.data[i].workNum.allDays,
+          start: dayjs(res.data[i].workNum.startTime).toDate().getTime(),
+          duration: dayjs(res.data[i].workNum.endTime).toDate().getTime() - dayjs(res.data[i].workNum.startTime).toDate().getTime(),
+          type: 'task'
+        }
+        this.tasks.push(parentItem)
+        for (const j in res.data[i].workData) {
+          const item = {
+            id: count++,
+            parentId: parentItem.id,
+            label: res.data[i].workData[j].name,
+            user: parentItem.user,
+            needLegalAllDays: res.data[i].workData[j].needDays + '/' + res.data[i].workData[j].legalDays + '/' + res.data[i].workData[j].allDays,
+            start: dayjs(res.data[i].workData[j].startTime).toDate().getTime(),
+            duration: dayjs(res.data[i].workData[j].endTime).toDate().getTime() - dayjs(res.data[i].workData[j].startTime).toDate().getTime(),
+            type: 'task'
+          }
+          this.tasks.push(item)
+        }
+      }
+    }
+  }
+
+}
+</script>
+
+<style>
+/* .gantt-elastic__task-list-header-label {
+  font-size: 16px !important;
+  font-weight: 500;
+}
+.gantt-elastic__task-list-item-value {
+  font-size: 14px!important;
+} */
+.gantt-view-header .el-date-editor {
+  padding-top: 0;
+  padding-bottom: 0;
+  height: 35px;
+  width: 240px;
+}
+.gantt-view-header .el-date-editor input {
+  height: 32px;
+  width: 90px;
+}
+.gantt-view-header .el-date-editor .el-range__close-icon {
+  display: none;
+}
+</style>
+
+// 布局
+<style scoped>
+.workbench_team {
+  background-color: #f2f3f6;
+  padding: 10px;
+}
+.workbench_team .layout_main,
+.layout_aside {
+  border-radius: 4px;
+  background-color: #ffffff;
+}
+</style>
+
+// 公共部分
+<style scoped>
+.module_title {
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+}
+.module_title__sign {
+  width: 4px;
+  height: 15px;
+  background: #409eff;
+  border-radius: 1px;
+}
+.module_title__caption {
+  width: 83px;
+  height: 18px;
+  font-size: 16px;
+  font-family: MicrosoftYaHei;
+  color: rgba(51, 59, 74, 1);
+  margin-left: 6px;
+  font-weight: 500;
+}
+</style>