瀏覽代碼

Merge branch 'master' of http://8.136.199.33:3000/eta_front/eta_mobile_front

cldu 3 天之前
父節點
當前提交
bc51640f0e

+ 2 - 1
package.json

@@ -32,6 +32,7 @@
     "minio-es": "0.0.2",
     "moment": "^2.29.4",
     "normalize.css": "^8.0.1",
+    "pdf-lib": "^1.17.1",
     "pinia": "^2.0.36",
     "pptxgenjs": "^3.12.0",
     "rollup-plugin-node-polyfills": "^0.2.1",
@@ -39,8 +40,8 @@
     "vant": "^4.6.4",
     "vconsole": "^3.15.0",
     "vue": "^3.5.13",
-    "vue-qr": "^4.0.9",
     "vue-i18n": "^10.0.1",
+    "vue-qr": "^4.0.9",
     "vue-router": "^4.1.6",
     "vue3-clipboard": "^1.0.0",
     "vue3-tree-org": "^4.2.2",

+ 2 - 1
src/CustomElement/EtaChart.ce.vue

@@ -35,7 +35,8 @@ async function getChartData(){
             IsReplace:1
         }): await chartInfoByCode({
             UniqueCode:params.code,
-            IsReplace:1
+            IsReplace:1,
+            AuthToken: params.authToken || ''
         })
         if(res.Ret!==200) return
         chartInfo.value=res.Data.ChartInfo

+ 2 - 1
src/CustomElement/EtaTable.ce.vue

@@ -31,7 +31,8 @@ async function getTableData(){
         UniqueCode: params.code,
         FromScene: Number(params.fromScene||''),
         ReferencedId: Number(params.sourceId||''),
-        Uuid: params.uid||""
+        Uuid: params.uid||"",
+        AuthToken: params.authToken || ''
     });
     if(res.Ret !== 200) return
 

+ 2 - 2
src/api/chart.js

