浏览代码

feature(首页):自定义首页

wanglj 2 年之前
父节点
当前提交
727f0dd752
共有 6 个文件被更改,包括 648 次插入328 次删除
  1. 1 0
      package.json
  2. 33 0
      src/api/index/index.js
  3. 118 56
      src/views/base/region/index.vue
  4. 6 3
      src/views/customer/list.vue
  5. 6 2
      src/views/customer/openSea.vue
  6. 484 267
      src/views/index/index.vue

+ 1 - 0
package.json

@@ -34,6 +34,7 @@
     "screenfull": "5.2.0",
     "vab-icons": "^0.0.23",
     "vue": "^2.6.14",
+    "vue-grid-layout": "^2.4.0",
     "vue-i18n": "^8.26.7",
     "vue-router": "^3.5.3",
     "vuedraggable": "^2.24.3",

+ 33 - 0
src/api/index/index.js

@@ -0,0 +1,33 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-02-16 11:41:32
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-02-17 17:24:59
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\api\index\index.js
+ */
+import micro_request from '@/utils/micro_request'
+
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  // 设置首页
+  setUpHomeConfig(query) {
+    return micro_request.postRequest(basePath, 'Home', 'SetUpHomeConfig', query)
+  },
+  // 获取首页
+  getHomeReport(query) {
+    return micro_request.postRequest(basePath, 'Home', 'GetHomeConfig', query)
+  },
+  // 日程
+  getSchedule(query) {
+    return micro_request.postRequest(basePath, 'Schedule', 'GetList', query)
+  },
+  // 可选指标
+  getReportList(query) {
+    return micro_request.postRequest(basePath, 'Report', 'GetList', query)
+  },
+  // 指标图表
+  getHomeDataReportData(query) {
+    return micro_request.postRequest(basePath, 'Home', 'GetHomeDataReportData', query)
+  },
+}

+ 118 - 56
src/views/base/region/index.vue

@@ -1,67 +1,83 @@
 <template>
-  <div class="user-management-container">
+  <div class="region-container">
+    <vab-query-form>
+      <vab-query-form-top-panel>
+        <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item>
+            <el-input v-model="filterText" placeholder="输入关键字进行过滤" />
+          </el-form-item>
+          <el-form-item prop="custCode">
+            <el-input v-model="queryForm.custCode" clearable placeholder="客户编码" />
+          </el-form-item>
+
+          <el-form-item prop="custName">
+            <el-input v-model="queryForm.custName" clearable placeholder="客户名称" />
+          </el-form-item>
+          <el-form-item label="" prop="custIndustry">
+            <el-select v-model="custIndustry" placeholder="请选择客户行业" style="width: 100%">
+              <el-option v-for="item in industryOptions" :key="item.value" :label="item.value" :value="item.value" />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
+            <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-top-panel>
+    </vab-query-form>
     <div class="side-layout">
       <div class="tree-side">
-        <div class="head-container" style="margin-bottom: 5px">
-          <el-input v-model="filterText" placeholder="输入关键字进行过滤" />
-          <el-tree
-            ref="tree"
-            class="filter-tree"
-            :data="deptOptions"
-            default-expand-all
-            :default-expanded-keys="[1]"
-            :filter-node-method="filterNode"
-            highlight-current
-            node-key="id"
-            :props="defaultProps"
-            @node-click="handleNodeClick" />
-        </div>
-        <vab-query-form-left-panel>
-          <el-button icon="el-icon-plus" type="primary" @click="getCheckedNodes($event)">添加区域</el-button>
-        </vab-query-form-left-panel>
+        <el-tree
+          ref="tree"
+          class="filter-tree"
+          :data="deptOptions"
+          default-expand-all
+          :default-expanded-keys="[1]"
+          :filter-node-method="filterNode"
+          highlight-current
+          node-key="id"
+          :props="defaultProps"
+          @node-click="handleNodeClick">
+          <span slot-scope="{ node }" class="custom-tree-node">
+            <span>
+              <el-input v-if="editId == node.id" :ref="node.id" v-model="node.label" size="mini" @blur="editEnd" />
+              <span v-else>{{ node.label }}</span>
+            </span>
+            <span>
+              <!-- <el-button icon="el-icon-more" type="text" /> -->
+              <el-dropdown @command="(command) => treeHandle(command, node)">
+                <span class="el-dropdown-link">
+                  <i class="el-icon-more"></i>
+                  <!-- <i class="el-icon-arrow-down el-icon--right"></i> -->
+                </span>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item command="edit">编辑</el-dropdown-item>
+                  <el-dropdown-item command="del">删除</el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </span>
+          </span>
+        </el-tree>
+        <footer>
+          <el-button icon="el-icon-plus" @click="getCheckedNodes($event)">添加区域</el-button>
+        </footer>
       </div>
       <div class="tree-table">
         <vab-query-form>
-          <vab-query-form-top-panel>
-            <el-form :inline="true" :model="queryForm" @submit.native.prevent>
-              <el-form-item prop="custCode">
-                <el-input v-model="queryForm.custCode" clearable placeholder="客户编码" />
-              </el-form-item>
-
-              <el-form-item prop="custName">
-                <el-input v-model="queryForm.custName" clearable placeholder="客户名称" />
-              </el-form-item>
-              <el-form-item label="" prop="custIndustry">
-                <el-select v-model="custIndustry" placeholder="请选择客户行业" style="width: 100%">
-                  <el-option
-                    v-for="item in industryOptions"
-                    :key="item.value"
-                    :label="item.value"
-                    :value="item.value" />
-                </el-select>
-              </el-form-item>
-
-              <el-form-item>
-                <el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
-                <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
-              </el-form-item>
-            </el-form>
-          </vab-query-form-top-panel>
+          <vab-query-form-left-panel>
+            <el-button icon="el-icon-plus" type="primary" @click="handleEdit($event)">添加</el-button>
+          </vab-query-form-left-panel>
+          <vab-query-form-right-panel>
+            <table-tool :check-list.sync="checkList" :columns="columns" />
+          </vab-query-form-right-panel>
         </vab-query-form>
-
-        <vab-query-form-left-panel>
-          <el-button icon="el-icon-plus" type="primary" @click="handleEdit($event)">添加</el-button>
-        </vab-query-form-left-panel>
-        <vab-query-form-right-panel>
-          <table-tool :check-list.sync="checkList" :columns="columns" />
-        </vab-query-form-right-panel>
-
         <el-table
           v-loading="listLoading"
           border
           :data="list"
           :height="$baseTableHeight(2)"
-          style="width: 100%; height: 500px"
+          style="width: 100%"
           @selection-change="setSelectRows">
           <el-table-column
             v-for="(item, index) in finallyColumns"
@@ -134,13 +150,12 @@
         treeDefaultExpandAll: true,
         regionId: 0,
         industryOptions: [], //客户行业
-
         checkList: [],
         columns: [
           {
             label: '省份',
             width: '200px',
-            prop: 'distName',
+            prop: 'custProvince',
             sortable: false,
           },
           {
@@ -165,6 +180,7 @@
             disableCheck: true,
           },
         ],
+        editId: '',
       }
     },
 
@@ -196,6 +212,45 @@
           custIndustry: '',
         }
       },
