123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- <script setup name="ReportEdit">
- import {ref,onMounted,onUnmounted} from 'vue'
- import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
- // import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
- import ReportInsertContent from './components/reportInsert/Index.vue'
- import ReportPublishTimeSet from './components/ReportPublishTimeSet.vue'
- import apiReport from '@/api/report'
- // import {getSystemInfo} from '@/api/common'
- import moment from 'moment'
- import { showToast,showDialog } from 'vant'
- import { useRoute, useRouter } from 'vue-router'
- import {useCachedViewsStore} from '@/store/modules/cachedViews'
- import {usePublicSettingStore} from '@/store/modules/publicSetting'
- import {reportManageBtn,useAuthBtn} from '@/hooks/useAuthBtn'
- import {useReportApprove} from '@/hooks/useReportApprove'
- import AddReportBaseInfoV2 from './components/AddReportBaseInfoV2.vue'
- import { useReportHandles,useChapterRepoprtHandles } from './hooks/useReport'
- const cachedViewsStore=useCachedViewsStore()
- const publicSettingStore = usePublicSettingStore()
- const {isApprove,hasApproveFlow,getEtaConfig,checkClassifyNameArr} = useReportApprove()
- const router=useRouter()
- const route=useRoute()
- const {checkAuthBtn} = useAuthBtn()
- const {lastFocusPosition,initFroalaEditor,imgUploadFlag,frolaEditorContentChange}=useInitFroalaEditor()
- let reportContentEditorIns=null//报告内容编辑器实例
- const { handleRefresh,handlePublishReportHook,handleDSPublish,showDSFBTime } = useReportHandles()
- const reportCoopType = ref(Number(route.query.coopType))
- let autoSaveTimer=null
- onMounted(() => {
- const el=document.getElementById('editor')
- reportContentEditorIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
- if(reportCoopType.value===1) {//单人报告
- getEtaConfig()
- getReportDetail()
- autoSaveTimer=setInterval(() => {
- autoSaveReportContent()
- }, 6000);
- }else if(reportCoopType.value===2) { //章节报告
- getChapterDetail()
- autoSaveTimer=setInterval(() => {
- autoSaveReportChapter()
- }, 6000);
- }
- })
- onUnmounted(()=>{
- clearInterval(autoSaveTimer)
- })
- // 自动保存报告
- async function autoSaveReportContent(type="auto"){
- if(!imgUploadFlag.value)return
- //如果富文本中有未上传完成的图片,去除这个dom
- $('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
- return new Promise(async (resolve,reject)=>{
- const res=await apiReport.reportContentSave({
- ReportId:Number(route.query.id),
- Content:$('.fr-element').html(),
- NoChange:frolaEditorContentChange.value?0:1
- })
- if(res.Ret === 200) {
- resolve(true)
- type==='save' && showToast("保存成功");
- frolaEditorContentChange.value=false
- }
- })
-
- }
- // 获取报告详情
- const reportData=ref(null)
- async function getReportDetail(){
- const res=await apiReport.getReportDetail({
- ReportId:Number(route.query.id)
- })
- if(res.Ret===200){
- reportData.value=res.Data
- reportBaseInfoData.addType=res.Data.AddType
- reportBaseInfoData.classifyName=[
- {
- id:res.Data.ClassifyIdFirst,
- text:res.Data.ClassifyNameFirst,
- },
- {
- id:res.Data.ClassifyIdSecond,
- text:res.Data.ClassifyNameSecond,
- },
- {
- id:res.Data.ClassifyIdThird,
- text:res.Data.ClassifyNameThird,
- }
- ]
- reportBaseInfoData.author=res.Data.Author ? res.Data.Author.split(',') : ['FICC团队']
- reportBaseInfoData.frequency=[res.Data.Frequency]
- reportBaseInfoData.createtime=moment(res.Data.CreateTime).format('YYYY-MM-DD')
- reportBaseInfoData.title=res.Data.Title
- reportBaseInfoData.abstract=res.Data.Abstract
- reportBaseInfoData.cooperationType=res.Data.CollaborateType
- reportBaseInfoData.cooperationUsers=res.Data.GrandAdminList
- ? res.Data.GrandAdminList.map(_ => ({
- NodeId: _.AdminId,
- NodeName: _.AdminName
- }))
- : []
- reportBaseInfoData.reportLayout=res.Data.ReportLayout
- reportBaseInfoData.isPublcPublish=res.Data.IsPublicPublish
- reportContentEditorIns.html.set(res.Data.Content);
-
- const classify = reportBaseInfoData.classifyName.map(i=>i.id)
- checkClassifyNameArr(1,classify)
- }
- }
- //获取章节详情
- async function getChapterDetail() {
- const res=await apiReport.getChapterDetail({
- ReportChapterId:Number(route.query.chapterId)
- })
- reportData.value=res.Data;
- reportContentEditorIns.html.set(res.Data.Content);
- }
- // 报告基本内容
- const showReportBaseInfo=ref(false)
- let reportBaseInfoData={
- addType:1,
- classifyName:[],
- author:['FICC团队'],
- frequency: ['日度'],
- createtime:moment().format('YYYY-MM-DD'),
- title:'',
- abstract:'',
- cooperationType:1,
- cooperationUsers:[],
- reportLayout: 1,
- isPublcPublish: 1
- }
- async function handleReportBaseInfoChange(e){
- reportBaseInfoData={
- ...e,
- classifyName:e.classifys,
- createtime:e.time,
- }
- const classify = e.classifys.map(i=>i.id)
- checkClassifyNameArr(1,classify)
- showReportBaseInfo.value=false
- }
- // 报告插入数据弹窗
- const showReportInsertPop=ref(false)
- /**
- * list:[UniqueCode] 图表code
- * type:iframe/img 插入的为iframe或者图片
- * chartType: chart-图表,sheet-表格
- */
- function handleInsert({list,type,chartType}){
- reportContentEditorIns.events.focus()
- if(lastFocusPosition.value){
- reportContentEditorIns.selection.get().removeAllRanges()
- reportContentEditorIns.selection.get().addRange(lastFocusPosition.value)
- }
- if(type==='iframe'){
- let link=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 => {
- reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
- <iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
- </p>`,false)
- });
- }else if(chartType==='sheet'){
- // link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/sheetshow':'https://charttest.hzinsights.com/sheetshow'
- link=link+'/sheetshow'
- list.forEach(item => {
- reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
- <iframe src='${link}?code=${item}' class='iframe${item}' width='100%' style='border-width:0px;'></iframe>
- </p>`,false)
- });
- }
- }else if(type==='img'){
- list.forEach(item=>{
- reportContentEditorIns.html.insert(`<img style='width:100%' src='${item}' />`,false)
- })
- }
- showReportInsertPop.value=false
- }
- // 更新sheet表格高度
- function reInitSheetIframe(e){
- const { height,code } = e.data;
- let iframeDom = document.getElementsByClassName(`iframe${code}`)
- Array.prototype.forEach.call(iframeDom, function (ele) {
- ele.height = `${height+45}px`;
- });
- }
- onMounted(()=>{
- window.addEventListener('message',reInitSheetIframe)
- })
- onUnmounted(()=>{
- window.removeEventListener('message',reInitSheetIframe)
- })
- // 刷新所有图表
- async function handleRefreshAllChart(){
- handleRefresh({ id: Number(route.query.id),chapterId:Number(route.query.chapterId) })
- }
- /* 预览 */
- function handlePreviewReport() {
- const params={
- ReportId:Number(route.query.id),
- AddType: reportBaseInfoData.addType,
- ClassifyIdFirst: reportBaseInfoData.classifyName[0].id,
- ClassifyNameFirst: reportBaseInfoData.classifyName[0].text,
- ClassifyIdSecond: reportBaseInfoData.classifyName[1].id,
- ClassifyNameSecond: reportBaseInfoData.classifyName[1].text,
- Title: reportBaseInfoData.title,
- Abstract: reportBaseInfoData.abstract,
- Author:reportBaseInfoData.author.join(','),
- Frequency: reportBaseInfoData.frequency[0],
- Content: $('.fr-element').html(),
- CreateTime: reportBaseInfoData.createtime,
- ReportVersion: 2,
- State:1
- }
- sessionStorage.setItem('reportPreData',JSON.stringify(params))
- router.push({
- path:'/report/preview',
- query:{
- id:-1
- }
- })
- return
- }
- //发布 定时 提交
- async function handlePublishReport(tp) {
- cachedViewsStore.removeCaches('ReportList')
- const saveRes = await autoSaveReportContent('auto');
- if(!saveRes) return
- handlePublishReportHook(tp,reportData.value)
- }
- // 定时发布报告选择时间
- function onConfirmDSFBTime(time){
- // console.log(time);
- handleDSPublish(time,reportData.value)
- }
- const { handlePublishChapterApi } = useChapterRepoprtHandles()
- //预览章节
- async function handlePreviewChapter() {
- const saveRes = await autoSaveReportChapter('auto');
- if(!saveRes) return
-
- router.push({
- path:'/report/chapter/preview',
- query:{
- id:reportData.value.ReportChapterId
- }
- })
- }
- // 自动保存章节报告
- async function autoSaveReportChapter(type="auto"){
- if(!imgUploadFlag.value)return
- if(!route.query.chapterId) return
- //如果富文本中有未上传完成的图片,去除这个dom
- $('.fr-element').find('img.fr-uploading').length&&$('.fr-element').find('img.fr-uploading').remove()
- return new Promise(async (resolve,reject)=>{
- const res=await apiReport.chapterDetailSave({
- ReportChapterId:Number(route.query.chapterId),
- Content:$('.fr-element').html(),
- })
- if(res.Ret === 200) {
- resolve(true)
- type==='save' && showToast("保存成功");
- }
- })
-
- }
- /* 提交章节 */
- async function handlePublishChapter() {
- showDialog({
- title: '提示',
- showCancelButton: true,
- message: '章节提交后不可编辑,是否确认提交?',
- confirmButtonText: '确认',
- cancelButtonText: '取消',
- }).then( async() => {
- const saveRes = await autoSaveReportChapter('auto');
- if(!saveRes) return
- handlePublishChapterApi(reportData.value.ReportChapterId)
- }).catch(() =>{})
- }
- </script>
- <template>
- <div class="add-report-page">
- <div class="main-wrap">
- <div class="editor-box" id="editor"></div>
- </div>
- <!-- 底部操作 -->
- <div class="bot-action-box">
- <div class="left-box" v-if="reportData">
- <div class="item" @click="showReportBaseInfo=true" v-if="!reportData.ReportChapterId">
- <img src="@/assets/imgs/report/icon_info.png" alt="">
- <span>基础信息</span>
- </div>
- <div class="item" @click="handleRefreshAllChart">
- <img src="@/assets/imgs/report/icon_refresh.png" alt="">
- <span>刷新</span>
- </div>
- <div class="item" @click="reportData.ReportChapterId?handlePreviewChapter():handlePreviewReport()" v-permission="reportManageBtn.reportManage_reportView">
- <img src="@/assets/imgs/report/icon_preview.png" alt="">
- <span>预览</span>
- </div>
- <div class="item" @click="reportData.ReportChapterId?autoSaveReportChapter('save'):autoSaveReportContent('save')">
- <img src="@/assets/imgs/report/icon_save2.png" alt="">
- <span>保存</span>
- </div>
- <!-- 章节报告提交章节 -->
- <div
- class="item"
- @click="handlePublishChapter"
- v-permission="reportManageBtn.reportManage_publish"
- v-if="reportData.ReportChapterId"
- >
- <img src="@/assets/imgs/report/icon_publish3.png" alt="">
- <span>提交</span>
- </div>
- <!-- 单人报告提交发布 -->
- <template v-else>
- <template v-if="!isApprove||!hasApproveFlow">
- <div class="item" @click="handlePublishReport('dsfb')" v-permission="reportManageBtn.reportManage_publish">
- <img src="@/assets/imgs/report/icon_time.png" alt="">
- <span>定时发布</span>
- </div>
- <div class="item" @click="handlePublishReport('fb')" v-permission="reportManageBtn.reportManage_publish">
- <img src="@/assets/imgs/report/icon_publish3.png" alt="">
- <span>发布</span>
- </div>
- </template>
- <template v-if="isApprove&&hasApproveFlow">
- <div class="item" @click="handlePublishReport('submit')" v-permission="reportManageBtn.reportManage_publish">
- <img src="@/assets/imgs/report/icon_publish3.png" alt="">
- <span>提交</span>
- </div>
- </template>
- </template>
- </div>
- <div class="right-btn" @click="showReportInsertPop=true">
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M12.0499 15.9499V27.5H15.9499V15.9499H27.5V12.0499H15.9499V0.5H12.0499V12.0499H0.5V15.9499H12.0499Z" fill="white"/>
- </svg>
- </div>
- </div>
- </div>
- <!-- 报告基础信息 -->
- <van-popup
- v-model:show="showReportBaseInfo"
- position="bottom"
- :style="{ height: '100%' }"
- >
- <AddReportBaseInfoV2
- @close="showReportBaseInfo=false"
- :id="Number(route.query.id)"
- :defaultData="reportBaseInfoData"
- @confirm="handleReportBaseInfoChange"
- />
- </van-popup>
- <!-- 报告插入数据模块 -->
- <van-popup
- v-model:show="showReportInsertPop"
- position="bottom"
- round
- >
- <report-insert-content v-if="showReportInsertPop" @insert="handleInsert"/>
- </van-popup>
- <!-- 定时发布选择时间 -->
- <ReportPublishTimeSet v-model="showDSFBTime" :prePublishTime="reportData?.PrePublishTime" @confirm="onConfirmDSFBTime" />
- </template>
- <style lang="scss" scoped>
- .publish-report-pop-box{
- padding: 48px;
- .title{
- font-size: 36px;
- text-align: center;
- margin-bottom: 32px;
- }
- .tips{
- color: $font-grey;
- margin-bottom: 48px;
- }
- .btns{
- .btn{
- line-height: 96px;
- border-radius: 12px;
- text-align: center;
- font-size: 32px;
- font-weight: 600;
- margin-bottom: 24px;
- background-color: #F2F3FF;
- color: $theme-color;
- }
- .blue{
- background-color: $theme-color;
- color: #fff;
- }
- .disabled{
- background-color: #a8b0fc;
- }
- }
- }
- @media screen and (min-width:$media-width){
- .publish-report-pop-box{
- padding: 24px;
- .title{
- font-size: 18px;
- margin-bottom: 16px;
- }
- .tips{
- margin-bottom: 24px;
- }
- .btns{
- .btn{
- line-height: 48px;
- border-radius: 12px;
- font-size: 16px;
- margin-bottom: 12px;
- }
- }
- }
- }
- .add-report-page{
- height: 100dvh;
- min-height: 95vh;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- @media screen and (min-width:$media-width){
- .add-report-page{
- height: calc(100dvh - 60px);
- min-height: calc(95vh - 60px);
- }
- }
- .van-cell{
- flex-shrink: 0;
- }
- .main-wrap{
- flex: 1;
- width: calc(100% - 32PX);
- margin: 0 auto;
- margin-top: 30px;
- .editor-box{
- width: 100%;
- height: 100%;
- }
- }
- .bot-action-box{
- padding: 20px 16PX;
- display: flex;
- align-items: center;
- .left-box{
- flex: 1;
- background: #FFFFFF;
- box-shadow: 0px 12px 60px 10px rgba(0, 0, 0, 0.05), 0px 32px 48px 4px rgba(0, 0, 0, 0.04), 0px 16px 20px -10px rgba(0, 0, 0, 0.08);
- border-radius: 100px;
- height: 112px;
- display: flex;
- align-items: center;
- margin-right: 20px;
- padding: 0 20px;
- .item{
- flex: 1;
- text-align: center;
- font-size: 20px;
- img{
- width: 40px;
- height: 40px;
- display: block;
- margin: 5px auto;
- }
- }
- }
- .right-btn{
- flex-shrink: 0;
- position: relative;
- width: 96px;
- height: 96px;
- background-color: $theme-color;
- border-radius: 50%;
- box-shadow: 0px 6px 28px 4px rgba(0, 0, 0, 0.05), 0px 16px 20px 2px rgba(0, 0, 0, 0.06), 0px 10px 10px -6px rgba(0, 0, 0, 0.1);
- svg{
- width: 27px;
- height: 27px;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%,-50%);
- }
- }
- }
- @media screen and (min-width:$media-width){
- .bot-action-box{
- margin: 0 auto;
- width: 600px;
- padding: 10px 16px;
- .left-box{
- border-radius: 50px;
- height: 56px;
- margin-right: 10px;
- padding: 0 10px;
- .item{
- font-size: 12px;
- img{
- width: 20px;
- height: 20px;
- margin: 3px auto;
- }
- }
- }
- .right-btn{
- width: 48px;
- height: 48px;
- svg{
- width: 14px;
- height: 14px;
- }
- }
- }
- }
- </style>
|