Browse Source

Merge branch 'master' into v1.5_chartETA

jwyu 1 năm trước cách đây
mục cha
commit
f98cfa2131
37 tập tin đã thay đổi với 2439 bổ sung287 xóa
  1. 1 1
      .env.development
  2. 1 0
      index.html
  3. 1 0
      package.json
  4. 304 0
      public/froala_editor_zh_cn.js
  5. 2 1
      src/api/index.js
  6. 48 0
      src/api/reportEn.js
  7. BIN
      src/assets/imgs/icon_back.png
  8. 2 2
      src/assets/styles/common.scss
  9. 1 1
      src/assets/styles/vant.scss
  10. 54 14
      src/hooks/useFroalaEditor.js
  11. 2 1
      src/layouts/Index.vue
  12. 3 2
      src/router/index.js
  13. 10 10
      src/router/report.js
  14. 17 1
      src/router/reportEn.js
  15. 59 9
      src/views/report/AddReport.vue
  16. 54 7
      src/views/report/EditReport.vue
  17. 46 39
      src/views/report/List.vue
  18. 19 1
      src/views/report/PreviewDetail.vue
  19. 37 6
      src/views/report/ReportDayWeekAdd.vue
  20. 68 17
      src/views/report/chapter/Detail.vue
  21. 59 7
      src/views/report/chapter/List.vue
  22. 72 14
      src/views/report/chapter/components/EditChapterBaseInfo.vue
  23. 31 6
      src/views/report/chapter/components/EidtBaseInfo.vue
  24. 10 1
      src/views/report/components/AudioBox.vue
  25. 43 6
      src/views/report/components/EditReportBaseInfo.vue
  26. 30 0
      src/views/report/components/reportInsert/ETAChart.vue
  27. 12 0
      src/views/report/components/reportInsert/Index.vue
  28. 46 5
      src/views/report/components/reportInsert/MyETAChart.vue
  29. 33 0
      src/views/report/components/reportInsert/SandTableImg.vue
  30. 33 0
      src/views/report/components/reportInsert/SheetTableChart.vue
  31. 509 0
      src/views/reportEn/AddReport.vue
  32. 35 118
      src/views/reportEn/Detail.vue
  33. 120 10
      src/views/reportEn/List.vue
  34. 366 0
      src/views/reportEn/components/EditReportBaseInfo.vue
  35. 270 0
      src/views/reportEn/components/EditStrategyReport.vue
  36. 35 6
      src/views/reportEn/components/ListClassify.vue
  37. 6 2
      src/views/tabbar/Home.vue

+ 1 - 1
.env.development

@@ -1,5 +1,5 @@
 # 接口地址http://8.136.199.33:8610/v1   https://rddptest.hzinsights.com/adminapi
-VITE_APP_API_URL="https://rddptest.hzinsights.com/adminapi"
+VITE_APP_API_URL="http://8.136.199.33:8610/v1"
 # 路由根地址
 VITE_APP_BASE_URL="/"
 # 打包输入文件名

+ 1 - 0
index.html

@@ -12,5 +12,6 @@
     <script type="module" src="/src/main.js"></script>
     <script src="/jquery-3.6.0.min.js"></script>
     <script type='text/javascript' src='/froala_editor.pkgd.min.js'></script>
+    <script type='text/javascript' src='/froala_editor_zh_cn.js'></script>
   </body>
 </html>

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "highcharts": "^9.3.2",
     "himalaya": "^1.1.0",
     "html-to-image": "^1.11.11",
+    "html2canvas": "^1.4.1",
     "js-base64": "^3.7.5",
     "js-md5": "^0.7.3",
     "lodash": "^4.17.21",

+ 304 - 0
public/froala_editor_zh_cn.js

@@ -0,0 +1,304 @@
+/*!
+ * froala_editor v4.0.19 (https://www.froala.com/wysiwyg-editor)
+ * License https://froala.com/wysiwyg-editor/terms/
+ * Copyright 2014-2023 Froala Labs
+ */
+
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('froala-editor')) :
+  typeof define === 'function' && define.amd ? define(['froala-editor'], factory) :
+  (factory(global.FroalaEditor));
+}(this, (function (FE) { 'use strict';
+
+  FE = FE && FE.hasOwnProperty('default') ? FE['default'] : FE;
+
+  /**
+  * Simplified Chinese spoken in China.
+  */
+  FE.LANGUAGE['zh_cn'] = {
+    translation: {
+      // Place holder
+      'Type something': '输入内容',
+      // Basic formatting
+      'Bold': '粗体',
+      'Italic': '斜体',
+      'Underline': '下划线',
+      'Strikethrough': '删除线',
+      // Main buttons
+      'Insert': '插入',
+      'Delete': '删除',
+      'Cancel': '取消',
+      'OK': '确定',
+      'Back': '后退',
+      'Remove': '删除',
+      'More': '更多',
+      'Update': '更新',
+      'Style': '样式',
+      // Font
+      'Font Family': '字体',
+      'Font Size': '字号',
+      // Colors
+      'Colors': '颜色',
+      'Background': '背景',
+      'Text': '字体',
+      'HEX Color': '十六进制颜色',
+      // Paragraphs
+      'Paragraph Format': '段落格式',
+      'Normal': '正文',
+      'Code': '代码',
+      'Heading 1': '标题1',
+      'Heading 2': '标题2',
+      'Heading 3': '标题3',
+      'Heading 4': '标题4',
+      // Style
+      'Paragraph Style': '段落样式',
+      'Inline Style': '内联样式',
+      // Alignment
+      'Align': '对齐方式',
+      'Align Left': '左对齐',
+      'Align Center': '居中',
+      'Align Right': '右对齐',
+      'Align Justify': '两端对齐',
+      'None': '无',
+      // Lists
+      'Ordered List': '编号',
+      'Unordered List': '项目符号',
+      // Indent
+      'Decrease Indent': '减少缩进量',
+      'Increase Indent': '增加缩进量',
+      // Links
+      'Insert Link': '插入超链接',
+      'Open in new tab': '在新标签页中打开',
+      'Open Link': '打开超链接',
+      'Edit Link': '编辑超链接',
+      'Unlink': '删除超链接',
+      'Choose Link': '选择超链接',
+      // Images
+      'Insert Image': '插入图片',
+      'Upload Image': '上传图片',
+      'By URL': '通过 URL',
+      'Browse': '浏览',
+      'Drop image': '拖入图片',
+      'or click': '或点击',
+      'Manage Images': '管理图片',
+      'Loading': '加载中',
+      'Deleting': '删除中',
+      'Tags': '标签',
+      'Are you sure? Image will be deleted.': '图片将会被删除,是否确认?',
+      'Replace': '替换',
+      'Uploading': '上传中',
+      'Loading image': '图片加载中',
+      'Display': '显示',
+      'Inline': '嵌入型',
+      'Break Text': '上下型环绕',
+      'Alternative Text': '替换文字',
+      'Change Size': '改变大小',
+      'Width': '宽度',
+      'Height': '高度',
+      'Something went wrong. Please try again.': '发生错误,请重试。',
+      'Image Caption': '图片标题',
+      'Advanced Edit': '高级编辑',
+      // Video
+      'Insert Video': '插入视频',
+      'Embedded Code': '嵌入代码',
+      'Paste in a video URL': '粘贴视频网址',
+      'Drop video': '拖入视频',
+      'Your browser does not support HTML5 video.': '您的浏览器不支持 HTML5 视频。',
+      'Upload Video': '上传视频',
+      // Tables
+      'Insert Table': '插入表格',
+      'Table Header': '表头',
+      'Remove Table': '删除表格',
+      'Table Style': '表格样式',
+      'Horizontal Align': '水平对齐方式',
+      'Row': '行',
+      'Insert row above': '在上方插入',
+      'Insert row below': '在下方插入',
+      'Delete row': '删除行',
+      'Column': '列',
+      'Insert column before': '在左侧插入',
+      'Insert column after': '在右侧插入',
+      'Delete column': '删除列',
+      'Cell': '单元格',
+      'Merge cells': '合并单元格',
+      'Horizontal split': '水平分割',
+      'Vertical split': '垂直分割',
+      'Cell Background': '单元格背景',
+      'Vertical Align': '垂直对齐方式',
+      'Top': '靠上',
+      'Middle': '居中',
+      'Bottom': '靠下',
+      'Align Top': '靠上对齐',
+      'Align Middle': '居中对齐',
+      'Align Bottom': '靠下对齐',
+      'Cell Style': '单元格样式',
+      // Files
+      'Upload File': '上传文件',
+      'Drop file': '拖入文件',
+      // Emoticons
+      'Emoticons': '表情符号',
+      'Grinning face': '露齿笑脸',
+      'Grinning face with smiling eyes': '露齿笑到眯起眼',
+      'Face with tears of joy': '笑哭',
+      'Smiling face with open mouth': '张嘴微笑',
+      'Smiling face with open mouth and smiling eyes': '眯眼张嘴微笑',
+      'Smiling face with open mouth and cold sweat': '带冷汗的张嘴微笑',
+      'Smiling face with open mouth and tightly-closed eyes': '紧闭双眼张嘴微笑',
+      'Smiling face with halo': '带光环微笑',
+      'Smiling face with horns': '带牛角的微笑',
+      'Winking face': '眨眼',
+      'Smiling face with smiling eyes': '眯眼微笑',
+      'Face savoring delicious food': '馋',
+      'Relieved face': '如释重负',
+      'Smiling face with heart-shaped eyes': '桃心眼微笑',
+      'Smiling face with sunglasses': '戴太阳镜微笑',
+      'Smirking face': '得意地笑',
+      'Neutral face': '中性脸',
+      'Expressionless face': '面无表情',
+      'Unamused face': '不高兴',
+      'Face with cold sweat': '冷汗',
+      'Pensive face': '沉思',
+      'Confused face': '迷惑',
+      'Confounded face': '困惑',
+      'Kissing face': '嘴巴嘟嘟',
+      'Face throwing a kiss': '飞吻',
+      'Kissing face with smiling eyes': '眯眼接吻',
+      'Kissing face with closed eyes': '闭眼接吻',
+      'Face with stuck out tongue': '吐舌',
+      'Face with stuck out tongue and winking eye': '眨眼吐舌',
+      'Face with stuck out tongue and tightly-closed eyes': '眯眼吐舌',
+      'Disappointed face': '失望',
+      'Worried face': '担心',
+      'Angry face': '生气',
+      'Pouting face': '撅嘴',
+      'Crying face': '大哭',
+      'Persevering face': '坚强',
+      'Face with look of triumph': '扬眉吐气',
+      'Disappointed but relieved face': '失望',
+      'Frowning face with open mouth': '皱眉',
+      'Anguished face': '痛苦',
+      'Fearful face': '害怕',
+      'Weary face': '疲惫',
+      'Sleepy face': '困了',
+      'Tired face': '累了',
+      'Grimacing face': '扭曲脸',
+      'Loudly crying face': '大哭',
+      'Face with open mouth': '张开嘴',
+      'Hushed face': '安静',
+      'Face with open mouth and cold sweat': '冷汗',
+      'Face screaming in fear': '害怕尖叫',
+      'Astonished face': '惊讶',
+      'Flushed face': '脸红',
+      'Sleeping face': '熟睡',
+      'Dizzy face': '眩晕',
+      'Face without mouth': '没有嘴的脸',
+      'Face with medical mask': '口罩脸',
+      // Line breaker
+      'Break': '换行',
+      // Math
+      'Subscript': '下标',
+      'Superscript': '上标',
+      // Full screen
+      'Fullscreen': '全屏',
+      // Horizontal line
+      'Insert Horizontal Line': '插入水平线',
+      // Clear formatting
+      'Clear Formatting': '清除格式',
+      // Save
+      'Save': '保存',
+      // Undo, redo
+      'Undo': '撤消',
+      'Redo': '恢复',
+      // Select all
+      'Select All': '全选',
+      // Code view
+      'Code View': '代码视图',
+      // Quote
+      'Quote': '引用',
+      'Increase': '增加引用级别',
+      'Decrease': '减少引用级别',
+      // Quick Insert
+      'Quick Insert': '快速插入',
+      // Spcial Characters
+      'Special Characters': '特殊字符',
+      'Latin': '拉丁字母',
+      'Greek': '希腊字母',
+      'Cyrillic': '西里尔字母',
+      'Punctuation': '标点',
+      'Currency': '货币',
+      'Arrows': '箭头',
+      'Math': '数学',
+      'Misc': '杂项',
+      // Print.
+      'Print': '打印',
+      // Spell Checker.
+      'Spell Checker': '拼写检查器',
+      // Help
+      'Help': '帮助',
+      'Shortcuts': '快捷键',
+      'Inline Editor': '内联编辑器',
+      'Show the editor': '显示编辑器',
+      'Common actions': '常用操作',
+      'Copy': '复制',
+      'Cut': '剪切',
+      'Paste': '粘贴',
+      'Basic Formatting': '基本格式',
+      'Increase quote level': '增加引用级别',
+      'Decrease quote level': '减少引用级别',
+      'Image / Video': '图像/视频',
+      'Resize larger': '放大',
+      'Resize smaller': '缩小',
+      'Table': '表格',
+      'Select table cell': '选择单元格',
+      'Extend selection one cell': '增加选中的单元格',
+      'Extend selection one row': '增加选中的行',
+      'Navigation': '导航',
+      'Focus popup / toolbar': '焦点弹出/工具栏',
+      'Return focus to previous position': '将焦点返回到上一个位置',
+      // Embed.ly
+      'Embed URL': '嵌入网址',
+      'Paste in a URL to embed': '粘贴要嵌入的网址',
+      // Word Paste.
+      'The pasted content is coming from a Microsoft Word document. Do you want to keep the format or clean it up?': '粘贴的内容来自微软 Word 文档。你想保留还是清除格式?',
+      'Keep': '保留',
+      'Clean': '清除',
+      'Word Paste Detected': '检测到粘贴自 Word 的内容',
+      // Character Counter
+      'Characters': '字数统计',
+      // More Buttons
+      'More Text': ' 更多文字',
+      'More Paragraph': '更多段落',
+      'More Rich': '更多丰富',
+      'More Misc': '更多杂项',
+      'Rounded': '圆角',
+      'Bordered': '边框',
+      'Shadow': '阴影',
+      'Download PDF': '下载PDF',
+      'Text Color': '字体颜色',
+      'Background Color': '背景颜色',
+      'Inline Class': '内联类',
+      'Highlighted': '高亮',
+      'Transparent': '透明',
+      'Big Red': '大号红',
+      'Small Blue': '小号蓝',
+      'Default': '默认',
+      'Lower Alpha': 'a,b,c...',
+      'Lower Greek': 'α,β,γ...',
+      'Lower Roman': 'i,ii,iii...',
+      'Upper Alpha': 'A,B,C...',
+      'Upper Roman': 'Ⅰ,Ⅱ,Ⅲ...',
+      'Circle': '○ 空心圆',
+      'Disc': '● 实心圆',
+      'Square': '■ 实心方块',
+      'Gray': '灰色',
+      'Spaced': '字母间隙',
+      'Uppercase': '大写',
+      'Line Height': '行高',
+      'Single': '1',
+      'Double': '2'
+    },
+    direction: 'ltr'
+  };
+
+})));
+//# sourceMappingURL=zh_cn.js.map