@@ -112,8 +112,8 @@ export default{
     /**
      * 获取ETA图库分类数据
      */
-    ETAChartClassifyList:()=>{
-        return get('/datamanage/chart_classify/items',{})
+    ETAChartClassifyList:params=>{
+        return get('/datamanage/chart_classify/items',params)
     },
     /**
      * ETA图库图表另存为

+ 10 - 0
src/api/common.js

@@ -93,4 +93,14 @@ export function getSystemInfo(){
  */
 export function uploadImgAPi(params) {
     return post('/banner/upload',params)
+}
+
+
+/**
+ * 获取复制链接
+ * @param {*} params 
+ * @returns 
+ */
+export function shareGenerate(params){
+    return post('/report/share/generate',params)
 }

+ 5 - 0
src/api/etaConfig.js

@@ -8,4 +8,9 @@ import { get,post } from "./index";
  */
 export const getBaseConfig = params =>{
     return get('/business_conf/fetch',params)
+}
+
+  /* 图表分享的临时有效期token */
+export const getChartShareToken = params => {
+    return get('/datamanage/chart_info/common/general_token',params)
 }

+ 2 - 1
src/hooks/chart/render.js

@@ -268,13 +268,14 @@ function setAxisPlotAreas(axis,axisType) {
             'middle': 0,
             'bottom': -10
         }
+        let text =  _.text.replace(/\n|\r\n/g, '<br/>');
 
         return { 
             from: fromMarkerValue,
             to: toMarkerValue,
             color: _.color,
             label: {
-                text: _.text||'',
+                text,
                 verticalAlign: _.textPosition,
                 y: positionMapValue[_.textPosition],
                 style: {

+ 287 - 59
src/hooks/useDownLoadFile.js

@@ -1,11 +1,11 @@
 // 下载文件
 import axios from "axios";
 import { showToast } from "vant";
-import {ref} from 'vue'
-
+import { ref } from "vue";
+import { PDFDocument } from "pdf-lib";
 
 /**
- * 
+ * 下载文件
  * @returns {
  *  progress 下载进度
  *  fileSize 文件大小
@@ -13,63 +13,291 @@ import {ref} from 'vue'
  *  cancelDownload  取消下载
  * }
  */
-export function useDownLoadFile(){
-    let progress=ref(0)//进度 0-1
-    let fileSize=ref(0)//文件大小
-
-    const controller = new AbortController();
-
-    // 开始下载
-    const startDownload=(url,filename)=>{
-        axios({
-            url:url,
-            method:'get',
-            responseType:'blob',
-            signal: controller.signal,
-            onDownloadProgress:function(progressEvent){
-                // console.log(progressEvent);
-                fileSize.value=Math.floor(progressEvent.total/1024/1024)
-                progress.value = Math.floor(progressEvent.progress*100)
-            }
-        }).then(res=>{
-            // console.log(res);
-            const {status,data}=res
-            if(status!=200){
-                showToast('下载失败')
-                return
-            }
-            const content = data
-            const blob = new Blob([content])
-            if ('download' in document.createElement('a')) {
-                const elink = document.createElement('a')
-                elink.download = filename || url.split('/')[url.split('/').length - 1]
-                elink.style.display = 'none'
-                elink.href = window.URL.createObjectURL(blob)
-                document.body.appendChild(elink)
-                elink.click()
-                window.URL.revokeObjectURL(elink.href)
-                document.body.removeChild(elink)
-            } else {
-                navigator.msSaveBlob(blob, filename)
+export function useDownLoadFile() {
+  let progress = ref(0); //进度 0-1
+  let fileSize = ref(0); //文件大小
+
+  const controller = new AbortController();
+
+  // 开始下载
+  const startDownload = (url, filename) => {
+    axios({
+      url: url,
+      method: "get",
+      responseType: "blob",
+      signal: controller.signal,
+      onDownloadProgress: function (progressEvent) {
+        // console.log(progressEvent);
+        fileSize.value = Math.floor(progressEvent.total / 1024 / 1024);
+        progress.value = Math.floor(progressEvent.progress * 100);
+      },
+    })
+      .then((res) => {
+        // console.log(res);
+        const { status, data } = res;
+        if (status != 200) {
+          showToast("下载失败");
+          return;
+        }
+        const content = data;
+        const blob = new Blob([content]);
+        if ("download" in document.createElement("a")) {
+          const elink = document.createElement("a");
+          elink.download = filename || url.split("/")[url.split("/").length - 1];
+          elink.style.display = "none";
+          elink.href = window.URL.createObjectURL(blob);
+          document.body.appendChild(elink);
+          elink.click();
+          window.URL.revokeObjectURL(elink.href);
+          document.body.removeChild(elink);
+        } else {
+          navigator.msSaveBlob(blob, filename);
+        }
+      })
+      .catch((e) => {
+        console.log(e);
+        showToast("下载失败");
+      });
+  };
+
+  //取消下载
+  const cancelDownload = () => {
+    // 取消请求
+    controller.abort();
+    progress.value = 0;
+    fileSize.value = 0;
+  };
+
+  return {
+    fileSize,
+    progress,
+    startDownload,
+    cancelDownload,
+  };
+}
+
+/**
+ * 下载文件加水印
+ */
+export function useDownLoadFileAddWaterMark() {
+  let progress = ref(0); //进度 0-1
+  let fileSize = ref(0); //文件大小
+
+  const controller = new AbortController();
+
+  // 获取水印图片数据
+  function getWaterMark(str) {
+    return new Promise((resolve, reject) => {
+      const text = str || ""; // 默认水印文字
+      const canvas = document.createElement("canvas");
+
+      // 设置canvas尺寸,增加分辨率
+      const canvasWidth = 800;
+      const canvasHeight = 400;
+      canvas.width = canvasWidth;
+      canvas.height = canvasHeight;
+
+      const ctx = canvas.getContext("2d");
+
+      // 增大字体尺寸并设置样式
+      ctx.font = "50px Arial"; // 字体大小
+      ctx.textAlign = "center"; // 水平居中
+      ctx.textBaseline = "middle"; // 垂直居中
+      ctx.fillStyle = "#333"; // 水印文字颜色
+      ctx.globalAlpha = 0.1; // 水印透明度,避免过于显眼
+
+      ctx.translate(canvasWidth / 2, canvasHeight / 2); // 移动到中心
+      ctx.rotate((-45 * Math.PI) / 200); // 旋转 -45 度
+      ctx.fillText(text, 0, 0); // 在旋转后的中心绘制文字
+
+      // 将canvas内容转换为base64编码的PNG图片
+      const data = canvas.toDataURL("image/png");
+      resolve(data);
+    });
+  }
+
+  // 辅助方法:将 Blob 转换为 ArrayBuffer
+  function blobToArrayBuffer(blob) {
+    return new Promise((resolve, reject) => {
+      const reader = new FileReader();
+      reader.onload = () => resolve(reader.result);
+      reader.onerror = (error) => reject(error);
+      reader.readAsArrayBuffer(blob);
+    });
+  }
+
+  /**
+   * 开始下载
+   * @param type pdf img
+   * @returns
+   */
+  const startDownload = async ({ url, filename, waterMark, type }) => {
+    try {
+      const res = await axios({
+        url: url,
+        method: "get",
+        responseType: "blob",
+        signal: controller.signal,
+        onDownloadProgress: function (progressEvent) {
+          // console.log(progressEvent);
+          fileSize.value = Math.floor(progressEvent.total / 1024 / 1024);
+          progress.value = Math.floor(progressEvent.progress * 100);
+        },
+      });
+      // console.log(res);
+      const { status, data } = res;
+      if (status != 200) {
+        showToast("下载失败");
+        return;
+      }
+      const blob = new Blob([data]);
+      // 不需要水印
+      if (!waterMark) {
+        if ("download" in document.createElement("a")) {
+          const elink = document.createElement("a");
+          elink.download = filename || url.split("/")[url.split("/").length - 1];
+          elink.style.display = "none";
+          elink.href = window.URL.createObjectURL(blob);
+          document.body.appendChild(elink);
+          elink.click();
+          window.URL.revokeObjectURL(elink.href);
+          document.body.removeChild(elink);
+        } else {
+          navigator.msSaveBlob(blob, filename);
+        }
+        return;
+      }
+
+      if (type === "pdf") {
+				// 加载 PDF 文档 将 blob 转化为 `string` or `Uint8Array` or `ArrayBuffer`
+				const pdfBytes = await blobToArrayBuffer(blob); // 将 Blob 转为 ArrayBuffer
+				const pdfDoc = await PDFDocument.load(pdfBytes);
+				const pages = pdfDoc.getPages();
+
+				// 获取水印图像
+				const watermarkImageData = await getWaterMark(waterMark);
+				const watermarkImage = await pdfDoc.embedPng(watermarkImageData);
+
+				pages.forEach(page => {
+					const { width, height } = page.getSize();
+
+					// 水印图像的大小 (适应页面大小并保持比例)
+					const imageWidth = width / 5;  // 水印宽度为页面宽度的1/5
+					const imageHeight = watermarkImage.height * (imageWidth / watermarkImage.width);
+
+					// 设置水印透明度
+					const opacity = 1;  // 控制透明度
+
+					// 计算水印重复的个数
+					const cols = Math.ceil(width / imageWidth);  // 水印在水平方向的个数
+					const rows = Math.ceil(height / imageHeight); // 水印在垂直方向的个数
+
+					// 在页面上绘制水印(按格子重复)
+					for (let row = 0; row < rows; row++) {
+						for (let col = 0; col < cols; col++) {
+							const x = col * imageWidth;
+							const y = row * imageHeight;
+
+							page.drawImage(watermarkImage, {
+								x: x,
+								y: y,
+								width: imageWidth,
+								height: imageHeight,
+								opacity: opacity,  // 设置透明度
+							});
+						}
+					}
+				});
+
+				// 导出加水印后的 PDF
+				const modifiedPdfBytes = await pdfDoc.save();
+				const blobRes = new Blob([modifiedPdfBytes], { type: 'application/pdf' });
+				if ('download' in document.createElement('a')) {
+					const elink = document.createElement('a');
+					elink.download = filename || url.split("/")[url.split("/").length - 1];
+					elink.style.display = 'none';
+					elink.href = window.URL.createObjectURL(blobRes);
+					document.body.appendChild(elink);
+					elink.click();
+					window.URL.revokeObjectURL(elink.href);
+					document.body.removeChild(elink);
+				} else {
+					navigator.msSaveBlob(blobRes, filename);
+				}
+
+      } else if (type === "img") {
+        // 处理图片加水印
+        const img = new Image();
+        img.src = URL.createObjectURL(blob);
+
+        img.onload = async () => {
+          // 创建canvas并绘制图片
+          const canvas = document.createElement("canvas");
+          const ctx = canvas.getContext("2d");
+          canvas.width = img.width;
+          canvas.height = img.height;
+
+          // 绘制原始图片
+          ctx.drawImage(img, 0, 0, img.width, img.height);
+
+          // 获取水印图像
+          const watermarkImageData = await getWaterMark(waterMark);
+          const watermarkImg = new Image();
+          watermarkImg.src = watermarkImageData;
+
+          watermarkImg.onload = () => {
+            const watermarkWidth = img.width / 5; // 设置水印宽度
+            const watermarkHeight = (watermarkImg.height / watermarkImg.width) * watermarkWidth; // 计算水印高度,保持比例
+
+            // 计算水印的重复个数
+            const cols = Math.ceil(img.width / watermarkWidth); // 水印在水平方向的个数
+            const rows = Math.ceil(img.height / watermarkHeight); // 水印在垂直方向的个数
+
+            // 绘制水印
+            for (let row = 0; row < rows; row++) {
+              for (let col = 0; col < cols; col++) {
+                const x = col * watermarkWidth;
+                const y = row * watermarkHeight;
+                ctx.drawImage(watermarkImg, x, y, watermarkWidth, watermarkHeight);
+              }
             }
-        }).catch((e)=>{
-            console.log(e);
-            showToast('下载失败')
-        })
-    }
 
-    //取消下载
-    const cancelDownload=()=>{
-        // 取消请求
-        controller.abort()
-        progress.value=0
-        fileSize.value=0
+            // 导出加水印后的图片
+            canvas.toBlob((modifiedBlob) => {
+              const blobRes = modifiedBlob;
+              if ("download" in document.createElement("a")) {
+                const elink = document.createElement("a");
+								elink.download = filename || url.split("/")[url.split("/").length - 1];
+								elink.style.display = "none";
+                elink.href = window.URL.createObjectURL(blobRes);
+                document.body.appendChild(elink);
+                elink.click();
+                window.URL.revokeObjectURL(elink.href);
+                document.body.removeChild(elink);
+              } else {
+                navigator.msSaveBlob(blobRes, filename);
+              }
+            }, "image/png");
+          };
+        };
+      }
+    } catch (error) {
+      showToast("下载失败,请稍后重试");
     }
+  };
 
-    return {
-        fileSize,
-        progress,
-        startDownload,
-        cancelDownload
-    }
-}
+  //取消下载
+  const cancelDownload = () => {
+    // 取消请求
+    controller.abort();
+    progress.value = 0;
+    fileSize.value = 0;
+  };
+
+  return {
+    fileSize,
+    progress,
+    startDownload,
+    cancelDownload,
+  };
+}

+ 6 - 2
src/hooks/useReportApprove.js

@@ -6,14 +6,17 @@ export function useReportApprove(){
     let isApprove = ref(false)
     let isOtherApprove = ref(false)
     let hasApproveFlow = ref(false)
+    const isLinkChartExpired = ref(false)//报告插入的图表是否开启有效期
+
     const configSettingStore = useConfigSettingStore()
     const { etaConfigInfo } = storeToRefs(configSettingStore)
     //获取基本配置,判断是否走审批流
     const getEtaConfig = async()=>{
         await configSettingStore.getBaseConfigSetting()
-        const {IsReportApprove='',ReportApproveType=''} = etaConfigInfo.value
+        const {IsReportApprove='',ReportApproveType='',IsOpenChartExpired} = etaConfigInfo.value
         isApprove.value = IsReportApprove==='true'?true:false
         isOtherApprove.value = isApprove.value&&ReportApproveType==='other' 
+        isLinkChartExpired.value = IsOpenChartExpired==='true'?true:false;
     }
     //检查分类是否存在审批流
     const checkClassifyNameArr = (type=1,classify=[])=>{
@@ -33,6 +36,7 @@ export function useReportApprove(){
         isOtherApprove,
         hasApproveFlow,
         getEtaConfig,
-        checkClassifyNameArr
+        checkClassifyNameArr,
+        isLinkChartExpired
     }
 }

+ 42 - 19
src/views/chartETA/ChartDetail.vue

@@ -6,6 +6,7 @@ import { useRoute, useRouter } from 'vue-router'
 import { useWindowSize } from '@vueuse/core'
 import {yearSelectOpt,sameOptionType} from '@/hooks/chart/config'
 import {useChartRender} from '@/hooks/chart/render'
+import { getChartShareExpireToken } from './hooks/useChartSharedToken'
 import moment from 'moment'
 import EdbInfo from './components/EdbInfo.vue'
 import SourceDetail from './components/SourceDetail.vue'
@@ -394,7 +395,12 @@ async function getShareLink(){
     }
     if(!confirmFlag.value) return 
     // const linkUrl = `${import.meta.env.VITE_CHART_LINK}?code=${chartInfo.value.UniqueCode}&fromType=share&lang=${currentLang}`
-    const linkUrl = `${publicSettingStore.publicSetting.ChartViewUrl}/chartshow?code=${chartInfo.value.UniqueCode}&fromType=share&lang=${currentLang}`
+
+    //临时权限token
+    const res = await getChartShareExpireToken(chartInfo.value.UniqueCode);
+    if(res.Ret !== 200) return
+
+    const linkUrl = `${publicSettingStore.publicSetting.ChartViewUrl}/chartshow?code=${chartInfo.value.UniqueCode}&fromType=share&lang=${currentLang}&authToken=${res.Data}`
     //console.log('url',linkUrl)
     if(navigator.clipboard&&window.isSecureContext){
          try{
@@ -612,29 +618,46 @@ function openAddToMyETADialog(){
     isShowAddToMyETADialog.value = true
 }
 //删除图表
-function deleteChart(){
+async function deleteChart(){
+    const res = await apiChart.deleteCheck({
+        ChartClassifyId:Number(routeQueryData.chartClassifyId),
+        ChartInfoId:Number(routeQueryData.id),
+    })
+    if(res.Ret!==200) return
+    const hintTextMap = {
+        0:'确定删除当前图表吗?',
+        1:'该分类下关联图表不可删除!',
+        2:'确认删除当前分类及包含的子分类吗?',
+        3:'删除后,该图表将从ETA投研资源库同步删除,影响客户的查看权限,是否确认删除?'
+    }
     showDialog({
         title: '提示',
-        message: '删除后该图表将不能再引用,确认删除吗?',
-        showCancelButton:true
+        message: hintTextMap[res.Data.DeleteStatus||0],
+        showCancelButton:res.Data.DeleteStatus!==1
     }).then(() => {
-        apiChart.deleteClassify({
-            ChartClassifyId:Number(routeQueryData.chartClassifyId),
-            ChartInfoId:Number(routeQueryData.id),
-        }).then(res=>{
-            if(res.Ret===200){
-                cachedViewsStore.removeCaches('ChartETAList')
-                cachedViewsStore.removeCaches('ChartETASearch')
-                showToast('删除成功')
-                setTimeout(() => {
-                    router.back()
-                }, 1000);
-            }
-        })
-    }).catch(()=>{
-        
+        if(res.Data.DeleteStatus!==1){
+            handleDeleteRequest()
+        }
+    }).catch(()=>{})
+}
+
+// 删除请求
+function handleDeleteRequest(){
+    apiChart.deleteClassify({
+        ChartClassifyId:Number(routeQueryData.chartClassifyId),
+        ChartInfoId:Number(routeQueryData.id),
+    }).then(res=>{
+        if(res.Ret===200){
+            cachedViewsStore.removeCaches('ChartETAList')
+            cachedViewsStore.removeCaches('ChartETASearch')
+            showToast('删除成功')
+            setTimeout(() => {
+                router.back()
+            }, 1000);
+        }
     })
 }
+
 // 保存至素材库
 const showSaveToMaterial=ref(false)
 function handleShowSaveToMaterial(){

+ 16 - 1
src/views/chartETA/List.vue

@@ -27,6 +27,7 @@ const {
     optArrNode,
     authOptArr,
 } = useCatalogList()
+const category = ref('')
 
 //跳转至图表详情页
 const goChartDetail = (item)=>{
@@ -370,6 +371,7 @@ async function getChartList(){
         CurrentIndex: page,
         ChartClassifyId: cid,
         IsShowMe,
+        IsSelected:category.value
     })
     if(res.Ret!==200) return 
     const arr = res.Data?res.Data.List:[]
@@ -396,6 +398,14 @@ function onLoad(){
     getChartList()
 }
 
+function onClickTab() {
+    getCatalogList(category.value)
+    listState.list=[]
+    listState.page=1
+    listState.finished = true 
+    getChartList()
+}
+
 getCatalogList()
 getChartList()
 </script>
@@ -457,7 +467,12 @@ getChartList()
         <van-popup v-model:show="IsShowCatalog" position="right" class="catalog-list-wrap" style="height:100%">
             <div class="catalog-list">
                 <div class="top sticky-part">
-                    <h3>分类</h3>
+                    <!-- <h3>分类</h3>
+                    <h3>精选资源</h3> -->
+                    <van-tabs v-model:active="category" @click-tab="onClickTab" :ellipsis="false">
+                        <van-tab title="分类"></van-tab>
+                        <van-tab title="精选资源"></van-tab>
+                    </van-tabs>
                     <van-icon name="cross" @click.stop="IsShowCatalog=false"/>
                 </div>
                 <!-- 将目录改为三级 -->

+ 5 - 2
src/views/chartETA/hooks/useCatalogList.js

@@ -70,8 +70,11 @@ export function useCatalogList(){
     const catalogNodes = ref([])
     const currentLang = ref('')
     //获取目录列表
-    async function getCatalogList(){
-        const res = await apiChart.ETAChartClassifyList()
+    async function getCatalogList(IsSelected){
+        const params = {
+            IsSelected: IsSelected
+        }
+        const res = await apiChart.ETAChartClassifyList(params)
         if(res.Ret!==200) return 
         currentLang.value = res.Data.Language
         localStorage.setItem('chartETALange',currentLang.value)

+ 15 - 0
src/views/chartETA/hooks/useChartSharedToken.js

@@ -0,0 +1,15 @@
+
+import { getBaseConfig,getChartShareToken } from '@/api/etaConfig.js';
+
+/* 分享图表link时 加有效期超时无权限 Source chart/table*/
+export async function getChartShareExpireToken(UniqueCode='',Source="chart") {
+    const res = await getBaseConfig();
+    if(res.Ret!==200) return
+    
+    const { IsOpenChartExpired } = res.Data;
+    let needCheckAuth = IsOpenChartExpired==='true'?true:false;
+    
+    if(!needCheckAuth) return { Data:'',Ret:200 }
+    return await getChartShareToken({ UniqueCode,Source })
+
+}

+ 36 - 19
src/views/myETA/ChartDetail.vue

@@ -22,6 +22,7 @@ import ChartSaveOther from './components/ChartSaveOther.vue'
 import _ from 'lodash';
 import { useWindowSize } from '@vueuse/core'
 import {setClipboardData} from '@/hooks/common'
+import { getChartShareExpireToken } from '@/views/chartETA/hooks/useChartSharedToken'
 import SetChartEnName from '@/components/SetChartEnName.vue'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import AddChartToMyETA from '@/views/chartETA/components/AddChartToMyETA.vue'
@@ -533,25 +534,37 @@ function handleRemoveChart(){
 }
 
 //删除图表
-function handleDeleteChart(){
+async function handleDeleteChart(){ 
+    const res = await apiChart.deleteCheck({
+        ChartClassifyId:chartInfo.value.chartClassifyId,
+        ChartInfoId:chartInfo.value.ChartInfoId,
+    })
+    if(res.Ret!==200) return
+    const hintTextMap = {
+        0:'确定删除当前图表吗?',
+        1:'该分类下关联图表不可删除!',
+        2:'确认删除当前分类及包含的子分类吗?',
+        3:'删除后,该图表将从ETA投研资源库同步删除,影响客户的查看权限,是否确认删除?'
+    }
     showDialog({
         title: '提示',
-        message: '删除后该图表将不能再引用,确认删除吗?',
-        showCancelButton:true
-    }).then(async () => {
-        // on close
-        const res=chartInfo.value.Source===11?await apiSheet.balanceSheetChartDel({
-            ChartInfoId:chartInfo.value.ChartInfoId
-        }):await apiMyETAChart.chartDel({
-            ChartInfoId:chartInfo.value.ChartInfoId
-        })
-        if(res.Ret===200){
-            showToast('删除成功')
-            cachedViewsStore.removeCaches('MyETAIndex')
-            cachedViewsStore.removeCaches('MyETASearchList')
-            setTimeout(() => {
-                router.back()
-            }, 1500)
+        message: hintTextMap[res.Data.DeleteStatus||0],
+        showCancelButton:res.Data.DeleteStatus!==1
+    }).then(async() => {
+        if(res.Data.DeleteStatus!==1){
+            const res=chartInfo.value.Source===11?await apiSheet.balanceSheetChartDel({
+                ChartInfoId:chartInfo.value.ChartInfoId
+            }):await apiMyETAChart.chartDel({
+                ChartInfoId:chartInfo.value.ChartInfoId
+            })
+            if(res.Ret===200){
+                showToast('删除成功')
+                cachedViewsStore.removeCaches('MyETAIndex')
+                cachedViewsStore.removeCaches('MyETASearchList')
+                setTimeout(() => {
+                    router.back()
+                }, 1500)
+            }
         }
     }).catch(()=>{})
 }
@@ -587,9 +600,13 @@ async function handleChartRefresh(){
 }
 
 //分享
-function handleChartShare(){
+async function handleChartShare(){
     // const url=import.meta.env.VITE_CHART_LINK+`?code=${chartInfo.value.UniqueCode}&fromType=share&lang=ch`
-    const url=publicSettingStore.publicSetting.ChartViewUrl+`/chartshow?code=${chartInfo.value.UniqueCode}&fromType=share&lang=ch`
+    //临时权限token
+    const res = await getChartShareExpireToken(chartInfo.value.UniqueCode);
+    if(res.Ret !== 200) return
+
+    const url=publicSettingStore.publicSetting.ChartViewUrl+`/chartshow?code=${chartInfo.value.UniqueCode}&fromType=share&lang=ch&authToken=${res.Data}`
     setClipboardData(url)
 }
 //保存图表:目前myETA里能保存的只有ETA图库的图和商品价格曲线图

+ 25 - 6
src/views/report/EditReport.vue

@@ -7,6 +7,7 @@ import ReportPublishTimeSet from './components/ReportPublishTimeSet.vue'
 import apiReport from '@/api/report'
 // import {getSystemInfo} from '@/api/common'
 import moment from 'moment'
+import { getChartShareToken } from '@/api/etaConfig.js';
 import { showToast,showDialog } from 'vant'
 import { useRoute, useRouter } from 'vue-router'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
@@ -18,7 +19,7 @@ import { useReportHandles,useChapterRepoprtHandles } from './hooks/useReport'
 
 const cachedViewsStore=useCachedViewsStore()
 const publicSettingStore = usePublicSettingStore()
-const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr} = useReportApprove()
+const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr,isLinkChartExpired} = useReportApprove()
 const router=useRouter()
 const route=useRoute()
 const {checkAuthBtn} = useAuthBtn()
@@ -157,6 +158,18 @@ async function handleReportBaseInfoChange(e){
     showReportBaseInfo.value=false
 }
 
+
+async function getLinkAuthToken(UniqueCode,Source='chart') {
+    let authToken = ''
+    if(isLinkChartExpired.value) { //开启了图表有效期
+        const res = await getChartShareToken({ UniqueCode,Source })
+        if(res.Ret !== 200) return
+        authToken = res.Data;
+    }
+
+    return authToken;
+}
+
 // 报告插入数据弹窗
 const showReportInsertPop=ref(false)
 /**
@@ -164,7 +177,7 @@ const showReportInsertPop=ref(false)
  * type:iframe/img 插入的为iframe或者图片
  * chartType: chart-图表,sheet-表格
  */
-function handleInsert({list,type,chartType}){
+async function handleInsert({list,type,chartType}){
     reportContentEditorIns.events.focus()
     if(lastFocusPosition.value){
         reportContentEditorIns.selection.get().removeAllRanges()
@@ -172,21 +185,27 @@ function handleInsert({list,type,chartType}){
     }
     if(type==='iframe'){
         let link=publicSettingStore.publicSetting.ChartViewUrl;
+
         if(chartType==='chart'){
             // link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
             link=link+'/chartshow'
-            list.forEach(item => {
+            list.forEach(async(item) => {
+                //临时权限token
+                let authToken = await getLinkAuthToken(item);
                 const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
                 reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
-						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=&isETAForumChart=${isETAForumChart}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=&isETAForumChart=${isETAForumChart}&authToken=${authToken}' 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'
             link=link+'/sheetshow'
-            list.forEach(item => {
+            list.forEach(async(item) => {
+                //临时权限token
+                let authToken = await getLinkAuthToken(item,'table');
+
                 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>
+						<iframe src='${link}?code=${item}&authToken=${authToken}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
 					</p>`,false)
             });
         }

+ 82 - 10
src/views/report/List.vue

@@ -9,7 +9,7 @@ import { showToast,showDialog,Dialog } from 'vant';
 import { useRouter } from 'vue-router';
 import { useWindowSize } from '@vueuse/core'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
-import {useDownLoadFile} from '@/hooks/useDownLoadFile'
+import {useDownLoadFileAddWaterMark} from '@/hooks/useDownLoadFile'
 import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {useReportApprove} from '@/hooks/useReportApprove'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
@@ -18,12 +18,13 @@ import {isWeiXin} from '@/hooks/common'
 import AddReportBaseInfoV2 from './components/AddReportBaseInfoV2.vue'
 import ReportFilter from './components/ReportFilter.vue'
 import AudioBox from './components/AudioBox.vue'
-
+import { useConfigSettingStore } from '@/store/modules/etaConfig'
+import { storeToRefs } from 'pinia'
 
 const cachedViewsStore=useCachedViewsStore()
 const publicSettingStore = usePublicSettingStore()
 
-const {startDownload}=useDownLoadFile()
+const {startDownload}=useDownLoadFileAddWaterMark()
 const {isApprove,isOtherApprove,getEtaConfig} = useReportApprove()
 
 const {checkAuthBtn} = useAuthBtn()
@@ -241,17 +242,53 @@ function handleReportCancel(item){
     }).catch(()=>{})
 }
 
-// type 1-pdf 2-长图
-function downloadPdfImg(item,type){
+// type 1-电脑版pdf 2-电脑版长图 3-移动版pdf 4-移动版长图
+async function downloadPdfImg(item,type){
+    let waterMark=''
+    // 判断是否需要水印
+    await useConfigSettingStore().getBaseConfigSetting()
+    const configSettingStore = useConfigSettingStore()
+    const { etaConfigInfo } = storeToRefs(configSettingStore)
+    if(etaConfigInfo.value.WatermarkDownloadPdf==='true'){
+        // 设置水印
+        waterMark=decodeURIComponent(Base64.decode(waterMarkStr.value))
+    }
     showReportItemOpt.value=false
     let name = `${item.Title}${moment().format('YYYYMMDD')}`
     if(type == 1){
         // window.open(item.DetailPdfUrl,"_blank")
-        startDownload(item.DetailPdfUrl,`${name}.pdf`)
-    }else{
-        startDownload(item.DetailImgUrl,`${name}.jpeg`)
+        startDownload({
+            url:item.DetailPdfUrl,
+            filename:`${name}.pdf`,
+            waterMark:waterMark,
+            type:'pdf'
+        })
+    }else if(type===2){
+        startDownload({
+            url:item.DetailImgUrl,
+            filename:`${name}.jpg`,
+            waterMark:waterMark,
+            type:'img'
+        })
+    }else if(type===3){
+        startDownload({
+            url:item.DetailPdfUrlMobile,
+            filename:`${name}.pdf`,
+            waterMark:waterMark,
+            type:'pdf'
+        })
+    }else if(type===4){
+        startDownload({
+            url:item.DetailImgUrlMobile,
+            filename:`${name}.jpg`,
+            waterMark:waterMark,
+            type:'img'
+        })
     }
+    showDownloadPdfImg.value=false
 }
+const showDownloadPdfImg=ref(false)
+const downloadPdfImgType=ref('pdf')
 
 // 发布弹窗关闭
 function handlePublishPopClose(refresh){
@@ -670,6 +707,41 @@ onMounted(async ()=>{
         </svg>
     </div>
 
+    <!-- 下载pdf和图片选择下载类型 -->
+    <van-action-sheet 
+        v-model:show="showDownloadPdfImg"
+        cancel-text="取消"
+        close-on-click-action
+    >
+        <div class="report-item-action-box" v-if="activeItem">
+            <div class="title">{{downloadPdfImgType==='pdf'?'下载pdf':'下载长图'}}</div>
+            <template v-if="downloadPdfImgType==='pdf'">
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(reportManageBtn.reportManage_exportPdf) && activeItem.DetailPdfUrl"
+                    @click="downloadPdfImg(activeItem,1)"
+                >电脑版</div>
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(reportManageBtn.reportManage_exportPdf) && activeItem.DetailPdfUrlMobile"
+                    @click="downloadPdfImg(activeItem,3)"
+                >手机版</div>
+            </template>
+            <template v-if="downloadPdfImgType==='img'">
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(reportManageBtn.reportManage_exportImg) && activeItem.DetailImgUrl"
+                    @click="downloadPdfImg(activeItem,2)"
+                >电脑版</div>
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(reportManageBtn.reportManage_exportImg) && activeItem.DetailImgUrlMobile"
+                    @click="downloadPdfImg(activeItem,4)"
+                >手机版</div>
+            </template>
+        </div>
+    </van-action-sheet>
+
     <!-- 报告item操作 -->
     <van-action-sheet 
         teleport="body"
@@ -702,9 +774,9 @@ onMounted(async ()=>{
                 <div class="item" v-if="checkAuthBtn(reportManageBtn.reportManage_cancelPublish)&&activeItem.State===6&&activeItem.HasAuth"
                     @click="handleReportCancel(activeItem)">撤销</div>
                 <div class="item" v-if="checkAuthBtn(reportManageBtn.reportManage_exportPdf) && activeItem.DetailPdfUrl"
-                    @click="downloadPdfImg(activeItem,1)">下载pdf</div>
+                    @click="showDownloadPdfImg=true;downloadPdfImgType='pdf'">下载pdf</div>
                 <div class="item" v-if="checkAuthBtn(reportManageBtn.reportManage_exportImg) && activeItem.DetailImgUrl"
-                    @click="downloadPdfImg(activeItem,2)">下载长图</div>   
+                    @click="showDownloadPdfImg=true;downloadPdfImgType='img'">下载长图</div>
                 <div class="item" @click="handldReportMsgSend(activeItem)" v-if="activeItem.MsgIsSend==0&&checkAuthBtn(reportManageBtn.reportManage_sendMsg)&&activeItem.HasAuth">推送消息</div>
             </template>
             <!-- 待审批,已驳回 -->

+ 92 - 16
src/views/report/PreviewDetail.vue

@@ -1,6 +1,8 @@
 <script setup name="ReportPreview">
 import { ref,computed, nextTick, reactive,toRefs } from 'vue'
 import { useRoute, useRouter } from "vue-router";
+import {getSystemInfo,shareGenerate} from '@/api/common'
+import {Base64} from 'js-base64'
 import apiReport from '@/api/report'
 import AudioBox from './components/AudioBox.vue'
 import {showToast} from 'vant'
@@ -8,6 +10,8 @@ import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import { copyText } from 'vue3-clipboard'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import vueQr from 'vue-qr/src/packages/vue-qr.vue'
+import { useConfigSettingStore } from '@/store/modules/etaConfig'
+import { storeToRefs } from 'pinia'
 
 
 const {checkAuthBtn} = useAuthBtn()
@@ -16,9 +20,39 @@ const publicSettingStore = usePublicSettingStore()
 const route=useRoute()
 const router=useRouter()
 
+// 智能布局移动端样式时内容排版全部变成1个1行的顺排
+function formatSmartStyle() {
+    nextTick(() =>{
+        const width=window.innerWidth;
+        if(width>600) return
+        $('.report-drag-item-wrap_child-wrap').css({
+            'flex-wrap': 'wrap',
+        });
+        $('.report-drag-item-wrap_child-wrap').children().css({
+            'flex': 'none',
+            'width': '100%'
+        });
+        
+        document.querySelectorAll('div[comp-type="text"]').forEach(function(div) {
+            div.style.height = 'auto';
+        });
+    })
+}
+
+const configStore = useConfigSettingStore()
+const { etaConfigInfo } = storeToRefs(configStore)
+    //获取基本配置,判断是否走审批流
+async function getEtaConfig(){
+    await configStore.getBaseConfigSetting()
+}
+getEtaConfig()
+const dynamicClassName = computed(() => {
+    return etaConfigInfo.value.IsOpenChartExpired==='true'?'select-text-disabled':'';
+})
 
 // 获取报告详情
 let reportInfo=ref(null)
+let shareUrls=ref(null)
 const smartState = reactive({
     bgColor:'',
     headImgStyle:null,//版头style
@@ -47,6 +81,11 @@ async function getReportDetail(){
         smartState.layoutBaseInfo['研报标题']=reportInfo.value.Title
         smartState.layoutBaseInfo['研报作者']=reportInfo.value.Author
         smartState.layoutBaseInfo['创建时间']=[2,6].includes(reportInfo.value.State)?reportInfo.value.PublishTime:''
+
+        if(res.Data.ReportLayout===2){
+            formatSmartStyle()
+        }
+        getSystemInfoFun()
     }
 }
 if(route.query.id==-1){
@@ -62,36 +101,73 @@ const { bgColor,headImgStyle,endImgStyle,layoutBaseInfo } = toRefs(smartState)
 
 
 const showImgPop = ref(false)
-const linkUrl = computed(() =>{
-    console.log(publicSettingStore)
+const handleCopyLink = (type) =>{
     let str=''
+    let url=''
     const baseUrl= publicSettingStore.publicSetting.ReportViewUrl;
     if(reportInfo.value.ReportCode){
         // 设置水印文案
-        let waterMarkStr= '';
+        // let waterMarkStr= '';
 
         str= reportInfo.value.ReportLayout===1 
-            ? `${baseUrl}/reportshare_crm_report?code=${reportInfo.value.ReportCode}&flag=${waterMarkStr}& ${reportInfo.value.Title}`
+            ? `${baseUrl}/reportshare_crm_report?code=${reportInfo.value.ReportCode}&flag=${waterMarkStr.value}& ${reportInfo.value.Title}`
             : `${baseUrl}/reportshare_smart_report?code=${reportInfo.value.ReportCode}& ${reportInfo.value.Title}`
+
+        const params={
+            "Url":str,
+            "ReportId":reportInfo.value.Id
+        } 
+        shareGenerate(params).then(res=>{
+            if(res.Ret===200){
+                console.log(res)
+                if(location.port=='5173'){
+                    url='http://8.136.199.33:8611'
+                }else{
+                    url=location.origin
+                }
+                shareUrls.value=url+'/v1/share/'+res.Data.UrlToken
+
+                if(type==='urcode') {
+                    showImgPop.value=true 
+                }else {
+                    copyText(shareUrls.value,undefined,(error,event)=>{
+                        if(error){
+                            showToast('复制链接失败')
+
+                            throw new Error('复制数据失败'+JSON.stringify(error))
+                        }else{
+                            showToast('复制链接成功')
+                        }
+                    })
+                }
+            }
+        })
     }
-    
-    return str
+}
+const shareCodeUrls = computed(() => {
+    let index = shareUrls.value.indexOf(' ');
+    return shareUrls.value.substring(0,index)
 })
-function handleCopyLink() {
-    copyText(linkUrl.value,undefined,(error,event)=>{
-        if(error){
-            showToast('复制链接成功')
 
-            throw new Error('复制数据失败'+JSON.stringify(error))
-        }else{
-            showToast('复制链接成功')
+const waterMarkStr=ref('')
+const getSystemInfoFun=()=>{
+    getSystemInfo().then(res=>{
+        if(res.Ret===200){
+          const systemUserInfo=res.Data
+          // 设置水印文案
+          let waterMarkString=''
+          if(systemUserInfo){
+            waterMarkString=`${systemUserInfo.RealName}${systemUserInfo.Mobile?systemUserInfo.Mobile:systemUserInfo.Email}`
+            waterMarkString=encodeURIComponent(waterMarkString)
+            waterMarkStr.value=Base64.encode(waterMarkString)
+          }
         }
     })
 }
 </script>
 
 <template>
-    <div class="report-detail-page" v-if="reportInfo" :style="{backgroundColor:bgColor}">
+    <div class="report-detail-page" :class="dynamicClassName" v-if="reportInfo" :style="{backgroundColor:bgColor}">
         <!-- <div class="top-stage-box" v-if="$route.query.id!=-1">
             <span class="stage">第{{reportInfo.Stage}}期 / {{reportInfo.Frequency}}</span>
         </div> -->
@@ -155,7 +231,7 @@ function handleCopyLink() {
                 <img class="icon" src="@/assets/imgs/report/icon_copy.png" alt="">
                 <div>复制链接</div>
             </div>
-            <div class="item" @click="showImgPop=true" v-permission="reportManageBtn.reportManage_reportView_wechartShare">
+            <div class="item" @click="handleCopyLink('urcode')" v-permission="reportManageBtn.reportManage_reportView_wechartShare">
                 <img class="icon" src="@/assets/imgs/report/icon_wx_black.png" alt="">
                 <div>微信分享</div>
             </div>
@@ -167,7 +243,7 @@ function handleCopyLink() {
         v-model:show="showImgPop" 
         round
     >
-        <vue-qr :text="linkUrl" colorDark="#333" colorLight="#fff" :dotScale="1"></vue-qr>
+        <vue-qr :text="shareCodeUrls" colorDark="#333" colorLight="#fff" :dotScale="1"></vue-qr>
     </van-popup>
 </template>
 

+ 1 - 1
src/views/report/chapter/Preview.vue

@@ -25,7 +25,7 @@ async function getChapterDetail(){
 getChapterDetail()
 </script>
 <template>
-    <div class="report-detail-page" v-if="reportInfo">
+    <div class="report-detail-page select-text-disabled" v-if="reportInfo">
         <h1 class="report-title">{{reportInfo.Title}}</h1>
         <div class="auth-box">
             <span>{{reportInfo.Author}}</span>

+ 36 - 17
src/views/report/smartReport/EditReport.vue

@@ -4,6 +4,7 @@ import { V3ColorPicker } from "v3-color-picker-teleport"
 import ReportInsertContent from '../components/reportInsert/Index.vue'
 import ReportPublishTimeSet from '../components/ReportPublishTimeSet.vue'
 import apiReport from '@/api/report'
+import { getChartShareToken } from '@/api/etaConfig.js';
 import _ from 'lodash'
 import moment from 'moment'
 import { showToast,showDialog } from 'vant'
@@ -26,7 +27,7 @@ import ReportLayoutImg from './components/ReportLayoutImg.vue'
 
 const cachedViewsStore=useCachedViewsStore()
 const publicSettingStore = usePublicSettingStore()
-const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr} = useReportApprove()
+const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr,isLinkChartExpired} = useReportApprove()
 const router=useRouter()
 const route=useRoute()
 const {checkAuthBtn} = useAuthBtn()
@@ -159,6 +160,18 @@ watch(
 )
 
 
+async function getLinkAuthToken(UniqueCode,Source='chart') {
+    let authToken = ''
+    if(isLinkChartExpired.value) { //开启了图表有效期
+        const res = await getChartShareToken({ UniqueCode,Source })
+        if(res.Ret !== 200) return
+        authToken = res.Data;
+    }
+
+    return authToken;
+}
+
+
 /* map类型对应组件 */
 function getComponentName(item){
   const temMap=new Map([
@@ -499,7 +512,7 @@ function handleOpenComPop(e) {
   showReportInsertPop.value = true
 }
 /* 插入图表表格 */
-function handleChartInsert({list,type,chartType}){
+async function handleChartInsert({list,type,chartType}){
 		console.log(list,type,chartType)
     let tempCompDataArr=[]
     
@@ -509,30 +522,36 @@ function handleChartInsert({list,type,chartType}){
 
           link=link+'/chartshow'
 
-          tempCompDataArr = list.map(item =>{
+					for(let item of list) {
+						//临时权限token
+						const authToken = await getLinkAuthToken(item);
 						const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
-						console.log(item,isETAForumChart);
-						return {
+						tempCompDataArr.push({
 							compId:3,
 							compType:'chart',
 							id:getCompId(3),
-							content:`${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&isETAForumChart=${isETAForumChart}`,
+							content:`${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&isETAForumChart=${isETAForumChart}&authToken=${authToken}`,
 							titleText: '',
 							style:'height:350px',
 							child:[]
-						}
-					})
+						})
+					}
         }else if(chartType==='sheet'){
             link=link+'/sheetshow'
-            tempCompDataArr = list.map(item =>({
-                compId:4,
-                compType:'sheet',
-                id:getCompId(4),
-                content:`${link}?code=${item}&fromScene=1`,
-                titleText: '',
-                style:'',
-                child:[]
-            }))
+
+						for(let item of list) {
+							//临时权限token
+							const authToken = await getLinkAuthToken(item,'table');
+							tempCompDataArr.push({
+								compId:4,
+								compType:'sheet',
+								id:getCompId(4),
+								content:`${link}?code=${item}&fromScene=1&authToken=${authToken}`,
+								titleText: '',
+								style:'',
+								child:[]
+							})
+						}
         }
     }else if(type==='img'){
 

+ 23 - 5
src/views/reportEn/AddReport.vue

@@ -5,6 +5,7 @@ import ReportInsertContent from '../report/components/reportInsert/Index.vue'
 import ReportPublishTimeSet from '@/views/report/components/ReportPublishTimeSet.vue'
 import apiReportEn from '@/api/reportEn'
 import apiChart from '@/api/chart'
+import { getChartShareToken } from '@/api/etaConfig.js';
 import {getSystemInfo} from '@/api/common'
 import moment from 'moment'
 import { showToast,showDialog } from 'vant'
@@ -20,7 +21,7 @@ const cachedViewsStore=useCachedViewsStore()
 const router=useRouter()
 const route=useRoute()
 const publicSettingStore = usePublicSettingStore()
-const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr} = useReportApprove()
+const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr,isLinkChartExpired} = useReportApprove()
 
 const {lastFocusPosition,frolaEditorContentChange,imgUploadFlag,initFroalaEditor}=useInitFroalaEditor()
 
@@ -160,6 +161,18 @@ function handleSaveOverview(){
 }
 
 
+async function getLinkAuthToken(UniqueCode,Source='chart') {
+    let authToken = ''
+    if(isLinkChartExpired.value) { //开启了图表有效期
+        const res = await getChartShareToken({ UniqueCode,Source })
+        if(res.Ret !== 200) return
+        authToken = res.Data;
+    }
+
+    return authToken;
+}
+
+
 // 报告插入数据弹窗
 const showReportInsertPop=ref(false)
 /**
@@ -178,18 +191,23 @@ function handleInsert({list,type,chartType}){
         if(chartType==='chart'){
             // link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
             link=link+'/chartshow'
-            list.forEach(item => {
+            list.forEach(async(item) => {
+                //临时权限token
+                let authToken = await getLinkAuthToken(item);
                 const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
                 reportContentIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
-						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=en&isETAForumChart=${isETAForumChart}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=en&isETAForumChart=${isETAForumChart}&authToken=${authToken}' 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'
             link=link+'/sheetshow'
-            list.forEach(item => {
+            list.forEach(async(item) => {
+                //临时权限token
+                let authToken = await getLinkAuthToken(item,'table');
+
                 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>
+						<iframe src='${link}?code=${item}&authToken=${authToken}' class='iframe${item}'  width='100%' style='border-width:0px;'></iframe>
 					</p>`)
             });
         }

+ 15 - 2
src/views/reportEn/Detail.vue

@@ -1,9 +1,11 @@
 <script setup name="reportEnDetail">
-import {ref} from 'vue'
+import {ref,computed} from 'vue'
 import { useRoute, useRouter } from "vue-router";
 import apiReportEn from '@/api/reportEn'
 import { showToast,showDialog } from 'vant';
 import {enReportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
+import { useConfigSettingStore } from '@/store/modules/etaConfig'
+import { storeToRefs } from 'pinia'
 const {checkAuthBtn} = useAuthBtn()
 
 const route=useRoute()
@@ -58,10 +60,21 @@ async function goEdit(){
     })
 }
 
+const configStore = useConfigSettingStore()
+const { etaConfigInfo } = storeToRefs(configStore)
+    //获取基本配置,判断是否走审批流
+async function getEtaConfig(){
+    await configStore.getBaseConfigSetting()
+}
+getEtaConfig()
+const dynamicClassName = computed(() => {
+    return etaConfigInfo.value.IsOpenChartExpired==='true'?'select-text-disabled':'';
+})
+
 </script>
 
 <template>
-    <div class="report-detail-page" v-if="reportInfo">
+    <div class="report-detail-page" :class="dynamicClassName" v-if="reportInfo">
         <div class="top-stage-box" v-if="route.query.id>0">
             <span class="stage">第{{reportInfo.Stage}}期 / {{reportInfo.Frequency}}</span>
             <img v-if="reportInfo.State==1&&checkAuthBtn(enReportManageBtn.enReport_reportEdit)" class="edit-icon" src="@/assets/imgs/report/icon_edit2.png" alt="" @click="goEdit">

+ 86 - 10
src/views/reportEn/List.vue

@@ -12,15 +12,17 @@ import { useRouter } from 'vue-router';
 import { useWindowSize } from '@vueuse/core'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
-import {useDownLoadFile} from '@/hooks/useDownLoadFile'
+import {useDownLoadFileAddWaterMark} from '@/hooks/useDownLoadFile'
 import {enReportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {useReportApprove} from '@/hooks/useReportApprove'
 import {Base64} from 'js-base64'
+import { useConfigSettingStore } from '@/store/modules/etaConfig'
+import { storeToRefs } from 'pinia'
 
 const cachedViewsStore=useCachedViewsStore()
 const publicSettingStore = usePublicSettingStore()
 
-const {startDownload}=useDownLoadFile()
+const {startDownload}=useDownLoadFileAddWaterMark()
 const {checkAuthBtn} = useAuthBtn()
 const {isApprove,isOtherApprove,getEtaConfig} = useReportApprove()
 
@@ -186,16 +188,52 @@ function handleReportPublishCancle(item){
     })
 }
 
-// type 1-pdf 2-长图
-function downloadPdfImg(item,type){
+const showDownloadPdfImg=ref(false)
+const downloadPdfImgType=ref('pdf')
+// type 1-电脑版pdf 2-电脑版长图 3-移动版pdf 4-移动版长图
+async function downloadPdfImg(item,type){
+    let waterMark=''
+    // 判断是否需要水印
+    await useConfigSettingStore().getBaseConfigSetting()
+    const configSettingStore = useConfigSettingStore()
+    const { etaConfigInfo } = storeToRefs(configSettingStore)
+    if(etaConfigInfo.value.WatermarkDownloadPdf==='true'){
+        // 设置水印
+        waterMark=decodeURIComponent(Base64.decode(waterMarkStr.value))
+    }
     showReportItemOpt.value=false
     let name = `${item.Title}${moment().format('YYYYMMDD')}`
     if(type == 1){
         // window.open(item.DetailPdfUrl,"_blank")
-        startDownload(item.DetailPdfUrl,`${name}.pdf`)
-    }else{
-        startDownload(item.DetailImgUrl,`${name}.jpeg`)
+        startDownload({
+            url:item.DetailPdfUrl,
+            filename:`${name}.pdf`,
+            waterMark:waterMark,
+            type:'pdf'
+        })
+    }else if(type===2){
+        startDownload({
+            url:item.DetailImgUrl,
+            filename:`${name}.jpg`,
+            waterMark:waterMark,
+            type:'img'
+        })
+    }else if(type===3){
+        startDownload({
+            url:item.DetailPdfUrlMobile,
+            filename:`${name}.pdf`,
+            waterMark:waterMark,
+            type:'pdf'
+        })
+    }else if(type===4){
+        startDownload({
+            url:item.DetailImgUrlMobile,
+            filename:`${name}.jpg`,
+            waterMark:waterMark,
+            type:'img'
+        })
     }
+    showDownloadPdfImg.value=false
 }
 
 //提交报告
@@ -691,6 +729,41 @@ onMounted(()=>{
         </svg>
     </div>
 
+    <!-- 下载pdf和图片选择下载类型 -->
+    <van-action-sheet 
+        v-model:show="showDownloadPdfImg"
+        cancel-text="取消"
+        close-on-click-action
+    >
+        <div class="report-item-action-box" v-if="activeItem">
+            <div class="title">{{downloadPdfImgType==='pdf'?'下载pdf':'下载长图'}}</div>
+            <template v-if="downloadPdfImgType==='pdf'">
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(enReportManageBtn.enReport_exportPdf) && activeItem.DetailPdfUrl"
+                    @click="downloadPdfImg(activeItem,1)"
+                >电脑版</div>
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(enReportManageBtn.enReport_exportPdf) && activeItem.DetailPdfUrlMobile"
+                    @click="downloadPdfImg(activeItem,3)"
+                >手机版</div>
+            </template>
+            <template v-if="downloadPdfImgType==='img'">
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(enReportManageBtn.enReport_exportImg) && activeItem.DetailImgUrl"
+                    @click="downloadPdfImg(activeItem,2)"
+                >电脑版</div>
+                <div 
+                    class="item" 
+                    v-if="checkAuthBtn(enReportManageBtn.enReport_exportImg) && activeItem.DetailImgUrlMobile"
+                    @click="downloadPdfImg(activeItem,4)"
+                >手机版</div>
+            </template>
+        </div>
+    </van-action-sheet>
+
     <!-- 报告item操作 -->
     <van-action-sheet
         teleport="body"
@@ -724,9 +797,9 @@ onMounted(()=>{
                 <div class="item" v-if="checkAuthBtn(enReportManageBtn.enReport_cancelPublish)&&activeItem.State===6"
                     @click="handleReportCancle(activeItem)">撤销</div>
                 <div class="item" v-if="checkAuthBtn(enReportManageBtn.enReport_exportPdf) && activeItem.DetailPdfUrl"
-                    @click="downloadPdfImg(activeItem,1)">下载pdf</div>
+                    @click="showDownloadPdfImg=true;downloadPdfImgType='pdf'">下载pdf</div>
                 <div class="item" v-if="checkAuthBtn(enReportManageBtn.enReport_exportImg) && activeItem.DetailImgUrl"
-                    @click="downloadPdfImg(activeItem,2)">下载长图</div>   
+                    @click="showDownloadPdfImg=true;downloadPdfImgType='img'">下载长图</div>   
             </template>
             <!-- 待审批,已驳回 -->
             <template v-if="[4,5].includes(activeItem.State)">
@@ -1011,7 +1084,10 @@ onMounted(()=>{
 
 .report-item-action-box{
     .title{
-        padding: 20px 32px;
+        padding: 30px 32px;
+        font-weight: 700;
+        text-align: center;
+        background: #eee;
     }
     .item{
         text-align: center;