瀏覽代碼

产品及价值曲线

zhuliu 2 年之前
父節點
當前提交
ed2eb35c4c
共有 47 個文件被更改,包括 13837 次插入8 次删除
  1. 3 1
      RMS-FrontEnd/src/api/index.js
  2. 208 0
      RMS-FrontEnd/src/api/product.js
  3. 16 0
      RMS-FrontEnd/src/assets/css/selectButton.scss
  4. 1 1
      RMS-FrontEnd/src/main.js
  5. 91 2
      RMS-FrontEnd/src/router/index.js
  6. 21 1
      RMS-FrontEnd/src/views/components/articles/ContrastIndex.vue
  7. 262 0
      RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/components/MultipleLine/index.vue
  8. 7 0
      RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/components/echarts.js
  9. 206 0
      RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/components/echarts.vue
  10. 24 0
      RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/index.vue
  11. 307 0
      RMS-FrontEnd/src/views/components/articles/components/history/permit.vue
  12. 20 3
      RMS-FrontEnd/src/views/components/articles/index.vue
  13. 756 0
      RMS-FrontEnd/src/views/components/common/setting/mixins.js
  14. 446 0
      RMS-FrontEnd/src/views/components/common/setting/style.vue
  15. 7 0
      RMS-FrontEnd/src/views/layout/components/UserBar.vue
  16. 409 0
      RMS-FrontEnd/src/views/product/components/category.vue
  17. 275 0
      RMS-FrontEnd/src/views/product/components/echarts/components/MultipleLine/index.vue
  18. 7 0
      RMS-FrontEnd/src/views/product/components/echarts/components/echarts.js
  19. 254 0
      RMS-FrontEnd/src/views/product/components/echarts/components/echarts.vue
  20. 29 0
      RMS-FrontEnd/src/views/product/components/echarts/index.vue
  21. 142 0
      RMS-FrontEnd/src/views/product/components/framework/framework.vue
  22. 366 0
      RMS-FrontEnd/src/views/product/components/framework/frameworkTree.vue
  23. 35 0
      RMS-FrontEnd/src/views/product/components/framework/index.vue
  24. 43 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/dom.js
  25. 450 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/index.vue
  26. 8 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/types.js
  27. 39 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/util.js
  28. 357 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/js/jsmind.draggable.js
  29. 2930 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/js/jsmind.js
  30. 349 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/js/jsmind.screenshot.js
  31. 159 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/style/jsmind.css
  32. 1489 0
      RMS-FrontEnd/src/views/product/components/jsMind/components/mind.vue
  33. 23 0
      RMS-FrontEnd/src/views/product/components/jsMind/index.vue
  34. 353 0
      RMS-FrontEnd/src/views/product/components/marketingData.vue
  35. 23 0
      RMS-FrontEnd/src/views/product/components/mixins.js
  36. 662 0
      RMS-FrontEnd/src/views/product/components/product.vue
  37. 206 0
      RMS-FrontEnd/src/views/product/components/productCollapse.vue
  38. 493 0
      RMS-FrontEnd/src/views/product/components/products.vue
  39. 670 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/SelectedPatent.vue
  40. 141 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/dialog/PatentViewField.vue
  41. 272 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/import/SystemTask.vue
  42. 250 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/import/import.vue
  43. 34 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/import/index.vue
  44. 30 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/relatedPatents.vue
  45. 897 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/components/table.vue
  46. 40 0
      RMS-FrontEnd/src/views/product/components/relatedPatents/index.vue
  47. 27 0
      RMS-FrontEnd/src/views/product/index.vue

+ 3 - 1
RMS-FrontEnd/src/api/index.js

@@ -3,10 +3,12 @@ import report from "./report";
 import admin from "./admin";
 import patent from './patent';
 import permission from './permission';
+import product from './product'
 
 export default {
   ...report,
   ...admin,
   ...patent,
-  ...permission
+  ...permission,
+  ...product
 }

+ 208 - 0
RMS-FrontEnd/src/api/product.js

@@ -0,0 +1,208 @@
+import axios from '@/utils/axios'
+/**
+* 分析系统产品管理
+*/
+export default {
+/**
+ * 新增产品类别
+ */
+addNewProductCategorys(data) {
+    return axios.post('/v2/productCategorys/addNew', data)
+},
+/**
+ * 查询产品类别
+ */
+queryProductCategorys(data) {
+    return axios.post('/v2/productCategorys/query', data)
+},
+/**
+ * 修改产品类别
+ */
+updateProductCategorys(data) {
+    return axios.post('/v2/productCategorys/update', data)
+},
+/**
+ * 删除产品类别
+ */
+deleteProductCategorys(params) {
+    return axios.get('/v2/productCategorys/delete', {params})
+  },
+  /**
+ * 产品类别趋势图
+ */
+   showTrend(params) {
+    return axios.post('/v2/productCategorys/showTrend', params)
+  },
+   /**
+ * 获取统一产品下的公司集合
+ */
+getCompanyList(params) {
+    return axios.get('/v2/productCategorys/getCompanyList', {params})
+},
+       /**
+ * 获取营销数据的地区集合
+ */
+getAreaList(params) {
+    return axios.post('/v2/productCategorys/getAreaList', params)
+},
+/**
+ * 新增产品
+ */
+addNewProducts(data) {
+    return axios.post('/v2/products/addNew', data)
+},
+/**
+ * 修改产品
+ */
+updateProducts(data) {
+    return axios.post('/v2/products/update', data)
+},
+/**
+ * 查询产品
+ */
+queryProducts(data) {
+    return axios.post('/v2/products/query', data)
+},
+/**
+ * 删除产品
+ */
+deleteProducts(params) {
+    return axios.get('/v2/products/delete', {params})
+},
+  /**
+ * 产品趋势图
+ */
+   ProductShowTrend(params) {
+    return axios.post('/v2/products/showTrend', params)
+  },
+/**
+ * 新增产品架构
+ */
+addNewStructures(data) {
+    return axios.post('/v2/structures/addNew', data)
+},
+/**
+ * 修改产品架构
+ */
+updateStructures(data) {
+    return axios.post('/v2/structures/update', data)
+},
+/**
+ * 查询产品架构
+ */
+queryStructures(data) {
+    return axios.post('/v2/structures/query', data)
+},
+/**
+ * 删除产品架构
+ */
+deleteStructures(params) {
+    return axios.get('/v2/structures/delete', {params})
+},
+/**
+ * 查询产品架构路径
+ */
+queryPathStructureName(data) {
+    return axios.post('/v2/structures/queryPathStructureName', data)
+},
+/**
+ * 新增营销数据
+ */
+ addNewProductMarketData(params) {
+    return axios.post('/v2/ProductMarketDatas/addNew', params)
+},
+/**
+ * 查询营销数据
+ */
+ queryProductMarketData(params) {
+    return axios.post('/v2/ProductMarketDatas/query', params)
+},
+/**
+ * 修改营销数据
+ */
+ updateProductMarketData(params) {
+    return axios.post('/v2/ProductMarketDatas/update', params)
+},
+/**
+ * 删除营销数据
+ */
+ deleteProductMarketData(params) {
+    return axios.get('/v2/ProductMarketDatas/delete', {params})
+},
+
+/**
+ * 新增许可历史
+ */
+ addPermissionRecord(params) {
+    return axios.post('/v2/permissionRecord/add', params)
+},
+/**
+ * 查询许可历史
+ */
+ queryPermissionRecord(params) {
+    return axios.post('/v2/permissionRecord/query', params)
+},
+/**
+ * 修改许可历史
+ */
+ updatePermissionRecord(params) {
+    return axios.post('/v2/permissionRecord/update', params)
+},
+/**
+ * 删除许可历史
+ */
+ deletePermissionRecord(params) {
+    return axios.get('/v2/permissionRecord/delete', {params})
+},
+
+/**
+ * 更新专利与产品标引
+ */
+ updateProductStructurePatentIndex(params) {
+    return axios.post('/v2/productStructurePatentIndex/update', params)
+},
+
+/**
+ * 查询专利与产品标引
+ */
+ queryProductStructurePatentIndex(params) {
+    return axios.post('/v2/productStructurePatentIndex/query', params)
+},
+ 
+
+/**
+ * 查询产品相关专利
+ */
+proPatentQueryPatents(data) {
+    return axios.post('/v2/proPatent/queryPatents', data)
+},
+/**
+ * 上传导入产品相关专利
+ */
+proPatentImportPatents(data) {
+    return axios.post('/v2/proPatent/importPatents', data)
+  },
+/**
+ * 删除选中的产品相关专利
+ */
+proPatentDelete(data) {
+    return axios.post('/v2/proPatent/delete', data)
+  },
+/**
+ * 添加选中的产品专利
+ */
+addPatentDelete(data) {
+    return axios.post('/v2/proPatent/add', data)
+  },
+
+/**
+ * 产品相关专利新增部分专利查询
+*/
+PatentWorth(params) {
+    return axios.post('/v2/patent/showTrend', params)
+},
+
+
+}
+
+

+ 16 - 0
RMS-FrontEnd/src/assets/css/selectButton.scss

@@ -0,0 +1,16 @@
+.selectButton{
+    width:100%;
+    display:flex;
+    align-items: center;
+    .el-select{
+        width:100%;
+    }
+    .el-input__inner{
+        border-radius: 4px 0 0 4px;
+    }
+    .el-button{
+        background-color: #f5f7fa;
+        border-left: none;
+        border-radius: 0 4px 4px 0;
+    }
+}

+ 1 - 1
RMS-FrontEnd/src/main.js

@@ -27,7 +27,7 @@ Vue.prototype.$permission = hasPermission
 Vue.config.productionTip = false
 Vue.prototype.$constants = constants
 Vue.prototype.$api = api
-// Vue.prototype.$echarts = echarts
+Vue.prototype.$echarts = echarts
 Vue.prototype.$d = formatTableDate
 Vue.prototype.$p = Config.staticURL
 Vue.prototype.$p2 = Config.staticURL2

+ 91 - 2
RMS-FrontEnd/src/router/index.js

@@ -241,14 +241,103 @@ const routes = [
           // hiddenHeader:true
         },
         component:() => import('@/views/report/components/addPatentList'),
-      }
+      },
+      //产品管理
+      {
+        path: "/product",
+        meta: {
+          title: '产品管理',
+          button: [],
+          aside: true,
+          hiddenHeader:true
+        },
+        component: () => import('@/views/product'),
+      
+      },
+       {
+         path: "/frameworkIndex",
+         meta: {
+          title: '产品架构',
+          button: [],
+          aside: true,
+          hiddenHeader:true
+        },
+        component: () => import('@/views/product/components/framework/index.vue'),
+      
+      },
+       {
+         path: "/relatedPatentsIndex",
+         meta: {
+          title: '产品相关价值专利',
+          button: [],
+          aside: true,
+          hiddenHeader:true
+        },
+        component: () => import('@/views/product/components/relatedPatents/index.vue'),
+      
+      },
+       {
+         path: "/relatedPatentsImport",
+         meta: {
+          title: '产品相关专利上传',
+          button: [],
+          aside: true,
+          hiddenHeader:true
+        },
+        component: () => import('@/views/product/components/relatedPatents/components/import/index.vue'),
+      
+      },
+       {
+         path: "/articlesContrastIndex/:id",
+         meta: {
+          title: '专利详情',
+          button: [],
+          aside: true,
+          hiddenHeader:true
+        },
+        component: () => import('@/views/components/articles/ContrastIndex.vue'),
+      
+      },
+      
+      {
+        path: "/mindIndex",
+        meta: {
+         title: '可视化',
+         button: [],
+         aside: true,
+         hiddenHeader:true
+       },
+       component: () => import('@/views/product/components/jsMind'),
+     
+     },
+     {
+      path: "/chartIndex",
+      meta: {
+       title: '趋势图',
+       button: [],
+       aside: true,
+       hiddenHeader:true
+     },
+     component: () => import('@/views/product/components/echarts'),
+    },
+    {
+      path: "/patentWorth",
+      meta: {
+       title: '专利价值曲线',
+       button: [],
+       aside: true,
+       hiddenHeader:true
+     },
+     component: () => import('@/views/components/articles/components/history/components/echarts'),
+    },
     ]
   },
   {
     path: '/identificationCode',
     name:"identificationCode",
     meta:{
-      allowPath:true
+      allowPath:true,
+      aside: true,
     },
     component: () => import('@/views/components/identificationCode'),
   },

+ 21 - 1
RMS-FrontEnd/src/views/components/articles/ContrastIndex.vue

@@ -47,7 +47,7 @@
         </el-header>
         <el-main class="patent-articles-content" style="padding-left:30px">
           <div class="patent-articles-content-left" :style="{ width: showRight ? 'calc(100% - 321px)' : 'calc(100% - 21px)' }">
-            <component :is="componentName" :type1='type' :isTrue="isTrue" @openContrast='openContrast' :project-id="projectId" :patent="patent" :patent-id="patentId" :signPatentNo="signPatentNo" :patentNo="patentNo" :publicNo="publicNo" :reportId="reportId" @refresh="getPatent(patentId)"></component>
+            <component :is="componentName" :type1='type' :isTrue="isTrue" @openContrast='openContrast' :project-id="projectId" :patent="patent" :patent-id="patentId" :signPatentNo="signPatentNo" :patentNo="this.activeMenu == 'patentWorth'?[patentNo]:patentNo" :publicNo="publicNo" :domId="patentNo + '2'" :reportId="reportId" @refresh="getPatent(patentId)"></component>
           </div>
           <div class="patent-articles-content-right" v-if="showRight">
             <el-container>
@@ -95,6 +95,10 @@ import Litigation from './components/history/litigation.vue'
 import Other from './components/history/other.vue'
 import { getPatentCountry } from "@/utils";
 
+import Permit from './components/history/permit.vue'
+import ProductManage from '@/views/product/components/product.vue'
+import PatentWorth from './components/history/components/echarts/components/echarts.vue'
+
 export default {
   mixins: [patentKeywordsHighlight, changeTranslation],
   components: {
@@ -115,6 +119,9 @@ export default {
     Invalid,
     Litigation,
     Other,
+    Permit,
+    ProductManage,
+    PatentWorth
   },
   props:['patentMessage','publicNo','signPatentNo','reportId','type','isTrue',"taskId","patentStatus2A"],
   // props: {
@@ -178,6 +185,18 @@ export default {
           value: 'PatentExport',
           label: '导出文档'
         },
+        {
+          value:'Permit',
+          label:'许可历史'
+        },
+        {
+          value:'ProductManage',
+          label:'相关产品'
+        },
+        {
+          value:'patentWorth',
+          label:'价值曲线'
+        }
       ],
       menuList2: [
         {
@@ -351,6 +370,7 @@ export default {
 
     },
     handleSelect(index) {
+      
       if (index === 'PatentExport') {
         const router = this.$router.resolve({
           path: '/export/patent',

+ 262 - 0
RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/components/MultipleLine/index.vue

@@ -0,0 +1,262 @@
+<template>
+   <div style="background:#f4f4f4;padding:15px">
+    <div style="display:flex;justify-content: flex-end;cursor: pointer;;margin-bottom: -27px;margin-right:8px" :style="{ width: widths }">
+      <div class="set2">
+        <div>
+          <i class="el-icon-refresh" style="font-size:18px;" @click="review"></i>
+        </div>
+        <p class="text">还原</p>
+      </div>
+      <div class="set">
+        <div>
+          <i class="el-icon-setting" style="font-size:18px" @click="set"></i>
+        </div>
+        <p class="text">设置图表格式</p>
+      </div>
+    </div>
+    
+    <div :id="domId" :style="{ width: widths, height: heights }" style="background:white"></div>
+    <el-drawer
+            title="图表配置"
+            :visible.sync="drawer"
+            direction="rtl"
+            :before-close="handleClose"
+            size="500px">
+            <div style="width:500px">
+               <Style :form="form"></Style> 
+            </div>
+            
+        </el-drawer>
+  </div>
+</template>
+
+<script>
+import {chartOptionMixins,settingMethod} from '@/views/components/common/setting/mixins'
+import Style from '@/views/components/common/setting/style.vue'
+export default {
+  name: "CMultipleLine",
+  components:{
+        Style
+    },
+  mixins:[chartOptionMixins,settingMethod],
+  props: {
+    width: {
+      type: String,
+      default: '100%'
+    },
+    height: {
+      type: String,
+      default: '500px'
+    },
+    domId:{
+      default: 'multipleLine'
+    },
+   
+  },
+  data() {
+    return {
+      widths:'100%',
+      heights:'500px',
+      drawer:false,
+      changeColor:true,
+      id:this.domId,
+      relation:{},
+      chartData:[]
+    }
+  },
+  computed:{
+    formSetting(){
+      return this.form.setting
+    }
+  },
+  watch:{
+    formSetting(val){
+      this.dispose(this.id)
+      this.heights = val.height + val.heightUnit
+      this.widths = val.width + val.widthUnit
+      var a = this.getOption2()
+      this.$nextTick(() => {
+          this.initChart(this.id, a)
+      })
+   },
+   domId(val){
+    this.id = val
+   }
+  },
+  mounted() {
+  },
+  methods: {
+    handleClose(){
+      this.form = JSON.parse(JSON.stringify(this.form))
+      this.drawer = false
+    },
+    set(){
+      this.drawer = true
+      // this.$emit('form',this.form)
+    },
+    open(chartData,name,relation){
+      this.chartData = chartData
+      this.relation = relation
+      var a = this.getOption(name,relation)
+      this.$nextTick(() => {
+        this.initChart(this.id, a)
+      })
+    },
+    review(){
+      this.dispose(this.id)
+      var a = this.getOption2()
+      this.$nextTick(() => {
+          this.initChart(this.id, a)
+        })
+    },
+    getOption2(){
+      this.changeColor = false
+    let {backgroundColor,title, legend, xAxis, yAxis, series, grid,toolbox } = this.get2AxisOption('line', '')
+      return {
+        backgroundColor,
+        title,
+        tooltip: {
+          trigger: 'axis'
+        },
+        legend,
+        grid: grid,
+        xAxis: xAxis,
+        yAxis: yAxis,
+        series: series,
+        toolbox: toolbox
+      }
+  },
+    getOption(name,relation) {
+      this.form.setting.text.text = name
+      this.form.setting.title1 = relation.xStr
+      this.form.setting.title2 = relation.yStr+'/万元'
+      // var chartDom1 = document.getElementById(this.domId) 
+      // var myChart1 = this.$echarts.init(chartDom1)
+      var timeList = []
+      var patentNoList = []
+      var data = []
+      this.chartData.forEach(item => {
+        patentNoList.push(item.patentNo?item.patentNo:'全部')
+        data.push({
+          name:item.patentNo?item.patentNo:'全部',
+          type:'line',
+          connectNulls:true,
+          saleVOS:item.saleVOS,
+          data:[]
+        })
+        item.saleVOS.forEach(item1=>{
+          var index = timeList.findIndex(i=>{
+            return item1[relation.x] == i
+          })
+          if(index == -1){
+            timeList.push(item1[relation.x])
+          }
+        })
+      });
+      data.forEach(item=>{
+        if(item.saleVOS && item.saleVOS.length>0){
+          item.saleVOS.forEach(item2=>{
+            var index = timeList.findIndex(i=>{
+              return item2[relation.x] == i
+            })
+            item.data[index] = item2[relation.y]
+          })
+        }
+        
+      });
+      this.selected = {
+        x:timeList.sort((a,b)=>{
+          return a - b
+        }),
+        y:patentNoList,
+        data:data
+      }
+      if(this.changeColor){
+        this.setColor()
+      }
+      let {backgroundColor,title, legend, xAxis, yAxis, series, grid,toolbox } = this.get2AxisOption('line', '')
+      return {
+        backgroundColor,
+        title,
+        tooltip: {
+          trigger: 'axis'
+        },
+        legend,
+        grid: grid,
+        xAxis: xAxis,
+        yAxis: yAxis,
+        series: series,
+        toolbox:toolbox
+      }
+      // var option = {
+      //   title: {
+      //     text: name,
+      //     bottom:0,
+      //     left:'center'
+      //   },
+      //   tooltip: {
+      //     trigger: 'axis'
+      //   },
+      //   legend: {
+      //     data: patentNoList.length>1?patentNoList:[],
+      //   },
+      //   grid: {
+      //     left: '3%',
+      //     right: '4%',
+      //     bottom: '40',
+      //     containLabel: true
+      //   },
+      //   toolbox: {
+      //     feature: {
+      //       saveAsImage: {}
+      //     }
+      //   },
+      //   xAxis: {
+      //     type: 'category',
+      //     data: timeList,
+      //     name: '时间',
+      //     nameLocation: "end"
+      //   },
+      //   yAxis: {
+      //     type: 'value',
+      //     name: '销售额/万元',
+      //     nameLocation: "end"
+      //   },
+      //   series: data
+      // };
+      // myChart1.clear();
+      // myChart1.setOption(option);
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+  .text{
+    display: none;
+    font-size: 12px;
+    color: #4099c6;
+    position: absolute;
+    top: 10px;
+    left: -30px;
+    z-index: 999;
+    width: 200px;
+  }
+  .set{
+    position: relative;
+    // margin-bottom:-20px;
+    z-index: 999;
+    // padding: 10px 0;
+  }
+  .set2{
+    margin-right:6px;
+    position: relative;
+    z-index: 999;
+  }
+  .set2:hover .text{
+    left:0;
+    display: block;
+  }
+  .set:hover .text{
+    display: block;
+  }
+</style>

+ 7 - 0
RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/components/echarts.js

@@ -0,0 +1,7 @@
+import MultipleLine from "./MultipleLine";
+
+export const commonMethods = {
+    components:{
+        MultipleLine
+    }
+}

+ 206 - 0
RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/components/echarts.vue

@@ -0,0 +1,206 @@
+<template>
+    <div style="height:100%;padding:20px">
+        <div>
+            <div style="display:flex;justify-content: space-between;">
+            <el-form :inline="true">
+                <el-form-item label="时间">
+                    <el-select v-model="queryParams.timeUnit" @change="changeEcharts()"  placeholder="请选择时间">
+                        <el-option
+                            v-for="item in timeList"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="地区">
+                    <el-select v-model="queryParams.area" @change="changeEcharts()" clearable  placeholder="请选择地区">
+                        <el-option
+                            v-for="item in areaList"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <!-- <el-form-item label="公司">
+                    <el-select v-model="queryParams.companyName" @change="changeEcharts()" clearable  placeholder="请选择公司">
+                        <el-option
+                            v-for="item in companyList"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item> -->
+                <el-form-item label="趋势图">
+                    <el-select v-model="queryParams.relation" @change="changeRelation()"  placeholder="请选择公司">
+                        <el-option
+                            v-for="item in relationList"
+                            :key="item.value"
+                            :label="item.label.xStr+'与'+item.label.yStr"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+            </el-form>
+            <!-- <div>
+                <el-button type="primary" @click="setting">配置</el-button>
+            </div> -->
+            </div>
+        </div>
+        <div>
+            <!-- <component v-if="type == 1" :is="component" :ref="components" :domId="components" :height="height"></component> -->
+            <component :is='component' :ref="component" :height="height" :form="form" v-bind="$attrs"></component>  
+            
+        </div>
+        <el-drawer
+            title="图表配置"
+            :visible.sync="drawer"
+            direction="rtl"
+            :before-close="handleClose"
+            size="500px">
+            <div style="width:500px">
+               <Style :form="form"></Style> 
+            </div>
+            
+        </el-drawer>  
+    </div>
+</template>
+<script>
+import { commonMethods } from './echarts'
+import Style from '@/views/components/common/setting/style.vue'
+import {settingMethod} from '@/views/components/common/setting/mixins'
+export default {
+    mixins:[commonMethods,settingMethod],
+    props:['patentNo'],
+    components:{
+        Style
+    },
+    data() {
+        return {
+            drawer:false,
+            height:'400px',
+            ChartData:[],
+            component:'MultipleLine',
+            components:'MultipleLineAll',
+            queryParams:{
+                timeUnit:0,
+                relation:1
+            },
+            relationList:[
+                {
+                    label:{
+                        x:'marketDate',
+                        xStr:'时间',
+                        y:'saleTotalMoney',
+                        yStr:'销售额'
+                    },
+                    value:1
+                },
+                {
+                    label:{
+                        x:'marketDate',
+                        xStr:'时间',
+                        y:'customLicenseMoney',
+                        yStr:'自定义许可费'
+                    },
+                    value:2
+                },
+            ],
+            timeList:[
+                {
+                    value:0,
+                    label:'月'
+                },
+                {
+                    value:1,
+                    label:'季度'
+                },
+                {
+                    value:2,
+                    label:'年'
+                },
+            ],
+            areaList:[],
+            companyList:[],
+        }
+    },
+    watch:{
+        patentNo(val){
+            this.init()
+        },
+    },
+    mounted() {
+        this.init()
+    },
+    methods: {
+        handleClose(){
+            this.form = JSON.parse(JSON.stringify(this.form))
+            this.drawer = false
+        },
+        setting(){
+            this.drawer = true
+        },
+        changeRelation(){
+            var relation = this.relationList.find(i=>{
+                return i.value == this.queryParams.relation
+            })
+            this.$refs[this.component].open(this.chartData,'价值曲线',relation.label)
+
+        },
+        async changeEcharts(){
+            await this.getData()
+            this.changeRelation()
+            // this.$refs[this.component].open(this.chartData,'价值曲线')
+        },
+       async init(){
+           await this.getData()
+        //    this.$refs[this.component].open(this.chartData,'价值曲线')
+        this.changeRelation()
+            const [areaList] = await Promise.allSettled([this.getAreaList()])
+            this.areaList= areaList.status =='fulfilled'?areaList.value.map(item=>{
+                return {
+                    value:item,
+                    label:item
+                }
+            }):[]
+            if(this.type == 1){
+                this.getCompanyList()
+            }
+            
+            
+        },
+       async getData(){
+            this.queryParams.patentNoList =this.patentNo
+            this.queryParams.saleArea = this.queryParams.area
+            await this.$api.PatentWorth(this.queryParams).then(response=>{
+                    if(response.code == 200){
+                        this.chartData = response.data
+                    }
+            })
+           
+            
+        },
+        getAreaList(){
+            var params = {
+                patentNoList:this.patentNo
+            }
+            return this.$api.getAreaList(params).then(response=> response.data)
+        },
+        getCompanyList(){
+             this.$api.getCompanyList({id:this.id}).then(response=>{
+                if(response.code == 200){
+                     this.companyList=response.data?response.data.map(item=>{
+                        return {
+                            value:item,
+                            label:item
+                        }
+                    }):[]
+                }
+            }
+            )
+        }
+    },
+}
+</script>

+ 24 - 0
RMS-FrontEnd/src/views/components/articles/components/history/components/echarts/index.vue

@@ -0,0 +1,24 @@
+<template>
+    <div>
+        <chart :patentNo="patentNoList"></chart>
+    </div>
+</template>
+<script>
+import chart from './components/echarts.vue'
+export default{
+    components:{
+        chart
+    },
+    data() {
+        return {
+            
+        }
+    },
+    computed:{
+        patentNoList(){
+            // return this.$route.query.patentNoList
+            return this.$s.getSession('PatentWorth')
+        }
+    }
+}
+</script>

+ 307 - 0
RMS-FrontEnd/src/views/components/articles/components/history/permit.vue

@@ -0,0 +1,307 @@
+<template>
+    <div>
+        <div style="display:flex;justify-content: flex-end;margin-bottom:20px">
+            <!-- <el-button @click="checkEcharts">分析图</el-button> -->
+            <el-button @click="addPermit">添加</el-button>
+        </div>
+        <el-table :data="tableData" border style="width: 100%">
+            <el-table-column label="许可人" prop="licensor" align="center">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{scope.row.licensor}}</span>
+                    <el-input v-else v-model="scope.row.licensor"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="被许可人" prop="licensee" align="center">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{scope.row.licensee}}</span>
+                    <el-input v-else v-model="scope.row.licensee" placeholder="请输入被许可人姓名"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="被许可人国家" prop="licenseeCountry" align="center">
+                <template slot-scope="scope">
+                   <span v-if="!scope.row.vVisible">{{scope.row.licenseeCountry}}</span>
+                   <el-input v-else v-model="scope.row.licenseeCountry" placeholder="请输入被许可人国家"></el-input>
+               </template>
+           </el-table-column>
+           <el-table-column label="许可时间" prop="licenseTime" align="center">
+               <template slot-scope="scope">
+                   <span v-if="!scope.row.vVisible">{{scope.row.licenseTime? scope.row.licenseTime.slice(0,10):'' }}</span>
+                   <el-date-picker style="width:100%" v-else v-model="scope.row.licenseTime" type="date" value-format="yyyy-MM-dd" placeholder="选择日期时间"> </el-date-picker>
+               </template>
+           </el-table-column>
+           <el-table-column label="截止时间" prop="licenseDeadLine" align="center">
+               <template slot-scope="scope">
+                   <span v-if="!scope.row.vVisible">{{scope.row.licenseDeadLine? scope.row.licenseDeadLine.slice(0,10):'' }}</span>
+                   <el-date-picker style="width:100%" v-else v-model="scope.row.licenseDeadLine" type="date" value-format="yyyy-MM-dd" placeholder="选择日期时间"> </el-date-picker>
+               </template>
+           </el-table-column>
+           <el-table-column label="许可费用/万元" prop="licenseFee" align="center">
+               <template slot-scope="scope">
+                   <span v-if="!scope.row.vVisible">{{scope.row.licenseFee}}</span>
+                   <el-input v-else v-model="scope.row.licenseFee" placeholder="请输入许可费用"></el-input>
+               </template>
+           </el-table-column>
+            <el-table-column label="许可类型" prop="licenseType" align="center">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{scope.row.licenseType?permitTypeList.filter(item=>item.value == scope.row.licenseType )[0].label:''}}</span>
+                    <el-select v-else v-model="scope.row.licenseType" placeholder="请选择">
+                       <el-option
+                            v-for="item in permitTypeList"
+                           :key="item.value"
+                           :label="item.label"
+                           :value="item.value">
+                        </el-option>
+                    </el-select>
+                </template>
+            </el-table-column>
+            <el-table-column label="操作" width="160" align="center">
+                <template slot-scope="scope">
+                   <div class="special">
+                       <span v-if="!scope.row.vVisible" class="items" @click="edit(scope.row)">编辑</span>
+                       <span v-else class="items" @click="submit1(scope.row)">保存</span>
+                       <span>&nbsp;|&nbsp;</span>
+                       <span class="items" v-if="!scope.row.vVisible" @click="deleteit(scope.row)">删除</span>
+                       <span v-else class="items" @click="getList">取消</span>
+                   </div>
+               </template>
+            </el-table-column>
+        </el-table>
+        <div style="display: flex;justify-content: center;">
+            <el-pagination
+                background
+                layout="total,prev, pager, next,jumper"
+                :total="total"
+                @current-change="handleCurrentChange"
+                :current-page="queryParams.current" :page-size="queryParams.size">
+            </el-pagination>
+        </div>
+        <el-dialog title="新增许可历史" :visible.sync="permitVisible" width="1000px" :before-close="close">
+            <div>
+                <el-form ref="form" :model="form" :rules="rules">
+                    <el-form-item label="许可人" prop="licensor">
+                        <el-input v-model="form.licensor" placeholder="请输入许可人"></el-input>
+                    </el-form-item>
+                    <el-form-item label="被许可人" prop="licensee">
+                        <el-input v-model="form.licensee" placeholder="请输入被许可人"></el-input>
+                    </el-form-item>
+                    <el-form-item label="被许可人国家" prop="licenseeCountry">
+                        <el-input v-model="form.licenseeCountry" placeholder="请输入被许可人国家"></el-input>
+                    </el-form-item>
+                    <el-form-item label="许可时间" prop="licenseTime">
+                        <el-date-picker
+                            style="width: 100%"
+                            v-model="form.licenseTime"
+                            type="date"
+                            value-format="yyyy-MM-dd"
+                            placeholder="选择日期时间"
+                        >
+                        </el-date-picker>
+                    </el-form-item>
+                    <el-form-item label="截止时间" prop="licenseDeadLine">
+                        <el-date-picker
+                            style="width: 100%"
+                            v-model="form.licenseDeadLine"
+                            type="date"
+                            value-format="yyyy-MM-dd"
+                            placeholder="选择日期时间"
+                        >
+                     </el-date-picker>
+                    </el-form-item>
+                    <el-form-item label="许可费用/万元" prop="licenseFee" >
+                        <el-input v-model="form.licenseFee"  placeholder="请输入许可费用"></el-input>
+                    </el-form-item>
+                    <el-form-item label="许可类型" prop="licenseType">
+                        <el-select v-model="form.licenseType" placeholder="请选择许可类型" style="width:100%">
+                       <el-option
+                            v-for="item in permitTypeList"
+                           :key="item.value"
+                           :label="item.label"
+                           :value="item.value">
+                        </el-option>
+                    </el-select>
+                    </el-form-item>
+                </el-form>
+            </div> 
+            <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
+                <el-button @click="close">取 消</el-button>
+                <el-button type="primary" @click="submit" :loading="btnLoading">确定</el-button>
+            </div> 
+        </el-dialog>
+        <el-dialog title="分析图" :visible.sync="dialogVisible" width="1000px">
+               <div>
+                   <el-form :inline="true">
+                       <el-form-item label="时间">
+                           <el-select v-model="queryParams.time" @change="changeEcharts(1)" placeholder="请选择">
+                                <el-option
+                                    v-for="item in timeList"
+                                    :key="item.value"
+                                   :label="item.label"
+                                   :value="item.value">
+                                </el-option>
+                            </el-select>
+                       </el-form-item>
+                   </el-form>
+               </div>
+               <div id="echarts" style="width:850px;height:300px">
+
+               </div>
+        </el-dialog>
+       </div>
+</template>
+<script>
+import { commonMixins } from "../mixins"
+export default {
+   mixins: [commonMixins],
+   data() {
+       return {
+            permitVisible:false,
+            btnLoading:false,
+           dialogVisible:false,
+           timeList:[],
+           form:{},
+           queryParams:{  
+                patentNo:this.patent.patentNo,  
+                size:10,
+                current:1,
+           },
+           total:0
+,           permitTypeList:[
+               {
+                   label:'独占许可',
+                   value:'1'
+               },
+               {
+                   label:'普通许可',
+                   value:'2'
+               },
+               {
+                   label:'排他许可',
+                   value:'3'
+               },
+           ],
+           tableData:[],
+           rules:{},
+       }
+   },
+   watch:{
+        patent(val){
+            this.queryParams.patentNo = val.patentNo
+            this.getList()
+        }
+   },
+   computed:{
+   },
+   mounted() {
+        this.getList()
+   },
+   methods: {
+        //查询许可历史
+        getList(){
+            this.$api.queryPermissionRecord(this.queryParams).then(response=>{
+                if(response.code == 200){
+                    this.tableData = response.data.list
+                    this.total = response.data.total
+                }
+            })
+        },
+        // 分页
+        handleCurrentChange(val) {
+            this.queryParams.current = val
+            this.getList()
+        },
+        //编辑许可历史
+       edit(row){
+           this.$set(row,'vVisible',!row.vVisible)
+       },
+       //删除许可费率
+       deleteit(row){
+        this.$confirm('确认删除本条数据吗?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+        }).then(() => {
+            this.$api.deletePermissionRecord({id:row.id}).then(response=>{
+                if(response.code == 200){
+                    this.$message.success('删除成功!')
+                    if(this.tableData.length==1&&this.queryParams.current!=1){
+                        this.queryParams.current -= 1
+                    }
+                    this.getList()
+                }else{
+                    this.$message.error('删除失败!')
+                }
+            }).catch(e=>{
+                this.$message.error('删除失败!')
+            })
+        })
+            
+       },
+       //打开添加许可费率弹窗
+       addPermit(){
+            this.form = {
+                patentNo:this.patent.patentNo,
+                licensor:this.patent.applicant.filter(a => a.dataType === 1)[0].name
+            }
+            this.permitVisible = true
+       },
+       //关闭弹框
+       close(){
+            this.form = {}
+            this.btnLoading=false
+            this.permitVisible = false
+       },
+       //保存
+       submit1(row){
+            row.patentNo = this.patent.patentNo
+            this.$api.updatePermissionRecord(row).then(response=>{
+                if(response.code == 200){
+                    this.$message.success('修改成功!')
+                    this.$set(row,'vVisible',!row.vVisible) 
+                }else{
+                    this.$message.error('修改失败!')
+                }
+            }).catch(e=>{
+                this.$message.error('修改失败!')
+            })
+        },
+       //确认添加许可历史
+       submit(){
+            this.$refs.form.validate((valid) => {
+                if (valid) {
+                    this.$api.addPermissionRecord(this.form).then(response=>{
+                        if(response.code == 200){
+                            this.$message.success('添加成功!')
+                            this.getList()
+                            this.close()
+                        }else{
+                            this.$message.error('添加失败!')
+                            this.btnLoading = false
+                        }
+                    }).catch(e=>{
+                        this.$message.error('添加失败!')
+                        this.btnLoading = false
+                    })
+                }else {
+                    console.log('error submit!!');
+                    return false;
+                }
+        })
+       },
+       //查看分析图
+       checkEcharts(row){
+           this.dialogVisible = true
+           this.$nextTick(() => {
+               this.timeList=[]
+               var chartDom = document.getElementById('echarts') 
+               var myChart = this.$echarts.init(chartDom)
+           })
+          
+       }
+   },
+}
+</script>
+<style lang="scss" scoped>
+.items{
+   cursor: pointer;
+}
+</style>

+ 20 - 3
RMS-FrontEnd/src/views/components/articles/index.vue

@@ -77,7 +77,7 @@
             </el-header>
             <el-main class="patent-articles-content" style="padding-left:30px">
               <div class="patent-articles-content-left" :style="{ width: showRight ? 'calc(100% - 321px)' : 'calc(100% - 21px)' }">
-                <component :is="componentName" :reportType="reportType"  @getVisible="getVisible" :project-id="projectId" @isTrues="getIsTrue" :isTrue="isTrue" :type1="type" :patent="patent" :reportId="reportId" :patent-id="patentId" @openContrast='openContrast' @refresh="getPatent(patentId)" ></component>
+                <component :is="componentName" :reportType="reportType"  @getVisible="getVisible" :project-id="projectId" @isTrues="getIsTrue" :isTrue="isTrue" :type1="type" :patent="patent" :patentNo="[patent.patentNo]" :domId="patent.patentNo + '1'" :reportId="reportId" :patent-id="patentId" @openContrast='openContrast' @refresh="getPatent(patentId)" ></component>
               </div>
               <div class="patent-articles-content-right" v-if="showRight">
                 <el-container>
@@ -186,12 +186,14 @@ import File from './components/history/file.vue'
 import Invalid from './components/history/invalid.vue'
 import Litigation from './components/history/litigation.vue'
 import Other from './components/history/other.vue'
-// import Product from './components/history/product.vue'
 import { getPatentCountry } from "@/utils";
 import Import from '../import/index.vue'
-// import {addContrast} from './components/mixins';
 import Features from './components/features.vue'
 
+import Permit from './components/history/permit.vue'
+import ProductManage from '@/views/product/components/product.vue'
+import PatentWorth from './components/history/components/echarts/components/echarts.vue'
+
 export default {
   mixins: [patentKeywordsHighlight, changeTranslation],
   props:['patentNo','taskId1','location','state'],
@@ -215,6 +217,9 @@ export default {
     Invalid,
     Litigation,
     Other,
+    Permit,
+    ProductManage,
+    PatentWorth
     // Product,
 },
   data() {
@@ -288,6 +293,18 @@ export default {
           value: 'PatentExport',
           label: '导出文档'
         },
+        {
+          value:'Permit',
+          label:'许可历史'
+        },
+        {
+          value:'ProductManage',
+          label:'相关产品'
+        },
+        {
+          value:'patentWorth',
+          label:'价值曲线'
+        }
       ],
       menuList2: [
         // {

+ 756 - 0
RMS-FrontEnd/src/views/components/common/setting/mixins.js

@@ -0,0 +1,756 @@
+export const settingData = {
+    props: {
+      form: Object
+    },
+   
+  }
+
+export const settingMethod = {
+    data() {
+        return {
+            form:{
+                setting: {
+                  backgroundColor:'rgb(255 255 255)',
+                  text:{
+                    text:'',
+                    show: true,
+                    link:'',
+                    target: 'blank',
+                    subtext: '',
+                    sublink:'',
+                    subtarget: 'blank',
+                    left: 'center',
+                    bottom: 0,
+                    backgroundColor: 'rgba(0,0,0,0)',
+                    borderColor: "#ccc",
+                    borderWidth: 0,
+                    padding: 5,
+                    itemGap: 10,
+                    textStyle:{
+                      fontSize: 14,
+                      fontWeight: false,
+                      color: '#464646',
+                    },
+                    subtextStyle:{
+                      fontSize: 14,
+                      fontWeight: false,
+                      color: '#464646',
+                    }
+                  },
+                  yMin:0,
+                  splitNumber:'',
+                  yMax:'',
+                  type: 2,
+                  showType: 0,
+                  tableType: 0,
+                  theme: "customed",
+                  titleSize: 14,
+                  dataSize: 14,
+                  axisSize: 14,
+                  dataLabel: false,
+                  dataLabel2: false,
+                  dataLabel3: false,
+                  title1Dir: 0,
+                  title1: "",
+                  title2: "",
+                  dataPosition: "top",
+                  fontFamily: "sans-serif",
+                  fontFamily2: "sans-serif",
+                  fontFamily3: "sans-serif",
+                  fontFamily4: "sans-serif",
+                  fontWeight: false,
+                  fontWeight2: false,
+                  nameLocation: "end",
+                  nameLocation2: "end",
+                  width: "100",
+                  height: "500",
+                  widthUnit: "%",
+                  heightUnit: "px",
+                  splitLine: false,
+                  splitLine2: true,
+                  legend: true,
+                  gridTop: "10",
+                  gridLeft: "10",
+                  gridBottom: "10",
+                  gridRight: "10",
+                  labelColor: "#000000",
+                  legendColor: "#000000",
+                  legendLocation: "top",
+                  legendFontSize: "13",
+                  fontSize: "13",
+                  fontSize2: "13",
+                  fontColor: "#000000",
+                  fontColor2: "#000000",
+                  show: true,
+                  show2: true,
+                  interval: true,
+                  interval2: true,
+                  rotate: "0",
+                  rotate2: "0",
+                  paddingTop: 0,
+                  paddingBottom: 0,
+                  paddingRight: 0,
+                  paddingLeft: 0,
+                  paddingTop2: 0,
+                  paddingLeft2: 0,
+                  paddingBottom2: 0,
+                  paddingRight2: 0,
+                  type2: "scroll",
+                  barWidth: "",
+                  scatterSize: "0",
+                  config: {
+                    color: [],
+                    line: {
+                      name: "",
+                      first: "",
+                      second: "",
+                      operator: 0,
+                      enable: false
+                    },
+                    table: []
+                  }
+                },
+                schema: {
+                  x: {
+                    num: 10
+                  },
+                  y: {
+                    num: 10
+                  },
+                },
+                source: {
+                  x: [],
+                  y: []
+                },
+              },
+            selected:{},
+        }
+    },
+    watch:{
+        form(val){
+            // console.log(val)
+        }
+    },
+    methods: {
+        handleAdd() {
+            let table = []
+            for (let i = 0; i < 10; i++) {
+              table.push({
+                min: 0,
+                max: 0,
+                color: null
+              })
+            }
+            this.form = {
+              setting: {
+                type: 2,
+                showType: 0,
+                tableType: 0,
+                theme: "customed",
+                titleSize: 14,
+                dataSize: 14,
+                axisSize: 14,
+                dataLabel: false,
+                dataLabel2: false,
+                dataLabel3: false,
+                title1Dir: 0,
+                title1: "",
+                title2: "",
+                dataPosition: "top",
+                fontFamily: "sans-serif",
+                fontFamily2: "sans-serif",
+                fontFamily3: "sans-serif",
+                fontFamily4: "sans-serif",
+                fontWeight: false,
+                fontWeight2: false,
+                nameLocation: "end",
+                nameLocation2: "end",
+                width: "100",
+                height: "650",
+                widthUnit: "%",
+                heightUnit: "px",
+                splitLine: false,
+                splitLine2: false,
+                legend: true,
+                gridTop: "0",
+                gridLeft: "0",
+                gridBottom: "0",
+                gridRight: "0",
+                labelColor: "#000000",
+                legendColor: "#000000",
+                legendLocation: "top",
+                legendFontSize: "13",
+                fontSize: "13",
+                fontSize2: "13",
+                fontColor: "#000000",
+                fontColor2: "#000000",
+                show: true,
+                show2: true,
+                interval: true,
+                interval2: true,
+                rotate: "0",
+                rotate2: "0",
+                paddingTop: 0,
+                paddingBottom: 0,
+                paddingRight: 0,
+                paddingLeft: 0,
+                paddingTop2: 0,
+                paddingLeft2: 0,
+                paddingBottom2: 0,
+                paddingRight2: 0,
+                type2: "scroll",
+                barWidth: "",
+                scatterSize: "0",
+                config: {
+                  color: [],
+                  line: {
+                    name: "",
+                    first: "",
+                    second: "",
+                    operator: 0,
+                    enable: false
+                  },
+                  table: table
+                }
+              },
+              schema: {
+                x: {
+                  num: 10
+                },
+                y: {
+                  num: 10
+                },
+              },
+              source: {
+                x: [],
+                y: []
+              },
+            }
+            // this.$store.commit('SET_CHART_FORM', form)
+            // this.drawer = true
+          },
+    },
+}
+export const chartOption = {
+    // mixins: [customPage],
+    props: {
+      width: String,
+      height: String
+    },
+    data() {
+      return {}
+    },
+    methods: {
+      async handleScreenshot(id, name) {
+        const canvas = await html2canvas(document.getElementById(id))
+        const base64 = canvas.toDataURL('image/jpg')
+        downLoadBase64(base64, name + '.png')
+      },
+      getDataLabel() {
+        return {
+          show: this.form.setting.dataLabel,
+          fontSize: this.form.setting.dataSize,
+          position: this.form.setting.dataPosition,
+          fontFamily: this.form.setting.fontFamily2,
+          color: this.form.setting.labelColor,
+          formatter: (val) => {
+            switch (val.seriesType) {
+              case 'sunburst':
+                return val.name
+              case 'heatmap':
+                return val.value[2]
+              case 'tree':
+                return val.data.name
+              case 'scatter':
+                return !val.data[2] ? '' : val.data[2]
+              case 'treemap':
+                return `${val.name} (${val.value})`
+              case 'map':
+                return val.value ? `${val.name} \n ${val.value}` : ''
+              default:
+                if (val.value) {
+                  return val.value
+                } else {
+                  return ''
+                }
+            }
+          }
+        }
+      },
+      getAxisName(dimension) {
+        return this.form.setting[dimension]
+      },
+      getAxisData(dimension) {
+        return this.selected[dimension]
+      },
+      getColor(name) {
+        const color = this.form.setting.config.color.find(item => item.name === name);
+        return color ? color.color : undefined
+      },
+      getDataCount(x, y, b = true) {
+        if (b) {
+          if (this.form.schema.x.type === 6) {
+            const xn = getTreeDataList(this.form.source.x, []).find(item => item.name === x)
+            if (xn) {
+              x = xn.id
+            }
+          }
+          if (this.form.schema.y.type === 6) {
+            const yn = getTreeDataList(this.form.source.y, []).find(item => item.name === y)
+            if (yn) {
+              y = yn.id
+            }
+          }
+        }
+        if (x && y) {
+          if (this.count[x]) {
+            return this.count[x][y] || 0
+          } else {
+            return 0
+          }
+        }
+        if (x && !y) {
+          return this.count[x] || 0
+        }
+        if (!x && !y) {
+          return random(0, 100)
+        }
+      },
+      getSeriesData() {
+        let data = [], count = this.count
+        let source = this.selected.x
+        switch (this.$options.name) {
+          case 'CLine':
+          case 'CArea':
+          case 'CBar':
+          case 'CColumn':
+            source.map(s => {
+              const color = this.form.setting.config.color.find(item => item.name === s);
+              data.push({
+                value: this.getDataCount(s),
+                itemStyle: {
+                  color: color ? color.color : undefined
+                }
+              })
+            })
+            break
+          case 'CDoughnut':
+          case 'CPie':
+            source.map(s => {
+              const color = this.form.setting.config.color.find(item => item.name === s);
+              data.push({
+                value: this.getDataCount(s),
+                name: s,
+                label: {
+                  show: this.form.setting.dataLabel,
+                  fontSize: this.form.setting.dataSize,
+                  fontFamily: this.form.setting.fontFamily2,
+                  color: this.form.setting.labelColor,
+                  position: this.form.setting.dataPosition,
+                  formatter: ({ value, name, percent }) => {
+                    let text = ""
+                    if (this.form.setting.dataLabel) {
+                      text += name
+                    }
+                    if (this.form.setting.dataLabel2) {
+                      text += `-${value}件`
+                    }
+                    if (this.form.setting.dataLabel3) {
+                      text += `/${percent}%`
+                    }
+                    return text
+                  }
+                },
+                itemStyle: {
+                  color: color ? color.color : undefined
+                }
+              })
+            })
+            break
+        }
+        return data
+      },
+      getAxisLabel(type, axis) {
+        return  {
+          interval: axis === 'x' ? (this.form.setting.interval ? 0 : undefined) : (this.form.setting.interval2 ? 0 : undefined),
+          rotate: axis === 'x' ? this.form.setting.rotate : this.form.setting.rotate2,
+          color: axis === 'x' ? this.form.setting.fontColor : this.form.setting.fontColor2,
+          show: axis === 'x' ? this.form.setting.show : this.form.setting.show2,
+          fontSize: axis === 'x' ? this.form.setting.fontSize : this.form.setting.fontSize2,
+          fontFamily: axis === 'x' ? this.form.setting.fontFamily : this.form.setting.fontFamily2,
+          fontWeight: (axis === 'x' ? this.form.setting.fontWeight : this.form.setting.fontWeight2) ? 'bold' : 'normal'
+        }
+      },
+      getNameTextStyle(axis) {
+        return {
+          padding: axis === 'x' ? [this.form.setting.paddingTop, this.form.setting.paddingLeft, this.form.setting.paddingBottom, this.form.setting.paddingRight] : [this.form.setting.paddingTop2, this.form.setting.paddingLeft2, this.form.setting.paddingBottom2, this.form.setting.paddingRight2],
+          color: axis === 'x' ? this.form.setting.fontColor : this.form.setting.fontColor2,
+          fontSize: axis === 'x' ? this.form.setting.fontSize : this.form.setting.fontSize2,
+          fontFamily: axis === 'x' ? this.form.setting.fontFamily : this.form.setting.fontFamily2,
+          fontWeight: (axis === 'x' ? this.form.setting.fontWeight : this.form.setting.fontWeight2) ? 'bold' : 'normal'
+        }
+      },
+      dateTimeSort() {
+        let temp = JSON.parse(JSON.stringify(this.selected.x))
+        let data = []
+        let t, y, m
+        temp.map(item => {
+          switch (this.form.schema.x.expand) {
+            case 9:
+            case 12:
+              data.push({
+                name: item,
+                time: new Date(item).getTime()
+              })
+              break
+            case 10:
+              t = item.split('-')
+              y = t[0]
+              m = (parseInt(t[1].replaceAll('Q', '')) - 1) * 3 + 1
+              data.push({
+                name: item,
+                time: new Date(y + '-' + m).getTime()
+              })
+              break
+            case 11:
+              t = item.split('-')
+              y = t[0]
+              m = parseInt(t[1].replaceAll('H', '')) * 6
+              data.push({
+                name: item,
+                time: new Date(y + '-' + m).getTime()
+              })
+              break
+            case 13:
+            case 14:
+            case 15:
+              t = item.split('-')
+              y = t[0]
+              data.push({
+                name: item,
+                time: new Date(y + '-01').getTime()
+              })
+              break
+          }
+        })
+        this.selected.x = data.sort((a, b) => a.time - b.time).map(item => item.name)
+      },
+      get1AxisOption(type, areaStyle) {
+        if (this.form.schema.x.ptype === 2) {
+          this.dateTimeSort()
+        }
+        let xAxis = {
+          name: this.getAxisName('title1'),
+          nameTextStyle: this.getNameTextStyle('x'),
+          type: 'category',
+          data: this.getAxisData('x'),
+          nameLocation: this.form.setting.nameLocation,
+          splitLine: {
+            show: this.form.setting.splitLine
+          },
+          axisLabel: this.getAxisLabel('category', 'x')
+        }
+        let yAxis = {
+          name: this.getAxisName('title2'),
+          nameTextStyle: this.getNameTextStyle('y'),
+          type: 'value',
+          nameLocation: this.form.setting.nameLocation2,
+          splitLine: {
+            show: this.form.setting.splitLine2
+          },
+          axisLabel: this.getAxisLabel('value', 'y')
+        }
+        let series = [{
+          data: this.getSeriesData(),
+          type: type,
+          areaStyle: areaStyle,
+          itemStyle: {
+            color: this.form.setting.config.color[0].color
+          },
+          barWidth: this.form.setting.barWidth,
+          label: this.getDataLabel()
+        }]
+        let grid = {
+          top: this.form.setting.gridTop + '%',
+          left: this.form.setting.gridLeft + '%',
+          right: this.form.setting.gridRight + '%',
+          bottom: this.form.setting.gridBottom + '%',
+          containLabel: true,
+        }
+        return {
+          xAxis,
+          yAxis,
+          series,
+          grid
+        }
+      },
+      get2AxisOption(type, stack) {
+        if (this.form.schema.x.ptype === 2) {
+          this.dateTimeSort()
+        }
+        const enable = this.form.setting.config.line.enable
+        const operator = this.form.setting.config.line.operator
+        let backgroundColor = this.form.setting.backgroundColor
+        // let series = this.selected.data
+        let series = this.selected.y.map(y => {
+            return {
+              name: y,
+              type: type,
+              stack: stack,
+              itemStyle: {
+                color: this.getColor(y)
+              },
+              barWidth: this.form.setting.barWidth,
+              data: this.selected.data.find(item => item.name == y).data,
+              label: this.getDataLabel(),
+              connectNulls:true,
+            }
+          })
+        let xAxis = {
+          name: this.form.setting.title1,
+          nameTextStyle: this.getNameTextStyle('x'),
+          type: 'category',
+          nameLocation: this.form.setting.nameLocation,
+          data: this.selected.x,
+          splitLine: {
+            show: this.form.setting.splitLine
+          },
+          axisLabel: this.getAxisLabel('category', 'x')
+        }
+        let yAxis = {
+          min:this.form.setting.yMin,
+          splitNumber:this.form.setting.splitNumber,
+          // max:this.form.setting.yMax,
+          name: this.form.setting.title2,
+          nameTextStyle: this.getNameTextStyle('y'),
+          type: 'value',
+          nameLocation: this.form.setting.nameLocation2,
+          splitLine: {
+            show: this.form.setting.splitLine2
+          },
+          axisLabel: this.getAxisLabel('value', 'y')
+        }
+        let legend =this.selected.y.length>1? [...this.selected.y]:[]
+        let toolbox = {
+          show: true,
+          right:'50px',
+          feature:{
+            saveAsImage: {
+              show:true,
+              excludeComponents :['toolbox'],
+              pixelRatio: 2
+            },
+            magicType: {
+              show: true,
+              type: [ 'bar', 'stack']
+            },
+            dataZoom:{
+              show:true
+            },
+            dataView:{
+              show: true,
+              lang: ['数据视图', '关闭', '导出Excel'],
+              optionToContent: function(opt){
+                  //axisData是你想定义的表格第一列的数据,我这里设置为柱形图的x轴数据
+                  var axisData = opt.xAxis[0].data;
+                  //tAxis[0]为你想定义的表格第一行的数据
+                  var txisData = [];
+                  if(opt.series.length>1){
+                    txisData.push('产品名称')
+                  }
+                  txisData.push(opt.xAxis[0].name)
+                  txisData.push(opt.yAxis[0].name)
+                  var series = opt.series;
+                  //表头
+                  var tdHeads = '';
+                  var tdBodys = '';
+                  var nameData = txisData;
+                  for (var i = 0; i < nameData.length ; i++) {
+                      tdHeads += '<td style="padding: 0 10px;width:100px">' + nameData[i] + '</ td >';
+                  }
+                  var table = '<table id="Mytable" border="1" class="table table-bordered table-striped table-hover" style="width:100%;text-align:center" ><tbody><tr>' + tdHeads + ' </tr>';
+                
+                  for(var i = 0; i<series.length;i++){
+                    for(var j =0;j<axisData.length;j++){
+                      if(series.length>1){
+                        if(j==0){
+                          tdBodys += '<td rowspan="'+ axisData.length +'" colspan="1">' + series[i].name + '</td>';
+                        }
+                      }
+                      tdBodys += '<td>' + axisData[j] + '</td>';
+                      var temp = series[i].data[j]
+                      if (temp != null && temp != undefined) {
+                          tdBodys += '<td>' + temp + '</td>';
+                      } else {
+                          tdBodys += '<td>'+ " " +'</td>';
+                      }
+                      table += '<tr style="mso-number-format:'+'\@'+';">' + tdBodys + '</tr>';
+                      tdBodys = '';
+                    }
+                   
+                  }
+                  table += '</tbody></table>';
+                  return table;
+              },
+              contentToOption: function (HTMLDomElement, opt) {
+                    // Worksheet名
+                    const worksheet = 'Sheet1'
+                    const url = 'data:application/vnd.ms-excel;base64,'
+
+                    // 下载的表格模板数据
+                    const template = `<html xmlns:o="urn:schemas-microsoft-com:office:office"
+                      xmlns:x="urn:schemas-microsoft-com:office:excel"
+                      xmlns="http://www.w3.org/TR/REC-html40">
+                      <head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
+                      <x:Name>${worksheet}</x:Name>
+                      <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
+                      </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
+                      ${HTMLDomElement.innerHTML}`
+                    // 下载模板
+                    let link = document.createElement('a')
+                    link.setAttribute('href', url + window.btoa(unescape(encodeURIComponent(template))))
+                    link.setAttribute('download', opt.title[0].text+'.xls')
+                    link.click()
+              },
+
+            }
+          }
+        }
+        // if (enable && ([5, 6].indexOf(this.form.setting.type) !== -1)) {
+        //   let name = this.form.setting.config.line.name
+        //   series.push({
+        //     name: name,
+        //     type: 'line',
+        //     yAxisIndex: 1,
+        //     data: getLineDataArr(this.form, this.selected, this.count),
+        //     itemStyle: {
+        //       color: this.form.setting.config.line.color
+        //     },
+        //     label: {
+        //       ...this.getDataLabel(),
+        //       formatter({ value }) {
+        //         return operator === 1 ? `${Math.round(parseFloat(value) * 100)} %` : value
+        //       }
+        //     }
+        //   })
+        //   yAxis.push({
+        //     type: 'value',
+        //     name: name,
+        //   })
+        //   legend.push(name)
+        // }
+        let grid = {
+          top: this.form.setting.gridTop + '%',
+          left: this.form.setting.gridLeft + '%',
+          right: this.form.setting.gridRight + '%',
+          bottom: this.form.setting.gridBottom + '%',
+          containLabel: true
+        }
+        let title = JSON.parse(JSON.stringify(this.form.setting.text))
+        title.textStyle.fontWeight = this.form.setting.text.textStyle.fontWeight?'bold':'normal'
+        title.subtextStyle.fontWeight = this.form.setting.text.subtextStyle.fontWeight?'bold':'normal'
+        return {
+          backgroundColor,
+          title,
+          legend: {
+            show: this.form.setting.legend,
+            data: legend,
+            textStyle: {
+              fontFamily: this.form.setting.fontFamily3,
+              color: this.form.setting.legendColor,
+              fontSize: this.form.setting.legendFontSize
+            },
+            ...this.getLegendLocation(),
+            type: this.form.setting.type2,
+          },
+          grid,
+          xAxis,
+          yAxis,
+          series,
+          toolbox
+        }
+      },
+      getLegendLocation() {
+        let x = ''
+        let y = ''
+        switch (this.form.setting.legendLocation) {
+          case 'top':
+            x = 'center'
+            y = 'top'
+            break
+          case 'left':
+            x = 'left'
+            y = 'center'
+            break
+          case 'bottom':
+            x = 'center'
+            y = 'bottom'
+            break
+          case 'right':
+            x = 'right'
+            y = 'center'
+            break
+        }
+        return {
+          x: x,
+          y: y,
+          orient: ['top', 'bottom'].indexOf(this.form.setting.legendLocation) !== -1 ? 'horizontal' : "vertical",
+        }
+      },
+      dispose(id){
+        const chartDom = document.getElementById(id);
+        const myChart = this.$echarts.init(chartDom);
+        myChart.dispose();
+      },
+      initChart(id, chartOption) {
+        const chartDom = document.getElementById(id);
+        const myChart = this.$echarts.init(chartDom);
+        myChart.clear();
+        myChart.setOption(chartOption)
+        // this.$store.commit('SET_MY_CHART', myChart)
+        return myChart
+      },
+    }
+  }
+  
+  export const chartOptionMixins = {
+    data() {
+        return {
+            // form:{},
+            selected:{}
+        }
+    },
+    mixins: [chartOption],
+    mounted() {
+    //   this.$nextTick(() => {
+    //     this.initChart(this.id, this.getOption())
+    //   })
+    },
+    methods:{
+        setColor(){
+            const defaultColor = [
+                '#5470c6',
+                '#91cc75',
+                '#fac858',
+                '#ee6666',
+                '#73c0de',
+                '#3ba272',
+                '#fc8452',
+                '#9a60b4',
+                '#ea7ccc',
+              ]
+              let index = 0
+              if (this.selected.y.length>0) {
+                this.form.setting.config.color = this.selected.y.map(item => {
+                  if (index >= defaultColor.length) {
+                    index = 0
+                  }
+                  return {
+                    name: item,
+                    color: defaultColor[index++]
+                  }
+                })
+              }
+        }
+    }
+  }

+ 446 - 0
RMS-FrontEnd/src/views/components/common/setting/style.vue

@@ -0,0 +1,446 @@
+<template>
+    <div class="custom-analyse-tabs-style">
+      <el-collapse v-model="activeNames">
+        <el-collapse-item title="框架" name="8" v-if="!getShow([30])">
+          <div class="content">
+            <div style="display:flex">
+              <span class="label" style="width:50px">宽度</span>
+              <el-input placeholder="请输入宽度" v-model="form.setting.width" size="small" style="width: calc(100% - 170px);padding-left: 10px;"></el-input>
+              <el-select size="small" v-model="form.setting.widthUnit" placeholder="请选择" style="width: calc(100% - 190px);margin-left: 10px;">
+                <el-option label="百分比" value="%"></el-option>
+                <el-option label="像素" value="px"></el-option>
+              </el-select>
+            </div>
+            <div style="margin-top: 10px;display:flex">
+              <span class="label" style="width:50px">高度</span>
+              <el-input placeholder="请输入高度" v-model="form.setting.height" size="small" style="width: calc(100% - 170px);padding-left: 10px;"></el-input>
+              <el-select size="small" v-model="form.setting.heightUnit" placeholder="请选择" style="width: calc(100% - 190px);margin-left: 10px;">
+                <el-option label="百分比" value="%" disabled></el-option>
+                <el-option label="像素" value="px"></el-option>
+              </el-select>
+            </div>
+            <div style="margin-top: 10px;display:flex">
+              <span>背景色</span><el-color-picker size="small" v-model="form.setting.backgroundColor" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="气泡图" name="9" v-if="getShow([])">
+          <div class="content">
+            <span class="label">气泡比例</span>
+            <el-input placeholder="请输入气泡比例" v-model="form.setting.scatterSize" size="small" style="width: calc(100% - 160px);padding-left: 10px;"></el-input>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="柱形图" name="7" v-if="getShow([4, 7, 5, 6, 8, 9])">
+          <div class="content">
+            <span class="label">宽度</span>
+            <el-input placeholder="请输入宽度" v-model="form.setting.barWidth" size="small" style="width: calc(100% - 190px);padding-left: 10px;"></el-input>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="标签" name="1" v-if="!getShow([30, 19])">
+          <div class="content">
+            <div>
+              <el-checkbox v-model="form.setting.dataLabel">显示标签</el-checkbox>
+              <template v-if="!getShow([13, 12, 20, 18, 21, 23])">
+                <span style="margin-left: 42px;" class="label">位置</span>
+                <el-select size="small" v-model="form.setting.dataPosition" placeholder="请选择" style="width: calc(100% - 160px);margin-left: 10px;">
+                  <el-option v-for="item in positionList" :label="item.label" :value="item.value"></el-option>
+                </el-select>
+              </template>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.fontFamily2" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.dataSize" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.labelColor" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+            <div style="margin-top: 10px;" v-if="getShow([10, 11])">
+              <el-checkbox v-model="form.setting.dataLabel2">显示数值</el-checkbox>
+              <el-checkbox v-model="form.setting.dataLabel3">显示百分比</el-checkbox>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="名称" name="10" v-if="!getShow([30, 19])">
+          <div class="content">
+            <el-checkbox v-model="form.setting.text.show">显示名称</el-checkbox>
+              <div style="display:flex;margin-top: 10px;">
+                <span class="label" style="width:80px">主标题名称</span><el-input size="small" v-model="form.setting.text.text" placeholder="请输入" style="margin-left: 10px;"></el-input>
+              </div>
+              <div style="display:flex;margin-top: 10px;">
+                <span class="label" style="width:80px">主标题链接</span><el-input size="small" v-model="form.setting.text.link" placeholder="请输入" style="margin-left: 10px;"></el-input>
+              </div>
+              <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.text.textStyle.fontFamily" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.text.textStyle.fontSize" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.text.textStyle.color" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-checkbox v-model="form.setting.text.textStyle.fontWeight">字体加粗</el-checkbox>
+            </div>
+              <div style="margin-top: 10px;" class="title-padding">
+                <span class="label">位置</span>
+                <span class="label" style="margin: 0 5px;">垂直</span>
+                <el-input v-model.number="form.setting.text.bottom" size="small" placeholder="" style="width: 100px;"></el-input>
+                <span class="label" style="margin: 0 5px;">水平</span>
+                <el-input v-model.number="form.setting.text.left" size="small" placeholder="" style="width: 100px;"></el-input>
+            </div>
+            <div style="display:flex;margin-top: 10px;">
+                <span class="label" style="width:80px">副标题名称</span><el-input size="small" v-model="form.setting.text.subtext" placeholder="请输入" style="margin-left: 10px;"></el-input>
+              </div>
+              <div style="display:flex;margin-top: 10px;">
+                <span class="label" style="width:80px">副标题链接</span><el-input size="small" v-model="form.setting.text.sublink" placeholder="请输入" style="margin-left: 10px;"></el-input>
+              </div>
+              <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.text.subtextStyle.fontFamily" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.text.subtextStyle.fontSize" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.text.subtextStyle.color" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-checkbox v-model="form.setting.text.subtextStyle.fontWeight">字体加粗</el-checkbox>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="颜色" name="2"  v-if="!getShow([20, 18, 21, 23]) && form.setting.config.color &&form.setting.config.color.length>0">
+          <div class="content">
+            <el-container>
+              <el-main style="max-height: 300px;" class="color-config">
+                <template v-if="!getShow([15, 30])">
+                  <div v-for="item in form.setting.config.color" class="color-config-content">
+                    <el-color-picker size="small" v-model="item.color"></el-color-picker>
+                    <span class="name">{{ item.name }}</span>
+                  </div>
+                  <div v-if="form.setting.config.line.enable" class="color-config-content">
+                    <el-color-picker size="small" v-model="form.setting.config.line.color"></el-color-picker>
+                    <span class="name">{{ form.setting.config.line.name }}</span>
+                  </div>
+                </template>
+                <template v-else>
+                  <div v-for="(item, index) in form.setting.config.table">
+                    <el-color-picker size="small" v-model="item.color"></el-color-picker>
+                    <div style="float: right; padding-right: 20px;">
+                      <el-input v-model="item.min" style="width: 80px;" size="small"></el-input>
+                      <span class="o-code">≤</span><span>数值</span><span class="o-code">≤</span>
+                      <el-input v-model="item.max" style="width: 80px;" size="small"></el-input>
+                    </div>
+                  </div>
+                </template>
+              </el-main>
+            </el-container>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="图例" name="3" v-if="getShow([5, 6, 8, 9, 10, 11])">
+          <div class="content">
+            <div>
+              <el-checkbox v-model="form.setting.legend">显示图例</el-checkbox>
+              <span class="label" style="margin-left: 42px;">位置</span>
+              <el-select size="small" v-model="form.setting.legendLocation" style="width: calc(100% - 160px);margin-left: 10px;">
+                <el-option value="top" label="上"></el-option>
+                <el-option value="left" label="左"></el-option>
+                <el-option value="bottom" label="下"></el-option>
+                <el-option value="right" label="右"></el-option>
+              </el-select>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.fontFamily3" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.legendFontSize" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.legendColor" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.type2" style="width: calc(100% - 160px)">
+                <el-option value="scroll" label="滚动图例"></el-option>
+                <el-option value="plain" label="普通图例"></el-option>
+              </el-select>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="距离" name="4" v-if="!getShow([30, 22, 17, 19, 12, 20, 18, 21, 23, 13])">
+          <div class="content">
+            <div>
+              <span class="label">顶部</span>
+              <el-input size="small" v-model="form.setting.gridTop" style="width: 240px;margin-left: 10px;"></el-input>
+              <span style="margin-left: 10px;">%</span>
+            </div>
+            <div style="margin-top: 10px;">
+              <span class="label">底部</span>
+              <el-input size="small" v-model="form.setting.gridBottom" style="width: 240px;margin-left: 10px;"></el-input>
+              <span style="margin-left: 10px;">%</span>
+            </div>
+            <div style="margin-top: 10px;">
+              <span class="label">左侧</span>
+              <el-input size="small" v-model="form.setting.gridLeft" style="width: 240px;margin-left: 10px;"></el-input>
+              <span style="margin-left: 10px;">%</span>
+            </div>
+            <div style="margin-top: 10px;">
+              <span class="label">右侧</span>
+              <el-input size="small" v-model="form.setting.gridRight" style="width: 240px;margin-left: 10px;"></el-input>
+              <span style="margin-left: 10px;">%</span>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="X轴" name="5" v-if="getShow([1, 3, 4, 7, 5, 6, 8, 9, 2, 15, 14])">
+          <div class="content">
+            <div>
+              <el-checkbox v-model="form.setting.show">显示坐标轴</el-checkbox>
+              <el-checkbox v-model="form.setting.splitLine" v-if="!getShow([14])">显示背景线</el-checkbox>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.fontFamily" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.fontSize" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.fontColor" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-checkbox v-model="form.setting.fontWeight">字体加粗</el-checkbox>
+              <span class="label" style="margin-left: 15px;">标题位置</span>
+              <el-select size="small" v-model="form.setting.nameLocation" style="width: calc(100% - 160px);margin-left: 10px;">
+                <el-option value="start" label="左"></el-option>
+                <el-option value="middle" label="中"></el-option>
+                <el-option value="end" label="右"></el-option>
+              </el-select>
+            </div>
+            <div style="margin-top: 10px;">
+              <span class="label">标题</span>
+              <el-input size="small" v-model="form.setting.title1" placeholder="请输入" style="width: calc(100% - 37px);margin-left: 10px;"></el-input>
+            </div>
+            <div style="margin-top: 10px;" class="title-padding">
+              <span class="label">位移</span>
+              <span class="label" style="margin: 0 5px;">上</span>
+              <el-input v-model.number="form.setting.paddingTop" size="small" placeholder="" style="width: 45px;"></el-input>
+              <span class="label" style="margin: 0 5px;">左</span>
+              <el-input v-model.number="form.setting.paddingLeft" size="small" placeholder="" style="width: 45px;"></el-input>
+              <span class="label" style="margin: 0 5px;">下</span>
+              <el-input v-model.number="form.setting.paddingBottom" size="small" placeholder="" style="width: 45px;"></el-input>
+              <span class="label" style="margin: 0 5px;">右</span>
+              <el-input v-model.number="form.setting.paddingRight" size="small" placeholder="" style="width: 45px;"></el-input>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-checkbox v-model="form.setting.interval">完整显示</el-checkbox>
+              <span class="label" style="margin-left: 15px;">倾斜角度</span>
+              <el-input size="small" v-model="form.setting.rotate" placeholder="请输入" style="width: calc(100% - 160px);margin-left: 10px;"></el-input>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="Y轴" name="6" v-if="getShow([1, 3, 4, 7, 5, 6, 8, 9, 2, 15, 14])">
+          <div class="content">
+            <div style="display:flex">
+              <el-checkbox v-model="form.setting.show2">显示坐标轴</el-checkbox>
+              <el-checkbox v-model="form.setting.splitLine2" v-if="!getShow([14])">显示背景线</el-checkbox>
+            </div>
+            <div style="display:flex;margin-top:10px">
+              <div style="display:flex"><span style="width:100px">最小值:</span><el-input size="small" v-model="form.setting.yMin"></el-input></div>
+              <div style="display:flex;align-item:center;margin-left:10px"><span style="width:100px">值间隔:</span><el-input size="small" v-model="form.setting.splitNumber"></el-input></div>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.fontFamily2" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.fontSize2" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.fontColor2" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-checkbox v-model="form.setting.fontWeight2">字体加粗</el-checkbox>
+              <span class="label" style="margin-left: 15px;">标题位置</span>
+              <el-select size="small" v-model="form.setting.nameLocation2" style="width: calc(100% - 160px);margin-left: 10px;">
+                <el-option value="end" label="上"></el-option>
+                <el-option value="middle" label="中"></el-option>
+                <el-option value="start" label="下"></el-option>
+              </el-select>
+            </div>
+            <div style="margin-top: 10px;">
+              <span class="label">标题</span>
+              <el-input size="small" v-model="form.setting.title2" placeholder="请输入" style="width: calc(100% - 37px);margin-left: 10px;"></el-input>
+            </div>
+            <div style="margin-top: 10px;" class="title-padding">
+              <span class="label">位移</span>
+              <span class="label" style="margin: 0 5px;">上</span>
+              <el-input v-model.number="form.setting.paddingTop2" size="small" placeholder="" style="width: 45px;"></el-input>
+              <span class="label" style="margin: 0 5px;">左</span>
+              <el-input v-model.number="form.setting.paddingLeft2" size="small" placeholder="" style="width: 45px;"></el-input>
+              <span class="label" style="margin: 0 5px;">下</span>
+              <el-input v-model.number="form.setting.paddingBottom2" size="small" placeholder="" style="width: 45px;"></el-input>
+              <span class="label" style="margin: 0 5px;">右</span>
+              <el-input v-model.number="form.setting.paddingRight2" size="small" placeholder="" style="width: 45px;"></el-input>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-checkbox v-model="form.setting.interval2">完整显示</el-checkbox>
+              <span class="label" style="margin-left: 15px;">倾斜角度</span>
+              <el-input size="small" v-model="form.setting.rotate2" placeholder="请输入" style="width: calc(100% - 160px);margin-left: 10px;"></el-input>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item title="坐标" name="6" v-if="getShow([17, 22])">
+          <div class="content">
+            <div>
+              <el-checkbox v-model="form.setting.show">显示</el-checkbox>
+              <el-checkbox v-model="form.setting.fontWeight">字体加粗</el-checkbox>
+            </div>
+            <div style="margin-top: 10px;">
+              <el-select size="small" v-model="form.setting.fontFamily" placeholder="请选择" style="width: calc(100% - 160px)">
+                <el-option v-for="item in $constants.fontFamily" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+              <el-input size="small" v-model.number="form.setting.fontSize" type="number" placeholder="请输入" style="width: 105px;margin-left: 10px;"></el-input>
+              <el-color-picker size="small" v-model="form.setting.fontColor" style="margin-left: 10px; float:right;"></el-color-picker>
+            </div>
+          </div>
+        </el-collapse-item>
+      </el-collapse>
+    </div>
+</template>
+  
+<script>
+  import {settingData} from "./mixins";
+  import { detectionChartType } from "@/utils/chart";
+  
+  export default {
+    mixins: [settingData],
+    components: {
+     
+    },
+    data() {
+      return {
+        activeNames: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9','10'],
+        chartIcon: [],
+        positionList: [],
+      }
+    },
+    mounted() {
+      this.chartIcon = JSON.parse(JSON.stringify(this.$constants.chartType))
+      this.chartIcon.map(item => {
+        let { op, cg } = detectionChartType(item, this.form)
+        if (op && cg) {
+          cg = false
+        //   this.$store.dispatch('resetSettingColor')
+        }
+        if (op) {
+          this.$set(item, 'disable', true)
+          this.$set(item, 'active', false)
+        }
+        if (cg) {
+          this.$set(item, 'disable', false)
+          this.$set(item, 'active', true)
+        }
+      })
+    //   this.getPositionList()
+    },
+    methods: {
+      getShow(arr) {
+        return arr.indexOf(this.form.setting.type) !== -1
+      },
+      getPositionList() {
+        if ([10, 11].indexOf(this.form.setting.type) !== -1) {
+          this.positionList = [
+            { label: '外侧', value: 'outside' },
+            { label: '内部', value: 'inside' },
+            { label: '中心', value: 'center' },
+          ]
+        } else {
+          this.positionList = [
+            { label: '上', value: 'top' },
+            { label: '左', value: 'left' },
+            { label: '右', value: 'right' },
+            { label: '下', value: 'bottom' },
+            { label: '内', value: '' },
+          ]
+        }
+      },
+      handleClick(item) {
+        if (item.disable) {
+          return false
+        }
+        const chartType = this.$constants.chartType.find(c => c.value === this.form.setting.type)
+        if (item.value === 21 || this.form.setting.type === 21) {
+          this.$store.commit('SET_RELOAD_DATA', true)
+        }
+        if (item.value === 22 && chartType.type === 1) {
+          this.$store.commit('SET_RELOAD_DATA', true)
+        }
+        if (!chartType) {
+          this.$store.commit('SET_RELOAD_DATA', true)
+        }
+        if ((this.form.schema.x.type === 6 || this.form.schema.y.type === 6) && chartType) {
+          if (chartType.value === 30 && item.value !== 30) {
+            this.$store.commit('SET_RELOAD_DATA', true)
+          }
+          if (chartType.value !== 30 && item.value === 30) {
+            this.$store.commit('SET_RELOAD_DATA', true)
+          }
+          if (chartType.value === 20 && item.value !== 20) {
+            this.$store.commit('SET_RELOAD_DATA', true)
+          }
+          if (chartType.value !== 20 && item.value === 20) {
+            this.$store.commit('SET_RELOAD_DATA', true)
+          }
+        }
+        this.form.setting.type = item.value
+        this.chartIcon.map(c => {
+          if (c.value === item.value) {
+            this.$set(c, 'active', true)
+          } else {
+            this.$set(c, 'active', false)
+          }
+        })
+        // this.$store.dispatch('getItemSettingColor', [])
+        this.form.setting.dataPosition = ''
+        this.getPositionList()
+      },
+    }
+  }
+  </script>
+  
+  <style lang="scss">
+  .custom-analyse-tabs-style {
+    .el-collapse {
+      border-top: transparent !important;
+    }
+    .el-collapse-item__header {
+      font-weight: bold !important;
+      padding: 0 20px !important;
+      border-bottom: 1px solid #EBEEF5 !important;
+    }
+    .el-form-item {
+      padding: 0 15px;
+      margin-bottom: 0 !important;
+    }
+    .el-collapse {
+      border-top: 0 !important;
+    }
+    .color-config {
+      padding: 0 !important;
+      .o-code {
+        margin: 0 5px;
+        display: inline-block;
+      }
+      .color-config-content {
+        line-height: 50px;
+        position: relative;
+        .name {
+          margin-left: 10px;
+          font-size: 14px;
+          top: -10px;
+          position: absolute;
+        }
+      }
+    }
+    .title-padding {
+      .el-input__inner {
+        padding: 0 10px !important;
+      }
+    }
+    .el-collapse-item__content {
+      padding-bottom: 0 !important;
+    }
+    .content {
+      padding: 20px;
+    }
+    .label {
+      color: #6b6868;
+    }
+  }
+  </style>

+ 7 - 0
RMS-FrontEnd/src/views/layout/components/UserBar.vue

@@ -12,6 +12,7 @@
           <el-dropdown-item command="changePwd" v-if="$permission('/admin/updatePassword')" >修改密码</el-dropdown-item>
           <el-dropdown-item command="AnalysisSystem" v-if="$permission('/pcs/analysisSystem')">分析系统</el-dropdown-item>
           <el-dropdown-item command="clientble" v-if="$permission('/workspace/clientManage')">客户管理</el-dropdown-item>
+          <el-dropdown-item command="product" >产品管理</el-dropdown-item>
           <el-dropdown-item command="set">设置</el-dropdown-item>
           <el-dropdown-item divided command="doLogout">退出登录</el-dropdown-item>
         </el-dropdown-menu>
@@ -148,6 +149,12 @@ export default {
         case 'clientble':
           this.clientVisible = true
           break;
+        case 'product':
+            const router = this.$router.resolve({
+              path: '/product'
+            })
+          window.open(router.href, '_blank')
+          break;
         case 'set'://设置中自定义字段
           this.setVisible = true
           break;

+ 409 - 0
RMS-FrontEnd/src/views/product/components/category.vue

@@ -0,0 +1,409 @@
+<template>
+  <div style="height: 100%;background-color: white;padding: 10px;">
+    <div style="display: flex;margin-bottom: 20px;">
+      <el-input v-model="queryParams.productCategoryName" size="small" placeholder="请输入产品类别名称" style="width: 300px;margin-right: 10px;"></el-input>
+      <el-input v-model="queryParams.createPersonName" size="small" placeholder="请输入创建人名称" style="width: 300px;margin-right: 10px;"></el-input>
+      <el-button type="primary" size="small" @click="getList2">查询</el-button>
+      <el-button type="primary" size="small" @click="handleAdd">新增类别</el-button>
+    </div>
+    <!-- 表格 -->
+    <el-table
+      :data="tableData"
+      border
+      header-row-class-name="custom-table-header"
+      @sort-change="sortChange"
+      style="width: 100%;margin-bottom: 10px;"
+      row-key="id"
+      :expand-row-keys="expands"
+      @expand-change="expandBtn"
+      >
+      <!-- <el-table-column type="index" label="#" width="55" align="center">
+        <template slot-scope="scope">
+           <span>{{ (scope.$index + 1) + ((queryParams.current - 1) * queryParams.size) }}</span>
+        </template>
+      </el-table-column> -->
+      <!-- type="expand"开启后展开行 -->
+      <el-table-column type="expand" align="center"> 
+        <template slot-scope="scope">
+
+          <div>
+            <products :categoryRow="scope.row" :patentNo="patentNo" v-bind="$attrs" v-on="$listeners"></products>
+          </div>
+          
+        </template>
+      </el-table-column>
+      <el-table-column prop="productCategoryName" label="产品类别名称"   sortable="custom" align="center"> </el-table-column>
+      <el-table-column prop="licenseRate" label="许可费率" sortable="custom" align="center" width="180"> </el-table-column>
+      <el-table-column prop="createPersonName" label="创建人" sortable="custom" align="center"> </el-table-column>
+      <!-- <el-table-column prop="pictures" label="图片" align="center"> 
+        <template slot-scope="scope">
+          <span v-for="url in scope.row.pictures">
+            <img :src="$p + url.url" alt="" max-width="100"  max-height="100">
+          </span>
+        </template>
+      </el-table-column> -->
+      <el-table-column prop="remark" label="备注" align="center"> </el-table-column>
+      <el-table-column label="操作" width="180" align="center"> 
+        <template slot-scope="scope">
+          <!-- <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button> -->
+          <el-dropdown split-button type="primary" size="small" trigger="click"  @command="handleCommand($event,scope.row)" @click="handleEdit(scope.row)">
+            <p>编辑</p> 
+            <el-dropdown-menu slot="dropdown" style="text-align: center;">
+              <el-dropdown-item command="4">预览图片</el-dropdown-item>
+              <el-dropdown-item command="1">趋势图</el-dropdown-item>
+              <el-dropdown-item command="2">可视化</el-dropdown-item>
+              <el-dropdown-item :divided="true" command="3" >
+                <span style="color: red;">删除</span> 
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div style="display: flex;justify-content: center;">
+      <el-pagination
+        background
+        layout="total,prev, pager, next,jumper"
+        :total="total"
+        @current-change="handleCurrentChange"
+        :current-page="queryParams.current" :page-size="queryParams.size">
+      </el-pagination>
+    </div>
+    
+    <!-- 新增、编辑弹窗 :close-on-click-modal="false"点击遮罩层阻止弹窗关闭 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="visible"
+      width="500"
+      :close-on-click-modal="false"
+      :before-close="close">
+
+      <el-form :rules="rules" ref="categoryForm" :model="categoryForm" label-width="80px">
+        <el-form-item label="名称 " prop="productCategoryName">
+          <el-input v-model="categoryForm.productCategoryName" placeholder="请输入名称"></el-input>
+        </el-form-item>
+        <el-form-item label="许可费率 " prop="licenseRate">
+          <el-input v-model="categoryForm.licenseRate" placeholder="请输入许可费率(许可费率介于0-1之间)"></el-input>
+          <!-- <el-input-number v-model="categoryForm.licenseRate" :precision="2" :step="0.05" :max="1" :min="0" placeholder="请输入许可费率" style="width: 100%;"></el-input-number> -->
+        </el-form-item>
+        <el-form-item label="图片 ">
+          <!-- <el-input v-model="categoryForm.url"></el-input> -->
+          <el-upload ref="upload"  action="#" :auto-upload="false"  :on-change="handleChange"  list-type="picture" :show-file-list="false">
+            <span v-if="categoryForm.pictures&&categoryForm.pictures.length>0" class="avatar">
+              <span class="deleteImg">
+                <span> 
+                  <i class="el-icon-zoom-in" @click.stop="handlePictureCardPreview"></i>
+                  <i class="el-icon-delete" @click.stop="handleRemove"></i>
+                </span>
+              </span>
+              <!-- upload展示,preview-src-list大图展示 -->
+                <el-image ref="image" style="width:100%;height: 100%;" :src="categoryForm.pictures[0].id?$p + categoryForm.pictures[0].url:categoryForm.pictures[0].url" :preview-src-list="categoryForm.pictures"></el-image>
+            </span>
+            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+          </el-upload>
+        </el-form-item>
+        <el-form-item label="备注 ">
+          <el-input v-model="categoryForm.remark" type="textarea" placeholder="请输入备注"></el-input>
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="close">取 消</el-button>
+        <el-button type="primary" @click="sure">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import products from '@/views/workspace/product/components/products.vue'
+import { commonMethods } from './mixins'
+export default {
+  components: {
+    products
+  },
+  mixins:[commonMethods],
+  props:['patentNo'],
+  data() {
+    const licenseRateRule = (rule , value ,callback)=>{
+      if(value){
+         if (!isNaN(value)) {
+          if (value>=0 && value<=1) {
+            this.categoryForm.licenseRateA=value
+            this.categoryForm.licenseRate=this.categoryForm.licenseRateA
+            callback()
+          } else {
+            // if (value>1) {
+            //   this.categoryForm.licenseRate=1
+            // } else {
+            //   this.categoryForm.licenseRate=0
+            // }
+            callback(new Error('输入错误,许可费率介于0-1之间'))
+          }
+        } else {
+          // this.categoryForm.licenseRate=this.categoryForm.licenseRateA
+          callback(new Error('输入错误,许可费率介于0-1之间'))
+        }
+      }else{
+        callback()
+      }
+    }
+    return {
+      input: '',
+      left:'left',
+      tableData: [],
+      categoryForm:{},
+      visible: false,
+      title: null,
+      queryParams: {
+        orderBy:'',//排序名称
+        orderType:'',//排序类型
+        size: 10,
+        current:1,
+      },
+      total:0,
+      file:[],
+      expands:[],
+      rules: {
+        productCategoryName:[{ required: true, message: '请输入名称', trigger: 'blur' },],
+        licenseRate:[{ required: false, validator:licenseRateRule, trigger: 'blur' },],
+      },
+    }
+  },
+  // computed: {
+  //   userinfo() {
+  //     return this.$store.state.user.userinfo
+  //   }
+  // },
+  mounted() { 
+    this.getList()
+   
+    // console.log(this.userinfo.tenantName);
+  },
+  methods: {
+    //查询
+    getList() { 
+      this.queryParams.patentNo = this.patentNo?this.patentNo[0]:null
+      this.$api.queryProductCategorys(this.queryParams).then(res => {
+        if (res.code==200) {
+          this.tableData = res.data.list
+          this.total = res.data.totalSizes
+          if (this.expands.length==0) {
+            this.expands.push(this.tableData[0].id)
+          }
+        }
+      })
+    },
+    getList2() {
+      this.queryParams.current=1
+      this.getList()
+    },
+    // 展开table当前行
+    expandBtn(row) {
+      if (row) {
+        this.expands=[row.id]
+      } 
+    },
+    // 排序
+    sortChange(row) {
+      // console.log(row);
+    },
+    // 新增
+    handleAdd() { 
+      this.title = '新增类别'
+      this.visible=true
+    },
+    // 编辑
+    handleEdit(row) { 
+      this.title = '编辑类别'
+      this.categoryForm = JSON.parse(JSON.stringify(row))
+      this.visible=true
+    },
+    // 编辑右侧菜单项
+    handleCommand(ev, row) { 
+     
+      switch (ev) {
+        case '1':
+          this.showTrend(row,1)
+          // 趋势图
+          break;
+        case '2':
+          this.getMind(row,1)
+          // 可视化
+          break;
+        case '3':
+          this.$confirm('此操作将永久删除该类别, 是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }).then(() => {
+            // console.log(ev, row);
+            this.$api.deleteProductCategorys({id:row.id}).then(res => {
+              if (res.code==200) {
+                if (this.tableData.length == 1 && this.queryParams.current != 1) {
+                  this.queryParams.current -= 1
+                }
+                this.getList()
+                this.$message.success("删除产品类别成功")
+              }
+            })
+          }).catch(() => {
+            this.$message.info("已取消删除")
+          });
+          break;
+        case '4':// 图片预览
+          if (row.pictures && row.pictures.length>0) {
+            var item = row.pictures[0]
+              var FileUrl = this.$p + row.pictures[0].url
+              var isPicture = 1
+              const router = this.$router.resolve({
+                    path: '/checkFile',
+                    query: {
+                        row: JSON.stringify(item),
+                        FileUrl: FileUrl,
+                        isPicture:isPicture
+                    }
+                })
+                window.open(router.href, '_blank');
+          } else {
+            this.$message.info("暂无图片,请先上传图片再进行预览!")
+          }
+          break;
+        default:
+          break;
+      }
+    },
+    // 弹窗确定
+    sure() {
+      this.$refs.categoryForm.validate(valid => {
+        if (valid) {
+          var formData = new FormData()
+          
+          if (this.file) {
+            for (var i = 0; i < this.file.length; i++) {
+              formData.append("files", this.file[i]);
+            }
+          }
+          formData.append('jsons', JSON.stringify(this.categoryForm))
+          // console.log(formData);
+          if (this.categoryForm.id) {
+            this.$api.updateProductCategorys(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("编辑产品类别成功")
+                this.getList()
+                this.close()
+              }
+            })
+          } else {
+             this.$api.addNewProductCategorys(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("新增产品类别成功")
+                this.getList()
+                this.close()
+              }
+            })
+          }
+         
+        }
+      })
+      
+      
+    },
+    // 弹窗关闭
+    close() {
+      this.visible = false
+      this.$refs.upload.clearFiles()
+      this.categoryForm = {}
+      this.file=[]
+    },
+    // 分页
+    handleCurrentChange(val) {
+      this.queryParams.current = val
+      this.getList()
+    },
+    // licenseRateChange(val) {
+    //   if (val) {
+    //      if (!isNaN(val)) {
+    //       if (val>=0 && val<=1) {
+    //         this.categoryForm.licenseRateA=val
+    //         this.categoryForm.licenseRate=this.categoryForm.licenseRateA
+    //       } else {
+    //         if (val>1) {
+    //           this.categoryForm.licenseRate=1
+    //         } else {
+    //           this.categoryForm.licenseRate=0
+    //         }
+    //       }
+    //     } else {
+    //       this.categoryForm.licenseRate=this.categoryForm.licenseRateA
+    //     }
+    //   }
+    // },
+    // 上传图片//showViewer显示查看器
+    handlePictureCardPreview(file) {
+        this.$refs.image.showViewer = true
+    },
+    handleRemove() {
+      this.file = []
+      this.categoryForm.pictures = []//直接清空适用于单张图片
+    },
+    handleChange(file, fileList) {
+      // console.log(file, fileList);
+      this.$set(this.categoryForm,'pictures', [{url:file.url}])
+      // this.categoryForm.pictures = [file.url]
+      // this.file.push(file.raw)
+      this.file = [file.raw]
+    },
+   
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.avatar-uploader-icon {
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    font-size: 28px;
+    color: #8c939d;
+    width: 148px;
+    height: 148px;
+    line-height: 148px;
+    text-align: center;
+  }
+ .avatar {
+  
+  position: relative;
+    width: 148px;
+    height: 148px;
+    display: block;
+  }
+  .avatar:hover .deleteImg {
+    display: block;
+}
+.deleteImg {
+  display: none;
+  font-size: 30px;
+  width: 148px;
+  height: 148px;
+  background-color:black;
+	opacity: 0.6; 
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  margin: auto;
+  z-index: 999;
+}
+.deleteImg span i{
+  margin-left: 10px;
+  color: #fff;
+}
+.deleteImg span{
+  display: flex;
+  align-items:center;/*垂直居中*/
+  justify-content: center;/*水平居中*/
+  width:100%;
+  height:100%;
+}
+</style>

+ 275 - 0
RMS-FrontEnd/src/views/product/components/echarts/components/MultipleLine/index.vue

@@ -0,0 +1,275 @@
+<template>
+  <div style="margin-top:20px" >
+    <div style="display:flex;justify-content: flex-end;cursor: pointer;margin-bottom: -27px;margin-right:8px" :style="{ width: widths }">
+      <div class="set2">
+        <div>
+          <i class="el-icon-refresh" style="font-size:18px;" @click="review"></i>
+        </div>
+        <p class="text">还原</p>
+      </div>
+      <div class="set">
+        <div>
+          <i class="el-icon-setting" style="font-size: 18px;" @click="set"></i>
+        </div>
+        <p class="text">设置图表格式</p>
+      </div>
+      
+    </div>
+    <div :id="domId" :style="{ width: widths , height:heights }" ></div>
+    <el-drawer
+            title="图表配置"
+            :visible.sync="drawer"
+            direction="rtl"
+            :before-close="handleClose"
+            size="500px">
+            <div style="width:500px">
+               <Style :form="form"></Style> 
+            </div>
+            
+        </el-drawer>
+  </div>
+  
+</template>
+
+<script>
+import {chartOptionMixins,settingMethod} from '@/views/components/common/setting/mixins'
+import Style from '@/views/components/common/setting/style.vue'
+var that = this
+export default {
+  name: "CMultipleLine",
+  components:{
+        Style
+    },
+  mixins:[chartOptionMixins,settingMethod],
+  props: {
+    width: {
+      type: String,
+      default: '100%'
+    },
+    height: {
+      type: String,
+      default: '500px'
+    },
+    domId:{
+      default: 'multipleLine'
+    },
+   NewForm:{
+   }
+  },
+  data() {
+    return {
+      widths:'100%',
+      heights:'500px',
+      drawer:false,
+      changeColor:true,
+      id:this.domId,
+      relation:{},
+      name:'',
+      // domId: 'multipleLine',
+      chartData:[]
+    }
+  },
+  computed:{
+    formSetting(){
+      return this.form.setting
+    },
+  },
+  watch:{
+    formSetting(val){
+      this.dispose(this.id)
+      this.heights = val.height + val.heightUnit
+      this.widths = val.width + val.widthUnit
+      var a = this.getOption2()
+      this.$nextTick(() => {
+        this.initChart(this.id, a)
+      })
+   }
+  },
+  mounted() {
+    
+  },
+  
+  methods: {
+    handleClose(){
+      this.form = JSON.parse(JSON.stringify(this.form))
+      this.drawer = false
+    },
+    set(){
+      this.drawer = true
+      // this.$emit('form',this.form)
+    },
+    open(chartData,name,relation){
+      this.chartData = chartData
+      this.relation = relation
+      this.name = name
+      var a = this.getOption(name,relation)
+      this.$nextTick(() => {
+        this.initChart(this.id, a)
+      })
+    },
+  review(){
+      this.dispose(this.id)
+      var a = this.getOption2()
+      this.$nextTick(() => {
+          this.initChart(this.id, a)
+        })
+    },
+  getOption2(){
+    this.changeColor = false
+    let {backgroundColor,title,legend, xAxis, yAxis, series, grid,toolbox } = this.get2AxisOption('line', '')
+      // console.log(legend, xAxis, yAxis, series, grid)
+      return {
+        backgroundColor,
+        title,
+        tooltip: {
+          trigger: 'axis'
+        },
+        legend,
+        grid: grid,
+        xAxis: xAxis,
+        yAxis: yAxis,
+        series: series,
+        toolbox:toolbox
+      }
+  },
+    getOption(name,relation) {
+      this.form.setting.text.text = name
+      this.form.setting.title1 = relation.xStr
+      this.form.setting.title2 = relation.yStr+'/万元'
+      // var chartDom1 = document.getElementById(this.domId) 
+      // var myChart1 = this.$echarts.init(chartDom1)
+      var timeList = []
+      var productNameList = []
+      var data = []
+      this.chartData.forEach(item => {
+        productNameList.push(item.productName)
+        data.push({
+          name:item.productName,
+          type:'line',
+          connectNulls:true,
+          saleVOS:item.saleVOS,
+          data:[]
+        })
+        item.saleVOS.forEach(item1=>{
+          var index = timeList.findIndex(i=>{
+            return item1[relation.x] == i
+          })
+          if(index == -1){
+            timeList.push(item1[relation.x])
+          }
+        })
+      });
+      data.forEach(item=>{
+        if(item.saleVOS && item.saleVOS.length>0){
+          item.saleVOS.forEach(item2=>{
+            var index = timeList.findIndex(i=>{
+              return item2[relation.x] == i
+            })
+            item.data[index] = item2[relation.y]
+          })
+        }
+        
+      });
+      
+      this.selected = {
+        x:timeList.sort((a,b)=>{
+          return a - b
+        }),
+        y:productNameList,
+        data:data
+      }
+      if(this.changeColor){
+        this.setColor()
+      }
+      
+      // console.log(this.selected)
+      let {backgroundColor, title, legend, xAxis, yAxis, series, grid,toolbox } = this.get2AxisOption('line', '')
+      // console.log(legend, xAxis, yAxis, series, grid)
+      return {
+        backgroundColor,
+        title,
+        tooltip: {
+          trigger: 'axis'
+        },
+        legend,
+        grid: grid,
+        xAxis: xAxis,
+        yAxis: yAxis,
+        series: series,
+        toolbox:toolbox,
+       
+      }
+   
+      
+      // var option = {
+      //   title: {
+      //     text: name,
+      //     bottom:0,
+      //     left:'center'
+      //   },
+      //   tooltip: {
+      //     trigger: 'axis'
+      //   },
+      //   legend: {
+      //     data: productNameList.length>1?productNameList:[],
+      //   },
+      //   grid: {
+      //     left: '3%',
+      //     right: '4%',
+      //     bottom: '40',
+      //     containLabel: true
+      //   },
+      //   toolbox: {
+      //     feature: {
+      //       saveAsImage: {}
+      //     }
+      //   },
+      //   xAxis: {
+      //     type: 'category',
+      //     data: timeList,
+      //     name: '时间',
+      //     nameLocation: "end"
+      //   },
+      //   yAxis: {
+      //     type: 'value',
+      //     name: '销售额/万元',
+      //     nameLocation: "end"
+      //   },
+      //   series: data
+      // };
+      // myChart1.clear();
+      // myChart1.setOption(option);
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+  .text{
+    display: none;
+    font-size: 12px;
+    color: #4099c6;
+    position: absolute;
+    top: 10px;
+    left: -30px;
+    z-index: 999;
+    width: 200px;
+  }
+  .set{
+    position: relative;
+    // margin-bottom:-200%;
+    z-index: 999;
+    // padding: 10px 0;
+  }
+  .set2{
+    margin-right:6px;
+    position: relative;
+    z-index: 999;
+  }
+  .set2:hover .text{
+    left:0;
+    display: block;
+  }
+  .set:hover .text{
+    display: block;
+  }
+</style>

+ 7 - 0
RMS-FrontEnd/src/views/product/components/echarts/components/echarts.js

@@ -0,0 +1,7 @@
+import MultipleLine from "./MultipleLine";
+
+export const commonMethods = {
+    components:{
+        MultipleLine
+    }
+}

+ 254 - 0
RMS-FrontEnd/src/views/product/components/echarts/components/echarts.vue

@@ -0,0 +1,254 @@
+<template>
+    <div style="height:100%;padding:20px">
+        <div>
+            <div style="display:flex;justify-content: space-between;">
+                <el-form :inline="true">
+                <el-form-item label="时间">
+                    <el-select v-model="queryParams.timeUnit" @change="changeEcharts()"  placeholder="请选择时间">
+                        <el-option
+                            v-for="item in timeList"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="地区">
+                    <el-select v-model="queryParams.area" @change="changeEcharts()" clearable  placeholder="请选择地区">
+                        <el-option
+                            v-for="item in areaList"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="公司" v-if="type == 1">
+                    <el-select v-model="queryParams.companyName" @change="changeEcharts()" clearable  placeholder="请选择公司">
+                        <el-option
+                            v-for="item in companyList"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="趋势图">
+                    <el-select v-model="queryParams.relation" @change="changeRelation()"  placeholder="请选择公司">
+                        <el-option
+                            v-for="item in relationList"
+                            :key="item.value"
+                            :label="item.label.xStr+'与'+item.label.yStr"
+                            :value="item.value">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+            </el-form>
+            <!-- <div>
+                <el-button type="primary" @click="setting">配置</el-button>
+            </div> -->
+            </div>
+
+            
+        </div>
+        <div>
+            <component v-if="type == 1" :is="component" :ref="components" :domId="components" :NewForm="form"  @form="getForm"></component>
+            <component :is='component' :ref="component"  :NewForm="form" @form="getForm"></component>  
+            
+        </div>  
+        <el-drawer
+            title="图表配置"
+            :visible.sync="drawer"
+            direction="rtl"
+            :before-close="handleClose"
+            size="500px">
+            <div style="width:500px">
+               <Style :form="form"></Style> 
+            </div>
+            
+        </el-drawer>
+    </div>
+</template>
+<script>
+import { commonMethods } from './echarts'
+import Style from '@/views/components/common/setting/style.vue'
+// import {settingMethod} from '@/views/workspace/components/setting/mixins'
+export default {
+    mixins:[commonMethods],
+    props:['id','type','name'],
+    components:{
+        Style
+    },
+    data() {
+        return {
+            form:{},
+            drawer:false,
+            height:'400px',
+            ChartData:[],
+            component:'MultipleLine',
+            components:'MultipleLineAll',
+            queryParams:{
+                timeUnit:0,
+                relation:1
+            },
+            relationList:[
+                {
+                    label:{
+                        x:'marketDate',
+                        xStr:'时间',
+                        y:'saleTotalMoney',
+                        yStr:'销售额'
+                    },
+                    value:1
+                },
+                {
+                    label:{
+                        x:'marketDate',
+                        xStr:'时间',
+                        y:'customLicenseMoney',
+                        yStr:'自定义许可费'
+                    },
+                    value:2
+                },
+            ],
+            timeList:[
+                {
+                    value:0,
+                    label:'月'
+                },
+                {
+                    value:1,
+                    label:'季度'
+                },
+                {
+                    value:2,
+                    label:'年'
+                },
+            ],
+            areaList:[],
+            companyList:[],
+        }
+    },
+    watch:{
+        id(val){
+            this.init()
+        },
+        type(val){},
+        name(){}
+    },
+    mounted() {
+        // this.handleAdd()
+        this.init()
+    },
+    methods: {
+        getForm(val){
+            this.form = val
+            this.drawer = true
+        },
+        handleClose(){
+            this.form = JSON.parse(JSON.stringify(this.form))
+            this.drawer = false
+        },
+        setting(){
+            this.drawer = true
+        },
+        changeRelation(){
+            var relation = this.relationList.find(i=>{
+                return i.value == this.queryParams.relation
+            })
+            var name = this.name + (this.type==1?'产品类别':'产品') + '趋势图'
+           if(this.type==1){
+                this.$refs[this.components].open(this.chartData.filter(item=>{
+                    return item.productId==0
+                }),'趋势总图',relation.label)
+                this.$refs[this.component].open(this.chartData.filter(item=>{
+                    return item.productId!=0
+                }),name,relation.label)
+            }else{
+                this.$refs[this.component].open([this.chartData],name,relation.label)
+            }
+        },
+        async changeEcharts(){
+           var chartData =await this.getData()
+           this.chartData = chartData?chartData:[]
+        //    if(this.queryParams.timeUnit == 1 && this.type==1){
+        //         var arr ={
+        //             1:'第一季度',
+        //             2:'第二季度',
+        //             3:'第三季度',
+        //             4:'第四季度',
+        //         } 
+        //         this.chartData.forEach(item => {
+        //             item.saleVOS.forEach(item2=>{
+        //                 var str = item2.marketDate.slice(item2.marketDate.length-1,item2.marketDate.length)
+        //                 item2.marketDate=item2.marketDate.substring(0,item2.marketDate.length-1)
+        //                 item2.marketDate = item2.marketDate + arr[str]
+        //             })
+        //         });
+        //    }
+          this.changeRelation()
+            
+           
+        },
+       async init(){
+            const [chartData,areaList] = await Promise.allSettled([this.getData(),this.getAreaList()])
+            this.chartData = chartData.status =='fulfilled'?chartData.value:[]
+            this.areaList= areaList.status =='fulfilled'?areaList.value.map(item=>{
+                return {
+                    value:item,
+                    label:item
+                }
+            }):[]
+            if(this.type == 1){
+                this.getCompanyList()
+            }
+            this.changeRelation()
+            // var name = this.name + (this.type==1?'产品类别':'产品') + '趋势图'
+            // if(this.type==1){
+            //     this.$refs[this.components].open(this.chartData.filter(item=>{ return item.productId==0 }),'趋势总图')
+            //     this.$refs[this.component].open(this.chartData.filter(item=>{return item.productId !=0}),name)
+            // }else{
+            //     this.$refs[this.component].open([this.chartData],name)
+            // }
+            
+            
+        },
+       async getData(){
+            if(this.type == 1){
+                this.queryParams.categoryId = this.id
+                this.queryParams.categoryArea = this.queryParams.area
+                return await this.$api.showTrend(this.queryParams)
+                    .then( async response=>await response.data)
+            }else if(this.type == 2){
+                this.queryParams.productId = this.id
+                this.queryParams.saleArea = this.queryParams.area
+                return await this.$api.ProductShowTrend(this.queryParams).then( async response=>await response.data)
+            }
+           
+            
+        },
+        getAreaList(){
+            var params = {}
+            if(this.type == 1){
+                params.categoryId = this.id
+            }else{
+                params.productId = this.id
+            }
+            return this.$api.getAreaList(params).then(response=> response.data)
+        },
+        getCompanyList(){
+             this.$api.getCompanyList({id:this.id}).then(response=>{
+                if(response.code == 200){
+                     this.companyList=response.data?response.data.map(item=>{
+                        return {
+                            value:item,
+                            label:item
+                        }
+                    }):[]
+                }
+            }
+            )
+        }
+    },
+}
+</script>

+ 29 - 0
RMS-FrontEnd/src/views/product/components/echarts/index.vue

@@ -0,0 +1,29 @@
+<template>
+    <div style="background:rgb(244 244 244)">
+        <chart :id="id" :type="type" :name="name"></chart>
+    </div>
+</template>
+<script>
+import chart from './components/echarts.vue'
+export default{
+    components:{
+        chart
+    },
+    data() {
+        return {
+            
+        }
+    },
+    computed:{
+        id(){
+            return this.$route.query.id
+        },
+        type(){
+            return this.$route.query.type
+        },
+        name(){
+            return this.$route.query.name
+        }
+    }
+}
+</script>

+ 142 - 0
RMS-FrontEnd/src/views/product/components/framework/framework.vue

@@ -0,0 +1,142 @@
+<template>
+  <div>
+    <el-container>
+      <el-header>
+         <div style="padding-left: 20px;">产品名称:{{ productsRow.productName }}</div>
+      </el-header>
+      <el-container>
+        <el-aside width="450px" style="padding: 15px;height: 100vh;">
+            <frameworkTree :productsRow="productsRow" :isDelete="isDelete" :tableRow="tableRow" :isEdit="isEdit" @table="getTable"></frameworkTree>
+        </el-aside>
+        <el-main>
+          <div style="margin-bottom: 10px;">所属分类:{{ parentName }}</div>
+          <!-- 表格 -->
+          <el-table
+            :data="tableData.slice((queryParams.current-1)*queryParams.size,queryParams.current*queryParams.size)"
+            v-loading="loading"
+            border
+            header-row-class-name="custom-table-header"
+            style="width: 100%;margin-bottom: 10px;">
+          
+            <el-table-column prop="structureName" label="产品架构名称"    align="center"> </el-table-column>
+            <el-table-column prop="createPersonName" label="创建人"  align="center"> </el-table-column>
+            <el-table-column prop="createTime" label="创建时间"  align="center"> </el-table-column>
+            <el-table-column prop="remark" label="产品架构说明" align="center"></el-table-column>
+            <el-table-column label="操作" width="180" align="center"> 
+              <template slot-scope="scope">
+                <el-dropdown split-button type="primary" size="small" trigger="click"  @command="handleCommand($event,scope.row)" @click="handleEdit(scope.row)">
+                  <p>编辑</p> 
+                  <el-dropdown-menu slot="dropdown" style="text-align: center;">
+                    <el-dropdown-item command="1">预览图片</el-dropdown-item>
+                    <!-- <el-dropdown-item command="2">查看信息</el-dropdown-item> -->
+                    <el-dropdown-item :divided="true" command="3" > <span style="color: red;">删除</span>  </el-dropdown-item>
+                  </el-dropdown-menu>
+                </el-dropdown>
+              </template>
+            </el-table-column>
+          </el-table>
+          <!-- 分页 -->
+          <div style="display: flex;justify-content: center;">
+            <el-pagination background layout="total,prev, pager, next,jumper" :total="total"
+              @current-change="handleCurrentChange" :current-page.sync ="queryParams.current" :page-size="queryParams.size">
+            </el-pagination>
+          </div>
+        </el-main>
+      </el-container>
+    </el-container>
+   
+  </div>
+</template>
+
+<script>
+import frameworkTree from './frameworkTree.vue'
+export default {
+  props:['productsRow'],
+  components: {
+    frameworkTree,
+  },
+  data() {
+    return {
+      tableData: [],
+      loading:false,
+      queryParams: {
+        size: 10,
+        current:1,
+      },
+      total: 0,
+      rules: {
+        productName:[{ required: true, message: '请输入产品名称', trigger: 'blur' },],
+        elementName:[{ required: true, message: '请输入产品架构名称', trigger: 'blur' },],
+      },
+      isDelete: false,//是否删除
+      tableRow: {},
+      isEdit: false,//是否编辑
+      tab: [],
+      parentName:''
+    }
+  },
+ 
+  mounted() { 
+  },
+  methods: {
+    // 子组件返回数据
+    getTable(val) {
+      if (val) {
+        this.total = val.children.length
+        this.tableData = val.children
+        this.parentName = val.pathName?val.pathName:this.productsRow.productName
+      }
+    },
+    // 编辑架构
+    handleEdit(row) { 
+      this.isEdit = !this.isEdit
+      this.tableRow=JSON.parse(JSON.stringify(row))
+    },
+    // 编辑架构右侧下拉菜单
+    handleCommand(command, row) { 
+      switch (command) {
+        case '1'://预览图片
+          this.commandPackage(row)
+          break;
+        case '2'://查看信息
+
+          break;
+        case '3'://删除
+          this.isDelete = !this.isDelete
+          this.tableRow=row
+          break;
+      
+        default:
+          break;
+      }
+    },
+    // 下拉菜单图片预览
+    commandPackage(row) {
+      if (row.pictures && row.pictures.length>0) {
+         var item = row.pictures[0]//图片每条信息
+          var FileUrl = this.$p + row.pictures[0].url//每张图片的url
+          var isPicture = 1
+          const router = this.$router.resolve({
+                path: '/checkFile',
+                query: {
+                    row: JSON.stringify(item),
+                    FileUrl: FileUrl,
+                    isPicture:isPicture,
+                }
+            })
+          window.open(router.href, '_blank');
+      }else{
+        this.$message.info("暂无图片,请先上传图片再进行预览!")
+      }
+    },
+     // 分页
+    handleCurrentChange(val) {
+      this.queryParams.current = val
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 366 - 0
RMS-FrontEnd/src/views/product/components/framework/frameworkTree.vue

@@ -0,0 +1,366 @@
+<template>
+  <div>
+          <!-- <div v-else> -->
+            <div style="display: flex;margin: 10px 10px;">
+               <el-input v-model="queryParams.structureName" size="small" placeholder="请输入产品名称" style="margin-right: 10px;"></el-input>
+              <el-button type="primary" size="small" @click="getList"> 查询 </el-button>
+              <el-button type="primary" size="small" @click="handleAppen()"> 新增分类节点 </el-button>
+            </div>
+          <!-- lazy :load="loadNode" 懒加载 -->
+            <!-- <el-tree @node-click="handleNodeClick" node-key="id" lazy :load="loadNode" :highlight-current="true"> -->
+            <el-tree ref="tree" :data="treeData" :props="treeProps" :current-node-key="parentId" @node-click="handleNodeClick" node-key="id" :default-checked-keys="expandedKeys" :highlight-current="true" :default-expanded-keys="expandedKeys" :expand-on-click-node="false">
+              <span class="custom-tree-node" slot-scope="{ node, data }">
+                <span>{{ node.label }}</span>
+                <span style="padding-left: 10px;">
+                  <el-button type="text" size="mini" @click.stop.native="append(data,node)">
+                    <el-tooltip class="item" effect="dark" content="添加子级分类节点" placement="bottom-start">
+                      <i class="el-icon-plus" style="font-size: 16px;"></i>
+                    </el-tooltip>
+                  </el-button>
+                </span>
+                <span style="padding-left: 10px;">
+                  <el-button type="text" size="mini" @click.stop.native="edit(data,node)">
+                    <el-tooltip class="item" effect="dark" content="编辑分类节点" placement="bottom-start">
+                      <i class="el-icon-edit-outline" style="font-size: 16px;"></i>
+                    </el-tooltip>
+                  </el-button>
+                </span>
+                <span style="padding-left: 10px;">
+                  <el-button type="text" size="mini" @click.stop.native="remove(data,node)">
+                    <el-tooltip class="item" effect="dark" content="删除分类节点" placement="bottom-start">
+                      <i class="el-icon-delete" style="font-size: 16px;"></i>
+                    </el-tooltip>
+                  </el-button>
+                </span>
+              </span>
+            </el-tree>
+          <!-- </div> -->
+          <!-- 弹窗 -->
+          <el-dialog :title="title"
+            :visible.sync="visible"
+            width="500"
+            :close-on-click-modal="false"
+            :before-close="close">
+            <el-form :rules="rules" ref="frameworkForm" :model="frameworkForm" label-width="120px">
+                <el-form-item label="所属产品名称 " prop="productName">
+                  <el-input v-model="frameworkForm.productName" disabled placeholder="请输入产品名称"></el-input>
+                </el-form-item>
+                <el-form-item :label="frameworkForm.id?'上级产品架构':'所属架构'" prop="parentPath">
+                  <!-- <el-input v-model="frameworkForm.parentPath" placeholder="请选择上级架构"></el-input> -->
+                  <el-select v-model="frameworkForm.parentId" @change="getPatentData" placeholder="请选择" style="width: 100%;" :disabled="frameworkForm.id?false:true">
+                    <el-option key="0" label="/" :value='number' ></el-option>
+                    <!--  v-for="item in options.filter(item=>{ return item.id!= frameworkForm.id})" -->
+                    <el-option
+                      v-for="item in options.filter(item=>{return !item.path.includes(frameworkForm.id)})"
+                      :key="item.path"
+                      :label="item.pathStructureName"
+                      :value="item.id">
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="产品架构名称 " prop="structureName">
+                  <el-input v-model="frameworkForm.structureName" placeholder="请输入产品架构名称"></el-input>
+                </el-form-item>
+                <el-form-item label="产品架构图片 ">
+                  <el-upload ref="upload"  action="#" :auto-upload="false"  :on-change="handleChange"  list-type="picture" :show-file-list="false">
+                  <span v-if="frameworkForm.pictures&&frameworkForm.pictures.length>0" class="avatar">
+                    <span class="deleteImg">
+                    <span> 
+                      <i class="el-icon-zoom-in" @click.stop="handlePictureCardPreview"></i>
+                      <i class="el-icon-delete" @click.stop="handleRemove"></i></span>
+                    </span>
+                    <el-image ref="image" style="width:100%;height: 100%;" :src="frameworkForm.pictures[0].id? $p + frameworkForm.pictures[0].url:frameworkForm.pictures[0].url" :preview-src-list="frameworkForm.pictures"></el-image>
+                  </span>
+                  <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+                </el-upload>
+                </el-form-item>
+                <el-form-item label="产品架构说明">
+                  <el-input v-model="frameworkForm.remark" type="textarea" placeholder="请输入产品说明"></el-input>
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+              <el-button @click="close">取 消</el-button>
+              <el-button type="primary" @click="sure">确 定</el-button>
+            </span>
+        </el-dialog>
+  </div>
+</template>
+
+<script>
+
+export default {
+  props:['productsRow','isDelete','tableRow','isEdit'],
+  data() {
+    return {
+      treeData: [],
+      treeProps: {
+        children: 'children',
+        label: 'structureName',
+      },
+      title:'',
+      queryParams: {
+        structureId:0,
+      },
+      query:{},
+      frameworkForm: {
+      },
+      file:[],
+      options:[],
+      rules: {
+        productName:[{ required: true, message: '请输入产品名称', trigger: 'blur' },],
+        elementName:[{ required: true, message: '请输入产品架构名称', trigger: 'blur' },],
+      },
+      visible: false,
+      parentId:null,
+      expandedKeys: [],
+      parentName: '',
+      number:0,
+    }
+  },
+  created() {},
+  computed: {},
+  watch: {
+    isDelete() {
+      this.remove(this.tableRow)
+    },
+    isEdit() {
+      this.edit(this.tableRow)
+    },
+  },
+  async mounted() { 
+    if(this.productsRow.structureId){
+      this.queryParams.structureId = this.productsRow.structureId
+    }
+    await this.getList()
+    this.handleNode()
+    this.parentPath()
+    this.frameworkForm.productName=this.productsRow.productName
+  },
+  methods: {
+   async getList() {//产品的id
+      this.queryParams.productId = this.productsRow.id//产品id
+      if (this.queryParams.structureName) {//查询名称
+        this.queryParams.structureId=0
+      } 
+     await this.$api.queryStructures(this.queryParams).then(res => {
+        if (res.code == 200) {
+          if (this.queryParams.structureId) {
+            if(this.productsRow.structureId){
+              this.treeData = [res.data]
+            }
+            this.$emit('table',res.data)
+            this.queryParams.structureId=0
+          } else {
+            let a = JSON.parse(sessionStorage.getItem('parentId'))
+            if (!a) {
+              this.$emit('table', res.data)
+            } 
+            this.treeData = res.data.children
+          }
+          
+        }
+      })
+    },
+    // 路径
+    parentPath() {
+      let a = {
+        productId: this.productsRow.id,
+        structureId:this.productsRow.structureId?this.productsRow.structureId:null,
+      }
+      this.$api.queryPathStructureName(a).then(res => {
+        if (res.code==200) {
+          this.options = res.data
+        }
+      })
+    },
+    // 左侧树形结构事件
+    handleNodeClick(data, node, tree) { 
+      sessionStorage.setItem('parentId',JSON.stringify(data.id))
+      this.queryParams.structureId = data.id
+      this.getList()
+    },
+    handleNode() {//请求新数据后保持刷新前所点击的节点高亮并展开
+      this.expandedKeys = []
+      if (sessionStorage.getItem('parentId')) {
+        this.parentId = JSON.parse(sessionStorage.getItem('parentId'))
+        this.expandedKeys.push(this.parentId)
+        this.$nextTick(() => {
+          this.$refs.tree.setCurrentKey(this.parentId)
+          this.queryParams.structureId = this.parentId
+          this.getList()
+        })
+      }
+    },
+    // 新增一级架构
+    handleAppen(val) { 
+      this.title = "新增分类节点"
+      this.frameworkForm.parentId = 0
+      this.frameworkForm.parentPath='0'
+      this.frameworkForm.productName=this.productsRow.productName
+      this.visible=true
+    },
+    // 新增子架构
+    append(data,node) {
+      this.title="新增子级分类节点"
+      this.frameworkForm.parentId = data.id // 父级架构的parentId
+      this.frameworkForm.productName = this.productsRow.productName
+      this.frameworkForm.parentPath=data.path
+      this.visible=true
+    }, 
+     sure() {
+       this.$refs.frameworkForm.validate(valid => {
+         if (valid) {
+          var formData = new FormData()
+          if (this.file) {
+            for (var i = 0; i < this.file.length; i++) {
+              formData.append("files", this.file[i]);
+            }
+           }
+           this.frameworkForm.productId = this.productsRow.id//产品id
+          formData.append('jsons', JSON.stringify(this.frameworkForm))
+           if (this.frameworkForm.id) {
+             this.$api.updateStructures(formData).then(async res => {
+                if (res.code == 200) {
+                  this.$message.success("编辑分类节点成功")
+                  this.queryParams.structureId=0
+                  await this.getList()
+                  this.handleNode()
+                  if  (this.tableRow.parentId!=this.frameworkForm.parentId||this.tableRow.structureName!=this.frameworkForm.structureName) {
+                    this.parentPath()
+                  }
+                  this.close()
+                }
+             }).catch(err => {
+                this.$message.error(err.message)
+              })
+          } else {
+             this.$api.addNewStructures(formData).then(async res => {
+              if (res.code == 200) {
+                this.queryParams.structureId=0
+                this.$message.success("新增分类节点成功")
+                await this.getList()
+                this.handleNode()
+                this.parentPath()
+                this.close()
+              }
+             }).catch(err => {
+              this.$message.error(err.message)
+            })
+          }
+          
+        }
+      })
+    },
+    close() {
+      this.file = []
+      this.$refs.upload.clearFiles()
+      this.frameworkForm={}
+      this.visible = false
+    },
+    // 编辑
+    edit(val,node) {
+      for (let k in val) {
+        this.tableRow[k]=val[k]
+      }
+      this.frameworkForm = JSON.parse(JSON.stringify(val))
+      this.frameworkForm.productName = this.productsRow.productName
+      this.title="编辑分类节点"
+      this.visible=true
+    },
+    //编辑获取父级数据
+    getPatentData(val) {
+      if (val != 0) {
+        var arr = this.options.find(item => {
+          return item.id == val
+        })
+        this.frameworkForm.parentPath = arr.path
+      } else {
+        this.frameworkForm.parentPath = '0'
+      }
+      
+    },
+    // 删除架构
+    remove(val) {
+      this.$confirm('此操作将删除该当前分类节点及所属子级分类节点,不可恢复,是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }).then(() => {
+            this.$api.deleteStructures({id:val.id}).then(async res => {
+              if (res.code == 200) {
+                this.$message.success("删除该当前分类节点及所属子级分类节点成功")
+                await this.getList()
+                this.handleNode()
+                this.parentPath()
+              }
+            }).catch(err => {
+              this.$message.error(err.message)
+            })
+          }).catch(() => {
+            this.$message.info("已取消删除")
+      });
+    },
+    //  上传图片
+    handlePictureCardPreview(file) {
+        this.$refs.image.showViewer = true
+    },
+    handleRemove() {
+      this.file = []
+        this.frameworkForm.pictures = []
+    },
+    handleChange(file, fileList) {
+      this.$set(this.frameworkForm,'pictures', [{url:file.url}])
+      this.file=[file.raw]
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.avatar-uploader-icon {
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    font-size: 28px;
+    color: #8c939d;
+    width: 148px;
+    height: 148px;
+    line-height: 148px;
+    text-align: center;
+  }
+ .avatar {
+  
+  position: relative;
+    width: 148px;
+    height: 148px;
+    display: block;
+  }
+  .avatar:hover .deleteImg {
+    display: block;
+}
+.deleteImg {
+  display: none;
+  font-size: 30px;
+  width: 148px;
+  height: 148px;
+  background-color:black;
+	opacity: 0.6; 
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  margin: auto;
+  z-index: 999;
+}
+.deleteImg span i{
+  margin-left: 10px;
+  color: #fff;
+}
+.deleteImg span{
+  display: flex;
+  align-items:center;/*垂直居中*/
+  justify-content: center;/*水平居中*/
+  width:100%;
+  height:100%;
+}
+</style>

+ 35 - 0
RMS-FrontEnd/src/views/product/components/framework/index.vue

@@ -0,0 +1,35 @@
+<template>
+  <div>
+    <framework :productsRow="productsRow"></framework>
+  </div>
+</template>
+
+<script>
+import framework from "./framework.vue";
+export default {
+  components: {
+    framework
+    },
+    data() {
+      return {
+        
+      }
+  },
+  computed: {
+    productsRow() {
+      // return JSON.parse(this.$route.query.row)
+      return this.$s.getSession('productRow')
+    },
+  },
+  mounted() {
+    
+  },
+    methods: {
+      
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 43 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/dom.js

@@ -0,0 +1,43 @@
+/* istanbul ignore next */
+
+import Vue from 'vue';
+
+const isServer = Vue.prototype.$isServer;
+const SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g;
+const MOZ_HACK_REGEXP = /^moz([A-Z])/;
+const ieVersion = isServer ? 0 : Number(document.documentMode);
+
+/* istanbul ignore next */
+export const on = (function () {
+  if (!isServer && document.addEventListener) {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.addEventListener(event, handler, false);
+      }
+    };
+  } else {
+    return function (element, event, handler) {
+      if (element && event && handler) {
+        element.attachEvent('on' + event, handler);
+      }
+    };
+  }
+})();
+
+/* istanbul ignore next */
+export const off = (function () {
+  if (!isServer && document.removeEventListener) {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.removeEventListener(event, handler, false);
+      }
+    };
+  } else {
+    return function (element, event, handler) {
+      if (element && event) {
+        element.detachEvent('on' + event, handler);
+      }
+    };
+  }
+})();
+

+ 450 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/index.vue

@@ -0,0 +1,450 @@
+<template>
+    <transition name="viewer-fade">
+      <div class="el-image-viewer__wrapper" :style="{ 'z-index': zIndex }">
+        <div class="el-image-viewer__mask"></div>
+        <!-- CLOSE -->
+        <span class="el-image-viewer__btn el-image-viewer__close" @click="hide">
+          <i class="el-icon-circle-close"></i>
+        </span>
+        <!-- ARROW -->
+        <template v-if="!isSingle">
+          <span class="el-image-viewer__btn el-image-viewer__prev" :class="{ 'is-disabled': !infinite && isFirst }" @click="prev">
+            <i class="el-icon-arrow-left" />
+          </span>
+          <span class="el-image-viewer__btn el-image-viewer__next" :class="{ 'is-disabled': !infinite && isLast }" @click="next">
+            <i class="el-icon-arrow-right" />
+          </span>
+        </template>
+        <!-- ACTIONS -->
+        <div class="el-image-viewer__btn el-image-viewer__actions">
+          <div class="el-image-viewer__actions__inner">
+            <i class="el-icon-zoom-out" style="margin-right: 10px;" @click="handleActions('zoomOut')"></i>
+            <i class="el-icon-zoom-in" style="margin-right: 10px;" @click="handleActions('zoomIn')"></i>
+            <i class="el-icon-refresh" @click="handleActions('clocelise')"></i>
+            <i class="el-image-viewer__actions__divider"></i>
+            <i :class="mode.icon" @click="toggleMode"></i>
+            <i class="el-image-viewer__actions__divider"></i>
+            <i class="el-icon-refresh-left" @click="handleActions('anticlocelise')"></i>
+            <i class="el-icon-refresh-right" @click="handleActions('clocelise')"></i>
+          </div>
+        </div>
+        <!-- CANVAS -->
+        <div class="el-image-viewer__canvas">
+          <div v-for="(url, i) in urlList" v-if="i === index">
+          
+            <img ref="img" class="el-image-viewer__img" :key="url" :src="currentImg" :style="imgStyle" @load="handleImgLoad" @error="handleImgError" @mousedown="handleMouseDown">
+          </div>
+        </div>
+      </div>
+    </transition>
+</template>
+  
+<script>
+  import { on, off } from './dom';
+  import { rafThrottle, isFirefox } from './util';
+  
+  const Mode = {
+    CONTAIN: {
+      name: 'contain',
+      icon: 'el-icon-full-screen'
+    },
+    ORIGINAL: {
+      name: 'original',
+      icon: 'el-icon-c-scale-to-original'
+    }
+  };
+  const mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel';
+  export default {
+    name: 'elImageViewer',
+    props: {
+      urlList: {
+        type: Array,
+        default: () => []
+      },
+      zIndex: {
+        type: Number,
+        default: 2300
+      },
+      onSwitch: {
+        type: Function,
+        default: () => { }
+      },
+      onClose: {
+        type: Function,
+        default: () => { }
+      }
+    },
+    data () {
+      return {
+        index: 0,
+        isShow: false,
+        infinite: true,
+        loading: false,
+        mode: Mode.CONTAIN,
+        transform: {
+          scale: 1,
+          deg: 0,
+          offsetX: 0,
+          offsetY: 0,
+          enableTransition: false
+        }
+      };
+    },
+    components: {
+    },
+    computed: {
+      isSingle () {
+        return this.urlList.length <= 1;
+      },
+      isFirst () {
+        return this.index === 0;
+      },
+      isLast () {
+        return this.index === this.urlList.length - 1;
+      },
+
+      currentImg () {
+        return this.urlList[this.index]
+      },
+
+      imgStyle () {
+        const { scale, deg, offsetX, offsetY, enableTransition } = this.transform;
+        const style = {
+          transform: `scale(${scale}) rotate(${deg}deg)`,
+          transition: enableTransition ? 'transform .3s' : '',
+          'margin-left': `${offsetX}px`,
+          'margin-top': `${offsetY}px`
+        };
+        if (this.mode === Mode.CONTAIN) {
+          style.maxWidth = style.maxHeight = '100%';
+        }
+        return style;
+      }
+    },
+    watch: {
+      index: {
+        handler: function (val) {
+          this.reset();
+          this.onSwitch(val);
+        }
+      },
+      currentImg (val) {
+        this.$nextTick(_ => {
+          const $img = this.$refs.img[0];
+          if (!$img.complete) {
+            this.loading = true;
+          }
+        });
+      }
+    },
+    methods: {
+      hide () {
+        this.deviceSupportUninstall();
+        this.onClose();
+      },
+      deviceSupportInstall () {
+        this._keyDownHandler = rafThrottle(e => {
+          const keyCode = e.keyCode;
+          switch (keyCode) {
+            // ESC
+            case 27:
+              this.hide();
+              break;
+            // SPACE
+            case 32:
+              this.toggleMode();
+              break;
+            // LEFT_ARROW
+            case 37:
+              this.prev();
+              break;
+            // UP_ARROW
+            case 38:
+              this.handleActions('zoomIn');
+              break;
+            // RIGHT_ARROW
+            case 39:
+              this.next();
+              break;
+            // DOWN_ARROW
+            case 40:
+              this.handleActions('zoomOut');
+              break;
+          }
+        });
+        this._mouseWheelHandler = rafThrottle(e => {
+          const delta = e.wheelDelta ? e.wheelDelta : -e.detail;
+          if (delta > 0) {
+            this.handleActions('zoomIn', {
+              zoomRate: 0.015,
+              enableTransition: false
+            });
+          } else {
+            this.handleActions('zoomOut', {
+              zoomRate: 0.015,
+              enableTransition: false
+            });
+          }
+        });
+        on(document, 'keydown', this._keyDownHandler);
+        on(document, mousewheelEventName, this._mouseWheelHandler);
+      },
+      deviceSupportUninstall () {
+        off(document, 'keydown', this._keyDownHandler);
+        off(document, mousewheelEventName, this._mouseWheelHandler);
+        this._keyDownHandler = null;
+        this._mouseWheelHandler = null;
+      },
+      handleImgLoad (e) {
+        this.loading = false;
+      },
+      handleImgError (e) {
+        this.loading = false;
+        e.target.alt = '加载失败';
+      },
+      handleMouseDown (e) {
+        if (this.loading || e.button !== 0) return;
+        const { offsetX, offsetY } = this.transform;
+        const startX = e.pageX;
+        const startY = e.pageY;
+        this._dragHandler = rafThrottle(ev => {
+          this.transform.offsetX = offsetX + ev.pageX - startX;
+          this.transform.offsetY = offsetY + ev.pageY - startY;
+        });
+        on(document, 'mousemove', this._dragHandler);
+        on(document, 'mouseup', ev => {
+          off(document, 'mousemove', this._dragHandler);
+        });
+        e.preventDefault();
+      },
+      reset () {
+        this.transform = {
+          scale: 1,
+          deg: 0,
+          offsetX: 0,
+          offsetY: 0,
+          enableTransition: false
+        };
+      },
+      toggleMode () {
+        if (this.loading) return;
+        const modeNames = Object.keys(Mode);
+        const modeValues = Object.values(Mode);
+        const index = modeValues.indexOf(this.mode);
+        const nextIndex = (index + 1) % modeNames.length;
+        this.mode = Mode[modeNames[nextIndex]];
+        this.reset();
+      },
+      prev () {
+        if (this.isFirst && !this.infinite) return;
+        const len = this.urlList.length;
+        this.index = (this.index - 1 + len) % len;
+      },
+      next () {
+        if (this.isLast && !this.infinite) return;
+        const len = this.urlList.length;
+        this.index = (this.index + 1) % len;
+      },
+      handleActions (action, options = {}) {
+        if (this.loading) return;
+        const { zoomRate, rotateDeg, enableTransition } = {
+          zoomRate: 0.2,
+          rotateDeg: 90,
+          enableTransition: true,
+          ...options
+        };
+        const { transform } = this;
+        switch (action) {
+          case 'zoomOut':
+            if (transform.scale > 0.2) {
+              transform.scale = parseFloat((transform.scale - zoomRate).toFixed(3));
+            }
+            break;
+          case 'zoomIn':
+            transform.scale = parseFloat((transform.scale + zoomRate).toFixed(3));
+            break;
+          case 'clocelise':
+            transform.deg += rotateDeg;
+            break;
+          case 'anticlocelise':
+            transform.deg -= rotateDeg;
+            break;
+        }
+        transform.enableTransition = enableTransition;
+      }
+    },
+    mounted () {
+      this.deviceSupportInstall();
+    }
+  };
+  </script>
+  <style  scoped>
+  .el-image__error,
+  .el-image__inner,
+  .el-image__placeholder {
+    width: 100%;
+    height: 100%;
+  }
+  
+  .el-image {
+    position: relative;
+    display: inline-block;
+    overflow: hidden;
+  }
+  
+  .el-image__inner {
+    vertical-align: top;
+  }
+  
+  .el-image__inner--center {
+    position: relative;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    display: block;
+  }
+  
+  .el-image__error,
+  .el-image__placeholder {
+    background: #f5f7fa;
+  }
+  
+  .el-image__error {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 14px;
+    color: #c0c4cc;
+    vertical-align: middle;
+  }
+  
+  .el-image__preview {
+    cursor: pointer;
+  }
+  
+  .el-image-viewer__wrapper {
+    
+    position: fixed;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+  
+  .el-image-viewer__btn {
+    position: absolute;
+    z-index: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 50%;
+    opacity: 0.8;
+    cursor: pointer;
+    box-sizing: border-box;
+    user-select: none;
+  }
+  
+  .el-image-viewer__close {
+    top: 40px;
+    right: 40px;
+    width: 40px;
+    height: 40px;
+    font-size: 40px;
+  }
+  
+  .el-image-viewer__canvas {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  
+  .el-image-viewer__actions {
+    left: 50%;
+    bottom: 30px;
+    transform: translateX(-50%);
+    width: 100px;
+    height: 44px;
+    padding: 0 23px;
+    background-color: #606266;
+    border-color: #fff;
+    border-radius: 22px;
+  }
+  
+  .el-image-viewer__actions__inner {
+    width: 100%;
+    height: 100%;
+    text-align: justify;
+    cursor: default;
+    font-size: 23px;
+    color: #fff;
+    display: flex;
+    align-items: center;
+    justify-content: space-around;
+  }
+  
+  .el-image-viewer__prev {
+    left: 40px;
+  }
+  
+  .el-image-viewer__next,
+  .el-image-viewer__prev {
+    top: 50%;
+    transform: translateY(-50%);
+    width: 44px;
+    height: 44px;
+    font-size: 24px;
+    color: #fff;
+    background-color: #606266;
+    border-color: #fff;
+  }
+  
+  .el-image-viewer__next {
+    right: 40px;
+    text-indent: 2px;
+  }
+  
+  .el-image-viewer__mask {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+    opacity: 0.5;
+    background: #000;
+  }
+  
+  .viewer-fade-enter-active {
+    animation: viewer-fade-in 0.3s;
+  }
+  
+  .viewer-fade-leave-active {
+    animation: viewer-fade-out 0.3s;
+  }
+  
+  @keyframes viewer-fade-in {
+    0% {
+      transform: translate3d(0, -20px, 0);
+      opacity: 0;
+    }
+  
+    to {
+      transform: translateZ(0);
+      opacity: 1;
+    }
+  }
+  
+  @keyframes viewer-fade-out {
+    0% {
+      transform: translateZ(0);
+      opacity: 1;
+    }
+  
+    to {
+      transform: translate3d(0, -20px, 0);
+      opacity: 0;
+    }
+  }
+  </style>
+  

+ 8 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/types.js

@@ -0,0 +1,8 @@
+export function isString(obj) {
+    return Object.prototype.toString.call(obj) === '[object String]';
+  }
+  export function isObject(obj) {
+    return Object.prototype.toString.call(obj) === '[object Object]';
+  }
+  
+  

+ 39 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/imageViewer/util.js

@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import {
+  isString,
+  isObject
+} from './types';
+
+const hasOwnProperty = Object.prototype.hasOwnProperty;
+
+export const isFirefox = function () {
+  return !Vue.prototype.$isServer && !!window.navigator.userAgent.match(/firefox/i);
+};
+
+export const autoprefixer = function (style) {
+  if (typeof style !== 'object') return style;
+  const rules = ['transform', 'transition', 'animation'];
+  const prefixes = ['ms-', 'webkit-'];
+  rules.forEach(rule => {
+    const value = style[rule];
+    if (rule && value) {
+      prefixes.forEach(prefix => {
+        style[prefix + rule] = value;
+      });
+    }
+  });
+  return style;
+};
+
+export function rafThrottle(fn) {
+  let locked = false;
+  return function (...args) {
+    if (locked) return;
+    locked = true;
+    window.requestAnimationFrame(_ => {
+      fn.apply(this, args);
+      locked = false;
+    });
+  };
+}
+

+ 357 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/js/jsmind.draggable.js

@@ -0,0 +1,357 @@
+/*
+ * Released under BSD License
+ * Copyright (c) 2014-2015 hizzgdev@163.com
+ * 
+ * Project Home:
+ *   https://github.com/hizzgdev/jsmind/
+ */
+
+(function ($w) {
+    'use strict';
+    var $d = $w.document;
+    var __name__ = 'jsMind';
+    var jsMind = $w[__name__];
+    if (!jsMind) { return; }
+    if (typeof jsMind.draggable != 'undefined') { return; }
+
+    var jdom = jsMind.util.dom;
+    var jcanvas = jsMind.util.canvas;
+
+    var clear_selection = 'getSelection' in $w ? function () {
+        $w.getSelection().removeAllRanges();
+    } : function () {
+        $d.selection.empty();
+    };
+
+    var options = {
+        line_width: 5,
+        lookup_delay: 500,
+        lookup_interval: 80
+    };
+
+    jsMind.draggable = function (jm) {
+        this.jm = jm;
+        this.e_canvas = null;
+        this.canvas_ctx = null;
+        this.shadow = null;
+        this.shadow_w = 0;
+        this.shadow_h = 0;
+        this.active_node = null;
+        this.target_node = null;
+        this.target_direct = null;
+        this.client_w = 0;
+        this.client_h = 0;
+        this.offset_x = 0;
+        this.offset_y = 0;
+        this.hlookup_delay = 0;
+        this.hlookup_timer = 0;
+        this.capture = false;
+        this.moved = false;
+    };
+
+    jsMind.draggable.prototype = {
+        init: function () {
+            this._create_canvas();
+            this._create_shadow();
+            this._event_bind();
+        },
+
+        resize: function () {
+            this.jm.view.e_nodes.appendChild(this.shadow);
+            this.e_canvas.width = this.jm.view.size.w;
+            this.e_canvas.height = this.jm.view.size.h;
+        },
+
+        _create_canvas: function () {
+            var c = $d.createElement('canvas');
+            this.jm.view.e_panel.appendChild(c);
+            var ctx = c.getContext('2d');
+            this.e_canvas = c;
+            this.canvas_ctx = ctx;
+        },
+
+        _create_shadow: function () {
+            var s = $d.createElement('jmnode');
+            s.style.visibility = 'hidden';
+            s.style.zIndex = '3';
+            s.style.cursor = 'move';
+            s.style.opacity = '0.7';
+            this.shadow = s;
+        },
+
+        reset_shadow: function (el) {
+            var s = this.shadow.style;
+            this.shadow.innerHTML = el.innerHTML;
+            s.left = el.style.left;
+            s.top = el.style.top;
+            s.width = el.style.width;
+            s.height = el.style.height;
+            s.backgroundImage = el.style.backgroundImage;
+            s.backgroundSize = el.style.backgroundSize;
+            s.transform = el.style.transform;
+            this.shadow_w = this.shadow.clientWidth;
+            this.shadow_h = this.shadow.clientHeight;
+
+        },
+
+        show_shadow: function () {
+            if (!this.moved) {
+                this.shadow.style.visibility = 'visible';
+            }
+        },
+
+        hide_shadow: function () {
+            this.shadow.style.visibility = 'hidden';
+        },
+
+        clear_lines: function () {
+            jcanvas.clear(this.canvas_ctx, 0, 0, this.jm.view.size.w, this.jm.view.size.h);
+        },
+
+        _magnet_shadow: function (node) {
+            if (!!node) {
+                this.canvas_ctx.lineWidth = options.line_width;
+                this.canvas_ctx.strokeStyle = 'rgba(0,0,0,0.3)';
+                this.canvas_ctx.lineCap = 'round';
+                this.clear_lines();
+                jcanvas.lineto(this.canvas_ctx,
+                    node.sp.x,
+                    node.sp.y,
+                    node.np.x,
+                    node.np.y);
+            }
+        },
+
+        _lookup_close_node: function () {
+            var root = this.jm.get_root();
+            var root_location = root.get_location();
+            var root_size = root.get_size();
+            var root_x = root_location.x + root_size.w / 2;
+
+            var sw = this.shadow_w;
+            var sh = this.shadow_h;
+            var sx = this.shadow.offsetLeft;
+            var sy = this.shadow.offsetTop;
+
+            var ns, nl;
+
+            var direct = (sx + sw / 2) >= root_x ?
+                jsMind.direction.right : jsMind.direction.left;
+            var nodes = this.jm.mind.nodes;
+            var node = null;
+            var min_distance = Number.MAX_VALUE;
+            var distance = 0;
+            var closest_node = null;
+            var closest_p = null;
+            var shadow_p = null;
+            for (var nodeid in nodes) {
+                var np, sp;
+                node = nodes[nodeid];
+                if (node.isroot || node.direction == direct) {
+                    if (node.id == this.active_node.id) {
+                        continue;
+                    }
+                    ns = node.get_size();
+                    nl = node.get_location();
+                    if (direct == jsMind.direction.right) {
+                        if (sx - nl.x - ns.w <= 0) { continue; }
+                        distance = Math.abs(sx - nl.x - ns.w) + Math.abs(sy + sh / 2 - nl.y - ns.h / 2);
+                        np = { x: nl.x + ns.w - options.line_width, y: nl.y + ns.h / 2 };
+                        sp = { x: sx + options.line_width, y: sy + sh / 2 };
+                    } else {
+                        if (nl.x - sx - sw <= 0) { continue; }
+                        distance = Math.abs(sx + sw - nl.x) + Math.abs(sy + sh / 2 - nl.y - ns.h / 2);
+                        np = { x: nl.x + options.line_width, y: nl.y + ns.h / 2 };
+                        sp = { x: sx + sw - options.line_width, y: sy + sh / 2 };
+                    }
+                    if (distance < min_distance) {
+                        closest_node = node;
+                        closest_p = np;
+                        shadow_p = sp;
+                        min_distance = distance;
+                    }
+                }
+            }
+            var result_node = null;
+            if (!!closest_node) {
+                result_node = {
+                    node: closest_node,
+                    direction: direct,
+                    sp: shadow_p,
+                    np: closest_p
+                };
+            }
+            return result_node;
+        },
+
+        lookup_close_node: function () {
+            var node_data = this._lookup_close_node();
+            if (!!node_data) {
+                this._magnet_shadow(node_data);
+                this.target_node = node_data.node;
+                this.target_direct = node_data.direction;
+            }
+        },
+
+        _event_bind: function () {
+            var jd = this;
+            var container = this.jm.view.container;
+            jdom.add_event(container, 'mousedown', function (e) {
+                var evt = e || event;
+                jd.dragstart.call(jd, evt);
+            });
+            jdom.add_event(container, 'mousemove', function (e) {
+                var evt = e || event;
+                jd.drag.call(jd, evt);
+            });
+            jdom.add_event(container, 'mouseup', function (e) {
+                var evt = e || event;
+                jd.dragend.call(jd, evt);
+            });
+            jdom.add_event(container, 'touchstart', function (e) {
+                var evt = e || event;
+                jd.dragstart.call(jd, evt);
+            });
+            jdom.add_event(container, 'touchmove', function (e) {
+                var evt = e || event;
+                jd.drag.call(jd, evt);
+            });
+            jdom.add_event(container, 'touchend', function (e) {
+                var evt = e || event;
+                jd.dragend.call(jd, evt);
+            });
+            jdom.add_event(container, 'handleDrag', function (e) {
+                var evt = e || event;
+                jd.dragend.call(jd, evt);
+            });
+        },
+
+        dragstart: function (e) {
+            if (!this.jm.get_editable()) { return; }
+            if (this.capture) { return; }
+            this.active_node = null;
+
+            var jview = this.jm.view;
+            var el = e.target || event.srcElement;
+            if (el.tagName.toLowerCase() != 'jmnode') { return; }
+            var nodeid = jview.get_binded_nodeid(el);
+            if (!!nodeid) {
+                var node = this.jm.get_node(nodeid);
+                if (!node.isroot) {
+                    this.reset_shadow(el);
+                    this.active_node = node;
+                    this.offset_x = (e.clientX || e.touches[0].clientX) - el.offsetLeft;
+                    this.offset_y = (e.clientY || e.touches[0].clientY) - el.offsetTop;
+                    this.client_hw = Math.floor(el.clientWidth / 2);
+                    this.client_hh = Math.floor(el.clientHeight / 2);
+                    if (this.hlookup_delay != 0) {
+                        $w.clearTimeout(this.hlookup_delay);
+                    }
+                    if (this.hlookup_timer != 0) {
+                        $w.clearInterval(this.hlookup_timer);
+                    }
+                    var jd = this;
+                    this.hlookup_delay = $w.setTimeout(function () {
+                        jd.hlookup_delay = 0;
+                        jd.hlookup_timer = $w.setInterval(function () {
+                            jd.lookup_close_node.call(jd);
+                        }, options.lookup_interval);
+                    }, options.lookup_delay);
+                    this.capture = true;
+                }
+            }
+        },
+
+        drag: function (e) {
+            if (!this.jm.get_editable()) { return; }
+            if (this.capture) {
+                e.preventDefault();
+                this.show_shadow();
+                this.moved = true;
+                clear_selection();
+                var px = (e.clientX || e.touches[0].clientX) - this.offset_x;
+                var py = (e.clientY || e.touches[0].clientY) - this.offset_y;
+                var cx = px + this.client_hw;
+                var cy = py + this.client_hh;
+                this.shadow.style.left = px + 'px';
+                this.shadow.style.top = py + 'px';
+                clear_selection();
+            }
+        },
+
+        dragend: function (e) {
+            if (!this.jm.get_editable()) { return; }
+            if (this.capture) {
+                if (this.hlookup_delay != 0) {
+                    $w.clearTimeout(this.hlookup_delay);
+                    this.hlookup_delay = 0;
+                    this.clear_lines();
+                }
+                if (this.hlookup_timer != 0) {
+                    $w.clearInterval(this.hlookup_timer);
+                    this.hlookup_timer = 0;
+                    this.clear_lines();
+                }
+                if (this.moved) {
+                    var src_node = this.active_node;
+                    var target_node = this.target_node;
+                    var target_direct = this.target_direct;
+                    this.move_node(src_node, target_node, target_direct);
+                }
+                this.hide_shadow();
+            }
+            this.moved = false;
+            this.capture = false;
+        },
+
+        move_node: function (src_node, target_node, target_direct) {
+            var shadow_h = this.shadow.offsetTop;
+            if (!!target_node && !!src_node && !jsMind.node.inherited(src_node, target_node)) {
+                // lookup before_node
+                var sibling_nodes = target_node.children;
+                var sc = sibling_nodes.length;
+                var node = null;
+                var delta_y = Number.MAX_VALUE;
+                var node_before = null;
+                var beforeid = '_last_';
+                while (sc--) {
+                    node = sibling_nodes[sc];
+                    if (node.direction == target_direct && node.id != src_node.id) {
+                        var dy = node.get_location().y - shadow_h;
+                        if (dy > 0 && dy < delta_y) {
+                            delta_y = dy;
+                            node_before = node;
+                            beforeid = '_first_';
+                        }
+                    }
+                }
+                if (!!node_before) { beforeid = node_before.id; }
+                this.jm.move_node(src_node.id, beforeid, target_node.id, target_direct);
+                this.handleDrag(src_node, target_node, target_direct)
+            }
+            this.active_node = null;
+            this.target_node = null;
+            this.target_direct = null;
+        },
+        // 新增自定义方法
+        handleDrag: function (src_node, target_node, target_direct) {
+        },
+
+        jm_event_handle: function (type, data) {
+            if (type === jsMind.event_type.resize) {
+                this.resize();
+            }
+        }
+    };
+
+    var draggable_plugin = new jsMind.plugin('draggable', function (jm) {
+        var jd = new jsMind.draggable(jm);
+        jd.init();
+        jm.add_event_listener(function (type, data) {
+            jd.jm_event_handle.call(jd, type, data);
+        });
+    });
+
+    jsMind.register_plugin(draggable_plugin);
+
+})(window);

File diff suppressed because it is too large
+ 2930 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/js/jsmind.js


+ 349 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/js/jsmind.screenshot.js

@@ -0,0 +1,349 @@
+/*
+ * Released under BSD License
+ * Copyright (c) 2014-2015 hizzgdev@163.com
+ * 
+ * Project Home:
+ *   https://github.com/hizzgdev/jsmind/
+ */
+
+(function($w){
+    'use strict';
+
+    var __name__ = 'jsMind';
+    var jsMind = $w[__name__];
+    if(!jsMind){return;}
+    if(typeof jsMind.screenshot != 'undefined'){return;}
+
+    var $d = $w.document;
+    var $c = function(tag){return $d.createElement(tag);};
+
+    var css = function(cstyle,property_name){
+        return cstyle.getPropertyValue(property_name);
+    };
+    var is_visible = function(cstyle){
+        var visibility = css(cstyle,'visibility');
+        var display  = css(cstyle,'display');
+        return (visibility !== 'hidden' && display !== 'none');
+    };
+    var jcanvas = jsMind.util.canvas;
+    jcanvas.rect = function (ctx,x,y,w,h,r) {
+        if (w < 2 * r) r = w / 2;
+        if (h < 2 * r) r = h / 2;
+        ctx.moveTo(x+r, y);
+        ctx.arcTo(x+w, y,   x+w, y+h, r);
+        ctx.arcTo(x+w, y+h, x,   y+h, r);
+        ctx.arcTo(x,   y+h, x,   y,   r);
+        ctx.arcTo(x,   y,   x+w, y,   r);
+    };
+
+    jcanvas.text_multiline = function(ctx,text,x,y,w,h,lineheight){
+        var line = '';
+        var text_len = text.length;
+        var chars = text.split('');
+        var test_line = null;
+        ctx.textAlign = 'left';
+        ctx.textBaseline = 'top';
+        for(var i=0;i<text_len;i++){
+            test_line = line + chars[i];
+            if(ctx.measureText(test_line).width > w && i>0){
+                ctx.fillText(line,x,y);
+                line = chars[i];
+                y += lineheight;
+            }else{
+                line = test_line;
+            }
+        }
+        ctx.fillText(line,x,y);
+    };
+
+    jcanvas.text_ellipsis = function(ctx,text,x,y,w,h){
+        var center_y = y+h/2;
+        var text = jcanvas.fittingString(ctx,text,w);
+        ctx.textAlign = 'left';
+        ctx.textBaseline = 'middle';
+        ctx.fillText(text,x,center_y,w);
+    };
+
+    jcanvas.fittingString = function(ctx,text,max_width) {
+        var width = ctx.measureText(text).width;
+        var ellipsis = '…'
+        var ellipsis_width = ctx.measureText(ellipsis).width;
+        if (width<=max_width || width<=ellipsis_width) {
+            return text;
+        } else {
+            var len = text.length;
+            while (width>=max_width-ellipsis_width && len-->0) {
+                text = text.substring(0, len);
+                width = ctx.measureText(text).width;
+            }
+            return text+ellipsis;
+        }
+    };
+
+    jcanvas.image = function(ctx,backgroundUrl,x,y,w,h,r,rotation,callback){
+        var img = new Image();
+        img.onload = function () {
+            ctx.save();
+            ctx.translate(x,y);
+            ctx.save();
+            ctx.beginPath();
+            jcanvas.rect(ctx,0,0,w,h,r);
+            ctx.closePath();
+            ctx.clip();
+            ctx.translate(w/2,h/2);
+            ctx.rotate(rotation*Math.PI/180);
+            ctx.drawImage(img,-w/2,-h/2);
+            ctx.restore();
+            ctx.restore();
+            callback();
+        }
+        img.src = backgroundUrl;
+    };
+
+    jsMind.screenshot = function(jm){
+        this.jm = jm;
+        this.canvas_elem = null;
+        this.canvas_ctx = null;
+        this._inited = false;
+    };
+
+    jsMind.screenshot.prototype = {
+        init:function(){
+            if(this._inited){return;}
+            console.log('init');
+            var c = $c('canvas');
+            var ctx = c.getContext('2d');
+
+            this.canvas_elem = c;
+            this.canvas_ctx = ctx;
+            this.jm.view.e_panel.appendChild(c);
+            this._inited = true;
+            this.resize();
+        },
+
+        shoot:function(callback){
+            this.init();
+            this._watermark();
+            var jms = this;
+            this._draw(function(){
+                if(!!callback){
+                    callback(jms);
+                }
+                jms.clean();
+            });
+        },
+
+        shootDownload: function(){
+            this.shoot(function(jms){
+                jms._download();
+            });
+        },
+
+        shootAsDataURL: function(callback){
+            this.shoot(function(jms){
+                callback(jms.canvas_elem.toDataURL());
+            });
+        },
+
+        resize:function(){
+            if(this._inited){
+                this.canvas_elem.width=this.jm.view.size.w;
+                this.canvas_elem.height=this.jm.view.size.h;
+            }
+        },
+
+        clean:function(){
+            var c = this.canvas_elem;
+            this.canvas_ctx.clearRect(0,0,c.width,c.height);
+        },
+
+        _draw:function(callback){
+            var ctx = this.canvas_ctx;
+            ctx.textAlign = 'left';
+            ctx.textBaseline = 'top';
+            this._draw_lines();
+            this._draw_nodes(callback);
+        },
+
+        _watermark:function(){
+            var c = this.canvas_elem;
+            var ctx = this.canvas_ctx;
+            ctx.textAlign='right';
+            ctx.textBaseline='bottom';
+            ctx.fillStyle='#000';
+            ctx.font='11px Verdana,Arial,Helvetica,sans-serif';
+            ctx.fillText('hizzgdev.github.io/jsmind',c.width-5.5,c.height-2.5);
+            ctx.textAlign='left';
+            ctx.fillText($w.location,5.5,c.height-2.5);
+        },
+
+        _draw_lines:function(){
+            this.jm.view.show_lines(this.canvas_ctx);
+        },
+
+        _draw_nodes:function(callback){
+            var nodes = this.jm.mind.nodes;
+            var node;
+            for(var nodeid in nodes){
+                node = nodes[nodeid];
+                this._draw_node(node);
+            }
+
+            function check_nodes_ready() {
+                console.log('check_node_ready'+new Date());
+                var allOk = true;
+                for(var nodeid in nodes){
+                    node = nodes[nodeid];
+                    allOk = allOk & node.ready;
+                }
+
+                if(!allOk) {
+                    $w.setTimeout(check_nodes_ready, 200);
+                } else {
+                    $w.setTimeout(callback, 200);
+                }
+            }
+            check_nodes_ready();
+        },
+
+        _draw_node:function(node){
+            var ctx = this.canvas_ctx;
+            var view_data = node._data.view;
+            var node_element = view_data.element;
+            var ncs = getComputedStyle(node_element);
+            if(!is_visible(ncs)){
+                node.ready = true;
+                return;
+            }
+
+            var bgcolor = css(ncs,'background-color');
+            var round_radius = parseInt(css(ncs,'border-top-left-radius'));
+            var color = css(ncs,'color');
+            var padding_left = parseInt(css(ncs,'padding-left'));
+            var padding_right = parseInt(css(ncs,'padding-right'));
+            var padding_top = parseInt(css(ncs,'padding-top'));
+            var padding_bottom = parseInt(css(ncs,'padding-bottom'));
+            var text_overflow = css(ncs,'text-overflow');
+            var font = css(ncs,'font-style')+' '+
+                        css(ncs,'font-variant')+' '+
+                        css(ncs,'font-weight')+' '+
+                        css(ncs,'font-size')+'/'+css(ncs,'line-height')+' '+
+                        css(ncs,'font-family');
+
+            var rb = {x:view_data.abs_x,
+                      y:view_data.abs_y,
+                      w:view_data.width+1,
+                      h:view_data.height+1};
+            var tb = {x:rb.x+padding_left,
+                      y:rb.y+padding_top,
+                      w:rb.w-padding_left-padding_right,
+                      h:rb.h-padding_top-padding_bottom};
+
+            ctx.font=font;
+            ctx.fillStyle = bgcolor;
+            ctx.beginPath();
+            jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius);
+            ctx.closePath();
+            ctx.fill();
+
+            ctx.fillStyle = color;
+            if ('background-image' in node.data) {
+                var backgroundUrl = css(ncs,'background-image').slice(5, -2);
+                node.ready = false;
+                var rotation = 0;
+                if ('background-rotation' in node.data) {
+                    rotation = node.data['background-rotation'];
+                }
+                jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation,
+                    function() {
+                        node.ready = true;
+                    });
+            }
+            if (!!node.topic) {
+                if(text_overflow === 'ellipsis'){
+                    jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h);
+                }else{
+                    var line_height = parseInt(css(ncs,'line-height'));
+                    jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h,line_height);
+                }
+            }
+            if(!!view_data.expander){
+                this._draw_expander(view_data.expander);
+            }
+            if (!('background-image' in node.data)) {
+                node.ready = true;
+            }
+        },
+
+        _draw_expander:function(expander){
+            var ctx = this.canvas_ctx;
+            var ncs = getComputedStyle(expander);
+            if(!is_visible(ncs)){ return; }
+
+            var style_left = css(ncs,'left');
+            var style_top = css(ncs,'top');
+            var font = css(ncs,'font');
+            var left = parseInt(style_left);
+            var top = parseInt(style_top);
+            var is_plus = expander.innerHTML === '+';
+
+            ctx.lineWidth = 1;
+
+            ctx.beginPath();
+            ctx.arc(left+7,top+7,5,0,Math.PI*2,true);
+            ctx.moveTo(left+10,top+7);
+            ctx.lineTo(left+4,top+7);
+            if(is_plus){
+                ctx.moveTo(left+7,top+4);
+                ctx.lineTo(left+7,top+10);
+            }
+            ctx.closePath();
+            ctx.stroke();
+        },
+
+        _download:function(){
+            var c = this.canvas_elem;
+            var name = this.jm.mind.name+'.png';
+
+            if (navigator.msSaveBlob && (!!c.msToBlob)) {
+                var blob = c.msToBlob();
+                navigator.msSaveBlob(blob,name);
+            } else {
+                var bloburl = this.canvas_elem.toDataURL();
+                var anchor = $c('a');
+                if ('download' in anchor) {
+                    anchor.style.visibility = 'hidden';
+                    anchor.href = bloburl;
+                    anchor.download = name;
+                    $d.body.appendChild(anchor);
+                    var evt = $d.createEvent('MouseEvents');
+                    evt.initEvent('click', true, true);
+                    anchor.dispatchEvent(evt);
+                    $d.body.removeChild(anchor);
+                } else {
+                    location.href = bloburl;
+                }
+            }
+        },
+
+        jm_event_handle:function(type,data){
+            if(type === jsMind.event_type.resize){
+                this.resize();
+            }
+        }
+    };
+
+    var screenshot_plugin = new jsMind.plugin('screenshot',function(jm){
+        var jss = new jsMind.screenshot(jm);
+        jm.screenshot = jss;
+        jm.shoot = function(){
+            jss.shoot();
+        };
+        jm.add_event_listener(function(type,data){
+            jss.jm_event_handle.call(jss,type,data);
+        });
+    });
+
+    jsMind.register_plugin(screenshot_plugin);
+
+})(window);

+ 159 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/jsmind/style/jsmind.css

@@ -0,0 +1,159 @@
+/*
+ * Released under BSD License
+ * Copyright (c) 2014-2015 hizzgdev@163.com
+ * 
+ * Project Home:
+ *   https://github.com/hizzgdev/jsmind/
+ */
+
+/* important section */
+.jsmind-inner{position:relative;overflow:auto;width:100%;height:100%;}/*box-shadow:0 0 2px #000;*/
+.jsmind-inner{
+    moz-user-select:-moz-none;
+    -moz-user-select:none;
+    -o-user-select:none;
+    -khtml-user-select:none;
+    -webkit-user-select:none;
+    -ms-user-select:none;
+    user-select:none;
+}
+
+/* z-index:1 */
+canvas{position:absolute;z-index:1;}
+
+/* z-index:2 */
+jmnodes{position:absolute;z-index:2;background-color:rgba(0,0,0,0);}/*background color is necessary*/
+jmnode{position:absolute;cursor:default;max-width:400px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
+jmexpander{position:absolute;width:11px;height:11px;display:block;overflow:hidden;line-height:12px;font-size:12px;text-align:center;border-radius:6px;border-width:1px;border-style:solid;cursor:pointer;}
+
+/* default theme */
+jmnode{padding:10px;background-color:#fff;color:#333;border-radius:5px;box-shadow:1px 1px 1px #666;font:16px/1.125 Verdana,Arial,Helvetica,sans-serif;}
+jmnode:hover{box-shadow:2px 2px 8px #000;background-color:#ebebeb;color:#333;}
+jmnode.selected{background-color:#11f;color:#fff;box-shadow:2px 2px 8px #000;}
+jmnode.root{font-size:24px;}
+jmexpander{border-color:gray;}
+jmexpander:hover{border-color:#000;}
+
+@media screen and (max-device-width: 1024px) {
+    jmnode{padding:5px;border-radius:3px;font-size:14px;}
+    jmnode.root{font-size:21px;}
+}
+/* primary theme */
+jmnodes.theme-primary jmnode{background-color:#428bca;color:#fff;border-color:#357ebd;}
+jmnodes.theme-primary jmnode:hover{background-color:#3276b1;border-color:#285e8e;}
+jmnodes.theme-primary jmnode.selected{background-color:#f1c40f;color:#fff;}
+jmnodes.theme-primary jmnode.root{}
+jmnodes.theme-primary jmexpander{}
+jmnodes.theme-primary jmexpander:hover{}
+
+/* warning theme */
+jmnodes.theme-warning jmnode{background-color:#f0ad4e;border-color:#eea236;color:#fff;}
+jmnodes.theme-warning jmnode:hover{background-color:#ed9c28;border-color:#d58512;}
+jmnodes.theme-warning jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-warning jmnode.root{}
+jmnodes.theme-warning jmexpander{}
+jmnodes.theme-warning jmexpander:hover{}
+
+/* danger theme */
+jmnodes.theme-danger jmnode{background-color:#d9534f;border-color:#d43f3a;color:#fff;}
+jmnodes.theme-danger jmnode:hover{background-color:#d2322d;border-color:#ac2925;}
+jmnodes.theme-danger jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-danger jmnode.root{}
+jmnodes.theme-danger jmexpander{}
+jmnodes.theme-danger jmexpander:hover{}
+
+/* success theme */
+jmnodes.theme-success jmnode{background-color:#5cb85c;border-color:#4cae4c;color:#fff;}
+jmnodes.theme-success jmnode:hover{background-color:#47a447;border-color:#398439;}
+jmnodes.theme-success jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-success jmnode.root{}
+jmnodes.theme-success jmexpander{}
+jmnodes.theme-success jmexpander:hover{}
+
+/* info theme */
+jmnodes.theme-info jmnode{background-color:#5dc0de;border-color:#46b8da;;color:#fff;}
+jmnodes.theme-info jmnode:hover{background-color:#39b3d7;border-color:#269abc;}
+jmnodes.theme-info jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-info jmnode.root{}
+jmnodes.theme-info jmexpander{}
+jmnodes.theme-info jmexpander:hover{}
+
+/* greensea theme */
+jmnodes.theme-greensea jmnode{background-color:#1abc9c;color:#fff;}
+jmnodes.theme-greensea jmnode:hover{background-color:#16a085;}
+jmnodes.theme-greensea jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-greensea jmnode.root{}
+jmnodes.theme-greensea jmexpander{}
+jmnodes.theme-greensea jmexpander:hover{}
+
+/* nephrite theme */
+jmnodes.theme-nephrite jmnode{background-color:#2ecc71;color:#fff;}
+jmnodes.theme-nephrite jmnode:hover{background-color:#27ae60;}
+jmnodes.theme-nephrite jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-nephrite jmnode.root{}
+jmnodes.theme-nephrite jmexpander{}
+jmnodes.theme-nephrite jmexpander:hover{}
+
+/* belizehole theme */
+jmnodes.theme-belizehole jmnode{background-color:#3498db;color:#fff;}
+jmnodes.theme-belizehole jmnode:hover{background-color:#2980b9;}
+jmnodes.theme-belizehole jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-belizehole jmnode.root{}
+jmnodes.theme-belizehole jmexpander{}
+jmnodes.theme-belizehole jmexpander:hover{}
+
+/* wisteria theme */
+jmnodes.theme-wisteria jmnode{background-color:#9b59b6;color:#fff;}
+jmnodes.theme-wisteria jmnode:hover{background-color:#8e44ad;}
+jmnodes.theme-wisteria jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-wisteria jmnode.root{}
+jmnodes.theme-wisteria jmexpander{}
+jmnodes.theme-wisteria jmexpander:hover{}
+
+/* asphalt theme */
+jmnodes.theme-asphalt jmnode{background-color:#34495e;color:#fff;}
+jmnodes.theme-asphalt jmnode:hover{background-color:#2c3e50;}
+jmnodes.theme-asphalt jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-asphalt jmnode.root{}
+jmnodes.theme-asphalt jmexpander{}
+jmnodes.theme-asphalt jmexpander:hover{}
+
+/* orange theme */
+jmnodes.theme-orange jmnode{background-color:#f1c40f;color:#fff;}
+jmnodes.theme-orange jmnode:hover{background-color:#f39c12;}
+jmnodes.theme-orange jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-orange jmnode.root{}
+jmnodes.theme-orange jmexpander{}
+jmnodes.theme-orange jmexpander:hover{}
+
+/* pumpkin theme */
+jmnodes.theme-pumpkin jmnode{background-color:#e67e22;color:#fff;}
+jmnodes.theme-pumpkin jmnode:hover{background-color:#d35400;}
+jmnodes.theme-pumpkin jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-pumpkin jmnode.root{}
+jmnodes.theme-pumpkin jmexpander{}
+jmnodes.theme-pumpkin jmexpander:hover{}
+
+/* pomegranate theme */
+jmnodes.theme-pomegranate jmnode{background-color:#e74c3c;color:#fff;}
+jmnodes.theme-pomegranate jmnode:hover{background-color:#c0392b;}
+jmnodes.theme-pomegranate jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-pomegranate jmnode.root{}
+jmnodes.theme-pomegranate jmexpander{}
+jmnodes.theme-pomegranate jmexpander:hover{}
+
+/* clouds theme */
+jmnodes.theme-clouds jmnode{background-color:#ecf0f1;color:#333;}
+jmnodes.theme-clouds jmnode:hover{background-color:#bdc3c7;}
+jmnodes.theme-clouds jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-clouds jmnode.root{}
+jmnodes.theme-clouds jmexpander{}
+jmnodes.theme-clouds jmexpander:hover{}
+
+/* asbestos theme */
+jmnodes.theme-asbestos jmnode{background-color:#95a5a6;color:#fff;}
+jmnodes.theme-asbestos jmnode:hover{background-color:#7f8c8d;}
+jmnodes.theme-asbestos jmnode.selected{background-color:#11f;color:#fff;}
+jmnodes.theme-asbestos jmnode.root{}
+jmnodes.theme-asbestos jmexpander{}
+jmnodes.theme-asbestos jmexpander:hover{}

File diff suppressed because it is too large
+ 1489 - 0
RMS-FrontEnd/src/views/product/components/jsMind/components/mind.vue


+ 23 - 0
RMS-FrontEnd/src/views/product/components/jsMind/index.vue

@@ -0,0 +1,23 @@
+<template>
+    <div>
+        <mind :row="row"></mind>
+    </div>
+</template>
+<script>
+import mind from './components/mind.vue'
+export default{
+    components:{
+        mind,
+    },
+    data() {
+        return {
+            
+        }
+    },
+    computed:{
+        row(){
+            return this.$s.getSession('mindRow')
+        }
+    }
+}
+</script>

+ 353 - 0
RMS-FrontEnd/src/views/product/components/marketingData.vue

@@ -0,0 +1,353 @@
+<template>
+    <div>
+        <div style="display: flex; justify-content: flex-end; margin-bottom: 20px">
+            <el-button @click="addData()">添加</el-button>
+        </div>
+        <el-table :data="tableData" border @sort-change="sortChange">
+            <el-table-column label="时间" prop="saleTime" align="center" sortable="custom">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{ scope.row.saleTime }}</span>
+                    <el-date-picker
+                        style="width: 100%"
+                        v-else
+                        v-model="scope.row.saleTime"
+                        type="month"
+                        value-format="yyyy-MM"
+                        placeholder="选择日期时间"
+                    >
+                    </el-date-picker>
+                </template>
+            </el-table-column>
+            <el-table-column label="地区" prop="saleArea" align="center" width="120px" sortable="custom">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{ scope.row.saleArea }}</span>
+                    <el-input v-else v-model="scope.row.saleArea" placeholder="请输入地区"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="销售量/件" prop="saleCount" align="center" width="120px" sortable="custom">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{ scope.row.saleCount }}</span>
+                    <el-input v-else v-model="scope.row.saleCount" placeholder="请输入销售量/件"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="销售额/万元" prop="saleMoney" align="center" width="120px" sortable="custom">
+                <template slot-scope="scope">
+                    <span v-if="!scope.row.vVisible">{{ scope.row.saleMoney }}</span>
+                    <el-input v-else v-model="scope.row.saleMoney" placeholder="请输入销售额/万元"></el-input>
+                </template>
+            </el-table-column>
+            <el-table-column label="许可参考值" prop="reference" align="center" width="120px">
+                <template slot-scope="scope">
+                    <span>{{row.licenseRate==0||row.licenseRate?(Number(scope.row.saleMoney) * Number(row.licenseRate)).toFixed(2):'' }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column label="自定义许可费" prop="customLicenseFee" align="center" width="200px">
+                <template slot-scope="scope">
+                    <span v-if="editId == scope.row.id"><el-input v-model="scope.row.customLicenseRates" placeholder="请输入自定义许可费率(许可费率介于0-1之间)" @blur="changeEditId(scope.row)" @change="inpBlur" autofocus=true></el-input></span>
+                    <span v-else>{{ scope.row.customLicenseRate==0||scope.row.customLicenseRate?(Number(scope.row.saleMoney) * Number(scope.row.customLicenseRate)).toFixed(2):'' }} <span><i class="el-icon-edit" @click="editCustomLicenseRate(scope.row)"></i></span> </span>
+                </template>
+            </el-table-column>
+            <el-table-column label="操作" width="160" align="center">
+                <template slot-scope="scope">
+                    <div class="special">
+                        <span
+                        v-if="!scope.row.vVisible"
+                        class="items"
+                        @click="edit(scope.row)"
+                        >编辑</span
+                        >
+                        <span v-else class="items" @click="submit1(scope.row)">保存</span>
+                        <span>&nbsp;|&nbsp;</span>
+                        <span class="items" @click="deleteIt(scope.row)"  v-if="!scope.row.vVisible">删除</span>
+                        <span v-else class="items" @click="getList">取消</span>
+                    </div>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div style="display: flex;justify-content: center;">
+            <el-pagination
+                background
+                layout="total,prev, pager, next,jumper"
+                :total="total"
+                @current-change="handleCurrentChange"
+                :current-page="queryParams.current" :page-size="queryParams.size">
+            </el-pagination>
+        </div>
+        <el-dialog :title="title" :visible.sync="visible" width="1000px" append-to-body :close-on-click-modal="false" :before-close="close">
+            <div>
+                <el-form ref="form" :model="form" :rules="rules">
+                    <el-form-item label="所属产品" prop="productName" v-if="form.productName">
+                        <el-input v-model="form.productName" :disabled="true"></el-input>
+                    </el-form-item>
+                    <el-form-item label="时间" prop="saleTime">
+                        <el-date-picker
+                            style="width: 100%"
+                            v-model="form.saleTime"
+                            type="month"
+                            value-format="yyyy-MM"
+                            placeholder="选择日期时间"
+                        >
+                     </el-date-picker>
+                    </el-form-item>
+                    <el-form-item label="地区" prop="saleArea" >
+                        <el-input v-model="form.saleArea"  placeholder="请输入地区"></el-input>
+                    </el-form-item>
+                    <el-form-item label="销售量/件" prop="saleCount">
+                        <el-input v-model="form.saleCount" placeholder="请输入销售量"></el-input>
+                    </el-form-item>
+                    <el-form-item label="销售额/万元" prop="saleMoney">
+                        <el-input v-model="form.saleMoney" placeholder="请输入销售额"></el-input>
+                    </el-form-item>
+                    <!-- <el-form-item label="许可参考值" prop="reference">
+                        <el-input v-model="form.reference" placeholder="请输入销售额"></el-input>
+                    </el-form-item> -->
+                    <el-form-item label="自定义许可费率" prop="customLicenseRates">
+                        <el-input v-model="form.customLicenseRates" placeholder="请输入自定义许可费率(许可费率介于0-1之间)" ></el-input>
+                    </el-form-item>
+                </el-form>
+            </div> 
+            <div slot="footer" class="dialog-footer" style="display: flex;justify-content: flex-end;">
+                <el-button @click="close">取 消</el-button>
+                <el-button type="primary" @click="submit" :loading="btnLoading">确定</el-button>
+            </div> 
+        </el-dialog>
+    </div>
+</template>
+<script>
+export default {
+    props:['row'],
+    data() {
+        const customLicenseRateRule = (rule , value ,callback)=>{
+            if(value){
+                if (!isNaN(value)) {
+                if (value>=0 && value<=1) {
+                    this.form.customLicenseRates=value
+                    this.form.customLicenseRate=this.form.customLicenseRates
+                    callback()
+                } else {
+                    // if (value>1) {
+                    //   this.categoryForm.licenseRate=1
+                    // } else {
+                    //   this.categoryForm.licenseRate=0
+                    // }
+                    callback(new Error('输入错误,自定义许可费率介于0-1之间'))
+                }
+                } else {
+                // this.categoryForm.licenseRate=this.categoryForm.licenseRateA
+                callback(new Error('输入错误,自定义许可费率介于0-1之间'))
+                }
+            }else{
+                callback()
+            }
+        }
+        return {
+            editId:null,
+            visible:false,
+            btnLoading:false,
+            title:'',
+            form:{},
+            queryParams:{
+                size:10,
+                current:1
+            },
+            total:0,
+            tableData:[],
+            rules:{
+                saleTime:[ {required: true, message: '请选择日期', trigger: 'change' }],
+                saleMoney:[{ required: true, message: '请输入销售额', trigger: 'blur' }],
+                customLicenseRates:[{required: false, validator:customLicenseRateRule, trigger: 'blur' }]
+            }
+        }
+    },
+    watch:{
+        row(val){
+            this.getList2()
+        },
+    },
+    mounted() {
+        this.getList()
+    },
+    methods: {
+        //编辑自定义许可费
+        editCustomLicenseRate(row){
+            this.$set(row,'customLicenseRates',row.customLicenseRate)
+            this.editId = row.id
+        },
+        // 许可费率事件//优化
+        inpBlur(row) {
+            var val = row.customLicenseRates
+            if (val) {
+                if (!isNaN(val)) {
+                    if (val >= 0 && val <= 1) {
+                        row.customLicenseRate = val
+                    } else {
+                        if (val > 1) {
+                            row.customLicenseRate = 1
+                            row.customLicenseRates = 1
+                        }
+                        if (val < 0) {
+                            row.customLicenseRate = 0
+                            row.customLicenseRates = 0
+                        }
+                    }
+                } else {
+                    row.customLicenseRates = row.customLicenseRate?row.customLicenseRate:''
+                }
+            }
+            
+        },
+        //失去焦点
+        changeEditId(row){
+            this.inpBlur(row)
+            if(this.editId){
+               this.$set(this,'editId',null)
+            }
+            this.update(row)
+        },
+        //重新查询
+        getList2(){
+            this.queryParams.current = 1
+            this.getList()
+        },
+        //获取营销数据
+        getList(){
+            this.queryParams.productId = this.row.id
+            this.$api.queryProductMarketData(this.queryParams).then(response=>{
+                if(response.code == 200){
+                    this.tableData = response.data.list
+                    this.total = response.data.totalSizes
+                }
+            })
+        },
+        // 分页
+        handleCurrentChange(val) {
+            this.queryParams.current = val
+            this.getList()
+        },
+        // 排序
+        sortChange({ column, prop, order }) {
+            if (!order) {
+                return false
+            }
+            const o = {
+                'descending': 'desc',
+                'ascending': 'asc',
+            }
+            console.log({ column, prop, order })
+            this.queryParams.orderBy = prop
+            this.queryParams.orderType = o[order]
+            this.getList2()
+        },
+        //打开添加营销数据弹窗
+        addData(){
+            this.title = '添加'+this.row.productName+'营销数据'
+            this.form = {
+                productId : this.row.id,
+                productName : this.row.productName,
+                customLicenseRate : this.row.licenseRate,
+                customLicenseRates : this.row.licenseRate
+            }
+            this.visible = true
+        },
+        //关闭弹窗
+        close(){
+            this.btnLoading = false
+            this.visible = false
+            this.form = {}
+        },
+        //编辑销售量
+        edit(row) {
+            this.$set(row,'vVisible',!row.vVisible )
+        },
+        //保存
+        submit1(row){
+            var str = ''
+            if(!row.saleTime){
+                str = str + '时间'
+            }
+            if(!row.saleMoney){
+                if(str.length>0){
+                    str = str + '和销售额'
+                }else{
+                    str = str + '销售额'
+                }
+            }
+            if(str.length>0){
+                this.$alert(str+'不可为空', '提示', {
+                    confirmButtonText: '确定',
+                    callback: action => {
+                    }
+                });
+                return false
+            }
+            this.update(row)
+        },
+        //更新营销数据
+        update(row){
+            row.productId = this.row.id
+            this.$api.updateProductMarketData(row).then(response=>{
+                if(response.code == 200){
+                    this.$message.success('修改成功!')
+                    row.vVisible = false
+                }else{
+                    this.$message.error('修改失败!')
+                }
+            }).catch(e=>{
+                this.$message.error('修改失败!')
+            })
+        },
+        //删除营销数据
+        deleteIt(row){
+            this.$confirm('确认删除本条数据吗?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                this.$api.deleteProductMarketData({id:row.id}).then(response=>{
+                    if(response.code == 200){
+                        this.$message.success('删除成功!')
+                        if(this.tableData.length==1&&this.queryParams.current!=1){
+                            this.queryParams.current -= 1
+                        }
+                        this.getList()
+                    }else{
+                        this.$message.error('删除失败!')
+                    }
+                }).catch(e=>{
+                    this.$message.error('删除失败!')
+                })
+            })
+           
+        },
+        //确认添加营销数据
+        submit(){
+            this.$refs.form.validate((valid) => {
+                if (valid) {
+                    this.btnLoading = true
+                    this.$api.addNewProductMarketData(this.form).then(response=>{
+                        if(response.code == 200){
+                            this.$message.success('添加成功!')
+                            this.getList()
+                            this.close()
+                        }else{
+                            this.$message.error('添加失败!')
+                            this.btnLoading = false
+                        }
+                    }).catch(e=>{
+                        this.$message.error('添加失败!')
+                        this.btnLoading = false
+                    })
+                } else {
+                    console.log('error submit!!');
+                    return false;
+                }
+            });
+        }
+    },
+}
+</script>
+<style lang="scss" scoped>
+.items{
+    cursor: pointer;
+}
+</style>

+ 23 - 0
RMS-FrontEnd/src/views/product/components/mixins.js

@@ -0,0 +1,23 @@
+export const commonMethods = {
+    methods: {
+        getMind(row,val){//val为数据类型,1为产品类别,2为产品
+            row.type = val
+            this.$s.setSession('mindRow',row)
+            const router = this.$router.resolve({
+                  path: '/mindIndex',
+              })
+            window.open(router.href, '_blank');
+          },
+          showTrend(row,val){//val为数据类型,1为产品类别,2为产品
+            const router = this.$router.resolve({
+                path: '/chartIndex',
+                query:{
+                    id:row.id,
+                    type:val,
+                    name:row.productCategoryName || row.productName
+                }
+            })
+            window.open(router.href, '_blank');
+          }
+    },
+}

+ 662 - 0
RMS-FrontEnd/src/views/product/components/product.vue

@@ -0,0 +1,662 @@
+<template>
+  <div style="padding: 20px;background: white;height: 100%;">
+    <div style="display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;">
+      <div>
+        <el-input v-model.trim="queryParams.productCategoryName" size="small" placeholder="请输入类别名称" style="width: 200px;margin-right: 10px;"></el-input>
+        <el-input v-model.trim="queryParams.productName" size="small" placeholder="请输入产品名称" style="width: 200px;margin-right: 10px;"></el-input>
+        <el-input v-model.trim="queryParams.companyName" size="small" placeholder="请输入产品所属公司名称" style="width: 200px;margin-right: 10px;"></el-input>
+          <el-button type="primary" size="small" @click="getList2">查询</el-button>
+          <el-button type="primary" size="small" @click="handleAdd">新增类别</el-button>
+          <el-dropdown @command="handleCommandAdd">
+            <el-button type="primary" size="small">
+              新增产品<i class="el-icon-arrow-down el-icon--right"></i>
+            </el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="1">新增本公司产品</el-dropdown-item>
+              <el-dropdown-item command="2">新增外公司产品</el-dropdown-item>
+            </el-dropdown-menu>
+         </el-dropdown>
+      </div>
+      <div style="display: flex;">
+        <span style="margin-right: 10px;">分组查询:</span>
+        <el-checkbox-group v-model="checkList"  @change="setMealSelect">
+          <el-checkbox  label="product_category_id">类别</el-checkbox>
+          <el-checkbox  label="company_name">公司</el-checkbox>
+        </el-checkbox-group>
+      </div>
+    </div>
+    <!-- 表格 -->
+    <el-table
+     v-if="!isCollapse"
+      :data="tableData"
+      border
+      header-row-class-name="custom-table-header"
+      @sort-change="sortChange"
+      style="width: 100%">
+      <!-- type="expand"开启后展开行 -->
+      <el-table-column prop="productName" label="产品名称" sortable="custom" align="center">
+          <template slot-scope="scope">
+            <div class="items">
+              <el-link v-if="sign == 1" @click="rowClick(scope.row)">{{scope.row.productName}}  </el-link>
+              <span v-else>{{scope.row.productName}} </span>
+            </div>
+          </template>  
+      </el-table-column>
+      <el-table-column prop="marketTime" label="上市时间" sortable="custom" align="center">
+        <template slot-scope="scope">
+          <span>{{ scope.row.marketTime?scope.row.marketTime.slice(0,10):'' }}</span>
+        </template>
+       </el-table-column>
+      <el-table-column prop="companyName" label="所属公司" align="center"> </el-table-column>
+      <el-table-column prop="licenseRate" label="许可费率" sortable="custom" align="center" width="180"> </el-table-column>
+      <el-table-column prop="productCategory.createPersonName" label="创建人" sortable="custom" align="center"> </el-table-column>
+      <el-table-column prop="productExplain" label="产品说明" align="center"> </el-table-column>
+      <el-table-column prop="productCategory.productCategoryName" label="产品类别名称"   sortable="custom" align="center">
+       <template slot-scope="scope">
+        <el-dropdown @command="categoryCommand($event,scope.row.productCategory)" placement="bottom">
+          <span class="el-dropdown-link">
+            {{ scope.row.productCategory? scope.row.productCategory.productCategoryName:'' }}<i class="el-icon-arrow-down el-icon--right"></i>
+          </span>
+          <el-dropdown-menu slot="dropdown" style="text-align: center;">
+            <el-dropdown-item command="1">编辑类别</el-dropdown-item>
+            <el-dropdown-item command="2">趋势图</el-dropdown-item>
+            <el-dropdown-item command="3">预览图片</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+       </template>
+
+      </el-table-column>
+      <el-table-column label="操作" width="180" align="center"> 
+        <template slot-scope="scope">
+          <el-dropdown split-button type="primary" size="small" @command="handleCommand($event,scope.row)" @click="handleEdit(scope.row)">
+            <p>编辑</p> 
+            <el-dropdown-menu slot="dropdown" style="text-align: center;">
+              <el-dropdown-item command="7">预览图片</el-dropdown-item>
+              <el-dropdown-item command="1">产品的架构</el-dropdown-item>
+              <el-dropdown-item command="2">产品的营销数据</el-dropdown-item>
+              <el-dropdown-item command="3">产品趋势图</el-dropdown-item>
+              <el-dropdown-item command="4">产品可视化</el-dropdown-item>
+              <el-dropdown-item command="5">产品相关价值专利</el-dropdown-item>
+              <el-dropdown-item :divided="true" command="6"> <span style="color: red;">删除</span> </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 折叠面板 -->
+    <productCollapse v-else :tableData="tableData" :sign="sign" @editCollapse="handleEditCollapse" @deleteCollapse="deleteCollapse" @sortCollapse="sortCollapse" @categoryEdit="categoryEdit" @rowClickName="rowClick"></productCollapse>
+    <!-- 分页 -->
+    <div style="display: flex;justify-content: center; margin-top: 20px;">
+      <el-pagination
+        background
+        layout="total,prev, pager, next,jumper"
+        :total="total"
+        @current-change="handleCurrentChange"
+        :current-page="queryParams.current" :page-size="queryParams.size">
+      </el-pagination>
+    </div>
+    
+    <!-- 新增、编辑产品弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="visible"
+      width="500"
+      :close-on-click-modal="false"
+      append-to-body
+      :before-close="close">
+
+      <el-form :rules="rules" ref="productForm" :model="productForm" label-width="110px">
+        <el-form-item label="所属产品类别 " prop="productCategoryName" >
+          <div class="selectButton">
+            <el-select v-model="productForm.productCategoryId" @change="productFormSelect" filterable placeholder="请选择所属产品类别" clearable >
+              <el-option
+                v-for="item in options"
+                :key="item.id"
+                :label="item.productCategoryName"
+                :value="item.id">
+              </el-option>
+            </el-select>
+            <el-button @click="handleAdd"><i class="el-icon-plus"></i></el-button>
+          </div>
+        </el-form-item>
+        <el-form-item label="产品名称 " prop="productName">
+          <el-input v-model="productForm.productName" placeholder="请输入产品名称"></el-input>
+        </el-form-item>
+        <el-form-item label="上市时间 " prop="marketTime">
+          <el-date-picker
+            v-model="productForm.marketTime"
+            type="date"
+            placeholder="请选择上市时间"
+            value-format="yyyy-MM-dd"
+            style="width: 100%;">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="所属公司 " prop="companyName">
+          <el-input v-model="productForm.companyName" :disabled="productForm.tenantId?true:false" placeholder="请输入所属公司"></el-input>
+        </el-form-item>
+       
+        <el-form-item label="许可费率 " prop="licenseRates">
+          <el-input v-model="productForm.licenseRates" placeholder="请输入许可费率(许可费率介于0-1之间)"></el-input>
+        </el-form-item>
+        <el-form-item label="图片 ">
+          <el-upload ref="upload"  action="#" :auto-upload="false"  :on-change="handleChange"  list-type="picture" :show-file-list="false">
+          <span v-if="productForm.pictures && productForm.pictures.length>0" class="avatar">
+            <span class="deleteImg">
+             <span> 
+              <i class="el-icon-zoom-in" @click.stop="handlePictureCardPreview"></i>
+              <i class="el-icon-delete" @click.stop="handleRemove"></i></span>
+            </span>
+            <el-image ref="image" style="width:100%;height: 100%;" :src="productForm.pictures[0].id? $p + productForm.pictures[0].url:productForm.pictures[0].url" :preview-src-list="productForm.pictures"></el-image>
+          </span>
+          <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+        </el-upload>
+        </el-form-item>
+        <el-form-item label="产品说明 ">
+          <el-input v-model="productForm.productExplain" type="textarea" placeholder="请输入产品说明"></el-input>
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="close">取 消</el-button>
+        <el-button type="primary" @click="sure">确 定</el-button>
+      </span>
+    </el-dialog>
+    <el-dialog :title="title" :visible.sync="MarketVisible" append-to-body width="1200px" :close-on-click-modal="false">
+      <div>
+        <Marketing-Data :row="rowData"></Marketing-Data>
+      </div>
+    </el-dialog>
+    <!-- 新增、编辑类别弹窗 :close-on-click-modal="false"点击遮罩层阻止弹窗关闭 -->
+    <el-dialog
+      :title="categoryTitle"
+      :visible.sync="categoryVisible"
+      width="500"
+      append-to-body
+      :close-on-click-modal="false"
+      :before-close="categoryClose">
+      <el-form :rules="rules" ref="categoryForm" :model="categoryForm" label-width="80px">
+        <el-form-item label="名称 " prop="productCategoryName">
+          <el-input v-model="categoryForm.productCategoryName" placeholder="请输入名称"></el-input>
+        </el-form-item>
+        <el-form-item label="许可费率 " prop="licenseRate">
+          <el-input v-model="categoryForm.licenseRate" placeholder="请输入许可费率(许可费率介于0-1之间)"></el-input>
+        </el-form-item>
+        <el-form-item label="图片 ">
+          <el-upload ref="upload"  action="#" :auto-upload="false"  :on-change="handleChange"  list-type="picture" :show-file-list="false">
+            <span v-if="categoryForm.pictures&&categoryForm.pictures.length>0" class="avatar">
+              <span class="deleteImg">
+                <span> 
+                  <i class="el-icon-zoom-in" @click.stop="handlePictureCardPreview"></i>
+                  <i class="el-icon-delete" @click.stop="handleRemove"></i>
+                </span>
+              </span>
+                <el-image ref="image" style="width:100%;height: 100%;" :src="categoryForm.pictures[0].id?$p + categoryForm.pictures[0].url:categoryForm.pictures[0].url" :preview-src-list="categoryForm.pictures"></el-image>
+            </span>
+            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+          </el-upload>
+        </el-form-item>
+        <el-form-item label="备注 ">
+          <el-input v-model="categoryForm.remark" type="textarea" placeholder="请输入备注"></el-input>
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="categoryClose">取 消</el-button>
+        <el-button type="primary" @click="categorySure">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import MarketingData from './marketingData.vue'
+import productCollapse from './productCollapse.vue'
+import { commonMethods } from './mixins'
+export default {
+  props:['clientName','sign','patentNo'],
+  components:{
+    MarketingData,
+    productCollapse,
+  },
+  mixins:[commonMethods],
+  data() {
+    const licenseRateRule = (rule , value ,callback)=>{
+      if(value){
+         if (!isNaN(value)) {
+          if (value>=0 && value<=1) {
+            this.productForm.licenseRates=value
+            this.productForm.licenseRate=this.productForm.licenseRates
+            callback()
+          } else {
+            callback(new Error('输入错误,许可费率介于0-1之间'))
+          }
+        } else {
+          callback(new Error('输入错误,许可费率介于0-1之间'))
+        }
+      }else{
+        callback()
+      }
+    }
+    return {
+      input: '',
+      rowData:{},
+      left:'left',
+      tableData: [],
+      options: [],
+      checkList: [],//分组查询(勾中的值)
+      checkListLable: [{lable:'公司',value:'company_name'},{lable:'类别',value:'product_category_id'}],//分组查询(勾中的值)
+      productForm:{},
+      categoryForm:{},
+      visible: false,
+      categoryVisible: false,
+      MarketVisible:false,
+      title: null,
+      categoryTitle: null,
+      queryParams: {
+        patentNo: this.patentNo ? this.patentNo[0] : null,
+        groupBy:'',
+        orderBy:'',//排序名称
+        orderType:'',//排序类型
+        size: 10,
+        current:1,
+      },
+      total:0,
+      file:[],
+      rules: {
+        productName:[{ required: true, message: '请输入产品名称', trigger: 'blur' },],
+        licenseRate:[{ required: false, validator:licenseRateRule, trigger: 'blur' },],
+        productCategoryName:[{ required: true, message: '请选择产品类别名称', trigger: 'blur' },],
+      },
+      isCollapse:false,
+    }
+  },
+  computed: {
+    userinfo() {
+      return this.$store.state.user.userinfo
+    }
+  },
+  watch:{
+    patentNo(val){
+      this.queryParams.patentNo = val[0]
+      this.getList()
+    },
+  },
+  async mounted() { 
+    // 查询产品
+    await this.getList()
+    // 查询类别
+    await this.getListCategory()
+  },
+  methods: {
+    // 面板的产品类别编辑
+    categoryEdit(row) {
+      this.categoryCommand('1',row)
+    },
+    // 面板的table产品排序
+    sortCollapse({ column, prop, order }) {
+      this.sortChange({ column, prop, order })
+    },
+    // 面板的table产品编辑
+    handleEditCollapse(val) {
+      this.handleEdit(val)
+    },
+    // 面板的table产品删除
+    deleteCollapse(val) {
+      this.handleCommand('6',val)
+    },
+    // 多选框只能选中一个
+    async setMealSelect() {
+      this.checkList = this.checkList.slice(-1)
+      this.queryParams.groupBy = this.checkList[0]
+      await this.getList2()
+      this.queryParams.orderBy=''
+      this.queryParams.orderType = ''
+      this.$nextTick(() => {
+        if (this.checkList.length==0) {
+        this.isCollapse=false
+      } else {
+        this.isCollapse=true
+      }
+     })
+     
+    },
+    //查询类别
+    getListCategory() { 
+      this.$api.queryProductCategorys({}).then(res => {
+        if (res.code == 200) {
+          this.options = res.data.list
+        }
+      })
+    },
+    // 新增类别
+    handleAdd() { 
+      this.categoryTitle = '新增类别'
+      this.categoryVisible=true
+    },
+    // 类别操作
+    categoryCommand(command, row) {
+      switch (command) {
+        case '1'://编辑
+          this.categoryTitle = '编辑类别'
+          this.categoryForm = JSON.parse(JSON.stringify(row))
+          this.categoryVisible=true
+          break;
+        case '2'://趋势图
+          this.showTrend(row,1)
+          break;
+        case '3'://预览图片
+          this.previewPictures(row)
+          break;
+      
+        default:
+          break;
+      }
+    },
+     // 类别弹窗确定
+     categorySure() {
+      this.$refs.categoryForm.validate(valid => {
+        if (valid) {
+          var formData = new FormData()
+          if (this.file) {
+            for (var i = 0; i < this.file.length; i++) {
+              formData.append("files", this.file[i]);
+            }
+          }
+          formData.append('jsons', JSON.stringify(this.categoryForm))
+          if (this.categoryForm.id) {
+            this.$api.updateProductCategorys(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("编辑产品类别成功")
+                this.getList()
+                this.categoryClose()
+              }
+            })
+          } else {
+            this.$api.addNewProductCategorys(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("新增产品类别成功")
+                this.getListCategory()
+                this.categoryClose()
+              }
+            })
+          }
+        }
+      })
+    },
+    // 类别弹窗关闭
+    categoryClose() {
+      this.categoryVisible = false
+      this.$refs.upload.clearFiles()
+      this.categoryForm = {}
+      this.file=[]
+    },
+    rowClick (row) {
+      this.$emit('productId', row.id)
+    },
+    //查询产品
+    async getList() { 
+      await this.$api.queryProducts(this.queryParams).then(res => {
+        if (res.code == 200) {
+          this.tableData = res.data.list
+          this.total = res.data.totalSizes
+        }
+      })
+    },
+    async getList2() {
+      this.queryParams.current = 1
+      await this.getList();
+    },
+    // 排序
+    sortChange({ column, prop, order }) {
+      if (!order) {
+        return false
+      }
+      const o = {
+        'descending': 'desc',
+        'ascending': 'asc',
+      }
+      if (prop.search('productCategory') != -1) {
+        this.queryParams.orderBy = prop.slice(16)
+      } else {
+         this.queryParams.orderBy = prop
+      }
+      this.queryParams.orderType = o[order]
+      this.getList()
+    },
+    // 新增产品
+    handleCommandAdd(command) {
+      this.productForm.patentNo = this.queryParams.patentNo
+      switch (command) {
+        case '1':
+          this.title = '新增本公司产品'
+          // 获取所属租户公司
+          this.productForm.companyName=this.userinfo.tenantName
+          this.productForm.tenantId=this.userinfo.tenantId
+          this.visible=true
+          break;
+        case '2':
+          this.title = '新增外公司产品'
+          this.$set(this.productForm,'companyName',this.clientName)
+          this.visible=true
+          break;
+      
+        default:
+          break;
+      }
+     
+    },
+    // 新增产品选择产品类别
+    productFormSelect(val) {
+      this.options.forEach(item => {
+        if (item.id == val) {
+          this.productForm.licenseRates=item.licenseRate
+        }
+      })
+    },
+    // 编辑
+    handleEdit(row) { 
+      this.title = '编辑产品'
+      this.productForm = JSON.parse(JSON.stringify(row))
+      this.$set(this.productForm,'licenseRates',this.productForm.licenseRate)
+      this.productForm.productCategoryName = row.productCategory.productCategoryName
+      this.visible=true
+    },
+    // 编辑右侧菜单项
+    handleCommand(ev,row) { 
+      switch (ev) {
+        case '1':// 产品的架构
+          this.frameworkIndex(row)
+          break;
+        case '2': // 产品的营销数据
+          this.rowData = row
+          this.title = row.productName+'营销数据'
+          this.MarketVisible = true
+          break;
+        case '3':
+          this.showTrend(row,2)
+          // 产品趋势图
+          break;
+        case '4':
+          this.getMind(row,2)
+          // 产品可视化
+          break;
+        case '5':
+          // 产品相关价值专利
+          this.relatedPatentsIndex(row)
+          break;
+        case '6':
+          this.$confirm('此操作将永久删除该产品, 是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }).then(() => {
+            this.$api.deleteProducts({id:row.id}).then(res => {
+              if (res.code == 200) {
+                if (this.tableData.length == 1 && this.queryParams.current != 1) {
+                  this.queryParams.current -= 1
+                }
+                this.getList()
+                this.$message.success("删除产品成功")
+              }
+            })
+          }).catch(() => {
+            this.$message.info("已取消删除")
+          });
+          break;
+        case '7':// 图片预览
+          this.previewPictures(row)
+          break;
+      
+        default:
+          break;
+      }
+    },
+    // 预览图片
+    previewPictures(row) {
+      if (row.pictures && row.pictures.length>0) {
+              var item = row.pictures[0]//图片每条信息
+              var FileUrl = this.$p + row.pictures[0].url//每张图片的url
+              var isPicture = 1
+              const router = this.$router.resolve({
+                    path: '/checkFile',
+                    query: {
+                        row: JSON.stringify(item),
+                        FileUrl: FileUrl,
+                        isPicture:isPicture,
+                    }
+                })
+              window.open(router.href, '_blank');
+          } else {
+            this.$message.info("暂无图片,请先上传图片再进行预览!")
+          }
+    },
+    frameworkIndex(row) {
+      const router = this.$router.resolve({
+        path: '/frameworkIndex',
+      })
+      this.$s.setSession('productRow',row)
+      window.open(router.href, '_blank');
+    },
+    relatedPatentsIndex(row) {
+      const router = this.$router.resolve({
+        path: '/relatedPatentsIndex',
+        query: {
+          id:row.id,
+          name:row.productName
+        }
+      })
+      // this.$s.setSession('patentsProductRow',row)
+      window.open(router.href, '_blank');
+    },
+    // 弹窗确定
+    sure() {
+      this.$refs.productForm.validate(valid => {
+        if (valid) {
+          var formData = new FormData()
+          if (this.file) {
+            for (var i = 0; i < this.file.length; i++) {
+              formData.append("files", this.file[i]);
+            }
+          }
+          formData.append('jsons', JSON.stringify(this.productForm))
+          if (this.productForm.id) {
+            this.$api.updateProducts(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("编辑产品成功")
+                this.getList()
+                this.close()
+              }
+            })
+          } else {
+             this.$api.addNewProducts(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("新增产品成功")
+                this.getList()
+                this.close()
+              }
+            })
+          }
+         
+        }
+      })
+      
+      
+    },
+    // 弹窗关闭
+    close() {
+      this.productForm = {}
+      this.file = []
+      this.$refs.upload.clearFiles()
+      this.visible = false
+    },
+    // 分页
+    handleCurrentChange(val) {
+      this.queryParams.current = val
+      this.getList()
+    },
+    // 上传图片
+    handlePictureCardPreview(file) {
+        this.$refs.image.showViewer = true
+    },
+    handleRemove() {
+      this.file = []
+        this.productForm.pictures = []
+    },
+    handleChange(file, fileList) {
+      this.$set(this.productForm,'pictures', [{url:file.url}])
+      this.file=[file.raw]
+    },
+   
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+@import '@/assets/css/selectButton.scss';
+.avatar-uploader-icon {
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    font-size: 28px;
+    color: #8c939d;
+    width: 148px;
+    height: 148px;
+    line-height: 148px;
+    text-align: center;
+  }
+ .avatar {
+  
+  position: relative;
+    width: 148px;
+    height: 148px;
+    display: block;
+  }
+  .avatar:hover .deleteImg {
+    display: block;
+}
+.deleteImg {
+  display: none;
+  font-size: 30px;
+  width: 148px;
+  height: 148px;
+  background-color:black;
+	opacity: 0.6; 
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  margin: auto;
+  z-index: 999;
+}
+.deleteImg span i{
+  margin-left: 10px;
+  color: #fff;
+}
+.deleteImg span{
+  display: flex;
+  align-items:center;/*垂直居中*/
+  justify-content: center;/*水平居中*/
+  width:100%;
+  height:100%;
+}
+</style>

+ 206 - 0
RMS-FrontEnd/src/views/product/components/productCollapse.vue

@@ -0,0 +1,206 @@
+<template>
+  <div>
+    <el-collapse v-model="activeNames" @change="handleChange" :accordion="true">
+      <el-collapse-item v-for="(item,index) in tableData" :key="index" :title="item.productCategoryName?item.productCategoryName:item.companyName" :name="item.id?item.id:index">
+        <el-table
+            :data="item.products"
+            border
+            header-row-class-name="custom-table-header"
+            @sort-change="sortChange"
+            style="width: 100% ;padding: 20px;">
+            <el-table-column prop="productName" label="产品名称" sortable="custom" align="center">
+                <template slot-scope="scope">
+                  <div class="items">
+                    <el-link v-if="sign == 1" @click="rowClick(scope.row)">{{scope.row.productName}}  </el-link>
+                    <span v-else>{{scope.row.productName}} </span>
+                  </div>
+                </template>  
+            </el-table-column>
+            <el-table-column prop="marketTime" label="上市时间" sortable="custom" align="center">
+              <template slot-scope="scope">
+                <span>{{ scope.row.marketTime?scope.row.marketTime.slice(0,10):'' }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="companyName" label="所属公司" align="center"> </el-table-column>
+            <el-table-column prop="licenseRate" label="许可费率" sortable="custom" align="center" width="100"> </el-table-column>
+            <el-table-column prop="productCategory.createPersonName" label="创建人" sortable="custom" align="center"> </el-table-column>
+            <el-table-column prop="productExplain" label="产品说明" align="center"> </el-table-column>
+            <el-table-column prop="productCategory.productCategoryName" label="产品类别名称"   sortable="custom" align="center"> 
+              <template slot-scope="scope">
+                <el-dropdown @command="categoryCommand($event,scope.row.productCategory)" placement="bottom">
+                  <span class="el-dropdown-link">
+                    {{ scope.row.productCategory?scope.row.productCategory.productCategoryName:'' }}<i class="el-icon-arrow-down el-icon--right"></i>
+                  </span>
+                  <el-dropdown-menu slot="dropdown" style="text-align: center;">
+                    <el-dropdown-item command="1">编辑类别</el-dropdown-item>
+                    <el-dropdown-item command="2">趋势图</el-dropdown-item>
+                    <el-dropdown-item command="3">预览图片</el-dropdown-item>
+                  </el-dropdown-menu>
+                </el-dropdown>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="180" align="center"> 
+              <template slot-scope="scope">
+                <el-dropdown split-button type="primary" size="small" @command="handleCommand($event,scope.row)" @click="handleEdit(scope.row)">
+                  <p>编辑</p> 
+                  <el-dropdown-menu slot="dropdown" style="text-align: center;">
+                    <el-dropdown-item command="7">预览图片</el-dropdown-item>
+                    <el-dropdown-item command="1">产品的架构</el-dropdown-item>
+                    <el-dropdown-item command="2">产品的营销数据</el-dropdown-item>
+                    <el-dropdown-item command="3">产品趋势图</el-dropdown-item>
+                    <el-dropdown-item command="4">产品可视化</el-dropdown-item>
+                    <el-dropdown-item command="5">产品相关价值专利</el-dropdown-item>
+                    <el-dropdown-item :divided="true" command="6"> <span style="color: red;">删除</span> </el-dropdown-item>
+                  </el-dropdown-menu>
+                </el-dropdown>
+              </template>
+            </el-table-column>
+          </el-table>
+      </el-collapse-item>
+    </el-collapse>
+    <el-dialog :title="title" :visible.sync="MarketVisible" append-to-body width="1200px" :close-on-click-modal="false">
+      <div>
+        <Marketing-Data :row="rowData"></Marketing-Data>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import MarketingData from './marketingData.vue'
+import { commonMethods } from './mixins'
+export default {
+  props: ['tableData', 'sign'],
+  mixins:[commonMethods],
+  components:{
+    MarketingData,
+  },
+  data() {
+    return {
+      activeNames: [],
+      rowData: {},
+      title:'',
+      MarketVisible:false,
+    };
+  },
+  methods: {
+        // 类别操作
+    categoryCommand(command,row) {
+      switch (command) {
+        case '1'://编辑
+        this.$emit('categoryEdit',row)
+          break;
+        case '2'://趋势图
+          this.showTrend(row,1)
+          break;
+        case '3'://预览图片
+          this.previewPictures(row)
+          break;
+      
+        default:
+          break;
+      }
+    },
+    handleCommand(ev,row) { 
+      switch (ev) {
+        case '1':// 产品的架构
+          this.frameworkIndex(row)
+          break;
+        case '2': // 产品的营销数据
+          this.rowData = row
+          this.title = row.productName+'营销数据'
+          this.MarketVisible = true
+          break;
+        case '3':
+          this.showTrend(row,2)
+          // 产品趋势图
+          break;
+        case '4':
+          this.getMind(row,2)
+          // 产品可视化
+          break;
+        case '5':
+          // 产品相关价值专利
+          this.relatedPatentsIndex(row)
+          break;
+        case '6':
+            this.$emit('deleteCollapse',row)
+          break;
+        case '7':// 图片预览
+          this.previewPictures(row)
+          break;
+      
+        default:
+          break;
+      }
+    },
+    previewPictures(row) {
+      if (row.pictures && row.pictures.length>0) {
+              var item = row.pictures[0]//图片每条信息
+              var FileUrl = this.$p + row.pictures[0].url//每张图片的url
+              var isPicture = 1
+              const router = this.$router.resolve({
+                    path: '/checkFile',
+                    query: {
+                        row: JSON.stringify(item),
+                        FileUrl: FileUrl,
+                        isPicture:isPicture,
+                    }
+                })
+              window.open(router.href, '_blank');
+          } else {
+            this.$message.info("暂无图片,请先上传图片再进行预览!")
+          }
+    },
+    frameworkIndex(row) {
+      const router = this.$router.resolve({
+        path: '/frameworkIndex',
+      })
+      this.$s.setSession('productRow',row)
+      window.open(router.href, '_blank');
+    },
+    relatedPatentsIndex(row) {
+      const router = this.$router.resolve({
+        path: '/relatedPatentsIndex',
+        query: {
+          id:row.id,
+          name:row.productName
+        }
+      })
+      // this.$s.setSession('patentsProductRow',row)
+      window.open(router.href, '_blank');
+    },
+    // 编辑
+    handleEdit(row) { 
+      this.$emit('editCollapse',row)
+    },
+    handleChange(val) {
+    },
+    rowClick (row) {
+      this.$emit('rowClickName', row)
+    },
+    // 排序
+    sortChange({ column, prop, order }) {
+      this.$emit('sortCollapse',{ column, prop, order })
+    },
+  }
+}
+</script>
+
+<style lang="scss" >
+.el-collapse-item__header {
+    padding-left: 15px;
+    /* display: flex; */
+    align-items: center;
+    height: 48px;
+    line-height: 48px;
+    background-color: #FFF;
+    color: #303133;
+    cursor: pointer;
+    border-bottom: 1px solid #EBEEF5;
+    font-size: 13px;
+    font-weight: 500;
+    transition: border-bottom-color .3s;
+    outline: 0;
+}
+</style>

+ 493 - 0
RMS-FrontEnd/src/views/product/components/products.vue

@@ -0,0 +1,493 @@
+<template>
+  <div style="margin: 20px;">
+    <div style="display: flex;margin-bottom: 20px;">
+      <el-input v-model="queryParams.productName"  size="small" placeholder="请输入产品名称" style="width: 300px;margin-right: 10px;"></el-input>
+      <el-input v-model="queryParams.companyName"  size="small" placeholder="请输入产品所属公司名称" style="width: 300px;margin-right: 10px;"></el-input>
+      <el-button type="primary" size="small" @click="getList2">查询</el-button>
+      <!-- <el-button type="primary" size="small" @click="handleAdd">新增</el-button> -->
+      <el-dropdown @command="handleCommandAdd">
+        <el-button type="primary" size="small">
+          新增产品<i class="el-icon-arrow-down el-icon--right"></i>
+        </el-button>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item command="1">新增本公司产品</el-dropdown-item>
+          <el-dropdown-item command="2">新增外公司产品</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+    <!-- 表格 -->
+    <el-table
+      :data="tableData"
+      border
+      header-row-class-name="custom-table-header"
+      @sort-change="sortChange"
+      style="width: 100%">
+      <!-- type="expand"开启后展开行 -->
+      <el-table-column prop="productName" label="产品名称" sortable="custom" align="center">
+          <template slot-scope="scope">
+            <div class="items">
+              <el-link v-if="sign == 1" @click="rowClick(scope.row)">{{scope.row.productName}}  </el-link>
+              <span v-else>{{scope.row.productName}} </span>
+            </div>
+          </template>  
+      </el-table-column>
+      <el-table-column prop="marketTime" label="上市时间" sortable="custom" align="center">
+        <template slot-scope="scope">
+          <span>{{ scope.row.marketTime?scope.row.marketTime.slice(0,10):'' }}</span>
+        </template>
+       </el-table-column>
+      <el-table-column prop="companyName" label="所属公司或租户名称" align="center"> </el-table-column>
+      <el-table-column prop="licenseRate" label="许可费率" sortable="custom" align="center" width="180"> </el-table-column>
+      <!-- <el-table-column prop="pictures" label="产品图片" align="center"> </el-table-column> -->
+      <el-table-column prop="productExplain" label="产品说明" align="center"> </el-table-column>
+      <el-table-column label="操作" width="180" align="center"> 
+        <template slot-scope="scope">
+          <!-- <el-button type="text" size="small" @click="handleEdit(scope.row)">编辑</el-button> -->
+          <el-dropdown split-button type="primary" size="small" @command="handleCommand($event,scope.row)" @click="handleEdit(scope.row)">
+            <p>编辑</p> 
+            <el-dropdown-menu slot="dropdown" style="text-align: center;">
+              <el-dropdown-item command="7">预览图片</el-dropdown-item>
+              <el-dropdown-item command="1">产品的架构</el-dropdown-item>
+              <el-dropdown-item command="2">产品的营销数据</el-dropdown-item>
+              <el-dropdown-item command="3">产品趋势图</el-dropdown-item>
+              <el-dropdown-item command="4">产品可视化</el-dropdown-item>
+              <el-dropdown-item command="5">产品相关价值专利</el-dropdown-item>
+              <el-dropdown-item :divided="true" command="6"> <span style="color: red;">删除</span> </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div style="display: flex;justify-content: center;">
+      <el-pagination
+        background
+        layout="total,prev, pager, next,jumper"
+        :total="total"
+        @current-change="handleCurrentChange"
+        :current-page="queryParams.current" :page-size="queryParams.size">
+      </el-pagination>
+    </div>
+    
+    <!-- 新增、编辑弹窗 -->
+    <el-dialog
+      :title="title"
+      :visible.sync="visible"
+      width="500"
+      :close-on-click-modal="false"
+      append-to-body
+      :before-close="close">
+
+      <el-form :rules="rules" ref="productForm" :model="productForm" label-width="100px">
+        <el-form-item label="所属产品类别 " prop="productCategoryName">
+          <el-input v-model="productForm.productCategoryName" disabled placeholder="请输入所属产品类别"></el-input>
+        </el-form-item>
+        <el-form-item label="产品名称 " prop="productName">
+          <el-input v-model="productForm.productName" placeholder="请输入产品名称"></el-input>
+        </el-form-item>
+        <el-form-item label="上市时间 " prop="marketTime">
+          <!-- <el-input v-model="productForm.marketTime" placeholder="请输入上市时间"></el-input> -->
+          <el-date-picker
+            v-model="productForm.marketTime"
+            type="date"
+            placeholder="请选择上市时间"
+            value-format="yyyy-MM-dd"
+            style="width: 100%;">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="所属公司 " prop="companyName">
+          <el-input v-model="productForm.companyName" :disabled="productForm.tenantId?true:false" placeholder="请输入所属公司或租户名称"></el-input>
+        </el-form-item>
+       
+        <el-form-item label="许可费率 " prop="licenseRates">
+          <el-input v-model="productForm.licenseRates" placeholder="请输入许可费率(许可费率介于0-1之间)"></el-input>
+          <!-- <el-input-number v-model="productForm.licenseRate" :precision="2" :step="0.05" :max="1" :min="0" placeholder="请输入许可费率" style="width: 100%;"></el-input-number> -->
+        </el-form-item>
+        <el-form-item label="图片 ">
+          <!-- <el-input v-model="productForm.pictures"></el-input> -->
+          <el-upload ref="upload"  action="#" :auto-upload="false"  :on-change="handleChange"  list-type="picture" :show-file-list="false">
+          <span v-if="productForm.pictures && productForm.pictures.length>0" class="avatar">
+            <span class="deleteImg">
+             <span> 
+              <i class="el-icon-zoom-in" @click.stop="handlePictureCardPreview"></i>
+              <i class="el-icon-delete" @click.stop="handleRemove"></i></span>
+            </span>
+            <el-image ref="image" style="width:100%;height: 100%;" :src="productForm.pictures[0].id? $p + productForm.pictures[0].url:productForm.pictures[0].url" :preview-src-list="productForm.pictures"></el-image>
+          </span>
+          <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+        </el-upload>
+        </el-form-item>
+        <el-form-item label="产品说明 ">
+          <el-input v-model="productForm.productExplain" type="textarea" placeholder="请输入产品说明"></el-input>
+        </el-form-item>
+      </el-form>
+
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="close">取 消</el-button>
+        <el-button type="primary" @click="sure">确 定</el-button>
+      </span>
+    </el-dialog>
+    <el-dialog :title="title" :visible.sync="MarketVisible" width="1200px" :close-on-click-modal="false">
+      <div>
+        <Marketing-Data :row="rowData"></Marketing-Data>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import MarketingData from './marketingData.vue'
+import { commonMethods } from './mixins'
+export default {
+  props:['categoryRow','clientName','sign','patentNo'],
+  components:{
+    MarketingData
+  },
+  mixins:[commonMethods],
+  data() {
+    const licenseRateRule = (rule , value ,callback)=>{
+      if(value){
+         if (!isNaN(value)) {
+          if (value>=0 && value<=1) {
+            this.productForm.licenseRates=value
+            this.productForm.licenseRate=this.productForm.licenseRates
+            callback()
+          } else {
+            callback(new Error('输入错误,许可费率介于0-1之间'))
+          }
+        } else {
+          callback(new Error('输入错误,许可费率介于0-1之间'))
+        }
+      }else{
+        callback()
+      }
+    }
+    return {
+      input: '',
+      rowData:{},
+      left:'left',
+      tableData: [],
+      productForm:{},
+      visible: false,
+      MarketVisible:false,
+      title: null,
+      queryParams: {
+        productCategoryId : this.categoryRow.id,
+        patentNo:this.patentNo?this.patentNo[0]:null,
+        // licenseRate : this.categoryRow.licenseRate,
+        orderBy:'',//排序名称
+        orderType:'',//排序类型
+        size: 10,
+        current:1,
+      },
+      total:0,
+      file:[],
+      rules: {
+        // productCategoryName:[{ required: true, message: '请输入产品类别名称', trigger: 'change' },],
+        productName:[{ required: true, message: '请输入产品名称', trigger: 'blur' },],
+        licenseRates:[{ required: false, validator:licenseRateRule, trigger: 'blur' },],
+      }
+    }
+  },
+  computed: {
+    userinfo() {
+      return this.$store.state.user.userinfo
+    }
+  },
+  watch:{
+    patentNo(val){
+      this.queryParams.patentNo = val[0]
+      this.getList()
+    }
+  },
+  mounted() { 
+    this.getList()
+  },
+  methods: {
+    rowClick (row) {
+      this.$emit('productId', row.id)
+    },
+    //查询
+    getList() { 
+      this.$api.queryProducts(this.queryParams).then(res => {
+        if (res.code==200) {
+          this.tableData = res.data.list
+          this.total=res.data.totalSizes
+        }
+      })
+    },
+    getList2() {
+      this.queryParams.current = 1
+      this.getList();
+    },
+    // 排序
+    sortChange(row) {
+      switch (row.order) {
+        case "ascending":
+          // this.queryParams.orderType = 
+          this.queryParams.orderBy = row.prop
+          this.getList()
+          break;
+        case "descending":
+          // this.queryParams.orderType = 
+          this.queryParams.orderBy = row.prop
+          this.getList()
+          break;
+        default:
+          break;
+      }
+    },
+    // 新增产品
+    handleCommandAdd(command) {
+       //产品类别
+      this.productForm.productCategoryId = this.categoryRow.id
+      this.productForm.productCategoryName = this.categoryRow.productCategoryName
+      this.productForm.patentNo = this.queryParams.patentNo
+      this.$set(this.productForm,'licenseRates',this.categoryRow.licenseRate)
+      this.productForm.licenseRate = this.categoryRow.licenseRate
+      switch (command) {
+        case '1':
+          this.title = '新增本公司产品'
+          // 获取所属租户公司
+          this.productForm.companyName=this.userinfo.tenantName
+          this.productForm.tenantId=this.userinfo.tenantId
+          this.visible=true
+          break;
+        case '2':
+          this.title = '新增外公司产品'
+          this.$set(this.productForm,'companyName',this.clientName)
+          this.visible=true
+          break;
+      
+        default:
+          break;
+      }
+     
+    },
+    // 编辑
+    handleEdit(row) { 
+      this.title = '编辑产品'
+      this.productForm = JSON.parse(JSON.stringify(row))
+      this.$set(this.productForm,'licenseRates',this.productForm.licenseRate)
+      this.productForm.productCategoryName = this.categoryRow.productCategoryName
+      this.visible=true
+    },
+    // 编辑右侧菜单项
+    handleCommand(ev,row) { 
+      switch (ev) {
+        case '1':// 产品的架构
+          this.frameworkIndex(row)
+          break;
+        case '2': // 产品的营销数据
+          this.rowData = row
+          this.title = row.productName+'营销数据'
+          this.MarketVisible = true
+          break;
+        case '3':
+          this.showTrend(row,2)
+          // 产品趋势图
+          break;
+        case '4':
+          this.getMind(row,2)
+          // 产品可视化
+          break;
+        case '5':
+          // 产品相关价值专利
+          this.relatedPatentsIndex(row)
+          break;
+        case '6':
+          this.$confirm('此操作将永久删除该产品, 是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          }).then(() => {
+            this.$api.deleteProducts({id:row.id}).then(res => {
+              if (res.code == 200) {
+                if (this.tableData.length == 1 && this.queryParams.current != 1) {
+                  this.queryParams.current -= 1
+                }
+                this.getList()
+                this.$message.success("删除产品成功")
+              }
+            })
+          }).catch(() => {
+            this.$message.info("已取消删除")
+          });
+          break;
+        case '7':// 图片预览
+          if (row.pictures && row.pictures.length>0) {
+              var item = row.pictures[0]//图片每条信息
+              var FileUrl = this.$p + row.pictures[0].url//每张图片的url
+              var isPicture = 1
+              const router = this.$router.resolve({
+                    path: '/checkFile',
+                    query: {
+                        row: JSON.stringify(item),
+                        FileUrl: FileUrl,
+                        isPicture:isPicture,
+                    }
+                })
+              window.open(router.href, '_blank');
+          } else {
+            this.$message.info("暂无图片,请先上传图片再进行预览!")
+          }
+          break;
+      
+        default:
+          break;
+      }
+    },
+    
+    frameworkIndex(row) {
+      const router = this.$router.resolve({
+        path: '/frameworkIndex',
+      })
+      this.$s.setSession('productRow',row)
+      window.open(router.href, '_blank');
+    },
+    relatedPatentsIndex(row) {
+      const router = this.$router.resolve({
+        path: '/relatedPatentsIndex',
+        query: {
+          id:row.id,
+          name:row.productName
+        }
+      })
+      // this.$s.setSession('patentsProductRow',row)
+      window.open(router.href, '_blank');
+    },
+    // 弹窗确定
+    sure() {
+      this.$refs.productForm.validate(valid => {
+        if (valid) {
+          
+          var formData = new FormData()
+          if (this.file) {
+            for (var i = 0; i < this.file.length; i++) {
+              formData.append("files", this.file[i]);
+            }
+          }
+          formData.append('jsons', JSON.stringify(this.productForm))
+
+          if (this.productForm.id) {
+            this.$api.updateProducts(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("编辑产品成功")
+                this.getList()
+                this.close()
+              }
+            })
+          } else {
+             this.$api.addNewProducts(formData).then(res => {
+              if (res.code == 200) {
+                this.$message.success("新增产品成功")
+                this.getList()
+                this.close()
+              }
+            })
+          }
+         
+        }
+      })
+      
+      
+    },
+    // 弹窗关闭
+    close() {
+      this.productForm = {}
+      this.file = []
+      this.$refs.upload.clearFiles()
+      this.visible = false
+    },
+    // 分页
+    handleCurrentChange(val) {
+      this.queryParams.current = val
+      this.getList()
+    },
+    // 许可费率事件//优化
+    // inpBlur(val) {
+    //   console.log(val)
+    //   if (val) {
+    //     if (!isNaN(val)) {
+    //       if (val >= 0 && val <= 1) {
+    //         this.productForm.licenseRate = val
+    //       } else {
+    //         if (val > 1) {
+    //           this.productForm.licenseRate = 1
+    //           this.productForm.licenseRates = 1
+    //         }
+    //         if (val < 0) {
+    //           this.productForm.licenseRate = 0
+    //           this.productForm.licenseRates = 0
+    //         }
+    //       }
+    //     } else {
+    //       this.productForm.licenseRates = this.productForm.licenseRate?this.productForm.licenseRate:''
+    //     }
+    //   }
+      
+    // },
+    // 上传图片
+    handlePictureCardPreview(file) {
+        this.$refs.image.showViewer = true
+    },
+    handleRemove() {
+      this.file = []
+        this.productForm.pictures = []
+    },
+    handleChange(file, fileList) {
+      this.$set(this.productForm,'pictures', [{url:file.url}])
+      this.file=[file.raw]
+    },
+   
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.avatar-uploader-icon {
+    background-color: #fbfdff;
+    border: 1px dashed #c0ccda;
+    font-size: 28px;
+    color: #8c939d;
+    width: 148px;
+    height: 148px;
+    line-height: 148px;
+    text-align: center;
+  }
+ .avatar {
+  
+  position: relative;
+    width: 148px;
+    height: 148px;
+    display: block;
+  }
+  .avatar:hover .deleteImg {
+    display: block;
+}
+.deleteImg {
+  display: none;
+  font-size: 30px;
+  width: 148px;
+  height: 148px;
+  background-color:black;
+	opacity: 0.6; 
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  bottom: 0;
+  margin: auto;
+  z-index: 999;
+}
+.deleteImg span i{
+  margin-left: 10px;
+  color: #fff;
+}
+.deleteImg span{
+  display: flex;
+  align-items:center;/*垂直居中*/
+  justify-content: center;/*水平居中*/
+  width:100%;
+  height:100%;
+}
+</style>

+ 670 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/SelectedPatent.vue

@@ -0,0 +1,670 @@
+<template>
+  <div style="padding:20px">
+    <div style="display: flex; justify-content: space-between;">
+      <div :style="{ visibility: selectedTotal > 0 ? 'visible' : 'hidden' }">
+        已勾选 <b>{{ selectedTotal }}</b> 条
+      </div>
+      <div style="display: flex;padding-bottom: 20px;">
+        <el-input v-model="params.patentNo" size="small" placeholder="请输入专利号进行查询(多个专利号查询中间请用符号“|”隔开)"></el-input>
+        <el-button type="primary" size="small" @click="getList">查询</el-button>
+      </div>
+      
+      <div style="display:flex;justify-content: flex-end;padding-bottom:20px">
+        <div class="btn1" @click="sift"
+          style="width:80px;height:32px;display:flex;justify-content:space-around;border-radius:5px; cursor: pointer;">
+          <img src="@/assets/img/filtrationSearch.png" alt=""
+            style="width:16px;height:16px;margin-top:9px;margin-left:8px">
+          <p style="margin:0 8px 0 0;line-height:32px;color:white;font-size:14px">筛选</p>
+        </div>
+        <el-popover placement="bottom" title="" width="250" trigger="click">
+          <el-main class="patent-fast-edit-popover" v-loading="selectNumberLoading">
+            <div class="btn" @click="handleSelectNumber(0)">本页选择</div>
+            <div class="btn" @click="handleSelectNumber(1)">全部选择</div>
+            <el-divider></el-divider>
+            <div class="select-number">
+              <span>从</span>
+              <el-input size="mini" v-model="queryParams.startNumber"></el-input>
+              <span>到</span>
+              <el-input size="mini" v-model="queryParams.endNumber"></el-input>
+              <el-button type="text" size="" @click="handleSelectNumber(2)">确定</el-button>
+            </div>
+          </el-main>
+          <el-button type="info" size="small" class="margin-left_10" slot="reference">
+            选择专利<i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+        </el-popover>
+        <el-button type="text" size="small" class="margin-left_10" @click="handleCancelSelectNumber">取消选择</el-button>
+        <el-button type="primary" size="small" @click="savePatentList" :loading="btnLoading">保存</el-button>
+      </div>
+    </div>
+    <!-- 数据表格 -->
+    <el-table :data="tableData" border :row-key="getRowKeys"  ref="table" id="table"
+      header-row-class-name="custom-table-header" style="min-width: 100%; overflow:auto">
+      <el-table-column width="80" align="center">
+        <template slot-scope="scope">
+          <div>
+            <el-checkbox-group v-model="checkList" style="display:inline-block">
+              <el-checkbox :label="scope.row.patentNo" @change="getFunInfo(scope.row.patentNo)">
+                <span>{{ (scope.$index + 1) + ((queryParams.current - 1) * queryParams.size) }}</span>
+              </el-checkbox>
+            </el-checkbox-group>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="patentNo" label="专利号" align="center" width="200px">
+        <template slot-scope="scope">
+          <div>
+            <el-link type="primary" @click="toPatentDetails(scope.row.patentNo)">{{ scope.row.patentNo }}</el-link>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="name" label="专利标题" align="center" width="300px">
+      </el-table-column>
+      <el-table-column prop="simpleStatus" align="center" label="状态">
+      </el-table-column>
+
+    </el-table>
+    <div class="pagination">
+      <el-pagination :current-page.sync="queryParams.current" :page-size="queryParams.size" :total="total"
+        @current-change="handleCurrentChange" layout="total, prev, pager, next, jumper" background></el-pagination>
+    </div>
+
+    <el-dialog title="筛选" :visible.sync="visible" width="1200px" max-height="800" :before-close="close" append-to-body>
+      <div style="height: 500px">
+        <el-form ref="form" :model="form" label-width="80px" label-position="left">
+          <el-form-item label="标题">
+            <el-input type="textarea" v-model="form.patentName" placeholder="请输入标题查询"></el-input>
+          </el-form-item>
+          <el-form-item label="摘要">
+            <el-input type="textarea" v-model="form.abstractStr" placeholder="请输入摘要查询"></el-input>
+          </el-form-item>
+          <el-form-item label="专利号">
+            <el-input type="textarea" v-model="form.patentNo"
+              placeholder="请输入专利号查询(多个专利号查询中间请用符号“|”隔开,示例:“专利号|专利号”)"></el-input>
+          </el-form-item>
+          <el-form-item label="申请号">
+            <el-input type="textarea" v-model="form.applicationNo" placeholder="请输入申请号查询"></el-input>
+          </el-form-item>
+          <el-form-item label="申请人">
+            <el-input type="textarea" v-model="form.applicationName"
+              placeholder="请输入申请人查询(多个申请人查询中间请用符号“|”隔开,示例:“申请人|申请人”)"></el-input>
+          </el-form-item>
+          <el-form-item label="权利人">
+            <el-input type="textarea" v-model="form.obligeeName"
+              placeholder="请输入权利人查询(多个权利人查询中间请用符号“|”隔开,示例:“权利人|权利人”)"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="close">取 消</el-button>
+        <el-button type="primary" @click="sure">确 定</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+<script>
+
+export default {
+  components: {
+  },
+  props: ['productId'],
+  data() {
+    return {
+      btnLoading: false,
+      loadPatent: false,
+      selectedTotal: 0,
+      isFetch: true,
+      quickSelect: false,//是否快速选择
+      loading: true,
+      visible: false,
+      selected: [],
+      checkList: [],//回显
+      tableData: [],
+      total: 0,
+      form: {},
+      formS: {},
+      startNumber: 1,
+      endNumber: 0,
+      Params: {
+        current: 1,
+        size: 10,
+      },
+      params:{},
+      queryParams: {
+        state: 0,
+        patentNos: [],//原isAdd
+        notInPatentNos: [],//原isDelete
+        folder: null,
+        projectId: null,
+        read: 'all',
+        current: 1,
+        size: 10,
+        name: '',
+        abstractStr: '',
+        publicNo: '',
+        applicationNo: '',
+        rightContent: '',
+        sort: {
+          order: 'asc',
+          prop: 'id'
+        },
+        field: [],
+        tree: [],
+        selected: [],
+        startNumber: 1,
+        endNumber: 0,
+        family: 0,
+        showPatent: "0",
+        patentName: "",
+        patentNo: "",
+        filedOptions: [],
+        pasOptions: [],//专题库自定义字段
+      },
+      mergeArr: ['id', 'patentNo'],
+      mergeObj: {},
+      selectNumberLoading: false,
+    }
+  },
+  created() {
+
+  },
+  mounted() {
+    // this.getList()
+  },
+  methods: {
+    //子页面自定义字段值
+    onChangeList({ field, a }) {
+      // console.log(field,a);
+      this.queryParams.pasOptions = field
+      this.queryParams.projectId = a
+      this.handleCancelSelectNumber()
+      this.getList2()
+
+    },
+    getListPro() {
+      //console.log("进来");
+      this.tableData = []
+      this.$api.getPatentListPAS(this.queryParams).then(res => {
+        this.tableData = res.data.records
+        this.total = res.data.total
+      })
+    },
+    sift() {
+      this.visible = true
+    },
+    sure() {
+      this.formS = JSON.parse(JSON.stringify(this.form))
+      if (Object.keys(this.formS).length != 0) {
+        for (let key in this.formS) {
+          this.queryParams[key] = this.formS[key]
+        }
+        this.handleCancelSelectNumber()
+        this.getList2()
+      }
+      this.visible = false
+    },
+    close() {
+      this.visible = false
+      this.form = this.formS
+    },
+    getSelectedTotal() {
+      //console.log(this.queryParams.patentNos,this.queryParams.notInPatentNos);
+      this.selectedTotal = Number(this.endNumber) - Number(this.startNumber) + 1 + Number(this.queryParams.patentNos.length) - Number(this.queryParams.notInPatentNos.length)
+    },
+    async Switch(type) {//选择...确定公用
+      let params = { ...this.queryParams }
+      if (params.field.length == 0) {
+        params.field = params.tree
+      }
+      switch (type) {
+        case 0:
+          this.queryParams.selected = this.tableData.map(item => item.patentNo);
+          this.checkList = [...new Set(this.checkList.concat(this.queryParams.selected))]
+          // this.selectedTotal = this.checkList.length
+          this.queryParams.selected = []
+          if (!this.quickSelect) {
+            this.queryParams.patentNos = JSON.parse(JSON.stringify(this.checkList))
+            this.getSelectedTotal()
+            break;
+          }
+          this.tableData.forEach((item, index) => {
+            var position = (this.queryParams.current - 1) * this.queryParams.size + index + 1
+            if (position >= this.startNumber) {
+              if (this.endNumber >= this.queryParams.current * this.queryParams.size) {
+                var index1 = this.queryParams.notInPatentNos.findIndex(i => {
+                  return i == item.patentNo
+                })
+                if (index1 != -1) {
+                  this.queryParams.notInPatentNos.splice(index1, 1)
+                }
+              } else {
+                if (position <= this.endNumber) {
+                  var index1 = this.queryParams.notInPatentNos.findIndex(i => {
+                    return i == item.patentNo
+                  })
+                  if (index1 != -1) {
+                    this.queryParams.notInPatentNos.splice(index1, 1)
+                  }
+                } else {
+                  var index2 = this.queryParams.patentNos.findIndex(i => {
+                    return i == item.patentNo
+                  })
+                  if (index2 == -1) {
+                    this.queryParams.patentNos.push(item.patentNo)
+                  }
+                }
+              }
+            } else {
+              var index2 = this.queryParams.patentNos.findIndex(i => {
+                return i == item.patentNo
+              })
+              if (index2 == -1) {
+                this.queryParams.patentNos.push(item.patentNo)
+              }
+            }
+          })
+          this.getSelectedTotal()
+          this.getList()
+          break
+        case 1:
+          params.startNumber = 1;
+          params.endNumber = this.total
+          this.startNumber = 1
+          this.endNumber = this.total
+        // this.commonSwitch()
+        // this.quickSelect = true
+        // break
+        case 2:
+          // this.selectNumberLoading = true
+          // this.$api.getComPatentNos(params).then(response => {
+          //   //console.log(response.data)
+          //   this.queryParams.selected = response.data
+          //   this.checkList = [...new Set(this.checkList.concat(this.queryParams.selected))]
+          //   // this.checkList = this.queryParams.selected
+          //   this.selectNumberLoading = false
+          //   this.getList()
+          // }).catch(error => {
+          //   this.selectNumberLoading = false
+          // })
+          this.queryParams.notInPatentNos = []
+          this.queryParams.patentNos = []
+          if (type == 2) {
+            if (!Number(this.queryParams.startNumber) || !Number(this.queryParams.endNumber)) {
+              this.queryParams.endNumber = this.endNumber > 0 ? this.endNumber : this.total
+              this.queryParams.startNumber = this.endNumber > 0 ? this.startNumber : 1
+              break;
+            }
+            this.startNumber = this.queryParams.startNumber
+            this.endNumber = this.queryParams.endNumber
+
+          }
+
+          // this.checkList =JSON.parse(JSON.stringify(this.queryParams.selected)) 
+          this.checkList = []
+          this.queryParams.notInPatentNos = []
+          this.queryParams.patentNos = []
+          await this.getList()
+          this.commonSwitch()
+          this.quickSelect = true
+          break
+      }
+    },
+    commonSwitch() {
+      //console.log(this.queryParams.startNumber,this.queryParams.endNumber)
+      if (this.queryParams.size * this.queryParams.current >= this.startNumber) {
+        if (this.queryParams.size * this.queryParams.current >= this.endNumber) {
+          if (this.queryParams.size * (this.queryParams.current - 1) + 1 <= this.startNumber) {
+            var a = this.startNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.endNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            //console.log(a,b)
+            for (var y = a; y <= b; y++) {
+              //console.log(this.tableData[y])
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          } else {
+            var a = (this.queryParams.size * (this.queryParams.current - 1) + 1) - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.endNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            for (var y = a; y <= b; y++) {
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          }
+        } else {
+          if (this.queryParams.size * (this.queryParams.current - 1) + 1 <= this.startNumber) {
+            var a = this.startNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.queryParams.size * this.queryParams.current - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            for (var y = a; y <= b; y++) {
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          } else {
+            var a = (this.queryParams.size * (this.queryParams.current - 1) + 1) - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.queryParams.size * this.queryParams.current - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            for (var y = a; y <= b; y++) {
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          }
+        }
+      }
+      this.checkList = [...new Set(this.checkList)]
+      this.selected = this.checkList
+      this.getSelectedTotal()
+      // //console.log(this.checkList)
+
+    },
+    toIsDelete(patentNo, y) {
+      var index2 = this.queryParams.notInPatentNos.findIndex(i => {
+        return i == patentNo
+      })
+      if (index2 != -1) {
+
+      } else {
+        this.checkList.push(this.tableData[y].patentNo)
+      }
+      var index = this.queryParams.selected.findIndex(item => {
+        return item == patentNo
+      })
+      if (index != -1) {
+        var index3 = this.queryParams.notInPatentNos.findIndex(m => {
+          return m == patentNo
+        })
+        if (index3 != -1) {
+
+        } else {
+          this.queryParams.notInPatentNos.push(this.queryParams.selected[index])
+        }
+
+      }
+
+
+    },
+    handleSelectNumber(type) {
+
+      this.Switch(type)
+
+    },
+
+    handleCancelSelectNumber() {//取消选择
+      this.queryParams.selected = []
+      this.queryParams.patentNos = []
+      this.queryParams.notInPatentNos = []
+      this.checkList = []
+      this.quickSelect = false
+      this.startNumber = 1
+      this.queryParams.startNumber = 1
+      this.queryParams.endNumber = this.total
+      this.endNumber = 0
+      this.selectedTotal = 0
+      // this.getList()
+    },
+    getFunInfo(val) {
+      //console.log(this.quickSelect);
+      if (this.quickSelect) {
+        var index4 = this.queryParams.selected.findIndex(item => {
+          return item == val
+        })
+        if (index4 != -1) {
+          var a = {
+            productId: this.productId,
+            patentNo: val
+          }
+          this.$api.deleteCompareNo(a).then(response => {
+            if (response.code == 200) {
+              // this.getList()
+              this.queryParams.selected.splice(index4, 1)
+            }
+          })
+
+          this.isFind(val)
+
+        } else {
+          this.isFind(val)
+        }
+      } else {
+        var index = this.queryParams.selected.findIndex(item => {
+          return item == val
+        })
+        if (index != -1) {
+          var a = {
+            productId: this.productId,
+            patentNo: val
+          }
+          this.$api.deleteCompareNo(a).then(response => {
+            if (response.code == 200) {
+              this.getList()
+            }
+          })
+        } else {
+          var index5 = this.queryParams.patentNos.findIndex(item => {
+            return item == val
+          })
+          if (index5 != -1) {
+            this.queryParams.patentNos.splice(index5, 1)
+          } else {
+            this.queryParams.patentNos.push(val)
+          }
+
+        }
+      }
+      this.getSelectedTotal()
+    },
+    isFind(val) {
+      if (this.isFetch) {
+        var index = this.selected.findIndex(item => {
+          return item == val
+        })
+        if (index != -1) {
+          //console.log(val)
+          this.queryParams.notInPatentNos.push(val)
+          this.selected.splice(index, 1)
+        } else {
+          var index2 = this.queryParams.notInPatentNos.findIndex(i => {
+            return i == val
+          })
+          if (index2 != -1) {
+            this.queryParams.notInPatentNos.splice(index2, 1)
+          } else {
+            var index3 = this.queryParams.patentNos.findIndex(m => {
+              return m == val
+            })
+            if (index3 != -1) {
+              this.queryParams.patentNos.splice(index3, 1)
+            } else {
+              this.queryParams.patentNos.push(val)
+            }
+          }
+        }
+        this.isFetch = false
+      } else {
+        var index3 = this.queryParams.patentNos.findIndex(m => {
+          return m == val
+        })
+        if (index3 != -1) {
+          this.queryParams.patentNos.splice(index3, 1)
+        } else {
+          var index2 = this.queryParams.notInPatentNos.findIndex(i => {
+            return i == val
+          })
+          if (index2 != -1) {
+            this.queryParams.notInPatentNos.splice(index2, 1)
+          } else {
+            // this.isFetch = true
+            // this.getFunInfo()
+            var index = this.selected.findIndex(item => {
+              return item == val
+            })
+            if (index != -1) {
+              var index4 = this.queryParams.selected.findIndex(item => {
+                return item == val
+              })
+              if (index4 != -1) {
+                var a = {
+                  productId: this.productId,
+                  patentNo: val
+                }
+                this.$api.deleteCompareNo(a).then(response => {
+                  if (response.code == 200) {
+                    // this.getList()
+                    this.queryParams.selected.splice(index4, 1)
+                  }
+                })
+              } else {
+                this.queryParams.notInPatentNos.push(val)
+                this.selected.splice(index, 1)
+              }
+            } else {
+              this.queryParams.patentNos.push(val)
+            }
+          }
+        }
+      }
+    },
+    getRowKeys(row) {
+      return row.id
+    },
+    async handleCurrentChange(val) {//分页
+      this.queryParams.current = val;
+      // this.checkList = []
+      // this.queryParams.endNumber=0
+      await this.getList();
+      if (this.quickSelect) {
+        this.commonSwitch()
+      }
+
+    },
+    savePatentList() {//保存
+      this.saveDeleteQueryParams()
+      this.btnLoading = true
+      this.$api.addPatentDelete(this.queryParams).then(res => {
+        if (res.code == 200) {
+          this.btnLoading = false
+          this.saveDelete()
+          this.$message.success('保存成功')
+        }
+      })
+    },
+    saveDeleteQueryParams() {
+      this.queryParams.startNumber = this.startNumber
+      this.queryParams.endNumber = this.endNumber
+      this.queryParams.productId = this.productId
+    },
+    saveDelete() {
+          this.startNumber = 1
+          this.endNumber = 0
+          this.selectedTotal = 0
+          this.quickSelect = false
+          this.queryParams.patentNos = []
+          this.queryParams.notInPatentNos = []
+          this.checkList = []
+          this.getList2()
+    },
+    getList2() {
+      this.queryParams.current = 1
+      this.getList()
+    },
+    async getList(key) {
+      // this.queryParams.productId = this.productId
+      // let params = JSON.parse(JSON.stringify(this.queryParams))
+      // //console.log(params);
+      // params.tree.map(tree => {
+      //   let field = params.field.filter(item => item.key === tree.key)
+      //   if (field.length === 0) {
+      //     params.field.push(tree)
+      //   }
+      // })
+      // params.tree = undefined
+      // //console.log(params);
+      // this.$store.commit('SET_PATENT_PARAMS', params)
+      // this.loading = true
+      
+      await this.$api.getPatentsDTOById(this.params).then(response => {
+        this.loading = false
+        this.total = response.data.total
+        this.queryParams.endNumber = this.endNumber > 0 ? this.endNumber : response.data.total
+        this.queryParams.startNumber = this.endNumber > 0 ? this.startNumber : 1
+
+        this.tableData = response.data.records
+        // this.queryParams.selected = response.data.select
+        // this.queryParams.selectedTotal = response.data.selectedTotal
+        this.checkList = JSON.parse(JSON.stringify([...new Set(this.checkList.concat(this.queryParams.selected))]))
+        this.selected = this.checkList
+        this.isFetch = true
+
+
+
+      }).catch(error => {
+
+      })
+    },
+    showPatent(even) {//选择下拉框
+      //console.log(even);
+    },
+  },
+  watch: {
+  }
+}
+</script>
+
+<style lang="scss" >
+.btn1 {
+  background: #909399;
+}
+
+.btn1:hover {
+
+  background-color: #a4a7ab;
+}
+
+.patent-fast-edit-popover {
+  padding: 0 !important;
+
+  .btn {
+    color: #000;
+    line-height: 40px;
+    border-radius: 5px;
+    padding-left: 10px;
+    cursor: pointer;
+
+    &:hover {
+      background: #adadad;
+      color: #fff;
+    }
+  }
+
+  .disabled {
+    cursor: not-allowed !important;
+  }
+
+  .bottom {
+    text-align: right;
+    color: #1e9fff;
+    line-height: 40px;
+    padding-left: 10px;
+    font-size: 18px;
+  }
+
+  .el-divider--horizontal {
+    margin: 10px 0 !important;
+  }
+
+  .select-number {
+    .el-input {
+      width: 70px;
+    }
+
+    span {
+      padding: 0 3px;
+    }
+  }
+
+}
+
+.patent-left {
+  margin-top: 69px;
+  overflow: hidden;
+  border-top: 1px solid rgb(228 231 237);
+}
+</style>

+ 141 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/dialog/PatentViewField.vue

@@ -0,0 +1,141 @@
+<template>
+  <div class="">
+    <el-dialog title="视图字段管理" :visible.sync="visible" width="650px" append-to-body destroy-on-close :before-close="close" top="10vh">
+      <div class="patent-view-field-manage">
+        <el-transfer
+            class="transfer"
+            v-model="selected"
+            filterable
+            :filter-method="filterMethod"
+            :titles="['未选择', '已选择']"
+            :button-texts="['', '']"
+            :format="{ noChecked: '${total}', hasChecked: '${checked}/${total}' }"
+            @right-check-change="rightCheckChange"
+            @change="handleChange2"
+            :data="dataList">
+          <span slot-scope="{ option }">{{ option.name }}</span>
+          <div slot="left-footer"></div>
+          <div class="transfer-footer" slot="right-footer">
+            <el-button @click="handleQueryOrder(-1)" type="primary" size="small" icon="el-icon-top" circle></el-button>
+            <el-button @click="handleQueryOrder(1)" size="small" icon="el-icon-bottom" circle></el-button>
+          </div>
+        </el-transfer>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="warning" class="float_left" @click="reset">重置</el-button>
+        <el-button @click="close">取 消</el-button>
+        <el-button type="primary" @click="submit" :loading="btnLoading">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+
+export default {
+  data() {
+    return {
+      visible: false,
+      dataList: [],
+      btnLoading: false,
+      selected: [],
+      rightSelect: [],
+      projectId: 0,
+      viewType: ''
+    }
+  },
+  mounted() {
+  },
+  methods: {
+    handleQueryOrder(order) {
+      this.rightSelect.map(item => {
+        const index = this.selected.indexOf(item)
+        const cIndex = index + order
+        if (cIndex < 0 || cIndex >= this.selected.length) {
+          return false
+        }
+        let current = this.dataList.find(item => item.key === this.selected[index])
+        let exchange = this.dataList.find(item => item.key === this.selected[cIndex])
+        current.order = current.order + order
+        exchange.order = exchange.order + (order === -1 ? 1 : -1)
+        this.dataList.sort((a, b) => {
+          return a.order - b.order
+        })
+        const temp = this.selected[index]
+        this.selected[index] = this.selected[cIndex]
+        this.selected[cIndex] = temp
+      })
+    },
+    handleChange2() {
+      let order = 1
+      for (let i = 0; i < this.selected.length; i++) {
+        const index = this.dataList.map(item => item.key).indexOf(this.selected[i])
+        this.dataList[index].order = order++
+      }
+    },
+    filterMethod(query, item) {
+      return item.name.indexOf(query) !== -1
+    },
+    rightCheckChange(data) {
+      this.rightSelect = data
+    },
+    open(dataList, viewType) {
+      this.viewType = viewType
+        this.dataList = JSON.parse(JSON.stringify(dataList))
+      // if (viewType === 'patent-table-list-view') {
+      //   this.dataList = JSON.parse(JSON.stringify(dataList))
+      // } else {
+      //   this.dataList = JSON.parse(JSON.stringify(dataList.filter(item => item.type !== 'list' || ['label', 'simpleFamily', 'inpadocFamily', 'patSnapFamily'].indexOf(item.key) !== -1)))
+      // }
+      this.selected = this.dataList.filter(item => !item.hidden).map(item => item.key)
+      this.projectId = null
+      this.visible = true
+    },
+    close() {
+      this.visible = false
+      this.$emit('close')
+    },
+    reset() {
+      this.visible = false
+      this.$emit('reset')
+    },
+    submit() {
+      this.dataList.map(item => item.hidden = this.selected.indexOf(item.key) === -1)
+      let data = {
+        data: this.dataList,
+        projectId: null,
+        type: 'list',
+        view: this.viewType,
+      }
+      this.btnLoading = true
+      this.$api.updateUserSettingField(data).then(response => {
+        this.$emit('update', response.data)
+        this.visible = false
+        this.btnLoading = false
+        this.$message.success('操作成功')
+      }).catch(error => {
+        this.btnLoading = false
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.patent-view-field-manage {
+  text-align: center;
+  .transfer {
+    text-align: left;
+    display: inline-block;
+  }
+  .el-transfer-panel__body {
+    height: 400px;
+  }
+  .el-transfer-panel__list.is-filterable {
+    height: 350px;
+  }
+  .transfer-footer {
+    margin: 5px;
+  }
+}
+</style>

+ 272 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/import/SystemTask.vue

@@ -0,0 +1,272 @@
+<template>
+  <div class="system-task">
+    <el-container>
+      <el-header class="position_relative">
+        <div class="header-tabs">
+          <el-tabs v-model="activeName" type="card">
+            <el-tab-pane label="实时进度" name="0"></el-tab-pane>
+            <el-tab-pane label="任务列表" name="1"></el-tab-pane>
+          </el-tabs>
+        </div>
+      </el-header>
+      <el-main class="system-task-main">
+        <div v-if="activeName === '0'" key="task1">
+          <el-table :data="taskData" border header-row-class-name="custom-table-header">
+            <el-table-column v-if="form === 1" label="文件名称" prop="oldName" align="center" show-overflow-tooltip></el-table-column>
+            <el-table-column label="任务数量" prop="total" align="center" show-overflow-tooltip></el-table-column>
+            <el-table-column label="当前下标" prop="index" align="center" show-overflow-tooltip></el-table-column>
+            <el-table-column label="实时进度" align="center" min-width="200px" show-overflow-tooltip>
+              <template slot-scope="scope">
+                <el-progress :text-inside="true" :stroke-width="20" :percentage="scope.row.percentage" :color="customColors"></el-progress>
+              </template>
+            </el-table-column>
+            <el-table-column v-if="form === 2" label="操作" align="center" width="120" show-overflow-tooltip>
+              <template slot-scope="scope">
+                <span v-if="!scope.row.url">等待中</span>
+                <el-link v-else type="primary" @click.native="handleDownload(scope.row)">下载文件</el-link>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div v-else key="task2">
+          <el-form inline>
+            <el-form-item label="创建人">
+              <el-input v-model="queryParams.createName" size="small" placeholder="请输入创建人名称"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button size="small" type="" @click="getList">搜索</el-button>
+            </el-form-item>
+          </el-form>
+          <el-table v-loading="loading" :data="tableData" border header-row-class-name="custom-table-header">
+            <el-table-column v-if="form === 1" label="文件名称" prop="oldName" align="center" show-overflow-tooltip></el-table-column>
+            <el-table-column v-else label="#" type="index" align="center" width="55"></el-table-column>
+            <el-table-column label="开始时间" align="center" show-overflow-tooltip>
+              <template slot-scope="scope">
+                <span>{{ $d(scope.row.startTime) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="结束时间" align="center" show-overflow-tooltip>
+              <template slot-scope="scope">
+                <span>{{ $d(scope.row.endTime) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="任务状态" align="center" show-overflow-tooltip>
+              <template slot-scope="scope">
+                <span>{{ statusObj[scope.row.status] }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="createName" label="创建人" align="center" width="120" show-overflow-tooltip></el-table-column>
+            <el-table-column label="操作" align="center" width="120" show-overflow-tooltip>
+              <template slot-scope="scope" v-if="scope.row.status !== 0">
+                <el-link type="primary" @click.native="handleDownload(scope.row)" v-if="$permission('/workspace/common/taskDownload')">下载</el-link>
+                <el-link class="margin-left_10" type="danger" @click.native="handleDelete(scope.row)" >删除</el-link>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div class="pagination">
+            <el-pagination :current-page.sync="queryParams.current" :page-size="queryParams.size" :total="total" @current-change="handleCurrentChange" layout="total, prev, pager, next, jumper" background></el-pagination>
+          </div>
+        </div>
+      </el-main>
+    </el-container>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import { downLoad2, getFileName } from "@/utils";
+
+export default {
+  // props: {
+  //   form: Number,
+  // },
+  props:['form','productId'],
+  data() {
+    return {
+      activeName: '0',
+      taskData: [],
+      loading: false,
+      total: 0,
+      tableData: [],
+      queryParams: {
+        // status: null,
+        state:0,
+        size: 10,
+        current: 1,
+        // projectName: '',
+        // type: 0,
+        // order: 'desc',
+        // createName: '',
+        productId: '',
+      },
+      statusObj: {
+        0: '队列中',
+        1: '进行中',
+        2: '成功',
+        3: '失败',
+      },
+      customColors: [
+        {color: '#f56c6c', percentage: 20},
+        {color: '#e6a23c', percentage: 40},
+        {color: '#5cb87a', percentage: 60},
+        {color: '#1989fa', percentage: 80},
+        {color: '#6f7ad3', percentage: 100}
+      ],
+      taskType: {
+        1: '专利导入',
+        2: '专利导出',
+        3: '说明书导入',
+      }
+    }
+  },
+  computed: {
+    ...mapGetters(['webSocket', 'userinfo'])
+
+  },
+  watch:{
+    activeName(val){
+      if(val==1){
+        this.getList()
+      }else{
+        this.getQueueList()
+      }
+    },
+  },
+  mounted() {
+    // this.queryParams.type = this.form
+    this.getList()
+   this.getQueueList()
+    this.initTask()
+    
+  },
+  methods: {
+    initTask() {
+      if(this.webSocket){
+      this.webSocket.onmessage = (e) => {
+        // console.log(e)
+        const { code, data, message } = JSON.parse(e.data)
+        // console.log(JSON.parse(e.data))
+        if (code === 903 || code === 904) {
+          const index = this.taskData.map(item => item.taskId).indexOf(data.taskId)
+          if (index === -1) {
+            // this.taskData.push(data)
+          } else {
+            this.$set(this.taskData, index, data)
+          }
+          if (data.complete) {
+            if(this.form==1){
+              this.$message.success(`导入任务完成`)
+               for(var i = 0;i<this.taskData.length;i++){
+              if(this.taskData[i].complete==true){
+                this.taskData.splice(i,1)
+              }
+            }
+            }else{
+              this.$message.success(`导出任务完成`)
+            }
+            
+            // console.log(this.form)
+           
+            this.getList()
+          }
+        } else if (code === 803 || code === 804) {
+          this.$message.error(message)
+          this.getList()
+        }
+      }
+    }
+    },
+    getQueueList() {
+      
+      this.taskData = []
+      this.queryParams.productId = this.productId
+      this.queryParams.state = 0
+      this.queryParams.type= this.form,
+      this.queryParams.projectId= null,
+      // 查询导出任务
+      this.$api.getQueueList(this.queryParams).then(response => {
+        if (response.code==200 && response.data && response.data.length>0) {
+          response.data.map(item => {
+            this.taskData.push({
+              productId: item.productId,
+              total: item.importCount,
+              index: 0,
+              taskId: item.id,
+              oldName: item.oldName,
+              complete: false,
+              url: '',
+              fileName: '',
+              taskType: item.type,
+              percentage: 0,
+              total:item.total,
+            })
+          })
+        }
+      })
+    },
+    handleDownload(row) {
+      downLoad2(row.filePath)
+    },
+    handleDelete(row) {
+      this.$confirm('确认删除本条数据吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.loading = true
+        this.$api.deleteTask({ id: row.id }).then(response => {
+          this.$message.success('删除成功')
+          this.loading = false
+          this.getList()
+        }).catch(error => {
+          this.loading = false
+        })
+      })
+    },
+    handleCurrentChange(val) {
+      this.queryParams.current = val;
+      this.getList();
+    },
+    getList() {
+      this.loading = true
+      this.queryParams.state = 2
+      this.queryParams.productId = this.productId
+      this.$api.getTaskList(this.queryParams).then(response => {
+        this.tableData = response.data.records
+        this.total = response.data.total
+        this.loading = false
+      }).catch(error => {
+        this.loading = false
+      })
+    },
+  }
+}
+</script>
+
+<style lang="scss">
+.system-task {
+  height: 100%;
+  .system-task-main {
+    background: #fff;
+    padding: 5px 20px;
+    margin-top: 20px;
+  }
+  .header-tabs {
+    position: absolute;
+    bottom: 0;
+    height: 100%;
+    padding-right: 9px;
+    left: 12px;
+    .el-tabs {
+      margin-top: 19px;
+    }
+    .el-tabs__header {
+      margin-bottom: 0 !important;
+    }
+  }
+  .pagination {
+    text-align: center;
+    margin: 20px 0;
+  }
+}
+</style>

+ 250 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/import/import.vue

@@ -0,0 +1,250 @@
+<template>
+  <div class="import-patent">
+    <el-container class="import-patent">
+      <el-main class="import-patent-main">
+        <el-container class="import-patent-action">
+          <el-main class="import-patent-action-main" v-loading="loading">
+            <el-form :model="form">
+              
+              <el-form-item label="选择需要上传的专利信息导入文件">
+                <el-upload class="upload-file" drag action="#" :auto-upload="false" :show-file-list="false" :on-change="onChange">
+                  <i :class="!form.file ? 'el-icon-upload' : 'el-icon-refresh'"></i>
+                  <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+                  <div class="el-upload__tip" slot="tip"></div>
+                </el-upload>
+              </el-form-item>
+
+            </el-form>
+          </el-main>
+        </el-container>
+
+        <system-task ref="systemTask" :form="1" :productId="productId" />
+      </el-main>
+    </el-container>
+
+  </div>
+</template>
+
+<script>
+import SystemTask from './SystemTask.vue'
+import { mapGetters } from "vuex";
+export default {
+  props:['productId'],
+  components: {
+    SystemTask
+  },
+  data() {
+    return {
+      sourceId: '',
+      title: '',
+      customField: {},
+      loading: false,
+      btnLoading: false,
+      total: 0,
+      tableData2: [],
+      form: {
+        file: null,
+        json: {
+          projectId: 0,
+          field: [],
+          folder: {}
+        }
+      },
+      fieldTypeObj: {
+        0: '数字',
+        1: '日期',
+        2: '文本',
+        3: '下拉框',
+        4: '单选',
+        5: '多选',
+        6: '树',
+      },
+      tableData: [
+        {
+          name: '标引',
+          field: 'index'
+        }, {
+          name: '分类',
+          field: 'classify'
+        }, {
+          name: '文件夹',
+          field: 'folder'
+        }],
+      defaultProps: {
+        children: 'children',
+        label: 'name'
+      },
+    }
+  },
+  computed: {
+    ...mapGetters(['webSocket', 'userinfo'])
+  },
+  mounted() {
+
+  },
+  // watch:{
+  //   sourceId(val){
+  //     console.log(val)
+  //   }
+  // },
+  methods: {
+
+    getQueueList() {
+      this.$refs.systemTask.getQueueList()
+    },
+    cancel() {
+      this.dialogVisible = false
+    },
+    updateFolder() {
+      this.$api.getProjectFolderList({ projectId: this.projectId, patentTotal: false }).then(response => {
+        this.customField.folder = response.data
+      })
+    },
+    handleManage() {
+      this.$refs.projectFieldDrawer.open(this.projectId)
+    },
+    handleFolder() {
+      this.$refs.projectFolderDialog.open(this.projectId)
+    },
+    onChange(file, fileList) {
+      this.form.file = file.raw
+      this.handleConfirm()
+    },
+    handleClose() {
+
+    },
+    handleClose2() {
+      this.getCustomField()
+    },
+    getCustomField() {
+      this.$api.getCustomField({ projectId: this.projectId }).then(response => {
+        this.customField = response.data;
+        ['index', 'classify'].map(key => {
+          this.customField[key].map(item => {
+            let value = null
+            if (item.type === 5 || item.type === 6) {
+              value = []
+            }
+            this.$set(this.form.json.field, item.id, { type: item.type, value })
+          })
+        })
+        // console.log(this.customField);
+      })
+    },
+    handleConfirm() {
+      // const json = JSON.parse(JSON.stringify(this.form.json))
+      // const refs = this.$refs
+
+      // let data = {
+      //   projectId: this.projectId,
+      //   sourceId: this.sourceId,
+      //   fieldList: [],
+      //   folderIds: refs.folderTree ? refs.folderTree.getCheckedKeys() : []
+      // }
+      // for (let id in json.field) {
+      //   const field = json.field[id]
+      //   if (!field) {
+      //     continue;
+      //   }
+      //   const type = field.type
+      //   const value = field.value
+      //   if (value) {
+      //     if (type === 5) {
+      //       value.map(option => {
+      //         data.fieldList.push({
+      //           fieldId: parseInt(id),
+      //           type: type,
+      //           optionId: option
+      //         })
+      //       })
+      //     } else if (type === 6) {
+      //       const tree = refs[id]
+      //       if (tree) {
+      //         const treeNode = tree[0].getCheckedKeys()
+      //         treeNode.map(node => {
+      //           data.fieldList.push({
+      //             fieldId: parseInt(id),
+      //             type: type,
+      //             optionId: node
+      //           })
+      //         })
+      //       }
+      //     } else {
+      //       data.fieldList.push({
+      //         fieldId: parseInt(id),
+      //         type: type,
+      //         optionId: [0, 1, 2].indexOf(type) === -1 ? value : 0,
+      //         text: value
+      //       })
+      //     }
+      //   }
+      // }
+      if (!this.form.file) {
+        this.$message.error('请选择文件')
+        return false
+      }
+      // if (!this.sourceId) {
+      //   this.$message.error('请选择数据类型')
+      //   return false
+      // }
+      let formData = new FormData()
+      formData.append('productId', this.productId)
+      formData.append('file', this.form.file)
+      // formData.append('json', JSON.stringify(data))
+      this.btnLoading = true
+      this.loading = true
+      // console.log(formData)
+      this.$api.proPatentImportPatents(formData).then(response => {
+        this.$message.success('任务创建成功')
+        this.form.file = null
+        this.btnLoading = false
+        this.loading = false
+        // this.handleClose()
+        this.getQueueList()
+      }).catch(error => {
+        this.btnLoading = false
+        this.loading = false
+      })
+    }
+  }
+}
+</script>
+<style lang="scss">
+.el-divider--horizontal {
+  margin: 0
+}
+
+.import-patent {
+  height: 100%;
+
+  .import-patent-main {
+    padding: 0;
+    height: 100%;
+    background: #fff;
+  }
+
+  .import-patent-action {
+    width: 500px;
+    height: 100%;
+    border-right: 1px solid #d2d2d2;
+    float: left;
+
+    .custom-field-form .el-form-item__label {
+      font-weight: bold !important;
+    }
+
+    .import-patent-action-form {
+      margin-left: 20px;
+    }
+
+    .import-patent-action-main {
+      padding: 10px;
+    }
+  }
+
+  .import-patent-button {
+    height: 70px !important;
+    text-align: right;
+  }
+}
+</style>

+ 34 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/import/index.vue

@@ -0,0 +1,34 @@
+<template>
+  <div>
+    <imports :productId="productId"></imports>
+  </div>
+</template>
+
+<script>
+import imports from "./import.vue"
+export default {
+  components: {
+    imports,
+  },
+  data() {
+    return {
+
+    }
+  },
+  computed: {
+    productId() {
+      return this.$route.query.id
+    },
+  },
+  mounted() {
+
+  },
+  methods: {
+
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 30 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/relatedPatents.vue

@@ -0,0 +1,30 @@
+<template>
+  <div style="background:white;height:100%">
+        <tables :productId="productId" :structureId="structureId" v-bind="$attrs"></tables>
+  </div>
+</template>
+
+<script>
+import tables from "./table.vue";
+export default {
+  props:['productId','structureId'],
+  components: {
+    tables,
+  },
+  data() {
+    return {
+
+    }
+  },
+  mounted() {
+
+  },
+  methods: {
+    
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 897 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/components/table.vue

@@ -0,0 +1,897 @@
+<template>
+  <div style="padding:20px;background:white;height:100%">
+    <div style="margin-bottom:20px">
+      <span><span>{{ name }}</span>{{structureId?'架构':'产品'}}的相关价值专利</span>
+    </div>
+    <div style="display: flex; justify-content: space-between;" v-if="!structureId">
+      <div :style="{ visibility: selectedTotal > 0 ? 'visible' : 'hidden' }">
+        已勾选 <b>{{ selectedTotal }}</b> 条
+      </div>
+      <div style="display: flex;margin-bottom: 20px;" >
+             <el-input v-model.trim="paramsRuleForm.patentNo" size="small" style="width: 450px;" placeholder="请输入专利号进行添加(多个专利号同时添加中间请用符号“|”隔开)"></el-input>
+        <el-tooltip class="item" effect="dark" content="将搜索的专利添加至该产品的相关专利中" placement="top">
+          <el-button type="primary" size="small" @click="getListAdd" style="margin-left: 10px;"> 添 加 </el-button>
+        </el-tooltip>
+      </div>
+      <div style="display:flex;justify-content: flex-end;padding-bottom:20px">
+        <div style="margin-right: 10px;">
+          <!-- <el-button type="primary" size="small" @click="addRelevantPatents">新增</el-button> -->
+          <!-- <el-button type="primary" size="small" @click="savePatentList" :loading="btnLoading">保存</el-button> -->
+        </div>
+        <el-button size="small" type="warning" @click="handleFieldManage">显示栏位管理</el-button>
+        <el-button type="primary" size="small" style="margin-right:10px;width:70px"
+          @click="importPatent(productId)">导入</el-button>
+        <div class="btn1" @click="sift"
+          style="width:80px;height:32px;display:flex;justify-content:space-around;border-radius:5px; cursor: pointer;">
+          <img src="@/assets/img/filtrationSearch.png" alt=""
+            style="width:16px;height:16px;margin-top:9px;margin-left:8px">
+          <p style="margin:0 8px 0 0;line-height:32px;color:white;font-size:14px">筛选 </p>
+        </div>
+        <el-popover placement="bottom" title="" width="250" trigger="click">
+          <el-main class="patent-fast-edit-popover" v-loading="selectNumberLoading">
+            <div class="btn" @click="handleSelectNumber(0)">本页选择</div>
+            <div class="btn" @click="handleSelectNumber(1)">全部选择</div>
+            <el-divider></el-divider>
+            <div class="select-number">
+              <span>从</span>
+              <el-input size="mini" v-model="queryParams.startNumber" @change="change1"></el-input>
+              <span>到</span>
+              <el-input size="mini" v-model="queryParams.endNumber" @change="change2"></el-input>
+              <el-button type="text" size="" @click="handleSelectNumber(2)">确定</el-button>
+            </div>
+          </el-main>
+          <el-button type="info" size="small" class="margin-left_10" slot="reference">
+            选择专利<i class="el-icon-arrow-down el-icon--right"></i>
+          </el-button>
+        </el-popover>
+        <el-button type="text" size="small" class="margin-left_10" @click="handleCancelSelectNumber">取消选择</el-button>
+        <el-button type="primary" size="small" class="margin-left_10" @click.native="handleDelete">删除</el-button>
+      </div>
+    </div>
+    <!-- 数据表格 -->
+    <el-table :data="tableData" border :row-key="getRowKeys" v-loading="loading" ref="table" id="table"
+      header-row-class-name="custom-table-header" style="min-width: 100%; overflow:auto">
+      <el-table-column width="80" align="center" v-if="!structureId">
+        <template slot-scope="scope">
+          <div>
+            <el-checkbox-group v-model="checkList" style="display:inline-block">
+              <el-checkbox :label="scope.row.patentNo" @change="getFunInfo(scope.row.patentNo)">
+                <span>{{ (scope.$index + 1) + ((queryParams.current - 1) * queryParams.size) }}</span>
+              </el-checkbox>
+            </el-checkbox-group>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="patentNo" label="专利号" align="center" width="200px">
+        <template slot-scope="scope">
+          <div>
+            <el-link type="primary" @click="toPatentDetails(scope.row)">{{ scope.row.patentNo }}</el-link>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="name" label="专利标题" align="center" width="300px"></el-table-column>
+      <el-table-column v-for="item in fields" :prop="item.key" :label="item.name" align="center"></el-table-column>
+      <el-table-column prop="applicationNo" label="申请号" align="center" width=""></el-table-column>
+      <el-table-column prop="applicationDate" label="申请日" align="center" width=""></el-table-column>
+      <el-table-column prop="ipcList" label="IPC分类号" align="center" width=""></el-table-column>
+      <el-table-column prop="applicant" label="权利人" align="center" width="">
+        <template slot-scope="scope">
+          <span v-for="item in scope.row.applicant">
+           <span v-if="item.dataType==1">
+              {{ item.name }}
+           </span> 
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="applicant" label="申请人" align="center" width="">
+        <template slot-scope="scope">
+          <span v-for="item in scope.row.applicant">
+           <span v-if="item.dataType==2">
+              {{ item.name }}
+           </span> 
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="simpleStatus" align="center" label="状态"></el-table-column>
+
+    </el-table>
+    <div class="pagination">
+      <el-pagination :current-page.sync="queryParams.current" :page-size="queryParams.size" :total="total"
+        @current-change="handleCurrentChange" layout="total, prev, pager, next, jumper" background></el-pagination>
+    </div>
+
+    <el-dialog title="筛选" :visible.sync="visible" width="1100px" max-height="800" :before-close="close">
+      <div style="height: 500px">
+        <el-form ref="form" :model="form" label-width="80px" label-position="left">
+          <el-form-item label="标题">
+            <el-input type="textarea" v-model="form.patentName" placeholder="请输入标题查询"></el-input>
+          </el-form-item>
+          <el-form-item label="摘要">
+            <el-input type="textarea" v-model="form.abstractStr" placeholder="请输入摘要查询"></el-input>
+          </el-form-item>
+          <el-form-item label="专利号">
+            <el-input type="textarea" v-model="form.patentNo"
+              placeholder="请输入专利号查询(多个专利号查询中间请用符号“|”隔开,示例:“专利号|专利号”)"></el-input>
+          </el-form-item>
+          <el-form-item label="申请号">
+            <el-input type="textarea" v-model="form.applicationNo" placeholder="请输入申请号查询"></el-input>
+          </el-form-item>
+          <el-form-item label="申请人">
+            <el-input type="textarea" v-model="form.applicationName"
+              placeholder="请输入申请人查询(多个申请人查询中间请用符号“|”隔开,示例:“申请人|申请人”)"></el-input>
+          </el-form-item>
+          <el-form-item label="权利人">
+            <el-input type="textarea" v-model="form.obligeeName"
+              placeholder="请输入权利人查询(多个权利人查询中间请用符号“|”隔开,示例:“权利人|权利人”)"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="close">取 消</el-button>
+        <el-button type="primary" @click="sure">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="新增相关专利" :visible.sync="visibleSelected" width="1100px" :before-close="close2">
+      <Selected-Patent :loadPatent="loadPatent" :productId="productId" @save="getSave"></Selected-Patent>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="close2">关 闭</el-button>
+        <!-- <el-button type="primary" >确 定</el-button> -->
+      </div>
+    </el-dialog>
+    <patent-view-field @close="close" @reset="getPatentViewField(true)" @update="updatePatentViewField" ref="patentViewField" />
+  </div>
+</template>
+<script>
+import SelectedPatent from "./SelectedPatent.vue";
+import PatentViewField from "./dialog/PatentViewField.vue";
+
+export default {
+  components: {
+    SelectedPatent,
+    PatentViewField,
+  },
+  props: ['productId','structureId','name'],//name为产品名称或者架构名称
+  data() {
+    return {
+      btnLoading: false,
+      loadPatent: false,
+      visibleSelected: false,
+      selectedTotal: 0,
+      isFetch: true,
+      quickSelect: false,//是否快速选择
+      loading: true,
+      visible: false,
+      selected: [],
+      checkList: [],//回显
+      tableData: [],
+      total: 0,
+      form: {},
+      formS: {},
+      startNumber: 1,
+      endNumber: 0,
+      Params: {
+        current: 1,
+        size: 10,
+      },
+      paramsRuleForm: {
+        productId:this.productId,
+      },
+      queryParams: {
+        state: 0,
+        patentNos: [],//原isAdd
+        notInPatentNos: [],//原isDelete
+        folder: null,
+        projectId: null,
+        read: 'all',
+        current: 1,
+        size: 10,
+        name: '',
+        abstractStr: '',
+        publicNo: '',
+        applicationNo: '',
+        rightContent: '',
+        sort: {
+          order: 'asc',
+          prop: 'id'
+        },
+        field: [],
+        tree: [],
+        selected: [],
+        startNumber: 1,
+        endNumber: 0,
+        family: 0,
+        showPatent: "0",
+        patentName: "",
+        patentNo: "",
+        filedOptions: [],
+        pasOptions: [],//专题库自定义字段
+      },
+      mergeArr: ['id', 'patentNo'],
+      mergeObj: {},
+      selectNumberLoading: false,
+      tableHeight: null,
+      viewSelected:this.name + this.productId,
+      patentViewField:[],
+      fields:[],
+    }
+  },
+  created() {
+
+  },
+  mounted() {
+    this.getList()
+    this.getPatentViewField()
+  },
+  methods: {
+    // 请求显示栏位管理数据
+    async getPatentViewField(refresh) {
+      let params = {
+        projectId: null,
+        type: 'list',
+        view: this.viewSelected,
+        refresh: refresh
+      }
+      const { data } = await this.$api.getUserSettingField(params)
+      this.patentViewField = data
+      // this.patentViewField = data.filter(item => {
+      //   return item.type=='list'
+      // })
+      this.handleFields(this.patentViewField)
+    },
+    // 显示栏位管理
+    async handleFieldManage() {
+      await this.getPatentViewField()
+      this.$refs.patentViewField.open(this.patentViewField,this.viewSelected)
+    },
+    updatePatentViewField(data) {
+      this.patentViewField = data.filter(item => {
+        return item.type=='list'
+      })
+     this.handleFields(this.patentViewField)
+    },
+    handleFields(patentViewField) {
+      this.fields=[]
+      const customField=patentViewField.filter(item => {
+        return item.hidden==false
+      })
+      let isIncludes = ['applicant4', 'applicant2', 'ipcList', 'applicationDate', 'applicationNo', 'name']
+      customField.forEach(item => {
+        if (!item.key.includes('applicant4') && !item.key.includes('applicant2') && !item.key.includes('name') && !item.key.includes('ipcList') && !item.key.includes('applicationDate')&& !item.key.includes('applicationNo')) {
+          this.fields.push(item)
+        }
+      })
+    },
+    // 跳转专利详情
+    toPatentDetails(val) {
+      this.$api.getPatentById(val.id, { projectId: null }).then(response => {
+        var router = this.$router.resolve({
+          path: '/articlesContrastIndex/' + val.id,
+        })
+        window.open(router.href, '_blank');
+      })
+
+    },
+    // 导入
+    importPatent(val) {
+      this.$router.push({
+        path: '/relatedPatentsImport',
+        query: {
+          id: val,
+        }
+      })
+    },
+    // async getListAdd() {
+    //   this.getListAdd2()
+    // //   return false
+    // //   const [data] = await Promise.allSettled([this.getListAdd2])
+    // //  console.log(data)
+    // },
+    // 查询并添加到相关专利
+    getListAdd() {
+      // console.log(1)
+      // var a = this.$api.addPatentDelete(this.queryParams)
+      // console.log(a) 
+      // return false
+      if (!this.paramsRuleForm.patentNo) {
+        this.$message.error('请输入专利号')
+        return false
+      }
+
+      this.$api.addPatentDelete(this.paramsRuleForm).then(res => {
+        // console.log(res.code);
+        if (res.code == 200) {
+          if (res.data && res.data.length > 0) {
+            // if (!res.data.includes('')) {
+            this.alterMessage(res.data)
+            // }
+          } else {
+            this.$message.success('添加至本产品相关专利成功')
+          }
+          this.paramsRuleForm.patentNo = ''
+          this.getList()
+        }
+      }).catch(error => {
+      })
+    },
+    alterMessage(messages, num) {
+      if (num==1) {
+        this.$alert(`${messages} 该专利无法删除`, '提示', {
+          confirmButtonText: '确定',
+        })
+      } else {
+        this.$alert(`${messages} 添加失败,原因(专利号错误/此专利已存在该产品的相关专利/专题库无此专利)`, '提示', {
+          confirmButtonText: '确定',
+        })
+      }
+    },
+    change1(val) {
+      if (!isNaN(val)) {
+         if (!val || val <= 0) {
+          this.queryParams.startNumber=1
+        } else {
+          if (this.queryParams.startNumber>this.total) {
+            this.queryParams.startNumber=this.total
+          }
+        }
+      } else {
+        this.queryParams.startNumber=1
+      }
+     
+    },
+    change2(val) {
+      if (!isNaN(val)) {
+        if (!val || val <= 0) {
+          this.queryParams.endNumber = this.total
+        } else {
+          if (this.queryParams.endNumber > this.total) {
+            this.queryParams.endNumber = this.total
+          }
+        }
+      }else {
+        this.queryParams.endNumber=this.total
+      }
+    },
+    tableOffSetHeight() {
+      let b = document.getElementById("table")
+      this.tableHeight = b.offsetHeight
+    },
+    //子页面自定义字段值
+    onChangeList({ field, a }) {
+      // console.log(field,a);
+      this.queryParams.pasOptions = field
+      this.queryParams.projectId = a
+      this.handleCancelSelectNumber()
+      this.getList2()
+
+    },
+    getListPro() {
+      //console.log("进来");
+      this.tableData = []
+      this.$api.getPatentListPAS(this.queryParams).then(res => {
+        this.tableData = res.data.records
+        this.total = res.data.total
+      })
+    },
+    getSave(val) {
+      if (val) {
+        this.getList()
+      }
+
+    },
+    addRelevantPatents() {
+      this.loadPatent = !this.loadPatent
+      this.visibleSelected = true
+    },
+    close2() {
+      this.visibleSelected = false
+      this.loadPatent = !this.loadPatent
+    },
+    sift() {
+      this.visible = true
+    },
+    sure() {
+      this.formS = JSON.parse(JSON.stringify(this.form))
+      if (Object.keys(this.formS).length != 0) {
+        for (let key in this.formS) {
+          this.queryParams[key] = this.formS[key]
+        }
+        this.handleCancelSelectNumber()
+        this.getList2()
+      }
+      this.visible = false
+    },
+    close() {
+      this.visible = false
+      this.form = this.formS
+    },
+    getSelectedTotal() {
+      //console.log(this.queryParams.patentNos,this.queryParams.notInPatentNos);
+      this.selectedTotal = Number(this.endNumber) - Number(this.startNumber) + 1 + Number(this.queryParams.patentNos.length) - Number(this.queryParams.notInPatentNos.length)
+    },
+    async Switch(type) {//选择...确定公用
+      let params = { ...this.queryParams }
+      if (params.field.length == 0) {
+        params.field = params.tree
+      }
+      switch (type) {
+        case 0:
+          this.queryParams.selected = this.tableData.map(item => item.patentNo);
+          this.checkList = [...new Set(this.checkList.concat(this.queryParams.selected))]
+          // this.selectedTotal = this.checkList.length
+          this.queryParams.selected = []
+          if (!this.quickSelect) {
+            this.queryParams.patentNos = JSON.parse(JSON.stringify(this.checkList))
+            this.getSelectedTotal()
+            break;
+          }
+          this.tableData.forEach((item, index) => {
+            var position = (this.queryParams.current - 1) * this.queryParams.size + index + 1
+            if (position >= this.startNumber) {
+              if (this.endNumber >= this.queryParams.current * this.queryParams.size) {
+                var index1 = this.queryParams.notInPatentNos.findIndex(i => {
+                  return i == item.patentNo
+                })
+                if (index1 != -1) {
+                  this.queryParams.notInPatentNos.splice(index1, 1)
+                }
+              } else {
+                if (position <= this.endNumber) {
+                  var index1 = this.queryParams.notInPatentNos.findIndex(i => {
+                    return i == item.patentNo
+                  })
+                  if (index1 != -1) {
+                    this.queryParams.notInPatentNos.splice(index1, 1)
+                  }
+                } else {
+                  var index2 = this.queryParams.patentNos.findIndex(i => {
+                    return i == item.patentNo
+                  })
+                  if (index2 == -1) {
+                    this.queryParams.patentNos.push(item.patentNo)
+                  }
+                }
+              }
+            } else {
+              var index2 = this.queryParams.patentNos.findIndex(i => {
+                return i == item.patentNo
+              })
+              if (index2 == -1) {
+                this.queryParams.patentNos.push(item.patentNo)
+              }
+            }
+          })
+          this.getSelectedTotal()
+          this.getList()
+          break
+        case 1:
+          params.startNumber = 1;
+          params.endNumber = this.total
+          this.startNumber = 1
+          this.endNumber = this.total
+        // this.commonSwitch()
+        // this.quickSelect = true
+        // break
+        case 2:
+          // this.selectNumberLoading = true
+          // this.$api.getComPatentNos(params).then(response => {
+          //   //console.log(response.data)
+          //   this.queryParams.selected = response.data
+          //   this.checkList = [...new Set(this.checkList.concat(this.queryParams.selected))]
+          //   // this.checkList = this.queryParams.selected
+          //   this.selectNumberLoading = false
+          //   this.getList()
+          // }).catch(error => {
+          //   this.selectNumberLoading = false
+          // })
+          this.queryParams.notInPatentNos = []
+          this.queryParams.patentNos = []
+          if (type == 2) {
+            if (!Number(this.queryParams.startNumber) || !Number(this.queryParams.endNumber)) {
+              this.queryParams.endNumber = this.endNumber > 0 ? this.endNumber : this.total
+              this.queryParams.startNumber = this.endNumber > 0 ? this.startNumber : 1
+              break;
+            }
+            this.startNumber = this.queryParams.startNumber
+            this.endNumber = this.queryParams.endNumber
+
+          }
+
+          // this.checkList =JSON.parse(JSON.stringify(this.queryParams.selected)) 
+          this.checkList = []
+          this.queryParams.notInPatentNos = []
+          this.queryParams.patentNos = []
+          await this.getList()
+          this.commonSwitch()
+          this.quickSelect = true
+          break
+      }
+    },
+    commonSwitch() {
+      //console.log(this.queryParams.startNumber,this.queryParams.endNumber)
+      if (this.queryParams.size * this.queryParams.current >= this.startNumber) {
+        if (this.queryParams.size * this.queryParams.current >= this.endNumber) {
+          if (this.queryParams.size * (this.queryParams.current - 1) + 1 <= this.startNumber) {
+            var a = this.startNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.endNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            //console.log(a,b)
+            for (var y = a; y <= b; y++) {
+              //console.log(this.tableData[y])
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          } else {
+            var a = (this.queryParams.size * (this.queryParams.current - 1) + 1) - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.endNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            for (var y = a; y <= b; y++) {
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          }
+        } else {
+          if (this.queryParams.size * (this.queryParams.current - 1) + 1 <= this.startNumber) {
+            var a = this.startNumber - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.queryParams.size * this.queryParams.current - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            for (var y = a; y <= b; y++) {
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          } else {
+            var a = (this.queryParams.size * (this.queryParams.current - 1) + 1) - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            var b = this.queryParams.size * this.queryParams.current - (this.queryParams.size * (this.queryParams.current - 1) + 1)
+            for (var y = a; y <= b; y++) {
+              this.toIsDelete(this.tableData[y].patentNo, y)
+
+            }
+          }
+        }
+      }
+      this.checkList = [...new Set(this.checkList)]
+      this.selected = this.checkList
+      this.getSelectedTotal()
+      // //console.log(this.checkList)
+
+    },
+    toIsDelete(patentNo, y) {
+      var index2 = this.queryParams.notInPatentNos.findIndex(i => {
+        return i == patentNo
+      })
+      if (index2 != -1) {
+
+      } else {
+        this.checkList.push(this.tableData[y].patentNo)
+      }
+      var index = this.queryParams.selected.findIndex(item => {
+        return item == patentNo
+      })
+      if (index != -1) {
+        var index3 = this.queryParams.notInPatentNos.findIndex(m => {
+          return m == patentNo
+        })
+        if (index3 != -1) {
+
+        } else {
+          this.queryParams.notInPatentNos.push(this.queryParams.selected[index])
+        }
+
+      }
+
+
+    },
+    handleSelectNumber(type) {
+
+      this.Switch(type)
+
+    },
+
+    handleCancelSelectNumber() {//取消选择
+      this.queryParams.selected = []
+      this.queryParams.patentNos = []
+      this.queryParams.notInPatentNos = []
+      this.checkList = []
+      this.quickSelect = false
+      this.startNumber = 1
+      this.queryParams.startNumber = 1
+      this.queryParams.endNumber = this.total
+      this.endNumber = 0
+      this.selectedTotal = 0
+      // this.getList()
+    },
+    getFunInfo(val) {
+      //console.log(this.quickSelect);
+      if (this.quickSelect) {
+        var index4 = this.queryParams.selected.findIndex(item => {
+          return item == val
+        })
+        if (index4 != -1) {
+          var a = {
+            productId: this.productId,
+            patentNo: val
+          }
+          this.$api.deleteCompareNo(a).then(response => {
+            if (response.code == 200) {
+              // this.getList()
+              this.queryParams.selected.splice(index4, 1)
+            }
+          })
+
+          this.isFind(val)
+
+        } else {
+          this.isFind(val)
+        }
+      } else {
+        var index = this.queryParams.selected.findIndex(item => {
+          return item == val
+        })
+        if (index != -1) {
+          var a = {
+            productId: this.productId,
+            patentNo: val
+          }
+          this.$api.deleteCompareNo(a).then(response => {
+            if (response.code == 200) {
+              this.getList()
+            }
+          })
+        } else {
+          var index5 = this.queryParams.patentNos.findIndex(item => {
+            return item == val
+          })
+          if (index5 != -1) {
+            this.queryParams.patentNos.splice(index5, 1)
+          } else {
+            this.queryParams.patentNos.push(val)
+          }
+
+        }
+      }
+      this.getSelectedTotal()
+    },
+    isFind(val) {
+      if (this.isFetch) {
+        var index = this.selected.findIndex(item => {
+          return item == val
+        })
+        if (index != -1) {
+          //console.log(val)
+          this.queryParams.notInPatentNos.push(val)
+          this.selected.splice(index, 1)
+        } else {
+          var index2 = this.queryParams.notInPatentNos.findIndex(i => {
+            return i == val
+          })
+          if (index2 != -1) {
+            this.queryParams.notInPatentNos.splice(index2, 1)
+          } else {
+            var index3 = this.queryParams.patentNos.findIndex(m => {
+              return m == val
+            })
+            if (index3 != -1) {
+              this.queryParams.patentNos.splice(index3, 1)
+            } else {
+              this.queryParams.patentNos.push(val)
+            }
+          }
+        }
+        this.isFetch = false
+      } else {
+        var index3 = this.queryParams.patentNos.findIndex(m => {
+          return m == val
+        })
+        if (index3 != -1) {
+          this.queryParams.patentNos.splice(index3, 1)
+        } else {
+          var index2 = this.queryParams.notInPatentNos.findIndex(i => {
+            return i == val
+          })
+          if (index2 != -1) {
+            this.queryParams.notInPatentNos.splice(index2, 1)
+          } else {
+            // this.isFetch = true
+            // this.getFunInfo()
+            var index = this.selected.findIndex(item => {
+              return item == val
+            })
+            if (index != -1) {
+              var index4 = this.queryParams.selected.findIndex(item => {
+                return item == val
+              })
+              if (index4 != -1) {
+                var a = {
+                  productId: this.productId,
+                  patentNo: val
+                }
+                this.$api.deleteCompareNo(a).then(response => {
+                  if (response.code == 200) {
+                    // this.getList()
+                    this.queryParams.selected.splice(index4, 1)
+                  }
+                })
+              } else {
+                this.queryParams.notInPatentNos.push(val)
+                this.selected.splice(index, 1)
+              }
+            } else {
+              this.queryParams.patentNos.push(val)
+            }
+          }
+        }
+      }
+    },
+    getRowKeys(row) {
+      return row.id
+    },
+    async handleCurrentChange(val) {//分页
+      this.queryParams.current = val;
+      // this.checkList = []
+      // this.queryParams.endNumber=0
+      await this.getList();
+      if (this.quickSelect) {
+        this.commonSwitch()
+      }
+
+    },
+    // 删除proPatentDelete
+    handleDelete() { 
+        this.saveDeleteQueryParams()
+        this.$confirm('此操作将删除该产品相关专利,不可恢复, 是否继续?', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+        }).then(res => {
+          this.$api.proPatentDelete(this.queryParams).then(res => {
+            if (res.code == 200) {
+              if (res.data && !res.data.includes('删除成功')) {
+                this.alterMessage(res.data,1)
+              } else {
+                 this.$message.success('删除成功')
+              }
+              this.saveDelete()
+            }
+          })
+        }).catch(err => {
+          this.$message.info('取消删除')
+        })
+       
+    },
+    savePatentList() {//保存
+      this.saveDeleteQueryParams()
+      this.btnLoading = true
+      this.$api.addCompareFile(this.queryParams).then(res => {
+        if (res.code == 200) {
+          this.btnLoading = false
+          this.saveDelete()
+          this.$message.success('保存成功')
+        }
+      })
+    },
+    saveDeleteQueryParams() {
+      this.queryParams.startNumber = this.startNumber
+      this.queryParams.endNumber = this.endNumber
+      this.queryParams.productId = this.productId
+    },
+    saveDelete() {
+          this.startNumber = 1
+          this.endNumber = 0
+          this.selectedTotal = 0
+          this.quickSelect = false
+          this.queryParams.patentNos = []
+          this.queryParams.notInPatentNos = []
+          this.checkList = []
+          this.getList2()
+    },
+    getList2() {
+      this.queryParams.current = 1
+      this.getList()
+    },
+    async getList(key) {
+      this.queryParams.productId = this.productId
+      this.queryParams.structureId = this.structureId
+      let params = JSON.parse(JSON.stringify(this.queryParams))
+      //console.log(params);
+      params.tree.map(tree => {
+        let field = params.field.filter(item => item.key === tree.key)
+        if (field.length === 0) {
+          params.field.push(tree)
+        }
+      })
+      params.tree = undefined
+      //console.log(params);
+      this.$store.commit('SET_PATENT_PARAMS', params)
+      this.loading = true
+      await this.$api.proPatentQueryPatents(params).then(response => {
+        this.loading = false
+        this.total = response.data.total
+        this.queryParams.endNumber = this.endNumber > 0 ? this.endNumber : response.data.total
+        this.queryParams.startNumber = this.endNumber > 0 ? this.startNumber : 1
+
+        this.tableData = response.data.records
+        // this.queryParams.selected = response.data.select
+        // this.queryParams.selectedTotal = response.data.selectedTotal
+        this.checkList = JSON.parse(JSON.stringify([...new Set(this.checkList.concat(this.queryParams.selected))]))
+        this.selected = this.checkList
+        this.isFetch = true
+
+
+
+      }).catch(error => {
+
+      })
+    },
+    showPatent(even) {//选择下拉框
+      //console.log(even);
+    },
+  },
+  watch: {
+    'tableData'(val, val1) {
+
+      this.$nextTick(() => {
+        this.tableOffSetHeight()
+      })
+    },
+
+  }
+}
+</script>
+
+<style lang="scss" >
+.btn1 {
+  background: #909399;
+}
+
+.btn1:hover {
+
+  background-color: #a4a7ab;
+}
+
+.patent-fast-edit-popover {
+  padding: 0 !important;
+
+  .btn {
+    color: #000;
+    line-height: 40px;
+    border-radius: 5px;
+    padding-left: 10px;
+    cursor: pointer;
+
+    &:hover {
+      background: #adadad;
+      color: #fff;
+    }
+  }
+
+  .disabled {
+    cursor: not-allowed !important;
+  }
+
+  .bottom {
+    text-align: right;
+    color: #1e9fff;
+    line-height: 40px;
+    padding-left: 10px;
+    font-size: 18px;
+  }
+
+  .el-divider--horizontal {
+    margin: 10px 0 !important;
+  }
+
+  .select-number {
+    .el-input {
+      width: 70px;
+    }
+
+    span {
+      padding: 0 3px;
+    }
+  }
+
+}
+
+.patent-left {
+  margin-top: 69px;
+  overflow: hidden;
+  border-top: 1px solid rgb(228 231 237);
+}
+</style>

+ 40 - 0
RMS-FrontEnd/src/views/product/components/relatedPatents/index.vue

@@ -0,0 +1,40 @@
+<template>
+  <div>
+    <relatedPatents :productId="productId" :structureId="structureId" :name="name"></relatedPatents>
+  </div>
+</template>
+
+<script>
+import relatedPatents from "./components/relatedPatents.vue";
+export default {
+  components: {
+    relatedPatents,
+  },
+  data() {
+    return {
+
+    }
+  },
+  computed: {
+    productId() {
+      return this.$route.query.id
+    },
+    structureId(){
+      return this.$route.query.structureId
+    },
+    name(){
+      return this.$route.query.name
+    }
+  },
+  mounted() {
+
+  },
+  methods: {
+
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 27 - 0
RMS-FrontEnd/src/views/product/index.vue

@@ -0,0 +1,27 @@
+<template>
+  <div>
+    <product></product>
+  </div>
+</template>
+
+<script>
+import product from './components/product.vue'
+export default {
+  components: {
+    product,
+  },
+  data() {
+    return {
+
+    }
+  },
+  mounted() { },
+  methods: {
+    
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>