+ 2 - 1
src/api/index.js

@@ -13,7 +13,8 @@ import { showLoadingToast,showToast,closeToast } from "vant";
 const LOADINGWHITELIST=[
   '/public/wechat_warning',
   '/report/saveReportContent',
-  '/report/editDayWeekChapter'
+  '/report/editDayWeekChapter',
+  '/english_report/saveReportContent'
 ]
 // 请求数
 let LOADINGCOUNT = 0;

+ 48 - 0
src/api/reportEn.js

@@ -89,5 +89,53 @@ export default {
      */
     emailResend:params=>{
         return post('/english_report/email/resend',params)
+    },
+    /**
+     * 通过报告分类id获取报告内容(继承上一篇报告)
+     * @param ClassifyIdFirst
+     * @param ClassifyIdSecond
+     */
+    reportDetailByClassifyId(params){
+        return get('/english_report/classifyIdDetail',params)
+    },
+    // 获取研报作者列表
+    reportAuthorList(params){
+        return get('/english_report/author',params)
+    },
+    /**
+     * 新增英文报告
+     */
+    reportAdd(params){
+        return post('/english_report/add',params)
+    },
+    /**
+     * 编辑英文报告
+     * @param ReportId
+     */
+    reportEdit(params){
+        return post('/english_report/edit',params)
+    },
+    /**
+     * 报告mark
+     * @param ReportId
+     * @param Status
+     */
+    reportMark(params){
+        return post('/english_report/mark',params)
+    },
+    /**
+     * 保存报告内容
+     * @param ReportId
+     * @param Content
+     * @param NoChange 0内容没变 1内容变了
+     */
+    reportContentSave(params){
+        return post('/english_report/saveReportContent',params)
+    },
+    /**
+     * 策略同步过来的报告编辑
+     */
+    strategyReportEdit(params){
+        return post('/english_report/edit_policy',params)
     }
 }

BIN
src/assets/imgs/icon_back.png


+ 2 - 2
src/assets/styles/common.scss

@@ -114,10 +114,10 @@ img {
     .report-html-wrap{
         font-size: 18px;
         span{
-            font-size: 18px;
+            font-size: 18px !important;
         }
         p{
-            font-size: 18px;
+            font-size: 18px !important;
         }
     }
 }

+ 1 - 1
src/assets/styles/vant.scss

@@ -35,6 +35,6 @@
         font-size: 16px;
     }
     .van-cell__label{
-        font-size: 15px ;
+        font-size: 15px !important;
     }
 }

+ 54 - 14
src/hooks/useFroalaEditor.js

@@ -1,8 +1,9 @@
-import { ref } from "vue";
+import { ref,nextTick } from "vue";
 
 export function useInitFroalaEditor() {
-	let FroalaEditorIns = ref(null);
-	let frolaEditorContentChange=ref(false)
+	let frolaEditorContentChange=ref(false)//富文本内容是否改变
+	let lastFocusPosition=ref(null)//最后焦点位置
+	let imgUploadFlag=ref(true)//图片是否上传完成
 
 	const options = {
 		toolbarButtons: [
@@ -46,30 +47,69 @@ export function useInitFroalaEditor() {
 		toolbarSticky: false, //操作栏是否自动吸顶
 		saveInterval: 0,
 		charCounterCount: false,
-		reportloadding: false,
-		lastsavetime: '',
-		isAddEnter: false, //是否已经添加过
-		timer: null,
-		ischange: false,
-		isPublishloading: false,
 		events:{
+			// 内容变化事件
 			contentChanged:function (){
-
 				frolaEditorContentChange.value=true
-			}
+				getSelection().rangeCount&&(lastFocusPosition.value=getSelection().getRangeAt(0))
+			},
+			keyup:function(e,editor){
+				nextTick(()=>{
+					getSelection().rangeCount&&(lastFocusPosition.value=getSelection().getRangeAt(0))
+				})
+			},
+			click:function(e,editor){
+				nextTick(()=>{
+					getSelection().rangeCount&&(lastFocusPosition.value=getSelection().getRangeAt(0))
+				})
+			},
+			'image.beforePasteUpload':function(){
+				imgUploadFlag.value=false
+			},
+			'image.beforeUpload':function(){
+				imgUploadFlag.value=false
+			},
+			'image.inserted':function(){
+				imgUploadFlag.value=true
+			},
+			'image.error':function(){
+				imgUploadFlag.value=true
+			},
 		}
 	};
 
+	/**
+	 * 初始化编辑器
+	 * @param  el domid
+	 * @param  opts 配置项
+	 * 由于要获取到富文本实例 要在new FroalaEditor(el,opt,callback)的callback中才能获取到
+	 * 方案一返回一个promise
+	 * 方案二直接返回富文本实例 无法在调用initFroalaEditor方法后立即执行实例上的一些方法或者读取属性
+	 * 一般写个settimeout 可以解决
+	 */
 	const initFroalaEditor = (el,opts) => {
-		console.log(opts);
+		// 方案一
+		// let ins=null
+		// return new Promise((resolve,reject)=>{
+		// 	options.height=opts?.height??500
+		// 	console.log(options);
+		// 	ins=new FroalaEditor(el, options,()=>{
+		// 		// console.log(ins);
+		// 		resolve(ins)
+		// 	})
+		// })
+
+		// 方案二
 		options.height=opts?.height??500
+		options.height=options.height<350?350:options.height
 		console.log(options);
-		FroalaEditorIns.value = new FroalaEditor(el, options);
+		return new FroalaEditor(el, options);
 	};
 
 	return {
-		FroalaEditorIns,
+		lastFocusPosition,
 		frolaEditorContentChange,
+		imgUploadFlag,
 		initFroalaEditor,
 	}
 }

+ 2 - 1
src/layouts/Index.vue

@@ -42,7 +42,8 @@ function goBack(){
                 <div 
                     class="back-home-box"
                     @click="goBack"
-                >返回</div>
+                    v-if="!$route.meta.noBack"
+                >返回上级</div>
             </div>
             <van-popover v-model:show="showUserInfo" placement="bottom-end">
                 <div class="userinfo-box_pad" v-if="userInfo">

+ 3 - 2
src/router/index.js

@@ -5,6 +5,7 @@
  * meta:{
  * noHead:true pad端没有顶部头部
  * hasBackTop:true 有回到顶部
+ * noBack:true pad端没有返回上一家
  * }
  */
 import { createRouter, createWebHistory } from "vue-router";
@@ -43,13 +44,13 @@ const routes = [
 						path: "/tabbar/home",
 						name: "Home",
 						component: () => import("@/views/tabbar/Home.vue"),
-						meta: { title: "移动ETA" },
+						meta: { title: "移动ETA",noBack:true },
 					},
 					{
 						path: "/tabbar/user",
 						name: "User",
 						component: () => import("@/views/tabbar/User.vue"),
-						meta: { title: "移动ETA" },
+						meta: { title: "移动ETA",noBack:true },
 					},
 				],
 			},

+ 10 - 10
src/router/report.js

@@ -42,16 +42,16 @@ export const reportRoutes=[
             hasBackTop:true
         },
     },
