jwyu 6 ماه پیش
والد
کامیت
1da15bdbbb

+ 33 - 0
src/routes/modules/BIRoutes.js

@@ -0,0 +1,33 @@
+const home = (r) => require.ensure([], () => r(require("@/views/Home.vue")), "Home"); //主页
+export default [
+  {
+    path: "/",
+    component: home,
+    name: "BI看板",
+    hidden: false,
+    icon_path: require("@/assets/img/home/data_ic.png"),
+    meta: {
+      name_en: "",
+    },
+    children: [
+      {
+        path: "BIBoard",
+        name: "BI看板",
+        component: () => import("@/views/BI_manage/index.vue"),
+        hidden: false,
+        meta: {
+          name_en: "",
+        },
+      },
+      {
+        path: "editBIBoard",
+        name: "添加看板",
+        component: () => import("@/views/BI_manage/editBoard.vue"),
+        hidden: false,
+        meta: {
+          name_en: "",
+        },
+      },
+    ],
+  },
+];

+ 122 - 0
src/views/BI_manage/components/BoardContent.vue

@@ -0,0 +1,122 @@
+<template>
+  <div class="BI-board-content">
+    <div class="BI-board-list" v-infinite-scroll="handleScroll" :infinite-scroll-immediate="false">
+      <table-no-data v-if="list.length===0" style="flex:1"/>
+      <div
+        class="BI-board-item-box"
+        v-for="(item, index) in list"
+        :key="item.UniqueCode"
+        @dragover.prevent
+        @drop="drop(index)"
+      >
+        <component :is="getCompType(item.type)" :compData="item">
+          <template v-slot:drag>
+            <!-- Draggable icon -->
+            <img
+              v-if="canDrag"
+              class="icon"
+              src="~@/assets/img/data_m/move_ico.png"
+              alt=""
+              @mousedown="setDraggable(true, index)"
+              @dragstart="dragStart(index)"
+              @dragend="setDraggable(false, index)"
+              style="cursor: move"
+            />
+          </template>
+        </component>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import TableNoData from '../../../components/tableNoData.vue';
+import ChartBox from './ChartBox.vue';
+import TableBox from './TableBox.vue';
+
+export default {
+  components: { ChartBox, TableBox, TableNoData },
+  props: {
+    dataList: {
+      type: Array,
+      default: () => []
+    },
+    canDrag: {//能否拖动
+      type: Boolean,
+      default: true
+    }
+  },
+  watch: {
+    dataList:{
+      handler(){
+        this.init()
+      },
+      immediate:true,
+      deep:true
+    }
+  },
+  data() {
+    return {
+      page: 0,
+      pageSize: 6,
+      list: [],
+      draggedIndex: null,
+    };
+  },
+  methods: {
+    init() {
+      console.log('初始化看板');
+      this.page = 0
+      this.list = []
+      this.handleLoadContent()
+    },
+    handleLoadContent() {
+      this.list = this.list.concat(this.dataList.slice(this.page * this.pageSize, (this.page + 1) * this.pageSize))
+    },
+    handleScroll(){
+      if(this.list.length>=this.dataList.length) return
+      this.page++
+      this.handleLoadContent()
+    },
+
+    getCompType(type) {
+      return type === 1 ? ChartBox : TableBox;
+    },
+    setDraggable(draggable, index) {
+      const box = this.$el.querySelectorAll('.BI-board-item-box')[index];
+      box.draggable = draggable;
+    },
+    dragStart(index) {
+      this.draggedIndex = index;
+    },
+    drop(index) {
+      if (this.draggedIndex === null) return
+      // Swap the two items
+      const temp = this.list[this.draggedIndex];
+      this.$set(this.list, this.draggedIndex, this.list[index]);
+      this.$set(this.list, index, temp);
+      this.draggedIndex = null;
+      this.$emit('sortChange',this.list)
+    },
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.BI-board-content {
+  padding: 20px;
+  .BI-board-list {
+    height: calc(100vh - 300px);
+    overflow-y: auto;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .BI-board-item-box {
+      width: calc(50% - 14px);
+      height: 560px;
+      border: 1px solid #dcdfe6;
+      box-shadow: 0px 2px 8px 0px #00000014;
+    }
+  }
+}
+</style>

+ 63 - 0
src/views/BI_manage/components/ChartBox.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="chart-box" v-if="compData">
+    <div class="top-title-box">
+      <div class="title">标题{{compData.UniqueCode}}</div>
+      <div class="opt-box">
+        <img class="icon" src="~@/assets/img/icons/refresh_blue_new.png" alt="">
+        <slot name="drag"></slot>
+        <img class="icon" src="~@/assets/img/icons/delete-red.png" alt="">
+      </div>
+    </div>
+    <img class="bg" src="https://hzstatic.hzinsights.com/static/images/202409/20240924/iy8dDVu5HwzLdIKyuX2ajkqBrrB7.png" alt="">
+  </div>
+</template>
+
+<script>
+export default {
+  props:{
+    compData:null
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.chart-box{
+  width: 100%;
+  height: 100%;
+  padding: 20px;
+  box-sizing: border-box;
+  .top-title-box{
+    display: flex;
+    margin-bottom: 10px;
+    .title{
+      font-size: 20px;
+      font-weight: bold;
+      flex: 1;
+      &::before{
+        content:'';
+        display: inline-block;
+        width: 4px;
+        height: 20px;
+        background-color: #0052D9;
+        position: relative;
+        top: 2px;
+        margin-right: 5px;
+      }
+    }
+    .opt-box{
+      flex-shrink: 0;
+      margin-left: 10px;
+      .icon{
+        width: 24px;
+        height: 24px;
+        margin-left: 5px;
+      }
+    }
+  }
+  .bg{
+    width: 100%;
+    height: 200px;
+  }
+
+}
+</style>

+ 260 - 0
src/views/BI_manage/components/SelectChart.vue

@@ -0,0 +1,260 @@
+<template>
+  <div class="choose-chart-container">
+    <el-dialog
+      :visible.sync="show"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="cancelHandle"
+      custom-class="dialog"
+      title="添加图表"
+      top="3vh"
+      center
+      width="86%"
+			style="min-width:1200px;"
+      v-dialogDrag
+    >
+      <div class="dialog-top">
+        <el-input
+          :placeholder="$t('Chart.Detail.chart_name')"
+          v-model="search_txt"
+          style="width: 100%"
+          @input="searchHandle"
+          clearable
+        >
+          <i slot="prefix" class="el-input__icon el-icon-search"></i>
+        </el-input>
+        <div style="margin-top: 10px">
+          <el-radio-group v-model="chart_source" @change="searchHandle" style="margin-right:15px;">
+						<el-radio :label="1"><!-- ETA图库 -->{{$t('Chart.AllChartSource.eta_chart')}}</el-radio>
+						<el-radio :label="2"><!-- 商品价格曲线 -->{{$t('Chart.AllChartSource.commodity_chart')}}</el-radio>
+						<el-radio :label="3"><!-- 相关性图表 -->{{$t('Chart.AllChartSource.correla_chart')}}</el-radio>
+						<el-radio :label="6"><!-- 拟合方程曲线 -->{{$t('Chart.AllChartSource.equation_chart')}}</el-radio>
+						<el-radio :label="7"><!-- 统计特征 -->{{$t('Chart.AllChartSource.statis_chart')}}</el-radio>
+						<el-radio :label="10"><!-- 跨品种分析 -->{{$t('Chart.AllChartSource.cross_chart')}}</el-radio>
+					</el-radio-group>
+          <el-checkbox v-model="isShowMe"  @change="searchHandle"><!-- 只看我的 -->{{$t('MyEtaPage.label_see_mine')}}</el-checkbox>
+        </div>
+      </div>
+      <div class="choose-dialog-min">
+        <div
+          v-if="haveData"
+          class="chart-public-list"
+          style="margin-bottom: 20px;padding-right: 20px;"
+          :infinite-scroll-disabled="!haveMove"
+          v-infinite-scroll="loadMove"
+          ref="scrollCont"
+        >
+          <el-col
+            :span="5"
+            v-for="chart in chartPublicList"
+            :key="chart.ChartInfoId"
+            style="padding-right: 20px;margin-bottom:20px;min-width: 260px;"
+          >
+            <el-card shadow class="public-chart-item">
+              <div slot="header" class="item-top">
+                <span class="text_oneLine">{{ $parent.chart_lang==='en' ? (chart.ChartNameEn||chart.ChartName) : chart.ChartName }}</span>
+              </div>
+              <img :src="(chart_source===1&&!chart.HaveOperaAuth) ? $icons.lock_big : chart.ChartImage" alt="" class="chart-img" />
+              <div class="item-bottom">
+                <span class="last-time"><!-- 最近更新 -->{{$t('MyEtaPage.label_update_time')}}:{{ chart.ModifyTime.substr(0,10) }}</span>
+                <span class="join_txt" @click="addMychartHandle(chart)" 
+                v-if="(chart.HaveOperaAuth&&chart_source===1)||chart_source!==1">
+                  <img
+                    :src="$icons.chart_join_ico"
+                    alt=""
+                    style="width: 13px; height: 12px; vertical-align: middle"
+                  />
+                  <!-- 加入我的图库 -->{{$t('Chart.chart_addmy_btn')}}
+                </span>
+                <!-- <span style="color: #3BB737;" v-else>已加入</span> -->
+              </div>
+            </el-card>
+          </el-col>
+        </div>
+        <div v-else class="nodata">
+          <tableNoData :text="$t('Common.no_chart_msg')"/>
+        </div>
+      </div>
+      <div class="dia-bot">
+        <el-button
+          type="primary"
+          plain
+          @click="cancelHandle"
+          style="margin-right: 20px"
+          >{{ $t("Dialog.cancel_btn") }}</el-button
+        >
+        <el-button type="primary" @click="saveHandle">{{
+          $t("Dialog.confirm_save_btn")
+        }}</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { mychartInterface } from '@/api/api.js';
+import chartRelevanceApi from '@/api/modules/chartRelevanceApi';
+import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
+import futuresInterface from '@/api/modules/futuresBaseApi';
+export default {
+  model: {
+    prop: 'show',
+    event: 'showChange'
+  },
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  watch: {
+    show(newval) {
+      newval && this.getPublicChartList();
+    },
+  },
+  data() {
+    return {
+      haveData: false,
+      search_txt: '',
+      page_no: 1,
+      page_size: 15,
+      haveMove: true,
+      chartPublicList: [],
+
+      chart_source:1,
+      isShowMe: false
+    };
+  },
+  methods: {
+    /* 获取图库列表 */
+    async getPublicChartList() {
+      let params = {
+        PageSize: this.page_size,
+        CurrentIndex: this.page_no,
+        ChartClassifyId: 0,
+        KeyWord: this.search_txt,
+        IsShowMe: this.isShowMe
+      };
+
+       const apiMap = {
+        1: mychartInterface.publicList,
+        2: futuresInterface.searchChart,
+        3: chartRelevanceApi.getChartList,
+        6: fittingEquationInterface.getChartList,
+        7: statisticFeatureInterface.getChartList,
+        10: crossVarietyInterface.searchChart,
+      }
+      let res = await apiMap[this.chart_source](params)
+
+
+      if (res.Ret !== 200) return;
+      this.haveMove = res.Data ? this.page_no < res.Data.Paging.Pages : false;
+      this.chartPublicList = res.Data
+        ? this.page_no === 1
+          ? res.Data.List
+          : [...this.chartPublicList, ...res.Data.List]
+        : [];
+      this.haveData = this.chartPublicList.length ? true : false;
+      
+    },
+
+    addSuccess(params) {
+      this.isAddMyChart = false;
+      this.isHaveAdd = true;
+    },
+
+    loadMove() {
+      this.page_no++;
+      this.getPublicChartList();
+    },
+
+    searchHandle() {
+      this.page_no = 1;
+      if(this.$refs.scrollCont) this.$refs.scrollCont.scrollTop = 0;
+      this.getPublicChartList();
+    },
+
+    init() {
+      this.page_no = 1;
+      if(this.$refs.scrollCont) this.$refs.scrollCont.scrollTop = 0;
+      this.search_txt = '';
+    },
+    cancelHandle() {
+      this.init();
+      this.$emit('showChange', false)
+    },
+  },
+  created() {},
+  mounted() {},
+};
+</script>
+<style lang="scss">
+.choose-chart-container {
+  .el-col-5 {
+    width: 20%;
+  }
+  .el-card .el-card__header,
+  .el-card__body {
+    padding: 10px;
+  }
+  .el-dialog--center .el-dialog__body {
+    padding: 25px 0 10px;
+  }
+  .dialog-top {
+    padding: 0 25px;
+  }
+  .choose-dialog-min {
+    margin-top: 20px;
+    .chart-public-list {
+      display: flex;
+      flex-wrap: wrap;
+      /* height: 650px; */
+      max-height: 650px;
+      height: 65vh;
+      padding: 0 0 0 20px;
+      overflow: hidden;
+      overflow-y: auto;
+      .dragShdow {
+        box-shadow: 0 1px 8px rgba(64, 158, 255, 0.8);
+        opacity: 0.5;
+      }
+      .public-chart-item {
+        .item-top {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          font-size: 15px;
+          font-weight: 600;
+        }
+        .chart-img {
+          width: 100%;
+          height: 180px;
+          object-fit: fill !important;
+        }
+        .item-bottom {
+          margin-top: 10px;
+          font-size: 12px;
+          display: flex;
+          justify-content: space-between;
+          color: #666;
+          .join_txt {
+            color: #409eff;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+    .nodata {
+      height: 500px;
+      text-align: center;
+      font-size: 14px;
+      color: #666;
+    }
+  }
+}
+.dia-bot {
+  display: flex;
+  justify-content: center;
+  margin: 40px 0;
+}
+</style>

+ 268 - 0
src/views/BI_manage/components/SelectTable.vue

@@ -0,0 +1,268 @@
+<template>
+  <div class="choose-chart-container">
+    <el-dialog
+      :visible.sync="show"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="cancelHandle"
+      custom-class="dialog"
+      title="添加表格"
+      top="3vh"
+      center
+      width="86%"
+      style="min-width: 1200px"
+      v-dialogDrag
+    >
+      <div class="dialog-top">
+        <el-input
+          :placeholder="$t('Chart.Detail.chart_name')"
+          v-model="search_txt"
+          style="width: 100%"
+          @input="searchHandle"
+          clearable
+        >
+          <i slot="prefix" class="el-input__icon el-icon-search"></i>
+        </el-input>
+        <div style="margin-top: 10px">
+          <el-radio-group
+            v-model="table_source"
+            @change="searchHandle"
+            style="margin-right: 15px"
+          >
+            <el-radio :label="1">共享表格</el-radio>
+            <el-radio :label="2">时间序列表格</el-radio>
+            <el-radio :label="3">混合表格</el-radio>
+          </el-radio-group>
+          <el-checkbox style="float:right" v-model="isShowMe" @change="searchHandle"
+            ><!-- 只看我的 -->{{ $t("MyEtaPage.label_see_mine") }}</el-checkbox
+          >
+        </div>
+      </div>
+      <div class="choose-dialog-min">
+        <el-checkbox-group v-model="checkList" v-if="haveData">
+          <div
+            class="chart-public-list"
+            style="margin-bottom: 20px; padding-right: 20px"
+            :infinite-scroll-disabled="!haveMove"
+            v-infinite-scroll="loadMove"
+            ref="scrollCont"
+          >
+            <el-col
+              :span="5"
+              v-for="table in tablePublicList"
+              :key="table.ExcelInfoId"
+              style="padding-right: 20px; margin-bottom: 20px; min-width: 260px"
+            >
+              <el-card
+                shadow
+                :class="[
+                  'public-chart-item',
+                  checkList.includes(table.ExcelInfoId) ? 'select_box' : '',
+                ]"
+              >
+                <div slot="header" class="item-top">
+                  <span class="text_oneLine">{{ table.ExcelName }}</span>
+                  <el-checkbox :disabled="!table.HaveOperaAuth" :label="table.ExcelInfoId"
+                    ><span>&nbsp;</span></el-checkbox
+                  >
+                </div>
+                <img
+                  :src="
+                    !table.HaveOperaAuth ? $icons.lock_big : table.ExcelImage
+                  "
+                  alt=""
+                  class="chart-img"
+                />
+                <div class="item-bottom">
+                  <span class="last-time"
+                    >创建时间:{{ table.CreateTime.substr(0, 10) }}</span
+                  >
+                </div>
+              </el-card>
+            </el-col>
+          </div>
+        </el-checkbox-group>
+        <div v-else class="nodata">
+          <tableNoData text="暂无表格" />
+        </div>
+      </div>
+      <div class="dia-bot">
+        <el-button
+          type="primary"
+          plain
+          @click="cancelHandle"
+          style="margin-right: 20px"
+          >{{ $t("Dialog.cancel_btn") }}</el-button
+        >
+        <el-button type="primary" @click="saveHandle">{{
+          $t("Dialog.confirm_save_btn")
+        }}</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import * as sheetInterface from "@/api/modules/sheetApi.js";
+export default {
+  model: {
+    prop: 'show',
+    event: 'showChange'
+  },
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  watch: {
+    show(newval) {
+      newval && this.getPublicTableList();
+    },
+  },
+  data() {
+    return {
+      haveData: false,
+      search_txt: '',
+      page_no: 1,
+      page_size: 15,
+      haveMove: true,
+      tablePublicList: [],
+
+      checkList: [],
+
+      table_source: 1,
+      isShowMe: false
+    };
+  },
+  methods: {
+    saveHandle(){
+      const arr=this.tablePublicList.filter(item=>this.checkList.includes(item.ExcelInfoId))
+      this.$emit('addTable', arr)
+    },
+
+    /* 获取图库列表 */
+    async getPublicTableList() {
+      let params = {
+        PageSize: this.page_size,
+        CurrentIndex: this.page_no,
+        Source: this.table_source,
+        KeyWord: this.search_txt,
+        IsShowMe: this.isShowMe
+      };
+
+      let res = await sheetInterface.sheetList(params)
+
+      if (res.Ret !== 200) return;
+      this.haveMove = res.Data ? this.page_no < res.Data.Paging.Pages : false;
+      this.tablePublicList = res.Data
+        ? this.page_no === 1
+          ? res.Data.List
+          : [...this.tablePublicList, ...res.Data.List]
+        : [];
+      this.haveData = this.tablePublicList.length ? true : false;
+
+    },
+
+    loadMove() {
+      this.page_no++;
+      this.getPublicTableList();
+    },
+
+    searchHandle() {
+      this.page_no = 1;
+      if (this.$refs.scrollCont) this.$refs.scrollCont.scrollTop = 0;
+      this.getPublicTableList();
+    },
+
+    init() {
+      this.checkList=[]
+      this.page_no = 1;
+      if (this.$refs.scrollCont) this.$refs.scrollCont.scrollTop = 0;
+      this.search_txt = '';
+    },
+    cancelHandle() {
+      this.init();
+      this.$emit('showChange', false)
+    },
+  },
+  created() { },
+  mounted() { },
+};
+</script>
+<style lang="scss">
+.choose-chart-container {
+  .el-col-5 {
+    width: 20%;
+  }
+  .el-card .el-card__header,
+  .el-card__body {
+    padding: 10px;
+  }
+  .el-dialog--center .el-dialog__body {
+    padding: 25px 0 10px;
+  }
+  .dialog-top {
+    padding: 0 25px;
+  }
+  .choose-dialog-min {
+    margin-top: 20px;
+    .chart-public-list {
+      display: flex;
+      flex-wrap: wrap;
+      /* height: 650px; */
+      max-height: 650px;
+      height: 65vh;
+      padding: 0 0 0 20px;
+      overflow: hidden;
+      overflow-y: auto;
+      .el-card__header {
+        box-shadow: none !important;
+      }
+      .dragShdow {
+        box-shadow: 0 1px 8px rgba(64, 158, 255, 0.8);
+        opacity: 0.5;
+      }
+      .public-chart-item {
+        .item-top {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          font-size: 15px;
+          font-weight: 600;
+        }
+        .chart-img {
+          width: 100%;
+          height: 180px;
+          object-fit: fill !important;
+        }
+        .item-bottom {
+          margin-top: 10px;
+          font-size: 12px;
+          display: flex;
+          justify-content: space-between;
+          color: #666;
+          .join_txt {
+            color: #409eff;
+            cursor: pointer;
+          }
+        }
+      }
+      .select_box {
+        border-color: #409eff !important;
+      }
+    }
+    .nodata {
+      height: 500px;
+      text-align: center;
+      font-size: 14px;
+      color: #666;
+    }
+  }
+}
+.dia-bot {
+  display: flex;
+  justify-content: center;
+  margin: 40px 0;
+}
+</style>

+ 81 - 0
src/views/BI_manage/components/SetCommon.vue

@@ -0,0 +1,81 @@
+<template>
+  <el-dialog
+    title="设置公共看板"
+    :visible.sync="show"
+    :modal-append-to-body="false"
+    :close-on-click-modal="false"
+    :center="true"
+    v-dialogDrag
+    custom-class="dialogclass"
+    width="680px"
+    @close="handleClose"
+  >
+    <div class="BI-share-wrap">
+      <div class="board-title">
+        <div style="flex:1">看板名称</div>
+        <el-tag size="mini">审批状态</el-tag>
+      </div>
+      <el-select placeholder="请选择分类" style="width:100%;margin:20px 0"></el-select>
+      <div class="tips">提示:个人看板设为公共看板,请先选择公共看板分类,提交审批,审批通过后可设置公开!</div>
+      <div class="tips">提示:设置公开看板失败,若要编辑,请先执行撤销操作!<el-button type="text">撤销</el-button></div>
+      <div class="tips">提示:审批通过后,个人看板则设置公开成功!</div>
+      <div class="tips">提示:设置公开看板成功,若要编辑,请先撤销操作(即取消公开)!<el-button type="text">撤销</el-button></div>
+      
+      <div class="dia-bot">
+        <el-button
+          type="primary"
+          plain
+          @click="handleClose"
+          style="margin-right: 20px"
+          >{{ $t("Dialog.cancel_btn") }}</el-button
+        >
+        <el-button type="primary" @click="saveHandle">提交审批</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: "setCommon",
+  model: {
+    prop: 'show',
+    event: 'showChange'
+  },
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+    }
+  },
+  created() {
+  },
+  methods: {
+    handleClose() {
+      this.$emit('showChange', false)
+    },
+    
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.select-user-box {
+  margin-top: 20px;
+  padding: 20px;
+  border: 1px dashed #c8cdd9;
+}
+.board-title{
+  display: flex;
+  
+}
+.dia-bot {
+  display: flex;
+  justify-content: center;
+  margin: 40px 0;
+}
+</style>

+ 111 - 0
src/views/BI_manage/components/SetShare.vue

@@ -0,0 +1,111 @@
+<template>
+  <el-dialog
+    title="共享当前看板"
+    :visible.sync="show"
+    :modal-append-to-body="false"
+    :close-on-click-modal="false"
+    :center="true"
+    v-dialogDrag
+    custom-class="dialogclass"
+    width="68 0px"
+    @close="handleClose"
+  >
+    <div class="BI-share-wrap">
+      <div>
+        <span style="color: #0052d9">共享看板</span>
+        <el-switch v-model="open"> </el-switch>
+        <el-cascader
+          v-model="select_users"
+          :options="researcherList"
+          :show-all-levels="false"
+          filterable
+          :props="{
+            value: 'ItemId',
+            label: 'ItemName',
+            children: 'Children',
+            expandTrigger: 'hover',
+            emitPath: false,
+            multiple: true,
+          }"
+          clearable
+          placeholder="选择共享人"
+          :key="cascaderIdx"
+          @remove-tag="removeResearchersChange"
+        />
+      </div>
+      <div class="select-user-box">
+        <el-tag
+          v-for="tag in select_users"
+          :key="tag.name"
+          closable
+        >
+          {{ tag.name }}
+        </el-tag>
+      </div>
+
+      <div class="dia-bot">
+        <el-button
+          type="primary"
+          plain
+          @click="handleClose"
+          style="margin-right: 20px"
+          >{{ $t("Dialog.cancel_btn") }}</el-button
+        >
+        <el-button type="primary" @click="saveHandle">{{
+          $t("Dialog.confirm_save_btn")
+        }}</el-button>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { dataAuthInterface } from '@/api/api.js';
+export default {
+  name: "setShare",
+  model: {
+    prop: 'show',
+    event: 'showChange'
+  },
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      open: false,
+      select_users: [],
+      researcherList: [],
+      cascaderIdx: 1
+    }
+  },
+  created() {
+    this.getSystemUserList()
+  },
+  methods: {
+    handleClose() {
+      this.$emit('showChange', false)
+    },
+    async getSystemUserList() {
+      const res = await dataAuthInterface.userSearch();
+      if (res.Ret !== 200) return
+      this.researcherList = res.Data || []
+    }
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.select-user-box {
+  margin-top: 20px;
+  padding: 20px;
+  border: 1px dashed #c8cdd9;
+}
+.dia-bot {
+  display: flex;
+  justify-content: center;
+  margin: 40px 0;
+}
+</style>

+ 63 - 0
src/views/BI_manage/components/TableBox.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="table-box" v-if="compData">
+    <div class="top-title-box">
+      <div class="title">{{compData.ExcelName}}</div>
+      <div class="opt-box">
+        <img class="icon" src="~@/assets/img/icons/refresh_blue_new.png" alt="">
+        <slot name="drag"></slot>
+        <img class="icon" src="~@/assets/img/icons/delete-red.png" alt="">
+      </div>
+    </div>
+    <img class="bg" :src="compData.ExcelImage" alt="">
+  </div>
+</template>
+
+<script>
+export default {
+  props:{
+    compData:null
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.table-box{
+  width: 100%;
+  height: 100%;
+  padding: 20px;
+  box-sizing: border-box;
+  .top-title-box{
+    display: flex;
+    margin-bottom: 10px;
+    .title{
+      font-size: 20px;
+      font-weight: bold;
+      flex: 1;
+      &::before{
+        content:'';
+        display: inline-block;
+        width: 4px;
+        height: 20px;
+        background-color: #0052D9;
+        position: relative;
+        top: 2px;
+        margin-right: 5px;
+      }
+    }
+    .opt-box{
+      flex-shrink: 0;
+      margin-left: 10px;
+      .icon{
+        width: 24px;
+        height: 24px;
+        margin-left: 5px;
+      }
+    }
+  }
+  .bg{
+    width: 100%;
+    height: 200px;
+  }
+
+}
+</style>

+ 84 - 0
src/views/BI_manage/editBoard.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="edit-BI-board-page">
+    <div class="top-box">
+      <el-input placeholder="请输入看板名称" v-model="name" style="width:300px;margin-right:20px"></el-input>
+      <el-button type="primary" plain @click="showSelectTable=true">添加表格</el-button>
+      <el-button type="primary" plain @click="showSelectChart=true">添加图表</el-button>
+      <div class="right-btns">
+        <el-button type="primary" plain @click="$router.back()">取消</el-button>
+        <el-button type="primary">保存</el-button>
+      </div>
+    </div>
+    <!-- 看板内容模块 -->
+    <BIBoardContent :dataList="boardData" @sortChange="handleSortChange" />
+
+    <!-- 选择图表 -->
+    <SelectChart v-model="showSelectChart" @addChart="handleAddChart"/>
+    <!-- 选择表格 -->
+    <SelectTable v-model="showSelectTable" @addTable="handleAddTable"/>
+  </div>
+</template>
+
+<script>
+import BIBoardContent from './components/BoardContent.vue'
+import SelectChart from './components/SelectChart.vue'
+import SelectTable from './components/SelectTable.vue'
+export default {
+  components:{
+    BIBoardContent,
+    SelectChart,
+    SelectTable
+  },
+  data() {
+    return {
+      name:'',
+      boardData:[],
+
+      showSelectChart:false,
+      showSelectTable:false
+    }
+  },
+  methods: {
+    handleSortChange(arr){
+      // 因为分页加载的 可能arr比 boardData短
+      if(arr.length<this.boardData.length){
+        const temArr=this.boardData.slice(this.arr.length)
+        this.boardData=[...arr,...temArr]
+      }else{
+        this.boardData=arr
+      }
+      
+    },
+    handleAddChart(e){
+  
+    },
+    handleAddTable(e){
+      const arr=e||[]
+      arr.forEach(item => {
+        const obj={
+          type:2,
+          ...item
+        }
+        this.boardData.push(obj)
+      });
+      
+      this.showSelectTable=false
+    }
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.edit-BI-board-page{
+  $border-color: #c8cdd9;
+  background-color: #fff;
+  border: 1px solid $border-color;
+  .top-box{
+    padding: 14px 20px;
+    border-bottom: 1px solid $border-color;
+    .right-btns{
+      float: right;
+    }
+  }
+}
+</style>

+ 194 - 0
src/views/BI_manage/index.vue

@@ -0,0 +1,194 @@
+<template>
+  <div class="BI-page">
+    <div class="top-nav-box">
+      <div class="nav-btns">
+        <el-button
+          class="nav-btn"
+          type="primary"
+          :plain="navType !== 1"
+          @click="handleNavTypeChange(1)"
+          >我的看板</el-button
+        >
+        <el-button
+          class="nav-btn"
+          type="primary"
+          :plain="navType !== 2"
+          @click="handleNavTypeChange(2)"
+          >共享看板</el-button
+        >
+        <el-button
+          class="nav-btn"
+          type="primary"
+          :plain="navType !== 3"
+          @click="handleNavTypeChange(3)"
+          >公共看板</el-button
+        >
+      </div>
+      <!-- 添加看板 -->
+      <div class="right-btn-box" v-if="navType === 1">
+        <el-button type="primary" @click="$router.push('/editBIBoard')">添加看板</el-button>
+      </div>
+    </div>
+    <div class="opt-box">
+      <el-cascader clearable></el-cascader>
+      <div class="right-opt-box">
+        <el-button type="text" @click="showSetCommon = true"
+          >设置公共</el-button
+        >
+        <el-button type="text" @click="showSetShare = true">设置共享</el-button>
+        <el-button type="text">编辑</el-button>
+        <el-button type="text" style="color: #f00">删除</el-button>
+      </div>
+    </div>
+    <!-- 看板内容模块 -->
+    <BIBoardContent :dataList="boardData" />
+
+    <!-- 设置共享 -->
+    <set-share v-model="showSetShare" />
+    <!-- 设置公共 -->
+    <SetCommon v-model="showSetCommon" />
+  </div>
+</template>
+
+<script>
+import BIBoardContent from './components/BoardContent.vue'
+import SetCommon from './components/SetCommon.vue'
+import SetShare from './components/SetShare.vue'
+
+export default {
+  components: { BIBoardContent, SetShare, SetCommon },
+  data() {
+    return {
+      navType: 1,// 
+
+      showSetShare: false,
+      showSetCommon: false,
+
+      boardData: [
+        {
+          type: 1,
+          UniqueCode: 1
+        },
+        {
+          type: 2,
+          UniqueCode: 2
+        },
+        {
+          type: 2,
+          UniqueCode: 3
+        },
+        {
+          type: 1,
+          UniqueCode: 4
+        },
+        {
+          type: 1,
+          UniqueCode: 5
+        },
+        {
+          type: 2,
+          UniqueCode: 6
+        },
+        {
+          type: 1,
+          UniqueCode: 7
+        },
+        {
+          type: 2,
+          UniqueCode: 8
+        },
+        {
+          type: 2,
+          UniqueCode: 9
+        },
+        {
+          type: 1,
+          UniqueCode: 10
+        },
+        {
+          type: 2,
+          UniqueCode: 11
+        },
+        {
+          type: 1,
+          UniqueCode: 12
+        },
+        {
+          type: 2,
+          UniqueCode: 13
+        },
+        {
+          type: 1,
+          UniqueCode: 14
+        },
+        {
+          type: 2,
+          UniqueCode: 15
+        },
+        {
+          type: 1,
+          UniqueCode: 16
+        },
+        {
+          type: 1,
+          UniqueCode: 17
+        },
+        {
+          type: 2,
+          UniqueCode: 18
+        },
+        {
+          type: 1,
+          UniqueCode: 19
+        },
+        {
+          type: 2,
+          UniqueCode: 20
+        }
+      ]
+    }
+  },
+  methods: {
+    handleNavTypeChange(e) {
+      this.navType = e
+    }
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.BI-page {
+  $border-color: #c8cdd9;
+  background-color: #fff;
+  border: 1px solid $border-color;
+  .top-nav-box {
+    padding: 14px 20px;
+    border-bottom: 1px solid $border-color;
+    position: relative;
+    .nav-btns {
+      position: relative;
+      bottom: -15px;
+      .nav-btn {
+        border-bottom: none;
+        border-bottom-left-radius: 0;
+        border-bottom-right-radius: 0;
+      }
+    }
+    .right-btn-box {
+      position: absolute;
+      right: 20px;
+      top: 14px;
+      border-left: 1px solid $border-color;
+      padding-left: 10px;
+    }
+  }
+  .opt-box {
+    padding: 14px 20px;
+    border-bottom: 1px solid $border-color;
+    .right-opt-box {
+      float: right;
+    }
+  }
+}
+</style>
+<!-- 1+3+1+1+2+3+2=13 (后台) -->