Browse Source

Merge branch 'eta_web_comp'

hbchen 11 months ago
parent
commit
514b272f01

+ 2 - 0
.env.lib

@@ -0,0 +1,2 @@
+# 这个webcomponent的库用到 eta_report项目的
+VITE_APP_BASEAPIURL="/api"

+ 1 - 0
.gitignore

@@ -10,6 +10,7 @@ lerna-debug.log*
 node_modules
 dist
 dist-ssr
+lib_dist
 *.local
 package-lock.json
 hongze_ETA_mobile

+ 1 - 0
package.json

@@ -7,6 +7,7 @@
     "dev": "vite",
     "build": "vite build --mode production",
     "build.test": "vite build --mode test",
+    "build.lib": "vite build --mode lib",
     "preview": "vite preview"
   },
   "dependencies": {

+ 199 - 0
src/CustomElement/EtaChart.ce.vue

@@ -0,0 +1,199 @@
+<script setup>
+import {nextTick,ref} from 'vue'
+import {useChartRender} from '@/hooks/chart/render'
+import {chartInfoByCode} from './api/getData.js'
+import {parseQueryString} from "./utils/index"
+const {chartRender}=useChartRender()
+
+const props=defineProps({
+    src:'',
+    width:'',
+    height:'',
+    style:''
+})
+
+// const params=reactive(parseQueryString(props.src))
+const params=parseQueryString(props.src)
+const chartContentEl=ref(null)
+const chartBoxId=ref('')
+const chartInfo=ref(null)
+const dataList=ref(null)
+const haveData=ref(false)
+const markDom=ref(null)
+async function getChartData(){
+    if(!params.code) {
+        haveData.value = false; 
+        return
+    } 
+    try {
+        const res=await chartInfoByCode({
+            UniqueCode:params.code
+        })
+        if(res.Ret!==200) return
+        chartInfo.value=res.Data.ChartInfo
+        dataList.value=res.Data.ChartInfo.Source === 1 ? res.Data.EdbInfoList : [res.Data.EdbInfoList[0]];
+        chartBoxId.value=`hz-chart_${params.code}_${new Date().getTime()}`
+        //处理英文研报英文设置不全就展示中文
+        setLangFromEnReport();
+        haveData.value = true;
+        nextTick(()=>{
+            chartRender({
+                data:{
+                    ...res.Data,
+                    ChartInfo:{
+                        ...res.Data.ChartInfo,
+                        Calendar:'公历'
+                    },
+                },
+                renderId:chartContentEl.value,
+                lang:'zh',
+                changeLangIsCheck:false,
+                showChartTitle:false
+            })
+            res.Data.WaterMark&&(markDom.value.style.backgroundImage = `url(${res.Data.WaterMark})`)
+        })
+    }catch (e) {
+        haveData.value = false; 
+    }
+}
+
+const setLangFromEnReport=()=>{
+    //来源于英文研报
+    if(params.fromPage !== 'en') return
+    let is_name_en = chartInfo.value.ChartNameEn ? true : false;//名称是否有英文
+    let is_target_en = [2,9,10].includes(chartInfo.value.ChartType) ? true : dataList.value.every(_ => _.EdbNameEn);//指标是否有英文
+    params.language = (is_name_en && is_target_en) ? 'en' : 'ch';
+    params.language='ch'
+}
+
+getChartData()
+</script>
+
+<template>
+    <div class="hz-chart-wrap" :style="props.style+';height:'+props.height+'px'" style="margin: 0 0 10px 0;">
+        <header class="hz-chart-head">
+            <span 
+                v-if="chartInfo"
+                class="chart-title" 
+                :style="chartInfo.ChartThemeStyle?`
+                text-align:${JSON.parse(chartInfo.ChartThemeStyle).titleOptions.align};
+                font-size:${JSON.parse(chartInfo.ChartThemeStyle).titleOptions.style.fontSize}px;
+                color:${JSON.parse(chartInfo.ChartThemeStyle).titleOptions.style.color}
+                `:''"
+            >
+                {{ params.language === 'en'?chartInfo.ChartNameEn: chartInfo.ChartName}}
+            </span>
+        </header>
+        <template v-if="haveData">
+            <div :id="chartBoxId" ref="chartContentEl" class="hz-chart-content"></div>
+            <div class="mark" ref="markDom"></div>
+        </template>
+        <div class="notfound" v-else>
+            哎吆,你的图飞了,赶快去找管理员救命吧~
+        </div>
+        <div 
+        class="chart-bottom-info bootom-source"
+        v-if="chartInfo?.Instructions&&JSON.parse(chartInfo?.Instructions).isShow"
+        >
+            <div 
+                class="chart-instruction text_oneLine" 
+                v-text="JSON.parse(chartInfo.Instructions).text"
+                :style="`
+                color: ${JSON.parse(chartInfo.Instructions).color};
+                font-size: ${ JSON.parse(chartInfo.Instructions).fontSize }px
+                `"
+            ></div>
+        </div>
+        <div class="hz-chart-footer" 
+        v-if="chartInfo?.SourcesFrom&&JSON.parse(chartInfo.SourcesFrom)?.isShow"
+        :style="`
+            color: ${ JSON.parse(chartInfo.SourcesFrom).color };
+            font-size: ${ JSON.parse(chartInfo.SourcesFrom).fontSize }px;
+        `"
+        >
+            source:<em>{{ JSON.parse(chartInfo.SourcesFrom).text}}</em>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.hz-chart-wrap{
+    font:initial;
+    page-break-inside: avoid;
+    max-width: 1200PX;
+    height: 100%;
+    overflow: hidden;
+    position: relative;
+    margin: 0 auto;
+    border: 1PX solid rgba(0,0,0,.125) !important;
+    background: #fff;
+    border-radius: 5PX;
+    display: flex;
+    flex-direction: column;
+    .hz-chart-head{
+        box-sizing: border-box;
+        background-color: #F2F2F2;
+        border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+        cursor: pointer;
+        min-height: 40PX;
+        display: flex;
+        align-items: center;
+        padding: 2PX 10PX;
+        .chart-title {
+            width: 100%;
+        }
+    }
+    .hz-chart-content{
+        width: 100%;
+        flex: 1;
+    }
+    .mark{
+        position:absolute;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%,-50%);
+        background-image: none;
+        background-position:center center;
+        background-repeat:  no-repeat;
+        background-size: contain;
+        pointer-events: none;
+        width: 580PX;
+        height: 60PX;
+    }
+    .notfound {
+        flex: 1;
+        overflow: hidden;
+        padding: 10PX;
+        color: #777;
+        text-align: center;
+    }
+    .chart-bottom-info{
+        padding-right: 10PX;
+        padding-bottom: 0;
+        padding-left: 25PX;
+        display: flex;
+        .chart-instruction {
+            max-width: 80%;
+            flex: 1;
+            text-align: right;
+            margin-left: auto;
+        }
+    }
+    .hz-chart-footer{
+        padding-left: 25PX;
+        padding-right: 10PX;
+        padding-bottom: 10PX;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+    }
+    .text_oneLine{
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+    }
+}
+</style>

+ 147 - 0
src/CustomElement/EtaTable.ce.vue

@@ -0,0 +1,147 @@
+<script setup>
+import {ref} from 'vue'
+import {infoByCode} from './api/getData.js'
+import {parseQueryString} from "./utils/index"
+
+const props=defineProps({
+    src:'',
+    width:'',
+    height:'',
+    style:''
+})
+
+const params=parseQueryString(props.src)
+const info=ref({})
+const showData = ref(false);
+async function getTableData(){
+    const res = await infoByCode({  UniqueCode: params.code, FromScene: Number(params.fromScene||'') });
+    if(res.Ret !== 200) return
+
+    info.value=res.Data
+    showData.value = true;
+}
+
+getTableData()
+</script>
+
+<template>
+    <div 
+        v-if="showData"
+        class="sheet-show-wrapper"  
+    >
+        <h3 class="title">{{info.ExcelName}}</h3>
+        
+        <div class="table-wrapper">
+            <table 
+                cellpadding="0" 
+                cellspacing="0" 
+                :style="`font-size: ${info.Config?.FontSize||12}PX`"
+            >
+                <tbody>
+                <tr 
+                    v-for="(item,index) in info.TableInfo?.TableDataList"
+                    :key="index"
+                >
+                    <td 
+                    :class="['data-cell',{
+                        'one-bg':(index+1)%2&&index>0,
+                        'tow-bg': (index+1)%2!==0&&index>0,
+                        'head-column': index === 0
+                    }]"
+                    v-for="(cell,cell_index) in item"
+                    :key="cell_index"
+                    :colspan="cell.mc.cs||1"
+                    :rowspan="cell.mc.rs||1"
+                    :style="`
+                        color: ${cell.fc};
+                        font-weight: ${cell.bl ? 'bold' : 'normal'};
+                        font-style: ${cell.it ? 'italic' : 'normal'};
+                        background: ${cell.bg};
+                    `"
+                    >
+                    <div class="split-word" v-if="cell.ct.s">
+                        <span 
+                        v-for="(word,word_index) in cell.ct.s" 
+                        :key="`${index}_${cell_index}_${word_index}`"
+                        :style="`
+                            color: ${word.fc};
+                            font-weight: ${word.bl ? 'bold' : 'normal'};
+                            font-style: ${word.it ? 'italic' : 'normal'};
+                        `"
+                        >{{word.v}}</span>
+                    </div>
+                    <div v-else>{{cell.m}}</div>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.sheet-show-wrapper {
+  max-width: 1200PX;
+  overflow: hidden;
+  position: relative;
+  margin: 0 auto;
+  background: #fff;
+  .title {
+    font-size: 17PX;
+    font-weight: normal;
+    padding: 0 10PX;
+    margin: 0 0 8PX 0;
+  }
+.table-wrapper {
+    max-width: calc(100vw - 20PX);
+    margin: 0 auto;
+    overflow: auto;
+    table {
+        width: 100%;
+        font-size: 14PX;
+        color: #333;
+        table-layout: auto;
+        td,
+        th {
+            width: auto;
+            height: auto;
+            padding: 0.4em 0;
+            word-break: break-all;
+            word-wrap: break-word;
+            line-height: 1.2em;
+            border: 1PX solid #dcdfe6;
+            text-align: center;
+            background-color: #fff;
+            border-left: none;
+            border-top: none;
+            &:first-child {
+                border-left: 1PX solid #dcdfe6;
+            }
+        }
+
+        .data-cell{
+            color: #333;
+            &.one-bg {
+            background-color: #EFEEF1;
+            }
+            &.two-bg {
+            background-color: #fff;
+            }
+        }
+
+        .thead-sticky {
+            position: sticky;
+            top: 0;
+        }
+
+        .head-column {
+            background-color: #505B78;
+            color: #fff;
+        }
+        .split-word {
+            span { display: inline; }
+        }
+    }
+}
+}
+</style>

+ 20 - 0
src/CustomElement/api/getData.js

@@ -0,0 +1,20 @@
+//获取数据
+import { get,post } from "./index";
+
+
+// 接口
+/**
+ * 通过code获取图表详情
+ * @param UniqueCode 
+*/
+export const chartInfoByCode=params=>{
+  return post('/chart/detail',params)
+}
+
+/**
+ * 通过code获取表格详情
+ * @param UniqueCode 
+*/
+export const infoByCode=params=>{
+    return post('/excel/detail',params)
+}

+ 47 - 0
src/CustomElement/api/index.js

@@ -0,0 +1,47 @@
+"use strict";
+import axios from "axios";
+
+// import.meta.env.VITE_APP_BASEAPIURL
+let config = {
+  baseURL: import.meta.env.VITE_APP_BASEAPIURL,
+  timeout: 10*60 * 1000,
+};
+const _axios = axios.create(config);
+
+_axios.interceptors.request.use(
+  function (config) {
+    return config;
+  },
+  function (error) {
+    console.error(error);
+    return Promise.reject(error);
+  }
+);
+_axios.interceptors.response.use(
+  function (response) {
+    let data=response.data  
+    if(response.status!==200){
+      console.error('网络异常');
+    }
+    if(!data){
+      console.error('服务器开了个小差');
+    }
+    if(data.Ret===408){//token失效
+      console.error(data.Msg);
+    }
+    if(data.Ret===403){
+      console.error(data.Msg||'网络异常');
+    }
+    return data;
+  },
+  function (error) {
+    console.error(error);
+    return Promise.reject(error);
+  }
+);
+export const get = (url, params) => {
+  return _axios.get(url, { params });
+};
+export const post = (url, params) => {
+  return _axios.post(url, params);
+};

+ 11 - 0
src/CustomElement/index.js

@@ -0,0 +1,11 @@
+import { defineCustomElement } from 'vue';
+import EtaChartComp from './EtaChart.ce.vue';
+import EtaTableComp from './EtaTable.ce.vue'
+
+const EtaChart = defineCustomElement(EtaChartComp);
+const EtaTable = defineCustomElement(EtaTableComp);
+
+export function registerEtaComp() {
+  customElements.define('eta-chart', EtaChart)
+  customElements.define('eta-table', EtaTable)
+}

+ 20 - 0
src/CustomElement/utils/index.js

@@ -0,0 +1,20 @@
+// 获取url字符串中所有参数
+export function parseQueryString(url) {
+  // 如果链接地址不包含 "?",则没有查询参数,直接返回空对象
+  if (url.indexOf('?') === -1) {
+    return {};
+  }
+  // 获取 "?" 后面的查询参数部分
+  const queryString = url.split('?')[1];
+  // 将查询参数字符串拆分为键值对数组
+  const paramsArray = queryString.split('&');
+  // 创建一个对象来存储解析后的参数
+  const paramsObject = {};
+  // 遍历键值对数组,将每一对解析并存储到对象中
+  paramsArray.forEach(param => {
+    const [key, value] = param.split('=');
+    // 对于类似 "%20" 这样的编码,使用 decodeURIComponent 进行解码
+    paramsObject[key] = decodeURIComponent(value);
+  });
+  return paramsObject;
+}

+ 7 - 0
src/api/common.js

@@ -77,4 +77,11 @@ export function apiGetPublicSetting(){
  */
 export function apiCheckClassify(params){
     return post('/report_approve/classify/check_open',params)
+}
+
+/**
+ * 获取用户信息
+ */
+export function getSystemInfo(){
+    return get('/system/sysuser/detail')
 }

+ 9 - 0
src/api/report.js

@@ -231,4 +231,13 @@ export default {
     reportCnCancel(params){
         return post("/report/approve/cancel",params)
     },
+    /**
+     * 研报转pdf、长图
+     * @param {Object} params 
+     * @param {Number} params.ReportId
+     * @returns 
+     */
+    report2PdfImg(params){
+        return post("/smart_report/get_pdf_url",params)
+    },
 }

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

@@ -2288,7 +2288,7 @@ function setRadarChart({DataResp,EdbInfoList,ChartInfo}) {
     return {
       chart: {
         ...chartDefaultOpts.chart,
-        ...chartTheme.drawOption,
+        ...chartTheme?.drawOption,
         spacing: [2,10,2,10],
         polar:true,
       },

+ 4 - 0
src/hooks/useAuthBtn.js

@@ -10,6 +10,8 @@ export const reportManageBtn = {
     reportManage_reportDel:'reportManage:reportDel',//删除研报
     reportManage_reportEdit:'reportManage:reportEdit',//编辑研报
     reportManage_cancelPublish:'reportManage:cancelPublish',//取消发布
+    reportManage_exportPdf:'reportManage:exportPdf',//下载pdf
+    reportManage_exportImg:'reportManage:exportImg',//下载长图
     reportManage_publish:'reportManage:publish',//发布研报
     reportManage_reportList:'reportManage:reportList',//研报列表的选项
     reportManage_reportList_uv:'reportManage:reportList:uv',//研报列表-PV/UV
@@ -23,6 +25,8 @@ export const enReportManageBtn = {
     enReport_reportDel:'enReport:reportDel',//删除研报
     enReport_reportEdit:'enReport:reportEdit',//编辑研报
     enReport_cancelPublish:'enReport:cancelPublish',//取消发布
+    enReport_exportPdf:'enReport:exportPdf',//下载pdf
+    enReport_exportImg:'enReport:exportImg',//下载长图
     enReport_publish:'enReport:publish',//发布研报
     enReport_reportAdd:'enReport:reportAdd',//添加研报
 }

+ 39 - 8
src/views/report/AddReport.vue

@@ -6,6 +6,7 @@ import ReportInsertContent from './components/reportInsert/Index.vue'
 import ReportPublishTimeSet from './components/ReportPublishTimeSet.vue'
 import apiReport from '@/api/report'
 import apiChart from '@/api/chart'
+import {getSystemInfo} from '@/api/common'
 import moment from 'moment'
 import { showToast,showDialog  } from 'vant'
 import { useRouter } from 'vue-router'
@@ -13,6 +14,8 @@ import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import {useReportApprove} from '@/hooks/useReportApprove'
+import {Base64} from 'js-base64'
+
 const cachedViewsStore=useCachedViewsStore()
 const publicSettingStore = usePublicSettingStore()
 const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr} = useReportApprove()
@@ -26,10 +29,15 @@ const {lastFocusPosition,initFroalaEditor}=useInitFroalaEditor()
 let reportContentEditorIns=null//报告内容编辑器实例
 
 let reportId=0//报告id
+let reportCode=0//报告id
+
+// 水印
+const waterMarkStr=ref('')
 
 onMounted(() => {
     const el=document.getElementById('editor')
     reportContentEditorIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
+    getSystemInfoFun()
 })
 
 
@@ -211,7 +219,7 @@ async function handleReportOpt(type){
         if(hasTel==1){
             const res=await apiReport.reportAdd(params)
             if(res.Ret===200){
-                reportPublish(res.Data.ReportId)
+                reportPublish(res.Data.ReportId,res.Data.ReportCode)
             }
         }else{
             // 显示发布提示弹窗,提示推送客群
@@ -232,6 +240,7 @@ async function handleReportOpt(type){
         const res=await apiReport.reportAdd(params)
         if(res.Ret===200){
             reportId=res.Data.ReportId
+            reportCode=res.Data.ReportCode
             showDSFBTime.value=true
         }
     }
@@ -256,9 +265,9 @@ async function handleConfirmPublish(e){
     }
     const saveRes=await apiReport.reportAdd(params)
     if(e===1){//仅发布
-        reportPublish(saveRes.Data.ReportId)
+        reportPublish(saveRes.Data.ReportId,saveRes.Data.ReportCode)
     }else if(e===2){
-        const pubRes=await apiReport.reportPublish({ReportIds:saveRes.Data.ReportId.toString()})
+        const pubRes=await apiReport.reportPublish({ReportIds:saveRes.Data.ReportId.toString(),ReportUrl:generatePdfLinks(saveRes.Data.ReportCode)})
         if(pubRes.Ret!==200) return
         const msgRes=await apiReport.reportMessageSend({ReportId:saveRes.Data.ReportId})
         if(msgRes.Ret!==200) return
@@ -266,8 +275,12 @@ async function handleConfirmPublish(e){
     }
 }
 
-async function reportPublish(id){
-    const res=await apiReport.reportPublish({ReportIds:id.toString()})
+function generatePdfLinks(Code){
+    return `${publicSettingStore.publicSetting.ReportViewUrl}/reportshare_pdf?code=${Code}&flag=${waterMarkStr.value}`
+}
+
+async function reportPublish(id,code){
+    const res=await apiReport.reportPublish({ReportIds:id.toString(),ReportUrl:generatePdfLinks(code)})
     if(res.Ret===200){
         console.log('back');
         router.back()
@@ -291,7 +304,8 @@ function onConfirmDSFBTime(time){
             apiReport.reportPublishTimeSet({
                 ReportId:reportId,
                 PrePublishTime:time,
-                PreMsgSend:0
+                PreMsgSend:0,
+                ReportUrl:generatePdfLinks(reportCode)
             }).then(res=>{
                 if(res.Ret===200){
                     showToast('定时发布成功!')
@@ -306,7 +320,8 @@ function onConfirmDSFBTime(time){
         apiReport.reportPublishTimeSet({
             ReportId:reportId,
             PrePublishTime:time,
-            PreMsgSend:1
+            PreMsgSend:1,
+            ReportUrl:generatePdfLinks(reportCode)
         }).then(res=>{
             if(res.Ret===200){
                 showToast('定时发布成功!')
@@ -321,7 +336,8 @@ function onConfirmDSFBTime(time){
         apiReport.reportPublishTimeSet({
             ReportId:reportId,
             PrePublishTime:time,
-            PreMsgSend:0
+            PreMsgSend:0,
+            ReportUrl:generatePdfLinks(reportCode)
         }).then(res=>{
             if(res.Ret===200){
                 showToast('定时发布成功!')
@@ -358,6 +374,21 @@ async function handleReportSubmit(params){
     })
 }
 
+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>

+ 36 - 11
src/views/report/EditReport.vue

@@ -6,6 +6,7 @@ import ReportInsertContent from './components/reportInsert/Index.vue'
 import ReportPublishTimeSet from './components/ReportPublishTimeSet.vue'
 import apiReport from '@/api/report'
 import apiChart from '@/api/chart'
+import {getSystemInfo} from '@/api/common'
 import moment from 'moment'
 import { showToast,showDialog } from 'vant'
 import { useRoute, useRouter } from 'vue-router'
@@ -13,6 +14,8 @@ import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {useReportApprove} from '@/hooks/useReportApprove'
+import {Base64} from 'js-base64'
+
 const cachedViewsStore=useCachedViewsStore()
 const publicSettingStore = usePublicSettingStore()
 const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr} = useReportApprove()
@@ -20,12 +23,12 @@ const router=useRouter()
 const route=useRoute()
 const {checkAuthBtn} = useAuthBtn()
 
-
-
 const {lastFocusPosition,initFroalaEditor,imgUploadFlag,frolaEditorContentChange}=useInitFroalaEditor()
 let reportContentEditorIns=null//报告内容编辑器实例
 
 let autoSaveTimer=null
+// 水印
+const waterMarkStr=ref('')
 
 onMounted(() => {
     const el=document.getElementById('editor')
@@ -35,6 +38,7 @@ onMounted(() => {
     autoSaveTimer=setInterval(() => {
         autoSaveReportContent()
     }, 6000);
+    getSystemInfoFun()
 })
 onUnmounted(()=>{
     clearInterval(autoSaveTimer)
@@ -334,10 +338,10 @@ async function handleConfirmPublish(e){
     }
     const saveRes=await apiReport.reportEdit(params)
     if(e===1){//仅发布
-        reportPublish(saveRes.Data.ReportId)
+        reportPublish(saveRes.Data.ReportId,saveRes.Data.ReportCode)
     }else if(e===2){
         if(reportData.value.MsgIsSend===1) return
-        const pubRes=await apiReport.reportPublish({ReportIds:saveRes.Data.ReportId.toString()})
+        const pubRes=await apiReport.reportPublish({ReportIds:saveRes.Data.ReportId.toString(),ReportUrl:generatePdfLinks(saveRes.Data.ReportCode)})
         if(pubRes.Ret!==200) return
         const msgRes=await apiReport.reportMessageSend({ReportId:saveRes.Data.ReportId})
         if(msgRes.Ret!==200) return
@@ -345,8 +349,12 @@ async function handleConfirmPublish(e){
     }
 }
 
-async function reportPublish(id){
-    const res=await apiReport.reportPublish({ReportIds:id.toString()})
+function generatePdfLinks(Code){
+    return `${publicSettingStore.publicSetting.ReportViewUrl}/reportshare_pdf?code=${Code}&flag=${waterMarkStr.value}`
+}
+
+async function reportPublish(id,code){
+    const res=await apiReport.reportPublish({ReportIds:id.toString(),ReportUrl:generatePdfLinks(code)})
     if(res.Ret===200){
         console.log('back');
         router.back()
@@ -361,7 +369,8 @@ function onConfirmDSFBTime(time){
         apiReport.reportPublishTimeSet({
             ReportId:reportData.value.Id,
             PrePublishTime:time,
-            PreMsgSend:0
+            PreMsgSend:0,
+            ReportUrl:generatePdfLinks(reportData.value.ReportCode)
         }).then(res=>{
             if(res.Ret===200){
                 showToast('定时发布成功!')
@@ -384,7 +393,8 @@ function onConfirmDSFBTime(time){
             apiReport.reportPublishTimeSet({
                 ReportId:reportData.value.Id,
                 PrePublishTime:time,
-                PreMsgSend:0
+                PreMsgSend:0,
+                ReportUrl:generatePdfLinks(reportData.value.ReportCode)
             }).then(res=>{
                 if(res.Ret===200){
                     showToast('定时发布成功!')
@@ -399,7 +409,8 @@ function onConfirmDSFBTime(time){
         apiReport.reportPublishTimeSet({
             ReportId:reportData.value.Id,
             PrePublishTime:time,
-            PreMsgSend:1
+            PreMsgSend:1,
+            ReportUrl:generatePdfLinks(reportData.value.ReportCode)
         }).then(res=>{
             if(res.Ret===200){
                 showToast('定时发布成功!')
@@ -414,7 +425,8 @@ function onConfirmDSFBTime(time){
         apiReport.reportPublishTimeSet({
             ReportId:reportData.value.Id,
             PrePublishTime:time,
-            PreMsgSend:0
+            PreMsgSend:0,
+            ReportUrl:generatePdfLinks(reportData.value.ReportCode)
         }).then(res=>{
             if(res.Ret===200){
                 showToast('定时发布成功!')
@@ -445,7 +457,20 @@ async function handleReportSubmit(params){
     }).catch(()=>{})
 }
 
-
+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>

+ 45 - 3
src/views/report/List.vue

@@ -1,6 +1,7 @@
 <script setup name="ReportList">
 import {computed, nextTick, onMounted, reactive,ref} from 'vue'
 import apiReport from '@/api/report'
+import {getSystemInfo} from '@/api/common'
 import moment from 'moment'
 import ListClassify from './components/ListClassify.vue'
 import ReportPublishPop from './components/ReportPublishPop.vue'
@@ -9,9 +10,16 @@ import { useRouter } from 'vue-router';
 import { useWindowSize } from '@vueuse/core'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import {reportFrequencyOpts} from './utils/config'
+import {useDownLoadFile} from '@/hooks/useDownLoadFile'
 import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {useReportApprove} from '@/hooks/useReportApprove'
+import {usePublicSettingStore} from '@/store/modules/publicSetting'
+import {Base64} from 'js-base64'
+
 const cachedViewsStore=useCachedViewsStore()
+const publicSettingStore = usePublicSettingStore()
+
+const {startDownload}=useDownLoadFile()
 const {isApprove,isOtherApprove,getEtaConfig} = useReportApprove()
 
 const {checkAuthBtn} = useAuthBtn()
@@ -62,6 +70,8 @@ function handleCleanFilter(){
 // 分类弹窗
 const showClassify=ref(false)
 
+// 水印
+const waterMarkStr=ref('')
 
 const listState = reactive({
     publishStatus:'',
@@ -139,7 +149,6 @@ let activeReportData=ref('')
 let showPublishPop=ref(false)
 async function handleReportPublish(item){
     activeReportData.value=item
-
     // 周报校验是否上传了音频
     if(item.ClassifyNameFirst=='周报'){
         const validRes=await apiReport.weekReportValidAudio({ReportId:Number(item.Id)})
@@ -176,7 +185,8 @@ async function handleReportPublish(item){
 }
 function publishReportApprove(){
     apiReport.reportPublish({
-        ReportIds:activeReportData.value.Id.toString()
+        ReportIds:activeReportData.value.Id.toString(),
+        ReportUrl:generatePdfLinks(activeReportData.value.ReportCode)
     }).then(res=>{
         if(res.Ret!==200) return 
         showToast('发布成功')
@@ -184,6 +194,10 @@ function publishReportApprove(){
     })
 }
 
+function generatePdfLinks(Code){
+    return `${publicSettingStore.publicSetting.ReportViewUrl}/reportshare_pdf?code=${Code}&flag=${waterMarkStr.value}`
+}
+
 //提交报告
 function handleReportSubmit(item){
     showDialog({
@@ -216,7 +230,15 @@ function handleReportCancel(item){
         })
     }).catch(()=>{})
 }
-
+// type 1-pdf 2-长图
+function downloadPdfImg(item,type){
+    showReportItemOpt.value=false
+    if(type == 1){
+        window.open(item.DetailPdfUrl,"_blank")
+    }else{
+        startDownload(item.DetailImgUrl,`${item.Title}.jpeg`)
+    }
+}
 
 // 发布弹窗关闭
 function handlePublishPopClose(refresh){
@@ -494,8 +516,24 @@ async function handleReportEdit(e){
     })
 }
 
+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)
+          }
+        }
+    })
+}
+
 onMounted(async ()=>{
     getEtaConfig()
+    getSystemInfoFun()
 })
 
 </script>
@@ -697,6 +735,10 @@ onMounted(async ()=>{
                     @click="handleReportPublishCancle(activeItem)">撤销</div> <!-- 实际上是取消发布 -->
                 <div class="item" v-if="checkAuthBtn(reportManageBtn.reportManage_cancelPublish)&&activeItem.State===6"
                     @click="handleReportCancel(activeItem)">撤销</div>
+                <div class="item" v-if="checkAuthBtn(reportManageBtn.reportManage_exportPdf) && activeItem.DetailPdfUrl"
+                    @click="downloadPdfImg(activeItem,1)">下载pdf</div>
+                <div class="item" v-if="checkAuthBtn(reportManageBtn.reportManage_exportImg) && activeItem.DetailImgUrl"
+                    @click="downloadPdfImg(activeItem,2)">下载长图</div>   
                 <div class="item" @click="handldReportMsgSend(activeItem)" v-if="activeItem.MsgIsSend==0&&checkAuthBtn(reportManageBtn.reportManage_sendMsg)">推送消息</div>
             </template>
             <!-- 待审批,已驳回 -->

+ 7 - 1
src/views/report/components/ReportPublishPop.vue

@@ -9,6 +9,7 @@ const props=defineProps({
         default:''
     }
 })
+
 const emits=defineEmits(['close'])
 
 // 确认发布报告
@@ -16,7 +17,8 @@ const emits=defineEmits(['close'])
 function handleConfirmPublish(type){
     if(type==2&&props.reportData.MsgIsSend==1) return
     apiReport.reportPublish({
-        ReportIds:props.reportData.Id.toString()
+        ReportIds:props.reportData.Id.toString(),
+        ReportUrl:generatePdfLinks(props.reportData.ReportCode)
     }).then(res=>{
         if(res.Ret==200){
             cachedViewsStore.removeCaches('ReportList')
@@ -39,6 +41,10 @@ function handleConfirmPublish(type){
     })
 }
 
+function generatePdfLinks(Code){
+    return `${publicSettingStore.publicSetting.ReportViewUrl}/reportshare_pdf?code=${Code}&flag=${waterMarkStr.value}`
+}
+
 // 推送消息
 function handleReportMessageSend(publish){
     apiReport.reportMessageSend({

+ 32 - 6
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 {getSystemInfo} from '@/api/common'
 import moment from 'moment'
 import { showToast,showDialog } from 'vant'
 import { useRoute, useRouter } from 'vue-router'
@@ -13,6 +14,7 @@ import {useCachedViewsStore} from '@/store/modules/cachedViews'
 import {enReportManageBtn} from '@/hooks/useAuthBtn'
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import {useReportApprove} from '@/hooks/useReportApprove'
+import {Base64} from 'js-base64'
 
 const cachedViewsStore=useCachedViewsStore()
 const router=useRouter()
@@ -28,6 +30,10 @@ let overviewContentIns=null//overview内容编辑器实例
 let autoSaveTimer=null//自动保存定时器
 
 let reportId=route.query.id||0//报告id
+let reportCode=''
+
+// 水印
+const waterMarkStr=ref('')
 
 onMounted(async () => {
     const el=document.getElementById('editor')
@@ -39,6 +45,7 @@ onMounted(async () => {
             autoSaveReportContent()
         }, 6000);
     }
+    getSystemInfoFun()
 })
 onUnmounted(()=>{
     clearInterval(autoSaveTimer)
@@ -63,6 +70,7 @@ async function getReportDetail(){
     const res=await apiReportEn.getReportDetail({ReportId:Number(route.query.id)})
     if(res.Ret===200){
         reportData.value=res.Data
+        return console.log(reportData.value);
         reportBaseInfoData.addType=res.Data.AddType
         reportBaseInfoData.classifyName=[
             {
@@ -300,6 +308,7 @@ async function handleReportOpt(e){
     :await apiReportEn.reportAdd(params)
     if(res.Ret!==200) return
     reportId=res.Data.ReportId
+    reportCode=res.Data.ReportCode
     cachedViewsStore.removeCaches('ReportEnList')
     if(e==='cg'){
         showToast('保存成功')
@@ -318,7 +327,7 @@ async function handleReportOpt(e){
             message: reportData.value?.PrePublishTime?'该报告已设置定时发布,是否修改为立即发布?':`是否确定立即发布报告?`,
             showCancelButton:true
         }).then(()=>{
-            reportPublish(res.Data.ReportId)
+            reportPublish(res.Data.ReportId,res.Data.ReportCode)
         })
         
     }
@@ -331,8 +340,8 @@ async function handleReportOpt(e){
 }
 
 // 发布报告
-function reportPublish(id){
-    apiReportEn.reportPublish({ReportIds:id.toString()}).then(res=>{
+function reportPublish(id,code){
+    apiReportEn.reportPublish({ReportIds:id.toString(),ReportUrl:generatePdfLinks(code)}).then(res=>{
         if(res.Ret===200){
             showToast('发布成功')
             setTimeout(() => {
@@ -342,14 +351,16 @@ function reportPublish(id){
     })
 }
 
-
+function generatePdfLinks(Code){
+    return `${publicSettingStore.publicSetting.ReportViewUrl}/reportshare_pdf_en?code=${Code}&flag=${waterMarkStr.value}`
+}
 // 定时发布报告选择时间
 const showDSFBTime=ref(false)
 function onConfirmDSFBTime(time){
-    console.log(time);
     apiReportEn.reportPublishTimeSet({
         ReportId:reportId,
-        PrePublishTime:time
+        PrePublishTime:time,
+        ReportUrl:generatePdfLinks(reportCode)
     }).then(res=>{
         if(res.Ret===200){
             showToast('定时发布成功')
@@ -376,6 +387,21 @@ function handleReportSubmit(reportId){
         })
     }).catch(()=>{})
 }
+
+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>

+ 47 - 3
src/views/reportEn/List.vue

@@ -1,6 +1,8 @@
 <script setup name="ReportEnList">
 import {nextTick, reactive,ref,computed,onMounted} from 'vue'
 import apiReportEn from '@/api/reportEn'
+import apiReport from '@/api/report'
+import {getSystemInfo} from '@/api/common'
 import moment from 'moment'
 import ListClassify from './components/ListClassify.vue'
 import SendEmail from './components/SendEmail.vue';
@@ -9,16 +11,24 @@ import { showToast,showDialog,Dialog } from 'vant';
 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 {enReportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
 import {useReportApprove} from '@/hooks/useReportApprove'
+import {Base64} from 'js-base64'
+
 const cachedViewsStore=useCachedViewsStore()
+const publicSettingStore = usePublicSettingStore()
+
+const {startDownload}=useDownLoadFile()
 const {checkAuthBtn} = useAuthBtn()
 const {isApprove,isOtherApprove,getEtaConfig} = useReportApprove()
 
 const { width, height } = useWindowSize()
 
 const router=useRouter()
-
+// 水印
+const waterMarkStr=ref('')
 //是否显示一键清空选项
 const showCleanFilterBox=computed(()=>{
     if(isClickClose.value) return false
@@ -127,14 +137,14 @@ function handleReportDel(item){
 
 // 发布报告
 async function handleReportPublish(item){
-    console.log(item);
     showDialog({
         title: '提示',
         message: item.PrePublishTime?'该报告已设置定时发布,是否修改为立即发布?':`是否确定立即发布报告?`,
         showCancelButton:true
     }).then(()=>{
         apiReportEn.reportPublish({
-            ReportIds:item.Id.toString()
+            ReportIds:item.Id.toString(),
+            ReportUrl:generatePdfLinks(item.ReportCode)
         }).then(res=>{
             if(res.Ret===200){
                 showToast('发布成功')
@@ -146,6 +156,10 @@ async function handleReportPublish(item){
     
 }
 
+function generatePdfLinks(Code){
+    return `${publicSettingStore.publicSetting.ReportViewUrl}/reportshare_pdf_en?code=${Code}&flag=${waterMarkStr.value}`
+}
+
 // 取消发布
 function handleReportPublishCancle(item){
     showDialog({
@@ -162,6 +176,16 @@ function handleReportPublishCancle(item){
     })
 }
 
+// type 1-pdf 2-长图
+function downloadPdfImg(item,type){
+    showReportItemOpt.value=false
+    if(type == 1){
+        window.open(item.DetailPdfUrl,"_blank")
+    }else{
+        startDownload(item.DetailImgUrl,`${item.Title}.jpeg`)
+    }
+}
+
 //提交报告
 function handleReportSubmit(item){
     showDialog({
@@ -470,8 +494,24 @@ function handleGoEmailLog(e){
         }
     })
 }
+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)
+          }
+        }
+    })
+}
+
 onMounted(()=>{
     getEtaConfig()
+    getSystemInfoFun()
 })
 </script>
 
@@ -660,6 +700,10 @@ onMounted(()=>{
                     @click="handleReportPublishCancle(activeItem)">撤销</div> <!-- 实际上是取消发布 -->
                 <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>
+                <div class="item" v-if="checkAuthBtn(enReportManageBtn.enReport_exportImg) && activeItem.DetailImgUrl"
+                    @click="downloadPdfImg(activeItem,2)">下载长图</div>   
             </template>
             <!-- 待审批,已驳回 -->
             <template v-if="[4,5].includes(activeItem.State)">

+ 33 - 4
vite.config.js

@@ -10,11 +10,39 @@ import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfil
 import rollupNodePolyFill from 'rollup-plugin-node-polyfills'
 
 // https://vitejs.dev/config/
-export default ({ mode }) =>
-  defineConfig({
+export default ({ mode }) =>{
+  // eta webComponent组件打包配置
+  const etaWebCompBuildConfig={
+    outDir:'lib_dist',
+    copyPublicDir:false,
+    // minify:false,
+    lib: {
+      entry: path.resolve(__dirname,'./src/CustomElement/index.js'),
+      formats: ["es"],
+      name: "eta",
+      fileName:'eta_comp'
+    },
+    rollupOptions:{
+      external: ['vue'],
+      output: {
+        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
+        globals: {
+          vue: 'Vue',
+        },
+      },
+    }
+  }
+
+  return defineConfig({
     base: loadEnv(mode, process.cwd()).VITE_APP_BASE_URL, // 若服务器不是将该项目放在根目录的则 需要此设置 和服务器上同名
     plugins: [
-      vue(),
+      vue({
+        template:{
+          compilerOptions:{
+            isCustomElement:tag=>tag.startsWith('eta-')
+          }
+        }
+      }),
       Components({
         resolvers: [VantResolver()],
       }),
@@ -64,7 +92,7 @@ export default ({ mode }) =>
           ]
         }
       },
-    build: {
+    build: mode=='lib'?etaWebCompBuildConfig:{
       outDir: loadEnv(mode, process.cwd()).VITE_APP_OUTDIR,
       rollupOptions: {
         plugins: [
@@ -77,3 +105,4 @@ export default ({ mode }) =>
       host:true
     }
   });
+}