-    {
-        path:"/report/detail",
-        name:"reportDetail",
-        component: () => import("@/views/report/Detail.vue"),
-        meta: { 
-            title: "中文研报",
-            keepAlive:false,
-            hasBackTop:true
-        },
-    },
+    // {
+    //     path:"/report/detail",
+    //     name:"reportDetail",
+    //     component: () => import("@/views/report/Detail.vue"),
+    //     meta: { 
+    //         title: "中文研报",
+    //         keepAlive:false,
+    //         hasBackTop:true
+    //     },
+    // },
     {
         path:"/report/chapter/list",
         name:"ReportChapterList",

+ 17 - 1
src/router/reportEn.js

@@ -1,5 +1,21 @@
 // 英文研报路由模块
- export const reportEnRoutes=[
+export const reportEnRoutes=[
+    {
+        path:"/reportEn/edit",
+        name:"ReportEnEdit",
+        component: () => import("@/views/reportEn/AddReport.vue"),
+        meta: { 
+            title: "编辑研报"
+        },
+    },
+    {
+        path:"/reportEn/add",
+        name:"ReportEnAdd",
+        component: () => import("@/views/reportEn/AddReport.vue"),
+        meta: { 
+            title: "添加研报"
+        },
+    },
     {
         path:"/reportEn/list",
         name:"ReportEnList",

+ 59 - 9
src/views/report/AddReport.vue

@@ -1,4 +1,4 @@
-<script setup name="ReportAdd">
+<script setup>
 import {ref,onMounted,onUnmounted} from 'vue'
 import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
 import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
@@ -14,11 +14,13 @@ const cachedViewsStore=useCachedViewsStore()
 const router=useRouter()
 
 
-const {FroalaEditorIns,initFroalaEditor}=useInitFroalaEditor()
+const {lastFocusPosition,initFroalaEditor}=useInitFroalaEditor()
+
+let reportContentEditorIns=null//报告内容编辑器实例
 
 onMounted(() => {
     const el=document.getElementById('editor')
-    initFroalaEditor('#editor',{height:el.offsetHeight-150})
+    reportContentEditorIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
 })
 
 
@@ -51,7 +53,7 @@ async function handleReportBaseInfoChange(e){
                 reportBaseInfoData.createtime=moment().format('YYYY-MM-DD')
                 reportBaseInfoData.title=res.Data.Title
                 reportBaseInfoData.abstract=res.Data.Abstract
-                FroalaEditorIns.value.html.set(res.Data.Content);
+                reportContentEditorIns.html.set(res.Data.Content);
             }
         }
     }
@@ -67,26 +69,31 @@ const showReportInsertPop=ref(false)
  * chartType: chart-图表,sheet-表格
  */
 function handleInsert({list,type,chartType}){
+    reportContentEditorIns.events.focus()
+    if(lastFocusPosition.value){
+        reportContentEditorIns.selection.get().removeAllRanges()
+        reportContentEditorIns.selection.get().addRange(lastFocusPosition.value)
+    }
     if(type==='iframe'){
         let link;
         if(chartType==='chart'){
             link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
             list.forEach(item => {
-                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+                reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
 						<iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
-					</p>`,false)
+					</p>`)
             });
         }else if(chartType==='sheet'){
             link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
             list.forEach(item => {
-                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+                reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
 						<iframe src='${link}?code=${item}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
-					</p>`,false)
+					</p>`)
             });
         }
     }else if(type==='img'){
         list.forEach(item=>{
-            FroalaEditorIns.value.html.insert(`<img style='width:100%' src='${item}' />`,false)
+            reportContentEditorIns.html.insert(`<img style='width:100%' src='${item}' />`)
         })
     }
 
@@ -141,6 +148,8 @@ async function handleReportOpt(type){
         showToast('请填写报告标题')
         return
     }
+    //如果富文本中有未上传完成的图片,去除这个dom
+    $('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
     const params={
         AddType: reportBaseInfoData.addType,
 		ClassifyIdFirst: reportBaseInfoData.classifyName[0].id,
@@ -359,10 +368,17 @@ async function reportPublish(id){
 }
 .add-report-page{
     height: 100dvh;
+    min-height: 95vh;
     display: flex;
     flex-direction: column;
     overflow: hidden;
 }
+@media screen and (min-width:$media-width){
+    .add-report-page{
+        height: calc(100dvh - 60px);
+        min-height: calc(95vh - 60px);
+    }
+}
 .van-cell{
     flex-shrink: 0;
 }
@@ -376,6 +392,11 @@ async function reportPublish(id){
         height: 100%;
     }
 }
+@media screen and (min-width:$media-width){
+    .main-wrap{
+        margin-top: 15px;
+    }
+}
 .bot-action-box{
     padding: 20px 16PX;
     display: flex;
@@ -420,4 +441,33 @@ async function reportPublish(id){
         }
     }
 }
+@media screen and (min-width:$media-width){
+    .bot-action-box{
+        margin: 0 auto;
+        width: 600px;
+        padding: 10px 16px;
+        .left-box{
+            border-radius: 50px;
+            height: 56px;
+            margin-right: 10px;
+            padding: 0 10px;
+            .item{
+                font-size: 12px;
+                img{
+                    width: 20px;
+                    height: 20px;
+                    margin: 3px auto;
+                }
+            }
+        }
+        .right-btn{
+            width: 48px;
+            height: 48px;
+            svg{
+                width: 14px;
+                height: 14px;
+            }
+        }
+    }
+}
 </style>

+ 54 - 7
src/views/report/EditReport.vue

@@ -15,13 +15,14 @@ const router=useRouter()
 const route=useRoute()
 
 
-const {FroalaEditorIns,initFroalaEditor,frolaEditorContentChange}=useInitFroalaEditor()
+const {lastFocusPosition,initFroalaEditor,imgUploadFlag,frolaEditorContentChange}=useInitFroalaEditor()
+let reportContentEditorIns=null//报告内容编辑器实例
 
 let autoSaveTimer=null
 
 onMounted(() => {
     const el=document.getElementById('editor')
-    initFroalaEditor('#editor',{height:el.offsetHeight-150})
+    reportContentEditorIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
     getReportDetail()
     autoSaveTimer=setInterval(() => {
         autoSaveReportContent()
@@ -33,6 +34,9 @@ onUnmounted(()=>{
 
 // 自动保存报告
 async function autoSaveReportContent(){
+    if(!imgUploadFlag.value)return
+    //如果富文本中有未上传完成的图片,去除这个dom
+    $('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
     const res=await apiReport.reportContentSave({
         ReportId:Number(route.query.id),
         Content:$('.fr-element').html(),
@@ -68,7 +72,7 @@ async function getReportDetail(){
         reportBaseInfoData.title=res.Data.Title
         reportBaseInfoData.abstract=res.Data.Abstract
 
-        FroalaEditorIns.value.html.set(res.Data.Content);
+        reportContentEditorIns.html.set(res.Data.Content);
 
         // 查找选中的分类是否有电话会
         const classifyRes=await apiReport.getClassifyList({
@@ -124,7 +128,7 @@ async function handleReportBaseInfoChange(e){
                 reportBaseInfoData.createtime=moment().format('YYYY-MM-DD')
                 reportBaseInfoData.title=res.Data.Title
                 reportBaseInfoData.abstract=res.Data.Abstract
-                FroalaEditorIns.value.html.set(res.Data.Content);
+                reportContentEditorIns.html.set(res.Data.Content);
             }
         }
     }
@@ -140,26 +144,31 @@ const showReportInsertPop=ref(false)
  * chartType: chart-图表,sheet-表格
  */
 function handleInsert({list,type,chartType}){
+    reportContentEditorIns.events.focus()
+    if(lastFocusPosition.value){
+        reportContentEditorIns.selection.get().removeAllRanges()
+        reportContentEditorIns.selection.get().addRange(lastFocusPosition.value)
+    }
     if(type==='iframe'){
         let link;
         if(chartType==='chart'){
             link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
             list.forEach(item => {
-                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+                reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
 						<iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
 					</p>`,false)
             });
         }else if(chartType==='sheet'){
             link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
             list.forEach(item => {
-                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+                reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
 						<iframe src='${link}?code=${item}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
 					</p>`,false)
             });
         }
     }else if(type==='img'){
         list.forEach(item=>{
-            FroalaEditorIns.value.html.insert(`<img style='width:100%' src='${item}' />`,false)
+            reportContentEditorIns.html.insert(`<img style='width:100%' src='${item}' />`,false)
         })
     }
 
@@ -214,6 +223,8 @@ async function handleReportOpt(type){
         showToast('请填写报告标题')
         return
     }
+    //如果富文本中有未上传完成的图片,去除这个dom
+    $('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
     const params={
         ReportId:Number(route.query.id),
         AddType: reportBaseInfoData.addType,
@@ -429,10 +440,17 @@ async function reportPublish(id){
 }
 .add-report-page{
     height: 100dvh;
+    min-height: 95vh;
     display: flex;
     flex-direction: column;
     overflow: hidden;
 }
+@media screen and (min-width:$media-width){
+    .add-report-page{
+        height: calc(100dvh - 60px);
+        min-height: calc(95vh - 60px);
+    }
+}
 .van-cell{
     flex-shrink: 0;
 }
@@ -490,4 +508,33 @@ async function reportPublish(id){
         }
     }
 }
+@media screen and (min-width:$media-width){
+    .bot-action-box{
+        margin: 0 auto;
+        width: 600px;
+        padding: 10px 16px;
+        .left-box{
+            border-radius: 50px;
+            height: 56px;
+            margin-right: 10px;
+            padding: 0 10px;
+            .item{
+                font-size: 12px;
+                img{
+                    width: 20px;
+                    height: 20px;
+                    margin: 3px auto;
+                }
+            }
+        }
+        .right-btn{
+            width: 48px;
+            height: 48px;
+            svg{
+                width: 14px;
+                height: 14px;
+            }
+        }
+    }
+}
 </style>

+ 46 - 39
src/views/report/List.vue

@@ -8,6 +8,7 @@ import { showToast,showDialog,Dialog } from 'vant';
 import { useRouter } from 'vue-router';
 import { useWindowSize } from '@vueuse/core'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
+import {reportFrequencyOpts} from './utils/config'
 const cachedViewsStore=useCachedViewsStore()
 
 const { width, height } = useWindowSize()
@@ -229,40 +230,6 @@ const statusDropMenuIns=ref(null)
 const showMoreFilter=ref(false)
 const temFrequencyVal=ref('')
 const temMsgIsSendVal=ref('')
-const frequencyOpt=[
-    {
-        label:'年度',
-        value:'年度'
-    },
-    {
-        label:'半年度',
-        value:'半年度'
-    },
-    {
-        label:'季度',
-        value:'季度'
-    },
-    {
-        label:'月度',
-        value:'月度'
-    },
-    {
-        label:'双周度',
-        value:'双周度'
-    },
-    {
-        label:'周度',
-        value:'周度'
-    },
-    {
-        label:'日度',
-        value:'日度'
-    },
-    {
-        label:'不定时',
-        value:'不定时'
-    },
-]//频度筛选项
 const reportStatusOpt=[
     {
         label:'已推送',
@@ -428,10 +395,10 @@ async function handleReportEdit(e){
                         <ul>
                             <li 
                                 :class="['item',temFrequencyVal==item.value?'item-active':'']" 
-                                v-for="item in frequencyOpt" 
+                                v-for="item in reportFrequencyOpts" 
                                 :key="item.value"
                                 @click="handleSelectFrequency(item)"
-                            >{{item.label}}</li>
+                            >{{item.text}}</li>
                             <li class="item" style="height:0"></li>
                         </ul>
                         <div class="bot-btn-box">
@@ -481,11 +448,15 @@ async function handleReportEdit(e){
                     </h2>
                     <p class="van-multi-ellipsis--l2 des">{{item.Abstract}}</p>
                     <div class="bot-info">
-                        <div>
+                        <div class="time">
                             <span style="margin-right:10px">{{moment(item.ModifyTime).format('YYYY-MM-DD')}}</span>
                             <span>{{item.AdminRealName}}</span>
                         </div>
-                        <div>
+                        <div class="read-count">
+                            <span>PV:{{item.Pv}}</span>
+                            <span>UV:{{item.Uv}}</span>
+                        </div>
+                        <div class="status">
                             <span v-if="item.State===1">未发布</span>
                             <span v-if="item.State===2" class="active-status">已发布</span>
                         </div>
@@ -775,9 +746,20 @@ async function handleReportEdit(e){
             justify-content: space-between;
             color: $font-grey;
             font-size: 28px;
+            .time{
+                flex: 1;
+            }
             .active-status{
                 color: $font-success;
             }
+            .read-count{
+                color: $theme-warning;
+                margin-right: 30px;
+                span{
+                    display: inline-block;
+                    margin: 0 10px;
+                }
+            }
         }
     }
 }
@@ -796,6 +778,12 @@ async function handleReportEdit(e){
 }
 
 @media screen and (min-width:$media-width){
+    .add-report-btn{
+        svg{
+            width: 14px;
+            height: 14px;
+        }
+    }
     .sticky-box{
         top: 60px;
         .bot-btn-box{
@@ -832,9 +820,21 @@ async function handleReportEdit(e){
                 margin-bottom: 12px;
             }
         }
+        .clear-filter-box{
+            padding: 0 17px;
+            height: 42px;
+            font-size: 14px;
+            .rocket{
+                width: 24px;
+                height: 24px;
+            }
+            .close-icon{
+                right: 17px;
+            }
+        }
     }
     .top-box{
-        padding: 30px;
+        padding: 15px;
         .menu-icon{
             width: 40px;
             height: 40px;
@@ -854,6 +854,13 @@ async function handleReportEdit(e){
             .title{
                 font-size: 16px;
                 line-height: 22px;
+                .tag{
+                    width: 50px;
+                    height: 22px;
+                    line-height: 22px;
+                    font-size: 14px;
+                    border-radius: 2px;
+                }
             }
             .inline-title{
                 margin-left: -14px;

+ 19 - 1
src/views/report/PreviewDetail.vue

@@ -86,7 +86,7 @@ async function goEdit(){
                         <span>{{item.Title}}</span>
                     </div>
                     <!-- 音频 -->
-                    <AudioBox :url="item.VideoUrl" v-if="item.VideoUrl"/>
+                    <!-- <AudioBox :url="item.VideoUrl" v-if="item.VideoUrl"/> -->
                     <div class="report-html-wrap" v-html="item.Content"></div>
                 </li>
             </ul>
@@ -185,6 +185,20 @@ async function goEdit(){
             line-height: 27px;
             margin: 20px 0;
         }
+        .chapter-list-wrap{
+            .chapter-item-box{
+                padding-bottom: 20px;
+                margin-bottom: 20px;
+                .type-box{
+                    .tag{
+                        padding: 5px;
+                        font-size: 12px;
+                        border-radius: 2px;
+                        margin-right: 10px;
+                    }
+                }
+            }
+        }
     }
     .top-stage-box{
         .stage{
@@ -194,6 +208,10 @@ async function goEdit(){
             padding: 0 10px;
             font-size: 14px;
         }
+        .edit-icon{
+            width: 35px;
+            height: 35px;
+        }
     }
 }
 </style>

+ 37 - 6
src/views/report/ReportDayWeekAdd.vue

@@ -4,7 +4,10 @@ import {ref,reactive} from 'vue'
 import { useRouter } from 'vue-router'
 import apiReport from '@/api/report'
 import { showToast } from 'vant'
+import { useWindowSize } from '@vueuse/core'
 
+
+const { width, height } = useWindowSize()
 const router=useRouter()
 
 // 基本数据
@@ -160,13 +163,23 @@ function close(){
     </van-popup>
 
     <!-- 创建日期 -->
-    <van-calendar
-        :min-date="minDate"
-        :default-date="defaultDate"
+    <van-popup 
         v-model:show="showCreateTimePop"
-        title="选择创建日期"
-        @confirm="handleConfirmCreatime" 
-    />
+        :position="width>650?'center':'bottom'"
+        :style="width>650?{ width: '400px'}:''"
+        round
+    >
+        <van-calendar
+            :poppable="false"
+            :min-date="minDate"
+            :default-date="defaultDate"
+            v-model:show="showCreateTimePop"
+            title="选择创建日期"
+            @confirm="handleConfirmCreatime"
+            :style="{ height: '500px' }"
+        />
+    </van-popup>
+    
 
     <!-- 作者 -->
     <van-popup
@@ -187,6 +200,7 @@ function close(){
 <style lang="scss" scoped>
 .dayweek-report-add{
     height: 100dvh;
+    min-height: 95vh;
     position: relative;
     background: #EDEDED;
     :deep(.cell-con){
@@ -217,4 +231,21 @@ function close(){
         text-align: center;
     }
 }
+@media screen and (min-width:$media-width){
+    .dayweek-report-add{
+        height: calc(100dvh - 60px);
+        min-height: calc(95vh - 60px);
+        .bot-btns{
+            bottom: 24px;
+        }
+    }
+    .bot-btn{
+        margin: 0 10px;
+    }
+    .input-report-title-pop{
+        .bot-btns{
+            padding: 10px 0;
+        }
+    }
+}
 </style>

+ 68 - 17
src/views/report/chapter/Detail.vue

@@ -13,12 +13,13 @@ import { showToast,showDialog } from 'vant'
 import {useUploadFileToOSS} from '@/hooks/useUploadFileToOSS'
 import MD5 from 'js-md5'
 
+const route=useRoute()
+const router=useRouter()
 
 const userInfo=useUserInfo()
 
-const {FroalaEditorIns,initFroalaEditor,frolaEditorContentChange}=useInitFroalaEditor()
-const route=useRoute()
-const router=useRouter()
+const {lastFocusPosition,initFroalaEditor,imgUploadFlag,frolaEditorContentChange}=useInitFroalaEditor()
+let reportContentEditorIns=null//报告内容编辑器实例
 
 let autoSaveTimer=null
 
@@ -34,6 +35,10 @@ onUnmounted(()=>{
 // 自动保存
 function autoSaveReportContent(e){
     if(!e&&!frolaEditorContentChange.value) return
+    if(!imgUploadFlag.value){
+        e==='cg'&&showToast('有图片未上传完成,请稍等')
+        return
+    }
     let arr=[]
     ticketList.value.forEach(item=>{
         let obj={
@@ -48,6 +53,8 @@ function autoSaveReportContent(e){
 			arr.push(obj)
         }
     })
+    //如果富文本中有未上传完成的图片,去除这个dom
+	$('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
     apiReport.chapterDetailSave({
         ReportChapterId: Number(route.query.id),
 		Title:chapterBaseInfo.title,
@@ -83,7 +90,7 @@ function getChapterDetail(){
             chapterBaseInfo.type=res.Data.TypeName
             chapterBaseInfo.title=res.Data.Title
             chapterBaseInfo.addType=res.Data.AddType
-            chapterBaseInfo.author=res.Data.Author||userInfo.RealName
+            chapterBaseInfo.author=res.Data.Author||userInfo.value.RealName
             chapterBaseInfo.createTime=moment(res.Data.CreateTime).format('YYYY-MM-DD')
             if(res.Data.VideoKind==1){
                 chapterBaseInfo.audioUrl=res.Data.VideoUrl
@@ -99,9 +106,9 @@ function getChapterDetail(){
             // 由于周报有个上传音频区域 所以得在此处初始化富文本区域 不然高度有问题
             nextTick(()=>{
                 const el=document.getElementById('editor')
-                initFroalaEditor('#editor',{height:el.offsetHeight-150})
+                reportContentEditorIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
                 setTimeout(() => {
-                    FroalaEditorIns.value.html.set(res.Data.Content);
+                    reportContentEditorIns.html.set(res.Data.Content);
                 }, 100);
             })
             
@@ -155,14 +162,14 @@ async function handleChapterBaseInfoSave(e){
                 chapterBaseInfo.title=res.Data.Title
                 chapterBaseInfo.author=res.Data.Author||userInfo.RealName
                 chapterBaseInfo.createTime=moment(res.Data.CreateTime).format('YYYY-MM-DD')
-                FroalaEditorIns.value.html.set(res.Data.Content);
+                reportContentEditorIns.html.set(res.Data.Content);
                 // 晨报获取指标数据
-				if(res.data.ReportType=='day'){
+				if(res.Data.ReportType=='day'){
 					getDayTicketList()
 				}
             }
         }else{
-            FroalaEditorIns.value.html.set('');
+            reportContentEditorIns.html.set('');
         }
     }
     
@@ -241,26 +248,31 @@ const showReportInsertPop=ref(false)
  * chartType: chart-图表,sheet-表格
  */
 function handleInsert({list,type,chartType}){
+    reportContentEditorIns.events.focus()
+    if(lastFocusPosition.value){
+        reportContentEditorIns.selection.get().removeAllRanges()
+        reportContentEditorIns.selection.get().addRange(lastFocusPosition.value)
+    }
     if(type==='iframe'){
         let link;
         if(chartType==='chart'){
             link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
             list.forEach(item => {
-                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+                reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
 						<iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
-					</p>`,false)
+					</p>`)
             });
         }else if(chartType==='sheet'){
             link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
             list.forEach(item => {
-                FroalaEditorIns.value.html.insert(`<p style='text-align:left; margin-top:10px;'>
+                reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
 						<iframe src='${link}?code=${item}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
-					</p>`,false)
+					</p>`)
             });
         }
     }else if(type==='img'){
         list.forEach(item=>{
-            FroalaEditorIns.value.html.insert(`<img style='width:100%' src='${item}' />`,false)
+            reportContentEditorIns.html.insert(`<img style='width:100%' src='${item}' />`)
         })
     }
 
@@ -333,6 +345,8 @@ async function chapterReportPublish(PublishReport){
 			arr.push(obj)
         }
     })
+    //如果富文本中有未上传完成的图片,去除这个dom
+	$('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
     const res=await apiReport.chapterReportPublish({
         ReportChapterId: Number(route.query.id),
 		Title:chapterBaseInfo.title,
@@ -411,7 +425,7 @@ async function handleReportOpt(type){
 
 <template>
     <div class="chapter-detail-edit-page">
-        <van-cell title="基础信息" is-link @click="showChapterBaseInfo=true"/>
+        <van-cell title="章节基础配置" is-link @click="showChapterBaseInfo=true"/>
         <van-cell title="上传音频" is-link v-if="info?.ReportType==='week'" @click="handleShowUploadAudio"/>
         <div class="main-wrap">
             <div class="editor-box" id="editor"></div>
@@ -469,7 +483,7 @@ async function handleReportOpt(type){
     >
         <div class="upload-audio-wrap" v-if="showUploadAudio">
             <template v-if="temAudioData.url">
-                <h2>音频名称</h2>
+                <h2>音频链接</h2>
                 <p>{{temAudioData.url}}</p>
                 <AudioBox :url="temAudioData.url" />
             </template>
@@ -499,6 +513,7 @@ async function handleReportOpt(type){
 <style lang="scss" scoped>
 .chapter-detail-edit-page{
     height: 100dvh;
+    min-height: 95vh;
     display: flex;
     flex-direction: column;
     overflow: hidden;
@@ -584,5 +599,41 @@ async function handleReportOpt(type){
             margin: 0 10px;
         }
     }
-}   
+}
+@media screen and (min-width:$media-width){
+    .chapter-detail-edit-page{
+        height: calc(100dvh - 60px);
+        min-height: calc(95vh - 60px);
+    }
+    .main-wrap{
+        margin-top: 15px;
+    }
+    .bot-action-box{
+        margin: 0 auto;
+        width: 600px;
+        padding: 10px 16px;
+        .left-box{
+            border-radius: 50px;
+            height: 56px;
+            margin-right: 10px;
+            padding: 0 10px;
+            .item{
+                font-size: 12px;
+                img{
+                    width: 20px;
+                    height: 20px;
+                    margin: 3px auto;
+                }
+            }
+        }
+        .right-btn{
+            width: 48px;
+            height: 48px;
+            svg{
+                width: 14px;
+                height: 14px;
+            }
+        }
+    }
+}
 </style>

+ 59 - 7
src/views/report/chapter/List.vue

@@ -1,5 +1,5 @@
 <script setup name="reportChapterList">
-import {reactive, ref} from 'vue'
+import {nextTick, reactive, ref} from 'vue'
 import { useRoute, useRouter } from "vue-router";
 import apiReport from '@/api/report'
 import {apiGetWXQRCodeImg} from '@/api/common'
@@ -8,6 +8,7 @@ import { showToast,showDialog } from 'vant';
 import { useWindowSize } from '@vueuse/core'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import EditBaseInfo from './components/EidtBaseInfo.vue'
+import html2canvas from "html2canvas";
 
 const cachedViewsStore=useCachedViewsStore()
 const { width, height } = useWindowSize()
@@ -205,6 +206,7 @@ async function handlePublishReport(){
 // 显示海报
 const showChapterItemPoster=ref(false)
 const chapterItemPosterInfo=ref(null)
+const posterImgBase64=ref('')
 async function handleShowPoster(item){
     if(!item.QRCodeImg){//获取二维码
         const res=await apiGetWXQRCodeImg({
@@ -216,7 +218,23 @@ async function handleShowPoster(item){
         }
     }
     chapterItemPosterInfo.value=item
-    showChapterItemPoster.value=true
+
+    nextTick(()=>{
+        const targetEl = document.getElementById('chapter-poster-box');
+        if(targetEl){
+            html2canvas(targetEl,{
+                backgroundColor: "#ffffff",
+                useCORS: true, // 允许图片跨域
+                allowTaint: true, // 在渲染前测试图片
+                imageTimeout: 0, // 加载延时
+                scale:3,
+            }).then(res=>{
+                let img = res.toDataURL("image/png");
+                posterImgBase64.value=img
+                showChapterItemPoster.value=true
+            })
+        }
+    })
 }
 
 </script>
@@ -291,21 +309,31 @@ async function handleShowPoster(item){
         v-model:show="showChapterItemPoster" 
         round
     >
-        <div class="chapter-poster-box" v-screenshot="{target: '.chapter-poster-box', filename: 'my-screenshot.png'}">
+        <img class="chapter-poster-img" :src="posterImgBase64" alt="">
+    </van-popup>
+
+    <!-- 海报dom模块 -->
+        <div v-if="chapterItemPosterInfo" class="select-text-disabled chapter-poster-box" id="chapter-poster-box">
             <img class="bg" :src="chapterItemPosterInfo.TypeEditImg" alt="">
             <h2 class="title">【第{{reportInfo.Stage}}期|{{reportInfo.ClassifyNameFirst}}|{{chapterItemPosterInfo.TypeName}}】{{chapterItemPosterInfo.Title}}</h2>
             <img class="code-img" :src="chapterItemPosterInfo.QRCodeImg" alt="">
             <p class="tips">长按保存图片,发送给好友</p>
         </div>
-    </van-popup>
 </template>
 
 <style lang="scss" scoped>
 .report-chapterlist-page{
-    height: 100%;
+    height: 100dvh;
+    min-height: 95vh;
     display: flex;
     flex-direction: column;
+    padding-bottom: 130px;
     .bot-btns{
+        position: fixed;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        z-index: 99;
         padding: 20px 0;
         display: flex;
         justify-content: center;
@@ -407,7 +435,15 @@ async function handleShowPoster(item){
     }
 }
 
+.chapter-poster-img{
+    display: block;
+    width: 300PX;
+    border-radius: 6PX;
+    padding: 10PX;
+}
 .chapter-poster-box{
+    position: fixed;
+    z-index: -100;
     width: 300PX;
     border-radius: 6PX;
     padding: 10PX;
@@ -439,11 +475,21 @@ async function handleShowPoster(item){
 }
 
 @media screen and (min-width:$media-width){
+    .report-chapterlist-page{
+        padding-bottom: 80px;
+        .bot-btns{
+            padding: 10px 0;
+        }
+    }
     .chapter-list-wrap{
         border: none;
-        padding: 30px;
+        padding: var(--van-padding-md);
+        .list-lable{
+            font-size: 16px;
+            margin-bottom: 15px;
+        }
         .top-box{
-            margin-bottom: 27px;
+            margin-bottom: 15px;
             .btn{
                 width: 35px;
                 height: 35px;
@@ -470,6 +516,12 @@ async function handleShowPoster(item){
                 padding-left: 10px;
                 .title{
                     font-size: 16px;
+                    padding-right: 100px;
+                }
+                .audio-icon{
+                    width: 18px;
+                    height: 18px;
+                    right: 60px;
                 }
 
                 .icon{

+ 72 - 14
src/views/report/chapter/components/EditChapterBaseInfo.vue

@@ -2,6 +2,11 @@
 import { showToast } from "vant";
 import { computed, reactive,ref } from "vue";
 import apiReport from '@/api/report'
+import moment from "moment";
+import { useWindowSize } from '@vueuse/core'
+
+
+const { width, height } = useWindowSize()
 
 const props=defineProps({
     ticketData:{
@@ -32,7 +37,7 @@ const baseInfo=reactive({
     addType:props.defaultData.addType||1,
     ticket:[],
     author:props.defaultData.author||'',
-    createTime:props.defaultData.createTime||'',
+    createTime:props.defaultData.createTime||moment().format('YYYY-MM-DD'),
 })
 
 // 报告新增类型
@@ -59,7 +64,7 @@ const minDate=new Date(2015, 0, 1)
 const defaultDate=ref(new Date())
 const showCreateTimePop=ref(false)
 function handleShowCreatetime(){
-    defaultDate.value=new Date(baseInfo.createtime.replace(/-/g,'/'))
+    defaultDate.value=new Date(baseInfo.createTime.replace(/-/g,'/'))
     showCreateTimePop.value=true
 }
 function handleConfirmCreatime(e){
@@ -79,6 +84,18 @@ function handleConfirmReportTitle(){
     showReportTitlePop.value=false
 }
 
+//作者
+const showReportAuthorPop=ref(false)
+const temReportAuthorVal=ref('')
+function handleShowReportAuthor(){
+    temReportAuthorVal.value=baseInfo.author
+    showReportAuthorPop.value=true
+}
+function handleConfirmReportAuthor(){
+    baseInfo.author=temReportAuthorVal.value
+    showReportAuthorPop.value=false
+}
+
 
 
 
@@ -87,10 +104,10 @@ function close(){
 }
 
 function handleSave(){
-    if(!baseInfo.title){
-        showToast('请填写标题')
-        return
-    }
+    // if(!baseInfo.title){
+    //     showToast('请填写标题')
+    //     return
+    // }
     // 处理晨报选中的指标
     baseInfo.ticket=props.ticketData.map(item=>{
         return {
@@ -107,8 +124,7 @@ function handleSave(){
     <div class="chapter-baseinfo-wrap">
         <van-cell-group>
             <van-cell 
-                value-class="cell-con" 
-                required 
+                value-class="cell-con"
                 title="品种" 
                 :value="baseInfo.type"
             />
@@ -140,6 +156,7 @@ function handleSave(){
                 title="创建人" 
                 is-link 
                 :value="baseInfo.author"
+                @click="handleShowReportAuthor"
             />
             <van-cell 
                 value-class="cell-con" 
@@ -170,13 +187,21 @@ function handleSave(){
     />
 
     <!-- 创建日期 -->
-    <van-calendar
-        :min-date="minDate"
-        :default-date="defaultDate"
+    <van-popup 
         v-model:show="showCreateTimePop"
-        title="选择创建日期"
-        @confirm="handleConfirmCreatime" 
-    />
+        :position="width>650?'center':'bottom'"
+        :style="width>650?{ width: '400px'}:''"
+        round
+    >
+        <van-calendar
+            :poppable="false"
+            :min-date="minDate"
+            :default-date="defaultDate"
+            title="选择创建日期"
+            @confirm="handleConfirmCreatime"
+            :style="{ height: '500px' }"
+        />
+    </van-popup>
 
     <!-- 标题 -->
     <van-popup
@@ -193,6 +218,21 @@ function handleSave(){
         </div>
     </van-popup>
 
+    <!-- 作者 -->
+    <van-popup
+        v-model:show="showReportAuthorPop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="input-report-title-pop">
+             <van-field v-model="temReportAuthorVal" placeholder="请输入作者" />
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showReportAuthorPop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" :disabled="!temReportAuthorVal" @click="handleConfirmReportAuthor">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+
     <!-- 选择晨报的指标 -->
     <van-popup
         v-model:show="showTicketPop"
@@ -200,6 +240,10 @@ function handleSave(){
         :style="{ height: '100%' }"
     >
         <div class="day-report-ticket-list-wrap">
+            <div v-if="ticketData.length==0">
+                <img class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
+                <p style="text-align:center;color:#999999;font-size:12px">暂无数据</p>
+            </div>
             <ul class="list">
                 <van-checkbox-group v-model="activeTicket">
                     <van-checkbox 
@@ -275,4 +319,18 @@ function handleSave(){
         }
     }
 }
+
+@media screen and (min-width:$media-width){
+    .chapter-baseinfo-wrap{
+        .bot-btns{
+            bottom: 24px;
+        }
+    }
+    .input-report-title-pop{
+        .bot-btns{
+            padding: 10px 0;
+        }
+    }
+
+}
 </style>

+ 31 - 6
src/views/report/chapter/components/EidtBaseInfo.vue

@@ -2,6 +2,10 @@
 import moment from 'moment'
 import {ref,reactive} from 'vue'
 import { showToast } from 'vant'
+import { useWindowSize } from '@vueuse/core'
+
+
+const { width, height } = useWindowSize()
 
 const props=defineProps({
     defaultData:null
@@ -109,13 +113,22 @@ function close(){
     </van-popup>
 
     <!-- 创建日期 -->
-    <van-calendar
-        :min-date="minDate"
-        :default-date="defaultDate"
+    <van-popup 
         v-model:show="showCreateTimePop"
-        title="选择创建日期"
-        @confirm="handleConfirmCreatime" 
-    />
+        :position="width>650?'center':'bottom'"
+        :style="width>650?{ width: '400px'}:''"
+        round
+    >
+        <van-calendar
+            :poppable="false"
+            :min-date="minDate"
+            :default-date="defaultDate"
+            title="选择创建日期"
+            @confirm="handleConfirmCreatime" 
+            :style="{ height: '500px' }"
+        />
+    </van-popup>
+    
 
     <!-- 作者 -->
     <van-popup
@@ -166,4 +179,16 @@ function close(){
         text-align: center;
     }
 }
+@media screen and (min-width:$media-width){
+    .dayweek-report-add{
+        .bot-btns{
+            bottom: 24px;
+        }
+    }
+    .input-report-title-pop{
+        .bot-btns{
+            padding: 10px 0;
+        }
+    }
+}
 </style>

+ 10 - 1
src/views/report/components/AudioBox.vue

@@ -1,5 +1,5 @@
 <script setup>
-import {ref} from 'vue'
+import {ref,watch} from 'vue'
 const props=defineProps({
     url:{
         type:String,
@@ -7,6 +7,15 @@ const props=defineProps({
     }
 })
 
+watch(
+    ()=>props.url,
+    ()=>{
+        if(audioIns.value){
+            audioIns.value.src=props.url
+        }
+    }
+)
+
 // 格式化音频时间
 function formatDuration(e){
     let minus = parseInt(e / 60);

+ 43 - 6
src/views/report/components/EditReportBaseInfo.vue

@@ -6,7 +6,10 @@ import apiReport from '@/api/report'
 import {reportFrequencyOpts} from '../utils/config'
 import { showToast } from "vant"
 import { useRoute } from "vue-router"
+import { useWindowSize } from '@vueuse/core'
 
+
+const { width, height } = useWindowSize()
 const route=useRoute()
 
 const props=defineProps({
@@ -240,13 +243,21 @@ function handleSave(){
     </van-popup>
 
     <!-- 创建日期 -->
-    <van-calendar
-        :min-date="minDate"
-        :default-date="defaultDate"
+    <van-popup 
         v-model:show="showCreateTimePop"
-        title="选择创建日期"
-        @confirm="handleConfirmCreatime" 
-    />
+        :position="width>650?'center':'bottom'"
+        :style="width>650?{ width: '400px'}:''"
+        round
+    >
+        <van-calendar
+            :poppable="false"
+            :min-date="minDate"
+            :default-date="defaultDate"
+            title="选择创建日期"
+            @confirm="handleConfirmCreatime" 
+            :style="{ height: '500px' }"
+        />
+    </van-popup>
 
     <!-- 标题 -->
     <van-popup
@@ -335,4 +346,30 @@ function handleSave(){
         text-align: center;
     }
 }
+
+@media screen and (min-width:$media-width){
+    .report-baseinfo-wrap{
+        .bot-btns{
+            bottom: 24px;
+        }
+    }
+    .select-author-pop{
+        .van-checkbox-group{
+            padding: $page-padding;
+            .van-checkbox{
+                :deep(.van-checkbox__label){
+                    padding: 16px 0;
+                }
+            }
+        }
+        .bot-btns{
+            padding: 10px 0;
+        }
+    }
+    .input-report-title-pop{
+        .bot-btns{
+            padding: 10px 0;
+        }
+    }
+}
 </style>

+ 30 - 0
src/views/report/components/reportInsert/ETAChart.vue

@@ -124,6 +124,8 @@ watch(
                     </svg>
 
                 </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
             </ul>
         </div>
     </div>
@@ -138,6 +140,7 @@ watch(
         overflow: hidden;
         flex: 1;
         padding: var(--van-padding-sm);
+        min-height: 300PX;
     }
     .chart-list{
         height: 100%;
@@ -190,4 +193,31 @@ watch(
         margin: var(--van-padding-sm);
     }
 }
+@media screen and (min-width:$media-width){
+    .ETA-chart-wrap{
+        .chart-list{
+            
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                max-height: 220px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
 </style>

+ 12 - 0
src/views/report/components/reportInsert/Index.vue

@@ -83,4 +83,16 @@ function handleConfirmInsert(){
         padding: 48px;
     }
 }
+@media screen and (min-width:$media-width){
+    .report-insert-content-wrap{
+        .top-type-box{
+            .item{
+                padding: 12px 0;
+            }
+        }
+        .bot-btn{
+            padding: 24px;
+        }
+    }
+}
 </style>

+ 46 - 5
src/views/report/components/reportInsert/MyETAChart.vue

@@ -6,10 +6,10 @@ import { vInfiniteScroll } from '@vueuse/components'
 const emits=defineEmits(['update'])
 
 // 获取我的图库中分类
-const showClassify=ref(false)
+let showClassify=ref(false)
 let classifyList=[]
-const activeClassify=ref('')
-const activeClassifyName=ref('')
+let activeClassify=ref('')
+let activeClassifyName=ref('')
 let curClassifyList=ref([])
 async function getClassifyList(){
     const res=await apiMyETAChart.myClassifyList()
@@ -20,7 +20,7 @@ async function getClassifyList(){
 }
 getClassifyList()
 
-const searchVal=ref('')
+let searchVal=ref('')
 function handleRefreshList(){
     activeClassify=ref('')
     curClassifyList.value=classifyList.filter(item=>item.MyChartClassifyName.includes(searchVal.value))
@@ -75,7 +75,7 @@ watch(
 
 <template>
     <div class="myETA-insert-wrap">
-        <van-cell title="分类" :value="activeClassifyName" is-link @click="showClassify=true"/>
+        <van-cell style="flex-shrink:0" title="分类" :value="activeClassifyName" is-link @click="showClassify=true"/>
         <div class="content-box">
             <div v-if="listState.list.length==0">
                 <img class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
@@ -94,6 +94,8 @@ watch(
                         <path d="M14.5 28C22.232 28 28.5 21.732 28.5 14C28.5 6.26801 22.232 0 14.5 0C6.76801 0 0.5 6.26801 0.5 14C0.5 21.732 6.76801 28 14.5 28ZM7.5 14.413L8.913 13L12.5 16.586L20.085 9L21.5 10.415L12.5 19.414L7.5 14.413Z" fill="#0052D9"/>
                     </svg>
                 </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
             </ul>
         </div>
     </div>
@@ -101,6 +103,7 @@ watch(
     <van-popup
         v-model:show="showClassify"
         position="bottom"
+        teleport="body"
         :style="{ height: '100%' }"
     >
         <div class="select-classify-wrap">
@@ -127,6 +130,7 @@ watch(
         overflow: hidden;
         flex: 1;
         padding: var(--van-padding-sm);
+        min-height: 300PX;
     }
     .chart-list{
         height: 100%;
@@ -192,4 +196,41 @@ watch(
         }
     }
 }
+
+@media screen and (min-width:$media-width){
+    .myETA-insert-wrap{
+        .chart-list{
+            max-height: 100%;
+            height: auto;
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                max-height: 220px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+            }
+            .active{
+                border-width: 1px;
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+    .select-classify-wrap{
+        .list{
+            :deep(.van-radio__label){
+                padding: 16px 0;
+            }
+        }
+    }
+}
 </style>

+ 33 - 0
src/views/report/components/reportInsert/SandTableImg.vue

@@ -91,6 +91,8 @@ watch(
                     </svg>
 
                 </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
             </ul>
         </div>
     </div>
@@ -105,6 +107,7 @@ watch(
         overflow: hidden;
         flex: 1;
         padding: var(--van-padding-sm);
+        min-height: 300PX;
     }
     .chart-list{
         height: 100%;
@@ -145,4 +148,34 @@ watch(
         }
     }
 }
+@media screen and (min-width:$media-width){
+    .sandTableImg-insert-wrap{
+        .chart-list{
+            height: auto;
+            max-height: 100%;
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+                img{
+                    height: 150px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
 </style>

+ 33 - 0
src/views/report/components/reportInsert/SheetTableChart.vue

@@ -88,6 +88,8 @@ watch(
                     </svg>
 
                 </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
             </ul>
         </div>
     </div>
@@ -102,6 +104,7 @@ watch(
         overflow: hidden;
         flex: 1;
         padding: var(--van-padding-sm);
+        min-height: 300PX;
     }
     .chart-list{
         height: 100%;
@@ -142,4 +145,34 @@ watch(
         }
     }
 }
+@media screen and (min-width:$media-width){
+    .sheet-insert-wrap{
+        .chart-list{
+            height: auto;
+            max-height: 100%;
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+                img{
+                    height: 150px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
 </style>

+ 509 - 0
src/views/reportEn/AddReport.vue

@@ -0,0 +1,509 @@
+<script setup name="ReportEnAdd">
+import {ref,onMounted,onUnmounted, nextTick} from 'vue'
+import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
+import ReportInsertContent from '../report/components/reportInsert/Index.vue'
+import apiReportEn from '@/api/reportEn'
+import apiChart from '@/api/chart'
+import moment from 'moment'
+import { showToast } from 'vant'
+import { useRoute, useRouter } from 'vue-router'
+import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
+import {useCachedViewsStore} from '@/store/modules/cachedViews'
+
+const cachedViewsStore=useCachedViewsStore()
+const router=useRouter()
+const route=useRoute()
+
+const {lastFocusPosition,frolaEditorContentChange,imgUploadFlag,initFroalaEditor}=useInitFroalaEditor()
+
+let reportContentIns=null//报告内容编辑器实例
+let overviewContentIns=null//overview内容编辑器实例
+
+let autoSaveTimer=null//自动保存定时器
+
+onMounted(async () => {
+    const el=document.getElementById('editor')
+    reportContentIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
+    if(route.query.id>0){
+        //编辑时
+        getReportDetail()
+        autoSaveTimer=setInterval(() => {
+            autoSaveReportContent()
+        }, 6000);
+    }
+})
+onUnmounted(()=>{
+    clearInterval(autoSaveTimer)
+})
+
+// 自动保存报告
+async function autoSaveReportContent(){
+    if(!imgUploadFlag.value)return
+    //如果富文本中有未上传完成的图片,去除这个dom
+    $('#editor .fr-element').find('img.fr-uploading').length&&$('#editor .fr-element').find('img.fr-uploading').remove()
+    const res=await apiReportEn.reportContentSave({
+        ReportId:Number(route.query.id),
+        Content:$('#editor .fr-element').html(),
+        NoChange:frolaEditorContentChange.value?0:1
+    })
+    frolaEditorContentChange.value=false
+}
+
+// 获取报告详情
+const reportData=ref(null)
+async function getReportDetail(){
+    const res=await apiReportEn.getReportDetail({ReportId:Number(route.query.id)})
+    if(res.Ret===200){
+        reportData.value=res.Data
+        reportBaseInfoData.addType=res.Data.AddType
+        reportBaseInfoData.classifyName=[
+            {
+                id:res.Data.ClassifyIdFirst,
+                text:res.Data.ClassifyNameFirst
+            },
+            {
+                id:res.Data.ClassifyIdSecond,
+                text:res.Data.ClassifyNameSecond
+            }
+        ]
+        reportBaseInfoData.author=res.Data.Author ? res.Data.Author.split(',') : ['Horizon Insights FICC Team']
+        reportBaseInfoData.frequency=[res.Data.Frequency]
+        reportBaseInfoData.createtime=moment(res.Data.CreateTime).format('YYYY-MM-DD')
+        reportBaseInfoData.title=res.Data.Title
+        reportBaseInfoData.abstract=res.Data.Abstract
+
+        reportContentIns.html.set(res.Data.Content);
+        temOverviewData.value=res.Data.Overview
+
+    }
+}
+
+// 报告基本内容
+const showReportBaseInfo=ref(false)
+let reportBaseInfoData={
+    addType:1,
+    classifyName:[],
+    author:['Horizon Insights FICC Team'],
+    frequency: ['日度'],
+    createtime:moment().format('YYYY-MM-DD'),
+    title:'',
+    abstract:''
+}
+async function handleReportBaseInfoChange(e){
+    reportBaseInfoData=e
+
+    // 继承报告 覆盖一次
+    if(e.addType===2&&e.classifyName.length===2){
+        const res=await apiReportEn.reportDetailByClassifyId({
+            ClassifyIdFirst:e.classifyName[0].id,
+            ClassifyIdSecond:e.classifyName[1].id
+        })
+        if(res.Ret===200){
+            if(res.Data===null){
+                showToast('此分类暂无报告')
+            }else{
+                reportBaseInfoData.author=res.Data.Author ? res.Data.Author.split(',') : ['FICC团队']
+                reportBaseInfoData.frequency=[res.Data.Frequency]
+                reportBaseInfoData.createtime=moment().format('YYYY-MM-DD')
+                reportBaseInfoData.title=res.Data.Title
+                reportBaseInfoData.abstract=res.Data.Abstract
+                reportContentIns.html.set(res.Data.Content);
+                temOverviewData.value=res.Data.Overview
+            }
+        }
+    }
+    
+    showReportBaseInfo.value=false
+}
+
+// overview
+const showEditOverview=ref(false)
+const temOverviewData=ref('')
+function handleShowOverview(){
+    showEditOverview.value=true
+    nextTick(()=>{
+        const el=document.getElementById('editor-overview')
+        overviewContentIns=initFroalaEditor('#editor-overview',{height:el.offsetHeight-150})
+        if(temOverviewData.value){
+            setTimeout(() => {
+                overviewContentIns.html.set(temOverviewData.value)
+            }, 100);
+        }
+    })
+}
+function handleSaveOverview(){
+    temOverviewData.value=$('#editor-overview .fr-element').html()
+    showEditOverview.value=false
+}
+
+
+// 报告插入数据弹窗
+const showReportInsertPop=ref(false)
+/**
+ * list:[UniqueCode] 图表code
+ * type:iframe/img 插入的为iframe或者图片
+ * chartType: chart-图表,sheet-表格
+ */
+function handleInsert({list,type,chartType}){
+    reportContentIns.events.focus()
+    if(lastFocusPosition.value){
+        reportContentIns.selection.get().removeAllRanges()
+        reportContentIns.selection.get().addRange(lastFocusPosition.value)
+    }
+    if(type==='iframe'){
+        let link;
+        if(chartType==='chart'){
+            link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
+            list.forEach(item => {
+                reportContentIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
+						<iframe src='${link}?code=${item}&fromPage=en' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+					</p>`)
+            });
+        }else if(chartType==='sheet'){
+            link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
+            list.forEach(item => {
+                reportContentIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
+						<iframe src='${link}?code=${item}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
+					</p>`)
+            });
+        }
+    }else if(type==='img'){
+        list.forEach(item=>{
+            reportContentIns.html.insert(`<img style='width:100%' src='${item}' />`)
+        })
+    }
+
+    showReportInsertPop.value=false
+}
+
+// 更新sheet表格高度
+function reInitSheetIframe(e){
+    const { height,code } = e.data;
+    let iframeDom = document.getElementsByClassName(`iframe${code}`)
+    Array.prototype.forEach.call(iframeDom, function (ele) {
+        ele.height = `${height+45}px`;
+    });
+}
+
+onMounted(()=>{
+    window.addEventListener('message',reInitSheetIframe)
+})
+onUnmounted(()=>{
+    window.removeEventListener('message',reInitSheetIframe)
+})
+
+// 刷新所有图表
+async function handleRefreshAllChart(){
+    let code_arr = [];
+    $('iframe').each((k,i) => {
+        try {
+          let href = $(i).attr('src');
+          code_arr.push(href.slice(href.indexOf('code=') + 5));
+        } catch (err) {
+        }
+    });
+    if(!code_arr.length) return showToast('请插入图表');
+    const res=await apiChart.refreshChartMultiple({ChartInfoCode:code_arr})
+    if(res.Ret===200){
+        $('iframe').each((k,i) => {
+          $(i).attr('src',$(i).attr('src'))
+        });
+        showToast('刷新成功')
+    }
+}
+
+
+// 报告操作
+async function handleReportOpt(e){
+    //如果富文本中有未上传完成的图片,去除这个dom
+    $('#editor .fr-element').find('img.fr-uploading').length&&$('#editor .fr-element').find('img.fr-uploading').remove()
+    const params={
+        AddType:reportBaseInfoData.addType,
+        ClassifyIdFirst:reportBaseInfoData.classifyName[0]?.id,
+        ClassifyNameFirst:reportBaseInfoData.classifyName[0]?.text,
+        ClassifyIdSecond:reportBaseInfoData.classifyName[1]?.id,
+        ClassifyNameSecond:reportBaseInfoData.classifyName[1]?.text,
+        Title:reportBaseInfoData.title,
+        Abstract:reportBaseInfoData.abstract,
+        Author:reportBaseInfoData.author.join(','),
+        Frequency:reportBaseInfoData.frequency[0],
+        Content:$('#editor .fr-element').html(),
+        CreateTime:moment(reportBaseInfoData.createtime).format('YYYY.MM.DD'),
+        State: 1,
+        ReportVersion: 2,
+        Overview:temOverviewData.value
+    }
+    
+    if(reportBaseInfoData.classifyName.length!=2){
+        showToast('请选择分类')
+        return
+    }
+    if(!reportBaseInfoData.title){
+        showToast('请输入标题')
+        return
+    }
+    if(!params.Overview){
+        showToast('请输入overview')
+        return
+    }
+    if(!params.Content){
+        showToast('请输入content')
+        return
+    }
+    if(!params.Abstract){
+        showToast('请输入摘要')
+        return
+    }
+
+    if(e==='yl'){
+        sessionStorage.setItem('reportEnPreData',JSON.stringify(params))
+        const routerEl=router.resolve({
+            path:'/reportEn/detail',
+            query:{
+                id:-1
+            }
+        })
+        window.open(routerEl.href,'_blank')
+        return
+    }
+
+    const res=route.query.id
+    ?await apiReportEn.reportEdit({ReportId:Number(route.query.id),...params})
+    :await apiReportEn.reportAdd(params)
+    if(res.Ret!==200) return
+    cachedViewsStore.removeCaches('ReportEnList')
+    if(e==='cg'){
+        showToast('保存成功')
+        setTimeout(() => {
+            router.replace({
+                path:'/reportEn/edit',
+                query:{
+                    id:res.Data.ReportId
+                }
+            })
+        }, 1000);
+    }
+    if(e==='fb'){
+        reportPublish(res.Data.ReportId)
+    }
+    
+}
+
+// 发布报告
+function reportPublish(id){
+    apiReportEn.reportPublish({ReportIds:id.toString()}).then(res=>{
+        if(res.Ret===200){
+            showToast('发布成功')
+            setTimeout(() => {
+                router.back()
+            }, 1500);
+        }
+    })
+}
+
+</script>
+
+<template>
+    <div class="reporten-add-page">
+        <van-cell title="基础信息" is-link @click="showReportBaseInfo=true"/>
+        <van-cell title="Overview" is-link @click="handleShowOverview"/>
+        <div class="main-wrap">
+            <div class="editor-box" id="editor"></div>
+        </div>
+        <!-- 底部操作 -->
+        <div class="bot-action-box">
+            <div class="left-box">
+                <div class="item" @click="handleRefreshAllChart">
+                    <img src="@/assets/imgs/report/icon_refresh.png" alt="">
+                    <span>刷新</span>
+                </div>
+                <div class="item" @click="handleReportOpt('yl')">
+                    <img src="@/assets/imgs/report/icon_preview.png" alt="">
+                    <span>预览</span>
+                </div>
+                <div class="item" @click="handleReportOpt('cg')">
+                    <img src="@/assets/imgs/report/icon_save2.png" alt="">
+                    <span>保存</span>
+                </div>
+                <div class="item" @click="handleReportOpt('fb')">
+                    <img src="@/assets/imgs/report/icon_publish3.png" alt="">
+                    <span>发布</span>
+                </div>
+            </div>
+            <div class="right-btn" @click="showReportInsertPop=true">
+                <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+                    <path d="M12.0499 15.9499V27.5H15.9499V15.9499H27.5V12.0499H15.9499V0.5H12.0499V12.0499H0.5V15.9499H12.0499Z" fill="white"/>
+                </svg>
+            </div>
+        </div>
+    </div>
+
+    <!-- 报告基础信息 -->
+    <van-popup
+        v-model:show="showReportBaseInfo"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <EditReportBaseInfo v-if="showReportBaseInfo" :defaultData="reportBaseInfoData" @close="showReportBaseInfo=false" @confirm="handleReportBaseInfoChange"/>
+    </van-popup>
+
+    <!-- 报告overview -->
+    <van-popup
+        v-model:show="showEditOverview"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="overview-edit-wrap">
+            <h3>Overview</h3>
+            <div id="editor-overview"></div>
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="primary" @click="handleSaveOverview">保存</van-button>
+            </div>
+        </div>
+    </van-popup>
+
+    <!-- 报告插入数据模块 -->
+    <van-popup
+        v-model:show="showReportInsertPop"
+        position="bottom"
+        round
+    >
+        <report-insert-content v-if="showReportInsertPop" @insert="handleInsert"/>
+    </van-popup>
+
+</template>
+
+<style lang="scss" scoped>
+.reporten-add-page{
+    height: 100dvh;
+    min-height: 95vh;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+}
+@media screen and (min-width:$media-width){
+    .reporten-add-page{
+        height: calc(100dvh - 60px);
+        min-height: calc(95vh - 60px);
+    }
+}
+.van-cell{
+    flex-shrink: 0;
+}
+.main-wrap{
+    flex: 1;
+    width: calc(100% - 32PX);
+    margin: 0 auto;
+    margin-top: 30px;
+    .editor-box{
+        width: 100%;
+        height: 100%;
+    }
+}
+@media screen and (min-width:$media-width){
+    .main-wrap{
+        margin-top: 15px;
+    }
+}
+.bot-action-box{
+    padding: 20px 16PX;
+    display: flex;
+    align-items: center;
+    .left-box{
+        flex: 1;
+        background: #FFFFFF;
+        box-shadow: 0px 12px 60px 10px rgba(0, 0, 0, 0.05), 0px 32px 48px 4px rgba(0, 0, 0, 0.04), 0px 16px 20px -10px rgba(0, 0, 0, 0.08);
+        border-radius: 100px;
+        height: 112px;
+        display: flex;
+        align-items: center;
+        margin-right: 20px;
+        padding: 0 20px;
+        .item{
+            flex: 1;
+            text-align: center;
+            font-size: 20px;
+            img{
+                width: 40px;
+                height: 40px;
+                display: block;
+                margin: 5px auto;
+            }
+        }
+    }
+    .right-btn{
+        flex-shrink: 0;
+        position: relative;
+        width: 96px;
+        height: 96px;
+        background-color: $theme-color;
+        border-radius: 50%;
+        box-shadow: 0px 6px 28px 4px rgba(0, 0, 0, 0.05), 0px 16px 20px 2px rgba(0, 0, 0, 0.06), 0px 10px 10px -6px rgba(0, 0, 0, 0.1);
+        svg{
+            width: 27px;
+            height: 27px;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%,-50%);
+        }
+    }
+}
+@media screen and (min-width:$media-width){
+    .bot-action-box{
+        margin: 0 auto;
+        width: 600px;
+        padding: 10px 16px;
+        .left-box{
+            border-radius: 50px;
+            height: 56px;
+            margin-right: 10px;
+            padding: 0 10px;
+            .item{
+                font-size: 12px;
+                img{
+                    width: 20px;
+                    height: 20px;
+                    margin: 3px auto;
+                }
+            }
+        }
+        .right-btn{
+            width: 48px;
+            height: 48px;
+            svg{
+                width: 14px;
+                height: 14px;
+            }
+        }
+    }
+}
+
+.overview-edit-wrap{
+    overflow: hidden;
+    padding: $page-padding;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    #editor-overview{
+        flex: 1;
+    }
+    .bot-btns{
+        text-align: center;
+        padding-top: $page-padding;
+        .bot-btn{
+            width: 90%;
+        }
+    }
+}
+@media screen and (min-width:$media-width){
+    .overview-edit-wrap{
+        padding: $page-padding;
+        .bot-btns{
+            padding-top: $page-padding;
+        }
+    }
+    
+}
+
+</style>

+ 35 - 118
src/views/reportEn/Detail.vue

@@ -3,18 +3,26 @@ import {ref} from 'vue'
 import { useRoute, useRouter } from "vue-router";
 import apiReportEn from '@/api/reportEn'
 import { showToast,showDialog } from 'vant';
-import SendEmail from './components/SendEmail.vue';
-import {useCachedViewsStore} from '@/store/modules/cachedViews'
-const cachedViewsStore=useCachedViewsStore()
 
 const route=useRoute()
 const router=useRouter()
 
-const showSendEmail=ref(false)
-
 // 获取报告详情
 let reportInfo=ref(null)
 async function getReportDetail(){
+    if(route.query.id==-1){
+        //编辑时预览来的
+        const data=JSON.parse(sessionStorage.getItem('reportEnPreData'))
+        reportInfo.value={
+            Title:data.Title,
+            Author:data.Author,
+            PublishTime:data.CreateTime,
+            Abstract:data.Abstract,
+            Overview:data.Overview,
+            Content:data.Content
+        }
+        return
+    }
     const res=await apiReportEn.getReportDetail({ReportId:Number(route.query.id)})
     if(res.Ret===200){
         reportInfo.value=res.Data
@@ -23,101 +31,38 @@ async function getReportDetail(){
 }
 getReportDetail()
 
-// 发布报告
-async function handleReportPublish(){
-    showDialog({
-        title: '发布提示',
-        message: `是否发布报告?`,
-        showCancelButton:true
-    }).then(()=>{
-        apiReportEn.reportPublish({
-            ReportIds:route.query.id
-        }).then(res=>{
-            if(res.Ret===200){
-                showToast('发布成功')
-                cachedViewsStore.removeCaches('ReportEnList')
-                getReportDetail()
-            }
-        })
+//去编辑
+async function goEdit(){
+    // 先mark一下
+    const markRes=await apiReportEn.reportMark({
+        Status:1,
+        ReportId:reportInfo.value.Id
     })
-}
-
-// 取消发布
-function handleReportPublishCancle(){
-    showDialog({
-        title: '提示',
-        message: `是否确认取消发布?`,
-        showCancelButton:true
-    }).then(()=>{
-        apiReportEn.reportPublishCancle({ReportIds:Number(route.query.id)}).then(res=>{
-            if(res.Ret===200){
-                showToast('取消发布成功')
-                cachedViewsStore.removeCaches('ReportEnList')
-                getReportDetail()
-            }
-        })
-    })
-}
-
-// 删除报告
-function handleDelReport(){
-    showDialog({
-        title: '提示',
-        message: '删除操作不可恢复,确认删除吗?',
-        showCancelButton:true
-    }).then(() => {
-        // on close
-        apiReportEn.reportDel({ReportIds:Number(route.query.id)}).then(res=>{
-            if(res.Ret===200){
-                showToast('删除成功')
-                cachedViewsStore.removeCaches('ReportEnList')
-                router.back()
-            }
-        })
-    }).catch(()=>{})
-}
+    if(markRes.Ret===200){
+        if(markRes.Data.Status===1){
+            showToast(markRes.Data.Msg || '该研报正在编辑,不可重复编辑')
+            return
+        }
+    }else{
+        showToast(markRes.ErrMsg || '未知错误,请稍后重试')
+        return
+    }
 
-// 查看群发邮件日志
-function handleGoEmailLog(){
     router.push({
-        path:'/reportEn/sendemaillog',
+        path:'/reportEn/edit',
         query:{
-            id:route.query.id,
-            hasFail:reportInfo.value.EmailHasFail
+            id:reportInfo.value.Id
         }
     })
 }
+
 </script>
 
 <template>
     <div class="report-detail-page" v-if="reportInfo">
-        <div class="top-stage-box">
+        <div class="top-stage-box" v-if="route.query.id>0">
             <span class="stage">第{{reportInfo.Stage}}期 / {{reportInfo.Frequency}}</span>
-            <template v-if="reportInfo.State===1">
-                <!-- 删除 -->
-                <div class="btn-item red-bg" @click="handleDelReport">
-                    <img src="@/assets/imgs/icon_del.png" alt="">
-                </div>
-                <!-- 发布 -->
-                <div class="btn-item" @click="handleReportPublish">
-                    <img src="@/assets/imgs/report/icon_publish.png" alt="">
-                </div>
-            </template>
-            <template v-if="reportInfo.State===2">
-                <!-- 群发邮件 -->
-                <div class="btn-item"  @click="showSendEmail=true" v-if="reportInfo.EmailState===0&&reportInfo.EmailAuth">
-                    <img src="@/assets/imgs/report/icon_sendemail.png" alt="">
-                </div>
-                <!-- 群发日志 -->
-                <div class="btn-item"  @click="handleGoEmailLog" v-if="reportInfo.EmailState===1&&reportInfo.EmailAuth">
-                    <img src="@/assets/imgs/report/icon_sendemaillog.png" alt="">
-                </div>
-
-                <!-- 取消发布 -->
-                <div class="btn-item red-bg" @click="handleReportPublishCancle">
-                    <img src="@/assets/imgs/report/icon_publish_cancel2.png" alt="">
-                </div>
-            </template>
+            <img v-if="reportInfo.State==1" class="edit-icon" src="@/assets/imgs/report/icon_edit2.png" alt="" @click="goEdit">
         </div>
         <h1 class="report-title">{{reportInfo.Title}}</h1>
         <div class="auth-box">
@@ -129,16 +74,6 @@ function handleGoEmailLog(){
         
         <div class="report-html-wrap" v-html="reportInfo.Content"></div>
     </div>
-
-    <!-- 群发邮件弹窗 -->
-    <van-popup 
-        v-model:show="showSendEmail"
-        position="bottom"
-        :style="{ height: '100%' }"
-        closeable
-    >
-        <SendEmail :reportId="reportInfo.Id" :defaultThemeVal="reportInfo.Abstract" @close="showSendEmail=false;getReportDetail()"/>
-    </van-popup>
 </template>
 
 <style lang="scss" scoped>
@@ -196,23 +131,10 @@ function handleGoEmailLog(){
         padding: 0 20px;
         font-size: 28px;
     }
-    .btn-item{
+    .edit-icon{
         float: right;
         width: 70px;
         height: 70px;
-        border-radius: 50%;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        background-color: #F2F3FF;
-        margin-left: 40px;
-        img{
-            width: 48px;
-            height: 48px;
-        }
-    }
-    .red-bg{
-        background-color: #FFF2F2;
     }
 }
 
@@ -260,14 +182,9 @@ function handleGoEmailLog(){
             padding: 0 10px;
             font-size: 14px;
         }
-        .btn-item{
+        .edit-icon{
             width: 35px;
             height: 35px;
-            margin-left: 20px;
-            img{
-                width: 24px;
-                height: 24px;
-            }
         }
     }
 }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 120 - 10
src/views/reportEn/List.vue


+ 366 - 0
src/views/reportEn/components/EditReportBaseInfo.vue

@@ -0,0 +1,366 @@
+<script setup>
+import moment from "moment"
+import { computed, reactive,ref } from "vue"
+import ListClassify from './ListClassify.vue'
+import apiReportEn from '@/api/reportEn'
+import {reportFrequencyOpts} from '@/views/report/utils/config'
+import { showToast } from "vant"
+import { useRoute } from "vue-router"
+import { useWindowSize } from '@vueuse/core'
+
+const { width, height } = useWindowSize()
+
+const route=useRoute()
+
+const props=defineProps({
+    defaultData:null
+})
+
+const emits=defineEmits(['close','confirm'])
+
+// 基本数据
+const reportBaseInfo=reactive({
+    addType:props.defaultData?props.defaultData.addType:1,
+    classifyName:props.defaultData?props.defaultData.classifyName:[],
+    author:props.defaultData?props.defaultData.author:['Horizon Insights FICC Team'],
+    frequency: props.defaultData?props.defaultData.frequency:['日度'],
+    createtime:props.defaultData?props.defaultData.createtime:moment().format('YYYY-MM-DD'),
+    title:props.defaultData?props.defaultData.title:'',
+    abstract:props.defaultData?props.defaultData.abstract:''
+})
+
+// 报告新增类型
+const showAddTypePop=ref(false)
+const addTypeOpts=[
+    {
+        value:1,
+        name:'新增报告'
+    },
+    {
+        value:2,
+        name:'继承报告'
+    }
+]
+function handleShowAddType(){
+    // 编辑时不可操作
+    if(route.path=='/reportEn/edit') return
+    showAddTypePop.value=true
+}
+function getAddTypeName(value){
+    return addTypeOpts.filter(item=>item.value===value)[0].name
+}
+function selectAddType(e){
+    reportBaseInfo.addType=e.value
+}
+
+// 报告所属分类
+const showClassifyPop=ref(false)
+const setClassifyVal=computed(()=>{
+    if(reportBaseInfo.classifyName.length===2){
+        return `${reportBaseInfo.classifyName[0].text}/${reportBaseInfo.classifyName[1].text}`
+    }
+    return '请选择分类'
+})
+function handleShowClassify(){
+    showClassifyPop.value=true
+}
+function handleConfirmClassify({firstClassify,secondClassify}){
+    if(!firstClassify.id||!secondClassify.id){
+        showToast('请选择分类')
+        return
+    }
+    reportBaseInfo.classifyName=[firstClassify,secondClassify]
+    reportBaseInfo.title=secondClassify.text
+    showClassifyPop.value=false
+}
+
+// 研报作者
+const showAuthorPop=ref(false)
+const authorOpts=ref([])
+const temAuthorVal=ref([])
+function handleShowSelectAuthor(){
+    temAuthorVal.value=reportBaseInfo.author
+    showAuthorPop.value=true
+}
+function handleConfirmAuthor(){
+    reportBaseInfo.author=temAuthorVal.value
+    showAuthorPop.value=false
+}
+function getAuthorOpts(){
+    apiReportEn.reportAuthorList({}).then(res=>{
+        if(res.Ret===200){
+            authorOpts.value=res.Data?.List??[]
+        }
+    })
+}
+getAuthorOpts()
+
+// 报告频度
+const showFrequencyPop=ref(false)
+const temFrequencyVal=ref(['日度'])
+function handleShowFrequency(){
+    temFrequencyVal.value=reportBaseInfo.frequency
+    showFrequencyPop.value=true
+}
+function handleConfirmFrequency(){
+    reportBaseInfo.frequency=temFrequencyVal.value
+    showFrequencyPop.value=false
+}
+
+// 创建日期
+const minDate=new Date(2015, 0, 1)
+const defaultDate=ref(new Date())
+const showCreateTimePop=ref(false)
+function handleShowCreatetime(){
+    defaultDate.value=new Date(reportBaseInfo.createtime.replace(/-/g,'/'))
+    showCreateTimePop.value=true
+}
+function handleConfirmCreatime(e){
+    reportBaseInfo.createtime=moment(e).format('YYYY-MM-DD')
+    showCreateTimePop.value=false
+}
+
+// 报告标题
+const showReportTitlePop=ref(false)
+const temReportTitleVal=ref('')
+function handleShowReportTitle(){
+    temReportTitleVal.value=reportBaseInfo.title
+    showReportTitlePop.value=true
+}
+function handleConfirmReportTitle(){
+    reportBaseInfo.title=temReportTitleVal.value
+    showReportTitlePop.value=false
+}
+
+// 摘要
+const showReportAbsPop=ref(false)
+const temReportAbsVal=ref('')
+function handleShowReportAbs(){
+    temReportAbsVal.value=reportBaseInfo.abstract
+    showReportAbsPop.value=true
+}
+function handleConfirmReportAbs(){
+    reportBaseInfo.abstract=temReportAbsVal.value
+    showReportAbsPop.value=false
+}
+
+
+
+function close(){
+    emits('close')
+}
+
+function handleSave(){
+    emits('confirm',reportBaseInfo)
+}
+</script>
+
+<template>
+    <div class="report-baseinfo-wrap">
+        <van-cell-group>
+            <van-cell value-class="cell-con" required title="新增方式" :value="getAddTypeName(reportBaseInfo.addType)" :is-link="route.path!='/reportEn/edit'" @click="handleShowAddType"/>
+            <van-cell value-class="cell-con" required title="分类" :value="setClassifyVal" is-link @click="handleShowClassify"/>
+            <van-cell value-class="cell-con" title="作者" :value="reportBaseInfo.author.join(',')" is-link @click="handleShowSelectAuthor"/>
+            <van-cell value-class="cell-con" title="频度" :value="reportBaseInfo.frequency.join('')" is-link @click="handleShowFrequency"/>
+            <van-cell value-class="cell-con" title="创建时间" :value="reportBaseInfo.createtime" is-link @click="handleShowCreatetime"/>
+        </van-cell-group>
+
+        <van-cell-group style="margin:10px 0">
+            <van-cell required title="标题" :label="reportBaseInfo.title" is-link @click="handleShowReportTitle"/>
+        </van-cell-group>
+
+        <van-cell-group>
+            <van-cell required title="摘要" :label="reportBaseInfo.abstract" is-link @click="handleShowReportAbs"/>
+        </van-cell-group>
+
+        <div class="bot-btns">
+            <van-button class="bot-btn" type="default" @click="close">取消</van-button>
+            <van-button class="bot-btn" type="primary" @click="handleSave">保存</van-button>
+        </div>
+        
+    </div>
+
+    <!-- 新增方式 -->
+    <van-action-sheet 
+        v-model:show="showAddTypePop"
+        cancel-text="取消"
+        close-on-click-action
+        :actions="addTypeOpts" 
+        @select="selectAddType" 
+    />
+
+    <!-- 分类 -->
+    <van-popup
+        v-model:show="showClassifyPop"
+        position="bottom"
+        round
+    >
+        <ListClassify
+            v-if="showClassifyPop"
+            :defaultVal="reportBaseInfo.classifyName"
+            :noReset="true"
+            :firstClassifyDisabled="true" 
+            @close="showClassifyPop=false" 
+            @confirm="handleConfirmClassify"
+        />
+    </van-popup>
+
+    <!-- 作者 -->
+    <van-popup
+        v-model:show="showAuthorPop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="select-author-pop">
+            <van-checkbox-group v-model="temAuthorVal">
+                <van-checkbox
+                    v-for="item in authorOpts"
+                    :key="item.Id"
+                    :name="item.ReportAuthor"
+                >{{item.ReportAuthor}}</van-checkbox>
+            </van-checkbox-group>
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showAuthorPop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" @click="handleConfirmAuthor">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+
+    <!-- 报告频度 -->
+    <van-popup
+        v-model:show="showFrequencyPop"
+        position="bottom"
+        round
+    >
+        <van-picker 
+            v-model="temFrequencyVal" 
+            title="选择频度" 
+            :columns="reportFrequencyOpts"
+            @confirm="handleConfirmFrequency"
+            @cancel="showFrequencyPop=false"
+        />
+    </van-popup>
+
+    <!-- 创建日期 -->
+    <van-popup 
+        v-model:show="showCreateTimePop"
+        :position="width>650?'center':'bottom'"
+        :style="width>650?{ width: '400px'}:''"
+        round
+    >
+        <van-calendar
+            :poppable="false"
+            :min-date="minDate"
+            :default-date="defaultDate"
+            title="选择创建日期"
+            @confirm="handleConfirmCreatime"
+            :style="{ height: '500px' }"
+        />
+    </van-popup>
+
+    <!-- 标题 -->
+    <van-popup
+        v-model:show="showReportTitlePop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="input-report-title-pop">
+             <van-field v-model="temReportTitleVal" placeholder="请输入报告标题" />
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showReportTitlePop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" :disabled="!temReportTitleVal" @click="handleConfirmReportTitle">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+
+    <!-- 摘要 -->
+    <van-popup
+        v-model:show="showReportAbsPop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="input-report-title-pop">
+             <van-field type="textarea" autosize v-model="temReportAbsVal" placeholder="请输入报告摘要" />
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showReportAbsPop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" :disabled="!temReportAbsVal" @click="handleConfirmReportAbs">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+</template>
+
+<style lang="scss" scoped>
+.report-baseinfo-wrap{
+    height: 100%;
+    position: relative;
+    background: $page-bg-grey;
+    :deep(.cell-con){
+        flex: 2;
+    }
+    .bot-btns{
+        position: absolute;
+        bottom: 48px;
+        left: 0;
+        width: 100%;
+        text-align: center;
+    }
+}
+.bot-btn{
+    width: 315px;
+    margin: 0 10px;
+}
+
+.select-author-pop{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .van-checkbox-group{
+        padding: $page-padding;
+        flex: 1;
+        overflow-y: auto;
+        .van-checkbox{
+            :deep(.van-checkbox__label){
+                padding: 32px 0;
+                flex: 1;
+                border-bottom: 1px solid $border-color;
+            }
+        }
+    }
+    .bot-btns{
+        flex-shrink: 0;
+        padding: 20px 0;
+        text-align: center;
+    }
+}
+.input-report-title-pop{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    background-color: $page-bg-grey;
+    
+    .bot-btns{
+        flex-shrink: 0;
+        padding: 20px 0;
+        text-align: center;
+    }
+}
+
+@media screen and (min-width:$media-width){
+    .report-baseinfo-wrap{
+        .bot-btns{
+            bottom: 24px;
+        }
+    }
+    .select-author-pop{
+        .van-checkbox-group{
+            padding: $page-padding;
+            .van-checkbox{
+                :deep(.van-checkbox__label){
+                    padding: 16px 0;
+                }
+            }
+        }
+    }
+}
+</style>

+ 270 - 0
src/views/reportEn/components/EditStrategyReport.vue

@@ -0,0 +1,270 @@
+<script setup>
+import moment from "moment"
+import { reactive,ref } from "vue"
+import apiReportEn from '@/api/reportEn'
+import {reportFrequencyOpts} from '@/views/report/utils/config'
+import { showToast } from "vant"
+
+
+const props=defineProps({
+    defaultData:null
+})
+
+const emits=defineEmits(['close','confirm'])
+
+const baseInfo=reactive({
+    author:props.defaultData?[props.defaultData.Author]:['Horizon Insights FICC Team'],
+    frequency: props.defaultData?[props.defaultData.Frequency]:['日度'],
+    createtime:props.defaultData?moment(props.defaultData.CreateTime).format('YYYY-MM-DD'):moment().format('YYYY-MM-DD'),
+    title:props.defaultData?props.defaultData.Title:'',
+    abstract:props.defaultData?props.defaultData.Abstract:''
+})
+
+// 研报作者
+const showAuthorPop=ref(false)
+const authorOpts=ref([])
+const temAuthorVal=ref([])
+function handleShowSelectAuthor(){
+    temAuthorVal.value=baseInfo.author
+    showAuthorPop.value=true
+}
+function handleConfirmAuthor(){
+    baseInfo.author=temAuthorVal.value
+    showAuthorPop.value=false
+}
+function getAuthorOpts(){
+    apiReportEn.reportAuthorList({}).then(res=>{
+        if(res.Ret===200){
+            authorOpts.value=res.Data?.List??[]
+        }
+    })
+}
+getAuthorOpts()
+
+// 报告频度
+const showFrequencyPop=ref(false)
+const temFrequencyVal=ref(['日度'])
+function handleShowFrequency(){
+    temFrequencyVal.value=baseInfo.frequency
+    showFrequencyPop.value=true
+}
+function handleConfirmFrequency(){
+    baseInfo.frequency=temFrequencyVal.value
+    showFrequencyPop.value=false
+}
+
+// 创建日期
+const minDate=new Date(2015, 0, 1)
+const defaultDate=ref(new Date())
+const showCreateTimePop=ref(false)
+function handleShowCreatetime(){
+    defaultDate.value=new Date(baseInfo.createtime.replace(/-/g,'/'))
+    showCreateTimePop.value=true
+}
+function handleConfirmCreatime(e){
+    baseInfo.createtime=moment(e).format('YYYY-MM-DD')
+    showCreateTimePop.value=false
+}
+
+// 报告标题
+const showReportTitlePop=ref(false)
+const temReportTitleVal=ref('')
+function handleShowReportTitle(){
+    temReportTitleVal.value=baseInfo.title
+    showReportTitlePop.value=true
+}
+function handleConfirmReportTitle(){
+    baseInfo.title=temReportTitleVal.value
+    showReportTitlePop.value=false
+}
+
+// 摘要
+const showReportAbsPop=ref(false)
+const temReportAbsVal=ref('')
+function handleShowReportAbs(){
+    temReportAbsVal.value=baseInfo.abstract
+    showReportAbsPop.value=true
+}
+function handleConfirmReportAbs(){
+    baseInfo.abstract=temReportAbsVal.value
+    showReportAbsPop.value=false
+}
+
+
+
+function close(){
+    emits('close')
+}
+
+async function handleSave(){
+    const res=await apiReportEn.strategyReportEdit({
+        ReportId:props.defaultData.Id,
+        Title:baseInfo.title,
+        Abstract:baseInfo.abstract,
+        Author:baseInfo.author.join(','),
+        Frequency:baseInfo.frequency[0],
+        CreateTime:baseInfo.createtime
+    })
+    if(res.Ret===200){
+        showToast('编辑成功')
+        emits('confirm')
+    }
+}
+</script>
+
+<template>
+    <div class="edit-strategy-report-wrap">
+        <van-cell-group>
+            <van-cell value-class="cell-con" required title="分类" :value="defaultData.ClassifyNameFirst+'/'+defaultData.ClassifyNameSecond" />
+            <van-cell value-class="cell-con" title="作者" :value="baseInfo.author.join(',')" is-link @click="handleShowSelectAuthor"/>
+            <van-cell value-class="cell-con" title="频度" :value="baseInfo.frequency.join('')" is-link @click="handleShowFrequency"/>
+            <van-cell value-class="cell-con" title="创建时间" :value="baseInfo.createtime" is-link @click="handleShowCreatetime"/>
+        </van-cell-group>
+
+        <van-cell-group style="margin:10px 0">
+            <van-cell required title="标题" :label="baseInfo.title" is-link @click="handleShowReportTitle"/>
+        </van-cell-group>
+
+        <van-cell-group>
+            <van-cell required title="摘要" :label="baseInfo.abstract" is-link @click="handleShowReportAbs"/>
+        </van-cell-group>
+
+        <div class="bot-btns">
+            <van-button class="bot-btn" type="default" @click="close">取消</van-button>
+            <van-button class="bot-btn" type="primary" @click="handleSave">保存</van-button>
+        </div>
+    </div>
+
+    <!-- 作者 -->
+    <van-popup
+        v-model:show="showAuthorPop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="select-author-pop">
+            <van-checkbox-group v-model="temAuthorVal">
+                <van-checkbox
+                    v-for="item in authorOpts"
+                    :key="item.Id"
+                    :name="item.ReportAuthor"
+                >{{item.ReportAuthor}}</van-checkbox>
+            </van-checkbox-group>
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showAuthorPop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" @click="handleConfirmAuthor">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+
+    <!-- 报告频度 -->
+    <van-popup
+        v-model:show="showFrequencyPop"
+        position="bottom"
+        round
+    >
+        <van-picker 
+            v-model="temFrequencyVal" 
+            title="选择频度" 
+            :columns="reportFrequencyOpts"
+            @confirm="handleConfirmFrequency"
+            @cancel="showFrequencyPop=false"
+        />
+    </van-popup>
+
+    <!-- 创建日期 -->
+    <van-calendar
+        :min-date="minDate"
+        :default-date="defaultDate"
+        v-model:show="showCreateTimePop"
+        title="选择创建日期"
+        @confirm="handleConfirmCreatime" 
+    />
+
+    <!-- 标题 -->
+    <van-popup
+        v-model:show="showReportTitlePop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="input-report-title-pop">
+             <van-field v-model="temReportTitleVal" placeholder="请输入报告标题" />
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showReportTitlePop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" :disabled="!temReportTitleVal" @click="handleConfirmReportTitle">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+
+    <!-- 摘要 -->
+    <van-popup
+        v-model:show="showReportAbsPop"
+        position="bottom"
+        :style="{ height: '100%' }"
+    >
+        <div class="input-report-title-pop">
+             <van-field type="textarea" autosize v-model="temReportAbsVal" placeholder="请输入报告摘要" />
+            <div class="bot-btns">
+                <van-button class="bot-btn" type="default" @click="showReportAbsPop=false">取消</van-button>
+                <van-button class="bot-btn" type="primary" :disabled="!temReportAbsVal" @click="handleConfirmReportAbs">确定</van-button>
+            </div>
+        </div>
+    </van-popup>
+</template>
+
+<style lang="scss" scoped>
+.edit-strategy-report-wrap{
+    height: 100%;
+    position: relative;
+    background: $page-bg-grey;
+    :deep(.cell-con){
+        flex: 2;
+    }
+    .bot-btns{
+        position: absolute;
+        bottom: 48px;
+        left: 0;
+        width: 100%;
+        text-align: center;
+    }
+}
+.bot-btn{
+    width: 315px;
+    margin: 0 10px;
+}
+
+.select-author-pop{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .van-checkbox-group{
+        padding: $page-padding;
+        flex: 1;
+        overflow-y: auto;
+        .van-checkbox{
+            :deep(.van-checkbox__label){
+                padding: 32px 0;
+                flex: 1;
+                border-bottom: 1px solid $border-color;
+            }
+        }
+    }
+    .bot-btns{
+        flex-shrink: 0;
+        padding: 20px 0;
+        text-align: center;
+    }
+}
+.input-report-title-pop{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    background-color: $page-bg-grey;
+    
+    .bot-btns{
+        flex-shrink: 0;
+        padding: 20px 0;
+        text-align: center;
+    }
+}
+</style>

+ 35 - 6
src/views/reportEn/components/ListClassify.vue

@@ -2,6 +2,12 @@
 import {ref} from 'vue'
 import apiReportEn from '@/api/reportEn'
 
+const props=defineProps({
+    defaultVal:null,
+    noReset:false,
+    firstClassifyDisabled:false,//一级分类是否要判断禁用
+})
+
 const emits=defineEmits(['close','confirm'])
 
 const list=ref([])
@@ -9,7 +15,7 @@ function getClassifyList(){
     apiReportEn.getClassifyList({
         CurrentIndex:1,
         PageSize:1000,
-        KeyWord:''
+        KeyWord:'',
     }).then(res=>{
         if(res.Ret===200){
             const arr=res.Data.List||[]
@@ -17,14 +23,24 @@ function getClassifyList(){
                 const child=item.Child?item.Child.map(e=>{
                     return {
                         text:e.ClassifyName,
-                        id:e.ClassifyName
+                        id:e.Id
                     }
                 }):[]
                 return {
                     text:item.ClassifyName,
-                    children:child
+                    id:item.Id,
+                    children:child,
+                    disabled:props.firstClassifyDisabled&&child.length===0?true:false,
                 }
             })
+            if(props.defaultVal&&props.defaultVal[1]){
+                activeId.value=props.defaultVal[1].id
+                list.value.forEach((item,index)=>{
+                    if(item.id===props.defaultVal[0].id){
+                        activeIndex.value=index
+                    }
+                })
+            }
         }
     })
 }
@@ -44,8 +60,19 @@ function handleReset(){
 }
 
 function handleConfirm(){
-    const firstClassify=list.value[activeIndex.value].text
-    const secondClassify=activeId.value||''
+    const firstClassify={
+        text:list.value[activeIndex.value].text,
+        id:list.value[activeIndex.value].id
+    }
+    let secondClassify={text:'',id:''}
+    if(activeId.value){
+        list.value[activeIndex.value].children.forEach(e => {
+            if(e.id===activeId.value){
+                secondClassify.text=e.text
+                secondClassify.id=e.id
+            }
+        });
+    }
     emits('confirm',{firstClassify,secondClassify})
 }
 </script>
@@ -53,7 +80,8 @@ function handleConfirm(){
 <template>
     <div class="report-list-classify-wrap">
         <div class="top-box">
-            <span style="color:#666666" @click="handleReset">重置</span>
+            <span style="color:#666666" @click="handleCancle" v-if="props.noReset">取消</span>
+            <span style="color:#666666" @click="handleReset" v-else>重置</span>
             <span style="font-size:18px;font-weight:bold">选择分类</span>
             <span style="color:#0052D9" @click="handleConfirm">确定</span>
         </div>
@@ -61,6 +89,7 @@ function handleConfirm(){
             v-model:active-id="activeId"
             v-model:main-active-index="activeIndex"
             :items="list"
+            @click-nav="activeId=null"
         />
     </div>
 </template>

+ 6 - 2
src/views/tabbar/Home.vue

@@ -126,8 +126,12 @@ getMenuList()
                 <div>{{item.name}}</div>
             </div>
             
-            <div class="item-box" style="border:none"></div>
-            <div class="item-box" style="border:none"></div>
+            <div class="item-box" style="border:none;height:0"></div>
+            <div class="item-box" style="border:none;height:0"></div>
+            <div class="item-box" style="border:none;height:0"></div>
+            <div class="item-box" style="border:none;height:0"></div>
+            <div class="item-box" style="border:none;height:0"></div>
+            <div class="item-box" style="border:none;height:0"></div>
         </div>
         
     </div>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác