Просмотр исходного кода

Merge branch 'master' into eta1.4.6

Karsa 10 месяцев назад
Родитель
Сommit
82e11e8299
100 измененных файлов с 5263 добавлено и 271 удалено
  1. 0 9
      index.html
  2. 4 2
      src/api/api.js
  3. 2 2
      src/api/http.js
  4. 225 0
      src/api/modules/approve.js
  5. 7 0
      src/api/modules/dataSource.js
  6. 3 0
      src/api/modules/pptApi.js
  7. 3 0
      src/api/modules/pptEnApi.js
  8. 21 0
      src/api/modules/sheetApi.js
  9. 20 0
      src/api/modules/smartReport.js
  10. 60 2
      src/api/modules/thirdBaseApi.js
  11. BIN
      src/assets/img/approve_m/approve.png
  12. BIN
      src/assets/img/approve_m/future-icon.png
  13. 3 0
      src/assets/img/approve_m/future-icon.svg
  14. BIN
      src/assets/img/approve_m/passed-icon.png
  15. 10 0
      src/assets/img/approve_m/passed-icon.svg
  16. 3 0
      src/assets/img/approve_m/passed-msg.svg
  17. BIN
      src/assets/img/approve_m/passed.png
  18. BIN
      src/assets/img/approve_m/pending.png
  19. BIN
      src/assets/img/approve_m/process-icon.png
  20. 6 0
      src/assets/img/approve_m/process-icon.svg
  21. 3 0
      src/assets/img/approve_m/process-msg.svg
  22. BIN
      src/assets/img/approve_m/reject-icon.png
  23. 3 0
      src/assets/img/approve_m/reject-icon.svg
  24. 10 0
      src/assets/img/approve_m/reject-msg.svg
  25. BIN
      src/assets/img/approve_m/reject.png
  26. BIN
      src/assets/img/approve_m/return-icon.png
  27. 5 0
      src/assets/img/approve_m/return-icon.svg
  28. 3 0
      src/assets/img/approve_m/return-msg.svg
  29. BIN
      src/assets/img/approve_m/return.png
  30. BIN
      src/assets/img/approve_m/select.png
  31. BIN
      src/assets/img/approve_m/start.png
  32. BIN
      src/assets/img/icons/add_blue_new.png
  33. BIN
      src/assets/img/icons/fullsreen.png
  34. BIN
      src/assets/img/icons/more.png
  35. BIN
      src/assets/img/ppt_m/text-icon.png
  36. BIN
      src/assets/img/smartReport/icon17.png
  37. BIN
      src/assets/img/smartReport/icon18.png
  38. 130 42
      src/components/lzTable.vue
  39. 146 0
      src/components/notificationMsg.vue
  40. 65 0
      src/mixins/reportApproveConfig.js
  41. 27 0
      src/routes/modules/approveRoutes.js
  42. 16 0
      src/routes/modules/dataRoutes.js
  43. 12 0
      src/routes/modules/oldRoutes.js
  44. 26 4
      src/utils/buttonConfig.js
  45. 1 0
      src/utils/parseData.js
  46. 8 1
      src/utils/svgToblob.js
  47. 7 2
      src/views/Home.vue
  48. 12 12
      src/views/Login.vue
  49. 386 0
      src/views/approve_manage/approveDetail.vue
  50. 194 0
      src/views/approve_manage/approveEdit.vue
  51. 311 0
      src/views/approve_manage/approveList.vue
  52. 162 0
      src/views/approve_manage/approveSetting.vue
  53. 112 0
      src/views/approve_manage/components/flowEdiotr.vue
  54. 44 0
      src/views/approve_manage/components/flowNode/addNode.vue
  55. 280 0
      src/views/approve_manage/components/flowNode/approveNode.vue
  56. 26 0
      src/views/approve_manage/components/flowNode/endNode.vue
  57. 43 0
      src/views/approve_manage/components/flowNode/startNode.vue
  58. 81 0
      src/views/approve_manage/components/rejectDialog.vue
  59. 60 0
      src/views/approve_manage/components/timeLine.vue
  60. 120 0
      src/views/approve_manage/components/timeLineItem.vue
  61. 185 0
      src/views/approve_manage/components/treeTransfer.vue
  62. 95 0
      src/views/approve_manage/config/tableConfig.js
  63. 39 0
      src/views/approve_manage/css/nodeStyle.scss
  64. 6 0
      src/views/approve_manage/css/pageStyle.scss
  65. 31 0
      src/views/approve_manage/mixins/approveMixins.js
  66. 19 0
      src/views/chartFrame_manage/common/config.js
  67. 1 5
      src/views/chartFrame_manage/common/graph.js
  68. 31 6
      src/views/chartFrame_manage/components/frameContainer.vue
  69. 9 1
      src/views/chartFrame_manage/components/frameToolBar.vue
  70. 9 1
      src/views/chartFrame_manage/css/basePage.scss
  71. 1 1
      src/views/chartFrame_manage/css/customTree.scss
  72. 14 2
      src/views/chartFrame_manage/frameEditor.vue
  73. 16 8
      src/views/chartFrame_manage/index.vue
  74. 11 7
      src/views/chartRelevance_manage/crossVarietyAnalysis/components/tagSetDialog.vue
  75. 1 0
      src/views/dataEntry_manage/chartSetting.vue
  76. 41 10
      src/views/dataEntry_manage/components/addTarget.vue
  77. 7 2
      src/views/dataEntry_manage/components/insertData.vue
  78. 1 0
      src/views/dataEntry_manage/css/chartfit.scss
  79. 17 14
      src/views/dataEntry_manage/databaseComponents/addTargetDiaBase.vue
  80. 261 42
      src/views/dataEntry_manage/databaseComponents/computedDialog.vue
  81. 6 3
      src/views/dataEntry_manage/databaseComponents/util.js
  82. 11 9
      src/views/dataEntry_manage/databaseList.vue
  83. 564 0
      src/views/dataEntry_manage/thirdBase/YyzxData.vue
  84. 795 0
      src/views/dataEntry_manage/thirdBase/ysTargetBase.vue
  85. 4 3
      src/views/dataSource_manage/components/DelEDBTable.vue
  86. 5 8
      src/views/dataSource_manage/components/DetailTable.vue
  87. 68 10
      src/views/dataSource_manage/components/EDBInfoChangeTable.vue
  88. 138 4
      src/views/dataSource_manage/components/GLRefreshFailDetail.vue
  89. 3 3
      src/views/dataSource_manage/components/StatisticTable.vue
  90. 2 2
      src/views/datasheet_manage/components/SheetExcel.vue
  91. 7 1
      src/views/datasheet_manage/customAnalysis/list.vue
  92. 2 1
      src/views/futures_manage/chartEditor.vue
  93. 7 1
      src/views/login_manage/EmailModel.vue
  94. 8 5
      src/views/login_manage/MobileModel.vue
  95. 1 1
      src/views/login_manage/modelMixins.js
  96. 84 8
      src/views/mychart_manage/index.vue
  97. 4 0
      src/views/operation_manage/AIQA/AIQA.vue
  98. 2 1
      src/views/ppt_manage/mixins/layerMixins.js
  99. 9 1
      src/views/ppt_manage/mixins/pptEditorMixins.js
  100. 88 35
      src/views/ppt_manage/mixins/pptMixins.js

+ 0 - 9
index.html

@@ -47,15 +47,6 @@
             src: url(./static/css/fonts/SourceHanSerifCN-Regular.ttf);
         }
     </style>