+      async treeHandle(command, node) {
+        console.log(command, node, '111111111')
+        if (command == 'edit') {
+          this.editId = node.id
+          await this.$nextTick()
+          this.$refs[node.id].focus()
+        } else if (command == 'del') {
+          this.$confirm(`确认删除销售区域 ${node.label}?`, '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          })
+            .then(() => {
+              if (node.id == this.regionId) {
+                this.regionId = 0
+                this.getRegionList()
+              }
+              this.$message({
+                type: 'success',
+                message: '删除成功!',
+              })
+            })
+            .catch(() => {
+              this.$message({
+                type: 'info',
+                message: '已取消删除',
+              })
+            })
+        }
+      },
+      editEnd(val) {
+        const params = {
+          id: this.editId,
+          name: val,
+        }
+        params
+        this.editId = ''
+        this.getRegionList()
+      },
       getOptions() {
         Promise.all([this.getDicts('cust_idy')])
           .then(([industry]) => {
@@ -206,7 +261,6 @@
       async getRegionList() {
         const { data: data } = await regionApi.getRegionList()
         this.deptOptions = data.list
-
         this.regionId = data.list[0].id
         //默认第一选中
         this.$nextTick(() => {
@@ -286,7 +340,15 @@
     },
   }
 </script>
-<style>
+<style lang="scss" scoped>
+  .side-layout {
+    height: calc(100vh - 200px);
+  }
+  ::v-deep .custom-tree-node {
+    .el-input__inner {
+      padding: 0 !important;
+    }
+  }
   .el-tree-node:focus > .el-tree-node__content {
     /*设置选中的样式 */
     background-color: #dde9ff !important;

+ 6 - 3
src/views/customer/list.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-26 16:34:37
  * @LastEditors: wanglj
- * @LastEditTime: 2023-01-13 14:57:43
+ * @LastEditTime: 2023-02-13 17:46:36
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\customer\list.vue
 -->
@@ -39,7 +39,7 @@
           value-format="yyyy-MM-dd" />
       </el-col>
       <el-col :span="4">
-        <el-button icon="el-icon-plus" type="primary" @click="fetchData">查询</el-button>
+        <el-button icon="el-icon-plus" type="primary" @click="handleSearch">查询</el-button>
         <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
       </el-col>
     </el-row>
@@ -291,7 +291,10 @@
         this.total = res.data.total
         this.listLoading = false
       },
-
+      handleSearch() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
       exportData() {
         this.queryForm.isPublic = false
         let exportFrom = JSON.parse(JSON.stringify(this.queryForm))

+ 6 - 2
src/views/customer/openSea.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-15 15:38:21
  * @LastEditors: wanglj
- * @LastEditTime: 2023-01-13 10:12:12
+ * @LastEditTime: 2023-02-13 17:47:45
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\customer\openSea.vue
 -->
@@ -36,7 +36,7 @@
               value-format="yyyy-MM-dd" />
           </el-form-item>
           <el-form-item>
-            <el-button icon="el-icon-plus" type="primary" @click="fetchData">查询</el-button>
+            <el-button icon="el-icon-plus" type="primary" @click="handleSearch">查询</el-button>
             <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
           </el-form-item>
         </el-form>
@@ -306,6 +306,10 @@
         this.total = res.data.total
         this.listLoading = false
       },
+      handleSearch() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
       exportData() {
         this.queryForm.isPublic = true
         let exportFrom = JSON.parse(JSON.stringify(this.queryForm))

+ 484 - 267
src/views/index/index.vue

@@ -1,8 +1,16 @@
+<!--
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2023-01-11 15:52:49
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-02-17 17:57:12
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\views\index\index.vue
+-->
 <!--
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-13 10:28:33
  * @LastEditors: wanglj
- * @LastEditTime: 2023-01-11 19:16:12
+ * @LastEditTime: 2023-02-15 17:52:56
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\index\index.vue
 -->
@@ -12,157 +20,110 @@
       <el-card class="board">
         <div slot="header" class="card-title">
           <span>个人看板</span>
-          <vab-icon icon="question-line" />
+          <div class="buttons">
+            <el-button v-show="!editFlag" type="text" @click="editFlag = true">编辑布局</el-button>
+            <el-button v-show="editFlag" type="text" @click="visible = true">添加指标</el-button>
+            <el-button v-show="editFlag" type="text" @click="saveLayout">保存布局</el-button>
+          </div>
         </div>
-        <ul>
-          <li>
-            <vab-icon icon="account-circle-line" />
-            <div class="text">
-              <p class="num">124</p>
-              <p>当前客户数(个)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="user-add-line" />
-            <div class="text">
-              <p class="num">124</p>
-              <p>新增客户数(个)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="account-pin-box-line" />
-            <div class="text">
-              <p class="num">124</p>
-              <p>跟进客户数(个)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="account-circle-line" />
-            <div class="text">
-              <p class="num">124</p>
-              <p>合同订单数(个)</p>
-            </div>
-          </li>
-        </ul>
-        <ul>
-          <li>
-            <vab-icon icon="exchange-cny-fill" />
-            <div class="text">
-              <p class="num">{{ parseFloat(123).toFixed(2) }}</p>
-              <p>合同订单金额(元)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="mail-add-line" />
-            <div class="text">
-              <p class="num">124</p>
-              <p>新增机会数(个)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="exchange-cny-fill" />
-            <div class="text">
-              <p class="num">{{ parseFloat(123).toFixed(2) }}</p>
-              <p>新增机会金额(元)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="account-circle-line" />
-            <div class="text">
-              <p class="num">124</p>
-              <p>采购合同数(个)</p>
-            </div>
-          </li>
-        </ul>
-        <ul>
-          <li>
-            <vab-icon icon="money-cny-circle-line" />
-            <div class="text">
-              <p class="num">{{ parseFloat(123).toFixed(2) }}</p>
-              <p>采购金额(元)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="money-cny-circle-line" />
-            <div class="text">
-              <p class="num">{{ parseFloat(123).toFixed(2) }}</p>
-              <p>应收未收(元)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="money-cny-circle-line" />
-            <div class="text">
-              <p class="num">{{ parseFloat(123).toFixed(2) }}</p>
-              <p>计划付款(元)</p>
-            </div>
-          </li>
-          <li>
-            <vab-icon icon="money-cny-circle-line" />
-            <div class="text">
-              <p class="num">{{ parseFloat(123).toFixed(2) }}</p>
-              <p>当前库存成本(元)</p>
-            </div>
-          </li>
-        </ul>
-      </el-card>
-      <el-row class="chart-row" :gutter="12" type="flex">
-        <el-col :span="12">
-          <el-card class="chart">
-            <div slot="header" class="card-title">
-              <span>销售预测</span>
-              <div class="buttons">
-                <el-select v-model="forecast" style="width: 120px">
-                  <el-option label="公司数据" value="1" />
-                </el-select>
-              </div>
-            </div>
-            <div id="bar"></div>
-          </el-card>
-        </el-col>
-        <el-col :span="12">
-          <el-card class="chart">
-            <div slot="header" class="card-title">
-              <span>销售漏斗</span>
-              <div class="buttons">
-                <el-select v-model="forecast" style="width: 120px">
-                  <el-option label="公司数据" value="1" />
-                </el-select>
-                <el-date-picker
-                  v-model="date"
-                  :clearable="false"
-                  style="width: 120px; margin-left: 10px"
-                  value-format="yyyy-MM-dd" />
+        <el-row align="middle" :gutter="10" type="flex">
+          <VueDragger
+            v-model="privateBoard"
+            :animation="500"
+            :disabled="!editFlag"
+            drag-class="drag"
+            :force-fallback="true"
+            ghost-class="ghost"
+            handle=".mover">
+            <el-col v-for="(item, index) in privateBoard" :key="index" class="mover" :span="6">
+              <div class="board-container">
+                <vab-icon class="icon" :icon="item.report_icon" />
+                <div class="text">
+                  <p class="num">{{ item.id }}</p>
+                  <p>{{ item.report_name }}</p>
+                </div>
+                <vab-icon v-show="editFlag" class="close" icon="close-fill" @click.stop="handleDel(index)" />
               </div>
+            </el-col>
+          </VueDragger>
+        </el-row>
+      </el-card>
+      <grid-layout
+        :class="{ grid: editFlag }"
+        :col-num="24"
+        :is-draggable="editFlag"
+        :is-mirrored="false"
+        :is-resizable="editFlag"
+        :layout.sync="layout"
+        :margin="[10, 10]"
+        :row-height="30"
+        :use-css-transforms="true"
+        :use-style-cursor="false"
+        :vertical-compact="true"
+        @layout-ready="layoutReadyEvent">
+        <grid-item
+          v-for="(item, index) in layout"
+          :key="index"
+          :h="item.h"
+          :i="item.i"
+          :w="item.w"
+          :x="item.x"
+          :y="item.y"
+          @resized="handleResize">
+          <h4>
+            {{ item.name }}
+            <!-- <div class="buttons">
+              <el-select v-model="forecast" style="width: 120px">
+                <el-option label="公司数据" value="1" />
+              </el-select>
+            </div> -->
+            <div class="buttons">
+              <el-button v-show="!editFlag" type="text" @click="refresh(item, index)">刷新</el-button>
+              <vab-icon v-show="editFlag" icon="close-fill" @click="gridDel(index)" />
             </div>
-            <div id="funnel"></div>
-          </el-card>
-        </el-col>
-      </el-row>
+          </h4>
+          <div :id="item.i"></div>
+        </grid-item>
+      </grid-layout>
     </div>
     <div class="right">
       <el-card class="calendar">
         <div slot="header" class="card-title">
           <span>日程安排</span>
-          <el-button style="padding: 0" type="text">添加</el-button>
+          <!-- <el-button style="padding: 0" type="text">添加</el-button> -->
         </div>
         <div class="week">
-          <vab-icon icon="arrow-left-s-line" />
+          <vab-icon
+            icon="arrow-left-s-line"
+            @click="
+              date -= 7
+              getDateList()
+            " />
           <ul>
-            <li v-for="item in list" :key="item.date" :class="{ active: item.day == '日' }">
-              <span>{{ item.day }}</span>
+            <li
+              v-for="(item, index) in list"
+              :key="index"
+              :class="{ active: item.day == day }"
+              @click="dateClick(item)">
+              <span>{{ dayList[item.day] }}</span>
               <span>{{ item.date }}</span>
             </li>
           </ul>
-          <vab-icon icon="arrow-right-s-line" />
+          <vab-icon
+            icon="arrow-right-s-line"
+            @click="
+              date += 7
+              getDateList()
+            " />
         </div>
         <el-timeline>
           <el-timeline-item v-for="(activity, index) in activities" :key="index">
-            <div class="detail" :class="{ active: index > 2 }">
+            <div class="detail" :class="{ active: activity.active }">
               <p class="time">
-                {{ activity.timestamp }}
-                <el-button type="text">详情</el-button>
+                {{ activity.schDate }}
+                <!-- <el-button type="text">详情</el-button> -->
               </p>
-              <p>{{ activity.content }}</p>
+              <p>{{ activity.schTitle }}</p>
             </div>
           </el-timeline-item>
         </el-timeline>
@@ -192,101 +153,215 @@
         </ul>
       </el-card>
     </div>
+    <!-- 添加个人看板 -->
+    <el-dialog title="添加个人看板" :visible.sync="visible" @open="getBoardOptions">
+      <el-tabs v-model="activeName" type="card">
+        <el-tab-pane label="看板数据" name="first">
+          <ul class="add-board">
+            <li v-for="(item, index) in addBoard" :key="index" @click="item.checked = !item.checked">
+              <div class="board-container">
+                <vab-icon class="icon" :icon="item.reportIcon" />
+                <div class="text">
+                  <p class="num">{{ item.id }}</p>
+                  <p>{{ item.reportName }}</p>
+                </div>
+                <vab-icon v-show="item.checked" class="check" icon="check-fill" />
+              </div>
+            </li>
+          </ul>
+        </el-tab-pane>
+        <el-tab-pane label="销售指标" name="second">
+          <ul class="add-board">
+            <li v-for="(item, index) in addLayout" :key="index" @click="item.checked = !item.checked">
+              <div class="board-container">
+                <vab-icon class="icon" :icon="item.reportIcon" />
+                <div class="text">
+                  <p class="num">{{ item.id }}</p>
+                  <p>{{ item.reportName }}</p>
+                </div>
+                <vab-icon v-show="item.checked" class="check" icon="check-fill" />
+              </div>
+            </li>
+          </ul>
+        </el-tab-pane>
+      </el-tabs>
+      <template #footer>
+        <el-button @click="visible = false">取 消</el-button>
+        <el-button type="primary" @click="save">确 定</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script>
   import * as echarts from 'echarts'
+  import VueDragger from 'vuedraggable'
+  import VueGridLayout from 'vue-grid-layout'
+  import to from 'await-to-js'
+  import indexApi from '@/api/index'
   import messageApi from '@/api/system/message'
-
   export default {
     name: 'Index',
+    components: {
+      VueDragger,
+      GridLayout: VueGridLayout.GridLayout,
+      GridItem: VueGridLayout.GridItem,
+    },
     data() {
       return {
-        list: [
+        editFlag: false,
+        privateBoard: [
           {
-            day: '日',
-            date: '09',
+            report_name: '当前客户数(个)',
+            id: 1,
+            report_icon: 'account-circle-line',
+            checked: false,
           },
           {
-            day: '一',
-            date: '10',
+            report_name: '新增客户数(个)',
+            id: 2,
+            report_icon: 'user-add-line',
+            checked: false,
           },
           {
-            day: '二',
-            date: '11',
+            report_name: '跟进客户数(个)',
+            id: 3,
+            report_icon: 'account-pin-box-line',
+            checked: false,
           },
           {
-            day: '三',
-            date: '12',
+            report_name: '合同订单数(个)',
+            id: 4,
+            report_icon: 'account-circle-line',
+            checked: false,
           },
           {
-            day: '四',
-            date: '13',
+            report_name: '合同订单金额(元)',
+            id: 5,
+            report_icon: 'exchange-cny-fill',
+            checked: false,
           },
           {
-            day: '五',
-            date: '14',
+            report_name: '新增机会数(个)',
+            id: 6,
+            report_icon: 'mail-add-line',
+            checked: false,
           },
           {
-            day: '六',
-            date: '15',
+            report_name: '新增机会金额(元)',
+            id: 7,
+            report_icon: 'exchange-cny-fill',
+            checked: false,
           },
-        ],
-        activities: [
           {
-            type: '内部分享',
-            content: '活动按期开始',
-            timestamp: '2018-04-15',
+            report_name: '采购合同数(个)',
+            id: 8,
+            report_icon: 'account-circle-line',
+            checked: false,
           },
           {
-            type: '内部分享',
-            content: '如何建立良好的客户关系',
-            timestamp: '2018-04-13',
+            report_name: '采购金额(元)',
+            id: 9,
+            report_icon: 'money-cny-circle-line',
+            checked: false,
           },
           {
-            type: '内部分享',
-            content: '创建成功',
-            timestamp: '2018-04-11',
+            report_name: '应收未收(元)',
+            id: 10,
+            report_icon: 'money-cny-circle-line',
+            checked: false,
           },
           {
-            type: '内部分享',
-            content: '创建成功',
-            timestamp: '2018-04-11',
+            report_name: '计划付款(元)',
+            id: 11,
+            report_icon: 'money-cny-circle-line',
+            checked: false,
           },
           {
-            type: '内部分享',
-            content: '创建成功',
-            timestamp: '2018-04-11',
+            report_name: '当前库存成本(元)',
+            id: 12,
+            report_icon: 'money-cny-circle-line',
+            checked: false,
           },
         ],
-        chartBar: {},
-        chartFunnel: {},
+        addBoard: [],
+        addLayout: [],
+        visible: false,
+        layout: [],
+        day: -1,
+        list: [],
+        activities: [],
+        charts: [],
+        activeName: 'first',
         forecast: '1',
-        date: '2023-01-10',
+        date: 0,
+        dayList: ['日', '一', '二', '三', '四', '五', '六'],
         messageList: [],
         msgTypeOptions: [],
       }
     },
     mounted() {
-      this.initBar()
-      this.initFunnel()
-      window.addEventListener('resize', this.handleResize)
+      this.init()
+      this.getDateList()
+      this.getOptions()
+      this.handleNoticeList()
       this.$baseEventBus.$on('receivedMessage', () => {
         console.log('---------------通知更新公告----------------')
         this.handleNoticeList()
       })
-      this.getOptions()
-      this.handleNoticeList()
+      window.addEventListener('resize', this.handleResize)
     },
     methods: {
-      initBar() {
-        this.chartBar = echarts.init(document.getElementById('bar'))
+      // 布局信息
+      async init() {
+        const [err, res] = await to(indexApi.getHomeReport({ module_code: 'HomePage' }))
+        if (err) return
+        const obj = JSON.parse(res.data.configInfo)
+        this.privateBoard = obj.num_report_config
+        this.layout = obj.data_report_config.map((item) => {
+          return {
+            x: item.location_x,
+            y: item.location_y,
+            i: item.id,
+            desc: item.report_desc,
+            name: item.report_name,
+            type: item.report_type,
+            h: item.size_h,
+            w: item.size_w,
+          }
+        })
+        await this.$nextTick()
+        this.initChart()
+      },
+      initChart() {
+        console.log(this.layout, 'layout')
+        this.charts = []
+        for (let i = 0; i < this.layout.length; i++) {
+          this.getChartSize(this.layout[i].i)
+          this.drawChart(this.layout[i].i, i)
+        }
+      },
+      //刷新
+      refresh(item, index) {
+        this.charts[index].dispose()
+        this.drawChart(item.i, index)
+      },
+      getChartSize(id) {
+        console.log(id, 'id')
+        const bar = document.getElementById(id)
+        console.log(bar, 'bar')
+        bar.style.height = bar.parentNode.clientHeight - 65 + 'px'
+        bar.style.width = bar.parentNode.clientWidth + 'px'
+      },
+      async drawChart(id, i) {
+        const chartBar = echarts.init(document.getElementById(id))
+        const [err, res] = await to(indexApi.getHomeDataReportData({ id }))
+        if (err) return
         const option = {
           grid: {
-            bottom: 20,
-            top: 20,
-            right: 0,
+            bottom: 30,
+            top: 40,
+            right: 10,
           },
           tooltip: {
             trigger: 'axis',
@@ -294,37 +369,37 @@
           xAxis: [
             {
               type: 'category',
-              // prettier-ignore
-              data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+              data: res.data.data.xData,
+              axisTick: {
+                alignWithLabel: true,
+              },
             },
           ],
           yAxis: [
             {
               type: 'value',
+              name: '(元)',
             },
           ],
           series: [
             {
-              name: 'Rainfall',
+              name: '销售指标',
               type: 'bar',
-              data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3],
+              data: res.data.data.yDataTarget,
             },
             {
-              name: 'Evaporation',
+              name: '销售额度',
               type: 'bar',
-              data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3],
+              data: res.data.data.yDataReal,
             },
           ],
         }
-        this.chartBar.setOption(option)
+        chartBar.setOption(option)
+        this.charts[i] = chartBar
       },
       initFunnel() {
         this.chartFunnel = echarts.init(document.getElementById('funnel'))
         const option = {
-          grid: {
-            top: 0,
-            bottom: 0,
-          },
           tooltip: {
             trigger: 'item',
             formatter: '{b} : {d}%',
@@ -339,8 +414,8 @@
             {
               name: '销售漏斗',
               type: 'funnel',
-              top: 0,
-              bottom: 0,
+              top: 10,
+              bottom: 10,
               minSize: '20%',
               maxSize: '100%',
               label: {
@@ -359,8 +434,8 @@
             {
               name: '销售漏斗',
               type: 'funnel',
-              top: 0,
-              bottom: 0,
+              top: 10,
+              bottom: 10,
               minSize: '20%',
               maxSize: '100%',
               z: 1,
@@ -376,12 +451,101 @@
         }
         this.chartFunnel.setOption(option)
       },
-      handleResize() {
-        this.$nextTick(() => {
-          this.chartBar.resize()
-          this.chartFunnel.resize()
+      async handleResize() {
+        await this.$nextTick()
+        for (let i = 0; i < this.layout.length; i++) {
+          this.getChartSize(this.layout[i].i)
+          this.charts[i].resize()
+        }
+      },
+      layoutReadyEvent() {
+        this.initChart()
+      },
+      handleDel(index) {
+        this.privateBoard.splice(index, 1)
+      },
+      gridDel(index) {
+        this.layout.splice(index, 1)
+      },
+      async save() {
+        const board = this.addBoard.filter((item) => item.checked == true)
+        for (const item of board) {
+          this.privateBoard.push({
+            report_name: item.reportName,
+            id: item.id,
+            report_icon: item.reportIcon,
+          })
+        }
+        const layout = this.addLayout.filter((item) => item.checked == true)
+        for (const item of layout) {
+          const index = this.layout.findIndex((each) => each.i == item.id)
+          if (index > -1) continue
+          this.layout.push({ x: 0, y: 0, w: 12, h: 10, i: item.id, type: 'bar', name: item.reportName })
+        }
+        this.visible = false
+        await this.$nextTick()
+        this.initChart()
+      },
+      async saveLayout() {
+        this.editFlag = false
+        const params = {
+          module_code: 'HomePage',
+          data_report_config: [],
+          num_report_config: this.privateBoard,
+        }
+        params.data_report_config = this.layout.map((item) => {
+          return {
+            location_x: item.x,
+            location_y: item.y,
+            id: item.i,
+            report_desc: item.desc,
+            report_name: item.name,
+            report_type: item.type,
+            size_h: item.h,
+            size_w: item.w,
+          }
+        })
+        const [err, res] = await to(indexApi.setUpHomeConfig(params))
+        if (err) return
+        this.$message.success(res.msg)
+      },
+      getDateList() {
+        const now = new Date()
+        const date = new Date(now.getTime() + this.date * 1000 * 60 * 60 * 24)
+        const day = date.getDay()
+        this.day = day
+        this.list = new Array(7)
+        for (let i = 0; i < this.list.length; i++) {
+          this.list[i] = {}
+          const flag = -(day - i)
+          const time = new Date(date.getTime() + flag * 1000 * 60 * 60 * 24)
+          this.list[i].time = new Date(time)
+          this.list[i].day = this.list[i].time.getDay()
+          this.list[i].date = this.list[i].time.getDate()
+          if (flag == 0) this.dateClick(this.list[i])
+        }
+      },
+      async dateClick(item) {
+        this.day = item.day
+        const [err, res] = await to(indexApi.getSchedule({ schDate: this.parseTime(item.time) }))
+        if (err) return
+        this.activities = res.data.list || []
+        const date = new Date().getTime()
+        this.activities.forEach((item) => {
+          const start = new Date(item.schDate).getTime()
+          const end = new Date(item.schDateEnd).getTime()
+          item.active = start <= date && end >= date
         })
       },
+      async getBoardOptions() {
+        this.activeName = 'first'
+        const [err, res] = await to(indexApi.getReportList())
+        if (err) return
+        const arr = res.data.list || []
+        arr.forEach((item) => (item.checked = false))
+        this.addLayout = arr.filter((item) => item.reportType == 20)
+        this.addBoard = arr.filter((item) => item.reportType == 10)
+      },
       getOptions() {
         this.getDicts('sys_msg_type').then((response) => {
           this.msgTypeOptions = response.data.values || []
@@ -426,10 +590,10 @@
       font-size: 16px;
       font-weight: 700;
       line-height: 22px;
+      height: 22px;
       display: flex;
       justify-content: space-between;
       align-items: center;
-
       i {
         font-weight: normal;
         cursor: pointer;
@@ -445,84 +609,42 @@
       margin-right: 10px;
       overflow-x: hidden;
       overflow-y: auto;
-
       .board {
-        height: 60%;
         display: flex;
         flex-direction: column;
-
+        margin-bottom: 2px;
         ::v-deep .el-card__body {
           flex: 1;
           display: flex;
           flex-direction: column;
           justify-content: space-around;
         }
-
-        ul {
-          height: 60px;
-          display: flex;
-
-          li {
-            flex: 1;
-            height: 60px;
+        .el-row {
+          height: 100%;
+          flex-wrap: wrap;
+          > div,
+          > div span {
             display: flex;
+            height: 100%;
             align-items: center;
-            padding: 0 20px;
+            flex-wrap: wrap;
+            width: 100%;
+          }
+          .ghost {
+            background: #ecf5ff;
+          }
+          .drag {
+            background: pink;
+          }
+          .el-col {
+            display: flex;
+            height: 60px;
+            margin: 20px 0;
             user-select: none;
             cursor: pointer;
-
-            + li {
-              border-left: 1px solid #eee;
-            }
-
-            i {
-              font-size: 32px;
-              border-radius: 4px;
-              width: 40px;
-              height: 40px;
-              color: #1d66dc;
-              background: #ecf5ff;
-
-              &.ri-money-cny-circle-line {
-                color: #e6a23c;
-                background: #fdf6ec;
-              }
-
-              &.ri-exchange-cny-fill {
-                color: #f56c6c;
-                background: #fef0f0;
-              }
-            }
-
-            .text {
-              padding-left: 10px;
-              color: #9499a0;
-
-              .num {
-                font-size: 22px;
-                font-weight: bold;
-                color: #333;
-              }
-            }
           }
         }
       }
-
-      .chart-row {
-        // height: calc(40% - 12px);
-        // .el-card {
-        //   height: 100%;
-        //   display: flex;
-        //   flex-direction: column;
-        //   ::v-deep .el-card__body {
-        //     flex: 1;
-        //     > div {
-        //       height: 100%;
-        //       width: 100%;
-        //     }
-        //   }
-        // }
-      }
     }
 
     .right {
@@ -573,7 +695,6 @@
           height: 48px;
           display: flex;
           align-items: center;
-
           ul {
             flex: 1;
             display: flex;
@@ -585,7 +706,8 @@
               flex-direction: column;
               align-items: center;
               justify-content: center;
-
+              user-select: none;
+              cursor: pointer;
               &.active span:last-child {
                 background: rgb(34, 76, 218);
                 color: #fff;
@@ -675,9 +797,104 @@
   ::v-deep .el-timeline-item__tail {
     border-left: 2px dashed #e4e7ed;
   }
-
-  #funnel,
-  #bar {
-    height: 300px;
+  // #funnel,
+  // #bar {
+  //   height: 100%;
+  // }
+  .vue-grid-layout {
+    margin-left: -10px;
+    margin-right: -10px;
+    &.grid {
+      background: linear-gradient(90deg, #fff 10px, transparent 0), linear-gradient(#fff 10px, transparent 0);
+      background-size: calc((100% - 10px) / 24) 40px;
+      background-position: top 0px left 0px;
+    }
+  }
+  .vue-grid-item {
+    background: #fff;
+    border: 1px solid #ebeef5;
+    border-radius: 4px;
+    box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+    overflow: hidden;
+    h4 {
+      line-height: 22px;
+      height: 54px;
+      font-size: 16px;
+      padding: 16px 20px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      border-bottom: 1px solid #ebeef5;
+      i {
+        font-size: 24px;
+        font-weight: bold;
+        cursor: pointer;
+        &:hover {
+          color: #1d66dc;
+        }
+      }
+    }
+  }
+  .board-container {
+    display: flex;
+    height: 60px;
+    border: 1px solid #eee;
+    align-items: center;
+    border-radius: 8px;
+    padding: 0 10px;
+    flex: 1;
+    cursor: pointer;
+    user-select: none;
+    &:hover {
+      box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+    }
+    .icon {
+      font-size: 32px;
+      border-radius: 4px;
+      width: 40px;
+      height: 40px;
+      color: #1d66dc;
+      background: #ecf5ff;
+      &.ri-money-cny-circle-line {
+        color: #e6a23c;
+        background: #fdf6ec;
+      }
+      &.ri-exchange-cny-fill {
+        color: #f56c6c;
+        background: #fef0f0;
+      }
+    }
+    .full-icon {
+      flex: 1;
+      font-size: 32px;
+    }
+    .close {
+      font-size: 32px;
+      transition: 0.3s all;
+      &:hover {
+        color: #1d66dc;
+      }
+    }
+    .check {
+      font-size: 32px;
+      color: #67c23a;
+    }
+    .text {
+      padding-left: 10px;
+      color: #9499a0;
+      flex: 1;
+      .num {
+        font-size: 22px;
+        font-weight: bold;
+        color: #333;
+      }
+    }
+  }
+  .add-board {
+    height: 500px;
+    overflow: auto;
+    li + li {
+      margin-top: 10px;
+    }
   }
 </style>