-	<script>
-		var _hmt = _hmt || [];
-		(function() {
-		  var hm = document.createElement("script");
-		  hm.src = "https://hm.baidu.com/hm.js?2147f2700a1a306aa027116f80ef640d";
-		  var s = document.getElementsByTagName("script")[0]; 
-		  s.parentNode.insertBefore(hm, s);
-		})();
-	</script>	
 	<script>
 		window.addEventListener('error',(e)=>{
 			if(e.message=="Uncaught SyntaxError: Unexpected token '<'"){

+ 4 - 2
src/api/api.js

@@ -10,7 +10,8 @@ import {
   sciDataInterface,
   baiinfoInterface,
   nationalInterface,
-  databankInterface
+  databankInterface,
+  yongyiInterface
 } from './modules/thirdBaseApi';
 
 //手工指标 手工数据 手工数据权限
@@ -112,7 +113,8 @@ export {
   cloudDiskInterface,
   homeInterface,
   businessTripInterence,
-  reportVarietyENInterence
+  reportVarietyENInterence,
+  yongyiInterface
 };
 
 //老接口 研报 ppt等

+ 2 - 2
src/api/http.js

@@ -77,7 +77,7 @@ const cancelTimeoutUrlPost = ["/cloud_disk/resource/upload"];
 export default {
   post(url, data) {
     //post请求方式
-    let timeout = cancelTimeoutUrlPost.includes(url) ? 0 : 100000;
+    let timeout = cancelTimeoutUrlPost.includes(url) ? 0 : 600000;
     return axios({
       method: "post",
       url: url,
@@ -100,7 +100,7 @@ export default {
       baseURL: process.env.VUE_APP_API_ROOT,
       url: url,
       params: getData(data, url),
-      timeout: 100000,
+      timeout:600000,
       headers: {
         "Content-type": "application/x-www-form-urlencoded; charset=utf-8",
       },

+ 225 - 0
src/api/modules/approve.js

@@ -0,0 +1,225 @@
+import http from "@/api/http.js"
+
+export const approveInterence = {
+    /**
+     * 获取审批流列表
+     * @param {Object} params 
+     * @param {Number} params.PageSize
+     * @param {Number} params.CurrentIndex
+     * @param {Number} params.ReportType 报告类型:1-中文研报;2-英文研报;3-智能研报
+     * @param {Number} params.ClassifyFirstId 关联报告一级分类ID
+     * @param {Number} params.ClassifySecondId 关联报告二级分类ID
+     * @param {String} params.Keyword
+     * @param {Number} params.SortRule 排序方式: 1-正序; 2-倒序(默认)
+     * @returns 
+     */
+    getApproveFlowList(params){
+        return http.get("/report_approve/flow/list",params)
+    },
+    /**
+     * 获取报告分类树
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveFlowId 审批流ID(非必填)
+     * @returns 
+     */
+    getReportClassifyTree(params){
+        return http.get("/report_approve/report/classify_tree",params)
+    },
+    /**
+     * 删除审批流
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveFlowId
+     * @returns 
+     */
+    deleteApproveFlow(params){
+        return http.post("/report_approve/flow/remove",params)
+    },
+    /**
+     * 获取审批流详情
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveFlowId
+     * @returns 
+     */
+    getApproveFlowDetail(params){
+        return http.get("/report_approve/flow/detail",params)
+    },
+    /**
+     * 新增审批流
+     * @param {Object} params 
+     * @param {String} params.FlowName 审批流名称
+     * @param {Number} params.ReportType 报告类型:1-中文研报;2-英文研报;3-智能研报
+     * @param {Number} params.ClassifyFirstId 一级分类ID
+     * @param {Number} params.ClassifySecondId 二级分类ID
+     * @param {Object[]} params.Nodes 审批节点
+     * @param {Number} params.Nodes[].ApproveType 审批类型:1-依次审批;2-会签;3-或签
+     * @param {Object[]} params.Nodes[].Users 审批人信息
+     * @param {String} params.Nodes[].Users[].UserType 审批人类型: user-用户; role-角色
+     * @param {Number} params.Nodes[].Users[].UserId 用户/角色ID
+     * @param {String} params.Nodes[].Users[].UserName 用户/角色姓名
+     * @param {String} params.Nodes[].Users[].Sort 依次审批时的排序
+     * @returns 
+     */
+    addNewApproveFlow(params){
+        return http.post("/report_approve/flow/add",params)
+    },
+    /**
+     * 编辑审批流
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveFlowId 审批流Id
+     * 其他参数同上
+     * @returns 
+     */
+    editApproveFlow(params){
+        return http.post("/report_approve/flow/edit",params)
+    },
+
+
+    /**
+     * 获取审批列表
+     * @param {Object} params 
+     * @param {Number} params.PageSize
+     * @param {Number} params.CurrentIndex
+     * @param {Number} params.ReportType 报告类型:1-中文研报;2-英文研报;3-智能研报
+     * @param {Number} params.ClassifyFirstId 报告的倒数第二级id
+     * @param {Number} params.ClassifySecondId 报告的倒数第一级id
+     * @param {String} params.Keyword
+     * @param {Number} params.SortRule 排序方式: 1-正序; 2-倒序(默认)
+     * @param {Number} params.SortField 排序字段:1-提交时间;2-处理时间;3-审批时间
+     * @param {Number} params.ListType 列表类型:1-待处理;2-已处理;3-我发起的
+     * @param {Number} params.ApproveState 审批状态:1-待审批;2-已审批/已通过;3-已驳回;4-已撤回/已撤销
+     * @param {Number} params.TimeType 时间类型:1-提交时间;2-处理时间;3-审批时间
+     * @param {String} params.StartTime 开始时间
+     * @param {String} params.EndTime 结束时间
+     * @returns 
+     */
+    getApproveList(params){
+        return http.get("/report_approve/list",params)
+    },
+    /**
+     * 获取审批详情
+     * @param {Object} params
+     * @param {Number} params.ReportApproveId
+     * @returns 
+     */
+    getApproveDetail(params){
+        return http.get("/report_approve/detail",params)
+    },
+    /**
+     * 审批消息通知
+     * @param {Object} params 
+     * @param {Number} params.PageSize
+     * @param {Number} params.CurrentIndex
+     * @returns 
+     */
+    getApproveMsgList(params){
+        return http.get("/report_approve/message/list",params)
+    },
+    /**
+     * 消息通知已读
+     * @param {Object} params 
+     * @param {Number} params.MessageId
+     * @returns 
+     */
+    readApproveMsg(params){
+        return http.post("/report_approve/message/read",params)
+    },
+    /**
+     * 撤销审批
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveId
+     * @returns 
+     */
+    cancelApprove(params){
+        return http.post("/report_approve/cancel",params)
+    },
+    /**
+     * 通过审批
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveId
+     * @returns 
+     */
+    passApprove(params){
+        return http.post("/report_approve/approve",params)
+    },
+    /**
+     * 驳回审批
+     * @param {Object} params 
+     * @param {Number} params.ReportApproveId
+     * @param {String} params.ApproveRemark
+     * @returns 
+     */
+    rejectApprove(params){
+        return http.post("/report_approve/refuse",params)
+    },
+    /**
+     * 校验研报分类是否有审批流
+     * @param {Object} params 
+     * @param {Number} params.ReportType 报告类型:1-中文研报;2-英文研报;3-智能研报
+     * @param {Number} params.ClassifyFirstId 倒数第二级分类的id
+     * @param {Number} params.ClassifySecondId 倒数第一级分类的id
+     * @returns 
+     */
+    checkClassifyApprove(params){
+        return http.post("/report_approve/classify/check_open",params)
+    },
+
+
+
+
+    /*------------研报审批接口----------- */
+    /**
+     * 中文研报提交审批
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    reportCnSubmit(params){
+        return http.post("/report/approve/submit",params)
+    },
+    /**
+     * 中文研报撤销审批
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    reportCnCancel(params){
+        return http.post("/report/approve/cancel",params)
+    },
+    /**
+     * 英文研报提交审批
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    reportEnSubmit(params){
+        return http.post("/english_report/approve/submit",params)
+    },
+    /**
+     * 英文研报撤销审批
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    reportEnCancel(params){
+        return http.post("/english_report/approve/cancel",params)
+    },
+    /**
+     * 智能研报提交审批
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    reportSmartSubmit(params){
+        return http.post("/smart_report/approve/submit",params)
+    },
+    /**
+     * 智能研报撤销审批
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    reportSmartCancel(params){
+        return http.post("/smart_report/approve/cancel",params)
+    },
+
+}

+ 7 - 0
src/api/modules/dataSource.js

@@ -56,7 +56,14 @@ const apiDataSource={
     // 获取终端编码数据
     terminalCodeArr:params=>{
         return http.get('/data_stat/terminal/code',{})
+    },
+
+    //数据刷新失败详情列表
+    updateFailDetailList:params=>{
+        return http.get('/data_stat/edb_update_stat/failed/detail',params)
     }
+
+
     
 
 }

+ 3 - 0
src/api/modules/pptApi.js

@@ -23,6 +23,7 @@ export default{
      * FirstPage:{Title,ReportType,PptDate,ImgUrl} 封面信息
      * Content:string ppt具体内容,转字符串
      * GroupId:目录id
+     * CoverContent:string 封面页的具体内容,转字符串
      */
     addppt:params=>{
         return http.post('/pptv2/add',params)
@@ -32,6 +33,7 @@ export default{
      * PptId
      * FirstPage
      * Content
+     * CoverContent
      */
     editppt:params=>{
         return http.post('/pptv2/edit',params)
@@ -71,6 +73,7 @@ export default{
      * PptId
      * FirstPage
      * Content
+     * CoverContent
      */
     saveLog:params=>{
         return http.post('/pptv2/saveLog',params)

+ 3 - 0
src/api/modules/pptEnApi.js

@@ -16,6 +16,7 @@ export const pptEnInterface = {
      * FirstPage:{Title,ReportType,PptDate,ImgUrl} 封面信息
      * Content:string ppt具体内容,转字符串
      * GroupId:目录id
+     * CoverContent:string 封面页具体内容,转字符串
      */
     addppt:params=>{
         return http.post('/ppt_english/add',params)
@@ -25,6 +26,7 @@ export const pptEnInterface = {
      * PptId
      * FirstPage
      * Content
+     * CoverContent
      */
     editppt:params=>{
         return http.post('/ppt_english/edit',params)
@@ -56,6 +58,7 @@ export const pptEnInterface = {
      * PptId
      * FirstPage
      * Content
+     * CoverContent
      */
     saveLog:params=>{
         return http.post('/ppt_english/saveLog',params)

+ 21 - 0
src/api/modules/sheetApi.js

@@ -287,6 +287,27 @@ export const getMixedCalculateData = params => {
 	return http.post('/datamanage/excel_info/mixed/calculate',params)
 }
 
+/**
+ * 表格一键刷新
+ * @param {Object} params
+ * @param {Array} params.ExcelCodes 表格唯一编码
+ * @param {String} params.Source 来源,枚举值:report、english_report、smart_report
+ * @param {Number} params.ReportId 报告id
+ * @param {Number} params.ReportChapterId 章节id 非章节传0
+ */
+export const refreshSheet = (params)=>{
+    return http.post('/datamanage/excel_info/table/batch_refresh',params)
+}
+/**
+ * 获取表格刷新结果
+ * @param {Object} params
+ * @param {String} params.Source 来源,枚举值:report、english_report、smart_report
+ * @param {Number} params.ReportId 报告id
+ * @param {Number} params.ReportChapterId 章节id 非章节传0
+ */
+export const getRefreshResult = (params)=>{
+    return http.post('/datamanage/excel_info/table/batch_refresh/result',params)
+}
 
 /* =====自定义分析==== */
 

+ 20 - 0
src/api/modules/smartReport.js

@@ -76,6 +76,26 @@ const apiSmartReport={
     //获取上期已发布报告
     getLastReport:params=>{
         return http.get('/smart_report/last_published_report',params)
+    },
+
+    // 资源库列表
+    imgReourceList:params=>{
+        return http.get('/smart_report/resource/list',params)
+    },
+
+    //新增资源库图片
+    imgReourceAdd:params=>{
+        return http.post('/smart_report/resource/add',params)
+    },
+
+    //资源库图片重命名
+    imgReourceRename:params=>{
+        return http.post('/smart_report/resource/rename',params)
+    },
+
+    //资源库图片删除
+    imgReourceDel:params=>{
+        return http.post('/smart_report/resource/remove',params)
     }
 
 }

+ 60 - 2
src/api/modules/thirdBaseApi.js

@@ -186,7 +186,38 @@ const smmDataInterface = {
 	 */
   cleanClassify:params=>{
     return http.post('/datamanage/smm/reset',params)
-  }
+  },
+	// 有色原始数据库
+	/**
+   * 列表
+	*/
+	getYsDataList:params=>{
+		return http.get('datamanage/smm/api/list',params)
+	},
+	/**
+   * 分类列表
+	*/
+	getYsTypeDataList:params=>{
+		return http.get('datamanage/smm/api/type/list',params)
+	},
+	/**
+   * 检验有色指标是否存在
+	*/
+	getYsEdbCodeCheck:params=>{
+		return http.get('datamanage/edb_info/smm/exist/check',params)
+	},
+	/**
+   * 查询有色指标
+	*/
+	getYsEdbCodeSearch:params=>{
+		return http.get('datamanage/edb_info/smm/search',params)
+	},
+	/**
+   * 批量添加有色指标
+	*/
+	ysEdbAddBatch:params=>{
+		return http.post('datamanage/edb_info/smm/batch/add',params)
+	},
 }
 
 /* 中国煤炭网 */
@@ -720,6 +751,32 @@ const databankInterface = {
     },
 }
 
+/* 涌益咨询 */
+const yongyiInterface={
+	/**
+	 * 分类列表
+	 * @param {} params 
+	 * @returns 
+	 */
+	classifyList: params => {
+		return http.get('/datamanage/yongyi/classify',params);
+	},
+	/**
+	 * 获取指标列表详情
+	 */
+	dataList: params => {
+		return http.get('/datamanage/yongyi/index/data',params);
+	},
+	//单个指标数据
+	getTargetDataList:params=>{
+		return http.get('/datamanage/yongyi/single_data',params);
+	},
+	// 搜索
+	getTargetListByName:params=>{
+		return http.get('/datamanage/yongyi/search_list',params);
+	},
+}
+
 export { 
 	lzDataInterface,
 	glDataInterface,
@@ -730,5 +787,6 @@ export {
   sciDataInterface,
   baiinfoInterface,
   nationalInterface,
-  databankInterface
+  databankInterface,
+  yongyiInterface
 }

BIN
src/assets/img/approve_m/approve.png


BIN
src/assets/img/approve_m/future-icon.png


+ 3 - 0
src/assets/img/approve_m/future-icon.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="8" cy="8" r="7" stroke="#C0C4CC" stroke-width="2"/>
+</svg>

BIN
src/assets/img/approve_m/passed-icon.png


+ 10 - 0
src/assets/img/approve_m/passed-icon.svg

@@ -0,0 +1,10 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2561_11064)">
+<path d="M0 8C0 3.58179 3.58179 0 8 0C12.4182 0 16 3.58179 16 8C16 12.4182 12.4182 16 8 16C3.58179 16 0 12.4182 0 8ZM6.55172 11.0411C6.65221 11.1447 6.78931 11.2048 6.9336 11.2086C7.0779 11.2123 7.2179 11.1593 7.32359 11.061L12.5661 6.18703C12.6724 6.08722 12.735 5.94935 12.7399 5.80357C12.7449 5.65779 12.692 5.51596 12.5927 5.40911C12.4934 5.30227 12.3558 5.23909 12.2101 5.2334C12.0643 5.22771 11.9222 5.27997 11.8149 5.37876L6.98483 9.9029L4.71724 7.56469C4.66696 7.51198 4.60675 7.46975 4.54006 7.44043C4.47338 7.41111 4.40155 7.39529 4.32872 7.39387C4.25589 7.39246 4.1835 7.40548 4.11573 7.43219C4.04795 7.4589 3.98614 7.49876 3.93386 7.54948C3.88157 7.6002 3.83985 7.66077 3.81109 7.7277C3.78233 7.79463 3.76712 7.86659 3.76632 7.93943C3.76552 8.01227 3.77915 8.08455 3.80642 8.15209C3.8337 8.21964 3.87408 8.28111 3.92524 8.33297L6.552 11.0411H6.55172Z" fill="#0052D9"/>
+</g>
+<defs>
+<clipPath id="clip0_2561_11064">
+<rect width="16" height="16" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 3 - 0
src/assets/img/approve_m/passed-msg.svg

@@ -0,0 +1,3 @@
+<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.50115 0C4.25336 0 0 4.25284 0 9.5C0 14.7472 4.25336 19 9.50115 19C14.7489 19 19 14.7449 19 9.4977C19 4.25054 14.7444 0 9.50115 0ZM14.0439 7.59633L10.3922 11.1855L8.62384 12.9147C8.61695 12.9239 8.60776 12.933 8.59628 12.9445C8.5825 12.9583 8.56642 12.9721 8.55034 12.9859C8.44011 13.0915 8.29542 13.1489 8.14155 13.1489H8.11628C7.95552 13.1489 7.80624 13.0892 7.696 12.979C7.67992 12.9652 7.66614 12.9514 7.65236 12.9399C7.64318 12.9307 7.63629 12.9239 7.6294 12.9147L4.68053 9.96616C4.44168 9.72734 4.45775 9.32318 4.71498 9.06599C4.84588 8.9351 5.02043 8.86162 5.19727 8.86162C5.35803 8.86162 5.50731 8.92132 5.61755 9.03155L8.13006 11.5438L9.4644 10.2394L13.1183 6.65023C13.2286 6.5423 13.3756 6.48489 13.534 6.48489C13.7132 6.48489 13.89 6.56067 14.0209 6.69386C14.2735 6.95564 14.2873 7.3598 14.0439 7.59633Z" fill="#68BB8D"/>
+</svg>

BIN
src/assets/img/approve_m/passed.png


BIN
src/assets/img/approve_m/pending.png


BIN
src/assets/img/approve_m/process-icon.png


+ 6 - 0
src/assets/img/approve_m/process-icon.svg

@@ -0,0 +1,6 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="8" cy="8" r="7" fill="white" stroke="#0052D9" stroke-width="2"/>
+<circle cx="4" cy="8.5" r="1.5" fill="#0052D9"/>
+<circle cx="8" cy="8.5" r="1.5" fill="#0052D9"/>
+<circle cx="12" cy="8.5" r="1.5" fill="#0052D9"/>
+</svg>

+ 3 - 0
src/assets/img/approve_m/process-msg.svg

@@ -0,0 +1,3 @@
+<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.5 0C4.25327 0 0 4.25324 0 9.50002C0 12.894 1.81074 16.0301 4.75 17.7273C7.68945 19.4242 11.3107 19.4242 14.25 17.7273C17.1895 16.0303 19 12.8942 19 9.50002C19.0002 4.25343 14.7467 0 9.5 0ZM9.5 14.7779C8.91712 14.7779 8.44444 14.3053 8.44444 13.7224C8.44444 13.3454 8.64566 12.9968 8.97222 12.8083C9.29879 12.6198 9.70122 12.6198 10.0278 12.8083C10.3543 12.9968 10.5556 13.3454 10.5556 13.7224C10.5556 14.3051 10.0831 14.7779 9.5 14.7779ZM10.5556 10.5556C10.5556 11.1888 10.1333 11.6111 9.5 11.6111C8.86667 11.6111 8.44444 11.1888 8.44444 10.5556V5.27788C8.44444 4.64459 8.86667 4.22234 9.5 4.22234C10.1333 4.22234 10.5556 4.64459 10.5556 5.27788V10.5556Z" fill="#E98F36"/>
+</svg>

BIN
src/assets/img/approve_m/reject-icon.png


+ 3 - 0
src/assets/img/approve_m/reject-icon.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 7.93442C0.036889 5.7137 0.851645 3.75612 2.51279 2.18473C4.14539 0.640302 6.11295 -0.0867217 8.35598 0.00822671C10.511 0.0994046 12.3505 0.946285 13.8273 2.5179C15.3632 4.15237 16.0877 6.11726 15.9915 8.35695C15.8993 10.5061 15.0568 12.3422 13.4922 13.8175C11.8572 15.3593 9.89104 16.0873 7.6479 15.9917C5.49497 15.8998 3.6592 15.0518 2.18102 13.4843C0.735039 11.951 0.0268387 10.1078 0 7.93442ZM7.19038 7.98972C7.168 8.01406 7.15441 8.02971 7.1399 8.04434C6.69461 8.48983 6.24909 8.93521 5.80368 9.38081C5.46322 9.72142 5.12266 10.062 4.78278 10.4031C4.62677 10.5596 4.57172 10.747 4.62471 10.962C4.68981 11.2261 4.95203 11.4084 5.21859 11.3856C5.37289 11.3724 5.49612 11.3054 5.60347 11.1979C6.38351 10.4164 7.16423 9.63573 7.94484 8.85489C7.96071 8.839 7.97716 8.82392 8.01348 8.78907C8.02684 8.81352 8.03392 8.83797 8.04968 8.85374C8.82812 9.63401 9.60781 10.413 10.3861 11.1935C10.5262 11.3339 10.6882 11.4011 10.8888 11.3838C11.1686 11.3595 11.4141 11.0681 11.3896 10.7885C11.3755 10.6285 11.31 10.4993 11.1977 10.3872C10.4339 9.62464 9.6712 8.86083 8.90818 8.09747C8.80505 7.99418 8.80493 7.99429 8.90749 7.89169C9.67188 7.12696 10.4359 6.36189 11.201 5.59784C11.3367 5.46233 11.4045 5.30466 11.3859 5.11042C11.3538 4.77553 11.0216 4.53764 10.6937 4.61397C10.5534 4.64653 10.4503 4.73097 10.3522 4.82923C9.58337 5.60013 8.81316 6.36966 8.04454 7.1409C8.00776 7.17781 7.98801 7.17895 7.95055 7.14113C7.57515 6.76271 7.19781 6.38623 6.82104 6.00929C6.41195 5.60002 6.0032 5.19029 5.59353 4.78159C5.41857 4.607 5.2106 4.55181 4.9775 4.63751C4.58931 4.78021 4.47145 5.27975 4.78963 5.59499C5.46014 6.25928 6.12528 6.92918 6.79294 7.59645C6.92291 7.72659 7.0547 7.85558 7.19038 7.98972Z" fill="#AD352F"/>
+</svg>

+ 10 - 0
src/assets/img/approve_m/reject-msg.svg

@@ -0,0 +1,10 @@
+<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_5316_6053)">
+<path d="M9.5 19C4.25362 19 0 14.7464 0 9.5C0 4.25362 4.25362 0 9.5 0C14.7464 0 19 4.25362 19 9.5C19 14.7464 14.7464 19 9.5 19ZM9.5 8.38058L5.57808 4.45708C5.50453 4.38405 5.41731 4.32621 5.32141 4.28688C5.2255 4.24756 5.12279 4.2275 5.01914 4.22787C4.91548 4.22824 4.81292 4.24902 4.71729 4.28903C4.62167 4.32903 4.53487 4.38749 4.46183 4.46104C4.38799 4.53391 4.32929 4.62067 4.28911 4.71631C4.24892 4.81195 4.22804 4.9146 4.22767 5.01834C4.22731 5.12209 4.24745 5.22488 4.28696 5.3208C4.32647 5.41673 4.38455 5.5039 4.45787 5.57729L8.38058 9.5L4.45708 13.4219C4.38405 13.4955 4.32621 13.5827 4.28688 13.6786C4.24756 13.7745 4.2275 13.8772 4.22787 13.9809C4.22824 14.0845 4.24902 14.1871 4.28903 14.2827C4.32903 14.3783 4.38749 14.4651 4.46104 14.5382C4.53391 14.612 4.62067 14.6707 4.71631 14.7109C4.81195 14.7511 4.9146 14.772 5.01834 14.7723C5.12209 14.7727 5.22488 14.7525 5.3208 14.713C5.41673 14.6735 5.5039 14.6154 5.57729 14.5421L9.5 10.6194L13.4219 14.5421C13.4955 14.6152 13.5827 14.673 13.6786 14.7123C13.7745 14.7517 13.8772 14.7717 13.9809 14.7713C14.0845 14.771 14.1871 14.7502 14.2827 14.7102C14.3783 14.6702 14.4651 14.6117 14.5382 14.5382C14.612 14.4653 14.6707 14.3785 14.7109 14.2829C14.7511 14.1873 14.772 14.0846 14.7723 13.9809C14.7727 13.8771 14.7525 13.7743 14.713 13.6784C14.6735 13.5825 14.6154 13.4953 14.5421 13.4219L10.6194 9.5L14.5421 5.57808C14.6152 5.50453 14.673 5.41731 14.7123 5.32141C14.7517 5.2255 14.7717 5.12279 14.7713 5.01914C14.771 4.91548 14.7502 4.81292 14.7102 4.71729C14.6702 4.62167 14.6117 4.53487 14.5382 4.46183C14.4653 4.38799 14.3785 4.32929 14.2829 4.28911C14.1873 4.24892 14.0846 4.22804 13.9809 4.22767C13.8771 4.22731 13.7743 4.24745 13.6784 4.28696C13.5825 4.32647 13.4953 4.38455 13.4219 4.45787L9.5 8.38058Z" fill="#D81E06"/>
+</g>
+<defs>
+<clipPath id="clip0_5316_6053">
+<rect width="19" height="19" fill="white"/>
+</clipPath>
+</defs>
+</svg>

BIN
src/assets/img/approve_m/reject.png


BIN
src/assets/img/approve_m/return-icon.png


+ 5 - 0
src/assets/img/approve_m/return-icon.svg

@@ -0,0 +1,5 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="8" cy="8" r="8" fill="#999999"/>
+<path d="M6.21355 4.34426C6.26948 4.28793 6.34477 4.255 6.42411 4.25215C6.50345 4.24931 6.5809 4.27677 6.64072 4.32896C6.70055 4.38115 6.73827 4.45416 6.74622 4.53315C6.75418 4.61214 6.73177 4.6912 6.68355 4.75426L6.65563 4.78635L5.10855 6.33301L6.65563 7.87968C6.70906 7.9331 6.74133 8.00407 6.74646 8.07945C6.75159 8.15483 6.72924 8.22951 6.68355 8.28968L6.65563 8.32176C6.60221 8.3752 6.53124 8.40746 6.45586 8.4126C6.38048 8.41773 6.3058 8.39538 6.24563 8.34968L6.21355 8.32176L4.44605 6.55385C4.39262 6.50043 4.36035 6.42946 4.35521 6.35408C4.35008 6.2787 4.37243 6.20402 4.41813 6.14385L4.44605 6.11218L6.21355 4.34426Z" fill="white" stroke="white" stroke-width="0.3"/>
+<path d="M9.25023 6.02051C9.87811 6.01989 10.4811 6.26579 10.9296 6.70528C11.378 7.14477 11.636 7.74273 11.648 8.3705C11.66 8.99827 11.4251 9.60566 10.9938 10.062C10.5625 10.5183 9.96934 10.7871 9.3419 10.8105L9.25023 10.8122H4.6669C4.58772 10.8122 4.5115 10.7821 4.45365 10.728C4.3958 10.674 4.36062 10.6 4.35522 10.521C4.34982 10.442 4.37461 10.3639 4.42458 10.3025C4.47454 10.241 4.54596 10.2009 4.6244 10.1901L4.6669 10.1872H9.25023C9.71244 10.1872 10.1563 10.0064 10.4871 9.68358C10.8179 9.36073 11.0093 8.92135 11.0205 8.45927C11.0317 7.99719 10.8618 7.54905 10.5471 7.21054C10.2323 6.87204 9.79773 6.67001 9.33606 6.64759L9.25023 6.64551H4.6669C4.58772 6.64548 4.5115 6.61541 4.45365 6.56135C4.3958 6.5073 4.36062 6.4333 4.35522 6.35431C4.34982 6.27532 4.37461 6.19722 4.42458 6.1358C4.47454 6.07438 4.54596 6.03422 4.6244 6.02343L4.6669 6.02051H9.25023Z" fill="white" stroke="white" stroke-width="0.3"/>
+</svg>

+ 3 - 0
src/assets/img/approve_m/return-msg.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.6234 14.45C12.4885 14.45 12.3592 14.4018 12.2638 14.316C12.1685 14.2302 12.1149 14.1138 12.1149 13.9924C12.1149 13.871 12.1685 13.7546 12.2638 13.6688C12.3592 13.583 12.4885 13.5348 12.6234 13.5348C14.2122 13.5348 15.5047 12.3715 15.5047 10.9416C15.5047 9.51163 14.2122 8.34834 12.6234 8.34834H5.21424L8.66102 11.4501C8.75106 11.5369 8.80011 11.6518 8.79783 11.7705C8.79555 11.8893 8.74211 12.0026 8.64879 12.0865C8.55547 12.1705 8.42957 12.2185 8.29765 12.2205C8.16572 12.2225 8.0381 12.1783 7.94169 12.0972L3.26746 7.89071L7.94169 3.6839C8.03708 3.59805 8.16646 3.54982 8.30136 3.54982C8.43626 3.54982 8.56563 3.59805 8.66102 3.6839C8.7564 3.76975 8.80999 3.88618 8.80999 4.00759C8.80999 4.129 8.7564 4.24544 8.66102 4.33129L5.21424 7.43308H12.6234C14.7729 7.43308 16.5217 9.00702 16.5217 10.9416C16.5217 12.8761 14.7729 14.45 12.6234 14.45ZM10 0C4.48576 0 0 4.03749 0 9C0 13.9625 4.48576 18 10 18C15.5139 18 20 13.9625 20 9C20 4.03749 15.5139 0 10 0Z" fill="#797979"/>
+</svg>

BIN
src/assets/img/approve_m/return.png


BIN
src/assets/img/approve_m/select.png


BIN
src/assets/img/approve_m/start.png


BIN
src/assets/img/icons/add_blue_new.png


BIN
src/assets/img/icons/fullsreen.png


BIN
src/assets/img/icons/more.png


BIN
src/assets/img/ppt_m/text-icon.png


BIN
src/assets/img/smartReport/icon17.png


BIN
src/assets/img/smartReport/icon18.png


+ 130 - 42
src/components/lzTable.vue

@@ -65,19 +65,107 @@ export default {
 			defalut:'lz'
 		}
 	},
+	computed: {
+		headerArr(){
+			let arr=['QuotaName','LzCode','Frequency','UnitName','ModifyTime']
+
+			if(this.source==='gl'){
+				arr=['IndexName','IndexCode','FrequencyName','UnitName','UpdateTime']
+			}
+			if(this.source==='smm'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+			if(this.source==='coal'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+			if(this.source==='baiinfo'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+			if(this.source==='yyzx'){
+				arr=['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+			}
+
+			return arr
+		},
+		labelArr(){
+			let temMap=new Map([
+				['QuotaName', '指标名称'],
+				['LzCode', '指标ID'],
+				['Frequency', '频度'],
+				['UnitName', '单位'],
+				['ModifyTime', '更新时间'],
+			])
+
+			if(this.source==='gl'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['FrequencyName', '频度'],
+					['UnitName', '单位'],
+					['UpdateTime', '更新时间'],
+				])
+			}
+			if(this.source==='smm'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+			if(this.source==='coal'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+			if(this.source==='baiinfo'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+			if(this.source==='yyzx'){
+				temMap=new Map([
+					['IndexName', '指标名称'],
+					['IndexCode', '指标ID'],
+					['Frequency', '频度'],
+					['Unit', '单位'],
+					['ModifyTime', '更新时间'],
+				])
+			}
+
+			return temMap
+		},
+		dynamic_key(){
+			let key='InputValue'
+			if(['smm','baiinfo','coal','yyzx'].includes(this.source)){
+				key='Value'
+			}
+
+			return key
+		}
+	},
 	data() {
 		return {
-			dynamic_key: (this.source === 'smm'||this.source ==='baiinfo')? 'Value' 
-			:this.source==='coal'?'Value': 'InputValue',
-			headerArr: this.source === 'gl' 
-			? ['IndexName','IndexCode','FrequencyName','UnitName','UpdateTime']
-			: this.source === 'smm' 
-			? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
-			: this.source==='coal'
-			? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
-      : this.source==='baiinfo'
-      ? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
-			:['QuotaName','LzCode','Frequency','UnitName','ModifyTime'],
+			// dynamic_key: (this.source === 'smm'||this.source ==='baiinfo')? 'Value' 
+			// :this.source==='coal'?'Value': 'InputValue',
+	// 		headerArr: this.source === 'gl' 
+	// 		? ['IndexName','IndexCode','FrequencyName','UnitName','UpdateTime']
+	// 		: this.source === 'smm' 
+	// 		? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+	// 		: this.source==='coal'
+	// 		? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+    //   : this.source==='baiinfo'
+    //   ? ['IndexName','IndexCode','Frequency','Unit','ModifyTime']
+	// 		:['QuotaName','LzCode','Frequency','UnitName','ModifyTime'],
 			frequencyType:new Map([
 				[1,'日度'],
 				[2,'周度'],
@@ -86,37 +174,37 @@ export default {
 				[5,'年度'],
 				[99,'无固定频率'],
 			]),
-			labelArr: this.source === 'gl' ? new Map([
-				['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['FrequencyName', '频度'],
-				['UnitName', '单位'],
-				['UpdateTime', '更新时间'],
-			]): this.source === 'smm' ? new Map([
-				['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['Frequency', '频度'],
-				['Unit', '单位'],
-				['ModifyTime', '更新时间'],
-			]) :this.source === 'coal' ? new Map([
-				['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['Frequency', '频度'],
-				['Unit', '单位'],
-				['ModifyTime', '更新时间'],
-			]) :this.source==='baiinfo'?new Map([
-        ['IndexName', '指标名称'],
-				['IndexCode', '指标ID'],
-				['Frequency', '频度'],
-				['Unit', '单位'],
-				['ModifyTime', '更新时间'],
-      ]):new Map([
-				['QuotaName', '指标名称'],
-				['LzCode', '指标ID'],
-				['Frequency', '频度'],
-				['UnitName', '单位'],
-				['ModifyTime', '更新时间'],
-			]),
+			// labelArr: this.source === 'gl' ? new Map([
+			// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['FrequencyName', '频度'],
+			// 	['UnitName', '单位'],
+			// 	['UpdateTime', '更新时间'],
+			// ]): this.source === 'smm' ? new Map([
+			// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['Unit', '单位'],
+			// 	['ModifyTime', '更新时间'],
+			// ]) :this.source === 'coal' ? new Map([
+			// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['Unit', '单位'],
+			// 	['ModifyTime', '更新时间'],
+			// ]) :this.source==='baiinfo'?new Map([
+        	// 	['IndexName', '指标名称'],
+			// 	['IndexCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['Unit', '单位'],
+			// 	['ModifyTime', '更新时间'],
+      		// ]):new Map([
+			// 	['QuotaName', '指标名称'],
+			// 	['LzCode', '指标ID'],
+			// 	['Frequency', '频度'],
+			// 	['UnitName', '单位'],
+			// 	['ModifyTime', '更新时间'],
+			// ]),
 		};
 	},
 	methods: {

+ 146 - 0
src/components/notificationMsg.vue

@@ -0,0 +1,146 @@
+<template>
+    <!-- 消息通知 -->
+    <el-popover
+        placement="bottom"
+        width="443"
+        trigger="click" v-model="visible">
+        <div class="notifation-wrap">
+            <!-- 防止tabs在popover前渲染,会导致tab选中状态不正确 -->
+            <el-tabs v-model="activeName" v-if="visible">
+                <el-tab-pane :label="`研报审批(${UnreadTotal})`" name="first"></el-tab-pane>
+            </el-tabs>
+            <div class="massage-list" v-if="visible">
+                <div class="message-item" :class="{'IsRead':item.IsRead}" v-for="item in msgList" :key="item.Id" @click="readMsg(item)">
+                    <span class="icon">
+                        <img :src="require(`@/assets/img/approve_m/${approveState[item.ApproveState]||'process'}-msg.svg`)" alt="">
+                    </span>
+                    <div class="info">
+                        <div class="head">
+                            <span class="title">{{item.Content}}</span>
+                            <span class="time">{{item.CreateTime}}</span>
+                        </div>
+                        <div class="content">{{item.Remark||''}}&nbsp;</div>
+                    </div>
+                </div>
+                <tableNoData v-if="!msgList.length" text="暂无消息"></tableNoData>
+            </div>
+            <el-button type="text" class="close-btn" @click="visible=false">关闭</el-button>
+        </div>
+        <span slot="reference" @click="visible = !visible" class="msg-btn">
+            <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+                <path d="M16.7344 15.4904L15.2941 12.9564V7.84847C15.2941 5.89142 13.8231 3.77337 11.6786 2.99732C11.5741 1.96012 10.8811 1.20374 9.97287 1.20374C9.06595 1.20374 8.37036 1.96014 8.26717 2.99732C6.12392 3.77337 4.65292 5.8914 4.65292 7.84847V12.9564L3.14258 15.6146C3.04183 15.7903 3.04429 16.0054 3.14504 16.1799C3.24581 16.3544 3.43263 16.4601 3.63415 16.4601H7.22871C7.49293 17.7357 8.62233 18.6967 9.97289 18.6967C11.3247 18.6967 12.4541 17.7357 12.7171 16.4601H16.3116C16.3215 16.4601 16.3288 16.4601 16.3374 16.4601C16.6508 16.4601 16.904 16.2082 16.904 15.8948C16.904 15.7362 16.8401 15.5925 16.7344 15.4904ZM9.97287 2.33803C10.1769 2.33803 10.3415 2.49286 10.4472 2.7239C10.2899 2.70792 10.1375 2.67106 9.97287 2.67106C9.8082 2.67106 9.65583 2.70792 9.49851 2.7239C9.60422 2.49284 9.76889 2.33803 9.97287 2.33803ZM9.97287 17.5636C9.25396 17.5636 8.65303 17.1016 8.41951 16.4601H11.5262C11.2927 17.1015 10.693 17.5636 9.97287 17.5636ZM4.60745 15.3282L5.71102 13.3853C5.76015 13.3017 5.78719 13.2034 5.78719 13.1051V7.84847C5.78719 5.93934 7.57648 3.80413 9.97287 3.80413C12.3692 3.80413 14.161 5.93934 14.161 7.84847V13.1051C14.161 13.2034 14.1868 13.3017 14.2347 13.3853L15.3395 15.3282H4.60745Z" fill="currentColor"/>
+            </svg>
+            <div class="unread" v-if="UnreadTotal">{{UnreadTotal>99?'99+':UnreadTotal}}</div>
+        </span>
+    </el-popover>
+</template>
+
+<script>
+import {approveInterence} from '@/api/modules/approve.js';
+export default {
+    data() {
+        this.approveState=['','process','passed','reject','return',]
+        return {
+            activeName:'first',
+            visible:false,
+            msgList:[],
+            UnreadTotal:0
+        };
+    },
+    watch:{
+        visible(val){
+            if(val){
+                this.getMsgList()
+            }
+        }
+    },
+    methods:{
+        readMsg(msg){
+            const {ApproveState,ReportApproveId,Id} = msg
+            approveInterence.readApproveMsg({
+                MessageId:Id
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                this.getMsgList()
+            })
+            const type = ApproveState===1?'approve':ApproveState===4?'detail':'myself'
+            this.$router.push({
+                path:'/approveDetail',
+                query:{
+                    type,
+                    approveId:ReportApproveId
+                }
+            })
+        },
+        getMsgList(){
+            approveInterence.getApproveMsgList({
+                CurrentIndex:1,
+                PageSize:1000
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                this.msgList = res.Data.List||[]
+                this.UnreadTotal = res.Data.UnreadTotal||0
+            })
+        }
+    }
+};
+</script>
+
+<style lang="scss">
+.notifation-wrap{
+    position:relative;
+    .massage-list{
+        padding: 0 10px;
+        max-height: 320px;
+        overflow-y: auto;
+        .message-item{
+            cursor: pointer;
+            display: flex;
+            padding: 5px 0;
+            border-bottom: 1px solid #E4E7ED;
+            color:#333;
+            &.IsRead{
+                color:#999;
+            }
+            .icon{
+                width:40px;
+                display: flex;
+                align-items: flex-start;
+                justify-content: center;
+            }
+            .info{
+                flex:1;
+                .head{
+                    display: flex;
+                    justify-content: space-between;
+                }
+
+            }
+        }
+    }
+    .close-btn{
+        position:absolute;
+        top:0;
+        right:0;
+        box-sizing: border-box;
+    }
+}
+.msg-btn{
+    cursor: pointer;
+    display: inline-block;
+    width: 50px;
+    text-align: center;
+    position: relative;
+    .unread{
+        position: absolute;
+        width:23px;
+        height:14px;
+        color:#fff;
+        background-color:#AD352F;
+        border-radius: 40px;
+        top:-4px;
+        right:4px;
+        font-size: 12px;
+    }
+}
+</style>

+ 65 - 0
src/mixins/reportApproveConfig.js

@@ -0,0 +1,65 @@
+/*
+    研报及审批配置,涉及到:
+    智能研报
+    中文研报(除晨周报)
+    英文研报
+    审批流配置
+    审批管理
+    审批详情
+ */
+import {etaBaseConfigInterence} from '@/api/modules/etaBaseConfigApi.js';
+import {approveInterence} from '@/api/modules/approve.js';
+export default{
+    data(){
+        return{
+            ReportApproveType:'',
+            IsReportApprove:false,
+            pageLoading:false,
+            hasApproveFlow:false,
+            checkLoading:false,
+        }
+    },
+    computed:{
+        //是否开启审批流
+        isApprove(){
+            return this.IsReportApprove
+        },
+        //是否开启了ETA审批流
+        isETAApprove(){
+            return this.IsReportApprove&&this.ReportApproveType==='eta'
+        },
+        //是否开启了接口审批
+        isOtherApprove(){
+            return this.IsReportApprove&&this.ReportApproveType==='other'
+        },
+    },
+    methods:{
+        async getBaseConfig(){
+            //从基本配置中获取是否开启审批流的数据
+            this.pageLoading = true
+            const res = await etaBaseConfigInterence.getBaseConfig()
+            this.pageLoading = false
+            if(res.Ret!==200) return
+            const {IsReportApprove='',ReportApproveType=''} = res.Data
+            this.IsReportApprove = IsReportApprove==='true'?true:false,
+            this.ReportApproveType = ReportApproveType
+        },
+        //检查是否有审批流
+        checkClassifyNameArr(type=1,classify=[]){
+            this.checkLoading=true
+            let params = {
+                ReportType:type,
+                ClassifyFirstId:classify[classify.length-2]||0,
+                ClassifySecondId:classify[classify.length-1]||0,
+            }
+            approveInterence.checkClassifyApprove(params).then(res=>{
+                this.checkLoading=false
+                if(res.Ret!==200) return 
+                this.hasApproveFlow = res.Data||false
+            })
+        },
+    },
+    mounted(){
+        this.getBaseConfig()
+    }
+}

+ 27 - 0
src/routes/modules/approveRoutes.js

@@ -0,0 +1,27 @@
+const home = r => require.ensure([], () => r(require('@/views/Home.vue')), 'Home'); //主页
+export default [
+    {
+        path:'/',
+        component:home,
+        name:'审批管理',
+        hidden:false,
+        icon_path: require('@/assets/img/home/data_ic.png'),
+        children:[{
+            path:'approveList',
+            name:'审批列表',
+            component:()=>import('@/views/approve_manage/approveList.vue'),
+            hidden:false
+        },{
+            path:'approveDetail',
+            name:'审批详情',
+            component:()=>import('@/views/approve_manage/approveDetail.vue'),
+            hidden:false,
+            meta: {
+                pathFrom: "approveList",
+                pathName: "审批列表",
+                keepAlive: false,
+              },
+        }
+        ]
+    }
+]

+ 16 - 0
src/routes/modules/dataRoutes.js

@@ -201,6 +201,22 @@ export default [
           keepAlive: false,
         },
       },
+      {
+        path: "yyzx",
+        component: () => import("@/views/dataEntry_manage/thirdBase/YyzxData.vue"),
+        name: "涌益咨询",
+        hidden: false,
+        meta: {
+          keepAlive: false,
+        },
+      },
+      {
+        path: "ysTarget",
+        component: () =>
+          import("@/views/dataEntry_manage/thirdBase/ysTargetBase.vue"),
+        name: "有色原始数据库",
+        hidden: false,
+      },
     ],
   },
 ];

+ 12 - 0
src/routes/modules/oldRoutes.js

@@ -323,6 +323,18 @@ export default [
         name: "外部链接配置",
         hidden: true,
       },
+      {
+        path: "approveSetting",
+        component: () => import("@/views/approve_manage/approveSetting.vue"),
+        name: "审批流配置",
+        hidden: true,
+      },
+      {
+        path: "approveEdit",
+        component: () => import("@/views/approve_manage/approveEdit.vue"),
+        name: "编辑审批流",
+        hidden:true,
+      },
       {
         path: "chartThemeIndex",
         component: () => import("@/views/system_manage/chartTheme/index.vue"),

+ 26 - 4
src/utils/buttonConfig.js

@@ -42,7 +42,7 @@ export const reportManageBtn={
     reportManage_reportView:'smartReportManage:reportView',//研报预览:即是否能点击研报名称跳转预览页面
     reportManage_reportView_wechartShare:'smartReportManage:reportView:wechartShare',//研报预览页面-微信分享
     reportManage_reportView_copyWechat:'smartReportManage:reportView:copyWechat',//研报预览页面-复制链接
-    reportManage_reportView_exportImg:'smartReportManage:reportView:exportImg',//研报预览页面-导出图片
+    reportManage_exportImg:'smartReportManage:exportImg',//导出图片
     reportManage_audioDownload:'smartReportManage:audioDownload',//音频下载
     reportManage_audioUpload:'smartReportManage:audioUpload',//音频上传
     reportManage_reportDel:'smartReportManage:reportDel',//删除研报
@@ -233,12 +233,13 @@ export const dataSourcePermission = {
     eiaData_export:'eiaData:export',
     /*--------国家统计局---- */
     gjtjjData_export:'gjtjjData:export',
-
     /*--------数据报表管理---- */
     /*--------数据源终端管理---- */
     dataSource_account_add:'dataSourceAccount:add',//新增终端账号
     dataSource_account_edit:'dataSourceAccount:edit',//编辑
     dataSource_account_enable:'dataSourceAccount:enable',//禁用/启用
+    /*--------涌益咨询---- */
+    yyzxData_export:'yyzx:exportData'
 }
 
 /*
@@ -630,7 +631,7 @@ export const baseConfigPermission = {
     etaBaseConfig_ppt:'etaBaseConfig:ppt',
     etaBaseConfig_watermark:'etaBaseConfig:watermark',
     etaBaseConfig_watermark_ybChart:'etaBaseConfig:watermark:ybChart',//如果没权限,表单不显示也不校验
-
+    etaBaseConfig_approve:'etaBaseConfig:approve',//研报审批
 }
 
 /*-----------外部链接配置-------- */
@@ -640,6 +641,25 @@ export const outlinkConfigPermission = {
     outlinkListConfig_edit:'outlinkListConfig:edit',//编辑
     outlinkListConfig_del:'outlinkListConfig:del',//删除
 }
+/*----------审批流配置----*/
+export const approveFlowPermission = {
+    reportApprove_save:'reportApprove:save',//保存
+    reportApprove_remove:'reportApprove:remove',//删除
+    reportApprove_edit:'reportApprove:edit',//编辑审批流(按钮)
+    reportApprove_add:'reportApprove:add',//添加审批流
+}
+/*
+ * --------------------------------------------------------------------------审批管理------------------------------------------------
+*/
+export const approvePermission = {
+    reportApprove_approve:'reportApprove:approve',//审批(列表中的按钮)
+    reportApprove_reject:'reportApprove:reject',//驳回(列表中的按钮)
+    reportApprove_agree:'reportApprove:agree',//通过(审批详情-通过按钮)
+    reportApprove_detail:'reportApprove:detail',//详情(列表中的按钮)
+    reportApprove_rejectreason:'reportApprove:rejectreason',//驳回理由(列表中的按钮)
+    reportApprove_return:'reportApprove:return',//返回(审批详情-返回按钮)
+    reportApprove_repeal:'reportApprove:repeal',//撤回(列表中的按钮,审批详情-撤回按钮)
+}
 
 /* 图表主题配置 */
 export const chartThemePermission = {
@@ -651,7 +671,7 @@ export const chartThemePermission = {
 
 //创建了新的ManageBtn记得添加到这里
 const btnMap  = {
-    reportManageBtn,enReportManageBtn,
+    reportManageBtn,enReportManageBtn,smartReportManageBtn,
     classifyBtn,enClassifyBtn,authorManage,
     enChartPermission,cloudDisk,
     pptPermission,enPPTPermission,
@@ -662,6 +682,8 @@ const btnMap  = {
     statisticPermission,stockPlantPermission,
     productPricePermission,sysDepartPermission,
     operateAuthPermission,baseConfigPermission,
+    outlinkConfigPermission,approveFlowPermission,
+    approvePermission,
     outlinkConfigPermission,
     chartThemePermission
 }

+ 1 - 0
src/utils/parseData.js

@@ -2,6 +2,7 @@ import ParserData from '@/api/crypto.js';
 /* 解密数据处理 */
 export function parseData(response) {
   const headKeyStr=response.headers.dk
+  sessionStorage.setItem('dk',headKeyStr);
   const desKey=ParserData.Des3Decrypt(headKeyStr,'JMCqSoUrTAmyNNIRb0TtlrPk')
   let result = process.env.NODE_ENV == "production"
   ? JSON.parse(ParserData.Des3Decrypt(response.data,desKey))

+ 8 - 1
src/utils/svgToblob.js

@@ -50,7 +50,14 @@ export function copyFit(value) {
     navigator.clipboard.writeText(value)
   }else {
     setTimeout(() => {
-      bus.$copyText(value)
+      const input = document.createElement('input')
+      input.setAttribute('readonly','readonly')
+      input.setAttribute('id',new Date().getTime())
+      input.value = value
+      document.body.appendChild(input)
+      input.select();
+      document.execCommand('copy');
+      document.body.removeChild(input);
     })
   }
 

+ 7 - 2
src/views/Home.vue

@@ -248,7 +248,8 @@
                 style="width: 84px;height: 24; cursor: pointer;" />
                 </div>
               </div>
-
+               <!-- 消息通知 -->
+              <notification-msg ref="notification"/>
               <el-dropdown trigger="click" style="width:130px;">
                 <span class="el-dropdown-link userinfo-inner">
                   <img src="~@/assets/img/home/avatar.png"/>
@@ -316,13 +317,15 @@ import questionnaireDia from "../components/questionnaireDia.vue";
 import EventBus from "@/api/bus.js";
 
 import {recordActiveLoginFun,loginEndCalc} from "@/utils/TimeOnPage.js"
+import NotificationMsg from "../components/notificationMsg.vue";
 
 export default {
   components: {
     PwdDlg,
     questionMsgDia,
     questionnaireDia,
-  },
+    NotificationMsg
+},
   inject: ["reload"],
   filters: {
     formatDate(e) {
@@ -463,6 +466,7 @@ export default {
       //链接系统
       linkSystems: this.$setting.linkSystems,
       bus_code:"",
+
     };
   },
   created() {
@@ -488,6 +492,7 @@ export default {
       this.getMenuList();
     }
     this.getPublicSettings();
+    this.$refs.notification&&this.$refs.notification.getMsgList()
   },
   methods: {
     handleClickSubMenuItem(item,e){

+ 12 - 12
src/views/Login.vue

@@ -247,15 +247,15 @@ export default {
                                 case "researcher":
                                     path = "/reportlist";
                                     break;
-                                case "compliance": //合规
-                                    path = "/contractapprovallist";
-                                    break;
+                                // case "compliance": //合规
+                                //     path = "/contractapprovallist";
+                                //     break;
                                 case "special_researcher": //特邀研究员
                                     path = "/dataList";
                                     break;
-                                case "special_ficc_seller":
-                                    path = "/meetingCalendar";
-                                    break;
+                                // case "special_ficc_seller":
+                                //     path = "/meetingCalendar";
+                                //     break;
                                 default:
                                     path = await this.getOtherRolePath("myCalendar");
                             }
@@ -474,15 +474,15 @@ export default {
                 case "researcher":
                     path = "/reportlist";
                     break;
-                case "compliance": //合规
-                    path = "/contractapprovallist";
-                    break;
+                // case "compliance": //合规
+                //     path = "/contractapprovallist";
+                //     break;
                 case "special_researcher": //特邀研究员
                     path = "/dataList";
                     break;
-                case "special_ficc_seller":
-                    path = "/meetingCalendar";
-                    break;
+                // case "special_ficc_seller":
+                //     path = "/meetingCalendar";
+                //     break;
                 default:
                     path = await this.getOtherRolePath("myCalendar");
             }

+ 386 - 0
src/views/approve_manage/approveDetail.vue

@@ -0,0 +1,386 @@
+<template>
+    <!-- 审批详情:查看、进行审批操作 -->
+    <div class="approve-detail-wrap" v-if="isETAApprove">
+        <div class="approve-detail">
+            <div class="approve-info">
+                <span>研报名称:{{reportInfo.title||''}}</span>
+                <span>研报分类:{{reportInfo.classify||''}}</span>
+                <span style="min-width: 100px;">发起人:{{reportInfo.approver||''}}</span>
+            </div>
+            <div class="approve-content" 
+                v-loading="isLoading"
+                element-loading-text="研报加载中...">
+                <component v-if="reportInfo.reportId&&!isError"
+                    :is="reportInfo.componentName"
+                    :reportId="reportInfo.reportId"
+                    :isPreview="true"
+                    :isEn="reportInfo.type===2"
+                    @reportStartLoading="isLoading=true"
+                    @reportEndLoading="isLoading=false"
+                    @reportError="isError=true"
+                ></component>
+                <tableNoData v-if="isError" text="无法获取到报告内容,该报告可能已被删除!"></tableNoData>
+            </div>
+        </div>
+        <div class="approve-tool">
+            <div class="tool-btn">
+                <!-- 根据审批的状态决定显示 -->
+                <el-button type="danger" @click="changeApprove('reject')"
+                    v-if="formType==='approve'&&approveInfo.isCurrentApprover&&permissionBtn.isShowBtn('approvePermission','reportApprove_reject')">驳回</el-button>
+                <el-button type="primary" @click="changeApprove('pass')"
+                    v-if="formType==='approve'&&approveInfo.isCurrentApprover&&permissionBtn.isShowBtn('approvePermission','reportApprove_agree')">同意</el-button>
+                <el-button type="primary" @click="changeApprove('return')"
+                    v-if="formType==='myself'&&approveInfo.state!==4&&permissionBtn.isShowBtn('approvePermission','reportApprove_repeal')">撤销</el-button>
+                <el-button type="primary" plain @click="$router.replace(`/approveList?formType=${formType}`)">返回</el-button>
+            </div>
+            <div class="approve-timeline-wrap">
+                <p>审批流程</p>
+                <div class="timeline">
+                    <TimeLine 
+                        :TimeLineData="TimeLineData"
+                    />
+                </div>
+                <div class="approve-status" >
+                    <img :src="require(`@/assets/img/approve_m/${approveInfo.stateText||'pending'}.png`)" alt="">
+                </div>
+            </div>
+        </div>
+        <RejectDialog 
+            :isDetailDialogShow="isDetailDialogShow"
+            :isEdit="true"
+            @close="isDetailDialogShow=false;"
+            @edit="rejectApprove"
+        />
+    </div>
+    <div class="nodata-wrap approve-page-wrap" v-else>
+        <tableNoData :text="pageLoading?'':'系统暂未开通审批流程,请开启审批流程后再进行操作!'"></tableNoData>
+    </div>
+</template>
+
+<script>
+import ReportDetail from '@/views/smartReport/reportDetail.vue';
+import Reportdtl from '@/views/report_manage/reportdtl.vue'
+import TimeLine from './components/timeLine.vue';
+import RejectDialog from './components/rejectDialog.vue';
+import {approveInterence} from '@/api/modules/approve.js';
+import reportApproveConfig from "@/mixins/reportApproveConfig.js"
+
+const ApproveType = ['','依次审批','会签','或签']
+const ApproveState = ['','待审批','已同意','已驳回']
+export default {
+    mixins:[reportApproveConfig],
+    components: { RejectDialog },
+    data() {
+        return {
+            isError:false,
+            isLoading:false,
+            isDetailDialogShow:false,
+            TimeLineData:[],
+            formType:'detail',//进入审批详情的方式:approve(待处理)/detail(已处理)/myself(我发起的)
+            reportInfo:{ //报告相关信息
+                reportId:0,//研报or智能研报的报告id
+                title:'',
+                classify:'',
+                approver:'',
+                componentName:'',
+                type:0,//1中文研报 2英文研报 3智能研报
+            },
+            approveInfo:{//审批相关的信息
+                state:1,//当前审批的状态:1-待审批;2-已同意;3-已驳回;4-已撤回
+                stateText:'pending',//审批状态对应图片文字:待审批(pending),已同意(passed),已驳回(reject),已撤回(return)
+                isCurrentApprover:false,//审批进行中的节点审批人是否是当前用户,控制通过&驳回按钮显示
+            },
+        };
+    },
+    computed:{
+        UserId(){
+            return Number(localStorage.getItem("AdminId"));
+        }
+    },
+    methods: {
+        getApproveDetail(){
+            const {type,approveId} = this.$route.query
+            if(!approveId) return 
+            approveInterence.getApproveDetail({
+                ReportApproveId:Number(approveId)
+            }).then(res=>{
+                if(res.Ret!==200) return
+                //格式化时间线数据 
+                this.formatTimeLineData(res.Data)
+                const {Report={},Approve={}} = res.Data||{}
+                this.reportInfo = {
+                    reportId:Report.ReportId||0,
+                    title:Report.ReportTitle||'',
+                    classify:Report.ReportClassify||'',
+                    approver:Approve.ApplyUserName||'',
+                    componentName:Report.ReportType===3?'ReportDetail':'Reportdtl',
+                    type:Report.ReportType
+                }
+                this.formType = type||'detail'
+                this.approveInfo.state=Approve.State
+                this.approveInfo.stateText=['','pending','passed','reject','return'][Approve.State]
+            })
+        },
+        //转换接口数据
+        formatTimeLineData({Approve={},ApproveFlowNodes=[]}){
+            //发起人节点
+            const startNode = {
+                nodeType:1,
+                nodeText:'发起人:',
+                nodeStatus:'passed',
+                approveList:[{
+                    approverName:Approve.ApplyUserName||'',
+                    approveTime:Approve.CreateTime||''
+                }]
+            }
+            //排序过的审批节点,遍历
+            let currentIndex = 0
+            let Nodes = ApproveFlowNodes.map((i,index)=>{
+                //找到当前节点的index
+                if(i.ReportApproveNodeId===Approve.CurrNodeId){
+                    currentIndex = index
+                }
+                //审批节点只有一个人时不显示是什么审批流
+                const strApproveType = `(${ApproveType[i.ApproveType]})`
+                const node = {
+                    nodeType:2,
+                    nodeText:'审批人:'+i.Users.length+'人'+(i.Users.length>1?strApproveType:''),
+                    nodeStatus:i.ReportApproveNodeId===Approve.CurrNodeId?'process':'future',
+                    approveType:ApproveType[i.ApproveType]
+                }
+                const approveList = i.Users.map(u=>{
+                    const {State='',ApproveTime='',ApproveRemark=''}=u.ApproveRecord||{}
+                    return {
+                        approverName:u.UserName,
+                        approveStatus:ApproveState[State]||'',
+                        approveTime:ApproveTime,
+                        approveReason:ApproveRemark
+                    }
+                })
+                //检测当前节点的User与当前用户是否匹配 且审批需要是待审批状态
+                if(i.ReportApproveNodeId===Approve.CurrNodeId&&Approve.State===1){
+                    //当前节点的User中,有没有当前用户
+                    const hasUser = i.Users.findIndex(u=>u.UserId===this.UserId)
+                    if(hasUser!==-1){
+                        //只有依次审批需要看顺序
+                        if(i.ApproveType===1){
+                            this.approveInfo.isCurrentApprover = i.Users[hasUser].ApproveRecord?true:false
+                        }else{
+                            this.approveInfo.isCurrentApprover = true
+                        }
+                    }else{
+                        this.approveInfo.isCurrentApprover = false
+                    }
+                }
+                node.approveList = approveList
+                return node
+            })
+            //将当前节点之前的置为通过节点,当前节点之后的置为未进行节点
+            Nodes.forEach((n,index)=>{
+                if(index<currentIndex){
+                    n.nodeStatus = 'passed'
+                }
+                if(index>currentIndex){
+                    n.nodeStatus = 'future'
+                }
+            })
+
+            if(!Nodes.length){
+                this.TimeLineData = [startNode,...Nodes]
+                return
+            }
+
+            //如果当前审批是已驳回,移除当前节点后面的节点
+            if(Approve.State===3){
+                Nodes = Nodes.filter((_,index)=>index<=currentIndex)
+                Nodes[currentIndex].nodeStatus = 'reject'
+            }
+
+            //如果当前审批已撤销,移除当前节点后面的节点,在最后加上撤销节点
+            if(Approve.State===4){
+                Nodes = Nodes.filter((_,index)=>index<=currentIndex)
+                //检查最后一个节点是否有人审批
+                let hasApprove = false
+                let stateNum = 0
+                Nodes[currentIndex].approveList.forEach(u=>{
+                    if(u.approveStatus&&u.approveStatus!=='待审批'){
+                        hasApprove = true
+                    }
+                    //依次审批和会签,计算已同意的数量
+                    if(u.approveStatus==='已同意'){
+                        stateNum++
+                    }
+                    if(u.approveStatus==='已驳回'){
+                        Nodes[currentIndex].nodeStatus = 'reject'
+                    }
+                })
+                //若审批撤销,则检查当前节点是否是通过节点
+                if(Nodes[currentIndex].approveType!=='或签'&&stateNum===Nodes[currentIndex].approveList.length){
+                    Nodes[currentIndex].nodeStatus = 'passed'
+                }
+                if(Nodes[currentIndex].approveType==='或签'&&stateNum>0){
+                    Nodes[currentIndex].nodeStatus = 'passed'
+                }
+                //若没有人审批,则把这个节点删除
+                !hasApprove&&(Nodes.pop())
+                //在最后加上撤销节点
+                Nodes.push({
+                    nodeType:3,
+                    nodeText:'发起人:',
+                    nodeStatus:'return',
+                    approveList:[{
+                        approverName:(Approve.ApplyUserName||'')+'(已撤销)',
+                        approveTime:Approve.ModifyTime||''
+                    }],
+                })
+            }
+
+            //如果当前审批已同意,则最后一个节点状态改为已同意
+            if(Approve.State===2){
+                Nodes[currentIndex].nodeStatus = 'passed'
+            }
+
+            //再次遍历Nodes
+            //1.去除已同意的或签节点,没有审批的审批人状态
+            //2.去除已驳回的节点,没有参与审批的审批人的状态
+            Nodes.forEach(n=>{
+                if(n.nodeStatus==='passed'||n.nodeStatus==='reject'){
+                    n.approveList.forEach(a=>{
+                        if(a.approveStatus==='待审批'){
+                            a.approveStatus = ''
+                        }
+                    })
+                }
+            })
+
+            this.TimeLineData = [startNode,...Nodes]
+        },
+        async changeApprove(type){
+            const {approveId} = this.$route.query
+            if(!approveId) return
+            if(type==='reject'){
+                this.isDetailDialogShow = true
+                return 
+            }
+            let res = null
+            if(type==='return'){
+                res = await approveInterence.cancelApprove({
+                    ReportApproveId:Number(approveId)
+                })
+            }
+            if(type==='pass'){
+                res = await approveInterence.passApprove({
+                    ReportApproveId:Number(approveId)
+                })
+            }
+            if(res.Ret!==200) return 
+            this.$message.success(`${type==='return'?'撤销':'通过'}成功`)
+            type!=='reject'&&(this.$router.replace("/approveList"))
+        },
+        rejectApprove(reason){
+            this.isDetailDialogShow = false
+            approveInterence.rejectApprove({
+                ReportApproveId:Number(this.$route.query.approveId),
+                ApproveRemark:reason||''
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                this.$message.success("驳回成功")
+                this.$router.replace("/approveList")
+            })
+        }
+    },
+    mounted(){
+        this.getApproveDetail()
+    },
+    components: { TimeLine, ReportDetail, Reportdtl, RejectDialog }
+};
+</script>
+
+<style lang="scss">
+.approve-detail-wrap{
+    .approve-content{
+        .smart-report-detail{
+            .main-box{
+                width: auto;
+                box-sizing: border-box;
+                margin:0;
+                padding:10px;
+                border:none !important;
+                border-radius: 4px;
+            }
+        }
+        #reportdtl{
+            margin:0 auto;
+            padding:10px;
+            border:none !important;
+            border-radius: 4px;
+        }
+    }
+}
+</style>
+<style scoped lang="scss">
+@import url('./css/pageStyle.scss');
+.approve-detail-wrap{
+    height: calc(100vh - 120px);
+    display: flex;
+    .approve-detail,.approve-tool{
+        height: 100%;
+        box-sizing: border-box;
+    }
+    .approve-tool{
+        width:400px;
+        min-width: 360px;
+        background-color: #fff;
+        display: flex;
+        flex-direction: column;
+        overflow: hidden;
+        .tool-btn{
+            padding:20px;
+            text-align: right;
+        }
+        .approve-timeline-wrap{
+            position: relative;
+            margin-left:20px;
+            flex:1;
+            overflow-y: auto;
+            .timeline{
+                padding:20px;
+                padding-left: 0;
+            }
+            .approve-status{
+                position:absolute;
+                width:200px;
+                height:200px;
+                right:0;
+                bottom:0;
+                overflow: hidden;
+                pointer-events: none;
+                img{
+                    width:100%;
+                    height:100%;
+                }
+            }
+        }
+    }
+    .approve-detail{
+        flex: 1;
+        margin-right:20px;
+        background-color: #fff;
+        display: flex;
+        flex-direction: column;
+        .approve-info{
+            display: flex;
+            gap:60px;
+            padding:15px 20px;
+            border-bottom: 1px solid #C8CDD9;
+        }
+        .approve-content{
+            flex: 1;
+            min-width: 840px;
+            padding:20px;
+            overflow-y: auto;
+            position:relative;
+        }
+    }
+}
+</style>

+ 194 - 0
src/views/approve_manage/approveEdit.vue

@@ -0,0 +1,194 @@
+<template>
+    <!-- 添加编辑审批流 -->
+    <div class="approve-edit-wrap approve-page-wrap" v-if="isETAApprove">
+        <div class="head-box">
+            <el-form :inline="true" :model="approveForm" ref="approve-form" :rules="formRules"
+                label-width="100px" label-position="left">
+                <el-form-item label="审批流名称" prop="name">
+                    <el-input v-model="approveForm.name" :disabled="this.$route.query.flowId" placeholder="请输入审批流名称"></el-input>
+                </el-form-item>
+                <el-form-item label="关联报告" prop="classify">
+                    <el-cascader v-model="approveForm.classify"
+                        placeholder="请选择关联报告" clearable
+                        :disabled="this.$route.query.flowId"
+                        :options="classifyTree"
+                        :props="{value:'ClassifyId',label:'ClassifyName',children:'Children'}"
+                        style="margin-right: auto;margin-left: 15px;"></el-cascader>
+                </el-form-item>
+            </el-form>
+            <div class="form-btn">
+                <el-button type="primary" plain @click="$router.back()">取消</el-button>
+                <el-button v-permission="permissionBtn.approveFlowPermission.reportApprove_save"
+                    type="primary" @click="checkFlow">保存</el-button>
+            </div>
+        </div>
+        <div class="form-item" style="color:#606266;width:100px;">
+            <span style="color:#F56C6C;margin-right: 4px;">*</span>流程配置
+        </div>
+        <!-- 审批流画布 -->
+        <div class="flow-editor-wrap">
+            <FlowEdiotr
+                ref='floweditor'
+                :flowNodes="approveForm.flowNodes"
+            />
+        </div>
+    </div>
+    <div class="approve-page-wrap" v-else>
+        <tableNoData :text="pageLoading?'':'系统暂未开通审批流程,请开启审批流程后再进行操作!'"></tableNoData>
+    </div>
+</template>
+
+<script>
+import FlowEdiotr from './components/flowEdiotr';
+import {approveInterence} from '@/api/modules/approve.js';
+import approveMixins from './mixins/approveMixins';
+import reportApproveConfig from "@/mixins/reportApproveConfig.js"
+const findParentNode = (arr, id)=>{
+      // 遍历取父级code push数组
+      for (let i of arr) {
+        if (i.ClassifyId === id) {
+          return [i.ClassifyId];
+        }
+        if (i.Children) {
+          let node = findParentNode(i.Children, id);
+          if (node) {
+            return node.concat(i.ClassifyId);
+          }
+        }
+      }
+    }
+export default {
+    mixins:[approveMixins,reportApproveConfig],
+    data() {
+        return {
+            approveForm:{
+                name:'',
+                classify:'',
+                /* flowNodes:null */
+            },
+            formRules:{
+                name:[{ required: true, message: '请输入审批流名称名称', trigger: 'blur' },
+                      { max: 20, message: '长度在20个字符内', trigger: 'change' }],
+                classify:[{ required: true, message: '请选择关联报告', trigger: 'blur' },]
+            },
+
+        };
+    },
+    methods: {
+        async checkFlow(){
+            //检查name,region是否为空
+            await this.$refs["approve-form"].validate()
+            //检查审批流内容:每个审批节点是否都选择了审批人
+            const data = this.$refs.floweditor.flowData
+            if(data.length<3){
+                this.$message.warning("请添加审批节点")
+                return
+            }
+            for(let item of data){
+                if(item.nodeType===2&&!item.approvers.length){
+                    this.$message.warning("有节点未选择审批人,请检查")
+                    return
+                }
+            }
+            let Nodes = []
+            //不需要data的第一项和最后一项,其余转换为接口所需格式
+            for(let i=1;i<data.length-1;i++){
+                const Users = data[i].approvers.map((item,index)=>{
+                    return {
+                        UserType:'user',//目前只有审批节点,都是user
+                        UserId:item.ItemId,
+                        UserName:item.ItemName,
+                        Sort:data[i].ApproveType===1?index+1:0
+                    }
+                })
+                Nodes.push({
+                    ApproveType:data[i].ApproveType,
+                    Users
+                })
+            }
+            this.modifyFlow(Nodes)
+        },
+        async modifyFlow(Nodes){
+            const {name,classify} = this.approveForm
+            const params = {
+                FlowName:name,
+                ReportType:classify[0],
+                ClassifyFirstId:classify[classify.length-2]||0,
+                ClassifySecondId:classify[classify.length-1]||0,
+                Nodes
+            }
+            let res
+            const id = this.$route.query.flowId||0
+            if(id){
+                res = await approveInterence.editApproveFlow({
+                    ...params,
+                    ReportApproveFlowId:Number(id)
+                })
+            }else{
+                res = await approveInterence.addNewApproveFlow(params)
+            }
+            if(res.Ret!==200) return
+            this.$message.success(`${id?'编辑':'新增'}成功`)
+            this.$router.push('/approveSetting')
+        },
+        getFlowDetail(){
+            const id = this.$route.query.flowId||0
+            if(id){
+                approveInterence.getApproveFlowDetail({
+                    ReportApproveFlowId:Number(id)
+                }).then(res=>{
+                    if(res.Ret!==200) return 
+                    const {FlowName,ReportType,ClassifySecondId,Nodes} = res.Data||{}
+                    this.approveForm.name = FlowName||''
+                    //递归获取所有父级id
+                    const classify = this.classifyTree.find(i=>i.ClassifyId===ReportType)||{}
+                    const tempArr = findParentNode(classify.Children||[],ClassifySecondId)
+                    tempArr.push(ReportType)
+                    this.approveForm.classify = tempArr.reverse()
+                    this.approveForm.flowNodes = Nodes||[]
+                })
+            }
+        }
+    },
+    async mounted(){
+        this.getClassifyTree()
+    },
+    components: { FlowEdiotr }
+};
+</script>
+<style lang="scss">
+.approve-edit-wrap{
+    .el-form-item__content{
+        flex: 1;
+    }
+}
+</style>
+<style scoped lang="scss">
+@import url('./css/pageStyle.scss');
+.approve-edit-wrap{
+    display: flex;
+    flex-direction: column;
+    height: calc(100vh - 120px);
+    .head-box{
+        display: flex;
+        justify-content: space-between;
+        .el-form{
+            flex:1;
+            .el-input,.el-cascader{
+                min-width: 200px;
+                max-width: 420px;
+                width:100%;
+            }
+            .el-form-item{
+                width:45%;
+                display: inline-flex;
+            }
+
+        }
+    }
+    .flow-editor-wrap{
+        padding-left: 100px;
+        overflow: auto;
+    }
+}
+</style>

+ 311 - 0
src/views/approve_manage/approveList.vue

@@ -0,0 +1,311 @@
+<template>
+    <!-- 审批管理列表页 -->
+    <div class="approve-list-wrap approve-page-wrap" v-if="isETAApprove">
+        <div class="head-tab">
+            <el-tabs v-model="activeTab" @tab-click="handleClick">
+                <el-tab-pane label="待处理" name="pending"></el-tab-pane>
+                <el-tab-pane label="已处理" name="processed"></el-tab-pane>
+                <el-tab-pane label="我发起的" name="originate"></el-tab-pane>
+            </el-tabs>
+        </div>
+        <div class="approve-list">
+            <div class="select-box">
+                <el-cascader v-model="classify"
+                    placeholder="请选择关联报告" clearable
+                    :options="classifyTree"
+                    :props="{value:'ClassifyId',label:'ClassifyName',children:'Children'}"
+                    @change="handleCurrentChange(1)">
+                </el-cascader>
+                <div class="select-time-box">
+                    <el-select v-show="activeTab!=='pending'" v-model="timeType" 
+                        @change="handleCurrentChange(1)"
+                        placeholder="时间类型" style="max-width:120px;" class="custom-select">
+                        <el-option label="提交时间" :value="1" />
+                        <el-option label="处理时间" :value="2" v-if="activeTab==='processed'"/>
+                        <el-option label="审批时间" :value="3" v-if="activeTab==='originate'"/>
+                    </el-select>
+                    <div class="line" v-show="activeTab!=='pending'"></div>
+                    <el-date-picker v-model="timeDate"
+                        @change="handleCurrentChange(1)"
+                        type="daterange"
+                        value-format="yyyy-MM-dd"
+                        range-separator="至"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期">
+                    </el-date-picker>
+                </div>
+                
+                <el-select placeholder="请选择处理状态" v-model="ApproveState" clearable v-show="activeTab!=='pending'"
+                    @change="handleCurrentChange(1)">
+                    <template v-if="activeTab==='processed'">
+                        <el-option label="已同意" :value="2" />
+                        <el-option label="已驳回" :value="3" />
+                    </template>
+                    <template v-if="activeTab==='originate'">
+                        <el-option label="待审批" :value="1" />
+                        <el-option label="已通过" :value="2" />
+                        <el-option label="已驳回" :value="3" />
+                        <el-option label="已撤销" :value="4" />
+                    </template>
+                    
+                </el-select>
+
+                <el-input v-model="keyword" prefix-icon="el-icon-search" clearable @input="handleCurrentChange(1)"
+                    placeholder="请输入报告标题" style="width:260px;margin-left: auto;"></el-input>
+            </div>
+            <div class="list-box">
+                <el-table 
+                    ref="reftable"
+                    v-loading="tableLoading"
+                    :data="tableData" 
+                    @sort-change="sortChange" border>
+                    <el-table-column
+                        v-for="item in tableColumns"
+                        :key="item.key"
+                        :label="item.label"
+                        :prop="item.key"
+                        :sortable="item.sortable"
+                        align="center"
+                    >
+                        <template slot-scope="{row}">
+                            <span v-if="item.key==='State'">
+                                {{['','待审批','已通过','已驳回','已撤销'][row.State]}}
+                            </span>
+                            <span v-else-if="item.key==='RecordState'">
+                                {{['','待审批','已同意','已驳回'][row.RecordState]}}
+                            </span>
+                            <span v-else>{{row[item.key]}}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="操作" align="center">
+                        <template slot-scope="{row}">
+                            <template v-if="activeTab==='pending'">
+                                <el-button type="text" style="padding:0;" @click="toApproveDetail(row,'approve')"
+                                    v-if="permissionBtn.isShowBtn('approvePermission','reportApprove_approve')">审批</el-button>
+                            </template>
+                            <template v-if="activeTab==='processed'">
+                                <el-button type="text" style="padding:0;" @click="toApproveDetail(row,'detail')">详情</el-button>
+                                <el-button type="text" style="padding:0" @click="handleShowDetail(row)" 
+                                    v-if="row.State===3&&permissionBtn.isShowBtn('approvePermission','reportApprove_rejectreason')">驳回理由</el-button>
+                            </template>
+                            <template v-if="activeTab==='originate'">
+                                <el-button type="text" style="padding:0;" @click="toApproveDetail(row,'myself')">详情</el-button>
+                                <el-button type="text" style="padding:0;" @click="cancelApprove(row)" 
+                                    v-if="row.State!==4&&permissionBtn.isShowBtn('approvePermission','reportApprove_repeal')">撤销</el-button>
+                                <el-button type="text" style="padding:0" @click="handleShowDetail(row)" 
+                                    v-if="row.State===3&&permissionBtn.isShowBtn('approvePermission','reportApprove_rejectreason')">驳回理由</el-button>
+                            </template>
+                        </template>
+                        
+                    </el-table-column>
+                </el-table>
+            <div style="text-align:right;margin-top:20px">
+                <el-pagination 
+                        layout="total,prev,pager,next,jumper" 
+                        background 
+                        :current-page="page"
+                        @current-change="handleCurrentChange"
+                        :page-size="pageSize"
+                        :total="total"
+                        style="display: inline-block"
+                    />
+            </div>
+            </div>
+        </div>
+        <RejectDialog 
+            :isDetailDialogShow="isDetailDialogShow"
+            :isEdit="false"
+            :data="currentData"
+            @close="isDetailDialogShow=false;currentData={};"
+        />
+    </div>
+    <div class="approve-page-wrap" v-else>
+        <tableNoData :text="pageLoading?'':'系统暂未开通审批流程,请开启审批流程后再进行操作!'"></tableNoData>
+    </div>
+</template>
+
+<script>
+import RejectDialog from './components/rejectDialog.vue';
+import approveMixins from './mixins/approveMixins';
+import {approveInterence} from '@/api/modules/approve.js';
+import reportApproveConfig from "@/mixins/reportApproveConfig.js"
+import {
+        approve_pending_columns,
+        approve_processed_columns,
+        approve_originate_columns,
+    } from './config/tableConfig'
+const columnsMap = {
+    'pending':approve_pending_columns,
+    'processed':approve_processed_columns,
+    'originate':approve_originate_columns
+}
+const tabMap = {
+    'pending':1,
+    'processed':2,
+    'originate':3
+}
+export default {
+    mixins:[approveMixins,reportApproveConfig],
+    data() {
+        return {
+            activeTab: 'pending',
+            /* 筛选项 */
+            timeType:1,
+            timeDate:'',
+            ApproveState:'',
+            keyword:'',
+            classify:'',
+            /* 排序项 */
+            SortRule:0,
+            SortField:0,
+
+            tableLoading:false,
+            tableData: [],
+            tableColumns: approve_pending_columns,
+            page: 1,
+            pageSize: 10,
+            total: 0,
+
+            currentData: {},
+            isDetailDialogShow: false
+        };
+    },
+    methods: {
+        getTableData(){
+            this.tableLoading = true
+            this.tableData=[]
+            const baseParams = {
+                ListType:tabMap[this.activeTab]||0,
+                PageSize:this.pageSize,
+                CurrentIndex:this.page,
+                Keyword:this.keyword,
+            }
+            const selectParams = {
+                //关联报告
+                ReportType:this.classify[0]||0,
+                ClassifyFirstId:this.classify[this.classify.length-2]||0,
+                ClassifySecondId:this.classify[this.classify.length-1]||0,
+                //时间
+                TimeType:this.timeType,
+                StartTime:this.timeDate?this.timeDate[0]||'':'',
+                EndTime:this.timeDate?this.timeDate[1]||'':'',
+                //排序
+                SortRule:this.SortRule,
+                SortField:this.SortField,
+                //处理状态
+                ApproveState:this.ApproveState,
+            }
+            approveInterence.getApproveList({
+                ...baseParams,
+                ...selectParams
+            }).then(res=>{
+                this.tableLoading=false
+                if(res.Ret!==200) return 
+                const {List=[],Paging={}} = res.Data||{}
+                this.tableData = List||[]
+                this.total = Paging.Totals||0
+            })
+        },
+        handleCurrentChange(page) {
+            this.page = page;
+            this.getTableData()
+        },
+        sortChange({ prop, order }) {
+            this.SortRule = order==='ascending'?1:2
+            this.SortField= prop==='CreateTime'?1
+                            :prop==='HandleTime'?2:3
+            this.handleCurrentChange(1)
+        },
+        handleClick() {
+            this.timeType = 1
+            this.timeDate=''
+            this.SortField=0
+            this.SortField=0
+            this.tableColumns = columnsMap[this.activeTab];
+            this.$refs.reftable&&this.$refs.reftable.clearSort()
+            this.handleCurrentChange(1)
+        },
+        handleShowDetail(data) {
+            this.currentData = data;
+            this.isDetailDialogShow = true;
+        },
+        toApproveDetail(data,type){
+            this.$router.push({
+                path:'/approveDetail',
+                query:{
+                    type,
+                    approveId:data.ReportApproveId
+                }
+            })
+        },
+        cancelApprove(item){
+            approveInterence.cancelApprove({
+                ReportApproveId:Number(item.ReportApproveId)
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                this.$message.success("撤销成功")
+                this.handleCurrentChange(1)
+            })
+        },
+    },
+    mounted(){
+        this.getClassifyTree()
+        const {formType='approve'} = this.$route.query
+        const formTypeMap = {
+            'approve':'pending',
+            'detail':'processed',
+            'myself':'originate'
+        }
+        this.activeTab = formTypeMap[formType]||'pending'
+        this.getTableData()
+    },
+    components: { RejectDialog }
+};
+</script>
+
+<style lang="scss">
+.approve-list-wrap{
+    .custom-select{
+        .el-input__inner{
+            border-color: #0052D9;
+            background-color: #ECF2FE;
+            color:#0052D9;
+        }
+        .el-input__suffix{
+            color:#0052D9;
+            .el-select__caret{
+                color:#0052D9;
+            }
+        }
+        
+    }
+}
+</style>
+<style scoped lang="scss">
+@import url('./css/pageStyle.scss');
+.approve-list-wrap{
+    .head-tab{
+        margin-bottom: 5px;
+    }
+    .approve-list{
+        .select-box{
+            display: flex;
+            justify-content: space-between;
+            gap:10px;
+            .select-time-box{
+                display: flex;
+                align-items: center;
+                max-width: 380px;
+                .line{
+                    width:10px;
+                    height:1px;
+                    background-color: #C8CDD9;
+                }
+            }
+        }
+    }
+    .list-box{
+        margin-top:20px;
+    }
+}
+</style>

+ 162 - 0
src/views/approve_manage/approveSetting.vue

@@ -0,0 +1,162 @@
+<template>
+    <!-- 审批流配置列表 -->
+    <div class="approve-setting-wrap approve-page-wrap" v-if="isETAApprove">
+        <div class="head-box">
+            <el-button v-permission="permissionBtn.approveFlowPermission.reportApprove_add"
+                type="primary" @click="$router.push('/approveEdit')">添加审批流</el-button>
+            <el-cascader v-model="classify"
+                placeholder="请选择关联报告" clearable
+                :options="classifyTree"
+                :props="{value:'ClassifyId',label:'ClassifyName',children:'Children'}"
+                @change="handleSearchChange"
+                style="margin-right: auto;margin-left: 15px;"></el-cascader>
+            <el-input v-model="Keyword" @input="handleCurrentChange(1)"
+                placeholder="请输入审批流名称" prefix-icon="el-icon-search" clearable style="width:260px;"></el-input>
+        </div>
+        <div class="list-box">
+            <el-table :data="tableData" @sort-change="sortChange" border>
+                <el-table-column
+                    v-for="item in tableColumns"
+                    :key="item.key"
+                    :label="item.label"
+                    :prop="item.key"
+                    :sortable="item.sortable"
+                    align="center"
+                ></el-table-column>
+                <el-table-column label="操作" align="center">
+                    <template slot-scope="{row}">
+                        <el-button v-permission="permissionBtn.approveFlowPermission.reportApprove_edit"
+                            type="text" style="padding:0;" @click="handleEditFlow(row)">编辑</el-button>
+                        <el-button v-permission="permissionBtn.approveFlowPermission.reportApprove_remove"
+                            type="text" style="padding:0;color:red;" @click="handleDeleteFlow(row)">删除</el-button>
+                    </template>
+                    
+                </el-table-column>
+            </el-table>
+            <div style="text-align:right;margin-top:20px">
+                <el-pagination 
+                        layout="total,prev,pager,next,jumper" 
+                        background 
+                        :current-page="page"
+                        @current-change="handleCurrentChange"
+                        :page-size="pageSize"
+                        :total="total"
+                        style="display: inline-block"
+                    />
+            </div>
+        </div>
+    </div>
+    <div class="approve-page-wrap" v-else>
+        <tableNoData :text="pageLoading?'':'系统暂未开通审批流程,请开启审批流程后再进行操作!'"></tableNoData>
+    </div>
+</template>
+
+<script>
+import {approveInterence} from '@/api/modules/approve.js'
+import approveMixins from './mixins/approveMixins';
+import reportApproveConfig from "@/mixins/reportApproveConfig.js"
+export default {
+    mixins:[approveMixins,reportApproveConfig],
+    data() {
+        return {
+            /* 列表筛选项 */
+            ClassifyFirstId:0,//研报倒数第二级分类id
+            ClassifySecondId:0,//研报最后一级分类id
+            ReportType:0,//研报类型 1-中文研报;2-英文研报;3-智能研报
+            SortRule:0,//排序规则:1-正序; 2-倒序(默认)
+            Keyword:'',
+            classify:'',
+
+            tableData:[],
+            tableColumns:[{
+                label:'审批流名称',
+                key:'FlowName'
+            },{
+                label:'关联报告',
+                key:'ReportClassify'
+            },{
+                label:'创建时间',
+                key:'CreateTime',
+                sortable:'custom'
+            }],
+            page:1,
+            pageSize:10,
+            total:0,
+        };
+    },
+    methods: {
+        handleCurrentChange(page){
+            this.page = page
+            this.getTableData()
+        },
+        handleSearchChange(){
+            this.ReportType = this.classify[0]||0
+            this.ClassifySecondId = this.classify[this.classify.length-1]||0
+            if(this.classify.length>=3){
+                this.ClassifyFirstId = this.classify[this.classify.length-2]||0
+            }else{
+                this.ClassifyFirstId = 0
+            }
+            this.handleCurrentChange(1)
+        },
+        sortChange({prop,order}){
+            this.SortRule = order==='ascending'?1:2
+            this.handleCurrentChange(1)
+        },
+        getTableData(){
+            approveInterence.getApproveFlowList({
+                PageSize:this.pageSize,
+                CurrentIndex:this.page,
+                ReportType:this.ReportType,
+                ClassifyFirstId:this.ClassifyFirstId,
+                ClassifySecondId:this.ClassifySecondId,
+                Keyword:this.Keyword,
+                SortRule:this.SortRule
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                const {List=[],Paging={}} = res.Data||{}
+                this.tableData = List||[]
+                this.total = Paging.Totals||0
+            })
+        },
+        handleEditFlow(item){
+            this.$router.push({
+                path:'/approveEdit',
+                query:{
+                    flowId:item.ReportApproveFlowId
+                }
+            })
+        },
+        handleDeleteFlow(item){
+            this.$confirm("删除后不可恢复,确认删除吗?","提示",{
+                type:"warning"
+            }).then(()=>{
+                approveInterence.deleteApproveFlow({
+                    ReportApproveFlowId:item.ReportApproveFlowId
+                }).then(res=>{
+                    if(res.Ret!==200) return 
+                    this.$message.success("删除成功")
+                    this.handleCurrentChange(1)
+                })
+            }).catch(()=>{})
+        }
+    },
+    mounted(){
+        this.getClassifyTree()
+        this.getTableData()
+    },
+};
+</script>
+
+<style scoped lang="scss">
+@import url('./css/pageStyle.scss');
+.approve-setting-wrap{
+    .head-box{
+        display: flex;
+        justify-content: space-between;
+    }
+    .list-box{
+        margin-top:30px;
+    }
+}
+</style>

+ 112 - 0
src/views/approve_manage/components/flowEdiotr.vue

@@ -0,0 +1,112 @@
+<template>
+    <!-- 审批流内容 -->
+    <div class="flow-editor">
+        <span class="circle-point"></span>
+        <span class="circle-point end"></span>
+        <component v-for="(node,index) in flowData" :key="index"
+            :is="node.nodeName"
+            :flowNode="{...node,...{index}}"
+            @addNode="addFlowNode"
+            @editNode="editFlowNode"
+            @removeNode="removeNode">
+        </component>
+    </div>
+</template>
+
+<script>
+import StartNode from './flowNode/startNode.vue';
+import AddNode from './flowNode/addNode.vue';
+import ApproveNode from './flowNode/approveNode.vue';
+import EndNode from './flowNode/endNode.vue';
+export default {
+    props:{
+        flowNodes:{ //审批流原数据,只包含审批节点
+            type:Array,
+            default:[]
+        }
+    },
+    data() {
+        return {
+            flowData:[{
+                nodeType: 1,
+                nodeName: 'StartNode',
+            },{
+                nodeType:3,
+                nodeName:'EndNode'
+            }]
+        };
+    },
+    watch:{
+        flowNodes(val){
+            this.initFlowData()
+        }
+    },
+    methods: {
+        initFlowData(){
+            const approveData = this.flowNodes.map(n=>{
+                const approvers = n.Users.map(i=>{
+                    return {
+                        ItemId:i.UserId,
+                        ItemName:i.UserName
+                    }
+                })
+                return {
+                    ...n,
+                    nodeType:2,
+                    nodeName:'ApproveNode',
+                    approvers
+                }
+            })
+            this.flowData.splice(1,0,...approveData)
+        },
+        addFlowNode(node){
+            const {prevNode} = node
+            delete node.prevNode
+            const {index} = prevNode
+            this.flowData.splice(index+1,0,node)
+        },
+        removeNode(node){
+            const {index} = node
+            this.flowData.splice(index,1)
+        },
+        editFlowNode(node){
+            const {index} = node
+            this.flowData.splice(index,1,node)
+        }
+    },
+    mounted(){
+        //this.initFlowData()
+    },
+    components: { StartNode,AddNode,ApproveNode,EndNode }
+};
+</script>
+
+<style scoped lang="scss">
+@import url('../css/nodeStyle.scss');
+.flow-editor{
+    margin-left: 20px;
+    position:relative;
+    .circle-point{
+            position:absolute;
+            top:0;
+            left:-22px;
+            width:6px;
+            height:6px;
+            border-radius: 50%;
+            background-color: #0052D9;
+            &.end{
+                top:auto;
+                bottom:0;
+            }
+        }
+        &::before{
+            content: '';
+            top:0;
+            left:-20px;
+            bottom: 0;
+            width:2px;
+            background-color: #0052D9;
+            position: absolute;
+        }
+}
+</style>

+ 44 - 0
src/views/approve_manage/components/flowNode/addNode.vue

@@ -0,0 +1,44 @@
+<template>
+    <div class="add-node-wrap">
+        <div class="add-btn" @click="handleAddApprove">
+            <span> <i class="el-icon-circle-plus-outline"></i></span>
+            <span>添加审批人</span>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props:["node"],
+    data() {
+        return {
+            visible:false,
+        };
+    },
+    methods: {
+        handleAddApprove(){
+            this.visible = false
+            const data = {
+                nodeType:2,
+                ApproveType:1,
+                nodeName:'ApproveNode',
+                approvers:[],
+                prevNode:this.node
+            }
+            this.$emit("addNode",data)
+        }
+    },
+};
+</script>
+
+<style scoped lang="scss">
+.add-node-wrap{
+    margin: 20px 0;
+    .add-btn{
+        user-select: none;
+        cursor: pointer;
+        color:#0052D9;
+        display: inline-block;
+    }
+}
+</style>

+ 280 - 0
src/views/approve_manage/components/flowNode/approveNode.vue

@@ -0,0 +1,280 @@
+<template>
+    <div class="approve-node-wrap node-wrap">
+        <div class="node-content">
+            <div class="head">
+                <span class="icon"></span>
+                <span class="name">审核人</span>
+                <span class="icon-btn" @click="removeNode"><i class="el-icon-close"></i></span>
+            </div>
+            <div class="content" @click="showDrawer">
+                <ul class="approver-list" v-if="flowNode.approvers.length">
+                    <li class="list-item" v-for="(item,index) in flowNode.approvers" :key="item.ItemId">
+                        {{item.ItemName}}{{index===flowNode.approvers.length-1?'':'、'}}
+                    </li>
+                </ul>
+                <span v-else style="color:#0052D9;display: flex;align-items: center;">
+                    <img src="~@/assets/img/approve_m/select.png">
+                    请选择审批人
+                </span>
+            </div>
+        </div>
+        <AddNode :node="flowNode" v-on="$listeners"/>
+        <el-drawer
+            direction="rtl"
+            :visible.sync="drawerShow"
+            :withHeader="true"
+            :modal-append-to-body="false"
+            >
+            <div class="approve-drawer-wrap">
+                <div class="header">
+                    <p>审批人设置</p>
+                    <span class="close-icon" @click="drawerShow=false" style="cursor: pointer;"><i class="el-icon-close"></i></span>
+                </div>
+                <div class="drawer-content">
+                    <div class="block">
+                        <p>选择审批人</p>
+                        <div class="choose-box">
+                            <el-radio v-model="block1" :label="1">指定人员</el-radio>
+                            <el-button @click="chooseDialogShow=true" type="primary">
+                                <i class="el-icon-circle-plus-outline" style="margin-right: 5px;"></i>
+                                选择人员
+                            </el-button>
+                        </div>
+                        <draggable 
+                            v-model="choosedList"
+                            animation="300"
+                            tag="ul"
+                            class="approve-list">
+                            <li class="list-item" v-for="item in choosedList" :key="item.ItemId">
+                                <span class="name">{{item.ItemName}}</span>
+                                <span class="icon-btn">
+                                    <i class="el-icon-close" @click.stop="removeChoosedItem(item)"></i>
+                                </span>
+                            </li>
+                        </draggable>
+                    </div>
+                    <div class="line"></div>
+                    <div class="block" style="margin-top:30px;">
+                        <p>多人审批时</p>
+                        <el-radio-group v-model="approveType">
+                            <el-radio :label="1">
+                                依次审批
+                                <el-tooltip effect="dark" placement="top" content="多个审批人依次进行审批;只有当所有审批人同意,该节点才能通过;按选择顺序审批" >
+                                    <span class="hint-text">
+                                        <i class="el-icon-info"></i>
+                                    </span>
+                                </el-tooltip>
+                            </el-radio>
+                            <el-radio :label="2">
+                                会签(须所有审批人同意)
+                                <el-tooltip effect="dark" placement="top" content="所有审批人同意,该节点才能通过;审批无先后顺序" >
+                                    <span class="hint-text">
+                                        <i class="el-icon-info"></i>
+                                    </span>
+                                </el-tooltip>
+                            </el-radio>
+                            <el-radio :label="3">
+                                或签(一名审批人同意或拒绝即可)
+                                <el-tooltip effect="dark" placement="top" content="任意一名审批人同意,该节点即通过;审批无先后顺序" >
+                                    <span class="hint-text">
+                                        <i class="el-icon-info"></i>
+                                    </span>
+                                </el-tooltip>
+                            </el-radio>
+                        </el-radio-group>
+                    </div>
+                </div>
+                <div class="drawer-btn">
+                    <el-button type="primary" plain @click="drawerShow=false">取消</el-button>
+                    <el-button type="primary" @click="editApproveNode">确认</el-button>
+                </div>
+            </div>
+        </el-drawer>
+        <!-- 选择人员弹窗 -->
+        <el-dialog 
+            title="选择人员"
+            width="600px"
+            :visible.sync="chooseDialogShow"
+            :modal-append-to-body="false"
+            :append-to-body="false"
+            @close="chooseDialogShow=false"
+        >
+            <div class="dialog-wrap">
+                <TreeTransfer 
+                    ref="tree-trans"
+                    :defaultList="choosedList"
+                    :chooseDialogShow="chooseDialogShow"
+                />
+            </div>
+            <div class="dialog-btn">
+                <el-button type="primary" plain @click="chooseDialogShow=false">取消</el-button>
+                <el-button type="primary" @click="getChoosedList">确认</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import TreeTransfer from '../treeTransfer';
+import draggable from 'vuedraggable';
+import AddNode from './addNode';
+export default {
+    props:{
+        flowNode:{
+            type:Object,
+            default:{}
+        }
+    },
+    data() {
+        return {
+            drawerShow: false,
+            block1: 1,
+            approveType: 1,
+            chooseDialogShow: false,
+            choosedList:[]
+        };
+    },
+    watch:{
+        drawerShow(val){
+            if(val){
+                this.choosedList = _.cloneDeep(this.flowNode.approvers||[])
+                this.approveType = this.flowNode.ApproveType
+            }
+        }
+    },
+    methods: {
+        removeNode() {
+            this.$emit('removeNode', this.flowNode);
+        },
+        showDrawer() {
+            this.drawerShow = true;
+        },
+        getChoosedList(){
+            if(!this.$refs["tree-trans"].choosedList.length){
+                this.$message.warning("请至少选择一人")
+                return
+            }
+            this.choosedList = _.cloneDeep(this.$refs["tree-trans"].choosedList)||[]
+            this.chooseDialogShow = false
+        },
+        removeChoosedItem(item){
+            const index = this.choosedList.findIndex(i=>i.ItemId===item.ItemId)
+            index!==-1&&this.choosedList.splice(index,1)
+        },
+        editApproveNode(){
+            const node = _.cloneDeep(this.flowNode)
+            node.approvers = _.cloneDeep(this.choosedList)
+            node.ApproveType = this.approveType
+            this.$emit('editNode',node)
+            this.drawerShow = false
+        },
+    },
+    components: { TreeTransfer, draggable, AddNode }
+};
+</script>
+
+<style lang="scss">
+.approve-node-wrap{
+    .el-drawer__body{
+        overflow: hidden;
+    }
+    #el-drawer__title{ //drawer弹出时会自动聚焦第一个元素,设置无内容的title清除聚焦状态
+        height:0px;
+        margin:0px;
+        padding:0px;
+    }
+    .dialog-btn{
+        margin-top:60px;
+        padding-bottom: 40px;
+        text-align: center;
+    }
+}
+</style>
+<style scoped lang="scss">
+.approve-node-wrap{
+    .head{
+        background-color: #0052D9;
+        .icon{
+            background-image: url('~@/assets/img/approve_m/approve.png');
+        }
+        .icon-btn{
+            font-size: 20px;
+        }
+    }
+    .content{
+        .approver-list{
+            color:#0052D9;
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+        }
+    }
+    .approve-drawer-wrap{
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+        overflow: hidden;
+        .header{
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            /* font-weight: bold; */
+            font-size: 18px;
+            height:35px;
+            padding:20px;
+            border-bottom: 1px solid #C8CDD9;
+        }
+        .drawer-content{
+            flex:1;
+            overflow-y: auto;
+            box-sizing: border-box;
+            padding:30px;
+            .block{
+                min-height: 340px;
+                .choose-box{
+                    margin-top: 20px;
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+                }
+            }
+            .line{
+                margin:0 -30px;
+                height: 1px;
+                background-color:#C8CDD9;
+            }
+            .approve-list{
+                margin-top: 10px;
+                display: flex;
+                flex-wrap: wrap;
+                gap:10px;
+                .list-item{
+                    cursor: move;
+                    padding:8px;
+                    border-radius: 4px;
+                    border:1px solid #C8CDD9;
+                    .icon-btn{
+                        cursor: pointer;
+                    }
+                }
+            }
+            .el-radio-group{
+                margin-top: 20px;
+                .el-radio{
+                    display: block;
+                    margin-bottom: 10px;
+                    .hint-text{
+                        color:#C0C4CC;
+                    }
+
+                }
+            }
+        }
+        .drawer-btn{
+            text-align: center;
+            margin:10px;
+        }
+    }
+    
+}
+</style>

+ 26 - 0
src/views/approve_manage/components/flowNode/endNode.vue

@@ -0,0 +1,26 @@
+<template>
+    <div class="end-node-wrap node-wrap">
+        <div class="end-content">流程结束</div>
+    </div>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+
+        };
+    },
+    methods: {
+
+    },
+};
+</script>
+
+<style scoped lang="scss">
+.end-node-wrap{
+    .end-content{
+        color:#C0C4CC;
+    }
+}
+</style>

+ 43 - 0
src/views/approve_manage/components/flowNode/startNode.vue

@@ -0,0 +1,43 @@
+<template>
+    <div class="start-node-wrap node-wrap">
+        <div class="node-content">
+            <div class="head">
+                <span class="icon"></span>
+                <p class="name">发起人</p>
+            </div>
+            <div class="content">所有人</div>
+        </div>
+        <AddNode :node="flowNode" v-on="$listeners"/>
+    </div>
+</template>
+
+<script>
+import AddNode from './addNode.vue';
+export default {
+    components: { AddNode},
+    props:{
+        flowNode:{
+            type:Object,
+            default:{}
+        }
+    },
+    data() {
+        return {};
+    },
+    methods: {},
+};
+</script>
+
+<style scoped lang="scss">
+.start-node-wrap{
+    .head{
+        background-color: #ECF2FE;
+        .name{
+            color:#0052D9;
+        }
+        .icon{
+            background-image: url('~@/assets/img/approve_m/start.png');
+        }
+    }
+}
+</style>

+ 81 - 0
src/views/approve_manage/components/rejectDialog.vue

@@ -0,0 +1,81 @@
+<template>
+    <el-dialog custom-class="approve-reject-dialog"
+        title="审批驳回"
+        :visible.sync="isDetailDialogShow"
+        :close-on-click-modal="false"
+        :modal-append-to-body="false"
+        @close="$emit('close')"
+        width="692px"
+        v-dialogDrag
+        center
+    >
+        <div class="dialog-content-wrap">
+            <el-input type="textarea" v-model="content" :disabled="!isEdit" :rows="10" :placeholder="isEdit?'请输入驳回理由':'无'"></el-input>
+        </div>
+        <div class="dialog-btn-wrap">
+            
+            <template v-if="isEdit">
+                <el-button @click="$emit('close')">取消</el-button>
+                <el-button type="primary" @click="handleConfirm">确认</el-button>
+            </template>
+            <el-button v-else @click="$emit('close')">知道了</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+export default {
+    props:{
+        isDetailDialogShow:{
+            type:Boolean,
+            default:false
+        },
+        isEdit:{
+            type:Boolean,
+            default:true
+        },
+        data:{
+            type:Object,
+            default:()=>{return{}}
+        }
+    },
+    watch:{
+        isDetailDialogShow(val){
+            if(val){
+                this.content = this.data.ApproveRemark||''
+            }
+        }
+    },
+    data() {
+        return {
+            content:''
+        };
+    },
+    methods: {
+        handleConfirm(){
+            /* if(!this.content.length){
+                this.$message.warning("请输入驳回理由")
+                return
+            } */
+            this.$emit('edit',this.content)
+        }
+    },
+};
+</script>
+
+<style lang="scss">
+.approve-reject-dialog{
+    .dialog-content-wrap{
+        .el-textarea{
+            textarea{
+                resize: none !important;
+            }
+        }
+    }
+    .dialog-btn-wrap{
+        text-align: center;
+        margin-top: 20px;
+        padding-bottom: 25px;
+    }
+}
+</style>

+ 60 - 0
src/views/approve_manage/components/timeLine.vue

@@ -0,0 +1,60 @@
+<template>
+    <div class="time-line-wrap">
+        <ul>
+            <li v-for="(node,index) in TimeLineData" :key="index">
+                <TimeLineItem :node="node" :isLast="index===TimeLineData.length-1"/>
+            </li>
+        </ul>
+    </div>
+</template>
+
+<script>
+import TimeLineItem from './timeLineItem.vue';
+export default {
+    components:{TimeLineItem},
+    props:{
+        TimeLineData:{
+            type:Array
+        }
+    },
+    data() {
+        return {
+            //时间线数据格式
+            mockTimeLine:[
+                {
+                    nodeType:1,
+                    nodeText:'发起人:',
+                    approveList:[{
+                        approverName:'张三',
+                        approveTime:'2023-10-10 10:10:10'
+                    }],
+                    nodeStatus:'passed',//passed节点已通过,return节点已撤回,reject节点已驳回,future节点未进行到
+                },{
+                    nodeType:2,
+                    nodeText:'审批人:', //需要拼接成 审批人:2人(会签)
+                    nodeStatus:'process',//节点正在进行中
+                    approveType:'会签',
+                    approveList:[//参与审批的人员
+                        {
+                            approverName:'李四',
+                            approveTime:'2023-10-10 10:15:10',
+                            approveStatus:'已同意'
+                        },{
+                            approverName:'王五',
+                            approveTime:'2023-10-10 10:22:00',
+                            approveStatus:'已驳回',
+                            approveReason:'就是驳回'
+                        }
+                    ]
+                }
+            ]
+        };
+    },
+    methods: {
+
+    },
+};
+</script>
+
+<style scoped lang="scss">
+</style>

+ 120 - 0
src/views/approve_manage/components/timeLineItem.vue

@@ -0,0 +1,120 @@
+<template>
+    <div class="item-wrap">
+        <div class="icon-wrap">
+            <div :class="['icon',node.nodeStatus]">
+                <img :src="require(`@/assets/img/approve_m/${node.nodeStatus}-icon.svg`)" alt="">
+            </div>
+            <div :class="['line',isLast?'last':'',node.nodeStatus]"></div>
+        </div>
+        <div class="info-wrap">
+            <!-- 发起人或审批人 -->
+            <div class="normal-text">
+                <p>{{node.nodeText}}</p>
+            </div>
+            <ul class="approve-list">
+                <li v-for="(item,itemIndex) in node.approveList" :key="itemIndex">
+                    <div class="approve-item-wrap">
+                        <!-- 审批人(审批状态) 审批时间-->
+                        <div class="normal-text">
+                            <p :class="{'passed':item.approveStatus==='已同意'}">{{item.approverName}} <span v-if="item.approveStatus">{{'('+item.approveStatus+')'}}</span> </p>
+                            <span class="time">{{item.approveTime||''}}</span>
+                        </div>
+                        <!-- 驳回理由 -->
+                        <div class="approve-reason" v-if="item.approveReason">
+                            驳回理由:{{item.approveReason}}
+                        </div>
+                    </div>
+                </li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        node:{
+            type:Object,
+            default:{}
+        },
+        isLast:{
+            type:Boolean,
+            default:false
+        }
+    },
+    data() {
+        return {
+
+        };
+    },
+    methods: {
+
+    },
+};
+</script>
+
+<style scoped lang="scss">
+.item-wrap{
+    display: flex;
+    width:100%;
+    .icon-wrap{
+        width:25px;
+        /* border:1px solid black; */
+        position:relative;
+        .icon,.line{
+            position:absolute;
+            left:50%;
+            transform: translateX(-50%);
+        }
+        .icon{
+            width:16px;
+            height:16px;
+            border-radius: 50%;
+            background-color: #fff;
+            overflow: hidden;
+            img{
+                width:100%;
+                height:100%;
+            }
+        }
+        .line{
+            top:16px;
+            width:1px;
+            height:calc(100% - 16px);
+            background-color: gray;
+            &.passed{
+                background-color: #0052D9;
+            }
+            &.last{
+                background-color: transparent;
+            }
+        }
+    }
+    .info-wrap{
+        flex:1;
+        margin-left: 15px;
+        margin-bottom: 20px;
+        .approve-list{
+            .approve-item-wrap{
+                .normal-text{
+                    display: flex;
+                    justify-content: space-between;
+                    margin-bottom: 10px;
+                    color:gray;
+                    &.passed{
+                        color:#0052D9;
+                    }
+                }
+                .approve-reason{
+                   /*  margin-left: -40px; */
+                    padding:10px;
+                    /* padding-left: 40px; */
+                    color:#AD352F;
+                    background-color: #FFF0ED;
+                }
+            }
+        }
+        
+    }
+}
+</style>

+ 185 - 0
src/views/approve_manage/components/treeTransfer.vue

@@ -0,0 +1,185 @@
+<template>
+    <!-- 选择审批人组件 -->
+    <div class="tree-transfer">
+        <div class="before-transfer transfer">
+            <div class="search">
+                <el-input placeholder="搜索" suffix-icon="el-icon-search" style="width:100%;"
+                    v-model="searchText" @input="$refs['trans-tree'].filter(searchText)"></el-input>
+            </div>
+            <div class="content">
+                <el-tree 
+                    node-key="ItemId"
+                    ref="trans-tree"
+                    show-checkbox
+                    :data="treeData"
+                    :props="{
+                        label:'ItemName',
+                        children:'Children'
+                    }"
+                    :filter-node-method="filterNode"
+                    @check="SetCheckedNode"
+                ></el-tree>
+            </div>
+        </div>
+        <div class="after-transfer transfer">
+            <div class="head">
+                <span>已选{{choosedList.length}}项</span>
+                <span class="btn-text delete" @click="clearnItem">清空</span>
+            </div>
+            <draggable 
+                v-model="choosedList"
+                animation="300"
+                tag="ul"
+                class="content">
+                <li class="list-item" v-for="item in choosedList" :key="item.ItemId">
+                    <span class="name">{{item.ItemName}}</span>
+                    <span class="icon-btn" style="color:#C0C4CC;">
+                        <i class="el-icon-close" @click.stop="removeItem(item)"></i>
+                    </span>
+                </li>
+            </draggable>
+        </div>
+    </div>
+</template>
+
+<script>
+import { dataAuthInterface } from "@/api/api.js";
+import draggable from 'vuedraggable';
+export default {
+    components:{draggable},
+    props:{
+        defaultList:{
+            type:Array,
+            default:[]
+        },
+        chooseDialogShow:{
+            type:Boolean,
+            default:false
+        }
+    },
+    data() {
+        return {
+            totalNum:0,
+            treeData:[],
+            searchText:'',
+            choosedList:[]
+        };
+    },
+    watch:{
+        chooseDialogShow:{
+            handler(val){
+                if(val){
+                    this.searchText=''
+                    this.getTreeData()
+                }
+            },
+            immediate:true
+        }
+    },
+    methods: {
+        getTreeData(){
+            ///datamanage/manual/sysuser/search?KeyWord= ,要用新接口再换
+            dataAuthInterface.userSearch({
+                KeyWord: ''
+            }).then(res => {
+                if(res.Ret === 200) {
+                    this.treeData = res.Data||[];
+                }
+                this.$nextTick(()=>{
+                    const keys = this.defaultList.map(i=>{
+                        return i.ItemId
+                    })
+                    this.$refs["trans-tree"].setCheckedKeys(keys)
+                    this.choosedList = _.cloneDeep(this.defaultList)
+                })
+            })
+        },
+        filterNode(value,data){
+            if(!value) return true
+            return data.ItemName.indexOf(value)!==-1
+        },
+        SetCheckedNode(data,{checkedNodes}){
+            if(!this.choosedList.length){
+                 //只获取人员
+                this.choosedList = checkedNodes.filter(i=>{
+                    if(i.ItemId<10000&&!i.Children)
+                        return i
+                })
+            }else{
+                //遍历 filterArr choosedList里没有的,push进去
+                const filterArr = checkedNodes.filter(i=>{
+                    if(i.ItemId<10000&&!i.Children)
+                        return i
+                })
+                filterArr.forEach(i=>{
+                    const index = this.choosedList.findIndex(item=>i.ItemId===item.ItemId)
+                    if(index===-1){this.choosedList.push(i)}
+                })
+                //遍历choosedList,filterArr里没有的,删除
+                const tempArr = _.cloneDeep(this.choosedList)
+                tempArr.forEach(i=>{
+                    const checkIndex = filterArr.findIndex(item=>item.ItemId===i.ItemId)
+                    if(checkIndex===-1){
+                        const index = this.choosedList.findIndex(item=>item.ItemId===i.ItemId)
+                        index!==-1&&this.choosedList.splice(index,1)
+                    }
+                })
+            }
+        },
+        removeItem(item){
+            const {ItemId} = item
+            this.$refs["trans-tree"].setChecked(ItemId,false,false)
+            const index = this.choosedList.findIndex(i=>i.ItemId===ItemId)
+            index!==-1&&this.choosedList.splice(index,1)
+        },
+        clearnItem(){
+            this.$refs["trans-tree"].setCheckedKeys([])
+            this.choosedList = []
+        },
+    },
+    mounted(){
+        //this.getTreeData()
+    }
+};
+</script>
+
+<style scoped lang="scss">
+.tree-transfer{
+    display: flex;
+    align-items: center;
+    gap:12px;
+    .transfer{
+        display: flex;
+        flex-direction: column;
+        width:254px;
+        height: 400px;
+        border:1px solid #DCDCDC;
+        border-radius: 6px;
+        padding:8px;
+        .head,.search,.content{
+            padding:10px;
+        }
+        .head{
+            display: flex;
+            justify-content: space-between;
+            .btn-text{
+                cursor: pointer;
+                &.delete{
+                    color:red;
+                }
+            }
+        }
+        .content{
+            flex:1;
+            overflow-y: scroll;
+            margin-bottom: 10px;
+            .list-item{
+                cursor: pointer;
+                padding:6px 0;
+                display: flex;
+                justify-content: space-between;
+            }
+        }
+    }
+}
+</style>

+ 95 - 0
src/views/approve_manage/config/tableConfig.js

@@ -0,0 +1,95 @@
+//审批管理-待处理
+export const approve_pending_columns = [
+    {
+        key:'ReportTitle',
+        label:'报告标题'
+    },{
+        key:'ReportClassify',
+        label:'关联报告'
+    },{
+        key:'ApplyUserName',
+        label:'提交人'
+    },{
+        key:'CreateTime',
+        label:'提交时间',
+        sortable:'custom'
+    },{
+        key:'RecordState',
+        label:'审批状态',
+    }
+]
+export const appreve_pending_status = [
+    {
+        type:1,
+        label:'待审批'
+    }
+]
+//审批管理-已处理
+export const approve_processed_columns=[
+    {
+        key:'ReportTitle',
+        label:'报告标题'
+    },{
+        key:'ReportClassify',
+        label:'关联报告'
+    },{
+        key:'ApplyUserName',
+        label:'提交人'
+    },{
+        key:'CreateTime',
+        label:'提交时间',
+        sortable:'custom'
+    },{
+        key:'HandleTime',
+        label:'处理时间',
+        sortable:'custom'
+    },{
+        key:'RecordState',
+        label:'处理结果',
+    }
+]
+export const approve_processed_status = [
+    {
+        type:1,
+        label:'已同意'
+    },{
+        type:2,
+        label:'已驳回'
+    }
+]
+//审批管理-我发起的
+export const approve_originate_columns=[
+    {
+        key:'ReportTitle',
+        label:'报告标题'
+    },{
+        key:'ReportClassify',
+        label:'关联报告'
+    },{
+        key:'CreateTime',
+        label:'提交时间',
+        sortable:'custom'
+    },{
+        key:'ApproveTime',
+        label:'审批时间',
+        sortable:'custom'
+    },{
+        key:'State',
+        label:'审批状态',
+    }
+]
+export const approve_originate_status=[
+    {
+        type:1,
+        label:'待审批'
+    },{
+        type:2,
+        label:'已通过'
+    },{
+        type:3,
+        label:'已驳回'
+    },{
+        type:4,
+        label:'已撤销'
+    }
+]

+ 39 - 0
src/views/approve_manage/css/nodeStyle.scss

@@ -0,0 +1,39 @@
+.node-wrap{
+    display: flex;
+    flex-direction: column;
+    /* align-items: center; */
+    .node-content{
+        overflow: hidden;
+        cursor: pointer;
+        border-radius: 4px;
+        width:300px;
+        min-height: 100px;
+        background-color: #fff;
+        border:1px solid #0052D9;
+        .head{
+            user-select: none;
+            color:#fff;
+            height:35px;
+            display: flex;
+            align-items: center;
+            padding: 5px;
+            justify-content: space-between;
+            border-bottom: 1px solid #0052D9;
+            .name{
+                margin-left: 5px;
+                margin-right: auto;
+            }
+            .icon{
+                display: inline-block;
+                width:20px;
+                height:20px;
+            }
+        }
+        .content{
+            padding:5px;
+        }
+    }
+    div{
+        box-sizing: border-box;
+    }
+}

+ 6 - 0
src/views/approve_manage/css/pageStyle.scss

@@ -0,0 +1,6 @@
+.approve-page-wrap{
+    min-height: calc(100vh - 120px);
+    background-color: #fff;
+    box-sizing: border-box;
+    padding:30px;
+}

+ 31 - 0
src/views/approve_manage/mixins/approveMixins.js

@@ -0,0 +1,31 @@
+import {approveInterence} from '@/api/modules/approve.js'
+const filterNodes = (arr,hasDisabled)=>{
+    arr.length &&arr.forEach((item) => {
+        hasDisabled&&(item.disabled = item.HasFlow)
+        item.Children.length && filterNodes(item.Children,hasDisabled);
+        if (!item.Children.length) {
+            delete item.Children;
+        }
+    })
+}
+
+export default{
+    data(){
+        return {
+            classifyTree:[],
+        }
+    },
+    methods:{
+        getClassifyTree(){
+            approveInterence.getReportClassifyTree().then(res=>{
+                if(res.Ret!==200) return 
+                this.classifyTree = res.Data||[]
+                const hasDisabled = window.location.pathname.startsWith('/approveEdit')
+                filterNodes(this.classifyTree,hasDisabled)
+                if(this.getFlowDetail){
+                    this.getFlowDetail()
+                }
+            })
+        },
+    }
+}

+ 19 - 0
src/views/chartFrame_manage/common/config.js

@@ -10,6 +10,25 @@ const portStyle = {
         }
     }
     }
+//利用NodeTool实现角标
+export const textTool = {
+    name:'button',
+    args:{
+        x:'100%',
+        y:'100%',
+        offset:{x:-12,y:-5},
+        markup:[{
+            tagName:'text',
+            textContent:'aaa',
+            attrs:{
+                fill: '#0052D9',
+                fontSize: 12,
+                textAnchor: 'middle',
+                pointerEvents: 'none',
+            }
+        }]
+    }
+}
 //基础节点
 export const baseNode = {
     shape:'rect',

+ 1 - 5
src/views/chartFrame_manage/common/graph.js

@@ -9,13 +9,9 @@ const viewConfig = {
         restrict:true,//节点移动时无法超出画布
     },
     interacting:function (cellView){ //禁止节点移动
-        /* if(cellView.cell.getData().disableMove){
-            return false
-        } */
-        return false
+        return {'nodeMovable':false,'edgeMovable':false}
     },
     highlighting:{},
-    /* connecting:{}, */
     history:false,//关闭画布撤销/重做能力。
     keyboard:false,
     clipboard: false,

+ 31 - 6
src/views/chartFrame_manage/components/frameContainer.vue

@@ -32,7 +32,7 @@
 <script>
 import { ElDropdownMenu } from 'element-ui';
 import { myGraph } from '../common/graph';
-import { baseNode } from '../common/config';
+import { baseNode , textTool } from '../common/config';
 import FrameToolBar from './frameToolBar.vue';
 export default {
     components:{ElDropdownMenu,FrameToolBar},
@@ -40,6 +40,10 @@ export default {
         FrameworkContent:{ //框架内容
             type:String,
             default:''
+        },
+        Nodes:{
+            type:Array,
+            default:[]
         }
     },
     data() {
@@ -70,9 +74,6 @@ export default {
                 this.currentCell = null
             }
         },
-        FrameworkContent(newVal){//当框架内容发生改变时,画布内容也发生改变
-            newVal.length&&this.gragh&&this.graph.fromJSON(JSON.parse(newVal))
-        }
     },
     computed:{
         contextMenu(){//右键菜单,根据权限配置
@@ -95,6 +96,8 @@ export default {
             this.FrameworkContent.length&&this.graph.fromJSON(JSON.parse(this.FrameworkContent))
             //如果有内容,将画布内容居中
             this.FrameworkContent.length&&this.graph.scrollToContent({ animation: { duration: 600 }})
+            //如果有内容,遍历每个节点,赋值chartNum
+            this.FrameworkContent.length&&this.setNodeInfo()
             //如果是非编辑页,加载完成画布内容后冻结画布
             window.location.pathname.startsWith('/chartframe')&&this.graph.freeze()
             //如果是编辑页,加载完成后清除历史数据
@@ -104,6 +107,17 @@ export default {
         dispose(){
             this.graph&&this.graph.dispose()
         },
+        setNodeInfo(){
+            const nodes = this.graph.getNodes()
+            this.Nodes.forEach(node=>{
+                const currentNode = nodes.find(item=>item.id===node.NodeId)
+                if(currentNode){
+                    currentNode.removeTools()
+                    const toolOption = this.getToolOption({chartNum:node.ChartNum||0,color:currentNode.attrs.label.fill})
+                    currentNode.addTools(toolOption)
+                }
+            })
+        },
         //添加/编辑节点
         editNode(node){
             //获取视口范围
@@ -114,6 +128,9 @@ export default {
                 currentNode.data.id=node.nodeLink.MyChartClassifyId
                 currentNode.label=node.nodeName
                 currentNode.data.nodeLink = node.nodeLink
+                currentNode.removeTools()
+                const toolOption = this.getToolOption({chartNum:node.nodeLink.ChartNum,color:currentNode.attrs.label.fill})
+                currentNode.addTools(toolOption)
             }else{
                 //在视口范围内添加节点
                 this.graph.addNode({
@@ -125,12 +142,19 @@ export default {
                     height:50,
                     data:{
                         id:node.nodeLink.MyChartClassifyId,//存储节点对应的myETA分类id
-                        nodeLink:node.nodeLink
+                        nodeLink:node.nodeLink,
                     },
-                    label:node.nodeName||''
+                    label:node.nodeName||'',
+                    tools:[this.getToolOption({chartNum:node.nodeLink.ChartNum,color:baseNode.attrs.label.fill})]
                 }})
             }
         },
+        getToolOption({chartNum,color}){
+            const options = _.cloneDeep(textTool)
+            options.args.markup[0].textContent = chartNum +''
+            options.args.markup[0].attrs.fill = color
+            return options
+        },
         //点击右键菜单事件
         handleContext(key){
             const select_cell = this.graph.getSelectedCells()
@@ -191,6 +215,7 @@ export default {
         getContentNodes(){
             return this.graph.getNodes().map(node=>{
                 return {
+                    NodeId:node.id,
                     NodeName:node.label,
                     MyChartClassifyId:Number(node.data.id)
                 }

+ 9 - 1
src/views/chartFrame_manage/components/frameToolBar.vue

@@ -115,7 +115,7 @@
                             :class="{'img-disabled':!isSelectNode&&!isSelectEdge,'actived':isSelectNode||isSelectEdge}">
                     </label>
                     <input type="color" id="storke" style="width: 0;height: 0;visibility: hidden;" 
-                        :value="cellStyle.stroke"
+                        :value="nodeStyle.stroke"
                         @input="valueChange"/>
                     <span class="disabled" v-if="!isSelectNode&&!isSelectEdge"></span>
                 </span>
@@ -311,7 +311,15 @@ export default {
                 if(styleMap[id]){
                     attr = this.isSelectNode?styleMap[id][0]:styleMap[id][1]
                 } 
+                
                 this.currentCell.attr(attr,value)
+                //改变节点字体颜色时,角标的颜色也一起改变
+                if(attr==='label/fill'){
+                    let tool = this.currentCell.getTools().items[0]
+                    tool.args.markup[0].attrs.fill = value
+                    this.currentCell.removeTools()
+                    this.currentCell.addTools(tool)
+                }
             }
         },
         changeCellStyle({attr,value}){

+ 9 - 1
src/views/chartFrame_manage/css/basePage.scss

@@ -57,12 +57,20 @@
                 overflow-y: auto;
             } */
             .public-catalog{
+                .title{
+                    cursor: pointer;
+                    span{
+                        margin-left: 5px;
+                    }
+                }
                 .catalog-tree{
-                    margin: 20px 0;
+                   /*  margin: 20px 0; */
+                   margin-top:20px;
                     min-height: 100px;
                 }
             }
             .my-list{
+                margin-top: 20px;
                 .classify-item{
                     display: flex;
                     align-items: center;

+ 1 - 1
src/views/chartFrame_manage/css/customTree.scss

@@ -1,4 +1,4 @@
-.chart-frame-wrap{
+.chart-frame-wrap,.tree-wrap{
     .catalog-tree{
         .custom-tree-node {
             display: flex !important;

+ 14 - 2
src/views/chartFrame_manage/frameEditor.vue

@@ -3,7 +3,8 @@
     <div class="frame-editor-wrap">
         <div class="option-wrap">
             <el-input style="width:240px;" placeholder="请输入框架名称" v-model.trim="frameDetail.FrameworkName"></el-input>
-            <el-button type="primary" style="margin-left:auto;" @click="handleEditNode({})"
+            <el-button type="primary" plain style="margin-left:auto;" @click="returnListPage">返回</el-button>
+            <el-button type="primary" style="margin-left:20px;" @click="handleEditNode({})"
                 v-if="permissionBtn.isShowBtn('chartFramePermission','chartframe_my_editNode')">添加节点</el-button>
             <el-button type="primary" style="margin-left:20px;" @click="saveFrame"
                 v-if="permissionBtn.isShowBtn('chartFramePermission','chartframe_my_saveFrame')">保存</el-button>
@@ -12,6 +13,7 @@
             <!-- 沙盘图组件 -->
             <FrameContainer ref="container"
                 :FrameworkContent="frameDetail.FrameworkContent"
+                :Nodes="frameDetail.Nodes"
                 @editNode="handleEditNode"
                 @framePic="getFramePic"
             />
@@ -70,6 +72,7 @@ export default {
             frameDetail:{
                 FrameworkName:'',
                 FrameworkContent:'',
+                Nodes:[]
             },
             lockLoding:null,
             modifyNode: {},//正在编辑的节点
@@ -151,6 +154,11 @@ export default {
                     this.$router.replace({path:'/editframe',query:{frameId:this.frameId}})
                 })
             }
+            //重新加载一次画布
+            this.$refs.container.dispose()
+            this.$nextTick(()=>{
+                this.$refs.container.init()
+            })
         },
         getMyList(){
             mychartInterface.classifyList().then((res)=>{
@@ -163,7 +171,7 @@ export default {
             if(this.frameId){
                 const res = await chartFrameInterface.getFrameDetail({ChartFrameworkId:Number(this.frameId)})
                 if(res.Ret!==200) return 
-                this.frameDetail = res.Data||{FrameworkName:'',FrameworkContent:''}
+                this.frameDetail = res.Data||{FrameworkName:'',FrameworkContent:'',Nodes:[]}
             }
             //获取到框架内容后再加载graph
             this.$nextTick(()=>{
@@ -171,6 +179,10 @@ export default {
             })
             
         },
+        //返回至列表页,并选中当前页的框架(如果是编辑)
+        returnListPage(){
+            this.$router.push({path:'/chartframe',query:{frameId:this.frameId||''}})
+        }
     },
     mounted(){
         this.getMyList()

+ 16 - 8
src/views/chartFrame_manage/index.vue

@@ -34,8 +34,8 @@
             </div>
             <div class="catalog-list">
                 <div class="public-catalog">
-                    <p>公共框架</p>
-                    <div class="catalog-tree">
+                    <p @click="expandPublic = !expandPublic" class="title">公共框架<span><i :class="{'el-icon-arrow-down':!expandPublic,'el-icon-arrow-up':expandPublic}"></i></span></p>
+                    <div class="catalog-tree" v-show="expandPublic">
                         <el-tree
                             ref="catalogTree"
                             class="catalog-tree other-tree"
@@ -167,7 +167,8 @@
                     <div class="frame-wrap">
                         <!--沙盘图组件-->
                         <FrameContainer ref="container"
-                            :FrameworkContent="currentFrame.FrameworkContent"
+                            :FrameworkContent="currentFrameDetail.FrameworkContent"
+                            :Nodes="currentFrameDetail.Nodes"
                             @showDialog="handleShowDialog"/>
                     </div>
                 </div>
@@ -243,7 +244,7 @@
 
 <script>
 import draggable from 'vuedraggable';
-import FrameContainer from './components/frameContainer.vue';
+import FrameContainer from './components/frameContainer';
 import chartDetail from '@/views/mychart_manage/components/chartDetailDia.vue';
 import { mychartInterface,chartFrameInterface } from '@/api/api.js';
 import {copyBlob} from '@/utils/svgToblob.js';
@@ -273,6 +274,7 @@ export default {
     data() {
         return {
             isSlideLeft:false,//控制左侧目录栏是否显示
+            expandPublic:false,//控制公共框架展开收起
 
             /* drag 我的框架相关 */
             dragStartIndex:0,
@@ -291,6 +293,7 @@ export default {
             currentList:[],//选择公共框架时,框架列表
             myFrameList:[],//我的框架列表
             currentFrame:{},//选择的框架
+            currentFrameDetail:{},//选中的框架详情
             /* frame node */
             myETADetailDialogShow:false,//点击节点时,弹出myeta图表详情弹框
             modifyFrame:{},//正在修改的框架
@@ -391,16 +394,21 @@ export default {
                 this.currentFrame={}
             }
         },
-        handleInitGraph(){
+        async handleInitGraph(){
+            //获取框架详情
+            if(this.currentFrame.ChartFrameworkId){
+                const res = await chartFrameInterface.getFrameDetail({ChartFrameworkId:Number(this.currentFrame.ChartFrameworkId)})
+                this.currentFrameDetail = res.Data||{}
+            }
             //判断一下框架内容是否是合法的JSON,否则置为空
             try{
-                JSON.parse(this.currentFrame.FrameworkContent)
+                JSON.parse(this.currentFrameDetail.FrameworkContent)
             }catch(e){
-                this.currentFrame.FrameworkContent = ''
+                this.currentFrameDetail.FrameworkContent = ''
             }
             this.$nextTick(()=>{
                 //若框架有内容,才加载画布
-                this.currentFrame.FrameworkContent&&this.$refs.container.init()
+                this.currentFrameDetail.FrameworkContent&&this.$refs.container.init()
             })
         },
         /* 拖动相关 */

+ 11 - 7
src/views/chartRelevance_manage/crossVarietyAnalysis/components/tagSetDialog.vue

@@ -17,7 +17,7 @@
         prefix-icon="el-icon-search"
         size="medium"
         clearable 
-        @change="filterList"
+        @input="inputVisible = false;"
       />
       <div class="tag-list">
         <div
@@ -34,7 +34,7 @@
             class="input-edit"
             :ref="`inputRef${index}`"
             size="small"
-            @change="editItemHandle(item)"
+            @blur="editItemHandle(item)"
           />
           <span v-else>
             {{ item.label }}
@@ -57,10 +57,10 @@
           v-model="inputValue"
           ref="saveTagInput"
           size="small"
-          @change="addNewTagHandle"
+          @blur="addNewTagHandle"
         >
         </el-input>
-        <el-button v-else size="small" @click="addNewItem">+ 标签</el-button>
+        <el-button v-else size="small" @click="addNewItem">+ {{type==='tag'?'标签':'品种'}}</el-button>
 
       </div>
     </div>
@@ -121,8 +121,6 @@ export default {
 
       if(res.Ret !== 200) return
       this.$message.success('删除成功')
-      // let index = this.list.findIndex(_ => _.id === item.id);
-      // this.list.splice(index,1)
       
       this.refreshData()
     },
@@ -158,6 +156,11 @@ export default {
 
     /* 编辑标签 */
     async editItemHandle(item) {
+      if(item.label===item.ChartVarietyName || item.label===item.ChartTagName) {
+        item.isEdit = false;
+        return
+      }
+
       let res = this.type==='tag' 
         ? await crossVarietyInterface.tagEdit({ TagName: item.label,ChartTagId:item.id})
         : await crossVarietyInterface.varietyEdit({ VarietyName: item.label,ChartVarietyId:item.id})
@@ -181,7 +184,8 @@ export default {
     },
 
     cancelHandle() {
-      this.searchTxt = ''
+      this.searchTxt = '';
+      this.inputVisible = false;
       this.$emit('update:isShow',false)
     }
   },

+ 1 - 0
src/views/dataEntry_manage/chartSetting.vue

@@ -2801,6 +2801,7 @@ export default {
       }
       .main-right {
         flex:1;
+        overflow: hidden;
         .mx-datepicker {
           width: 220px !important;
         }

+ 41 - 10
src/views/dataEntry_manage/components/addTarget.vue

@@ -15,16 +15,22 @@
 			<span style="fontSize:16px;">{{dialogTitle}}</span>
 		</div>
 		<div class="dialog-min" style="height:460px;">
-			<div style="overflowY:auto;overflowX:hidden;">
-				<el-tree
-				class="type-list"
-				:data="typeList"
-				accordion
-				highlight-current
-				:props="defaultProps" 
-				@node-click="chooseType"
-				style="width:140px;">
-				</el-tree>
+			<div class="left-cont">
+				<div style="overflowY:auto;overflowX:hidden;">
+					<el-tree
+					class="type-list"
+					:data="typeList"
+					accordion
+					highlight-current
+					:props="defaultProps" 
+					@node-click="chooseType"
+					style="width:140px;">
+					</el-tree>
+				</div>
+				<div class="data-authority-row" @click="dataAuthorityJump">
+					<img src="~@/assets/img/icons/add_blue_new.png" /> 
+					<span>添加手工指标分类</span>
+				</div>
 			</div>
 			<div class="rigth-cont">
 				<el-form
@@ -377,6 +383,10 @@ import {dataInterence} from 'api/api.js';
 					this.classify_id = item.ClassifyId;
 				}
 			},
+			// 手工数据权限页面跳转
+			dataAuthorityJump(){
+				this.$router.push('Sysdatauth')
+			}
 		},
 		created() {
 			// this.getTargetlist();
@@ -393,6 +403,27 @@ import {dataInterence} from 'api/api.js';
 .dialog_container {
 	.dialog-min {
 		display: flex;
+		.left-cont{
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			padding-bottom: 15px;
+			.data-authority-row{
+				margin-top: 10px;
+				display: inline-flex;
+				align-items: center;
+				cursor: pointer;
+				img{
+					margin-right: 6px;
+					height: 17px;
+					width: 17px;
+				}
+				span{
+					color: #1146DB;
+					font-size: 12px;
+				}
+			}
+		}
 		.rigth-cont {
 			border-left: 1px solid #dcdcdc;
 			padding:  0 0 48px 60px;

+ 7 - 2
src/views/dataEntry_manage/components/insertData.vue

@@ -138,8 +138,13 @@ export default {
     },
     // 上传成功之后
     handleSuccess(result) {
-      console.log(result)
-      let res = this.$parseData(result);
+      //兼容下结构
+      let res = this.$parseData({
+        headers: {
+          dk: sessionStorage.getItem('dk')||""
+        },
+        data: result
+      });
       if (res.Ret === 200) {
         // 0成功 1部分失败 -1全部失败
         let str = `

+ 1 - 0
src/views/dataEntry_manage/css/chartfit.scss

@@ -142,6 +142,7 @@ $font-normal:14px;
 		}
 		.main-right {
 			width: 80%;
+			overflow: hidden;
 			.mx-datepicker {
 				width: 220px !important;
 			}

+ 17 - 14
src/views/dataEntry_manage/databaseComponents/addTargetDiaBase.vue

@@ -189,7 +189,7 @@
 
 <script>
 import { dataBaseInterface,dataInterence } from '@/api/api.js'
-import { fromArr, fromCode ,frequencyArr,windCommonIndexCodeArr,THSCommonIndexStockCodeArr,THSCommonIndexFuturesCodeArr} from './util';
+import { frequencyArr,windCommonIndexCodeArr,THSCommonIndexStockCodeArr,THSCommonIndexFuturesCodeArr} from './util';
 export default {
 	name: '',
 	props: {
@@ -235,14 +235,14 @@ export default {
 			search_txt: '',//搜素关键词 M001625518 M0001427
 			fromType: 'wind',
 			fromDatabase:'0',
-			fromArr,
+			fromArr:[],
 			indexCodeSelected:[],
 			databaseType:[
 				{value:'0',label:'经济数据库'},
 				{value:'1',label:'日期序列'}
 			],
 			wsdAddStep:1,
-			fromCode,
+			fromCode:[],
 			tableColums: [
 				{
 					label: '指标ID',
@@ -374,23 +374,26 @@ export default {
 					if (res.Ret === 200) {
 						this.haveResult = false;
 						//隆众指标和手工指标、交易所等将名称 频度 单位带到下一步
-						let opther_params = ['9', '10', '11', '15', '16', '17', '18', '19', '20', '21', '26','38','41','57','60','71'].includes(this.fromCode.get(this.fromType)) ? {
-							edb_name: res.Data.SearchItem.EdbName,
-							unit: res.Data.SearchItem.Unit,
-							frequency: res.Data.SearchItem.Frequency,
-						} : {}
-						let jy_params = Number(this.fromCode.get(this.fromType))>1000?{
-							edb_name:res.Data.SearchItem.EdbName,
-							unit:res.Data.SearchItem.Unit,
-							frequency:frequencyArr.includes(res.Data.SearchItem.Frequency)?res.Data.SearchItem.Frequency:''
-						}:{}
+						console.log(this.fromCode.get(this.fromType),'this.fromCode.get(this.fromType)');
+						// let opther_params = ['9', '10', '11', '15', '16', '17', '18', '19', '20', '21', '26','38','41','57','60','71'].includes(this.fromCode.get(this.fromType)) ? 
+						let opther_params={
+							edb_name: res.Data.SearchItem.EdbName || '',
+							unit: res.Data.SearchItem.Unit || '',
+							frequency: frequencyArr.includes(res.Data.SearchItem.Frequency)?res.Data.SearchItem.Frequency:'',
+						} 
+						// : {}
+						// let jy_params = Number(this.fromCode.get(this.fromType))>1000?{
+						// 	edb_name:res.Data.SearchItem.EdbName,
+						// 	unit:res.Data.SearchItem.Unit,
+						// 	frequency:frequencyArr.includes(res.Data.SearchItem.Frequency)?res.Data.SearchItem.Frequency:''
+						// }:{}
 						this.add_params = {
 							EdbCode: res.Data.SearchItem.EdbCode,
 							EndDate: res.Data.SearchItem.EndDate,
 							StartDate: res.Data.SearchItem.StartDate,
 							Source: Number(this.fromCode.get(this.fromType)),
 							...opther_params,
-							...jy_params
+							// ...jy_params
 						}
 						this.status = res.Data.Status;
 						let classify_arr = res.Data.ClassifyList || [];

+ 261 - 42
src/views/dataEntry_manage/databaseComponents/computedDialog.vue

@@ -70,15 +70,99 @@
 				添加更多参数
 			</span>
 			<div class="computed-min">
-				<div class="computed-top">
-					<span style="margin-right: 8px">计算公式</span>
-					<el-input placeholder="请输入公式" v-model="formula" clearable :disabled="calulateForm.view">
-					</el-input>
+				<div class="computed-section">
+					<div>
+						<label class="label">空值处理
+							<el-tooltip placement="top">
+								<div slot="content" v-html="formTips['null-val']" style="width:300px;line-height:20px;"/>
+								<i class="el-icon-question"/>
+							</el-tooltip>
+						</label>
+						<el-select
+							v-model="nullValueForm.nullValueWay"
+							placeholder="请选择"
+							:disabled="calulateForm.view"
+						>
+							<el-option
+								v-for="item in nullWayOptions"
+								:key="item.value"
+								:label="item.label"
+								:value="item.value"
+							>
+							</el-option>
+						</el-select>
+					</div>
+
+					<div style="margin-left: 120px" v-if="showMaxNullDeal">
+						<label class="label">MAX、MIN空值处理
+							<el-tooltip placement="top">
+								<div slot="content" v-html="formTips['max-null-val']" style="width:300px;line-height:20px;"/>
+								<i class="el-icon-question"/>
+							</el-tooltip>
+						</label>
+						<el-select
+							v-model="nullValueForm.maxNullWay"
+							placeholder="请选择"
+							:disabled="calulateForm.view"
+						>
+							<el-option label="等于0" :value="1" />
+							<el-option label="跳过空值" :value="2" />
+						</el-select>
+					</div>
+				</div>	
+				<div class="computed-section">
+					<label class="label">计算公式
+						<el-tooltip placement="top">
+							<div slot="content" v-html="formTips['formula']" style="width:300px;line-height:20px;"/>
+							<i class="el-icon-question"/>
+						</el-tooltip>
+					</label>
+					<!-- <el-input placeholder="请输入公式" v-model="formula" clearable :disabled="calulateForm.view">
+					</el-input> -->
+					<ul class="formula-list">
+						<li style="margin-bottom: 15px;">
+							<el-input placeholder="请输入公式" v-model="formulaList[0].formula" clearable :disabled="calulateForm.view" style="width: 220px"/>
+							
+							<span v-if="formulaDateArr.length" class="date-section-text">{{formulaDateArr[formulaDateArr.length-1]}}(含)之后</span>
+
+							<span class="example-txt">公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C) &nbsp;函数支持:MAX(),MIN(),ln(A),log(a,A),abs(),exp(),pow(),round()</span>
+						</li>
+
+						<li class="formula-item" v-for="(item,index) in formulaList.slice(1)" :key="index+1">
+							<el-input 
+								placeholder="请输入公式" 
+								v-model="item.formula" 
+								clearable 
+								:disabled="calulateForm.view"
+								style="width: 220px"
+							/>
+
+							<el-date-picker
+								v-model="item.date"
+								type="date"
+								value-format="yyyy-MM-dd"
+								style="margin: 0 10px;width: 220px"
+								placeholder="选择日期"
+								:disabled="calulateForm.view"
+								@change="selectFormulaDate($event,item)"
+							/>
+
+							<i class="el-icon-circle-close" style="font-size:20px;" v-if="!calulateForm.view" @click="removeFormulaItem(index+1)"/>
+
+							<template v-if="formulaDateArr.length&&item.date">
+								<span v-if="item.date===formulaDateArr[0]" class="date-section-text">{{formulaDateArr[0]}}之前</span>
+								<span v-else class="date-section-text">{{formulaDateArr[formulaDateArr.findIndex(_ =>_ ===item.date)-1]}}(含)——{{item.date}}</span>
+							</template>
+						</li>
+					</ul>
 				</div>
-				<span class="example-txt"
-					>公式示例:A*0.5+B*C*1.2+120-MAX(A,B,C)</span
-				>
-				<span class="example-txt">函数支持:MAX(),MIN(),ln(A),log(a,A)</span>
+				<el-button 
+					v-if="!calulateForm.view" 
+					icon="el-icon-plus" 
+					style="margin-left:70px;" 
+					@click="addFormulaHandle"
+				>新增分段</el-button>
+
 			</div>
 			<el-form
 				ref="diaForm"
@@ -148,7 +232,7 @@
 			placement="top-start"
 			width="360"
 			trigger="click">
-			<p style="padding:30px;line-height:25px;" v-html="$parent.tips.get(4)"/>
+			<p style="padding:30px;line-height:25px;" v-html="$parent.tips.get(type)"/>
 			<span slot="reference" class="tip-label">公式说明</span>
 		</el-popover>
 	</el-dialog>
@@ -156,6 +240,7 @@
 
 <script>
 import { dataBaseInterface } from '@/api/api.js';
+import * as preDictEdbInterface from '@/api/modules/predictEdbApi.js';
 import { formRules } from '../databaseComponents/util';
 import { unitArr } from '@/utils/defaultOptions';
 const tag_arr = [];
@@ -176,6 +261,26 @@ export default {
 		calulateList: {
 			type: Array,
 		},
+		edbSource: { // ''||'predict'
+			type: String,
+			default: ''
+		}
+	},
+	computed: {
+		type() {
+			return this.edbSource ? 31 : 4;
+		},
+
+		/* max空值处理显示 当输入的公式包含MAX、MIN且空值处理为0时,输入公式失焦后出现右侧选项; */
+		showMaxNullDeal() {
+			let haveMaxOrMin = this.formulaList.some(_ => _.formula.toUpperCase().includes('MAX') || _.formula.toUpperCase().includes('MIN'))
+
+			return haveMaxOrMin && this.nullValueForm.nullValueWay===4
+		},
+
+		formulaDateArr() {
+			return this.formulaList.map(_ => _.date).filter(_ => _).sort((a,b) => new Date(a)-new Date(b))
+		}
 	},
 	watch: {
 		isOpenComputed(newval) {
@@ -184,7 +289,10 @@ export default {
 			/* 回显 */
 			if (this.calulateList.length && newval) {
 				this.addList = _.cloneDeep(this.calulateList);
-				this.formula = this.calulateForm.formula;
+				this.formulaList = JSON.parse(this.calulateForm.formula).map(_ => ({
+					formula: _.f,
+					date: _.d||''
+				}));
 				this.formData = {
 					targetName: this.calulateForm.targetName,
 					unit: this.calulateForm.unit,
@@ -192,6 +300,11 @@ export default {
 					frequency: this.calulateForm.frequency,
 				};
 
+				this.nullValueForm = {
+					nullValueWay: this.calulateForm.emptyType,
+					maxNullWay: this.calulateForm.maxEmptyType,
+				}
+
 				this.searchOptions = this.calulateList.map(item => ({
 					EdbInfoId: item.target,
 					EdbName: item.edb_name,
@@ -241,6 +354,9 @@ export default {
 			],
 			searchOptions: [],
 			formula: '', //计算公式
+			formulaList: [ //公式数组
+				{ formula: '', }
+			],
 			dataloading: false,
 			formData: {
 				targetName: '',
@@ -252,18 +368,46 @@ export default {
 			search_page: 1,
 			current_search:'',
 
-			newestDate: ''
+			newestDate: '',
+
+			nullValueForm: {
+				nullValueWay: 0,
+				maxNullWay: 1
+			},//空值处理
+			nullWayOptions: [
+				{ label: '查找前后35天最近值',value: 0 },
+				{ label: '不计算',value: 1 },
+				{ label: '前值填充',value: 2 },
+				{ label: '后值填充',value: 3 },
+				{ label: '等于0',value: 4 },
+			],
+			formTips: {
+				'null-val': `1、查找前后35天最近值:在参与计算的日期序列上某指标无值时,该指标往前/往后找距离最近的值作为当天的值进行计算,遍历允许跨年,往前最多35天,往后最多35天<br>
+				2、不计算:只要有一个指标在某个日期没有值(即空值),则计算指标在该日期没有值 <br>
+				3、前值填充:空值优先以最近的前值填充,没有前值时,用后值填充 <br>
+				4、后值填充:空值优先以最近的后值填充,没有前值时,用后值填充 <br>
+				5、等于0:空值以0值参与计算 <br>
+				注意:此处缺失值的处理,作用于数据全部时间段`,
+				'max-null-val': `MAX、MIN公式中指标存在空值时按如下规则处理:<br>
+				1、等于0,空值用0参与计算;<br>
+				2、跳过空值,去除空值指标,剩余指标进行计算,若该日期所有指标均为空值,则该日期无值;`,
+				'formula':`1、支持新增分段,实现不同分段使用不同的计算公式,若未新增分段,则所有日期序列用统一公式计算<br>
+				2、新增分段需配置新公式和时间节点,在时间节点之前(不含)使用新公式,在时间节点之后(含)使用已配置公式,每个分段公式支持修改<br>
+				3、分段时间节点不允许重复,不允许超出第一个指标的日期区间`
+			}
+
 		};
 	},
 	methods: {
 		/* 获取目录结构 */
-		getMenu() {
-			dataBaseInterface.menuListV3().then((res) => {
-				if (res.Ret === 200) {
-					this.filterNodes(res.Data.AllNodes||[]);
-					this.options = res.Data.AllNodes || [];
-				}
-			});
+		async getMenu() {
+			const res = this.edbSource === 'predict'
+				? await preDictEdbInterface.classifyListV2()
+				: await dataBaseInterface.menuListV3()
+				if (res.Ret !== 200) return
+				
+				this.filterNodes(res.Data.AllNodes||[]);
+				this.options = res.Data.AllNodes || [];
 		},
 		// 递归改变第三级目录结构
 		filterNodes(arr) {
@@ -302,18 +446,25 @@ export default {
 			this.searchApi(this.current_search);
 		},
 
-		searchApi(query,page=1) {
-			dataBaseInterface.targetSearchByPage({
-				KeyWord:query,
-				CurrentIndex: page
-			}).then(res => {
+		async searchApi(query,page=1) {
+			const res = this.edbSource === 'predict'
+				? await preDictEdbInterface.edbSearch({
+            Keyword: query,
+            CurrentIndex: page,
+						FilterSource: this.type === 45 ? 3 : this.type === 66 ? 6 : 1,
+						Frequency: this.type===64?'季度': ''
+          })
+				: await dataBaseInterface.targetSearchByPage({
+					KeyWord:query,
+					CurrentIndex: page,
+					FilterSource: this.type === 14 ? 3 : this.type === 63 ? 6 : 1,
+					Frequency: this.type===61?'季度': ''
+				})
 				if(res.Ret !== 200) return
 
 				const { List,Paging } = res.Data;
 				this.search_have_more = page < Paging.Pages;
 				this.searchOptions = page === 1 ? List : this.searchOptions.concat(List);
-					
-			})
 		},
 
 		searchLoad() {
@@ -361,30 +512,41 @@ export default {
 			this.getNewestDate();
 		},
 		async saveHandle() {
-			if (!this.formula) return this.$message.warning('计算公式不能为空');
+				if (!this.formulaList[0].formula) return this.$message.warning('计算公式不能为空');
 				await this.$refs.diaForm.validate();
 
 				// 指标id数组
-				let target_arr = this.addList
-					.filter((item) => item.target)
-					.map((item) => {
-						return {
-							EdbInfoId: item.target,
-							FromTag: item.tag,
-						};
-					});
+				let EdbInfoIdArr = this.addList.filter((item) => item.target).map((item) => ({
+						EdbInfoId: item.target,
+						FromTag: item.tag,
+					}));
+
+				let formulaArr = this.formulaList
+					.filter((_,index) => index===0||(index>0&&_.formula&&_.date))
+					.map(_ => ({f: _.formula,d: _.date}))
+
+				const { nullValueWay,maxNullWay } = this.nullValueForm;
+				
 				let params = {
-					CalculateFormula: this.formula,
+					CalculateFormula: JSON.stringify(formulaArr),
 					ClassifyId: this.formData.menu[this.formData.menu.length - 1] || 0,
 					EdbName: this.formData.targetName,
 					Frequency: this.formData.frequency,
 					Unit: this.formData.unit,
-					EdbInfoIdArr: target_arr,
+					EdbInfoIdArr,
+					EmptyType: nullValueWay,
+					MaxEmptyType: maxNullWay
 				};
 				this.dataloading = true;
-				const res = this.calulateForm.edb_id
-					? await dataBaseInterface.calculateEdit({...params,EdbInfoId: this.calulateForm.edb_id})
-					: await dataBaseInterface.calculateAdd(params)
+
+				let res;
+				if(this.edbSource === 'predict') {
+					res = await preDictEdbInterface.calculateEdbSave(this.calulateForm.edb_id ? {...params,EdbInfoId:this.calulateForm.edb_id } : params)
+				}else {
+					res = this.calulateForm.edb_id
+						? await dataBaseInterface.calculateEdit({...params,EdbInfoId: this.calulateForm.edb_id})
+						: await dataBaseInterface.calculateAdd(params)
+				}
 
 				this.dataloading = false;
 				if (res.Ret !== 200) return
@@ -394,6 +556,42 @@ export default {
 						: this.$emit('addCallBack','add',{ code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:params.ClassifyId });
 				this.init();
 		},
+
+		/* 新增公式分段 */
+		addFormulaHandle() {
+			let addItem = {
+				formula: this.formulaList[this.formulaList.length-1].formula,
+				date: ''
+			}
+			this.formulaList.push(addItem)
+		},
+
+		/* 移除公式 */
+		removeFormulaItem(index) {
+			this.formulaList.splice(index,1)
+		},
+
+		/* 选择日期 检验排 */
+		selectFormulaDate(val,item) {
+			console.log(val,item)
+			const { start_date,end_date } = this.addList[0];
+
+			if(!start_date) return
+
+			let dateStamp = new Date(val).getTime(), 
+					startStamp = new Date(start_date).getTime(),
+					endStamp = new Date(end_date).getTime();
+			if (dateStamp > endStamp || dateStamp < startStamp) {
+				item.date = '';
+				return this.$message.warning('分段日期必须在第一个指标日期区间')
+			}
+
+			else if(this.formulaList.filter(_ => _.date===val).length>1) {
+				item.date = '';
+				return this.$message.warning('分段日期不可重复')
+			}
+		},
+
 		init() {
 			this.$refs.diaForm.resetFields();
 			this.addList = [
@@ -430,6 +628,14 @@ export default {
 				menu: '',
 				frequency: '',
 			};
+			this.formulaList = [
+				{ formula: '' }
+			]
+			this.nullValueForm = {
+				nullValueWay: 0,
+				maxNullWay: 1
+			}
+
 		},
 		cancelHandle(type) {
 			this.init();
@@ -437,7 +643,6 @@ export default {
 			type==='cancel' && !this.calulateForm.edb_id && this.$emit('openPrev');
 		},
 	},
-	created() {},
 	mounted() {},
 };
 </script>
@@ -501,10 +706,24 @@ export default {
 			margin: 50px 0;
 			padding-bottom: 40px;
 			border-bottom: 1px dashed #aab4cc;
+			.computed-section {
+				display: flex;
+				margin-top: 20px;
+			}
+			.label {
+				padding:10px 10px 10px 0;
+			}
 			.example-txt {
 				display: block;
-				margin-left: 70px;
-				margin-top: 15px;
+				margin-top: 10px;
+			}
+			.formula-item {
+				display: flex;
+				align-items: center;
+				margin-bottom: 15px;
+			}
+			.date-section-text {
+				margin-left: 15px;
 			}
 		}
 	}

+ 6 - 3
src/views/dataEntry_manage/databaseComponents/util.js

@@ -155,11 +155,12 @@ export const computedBatchTypes = [
 
 //频度
 export const frequencyArr = ['日度','周度','旬度','月度','季度','年度']
-//添加基础指标来源
+//添加基础指标来源 已改为从接口获取 datamanage/edb_source/list
 export const fromArr = ['wind','同花顺','彭博','彭博财务','路透','手工指标','隆众指标','SMM','Mysteel','郑商所',
 '大商所','上期所','中金所','上期能源','欧洲天然气','中国煤炭市场网','谷歌出行指数','EIA STEO报告','UN','卓创数据(红桃3)',
 '百川盈孚','国家统计局','富宝数据'];
 
+// 已改为从接口获取 datamanage/edb_source/list
 export const fromCode = new Map([
 	['同花顺','1'],
 	['wind','2'],
@@ -229,8 +230,10 @@ export const allFromArr = [
 
 //公式说明 eta指标库
 export const formulaTip = new Map([
-	[4,`指标运算:选择指标参数,按照输入的计算公式进行计算,生成新的指标<br>
-	在参与计算的日期序列上某指标无值时,该指标往前/往后找距离最近的值作为当天的值进行计算,遍历允许跨年,往前最多35天,往后最多35天`],
+	[4,`指标运算:<br>
+	1、选择指标参数<br>
+	2、生成指标的时间序列:以第一个指标为准,其他指标去匹配第一个指标的日期序列,没有值的,按照所选空值处理方式处理<br>
+	3、按照输入的计算公式进行计算`],
 	[5,`累计值转月值计算方法:<br>
 	<p style="display:flex;justify-content: space-between;">
 		<span style="width:45%">1月无值:1月=2月/2</span>

+ 11 - 9
src/views/dataEntry_manage/databaseList.vue

@@ -88,7 +88,6 @@
 						@node-drag-end="dropMouseLeave"
 						@node-drag-leave="dropMouseLeave"
 						@node-drag-enter="dropMouseOver"
-						@node-drag-over="dropMouseOver"
 					>
 						<span
 							class="custom-tree-node"
@@ -1422,8 +1421,8 @@ export default {
 				}
 			}else{//拖动的是目录
 				// console.log(dropNode.level,draggingNode.level);
-				//目录只能拖动到层级比他大的里面去
-				if(dropNode.level<draggingNode.level||(dropNode.level===draggingNode.level&&type!=='inner')){
+				//目录层级不能改变
+				if((dropNode.level+1==draggingNode.level&&type==='inner'&&!dropNode.data.EdbCode)||(dropNode.level===draggingNode.level&&type!=='inner')){
 					canDrop=true
 				}
 			}
@@ -1509,7 +1508,7 @@ export default {
 			// console.log(e.layerY);
 			
 			// 被拖拽节点对应的 Node、所进入节点对应的 Node、event
-			if((node1.level>node2.level||(node1.data.EdbInfoId>0&&!node2.data.EdbInfoId)) && (e.target.childNodes[0].className.includes('el-tree-node__content') 
+			if(!node2.data.EdbInfoId&&(node1.level>node2.level||(node1.data.EdbInfoId>0&&!node2.data.EdbInfoId)) && (e.target.childNodes[0].className.includes('el-tree-node__content') 
 			|| e.target.className.includes('el-tree-node__content'))) {
 				// console.log(e.target.childNodes[0])
 				e.target.childNodes[0].className.includes('el-tree-node__content') 
@@ -1702,14 +1701,17 @@ export default {
 						end_date: item.EndDate
 					}
 				})
+				const { EdbInfoId,CalculateFormula,EdbName,Unit,Frequency,EmptyType,MaxEmptyType } = res.EdbInfoDetail;
 				/* 公式和表单 */
 				this.calulateForm =  {
-					edb_id:res.EdbInfoDetail.EdbInfoId,
-					formula: res.EdbInfoDetail.CalculateFormula,
+					edb_id: EdbInfoId,
+					formula: CalculateFormula,
 					menu: menuArrId||[],
-					targetName: res.EdbInfoDetail.EdbName,
-					unit: res.EdbInfoDetail.Unit,
-					frequency: res.EdbInfoDetail.Frequency,
+					targetName: EdbName,
+					unit: Unit,
+					frequency: Frequency,
+					emptyType: EmptyType,
+					maxEmptyType: MaxEmptyType,
 					view
 				};
 			} else  {

+ 564 - 0
src/views/dataEntry_manage/thirdBase/YyzxData.vue

@@ -0,0 +1,564 @@
+<template>
+  <div class="smmTarget-container target-container" id="box">
+    <span
+        v-show="!isLeftWrapShow"
+        class="slide-btn-icon slide-right"
+        @click="isLeftWrapShow = !isLeftWrapShow"
+    >
+        <i :class="{'el-icon-d-arrow-left':isLeftWrapShow,'el-icon-d-arrow-right':!isLeftWrapShow}"></i>
+    </span>
+    <div class="left-cont minHeight" id="left" v-show="isLeftWrapShow">
+        <span
+            v-show="isLeftWrapShow"
+            class="slide-btn-icon slide-left"
+            @click="isLeftWrapShow = !isLeftWrapShow"
+        >
+            <i :class="{'el-icon-d-arrow-left':isLeftWrapShow,'el-icon-d-arrow-right':!isLeftWrapShow}"></i>
+        </span>
+      <div class="left-top">
+        <el-button
+          v-permission="permissionBtn.dataSourcePermission.yyzxData_export"
+          style="width: 100%"
+          type="primary"
+          plain
+          size="medium"
+          @click="exportClick"
+          :loading="btnload"
+          :disabled="!select_classify&&!leftSearchTradeCode"
+          >导出Excel</el-button
+        >
+        <el-autocomplete
+          style="margin: 20px 0; width: 100%"
+          prefix-icon="el-icon-search"
+          v-model="leftSearchVal"
+          :fetch-suggestions="handleLeftSearch"
+          :trigger-on-focus="false"
+          placeholder="指标名称/指标ID"
+          @select="handleSelectLeftSearchval"
+          popper-class="el-autocomplete-suggestion-data-entry"
+          clearable
+        >
+          <template slot-scope="scope">
+            <div v-if="scope.item.nodata" style="text-align: center">
+              暂无数据
+            </div>
+            <div v-else>
+              {{ scope.item.IndexName }}
+            </div>
+          </template>
+        </el-autocomplete>
+      </div>
+      <div class="scroll-wrap">
+        <el-tree
+          ref="treeRef"
+          class="target_tree"
+          :data="classifyList"
+          node-key="ClassifyId"
+          :props="{
+            label: 'ClassifyName',
+            children: 'Children',
+          }"
+          :current-node-key="select_classify"
+          :expand-on-click-node="false"
+          check-strictly
+          highlight-current
+          empty-text="暂无分类"
+          @current-change="nodeChangeHandle"
+        >
+        </el-tree>
+      </div>
+      <span
+        class="move-btn resize"
+        v-drag
+        id="resize"
+        @mousemove="dynamicNode && resetNodeStyle(dynamicNode)"
+      >
+      </span>
+    </div>
+    <div
+      class="right-cont minHeight"
+      id="right"
+      v-loading="dataloading"
+      element-loading-text="获取数据中..."
+    >
+      <template v-if="rightShow">
+        <div class="right-box" @scroll="scrollHandle">
+          <div class="data-header">
+            <lz-table
+              :tableOption="tableOption"
+              tableType="header"
+              ref="table"
+              source="yyzx"
+              @editTarget="editTargetByTable"
+            />
+          </div>
+          <div class="data-cont" v-if="dateArr.length">
+            <lz-table
+              :tableOption="tableOption"
+              tableType="data"
+              :dateArr="dateArr"
+              source="yyzx"
+            />
+          </div>
+          <div v-else class="nodata"></div>
+        </div>
+      </template>
+      <div v-else class="nodata-cont">
+        <tableNoData text="暂无数据"/>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import lzTable from "@/components/lzTable.vue";
+import { yongyiInterface } from "@/api/api.js";
+export default {
+  name: "Yyzx",
+  components: { lzTable },
+  directives: {
+    drag(el, bindings) {
+      el.onmousedown = function (e) {
+        var init = e.clientX;
+        // console.log(init);
+        var box = $('#box')[0];
+        // console.log(box.clientWidth)
+        let total_wid = box.offsetWidth;
+        var left = $('#left')[0];
+        var right = $('#right')[0];
+        var initWidth = left.offsetWidth;
+        document.onmousemove = function (e) {
+          var end = e.clientX;
+          var newWidth = end - init + initWidth;
+          left.style.width = newWidth + 'px';
+          right.style.width = newWidth > 320 ? total_wid - newWidth + 'px' : total_wid - 320 + 'px';
+        };
+        document.onmouseup = function () {
+          document.onmousemove = document.onmouseup = null;
+          e.releaseCapture && e.releaseCapture();
+        };
+        e.setCapture && e.setCapture();
+        return false;
+      };
+    }
+  }, 
+  data() {
+    return {
+      isLeftWrapShow:true,
+      exportBase:process.env.VUE_APP_API_ROOT + "/datamanage/yongyi/export", //ssm数据导出接口
+      dataloading: false,
+      rightShow: false,
+      select_classify: 0,
+      classifyList: [],
+      tableOption: [],
+      dateArr: [], //最长的日期数组
+      btnload: false,
+      page_no: 1,
+      page_size: 20,
+      havemore: true, //是否还有数据
+
+      leftSearchVal: "", //左侧搜索值
+      leftSearchTradeCode: "", //如果是搜索选择的 则有此code
+      isShowSingleData: false, //右侧是否展示的是单个指标数据
+    };
+  },
+  methods: {
+    /* 获取分类 */
+    getClassify() {
+      yongyiInterface.classifyList().then((res) => {
+        if (res.Ret !== 200) return;
+        this.classifyList = res.Data.List || [];
+      });
+    },
+    /* 获取数据 */
+    getDataList: _.throttle(function () {
+      this.isShowSingleData = false;
+      this.dataloading = true;
+      yongyiInterface
+        .dataList({
+          ClassifyId: this.select_classify,
+          PageSize: this.page_size,
+          CurrentIndex: this.page_no,
+        })
+        .then((res) => {
+          this.rightShow = true;
+          if (res.Ret !== 200) return;
+
+          // 找出最多的页码 判断是否还有数据
+          let page_arrs = res.Data.map((item) => item.Paging.Pages);
+          let totalPage = Math.max.apply(Math, page_arrs);
+          this.havemore = this.page_no < totalPage ? true : false;
+
+          // 合并数据
+          if (this.page_no === 1) {
+            this.tableOption = res.Data;
+          } else {
+            this.tableOption.forEach((item) => {
+              res.Data.forEach((_item) => {
+                if (item.IndexCode === _item.IndexCode) {
+                  item.DataList = item.DataList.concat(_item.DataList);
+                }
+              });
+            });
+          }
+
+          // 合并所有指标中的日期 作为日期数组
+          let arr = res.Data.map((item) => {
+            return item.DataList;
+          });
+
+          let obj = [];
+          for (let i of arr) {
+            for (let j of i) {
+              obj.push(j.DataTime);
+            }
+          }
+          let arr2 = [...new Set(obj)].sort().reverse();
+          let concatArr = [...new Set([...this.dateArr, ...arr2])]
+            .sort()
+            .reverse();
+          this.dateArr = this.page_no === 1 ? arr2 : concatArr;
+
+          /* 不满6个追加6个空的显示一排 别问 问就是为了美观  */
+          if (this.tableOption.length < 7)
+            for (let i = 0; i < 7; i++) {
+              this.tableOption.push({
+                DataList: [],
+              });
+              if (this.tableOption.length >= 7) break;
+            }
+
+          //数据最大长度小于12个 追加数据满12个 别问 问就是为了美观
+          if (this.dateArr.length < 12)
+            for (let i = 0; i < 12; i++) {
+              this.dateArr.push("");
+              if (this.dateArr.length >= 12) break;
+            }
+
+          this.dataloading = false;
+          this.page_no === 1 &&
+            this.$nextTick(() => {
+              this.rightShow && this.initWidth();
+            });
+        });
+    }, 200),
+    // 获取单个指标数据
+    async getTargetDataList(code) {
+      this.isShowSingleData = true;
+      this.dataloading = true;
+      try {
+        const res = await yongyiInterface.getTargetDataList({
+          IndexCode: code,
+        });
+        this.rightShow = true;
+        if (res.Ret !== 200) return;
+        const DataList = res.Data.Data || [];
+        // 设置为没有更多数据
+        this.haveMore = false;
+        // 合并数据
+        this.tableOption = [
+          {
+            DataList: DataList,
+            ...res.Data,
+          },
+        ];
+        // 这里是单个指标所以不用合并日期
+        const arr = DataList.map((item) => item.DataTime);
+        this.dateArr = [...new Set(arr)].sort().reverse();
+        /* 不满6个追加6个空的显示一排 别问 问就是为了美观  */
+        for (let i = 0; i < 7; i++) {
+          this.tableOption.push({
+            DataList: [],
+          });
+          if (this.tableOption.length >= 7) break;
+        }
+        //数据最大长度小于12个 追加数据满12个 别问 问就是为了美观
+        if (this.dateArr.length < 12)
+          for (let i = 0; i < 12; i++) {
+            this.dateArr.push("");
+            if (this.dateArr.length >= 12) break;
+          }
+        this.select_quota = res.Data.IndexName;
+        this.select_Unit = res.Data.Unit;
+        this.select_frequency = res.Data.Frequency;
+        this.select_ModifyTime = res.Data.ModifyTime || "";
+        this.dataloading = false;
+        this.rightShow && this.initWidth();
+      } catch (err) {
+        console.log(err);
+      }
+    },
+    initWidth() {
+      this.$nextTick(() => {
+        $(".right-box")[0].style.width =
+          this.$refs.table.$el.clientWidth + 5 + "px";
+        $(".right-box")[0].scrollTop = 0;
+        $(".right-box")[0].scrollLeft = 0;
+      });
+    },
+    /* 无频度的异常显示处理 7*12*/
+    nodataDeal() {
+      this.tableOption = [];
+      this.dateArr = [];
+      for (let i = 0; i < 7; i++) {
+        this.tableOption.push({
+          DataList: [],
+        });
+        if (this.tableOption.length >= 7) break;
+      }
+      for (let i = 0; i < 12; i++) {
+        this.dateArr.push("");
+        if (this.dateArr.length >= 12) break;
+      }
+    },
+    /* 滚动加载 */
+    scrollHandle(e) {
+      if (this.isShowSingleData) return;
+      const dom = e.target;
+      let total = dom.scrollTop + dom.clientHeight;
+      if (total >= dom.scrollHeight && this.havemore) {
+        this.page_no++;
+        console.log("load下一页");
+        this.getDataList();
+      }
+    },
+    /* 数据导出 */
+    exportClick() {
+      this.btnload = true;
+      const link = document.createElement("a");
+      link.href = this.exportDataUrl;
+      link.download = "";
+      link.click();
+      setTimeout(() => {
+        this.btnload = false;
+      }, 5000);
+    },
+    //左侧搜索
+    async handleLeftSearch(query, cb) {
+      cb([]);
+      if (!query) return;
+      const res = await yongyiInterface.getTargetListByName({
+        Keyword: query,
+      });
+      if (res.Ret === 200) {
+        let arr = res.Data || [];
+        if (!arr.length) {
+          cb([{ nodata: true }]);
+        } else {
+          cb(arr);
+        }
+      }
+    },
+    // 选中左侧搜索值
+    handleSelectLeftSearchval(e) {
+      if (!e.IndexCode) return;
+      this.leftSearchTradeCode = e.IndexCode;
+      this.leftSearchVal = e.IndexName;
+
+      // 获取单独指标数据
+      this.getTargetDataList(e.IndexCode);
+    },
+
+    getTargetData(code, arr) {
+      for (const item of arr) {
+        if (item.BaseFromBaiinfoIndexCode === code) return item;
+        if (item.Children && item.Children.length) {
+          const _item = this.getTargetData(code, item.Children);
+          if (_item) return _item;
+        }
+      }
+    },
+    // 对[#,;]转义
+    escapeStr(str) {
+      return str.replace(/#/g, escape("#")).replace(/;/g, escape(";")); 
+    },
+  
+    //改变选中节点
+    nodeChangeHandle(data, node) {
+      if (data.ClassifyId === this.select_classify) return;
+      this.select_classify = data.ClassifyId;
+      this.leftSearchVal=''
+      this.page_no = 1;
+      this.page_size = 20;
+      this.getDataList()
+    },
+   
+  },
+  computed: {
+    exportDataUrl() {
+      // 数据导出接口
+      let urlStr = this.exportBase;
+      // token
+      urlStr += `?${localStorage.getItem("auth") || ""}`;
+      if (this.isShowSingleData) {
+        // 指标id
+        urlStr += `&IndexCode=${
+          this.isShowSingleData ? this.leftSearchTradeCode : ""
+        }`;
+      } else {
+        // 目录id
+        urlStr += `&ClassifyId=${
+          this.isShowSingleData ? "" : this.select_classify
+        }`;
+      }
+      return this.escapeStr(urlStr);
+    },
+  },
+  created() {},
+  mounted() {
+    this.getClassify();
+  },
+};
+</script>
+<style lang="scss">
+@import "../css/customtree.scss";
+@import "../css/baseTargetPage.scss";
+.smmTarget-dialog-cont {
+  .el-cascader {
+    .el-input {
+      width: 100% !important;
+    }
+  }
+}
+</style>
+<style lang="scss" scoped>
+.smmTarget-container {
+  display: flex;
+  * {
+    box-sizing: border-box;
+  }
+  .minHeight {
+    height: calc(100vh - 120px);
+    background-color: #fff;
+    box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+    border-radius: 4px;
+  }
+  div::-webkit-scrollbar {
+    width: 5px !important;
+  }
+  .left-cont {
+    min-width: 300px;
+    width: 300px;
+    margin-right: 20px;
+    padding: 30px 0;
+    overflow: hidden;
+    position: relative;
+    .left-top {
+      padding: 0 20px;
+    }
+    .scroll-wrap {
+      padding: 0 10px;
+      height: calc(100vh - 280px);
+      overflow-y: auto;
+      .target_tree {
+        color: #333;
+        .label-input .el-input__inner {
+          height: 25px;
+          line-height: 25px;
+        }
+        .custom-tree-node {
+          display: flex !important;
+          justify-content: space-between;
+          align-items: center;
+          display: block;
+          flex: 1;
+          .node_label {
+            margin-right: 2px;
+          }
+          .el-icon-view {
+            color: #409eff;
+            font-size: 18px;
+            margin-left: 5px;
+          }
+        }
+      }
+      .add-cont {
+        margin: 50px 0 20px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: #409eff;
+        font-size: 16px;
+        cursor: pointer;
+      }
+    }
+    .move-btn {
+      height: 100%;
+      width: 4px;
+      position: absolute;
+      right: 0px;
+      top: 0;
+      &:hover {
+        cursor: col-resize;
+      }
+    }
+    .classify-list {
+      padding: 0 20px;
+      /* margin-top: 20px; */
+      height: calc(100vh - 280px);
+      overflow-y: auto;
+      .classify-item {
+        font-size: 14px;
+        color: #666;
+        margin-bottom: 20px;
+        &:hover {
+          cursor: pointer;
+          color: #409eff;
+        }
+        &.act {
+          color: #409eff;
+        }
+      }
+    }
+  }
+  .right-cont {
+    width: 82%;
+    padding: 30px;
+    .right-box {
+      max-width: 100%;
+      max-height: calc(100vh - 230px);
+      border-left: 1px solid #dcdfe6;
+      border-right: 1px solid #dcdfe6;
+      overflow: auto;
+      .data-header {
+        width: 100%;
+        position: sticky;
+        top: 0;
+        z-index: 2;
+      }
+      .data-cont {
+        height: calc(100vh - 444px);
+      }
+      .nodata {
+        height: calc(100vh - 460px);
+        border: 1px solid #dcdfe6;
+        font-size: 16px;
+        color: #999;
+      }
+    }
+    .frequency-list {
+      margin-top: 20px;
+      display: flex;
+      flex-wrap: wrap;
+      .frequency-btn {
+        width: 112px;
+        margin: 0 30px 10px 0;
+      }
+    }
+    .nodata-cont {
+      width: 150px !important;
+      text-align: center;
+      color: #666;
+      font-size: 16px;
+      margin: 0 auto;
+    }
+  }
+}
+.dialog-cont {
+  padding-left: 50px;
+}
+.dia-bot {
+  display: flex;
+  justify-content: center;
+}
+</style>

+ 795 - 0
src/views/dataEntry_manage/thirdBase/ysTargetBase.vue

@@ -0,0 +1,795 @@
+<template>
+  <div id="YS-data-container" v-loading="dataLoading" element-loading-text="指标数据加载中,请勿切换到其他页面">
+    <div class="search-zone">
+      <div class="search-row">
+        <el-cascader @change="searchList" :options="classifyList" v-model="typesList" clearable
+          placeholder="请选择分类" class="search-item"
+          :props="{
+            value:'Type',
+            label:'Type',
+            children:'Child'
+          }"></el-cascader>
+        <el-select v-model="searchParams.Frequency" class="search-item" placeholder="请选择频度" clearable @change="searchList">
+          <el-option :label="item" :value="item" v-for="item in frequencyList" :key="item"></el-option>  
+        </el-select>
+        <el-select v-model="searchParams.DataState" class="search-item" placeholder="请选择指标数据状态" clearable @change="searchList">
+          <el-option :label="item.label" :value="item.value" v-for="item in dataStatusList" :key="item.value"></el-option>  
+        </el-select>
+        <el-select v-model="edbCheckList" multiple filterable remote clearable collapse-tags class="search-item edb-filter"
+          reserve-keyword placeholder="指标ID/指标名称" :remote-method="remoteMethod" 
+          v-loadMore="searchLoad" @clear="searchList" @remove-tag="removeEdbFilterTag" ref="edbFilterRef" >
+          <el-option v-for="item in edbFilterDataList" :key="item.IndexCode"
+            :label="item.IndexName" :value="item.IndexCode">
+          </el-option>
+        </el-select>
+        <el-checkbox label="列表全选" v-model="isCheckAll" :indeterminate="isCheckIndeterminate" style="margin: 0 20px 10px 0;"
+        @change="listCheckAllChange"></el-checkbox>
+      </div>
+      <el-button type="primary" style="margin: 0 0 10px 0;padding:12px 18px ;" @click="addToEdbBase" 
+      :disabled="addBaseDisable">添加到指标库</el-button>
+    </div>
+    <div class="table-zone">
+      <el-table :data="tableData" border @sort-change="sortChange"  @selection-change="selectionChange"
+      ref="edbDataRef" @select="selectHandle" @select-all="selectAllHandle">
+        <el-table-column type="selection" min-width="50" align="center"></el-table-column>
+        <el-table-column label="指标ID" align="center" prop="IndexCode">
+          <template slot-scope="{row}">
+            {{row.IndexCode}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标名称" align="center" prop="IndexName">
+          <template slot-scope="{row}">
+            {{row.IndexName}}
+          </template>
+        </el-table-column>
+        <el-table-column label="频度" align="center" prop="Frequency" width="80">
+          <template slot-scope="{row}">
+            {{row.Frequency}}
+          </template>
+        </el-table-column>
+        <el-table-column label="单位" align="center" prop="Unit" >
+          <template slot-scope="{row}">
+            {{row.Unit}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标开始时间" align="center" prop="StartDate"
+        sortable="custom">
+          <template slot-scope="{row}">
+            {{row.StartDate}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标最新时间" align="center" prop="EndDate"
+        sortable="custom" >
+          <template slot-scope="{row}">
+            {{row.EndDate}}
+          </template>
+        </el-table-column>
+        <el-table-column label="更新时间" align="center" prop="ModifyTime" sortable="custom">
+          <template slot-scope="{row}">
+            {{row.ModifyTime}}
+          </template>
+        </el-table-column>
+        <el-table-column label="预计发布时间" align="center" prop="ReleaseTime" sortable="custom" >
+          <template slot-scope="{row}">
+            {{row.ReleaseTime}}
+          </template>
+        </el-table-column>
+        <el-table-column label="指标数据状态" align="center" prop="DataState" >
+          <template slot-scope="{row}">
+            {{row.DataState?
+            dataStatusList.find(it => it.value == row.DataState)?dataStatusList.find(it => it.value == row.DataState).label:''
+            :'正常更新'}}
+          </template>
+        </el-table-column>
+        <el-table-column label="分类" align="center" prop="TypeAll" >
+          <template slot-scope="{row}">
+            {{row.TypeAll}}
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <m-page class="table-page" v-show="total"
+    :total="total" :pageSize="searchParams.PageSize"
+    :page_no="searchParams.CurrentIndex"
+    @handleCurrentChange="pageNumberChange"/>
+    <!-- 指标重复弹窗 -->
+    <el-dialog :visible.sync="checkFailShow" :close-on-click-modal="false" :modal-append-to-body='false' 
+			width="600px" title="操作提示">
+			<div class="check-fail-box">
+				<div>
+					<div style="margin-bottom: 20px;">
+            {{ indexAllExist?'本次添加的指标均已在指标库中,请勿重复添加!':'指标库中已存在以下指标,会自动过滤!' }}
+          </div>
+					<div v-for="(item,index) in existIndexList" class="exist-index-item" @click="existIndexClick(item)">
+						{{ index+1+'、'+item.text }}
+					</div>
+				</div>
+				<div class="check-fail-button">
+					<el-button type="primary" @click="edbCheckFinished" style="width: 120px;">知道了</el-button>
+				</div>
+			</div>
+		</el-dialog>
+    <!-- 添加到指标库 -->
+    <el-dialog :visible.sync="addToEbdDiaShow" :close-on-click-modal="false" :modal-append-to-body='false' 
+    width="980px" title="添加到指标库">
+      <div class="edb-table-box">
+        <div class="edb-table-preview">
+          <table width="auto" border="0">
+            <thead>
+              <tr v-for="(item, index) in edbTableHeadKey" :key="item">
+                <td class="sticky" style="left: 0;text-align: center;">
+                  {{ edbTableHeadData.get(item) }}
+                </td>
+                <td v-for="(data, sub_index) in edbIndexDatas" :key="sub_index" :colspan="2" 
+                :class="highLightIndex.includes(data.EdbName) && item=='EdbName' ?'exist-highlight':''">
+                  <template v-if="item === 'ClassifyId'">
+                    <el-cascader :options="options" v-model="data[item]" placeholder="请选择所属目录"
+                    size="mini" :disabled="!data.Source"
+                    :props="{label: 'ClassifyName',
+                      value: 'ClassifyId',
+                      children: 'Children',
+                      checkStrictly: true,
+                      emitPath:false}">
+                    </el-cascader>
+                  </template>
+                  <template v-else-if="item === 'Unit'">
+                    <el-autocomplete
+                    :disabled="!data.Source"
+                      v-model.trim="data[item]"
+                      :fetch-suggestions="querySearchUnit"
+                      placeholder="请输入单位"
+                      suffix-icon="el-icon-arrow-down"
+                      size="mini"
+                    ></el-autocomplete>
+                  </template>
+                  <template v-else-if="item === 'Frequency'">
+                    <el-select v-model="data[item]" placeholder="请选择频度" size="mini" :disabled="!data.Source">
+                      <el-option :label="item" :value="item"
+                      v-for="item in edbFrequencyList" :key="item"></el-option>
+                    </el-select>
+                  </template>
+                  <template v-else-if="item === 'EdbName'">
+                    <el-input v-model.trim="data[item]" placeholder="请输入指标名称" size="mini" :disabled="!data.Source"></el-input>
+                  </template>
+                  <template v-else>
+                    <div style="padding: 0 7px;">{{ data[item] }}</div>
+                  </template>
+                </td>
+              </tr>
+            </thead>
+            <tbody v-if="edbIndexDatas && edbIndexDatas.length>0">
+              <tr v-for="(item,index) in edbIndexDatas[0].DataList.length" :key="index">
+                <td :rowspan="edbIndexDatas[0].DataList.length" v-if="index==0"
+                class="sticky" style="left: 0;text-align: center;">
+                  数据详情
+                </td>
+                <template v-for="(item1,index1) in edbIndexDatas.length">
+                  <td>{{ edbIndexDatas[index1].DataList[index].DataTime }}</td>
+                  <td>{{ edbIndexDatas[index1].DataList[index].Value }}</td>
+                </template>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+        <div class="dia-bot">
+          <el-button @click="addToEbdDiaShow=false" style="width: 120px;">取消</el-button>
+          <el-button type="primary" @click="ysEdbSaveHandle" style="width: 120px;margin-left: 30px;" :loading="saveLoading">保存</el-button>
+        </div>
+      </div>
+		</el-dialog>
+  </div>
+</template>
+
+<script>
+import mPage from '@/components/mPage.vue'
+import { dataBaseInterface,dataInterence,smmDataInterface} from '@/api/api.js'
+import { frequencyArr} from '../databaseComponents/util';
+
+  export default {
+    name:'ysTargetBase',
+    components:{mPage},
+    data() {
+      return {
+        searchParams:{
+          Types:'',
+          Frequency:'',
+          DataState:'',
+          CurrentIndex:1,
+          PageSize:10,
+          SortParam:'',
+          SortType:'',
+          IndexCodes:''
+        },
+        typesList:[],
+        options:[],
+        isCheckAll:false,
+        isCheckIndeterminate:false,
+        // 标志列表当前是全选状态还是不是全选状态和 isCheckAll不一样
+        checkAllStatus:false,
+        classifyList:[],
+        frequencyList: ["日度", "周度", "月度", "季度", "半年", "年度"],
+        edbFrequencyList:frequencyArr,
+        dataStatusList:[{value:'normal',label:'正常更新'},{value:'ceased',label:'已停更'},{value:'irregular',label:'更新依赖外部会有不定期延迟'}],
+        forSearchParams:{
+          Keyword:'',
+          CurrentIndex:1,
+          PageSize:20,
+        },
+        searchHaveMore: false,
+        edbFilterDataList:[],
+        // 选择的指标名称筛选项
+        edbCheckList:[],
+        // edb的过滤选项是否有变化,用于失焦是判断是否需要请求
+        hasEdbFilterChange:false,
+        selectionReactCancel:false, // 是否不触发 selection的逻辑
+        tableData:[],
+        tableDataIds:[],
+        total:0,
+        dataLoading:false,
+        tableDataCheckedList:[],
+        // 添加到指标库
+        existIndexList:[],
+        checkFailShow:false,
+        indexAllExist:false,//指标是否都存在
+        edbIndexDatas:[],
+        edbTableHeadKey: [
+          "ClassifyId",
+          "Unit",
+          "Frequency",
+          "EdbName",
+          "EdbCode",
+        ],
+        edbTableHeadData:new Map([
+          ["ClassifyId", "所属目录"],
+          ["Unit", "单位"],
+          ["Frequency", "频度"],
+          ["EdbName", "指标名称"],
+          ["EdbCode", "指标ID"],
+        ]),
+        BatchList:[],
+        addToEbdDiaShow:false,
+        highLightIndex:[],
+        saveLoading:false
+      }
+    },
+    computed:{
+      addBaseDisable(){
+        return !(this.isCheckAll || this.isCheckIndeterminate ) || (!(this.tableData && this.tableData.length>0))
+      }
+    },
+    watch:{
+      edbCheckList(value){
+        this.hasEdbFilterChange=true
+      },
+      typesList(value){
+        if(value && value.length>0){
+          this.searchParams.Types = value.join(',')
+        }else{
+          this.searchParams.Types=''
+        }
+      }
+    },
+    created() {
+      this.getList()
+      this.getYsClassifyList()
+    },
+    mounted(){
+      // el-select 的@blur绑定事件不生效代替方法
+      this.$refs.edbFilterRef.$refs.input.blur=()=>{
+        if(this.hasEdbFilterChange){
+          this.searchList()
+          this.hasEdbFilterChange=false
+        }
+      }
+    },
+    methods: {
+      //获取有色数据库数据列表
+      getList(type){
+        if(this.edbCheckList && this.edbCheckList.length>0){
+          this.searchParams.IndexCodes = this.edbCheckList.join(',')
+        }else{
+          this.searchParams.IndexCodes = ''
+        }
+        smmDataInterface.getYsDataList(this.searchParams).then(res=>{
+          if(res.Ret == 200){
+            this.tableData=res.Data.List || []
+            this.total = res.Data.Paging.Totals || 0
+            if(this.tableData.length>0){
+              this.tableDataIds = this.tableData.map(it => it.IndexCode)
+            }else{
+              this.tableDataIds = []
+            }
+            if(type == 'adjustSelection'){
+              this.adjustSelection()
+            }else{
+              if(this.searchParams.Types || this.searchParams.Frequency || this.searchParams.DataState || this.searchParams.IndexCodes){
+                this.isCheckAll=true
+                this.checkAllStatus=true
+                this.isCheckIndeterminate=false
+              }else{
+                this.isCheckAll=false
+                this.checkAllStatus=false
+                this.isCheckIndeterminate=false
+              }
+              this.listCheckAllChange(this.isCheckAll)
+            }
+          }
+        })
+      },
+      // 获取有色数据库分类列表
+      getYsClassifyList(){
+        smmDataInterface.getYsTypeDataList().then(res=>{
+          if(res.Ret == 200){
+            this.classifyList=res.Data || []
+          }
+        })
+
+      },
+      // 指标名称的远程搜索方法
+      remoteMethod(text){
+        this.forSearchParams.Keyword = text.trim()
+        this.forSearchParams.CurrentIndex = 1;
+        if(this.forSearchParams.Keyword){
+          this.searchApi()
+        }else{
+          this.edbFilterDataList = [];
+        }
+      },
+      searchApi(){
+        smmDataInterface.getYsDataList(this.forSearchParams).then(res=>{
+          if(res.Ret == 200){
+            let arr=res.Data.List || []
+            let {Paging}=res.Data
+            this.searchHaveMore = this.forSearchParams.CurrentIndex < Paging.Pages;
+            this.edbFilterDataList = this.forSearchParams.CurrentIndex === 1 ? arr : this.edbFilterDataList.concat(arr);
+          }
+        })
+      },
+      searchLoad(){
+        if(!this.searchHaveMore) return;
+        this.forSearchParams.CurrentIndex++
+			  this.searchApi();
+      },
+	    removeEdbFilterTag: _.throttle(function() {
+        this.searchList()
+		  },1000),
+      searchList(type){
+        this.searchParams.CurrentIndex=1
+        this.getList(type)
+      },
+      pageNumberChange(page_no){
+        this.searchParams.CurrentIndex=page_no
+        this.getList('adjustSelection')
+      },
+      sortChange({prop,order}){
+        this.searchParams.SortType = order?order==='ascending'?'asc':'desc':''
+        this.searchParams.SortParam = order?prop:''
+        this.searchList('adjustSelection')
+      },
+      selectionChange(val){
+        if(this.selectionReactCancel) return 
+        // selectAllHandle的触发在selectionChange后面,将selectionChange的逻辑延迟一下
+        setTimeout(()=>{
+          // 去重
+          let duplicateArr = Array.from(new Set(this.tableDataCheckedList))
+          
+          if((duplicateArr.length == this.total && (!this.checkAllStatus))
+          || (duplicateArr.length == 0 && this.checkAllStatus)){
+            this.isCheckAll = true
+            this.isCheckIndeterminate=false
+          }else if((duplicateArr.length == 0 && (!this.checkAllStatus))
+          || (duplicateArr.length == this.total && this.checkAllStatus)){
+            this.isCheckAll = false
+            this.isCheckIndeterminate=false
+          }else{
+            this.isCheckAll = false
+            this.isCheckIndeterminate=true
+          }
+        },1)
+
+      },
+      selectHandle(selection,row){
+        if(this.selectionReactCancel) return 
+
+        let check = false; // 从tableDataCheckedList 添加还是删除
+        if(selection.some(it => it.IndexCode == row.IndexCode)){
+          // 勾选
+          if(this.checkAllStatus){
+            check=false
+          }else{
+            check=true
+          }
+        }else{
+          // 取消勾选
+          if(this.checkAllStatus){
+            check=true
+          }else{
+            check=false
+          }
+        }
+        if(check){
+          this.tableDataCheckedList.push(row.IndexCode)
+        }else{
+          this.tableDataCheckedList=this.tableDataCheckedList.filter(it => it!=row.IndexCode)
+        }
+      },
+      selectAllHandle(selection){
+        if(this.selectionReactCancel) return 
+
+        let check = false; // 从tableDataCheckedList 添加还是删除
+        if(selection && selection.length>0){
+          // 全选
+          if(this.checkAllStatus){
+            check=false
+          }else{
+            check=true
+          }
+        }else{
+          // 全不选
+          if(this.checkAllStatus){
+            check=true
+          }else{
+            check=false
+          }
+        }
+        if(check){
+          this.tableDataCheckedList =  [...this.tableDataCheckedList,...this.tableDataIds]
+        }else{
+          this.tableDataCheckedList = this.tableDataCheckedList.filter(it => !this.tableDataIds.includes(it))
+        }
+      },
+      listCheckAllChange(value){
+        this.tableDataCheckedList=[]
+        this.checkAllStatus=value
+        if(value){
+          // 全选
+          this.$refs.edbDataRef && this.$refs.edbDataRef.clearSelection()
+          this.$refs.edbDataRef && this.$refs.edbDataRef.toggleAllSelection()
+        }else{
+          //全不选
+          this.$refs.edbDataRef && this.$refs.edbDataRef.clearSelection()
+        }
+      },
+      adjustSelection(){
+        this.selectionReactCancel=true
+        if(!this.checkAllStatus){
+          this.tableDataCheckedList.map(it =>{
+            let row = this.tableData.find(da => da.IndexCode==it)
+            if(row){
+              setTimeout(()=>{
+                this.$refs.edbDataRef.toggleRowSelection(row,true)
+              },10)
+            }
+          })
+        }else{
+          this.$refs.edbDataRef.toggleAllSelection()
+          this.tableDataCheckedList.map(it =>{
+            let row = this.tableData.find(da => da.IndexCode==it)
+            if(row){
+              setTimeout(()=>{
+                this.$refs.edbDataRef.toggleRowSelection(row,false)
+              },50)
+            }
+          })
+        }
+        setTimeout(()=>{
+          this.selectionReactCancel=false
+        },50)
+      },
+      // 添加指标库
+      addToEdbBase:_.throttle(function() {
+        if(this.dataLoading || this.addBaseDisable) return 
+        let params={
+          Types:this.searchParams.Types,
+          Frequency:this.searchParams.Frequency,
+          DataState:this.searchParams.DataState,
+          SelectAll:this.checkAllStatus,
+          SelectCode:this.searchParams.IndexCodes,
+          EdbCode:this.tableDataCheckedList.join(',')
+        }
+        // return 
+        smmDataInterface.getYsEdbCodeCheck(params).then(res=>{
+          if(res.Ret == 200){
+            // console.log(res,'res');
+            if(res.Data.IndexExist){
+              // 有重复
+              this.existIndexList=[]
+              let existEdbInfo=res.Data.ExistEdbInfo || []
+              let text=''
+              existEdbInfo.map(item =>{
+                text=`${item.EdbName}(${item.EdbCode})`
+                this.existIndexList.push({text,code:item.UniqueCode,id:item.EdbInfoId,Types:item.ClassifyId,edbCode:item.EdbCode})
+              })
+              this.checkFailShow=true
+              this.indexAllExist=res.Data.ExistAll+'' =='false' ? false :res.Data.ExistAll || true
+            }else{
+              // 没有重复
+              this.getTargetData()
+            }
+          }
+        })
+      },300),
+      edbCheckFinished(){
+        if(!this.indexAllExist){
+          this.getTargetData()
+        }
+        this.checkFailShow=false
+      },
+      getTargetData(){
+        this.dataLoading=true
+        this.getMenu()
+        this.getTargetUnitList()
+        let existEdbList = this.existIndexList.map(item => item.edbCode)
+        let afterFilterList=[]
+        if(this.checkAllStatus){
+          afterFilterList = Array.from(new Set([...this.tableDataCheckedList,...existEdbList]))
+        }else{
+          afterFilterList= this.tableDataCheckedList.filter(item => !existEdbList.includes(item))
+        }
+
+        // return 
+        let params={
+          Types:this.searchParams.Types,
+          Frequency:this.searchParams.Frequency,
+          DataState:this.searchParams.DataState,
+          SelectAll:this.checkAllStatus,
+          SelectCode:this.searchParams.IndexCodes,
+          EdbCode:afterFilterList.join(',')
+        }
+        smmDataInterface.getYsEdbCodeSearch(params).then(res => {
+          if(res.Ret == 200){
+            let stockList = res.Data.StockSearchList || []
+            this.edbIndexDatas=[]
+            this.BatchList=[]
+            stockList.map((item,index) =>{
+              let params={
+                Source:11,
+                ClassifyId:0,
+                Unit:item.Unit,
+                Frequency:this.edbFrequencyList.includes(item.Frequency)?item.Frequency:'',
+                EdbName:item.EdbName,
+                EdbCode:item.EdbCode,
+              }
+              this.BatchList.push(params)
+              let datas = item.DataList || []
+              let datasLength = datas.length
+              if( datasLength<10){
+                for (let i = datasLength; i < 10; i++) {
+                  datas.push({DataTime:'',Value:''})													
+                }
+              }
+              this.edbIndexDatas.push({...params,DataList:datas})
+            })
+
+            let edbIndexDataLength = this.edbIndexDatas.length
+            //填充空的列
+            for (let i = edbIndexDataLength; i < 4; i++) {
+              this.edbIndexDatas.push({
+                Source:0,
+                ClassifyId:0,
+                Unit:'',
+                Frequency:'',
+                EdbName:'',
+                EdbCode:'',
+                DataList:new Array(10).fill({DataTime:'',Value:''}),
+              })
+            }
+            this.addToEbdDiaShow=true
+          }
+        }).finally(()=>{
+          this.dataLoading=false
+        })
+      },
+      /* 获取目录结构 */
+      getMenu() {
+        dataBaseInterface.menuListV3().then(res => {
+          if(res.Ret === 200) {
+            this.options = res.Data.AllNodes || [];
+            this.filterNodes(this.options);
+          }
+        })
+      },
+      // 递归改变目录结构
+      filterNodes(arr) {
+        arr.length && arr.forEach(item => {
+          item.Children.length && this.filterNodes(item.Children)
+          if(!item.Children.length) {
+            item.Children=null
+          }
+        })
+      },
+      // 获取指标单位
+      async getTargetUnitList(){
+        let res=await dataInterence.getTargetUnitList()
+        if(res.Ret===200){
+          this.unitList=res.Data&&res.Data.map(item=>{
+            return {value:item}
+          })
+        }
+      },
+      //搜索单位
+      querySearchUnit(queryString, cb){
+        let results = queryString ? this.unitList.filter(item=>item.value.indexOf(queryString) === 0) : this.unitList;
+        // 调用 callback 返回建议列表的数据
+        cb(results);
+      },
+      existIndexClick({code,id,classifyId}){
+        const { href } = this.$router.resolve({ path: '/database',query:{code,id,classifyId}});
+        window.open(href, '_blank');
+      },
+      ysEdbSaveHandle(){
+        let flag = this.edbIndexDatas.some(it => {
+				  return it.Source && ((!it.ClassifyId) || (!it.Unit) || (!it.Frequency) || (!it.EdbName) || (!it.EdbCode))
+        })
+        if(flag){
+          this.$message.warning("指标信息未填写完整")
+          return 
+        }
+        this.BatchList.map((it,ind) => {
+          it.ClassifyId = this.edbIndexDatas[ind].ClassifyId
+          it.EdbCode = this.edbIndexDatas[ind].EdbCode
+          it.EdbName = this.edbIndexDatas[ind].EdbName
+          it.Frequency = this.edbIndexDatas[ind].Frequency
+          it.Unit = this.edbIndexDatas[ind].Unit
+        })
+        this.saveLoading=true
+        // console.log({BatchList:this.BatchList});
+        // return 
+        smmDataInterface.ysEdbAddBatch({BatchList:this.BatchList}).then(res=>{
+          if(res.Ret == 200){
+            this.$message.success("添加指标成功")
+            let {href} = this.$router.resolve({
+              path:'/database',
+              query:{code:res.Data.UniqueCode,id:res.Data.EdbInfoId,classifyId:res.Data.ClassifyId}
+            });
+            window.open(href,'_blank');
+            this.addToEbdDiaShow=false	
+          }else if(res.Ret == 403){
+            this.highLightIndex=res.Data?res.Data.ExistEdbName || []:[]
+          }
+        }).finally(()=>{
+          this.saveLoading=false
+        })
+      }
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  #YS-data-container{
+    // min-width: 1360px;
+    position: relative;
+    box-sizing: border-box;
+    .search-zone{
+      background-color: white;
+      display: flex;
+      align-items: flex-start;
+      justify-content: space-between;
+      padding: 20px 20px 10px;
+      border: 1px solid #C8CDD9;
+      margin-bottom: 20px;
+      .search-row{
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        .search-item{
+          width: 240px;
+          margin: 0 20px 10px 0;
+        }
+      }
+
+    }
+    .table-zone{
+      padding: 20px 20px 70px;
+      background-color: white;
+      border: 1px solid #C8CDD9;
+      min-height: calc(100vh - 215px);
+      box-sizing: border-box;
+    }
+    .table-page{
+      position: absolute;
+      bottom: 20px;
+      right: 20px;
+    }
+    .edb-table-box{
+      padding: 0 40px;
+      .edb-table-preview {
+        overflow: auto;
+        height: calc(100% - 124px);
+        &::-webkit-scrollbar {
+          width: 5px !important;
+        }
+        table {
+          border-color: #dcdfe6;
+          border-bottom: 1px solid #dcdfe6;
+          border-right: 1px solid #dcdfe6;
+          border-collapse: separate;
+        }
+        thead {
+          position: sticky;
+          z-index: 2;
+          top: 0;
+        }
+        td {
+          min-width: 100px;
+          max-width: 100px;
+          height: 30px;
+          // text-align: center;
+          color: #333;
+          box-sizing: border-box;
+          padding: 0 5px;
+          background-color: #fff;
+          border-top: 1px solid #dcdfe6;
+          border-left: 1px solid #dcdfe6;
+        }
+        .exist-highlight{
+          border: red solid 1px;
+        }
+      }
+      .dia-bot {
+        margin: 30px 0;
+        display: flex;
+        justify-content: center;
+      }
+    }
+
+    .check-fail-box{
+      padding: 0 0 15px 40px;	
+      color: #333333;
+      .exist-index-item{
+        cursor: pointer;
+        &:hover{
+          text-decoration: underline;
+        }
+      }
+      .check-fail-button{
+        margin-top: 80px;
+        display: flex;
+        justify-content: flex-end;
+      }
+    }
+  }
+</style>
+<style lang="scss">
+  #YS-data-container{
+    .el-select{
+      .el-tag{
+        max-width: 100%;
+        display: inline-flex;
+        align-items: center;
+        .el-select__tags-text{
+          flex-grow: 1;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+        .el-tag__close{
+          min-width: 16px;
+        }
+      }
+      .el-select__input{
+        min-width: 80px;
+      }
+    }
+    .edb-table-preview{
+      border: 1px solid #dcdfe6;
+      .el-cascader{
+        width: 100%;
+      }
+      .el-autocomplete{
+        width: 100%;
+      }
+      .el-select{
+        width: 100%;
+      }
+      .el-input{
+        width: 100%;
+        input{
+          border: none;
+          padding-left: 7px;
+          font-size: 14px;
+        }
+      }
+    }
+  }
+
+</style>

+ 4 - 3
src/views/dataSource_manage/components/DelEDBTable.vue

@@ -149,11 +149,12 @@ export default {
         // 设置列的宽度
         getColWidth(key){
             const widthMap={
-                DataUpdateTime:'150px',
-                ErDataUpdateDate:'200px',
+                EdbCode:'180px',
+                CreateTime:'150px',
                 StartDate:'150px',
                 LatestDate:'150px',
-                CreateTime:'150px'
+                DataUpdateTime:'150px',
+                ErDataUpdateDate:'120px',
             }
 
             return widthMap[key]

+ 5 - 8
src/views/dataSource_manage/components/DetailTable.vue

@@ -143,7 +143,7 @@ export default {
             checkAll:false,
             tableColOpts:[],
             tableData:[],
-            pageSize:15,
+            pageSize:10,
             page:1,
             total:0,
             tableLoading:false,
@@ -260,14 +260,11 @@ export default {
         // 设置列的宽度
         getColWidth(key){
             const widthMap={
-                EdbNameSource:'120px',
-                DataUpdateTime:'120px',
+                EdbCode:'150px',
+                LatestDate:'150px',
+                DataUpdateTime:'180px',
                 ErDataUpdateDate:'200px',
-                NeedRefresh:'200px',
-                HasRefresh:'200px',
-                SourceUpdateTime:'120px',
-                DataUpdateResult:'120px',
-                SourceUpdateFailedReason:'120px'
+                UpdateTime:'180px'
             }
 
             return widthMap[key]

+ 68 - 10
src/views/dataSource_manage/components/EDBInfoChangeTable.vue

@@ -1,6 +1,22 @@
 <template>
     <div class="edbinfo-table-wrap">
-        <div>
+        <div class="top-wrap">
+            <div class="filter-box">
+                <el-select
+                    v-model="filterState.changeType"
+                    placeholder="请选择变更类型"
+                    clearable
+                    style="width:250px"
+                    @change="handleRefreshList"
+                >
+                    <el-option
+                        v-for="item in changeTypeOpt"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                    />
+                </el-select> 
+            </div>
             <img src="~@/assets/img/data_m/set_icon.png" alt="" style="cursor: pointer;float:right" @click="showSetTableCols=true">
         </div>
         <el-table
@@ -30,6 +46,10 @@
                         <i class="el-icon-info" v-if="tipsKeysArr.includes(col.ColumnKey)"></i>
                     </el-tooltip>
                 </template>
+                <template slot-scope="scope">
+                    <span v-if="col.ColumnKey==='UpdateType'">{{getUpdateTypeText(scope.row)}}</span>
+                    <span v-else>{{scope.row[col.ColumnKey]}}</span>
+                </template>
             </el-table-column>
             <div slot="empty">
                 <tableNoData text="暂无数据"/>
@@ -68,7 +88,22 @@ export default {
                 dateVal:this.$moment().format('YYYY-MM-DD')||'',
                 sortKey:'',
                 sortType:'',
+                changeType:''
             },
+            changeTypeOpt:[
+                {
+                    label:'基础信息变更',
+                    value:1,
+                },
+                {
+                    label:'数据明细变更',
+                    value:0,
+                },
+                {
+                    label:'新增指标',
+                    value:2,
+                }
+            ],
 
             showSetTableCols:false,
         }
@@ -86,6 +121,7 @@ export default {
                 SortParam:this.filterState.sortKey,
                 SortType:this.filterState.sortType,
                 CreateTime:this.filterState.dateVal,
+                UpdateType:this.filterState.changeType===''?-1:this.filterState.changeType
             }).then(res=>{
                 this.tableLoading=false
                 if(res.Ret===200){
@@ -140,6 +176,16 @@ export default {
             this.handleRefreshList()
         },
 
+        getUpdateTypeText(e){
+            let str=''
+            this.changeTypeOpt.forEach(item=>{
+                if(item.value===e.UpdateType){
+                    str=item.label
+                }
+            })
+            return str
+        },
+
         // table说明文案
         getTableHeadTips(key){
             const tipsMap={
@@ -153,13 +199,12 @@ export default {
         // 设置列的宽度
         getColWidth(key){
             const widthMap={
-                EdbName:'150px',
-                DataUpdateTime:'150px',
-                ErDataUpdateDate:'200px',
+                EdbCode:'150px',
+                CreateTime:'150px',
                 StartDate:'150px',
                 LatestDate:'150px',
-                CreateTime:'150px',
-                EdbNameSource:'150px'
+                DataUpdateTime:'150px',
+                ErDataUpdateDate:'200px',
             }
 
             return widthMap[key]
@@ -172,11 +217,24 @@ export default {
 .edbinfo-table-wrap{
     .top-wrap{
         display: flex;
-        justify-content: flex-end;
+        justify-content: space-between;
+        align-items: flex-start;
         margin-bottom: 30px;
-        .set-icon{
-            width: 40px;
-            cursor: pointer;
+        .filter-box{
+            margin-right: 100px;
+            display: flex;
+            flex-wrap: wrap;
+            align-items: center;
+            gap: 10px 20px;
+        }
+        .right-opt-box{
+            display: flex;
+            align-items: center;
+            gap: 20px;
+            .set-icon{
+                width: 40px;
+                cursor: pointer;
+            }
         }
     }
 }

+ 138 - 4
src/views/dataSource_manage/components/GLRefreshFailDetail.vue

@@ -1,4 +1,4 @@
-<template>
+ <template>
     <el-dialog
         title="更新失败指标详情"
         :visible.sync="show"
@@ -40,6 +40,9 @@
                     prop="Num"
                     align="center"
                 >
+                    <template slot-scope="scope">
+                        <span style="color:#409EFF;cursor: pointer;" @click="handleShowFailDetail(scope.row)">{{scope.row.Num}}</span>
+                    </template>
                 </el-table-column>
                 <el-table-column
                     label="原因"
@@ -71,6 +74,51 @@
                 <div>5、打开文件目录下的一个Excel,选中“钢联数据2.0”插件,点击“更新所有页”按钮后,等待十几秒,查看Excel左下角是否提示额度超限。</div>
             </div>
         </el-dialog>
+
+        <!-- 失败详情 -->
+        <el-dialog
+            title="指标详情"
+            :visible.sync="showEDBList"
+            :close-on-click-modal="false"
+            :center="true"
+            v-dialogDrag
+            :append-to-body="true"
+            width="80vw"
+        >
+            <div class="gl-refresh-fail-edb-list-wrap">
+                <p class="info"> 
+                    <span>终端名称:{{info.Name}} </span>
+                    <span>文件夹路径:{{info.DirPath}} </span>
+                    <span>频度:{{activeFrequency}} </span>
+                    <span>原因:{{activeReason}} </span>
+                </p>
+                <el-table
+                    :data="edbList"
+                    border
+                    height="700"
+                >
+                    <el-table-column
+                        v-for="col in edbListTableCol"
+                        :key="col.key"
+                        :label="col.label"
+                        :prop="col.key"
+                        align="center"
+                    />
+                    <div slot="empty">
+                        <tableNoData text="暂无数据"/>
+                    </div>
+                </el-table>
+                <el-pagination
+                    layout="total,prev,pager,next,jumper"
+                    background
+                    @current-change="handleCurrentChange"
+                    :page-size="pageSize"
+                    :total="total"
+                    style="float: right;margin-top:20px"
+                />
+            </div>
+        </el-dialog>
+
     </el-dialog>
     
 </template>
@@ -106,7 +154,53 @@ export default {
     data() {
         return {
             info:{},
-            showBZ:false
+            showBZ:false,
+
+            showEDBList:false,
+            edbList:[],
+            edbListTableCol:[
+                {
+                    label:'指标全称',
+                    key:'EdbName'
+                },
+                {
+                    label:'指标编码',
+                    key:'EdbCode'
+                },
+                {
+                    label:'起始时间',
+                    key:'StartDate'
+                },
+                {
+                    label:'最新日期',
+                    key:'LatestDate'
+                },
+                {
+                    label:'最新值',
+                    key:'LatestValue'
+                },
+                {
+                    label:'终端编码',
+                    key:'TerminalCode'
+                },
+                {
+                    label:'创建人',
+                    key:'SysUserRealName'
+                },
+                {
+                    label:'频度',
+                    key:'Frequency'
+                },
+                {
+                    label:'单位',
+                    key:'Unit'
+                },
+            ],
+            activeFrequency:'',
+            activeReason:'',
+            page:1,
+            pageSize:10,
+            total:0
         }
     },
     methods: {
@@ -123,7 +217,37 @@ export default {
                     this.info=res.Data
                 }
             })
-        }
+        },
+
+        handleShowFailDetail(e){
+            this.page=1
+            this.edbList=[]
+            this.activeFrequency=e.Frequency
+            this.activeReason=e.SourceUpdateFailedReason
+            this.getFailDetailList()
+            this.showEDBList=true
+        },
+
+        async getFailDetailList(){
+            const res=await apiDataSource.updateFailDetailList({
+                CreateTime:this.date,
+                TerminalCode:this.TerminalCode,
+                Frequency:this.activeFrequency,
+                SourceUpdateFailedReason:this.activeReason,
+                CurrentIndex:this.page,
+                PageSize:this.pageSize,
+            })
+            if(res.Ret===200){
+                const arr=res.Data.List||[]
+                this.edbList=arr
+                this.total=res.Data.Paging.Totals
+            }
+        },
+
+        handleCurrentChange(e){
+            this.page=e
+            this.getFailDetailList()
+        },
     },
 }
 </script>
@@ -133,5 +257,15 @@ export default {
     color: #333;
     padding-bottom: 30px;
 }
-
+.gl-refresh-fail-edb-list-wrap{
+    padding-bottom: 80px;
+    .info{
+        color: #000;
+        margin-bottom: 20px;
+        span{
+            display: inline-block;
+            margin-right: 20px;
+        }
+    }
+}
 </style>

+ 3 - 3
src/views/dataSource_manage/components/StatisticTable.vue

@@ -22,10 +22,10 @@
                     <span>{{col.ColumnName}}</span>
                     <el-tooltip 
                         effect="dark" 
-                        :content="getTableHeadTips(col.ColumnKey)" 
                         placement="top-start"
                     >
                         <i class="el-icon-info" v-if="tipsKeysArr.includes(col.ColumnKey)"></i>
+                        <div slot="content" v-html="getTableHeadTips(col.ColumnKey)"></div>
                     </el-tooltip>
                 </template>
                 <template slot-scope="scope">
@@ -147,8 +147,8 @@ export default {
         // table说明文案
         getTableHeadTips(key){
             const tipsMap={
-                NeedRefreshNum:'今日该终端需定时刷新的指标数量',
-                HasRefreshNum:'今日该终端发起刷新任务的指标数量',
+                NeedRefreshNum:'1、今日该终端需定时刷新的指标数量<br>2、今日需刷新指标数=今日已更新指标数+今日更新失败指标数',
+                HasRefreshNum:'1、今日该终端发起刷新任务的指标数量<br>2、今日发起刷新任务指标数=今日已刷新成功指标数+今日刷新失败指标数',
                 RefreshSuccessNum:'今日该终端刷新成功的指标数量',
                 RefreshFailedNum:'今日该终端刷新失败的指标数量',
                 UpdateFailedNum:'今日该终端成功发起刷新任务,但指标数据未变化的指标数量',

+ 2 - 2
src/views/datasheet_manage/components/SheetExcel.vue

@@ -56,12 +56,12 @@ export default {
     copyData() {
       let rangeArr = luckysheet.getRangeArray('twoDimensional');
       let str = ''
-      rangeArr.forEach(item => {
+      rangeArr.forEach((item,r_index) => {
         let row = ''
         item.forEach((cell,index) => {
           row+= `${index!==0?'\t':''}${cell||''}`
         })
-        str+=`${row}\n`
+        str+= r_index===rangeArr.length-1 ? row : `${row}\n`
       });
       
       copyFit(str)

+ 7 - 1
src/views/datasheet_manage/customAnalysis/list.vue

@@ -171,7 +171,7 @@
                 <el-tooltip effect="dark" content="根据表格保存的最新内容,更新当前表格生成的所有指标" placement="top-start">
                     <li class="editsty" @click="refreshSheet" v-if="isSheetBtnShow('refresh')&&sheetDetailInfo.Button.RefreshEdbButton">
                       <img src="~@/assets/img/icons/refresh_blue_new.png"/>
-                      <span>刷新指标</span>
+                      <span>{{ isEdbReFreshLoading?'指标刷新中...':'刷新指标'}}</span>
                     </li>
                 </el-tooltip>
                 <li class="editsty" @click="saveHandle" v-if="isSheetBtnShow('save')&&sheetDetailInfo.Button.OpButton">
@@ -393,6 +393,8 @@ export default {
         '/sheetAnalysisList': 4,
       },
       saveTime:"",
+
+      isEdbReFreshLoading: false,//指标刷新
     };
   },
   watch: {
@@ -792,8 +794,12 @@ export default {
 
     /* 刷新表格 */
     refreshSheet: _.debounce(async function() {
+      if(this.isEdbReFreshLoading) return
+      
+      this.isEdbReFreshLoading = true;
       let res = await sheetInterface.sheetAnalysisInterface.sheetRefresh({ExcelInfoId: this.sheetDetailInfo.ExcelInfoId})
 
+      this.isEdbReFreshLoading = false;
       if(res.Ret !== 200) return 
       this.$message.success(res.Msg)
     },300),

+ 2 - 1
src/views/futures_manage/chartEditor.vue

@@ -100,6 +100,7 @@
 					<bar-option
 						v-if="!chart_code || (chart_code&&chartInfo.ChartType)"
 						ref="BarOptRef"
+						:chartInfo="chartInfo"
 						:edblist="tableData"
 						:datedata="commodityChartData"
 						:initData="initDateOptions"
@@ -331,7 +332,7 @@ export default {
 			let params = {
 				EdbInfoIdList: [
 					...this.tableData.map(_ => ({EdbInfoId: _.EdbInfoId,Name:'',Source: 1})),
-					{ EdbInfoId: this.chartInfo.futures_id,Name: '',Source: 2 }
+					{ EdbInfoId: Number(this.chartInfo.futures_id),Name: '',Source: 2 }
 				],
 				DateList: dateList.map(_ => ({
 					Type: _.Type,

+ 7 - 1
src/views/login_manage/EmailModel.vue

@@ -73,6 +73,7 @@ export default {
             timer:0,
             codeStr:'获取验证码',
             codeCountDown:60,
+            isGetNewCode:false,
             form:{
                 email:'',
                 picCode:'',
@@ -98,10 +99,13 @@ export default {
     methods: {
         checkForm(){
             //首先检查是否是重新获取验证码
-            if(this.codeStr==='重新获取'){
+            if(this.codeStr==='重新获取'&&!this.isGetNewCode){
                 //引导用户重新输入图形验证码
+                this.$message.warning('重新获取需再次输入图形验证码')
                 this.getCodePic()
                 this.form.picCode = ''
+                this.isGetNewCode = true
+                return
             }
             //检查邮箱和图形验证码是否正确
             const {email,picCode} = this.form
@@ -123,7 +127,9 @@ export default {
             }).then(res=>{
                 if(res.Ret!==200) return 
                 this.$message.success('验证码已发送')
+                this.isGetNewCode = false
                 //60秒倒计时
+                this.codeCountDown = 60
                 this.countDown()
                 this.timer = setInterval(()=>{
                     this.countDown()

+ 8 - 5
src/views/login_manage/MobileModel.vue

@@ -83,6 +83,7 @@ export default {
             timer:0,
             codeStr:'获取验证码',
             codeCountDown:60,
+            isGetNewCode:false,
             form:{
                 mobile:'',
                 picCode:'',
@@ -109,10 +110,13 @@ export default {
     methods: {
         checkForm(){
             //首先检查是否是重新获取验证码
-            if(this.codeStr==='重新获取'){
+            if(this.codeStr==='重新获取'&&!this.isGetNewCode){
                 //引导用户重新输入图形验证码
+                this.$message.warning('重新获取需再次输入图形验证码')
                 this.getCodePic()
                 this.form.picCode = ''
+                this.isGetNewCode = true
+                return
             }
             //检查手机号和图形验证码是否正确
             const {picCode,mobile} = this.form
@@ -124,10 +128,7 @@ export default {
                 this.$message.warning('请输入正确的手机号')
                 return
             }
-            /* this.countDown()
-                this.timer = setInterval(()=>{
-                    this.countDown()
-                },1000) */
+
             //通过请求发送验证码
             departInterence.getCodeVerify({
                 VerifyType:1,
@@ -139,7 +140,9 @@ export default {
             }).then(res=>{
                 if(res.Ret!==200) return 
                 this.$message.success('验证码已发送')
+                this.isGetNewCode = false
                 //60秒倒计时
+                this.codeCountDown = 60
                 this.countDown()
                 this.timer = setInterval(()=>{
                     this.countDown()

+ 1 - 1
src/views/login_manage/modelMixins.js

@@ -21,7 +21,7 @@ export default {
         },
         countDown(){
             /* if(!this.timer) return */
-            console.log('click down')
+            //console.log('click down')
             this.codeCountDown--
             this.codeStr=`重新获取(${this.codeCountDown})秒`
             if(this.codeCountDown<=0){

+ 84 - 8
src/views/mychart_manage/index.vue

@@ -48,8 +48,8 @@
       </div>
       <div class="left-bottom">
         <div class="public-classify" v-if="publicClassifyList.length">
-          <h3 class="classify-type">公共图库</h3>
-          <ul class="public-ul">
+          <h3 @click="expandPublic = !expandPublic" class="classify-type">公共图库<span><i :class="{'el-icon-arrow-down':!expandPublic,'el-icon-arrow-up':expandPublic}"></i></span></h3>
+          <!-- <ul class="public-ul">
             <li
               :class="[
                 'classify-item',
@@ -74,7 +74,32 @@
                 </el-dropdown>
               </div>
             </li>
-          </ul>
+          </ul> -->
+          <div class="tree-wrap" v-show="expandPublic">
+                <el-tree
+                    ref="catalogTree"
+                    class="catalog-tree other-tree"
+                    empty-text="暂无图库"
+                    :data="publicClassifyList"
+                    node-key="nodeKeyId"
+                    :expand-on-click-node="false"
+                    @current-change="(data,node)=>{nodeChange(data,node)}"
+                    >
+                    <div class="custom-tree-node" slot-scope="{ data,node }">
+                        <span class="tree-label">{{ data.name }}</span>
+                        <div class="right-item right-item-box" >
+                            <el-dropdown @command="handleCommand" trigger="click" v-if="isShowDropPublic&&data.MyChartClassifyId">
+                            <span class="el-dropdown-link"> 
+                                <i class="el-icon-more" style="font-size: 16px;transform: rotate(90deg);cursor: pointer"/>
+                            </span>
+                            <el-dropdown-menu slot="dropdown">
+                                <el-dropdown-item :command="{key:'copy'}" :disabled="select_classify_userid === roleId">复制</el-dropdown-item>
+                            </el-dropdown-menu>
+                            </el-dropdown>
+                        </div>
+                    </div>
+                </el-tree>
+            </div>
         </div>
         <div class="classify" v-if="classifyList.length">
           <h3 class="classify-type">我的图库</h3>
@@ -166,7 +191,7 @@
         
         <div class="cont-top">
           <span>共{{ total }}张图表</span>
-          <span v-if="ispublic === 1">分享人: {{ publicClassifyList.find(item => item.MyChartClassifyId === select_classify).RealName}}</span>
+          <span v-if="ispublic === 1">分享人: {{ RealName }}</span>
         </div>
         <div
           class="chart-public-list"
@@ -229,7 +254,7 @@
         </div>
       </div>
       <div v-else class="nodata">
-        <span v-if="ispublic === 1" class="sharer">分享人: {{ publicClassifyList.find(item => item.MyChartClassifyId === select_classify).RealName}}</span>
+        <span v-if="ispublic === 1" class="sharer">分享人: {{ RealName }}</span>
         <tableNoData text="暂无图表"/>
       </div>
     </div>
@@ -324,6 +349,8 @@ export default {
   },
   data() {
     return {
+      expandPublic:false,
+      RealName:'',
       haveData: true,
       isSlideLeft: false, //收起分类
       search_txt: '',
@@ -473,10 +500,36 @@ export default {
     getPublicClassify() {
       mychartInterface.publicClassify().then((res) => {
         if(res.Ret !== 200) return;
-        this.publicClassifyList = res.Data ? res.Data.List.map(item => ({
+        /* this.publicClassifyList = res.Data ? res.Data.List.map(item => ({
           ...item,
           fromPublic: 1
-        })) : [];
+        })) : []; */
+        this.publicClassifyList = res.Data?res.Data.List||[]:[]
+        /* this.publicClassifyList = [{
+            MenuAdminId:204,
+            MenuName:"ficc销售name",
+            Items:[{
+                MyChartClassifyId:370,
+                MyChartClassifyName:"图库2.0",
+                RealName:'ficc销售'
+            }]
+        }] */
+        this.publicClassifyList = this.publicClassifyList.map(list=>{
+            list.name = list.MenuName
+            list.nodeKeyId = 'list' + list.MenuAdminId
+            if(list.Items){
+                list.children = list.Items.map(item=>{
+                    return {
+                        ...item,
+                        ...{
+                            nodeKeyId:'item'+ item.MyChartClassifyId,
+                            name:item.MyChartClassifyName
+                        }
+                    }
+                })
+            }
+            return list
+        })
       })
     },
 
@@ -550,9 +603,28 @@ export default {
 			if(!this.search_have_more) return;
 			this.searchApi(this.current_search,++this.search_page);
 		},
-
+    nodeChange(data,node){
+        if(node.level===1){
+            this.select_classify = 0
+            this.haveData = false
+            this.total = 0
+            this.page_no = 1
+            this.chartList = []
+            this.ispublic=''
+            return
+        }
+        this.RealName = data.RealName
+        this.chooseClassify({
+            MyChartClassifyId:data.MyChartClassifyId,
+            fromPublic:1,
+            AdminId:data.AdminId
+        })
+    },
     /* 切换分类 */
     chooseClassify({MyChartClassifyId,fromPublic,AdminId}) {
+      if(fromPublic!==1){
+        this.$refs.catalogTree.setCurrentKey(null)
+      }
       this.select_classify = MyChartClassifyId;
       this.ispublic = fromPublic;
       this.select_classify_userid = AdminId;
@@ -936,6 +1008,7 @@ export default {
 };
 </script>
 <style lang="scss">
+@import "../chartFrame_manage/css/customTree.scss";
 .mychart_container {
   position: relative;
   .right-item-box {
@@ -1059,6 +1132,9 @@ export default {
         margin-bottom: 20px;
         /* .public-ul {
         } */
+        .tree-wrap{
+            padding:15px;
+        }
       }
     }
     .noclassify {

+ 4 - 0
src/views/operation_manage/AIQA/AIQA.vue

@@ -380,6 +380,10 @@ export default {
                 this.historyList.splice(this.historyList.length-1,1,msg)
             }).catch(()=>{
                 this.answerLoading=false
+                const msg = this.historyList[this.historyList.length-1]
+                msg.Answer = '回答超时,请重试!'
+                msg.isPlay = true
+                this.historyList.splice(this.historyList.length-1,1,msg)
             })
         },
         //获取窗口列表

+ 2 - 1
src/views/ppt_manage/mixins/layerMixins.js

@@ -54,9 +54,10 @@ export default {
       }
       //计算图层元素的百分比化的单位:基于.total-wrap而不是基于ppt-item
       const width = $('.total-wrap').width(),height = $('.total-wrap').height()
+      this.activeLayerEl = {...this.activeLayerEl,...options}
       const percentageShape = caclShapePercentage({layerWidth:width,layerHeight:height},this.activeLayerEl)
-      this.activeLayerEl = {...this.activeLayerEl,...percentageShape,...options}
       this.activeLayerEl.id = createRandomCode()
+      this.activeLayerEl = {...this.activeLayerEl,...percentageShape}
       layerEls.push(this.activeLayerEl)
       this.refleshLayerEl(layerEls)
     },

+ 9 - 1
src/views/ppt_manage/mixins/pptEditorMixins.js

@@ -16,6 +16,7 @@ export default{
       copyPages:[],//选中的ppt页
       copyPagesMap:{},//选中的ppt页Id
       savePagesArr:[],//存储上一次选中的ppt页
+      CoverContent:'',//自定义封面页内容
     }
   },
   directives: {
@@ -384,7 +385,14 @@ export default{
           //啥也不干
         }
       });
-    }
+    },
+    //保存修改的自定义封面页
+    saveCover2(info){
+        const {firstPage={},content=''} = info
+        this.firstPage = firstPage
+        this.CoverContent = content
+        this.isShowChooseCover = false
+    },
   },
   mounted(){
     document.addEventListener("keydown",this.handlePasteKey)

+ 88 - 35
src/views/ppt_manage/mixins/pptMixins.js

@@ -12,7 +12,7 @@ import futuresInterface from '@/api/modules/futuresBaseApi';
 import { fittingEquationInterface,statisticFeatureInterface,crossVarietyInterface } from '@/api/modules/chartRelevanceApi';
 import chartRelevanceApi from '@/api/modules/chartRelevanceApi.js';
 import { defaultOpts } from '@/utils/defaultOptions';
-import {formatPPTDate,checkPPTpageElemant,getStrSize,isShowPPTTitle} from '../newVersion/utils/untils.js';
+import {formatPPTDate,checkPPTpageElemant,getStrSize,isShowPPTTitle,toTextProps,toJson} from '../newVersion/utils/untils.js';
 import FormatOne from '../newVersion/components/formatPage/FormatOne.vue';
 import FormatTwo from '../newVersion/components/formatPage/FormatTwo.vue';
 import FormatThree from '../newVersion/components/formatPage/FormatThree.vue';
@@ -43,9 +43,10 @@ export default {
       pptCoverList:[],//配置ppt封面
       pptBgImage:'',//ppt背景图
       pptBackImage:'',//ppt封底
-      pptCoverCompenyName:'',//封面公司名称
-      pptCoverDepartName:'',//封面部门名称
-      pptCoverTextColor:'',//控制上面两个字段展示的颜色
+      pptCoverCompenyName:'',//封面公司名称 ETA1.4.9后不再使用
+      pptCoverDepartName:'',//封面部门名称 ETA1.4.9后不再使用
+      pptCoverTextColor:'',//控制上面两个字段展示的颜色 ETA1.4.9后不再使用
+      pptCoverContent:'',//自定义封面页的内容
 
 
       setEnName:false,
@@ -62,6 +63,54 @@ export default {
     }
   },
   methods: {
+    //配置自定义封面内容
+    setPPTCover(cover,pptCoverContent='',title=''){
+        let contentList = []
+        try{
+            contentList = JSON.parse(pptCoverContent)
+        }catch(e){
+            contentList=[]
+        }
+        //将contentList的内容通过addText写入,方法与图层写入文字一致
+        for(let i=0;i<contentList.length;i++){
+            const {
+                percentageTop,
+                percentageLeft,
+                percentageWidth,
+                percentageHeight,
+                richContent
+            } = contentList[i]
+            const position = {
+                x:percentageLeft*100+'%',
+                y:percentageTop*100+'%',
+                w:percentageWidth*100+'%',
+                h:percentageHeight*100+'%'
+            }
+            let textData = toTextProps(toJson(richContent))
+            cover.addText(textData,{
+                ...position,
+                margin:10,
+                fontSize:16*0.75,
+                valign:'top'
+            })
+        }
+        //不需要显示了
+        //若contentList为空,则在右下的位置显示标题
+        /* if(!contentList.length){
+            cover.addText(
+                [{text:title,options:{fontSize:28*0.75,breakLine:true}}],
+                {
+                    x:'38%',
+                    y:'50%',
+                    w:'60%',
+                    h:'28%',
+                    color:'ffffff',
+                    align:'center',
+                    fontFace:'SimHei'
+                })
+        } */
+        return cover
+    },
     //获取基本配置-ppt配置
     async getpptConfig(){
         const res = await etaBaseConfigInterence.getBaseConfig()
@@ -92,37 +141,41 @@ export default {
       }):await pptEnInterface.getpptDetail({PptId:id})
       if(res.Ret===200){
         const {
-			Content,
-			Title,
-			ReportType,
-			BackgroundImg,
-			PptDate,
-			TemplateType,
-      ReportId,
-      ModifyTime,
-      PublishTime,
-      Editor
-		} = res.Data
-      const pptDate = formatPPTDate(this.currentLang,PptDate)
-      let legalContent = JSON.parse(Content)
-      legalContent = legalContent.map(page=>{
-        page.elements = checkPPTpageElemant(page)
-        return page
-      })
-      this.result = {
-			status: 200,
-			content: legalContent,
-			FirstPage: {
-				Title,
-				ReportType,
-				BackgroundImg,
-				PptDate:pptDate,
-				TemplateType,
-			},
-      ReportId,
-      ModifyTime,
-      PublishTime,Editor
-		}
+            Content,
+            Title,
+            ReportType,
+            BackgroundImg,
+            PptDate,
+            TemplateType,
+            ReportId,
+            ModifyTime,
+            PublishTime,
+            Editor,
+            CoverContent
+        } = res.Data
+        const pptDate = formatPPTDate(this.currentLang, PptDate)
+        let legalContent = JSON.parse(Content)
+        legalContent = legalContent.map(page => {
+            page.elements = checkPPTpageElemant(page)
+            return page
+        })
+        this.pptCoverContent = CoverContent
+        this.result = {
+            status: 200,
+            content: legalContent,
+            FirstPage: {
+                Title,
+                ReportType,
+                BackgroundImg,
+                PptDate: pptDate,
+                TemplateType,
+            },
+            ReportId,
+            ModifyTime,
+            PublishTime,
+            Editor,
+            CoverContent
+        }
       }else{
         this.result = {status:'',content:'获取ppt数据失败!'}
       }

Некоторые файлы не были показаны из-за большого количества измененных файлов