浏览代码

Merge branch 'ETA1.1.4_report'

jwyu 1 年之前
父节点
当前提交
c5820d9e7c
共有 38 个文件被更改,包括 4397 次插入0 次删除
  1. 85 0
      src/api/modules/smartReport.js
  2. 二进制
      src/assets/img/smartReport/bg01.png
  3. 二进制
      src/assets/img/smartReport/icon01.png
  4. 二进制
      src/assets/img/smartReport/icon02.png
  5. 二进制
      src/assets/img/smartReport/icon03.png
  6. 二进制
      src/assets/img/smartReport/icon04.png
  7. 二进制
      src/assets/img/smartReport/icon05.png
  8. 二进制
      src/assets/img/smartReport/icon06.png
  9. 二进制
      src/assets/img/smartReport/icon07.png
  10. 二进制
      src/assets/img/smartReport/icon08.png
  11. 二进制
      src/assets/img/smartReport/icon09.png
  12. 二进制
      src/assets/img/smartReport/icon10.png
  13. 二进制
      src/assets/img/smartReport/icon11.png
  14. 二进制
      src/assets/img/smartReport/icon12.png
  15. 二进制
      src/assets/img/smartReport/icon13.png
  16. 二进制
      src/assets/img/smartReport/icon14.png
  17. 二进制
      src/assets/img/smartReport/icon15.png
  18. 二进制
      src/assets/img/smartReport/icon16.png
  19. 17 0
      src/routes/modules/oldRoutes.js
  20. 20 0
      src/utils/buttonConfig.js
  21. 312 0
      src/views/smartReport/components/BaseInfo.vue
  22. 20 0
      src/views/smartReport/components/ChartComp.vue
  23. 106 0
      src/views/smartReport/components/ETAChart.vue
  24. 177 0
      src/views/smartReport/components/ETAPriceChart.vue
  25. 169 0
      src/views/smartReport/components/ETASandBox.vue
  26. 130 0
      src/views/smartReport/components/ETASheet.vue
  27. 16 0
      src/views/smartReport/components/ImgComp.vue
  28. 141 0
      src/views/smartReport/components/ImgEdit.vue
  29. 142 0
      src/views/smartReport/components/ImportETAChart.vue
  30. 170 0
      src/views/smartReport/components/ImportMyETAChart.vue
  31. 169 0
      src/views/smartReport/components/SemanticAnalysis.vue
  32. 41 0
      src/views/smartReport/components/SheetComp.vue
  33. 243 0
      src/views/smartReport/components/StatisticAnalysis.vue
  34. 20 0
      src/views/smartReport/components/TextComp.vue
  35. 112 0
      src/views/smartReport/components/TextEdit.vue
  36. 1274 0
      src/views/smartReport/editReport.vue
  37. 239 0
      src/views/smartReport/reportDetail.vue
  38. 794 0
      src/views/smartReport/reportList.vue

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

@@ -0,0 +1,85 @@
+import http from "@/api/http.js"
+
+//智能研报模块
+
+const apiSmartReport={
+    //报告列表
+    reportList:params=>{
+        return http.get('/smart_report/list',params)
+    },
+
+    //分类数据
+    classifyList:params=>{
+        return http.get('/classify/list',params)
+    },
+
+    //删除报告
+    delReport:params=>{
+        return http.post('/smart_report/remove',params)
+    },
+
+    //报告推送
+    reportMsgSend:params=>{
+        return http.post('/smart_report/send_msg',params)
+    },
+
+    // 作者
+    reportAuthor:params=>{
+        return http.get('/report/author',params)
+    },
+
+    //新增报告
+    reportAdd:params=>{
+        return http.post('/smart_report/add',params)
+    },
+
+    //编辑报告
+    reportEdit:params=>{
+        return http.post('/smart_report/edit',params)
+    },
+
+    //报告详情
+    reportDetail:params=>{
+        return http.get('/smart_report/detail',params)
+    },
+
+    //保存报告内容
+    saveReportContent:params=>{
+        return http.post('/smart_report/save_content',params)
+    },
+
+    //定时发布报告
+    prePublishReport:params=>{
+        return http.post('/smart_report/pre_publish',params)
+    },
+
+    //发布/取消发布报告
+    publishReport:params=>{
+        return http.post('/smart_report/publish',params)
+    },
+
+    //编辑状态
+    markReport:params=>{
+        return http.post('/smart_report/mark_edit',params)
+    },
+
+    //音频上传
+    voiceupload:params=>{
+        return http.post('/smart_report/voice_upload',params)
+    },
+
+    //报告导出图片
+    getReportImg:params=>{
+        return http.get('/smart_report/detail_img',params)
+    },
+
+    //获取上期已发布报告
+    getLastReport:params=>{
+        return http.get('/smart_report/last_published_report',params)
+    }
+
+}
+
+export {
+	apiSmartReport
+}

二进制
src/assets/img/smartReport/bg01.png


二进制
src/assets/img/smartReport/icon01.png


二进制
src/assets/img/smartReport/icon02.png


二进制
src/assets/img/smartReport/icon03.png


二进制
src/assets/img/smartReport/icon04.png


二进制
src/assets/img/smartReport/icon05.png


二进制
src/assets/img/smartReport/icon06.png


二进制
src/assets/img/smartReport/icon07.png


二进制
src/assets/img/smartReport/icon08.png


二进制
src/assets/img/smartReport/icon09.png


二进制
src/assets/img/smartReport/icon10.png


二进制
src/assets/img/smartReport/icon11.png


二进制
src/assets/img/smartReport/icon12.png


二进制
src/assets/img/smartReport/icon13.png


二进制
src/assets/img/smartReport/icon14.png


二进制
src/assets/img/smartReport/icon15.png


二进制
src/assets/img/smartReport/icon16.png


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

@@ -52,6 +52,18 @@ export default [
   	name:'AI问答',
   	hidden:false
   },
+  // 智能研报编辑页
+  {
+    path: "/smartReportEdit",
+    name: "智能报告",
+    component: () => import("@/views/smartReport/editReport.vue"),
+  },
+  // 智能研报详情页
+  {
+    path: "/smartReportDetail",
+    name: "智能报告",
+    component: () => import("@/views/smartReport/reportDetail.vue"),
+  },
 
   // 主页
   {
@@ -235,6 +247,11 @@ export default [
         name: "英文品种配置",
         component: () => import("@/views/report_manage/reportVariety.vue"),
       },
+      {
+        path: "smartReportList",
+        name: "智能研报",
+        component: () => import("@/views/smartReport/reportList.vue"),
+      },
     ],
   },
 

+ 20 - 0
src/utils/buttonConfig.js

@@ -34,6 +34,26 @@ export const reportManageBtn={
     reportManage_dayWeekReportAdd:'reportManage:dayWeekReportAdd',//添加晨报周报
     reportManage_reportAdd:'reportManage:reportAdd',//添加研报
 }
+/*
+ *--------智能研报列表----------- 
+ */
+ export const smartReportManageBtn={
+    reportManage_sendMsg:'smartReportManage:sendMsg',//推送消息/已推送消息
+    reportManage_reportView:'smartReportManage:reportView',//研报预览:即是否能点击研报名称跳转预览页面
+    reportManage_reportView_wechartShare:'smartReportManage:reportView:wechartShare',//研报预览页面-微信分享
+    reportManage_reportView_copyWechat:'smartReportManage:reportView:copyWechat',//研报预览页面-复制链接
+    reportManage_reportView_exportImg:'smartReportManage:reportView:exportImg',//研报预览页面-导出图片
+    reportManage_audioDownload:'smartReportManage:audioDownload',//音频下载
+    reportManage_audioUpload:'smartReportManage:audioUpload',//音频上传
+    reportManage_reportDel:'smartReportManage:reportDel',//删除研报
+    reportManage_reportEdit:'smartReportManage:reportEdit',//编辑研报
+    reportManage_cancelPublish:'smartReportManage:cancelPublish',//取消发布
+    reportManage_publish:'smartReportManage:publish',//发布研报
+    reportManage_reportList:'smartReportManage:reportList',//研报列表的选项
+    reportManage_reportList_uv:'smartReportManage:reportList:uv',//研报列表-PV/UV
+    reportManage_reportList_sendTime:'smartReportManage:reportList:sendTime',//研报列表-报告推送时间
+    reportManage_reportAdd:'smartReportManage:reportAdd',//添加研报
+}
 /*
 *--------英文研报列表----------- 
 */

+ 312 - 0
src/views/smartReport/components/BaseInfo.vue

@@ -0,0 +1,312 @@
+<template>
+    <el-dialog
+        title="基础信息"
+        :visible.sync="show"
+        :modal-append-to-body="false"
+        :close-on-click-modal="false"
+        :center="true"
+        v-dialogDrag
+        custom-class="dialogclass"
+        width="440px"
+        @close="handleClose"
+    >
+        <el-form 
+            :model="formData" 
+            :rules="rules" 
+            ref="baseinfoForm"
+            class="baseinfo-form-wrap"
+        >  
+            <el-form-item prop="type">
+                <el-radio-group v-model="formData.type" :disabled="id" @change="handleUpdateBaseInfo">
+                    <el-radio :label="1">新增报告</el-radio>
+                    <el-radio :label="2">继承报告</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <el-form-item prop="classify">
+                <el-cascader
+					ref="cascader"
+					:options="classifyArr"
+					v-model="formData.classify"
+					placeholder="请选择分类"
+					size="medium"
+                    style="width:340px"
+                    @change="handleUpdateBaseInfo"
+				/>
+            </el-form-item>
+            <el-form-item prop="title">
+                <el-input placeholder="请输入标题" v-model="formData.title" style="width:340px"></el-input>
+            </el-form-item>
+            <el-form-item prop="abstract">
+                <el-input type="textarea" placeholder="请输入摘要" v-model="formData.abstract" style="width:340px"></el-input>
+            </el-form-item>
+            <el-form-item prop="author">
+				<el-select
+					v-model="formData.author"
+					multiple
+					placeholder="请选择作者"
+					size="medium"
+					style="width: 340px"
+				>
+					<el-option
+						v-for="(item, i) in authorlist"
+						:key="i"
+						:label="item.ReportAuthor"
+						:value="item.ReportAuthor"
+					></el-option>
+				</el-select>
+			</el-form-item>
+            <el-form-item prop="frequency">
+                <el-select
+					v-model="formData.frequency"
+					placeholder="请选择频度"
+					size="medium"
+					style="width: 340px"
+				>
+                    <el-option label="年度" value="年度"></el-option>
+                    <el-option label="半年度" value="半年度"></el-option>
+                    <el-option label="季度" value="季度"></el-option>
+                    <el-option label="月度" value="月度"></el-option>
+                    <el-option label="双周度" value="双周度"></el-option>
+                    <el-option label="周度" value="周度"></el-option>
+                    <el-option label="日度" value="日度"></el-option>
+                    <el-option label="不定时" value="不定时"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item prop="time">
+                <el-date-picker
+					v-model="formData.time"
+					type="date"
+					value-format="yyyy-MM-dd"
+					placeholder="选择日期"
+					size="medium"
+					:clearable="false"
+					style="width: 340px"
+				></el-date-picker>
+            </el-form-item>
+        </el-form>
+        <div style="text-align:center;margin-top:60px;margin-bottom:40px">
+            <el-button type="primary" plain style="width:120px" @click="handleClose">取消</el-button>
+            <el-button type="primary" style="width:120px" @click="handleConfirm">确定</el-button>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+import {apiSmartReport}  from '@/api/modules/smartReport'
+export default {
+    name:"BaseInfo",
+    model:{
+        prop:'show',
+        event:'showChange'
+    },
+    props:{
+        show:{
+            type:Boolean,
+            default:false
+        },
+        id:{
+            type:Number,
+            default:0
+        }
+    },
+    watch: {
+        show(n){
+            if(!n){
+                this.formData.type=1
+                this.formData.classify=[]
+                this.formData.title=''
+                this.formData.abstract=''
+                this.formData.author=['FICC团队']
+                this.formData.frequency='日度'
+                this.formData.time=this.$moment().format('YYYY-MM-DD')
+            }else{
+                if(this.id){
+                    apiSmartReport.reportDetail({
+                        SmartReportId:Number(this.id)
+                    }).then(res=>{
+                        if(res.Ret===200){
+                            this.formData.type=res.Data.AddType
+                            this.formData.classify=[res.Data.ClassifyIdFirst,res.Data.ClassifyIdSecond]
+                            this.formData.title=res.Data.Title
+                            this.formData.abstract=res.Data.Abstract
+                            this.formData.author=res.Data.Author ? res.Data.Author.split(',') : []
+                            this.formData.frequency=res.Data.Frequency
+                            this.formData.time=res.Data.CreateTime
+                        }
+                    })
+                }
+            }
+        }  
+    },
+    data() {
+        return {
+            rules:{
+                type:[{ required: true, message: '请选择报告类型', trigger: 'change' }],
+                classify:[{ required: true, message: '请选择报告分类', trigger: 'change' }],
+                title:[{ required: true, message: '请填写报告标题', trigger: 'blur' }],
+            },
+            formData:{
+                type:1,
+                classify:[],
+                title:'',
+                abstract:'',
+                author:['FICC团队'],
+                frequency:'日度',
+                time:this.$moment().format('YYYY-MM-DD')||''
+            },
+            classifyArr:[],
+            authorlist:[]
+        }
+    },
+   
+    methods: {
+        handleClose(){
+            this.$emit('showChange', false)
+        },
+
+        handleConfirm(){
+            this.$refs.baseinfoForm.validate((valid)=>{
+                if(valid){
+
+                    const params={
+                        AddType: this.formData.type,
+                        ClassifyIdFirst: this.formData.classify[0]?this.formData.classify[0]:0,
+                        ClassifyNameFirst: '',
+                        ClassifyIdSecond:this.formData.classify[1]?this.formData.classify[1]:0,
+                        ClassifyNameSecond:'',
+                        Title: this.formData.title,
+                        Abstract: this.formData.abstract,
+                        Author:
+                            this.formData.author.length > 0
+                                ? this.formData.author.join(',')
+                                : '',
+                        Frequency: this.formData.frequency,
+                        CreateTime: this.formData.time,
+                    }
+                    this.classifyArr.forEach(item=>{
+                        if(item.value===params.ClassifyIdFirst){
+                            params.ClassifyNameFirst=item.label
+                            const arr=item.children||[]
+                            arr.forEach(_item=>{
+                                if(_item.value===params.ClassifyIdSecond){
+                                    params.ClassifyNameSecond=_item.label
+                                }
+                            })
+                        }
+                    })
+                    // 编辑
+                    if(this.id){
+                        this.$emit('save',params)
+                        return
+                    }
+                    apiSmartReport.reportAdd(params).then(res=>{
+                        if(res.Ret===200){
+                            this.handleClose()
+                            let { href } = this.$router.resolve({
+                                path: "/smartReportEdit",
+                                query: { id: res.Data.SmartReportId },
+                            });
+                            window.open(href, "_blank");
+                        }
+                    })
+
+                }
+            })
+        },
+
+        handleUpdateBaseInfo(){
+            if(this.formData.type===1){
+                if(this.formData.classify.length===2){
+                    this.formData.title=this.getSelectClassifyName()[1]
+                }
+                return
+            }
+            //获取上次报告
+            apiSmartReport.getLastReport({
+                ClassifyIdFirst:this.formData.classify[0],
+                ClassifyIdSecond:this.formData.classify[1]
+            }).then(res=>{
+                if(res.Ret!==200) return
+                if (res.Data == null) {
+					this.$message.warning('此分类暂无报告');
+					return false;
+				}
+                this.formData.title=res.Data.Title
+                this.formData.abstract=res.Data.Abstract
+                this.formData.author=res.Data.Author ? res.Data.Author.split(',') : ''
+                this.formData.frequency=res.Data.Frequency
+            })
+        },
+
+        // 获取选择的分类名称
+        getSelectClassifyName(){
+            let arr=[]
+            this.classifyArr.forEach(item=>{
+                if(this.formData.classify[0]&&item.value===this.formData.classify[0]){
+                    arr.push(item.label)
+                    if(item.children&&item.children.length>0){
+                        item.children.forEach(_item=>{
+                            if(this.formData.classify[1]&&_item.value===this.formData.classify[1]){
+                                arr.push(_item.label)
+                            }
+                        })
+                    }
+                }
+            })
+            return arr
+        },
+
+        // 获取分类
+        getclassifylist() {
+            let params = { CurrentIndex: 0, PageSize: 1000, KeyWord: "",HideDayWeek:1,/*不显示晨报/周报*/ };
+            apiSmartReport.classifyList(params).then((res) => {
+                if (res.Ret == 200 && Array.isArray(res.Data.List)) {
+                    this.classifyArr = [];
+                    res.Data.List.forEach((item, index) => {
+                        let newitem = {
+                            label: item.ClassifyName,
+                            value: item.Id,
+                        };
+                        if (item.Child) {
+                            let childnode = [];
+                            item.Child.forEach((itemchild, i) => {
+                                childnode.push({
+                                    label: itemchild.ClassifyName,
+                                    value: itemchild.Id,
+                                });
+                            });
+                            newitem.children = childnode;
+                        }
+                        this.classifyArr.push(newitem);
+                    });
+                }
+            });
+        },
+        // 获取作者
+        getreportauthor() {
+			apiSmartReport.reportAuthor({}).then((res) => {
+				if (res.Ret == 200) {
+					this.authorlist = res.Data.List || [];
+				}
+			});
+		},
+    },
+    mounted(){
+        this.getclassifylist()
+        this.getreportauthor()
+    },
+}
+</script>
+
+<style lang="scss">
+.baseinfo-form-wrap{
+    .el-input{
+        width: 100%;
+    }
+    .el-form-item{
+        width: 340px;
+        margin-left: auto;
+        margin-right: auto;
+    }
+}
+</style>

+ 20 - 0
src/views/smartReport/components/ChartComp.vue

@@ -0,0 +1,20 @@
+<template>
+    <div 
+        class="report-comp-item chart-comp"
+        style="min-height:300px;width:100%;height:100%;overflow: hidden;display:flex;flex-direction: column;margin-bottom:10px"
+    >
+        <iframe :src="compData.content" style="flex:1;width:100%;height:100%;border-width:0px;"></iframe>
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        compData:{}
+    }
+}
+</script>
+
+<style>
+
+</style>

+ 106 - 0
src/views/smartReport/components/ETAChart.vue

@@ -0,0 +1,106 @@
+<template>
+    <div class="eta-chart-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span :class="['item',activeType==='ETA图库'?'active':'']" @click="activeTypeChange('ETA图库')">ETA图库</span>
+                <span :class="['item',activeType==='MyETA'?'active':'']" @click="activeTypeChange('MyETA')">MyETA</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="图表名称"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    v-if="activeType==='ETA图库'"
+                    @input="handleETAChartSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <ImportETAChart ref="ETAChartWrap" v-if="activeType==='ETA图库'"/>
+            <ImportMyETAChart @handleImportMyChart="handleImportMyChart" v-if="activeType==='MyETA'"/>
+        </div>
+    </div>
+</template>
+
+<script>
+import ImportETAChart from './ImportETAChart.vue';
+import ImportMyETAChart from './ImportMyETAChart.vue';
+export default {
+    name:"ETAChartWrap",
+    components:{ImportETAChart,ImportMyETAChart},
+    data() {
+        return {
+            keyword:'',
+            activeType:'ETA图库',
+        }
+    },
+    methods: {
+        activeTypeChange(e){
+            if(this.activeType===e) return
+            this.activeType=e
+        },
+
+        handleETAChartSearch(){
+            this.$refs.ETAChartWrap.handleSearch(this.keyword)
+        },
+
+        handleImportMyChart(list){
+            this.$emit('handleImportMyChart',list)
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.eta-chart-wrap{
+    width: 100%;
+    min-width: 600px;
+    overflow-x: auto;
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .left-card{
+            border-radius: 4px;
+            border: 1px solid var(--unnamed, #DCDFE6);
+            background: var(--gary-gy-3-disabled, #EBEFF6);
+            display: flex;
+            align-items: center;
+            padding: 6px;
+            .item{
+                cursor: pointer;
+                display: block;
+                width: 110px;
+                height: 30px;
+                line-height: 30px;
+                text-align: center;
+                border-radius: 4px;
+                font-size: 18px;
+                color: #666;
+                &.active{
+                    background: #FFF;
+                    color: #002D78;
+                }
+            }
+        }
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 0 20px 20px 20px;
+    }
+}
+</style>

+ 177 - 0
src/views/smartReport/components/ETAPriceChart.vue

@@ -0,0 +1,177 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span>商品价格曲线</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="图表名称"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <el-checkbox
+                class="onlyshowme-box"
+                v-model="isShowMe" 
+                @change="handleIsShowMeChange"
+            >只看我的</el-checkbox>
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.UniqueCode">
+                        <div class="title">{{item.ChartName}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无图表" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import futuresInterface from "@/api/modules/futuresBaseApi";
+export default {
+    data() {
+        return {
+            
+            keyword:'',
+            isShowMe:false,
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getChartList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            const obj={
+                compId:3,
+                compType:'chart',
+                content:`${LINK_CHART_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        /* 搜索图表分页 */
+        async getChartList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+                IsShowMe: this.isShowMe,
+            };
+            let res = await futuresInterface.searchChart(params);
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getChartList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 169 - 0
src/views/smartReport/components/ETASandBox.vue

@@ -0,0 +1,169 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span>沙盘图</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="沙盘名称/品种"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.SandboxId">
+                        <div class="title">{{item.Name}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.PicUrl+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无数据" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import {sandInterface} from "@/api/api.js";
+export default {
+    data() {
+        return {
+            
+            keyword:'',
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getSandBoxList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const obj={
+                compId:2,
+                compType:'img',
+                content:item.PicUrl
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSandBoxList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSandBoxList()
+        },
+
+        /* 搜索图表分页 */
+        async getSandBoxList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+            };
+            let res = await sandInterface.sandlistByQuote(params);
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getSandBoxList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: contain;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 130 - 0
src/views/smartReport/components/ETASheet.vue

@@ -0,0 +1,130 @@
+<template>
+    <div class="eta-sheet-wrap">
+        <div class="top-box">
+            <span>ETA表格</span>
+            <el-input
+                class="search-box"
+				placeholder="表格名称"
+				v-model="keyword"
+				size="medium"
+				prefix-icon="el-icon-search"
+                @input="handleSearch"
+			/>
+        </div>
+        <div class="main-box">
+            <draggable
+                :list="list"
+                :group="{ name: 'component', pull: 'clone', put: false }"
+                class="sheet-list-box"
+                animation="300"
+                :sort="false"
+                tag="div"
+            >
+                <div class="sheet-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.ExcelInfoId">
+                    <div class="title">{{item.ExcelName}}</div>
+                    <div class="img" :style="'backgroundImage:url('+item.ExcelImage+')'"></div>
+                </div>
+            </draggable>
+            <tableNoData text="暂无数据" size="mini" v-if="list.length===0"/>
+        </div>
+    </div>
+</template>
+
+<script>
+import * as sheetInterface from "@/api/modules/sheetApi.js";
+export default {
+    data() {
+        return {
+            keyword:'',
+            list:[]
+        }
+    },
+    created(){
+        this.getSheetList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/sheetshow';
+            // console.log(LINK_URL);
+            const obj={
+                compId:4,
+                compType:'sheet',
+                content:`${LINK_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleSearch(){
+            this.getSheetList()
+        },
+        getSheetList() {
+            this.list=[]
+            sheetInterface.sheetList({
+                Keyword: this.keyword,
+                CurrentIndex: 1,
+                PageSize: 10000,
+            }).then((res) => {
+                if (res.Ret !== 200) return;
+                this.list = res.Data.List || [];
+            });
+        },
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.eta-sheet-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        .search-box{
+            width: 330px;
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        overflow-y: auto;
+        .sheet-list-box{
+            display: flex;
+            flex-wrap: wrap;
+            gap: 20px;
+            .sheet-item{
+                cursor: move;
+                padding: 0 10px 10px 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: contain;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+        }
+    }
+}
+</style>

+ 16 - 0
src/views/smartReport/components/ImgComp.vue

@@ -0,0 +1,16 @@
+<template>
+    <div 
+        class="report-comp-item img-comp" 
+        style="width:100%;height:100%;overflow-y: auto;margin-bottom:10px"
+    >
+        <img style="width:100%" :src="compData.content" alt="">
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        compData:{}
+    }
+}
+</script>

+ 141 - 0
src/views/smartReport/components/ImgEdit.vue

@@ -0,0 +1,141 @@
+<template>
+    <div class="img-edit-wrap" @paste="handleListenPaste">
+        <div style="font-size:16px;margin-bottom:20px">图片上传</div>
+        <div class="main-box">
+            <input type="file" size="small" name="file" @change="fileSelected" id="file" class="true-file" style="display:none;">
+            <div class="upload-box" @click="clickinput">
+                <img class="bg" :src="content" alt="" v-if="content">
+                <img src="~@/assets/img/smartReport/icon15.png" alt="">
+                <div>点击上传图片</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { bannerupload } from 'api/api.js';
+export default {
+    name:'ImgEdit',
+    props:{
+        content:''
+    },
+    created() {
+        window.addEventListener('paste',this.handleListenPaste)
+    },
+     
+    destroyed(){
+        window.removeEventListener('paste',this.handleListenPaste)
+    },
+    methods: {
+        clickinput(){  //上传模拟点击
+			$(`#file`).click();
+		},
+        fileSelected(file){  //选择文件上传
+            let hostfile=document.getElementById('file').files[0]||file
+			const that = this;
+		    if( hostfile ){
+		        let size = Math.floor(hostfile.size / 1024 / 1024);
+		        if( size>200 ){
+		            that.$message.warning('上传文件大小不能大于200M!');
+		            hostfile = {};
+		            return false
+		        }
+		        if( hostfile.name.toLowerCase().includes('.png') || hostfile.name.toLowerCase().includes('.jpg') || hostfile.name.toLowerCase().includes('.jpeg') ){
+		        	let form = new FormData();
+		        	form.append('file',hostfile);  //hostfile.name
+					bannerupload(form).then((res) => {
+						if( res.Ret === 200 ){
+							that.$emit('imgChange', res.Data.ResourceUrl)
+                            that.content=res.Data.ResourceUrl
+						}
+						$("#file").val('');
+						hostfile = {};
+					});
+				}else{
+					that.$message.warning('上传文件格式不正确!');
+		        }
+		    } 
+		},
+
+        handleListenPaste:_.throttle(async function(e){
+            if(!e.clipboardData) return
+            const clipboardDataItems = e.clipboardData.items
+            const clipboardDataFirstItem = clipboardDataItems[0]
+            if(clipboardDataFirstItem){
+                for (const item of clipboardDataItems) {
+                    if (item.kind === 'file' && item.type.indexOf('image') !== -1) {
+                        const imageFile = item.getAsFile()
+                        console.log('读取成功1',imageFile);
+                        if (imageFile) this.fileSelected(imageFile)
+                        return
+                    }
+                }
+                return
+            }
+            //clipboardData中没有图片,从navigator.clipboard.read里获取图片
+            let clipboardItems = null
+            try{
+                clipboardItems = await navigator.clipboard.read()
+            }catch(error){
+                this.$message.warning("剪贴板读取不到文件!")
+                return
+            }
+            const blob = await this.checkClipboardItems(clipboardItems)
+            console.log('读取成功2',blob);
+            if(blob) this.fileSelected(blob)
+
+        },1000),
+
+        checkClipboardItems(clipboard){
+            for(const Item of clipboard){
+                for(const type of Item.types){
+                    if(type.includes('image')){
+                        return Item.getType(type)
+                    }
+                }
+            }
+            return null
+        }
+    },
+
+
+
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+
+.main-box{
+    width: 100%;
+    height: calc(100vh - 150px);
+    border-radius: 4px;
+    border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+    background: #FFF;
+    padding: 30px;
+    .upload-box{
+        cursor: pointer;
+        width: 120px;
+        height: 120px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        border: 1px dashed var(--gary-gy-5-line, #C8CDD9);
+        background: var(--gary-gy-1-bottom-bk, #F8F8F8);
+        color: #999;
+        position: relative;
+        .bg{
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            left: 0;
+            top: 0;
+            z-index: 10;
+            background: #FFF;
+        }
+    }
+}
+</style>

+ 142 - 0
src/views/smartReport/components/ImportETAChart.vue

@@ -0,0 +1,142 @@
+<template>
+    <div class="import-eta-chart-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+            <el-checkbox
+                class="onlyshowme-box"
+                v-model="isShowMe" 
+                @change="handleIsShowMeChange"
+            >只看我的</el-checkbox>
+            <draggable
+                :list="list"
+                :group="{ name: 'component', pull: 'clone', put: false }"
+                class="chart-list-box"
+                animation="300"
+                :sort="false"
+                tag="div"
+            >
+                <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.UniqueCode">
+                    <div class="title">{{item.ChartName}}</div>
+                    <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+                </div>
+            </draggable>
+            <tableNoData text="暂无图表" size="mini" v-if="list.length===0"/>
+    </div>
+</template>
+
+<script>
+import { dataBaseInterface } from "@/api/api.js";
+export default {
+    data() {
+        return {
+            isShowMe:false,
+            page:1,
+            pageSize:20,
+            list:[],
+            finished:false,
+            keyword:''
+        }
+    },
+    created() {
+        this.getETAChartList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            const obj={
+                compId:3,
+                compType:'chart',
+                content:`${LINK_CHART_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        async getETAChartList(){
+            const res=await dataBaseInterface.chartSearchByEs({
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+                IsShowMe: this.isShowMe,
+            })
+            if(res.Ret===200){
+                const arr=res.Data.List || []
+                this.list=[...this.list,...arr]
+                this.finished=res.Data.Paging.IsEnd
+            }
+        },
+
+        handleIsShowMeChange(){
+            this.page=1
+            this.finished=false
+            this.list=[]
+            this.getETAChartList()
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getETAChartList()
+        },
+
+        handleSearch(key){
+            this.page=1
+            this.finished=false
+            this.list=[]
+            this.keyword=key
+            this.getETAChartList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.import-eta-chart-wrap{
+    height: 100%;
+    width: 100%;
+    position: relative;
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding-top: 20px;
+    .onlyshowme-box{
+            display: block;
+            position: sticky;
+            top: -20px;
+            padding: 20px 0;
+            background-color: #FFF;
+        }
+        .chart-list-box{
+            display: flex;
+            flex-wrap: wrap;
+            gap: 20px;
+            .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+        }
+}
+</style>

+ 170 - 0
src/views/smartReport/components/ImportMyETAChart.vue

@@ -0,0 +1,170 @@
+<template>
+    <div class="import-mychart-wrap">
+        <div class="select-box">
+			<el-select 
+				style="width:300px" 
+				v-model="selectMyChartClassify" 
+				filterable 
+				placeholder="图分类名称"
+				@change="handleChange"
+			>
+				<el-option
+					v-for="item in myChartClassifyList"
+					:key="item.MyChartClassifyId"
+					:label="item.MyChartClassifyName"
+					:value="item.MyChartClassifyId"
+				>
+				</el-option>
+			</el-select>
+			<el-button type="primary" @click="handleImport">一键导入</el-button>
+			<el-tooltip class="item" effect="dark" content="该操作将批量导入所选图分类下所有图表" placement="top-start">
+				<i style="font-size:24px;color: #666;" class="el-icon-question"></i>
+			</el-tooltip>
+		</div>
+		<div class="list" v-infinite-scroll="load" v-if="list.length>0">
+            <div class="chart-item" v-for="item in list" :key="item.UniqueCode" @click="handleClickItem">
+                <div class="title">{{item.ChartName}}</div>
+                <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+            </div>
+		</div>
+        <tableNoData text="无数据" size="mini" v-if="list.length===0"/>
+    </div>
+</template>
+
+<script>
+import { mychartInterface } from '@/api/api.js';
+export default {
+    data() {
+        return {
+            myChartClassifyList:[],
+            selectMyChartClassify:'',
+            pageSize:10000,
+			CurrentIndex: 1,
+			list:[],
+            loading:false,
+            finished:false,
+
+        }
+    },
+    props:{
+        showEnMark:{
+            type:Boolean,
+            default:false
+        }
+    },
+    mounted(){
+        this.getMyChartClassify()
+    },  
+    methods: {
+        //获取我的图库中分类
+		getMyChartClassify(){
+			mychartInterface.classifyList().then(res=>{
+				if (res.Ret !== 200) return;
+				this.myChartClassifyList=res.Data?res.Data.List:[]
+			})
+		},
+        handleChange(){
+            this.CurrentIndex=1
+            this.list=[]
+            this.finished=false
+            this.loading=false
+            this.handleGetMyChartList()
+        },
+		async handleGetMyChartList(){
+            if(!this.selectMyChartClassify) return
+            this.loading=true
+            const res=await  mychartInterface.myList({
+				PageSize: this.pageSize,
+				CurrentIndex: this.CurrentIndex,
+				MyChartClassifyId: this.selectMyChartClassify || 0
+			})
+            this.loading=false
+            const arr=res.Data?res.Data.List.filter(_=>!_.Disabled):[]
+            this.list=[...this.list,...arr]
+            if(!res.Data){
+                this.finished=true
+            }
+            if(res.Data&&res.Data.Paging.IsEnd){
+                this.finished=true
+            }
+		},
+
+        load(){
+            if(this.finished) return
+            this.CurrentIndex++
+            this.handleGetMyChartList()
+        },
+
+        handleImport:_.throttle(function(){
+            if(!this.selectMyChartClassify){
+                this.$message.warning('请选择分类')
+                return
+            }
+            if(this.list.length==0){
+                this.$message.warning('该分类无图表')
+                return
+            }
+            this.$emit('handleImportMyChart',this.list)
+            setTimeout(() => {
+                this.CurrentIndex=1
+                this.list=[]
+                this.finished=false
+                this.loading=false
+                this.selectMyChartClassify=''
+            }, 300);
+        },1000),
+
+        handleClickItem(){
+            this.$message.warning('此处不支持单个插入图表')
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.import-mychart-wrap{
+    position: relative;
+    height: 100%;
+    padding-top: 20px;
+    .select-box{
+        position: absolute;
+        top: -75px;
+        right: 0;
+    }
+    .list{
+        max-height: calc(100vh - 220px);;
+        overflow-x: hidden;
+		overflow-y: auto;
+        display: flex;
+            flex-wrap: wrap;
+            gap: 20px;
+            .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                max-height: 260px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+    }
+}
+</style>

+ 169 - 0
src/views/smartReport/components/SemanticAnalysis.vue

@@ -0,0 +1,169 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span>语义分析</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="关键词搜索"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.SaCompareId">
+                        <div class="title">{{item.Title}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.ResultImg+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无数据" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import {semanticInterface} from '@/api/modules/semanticsApi.js';
+export default {
+    data() {
+        return {
+            
+            keyword:'',
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getSemanticList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const obj={
+                compId:2,
+                compType:'img',
+                content:item.ResultImg
+            }
+            return JSON.stringify(obj)
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSemanticList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getSemanticList()
+        },
+
+        /* 搜索图表分页 */
+        async getSemanticList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+            };
+            let res = await semanticInterface.compareSearch(params);
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getSemanticList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: contain;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 41 - 0
src/views/smartReport/components/SheetComp.vue

@@ -0,0 +1,41 @@
+<template>
+    <div 
+        class="report-comp-item sheet-comp"
+        style="width:100%;overflow: hidden;margin-bottom:10px"
+    >
+        <iframe :class="id" :src="compData.content" width="100%" style="border-width:0px;"></iframe>
+    </div>
+</template>
+
+<script>
+function GetQueryString(url) {
+    let urlStr=url.split('?')[1]
+    let obj={}
+    let paramsArr=urlStr.split('&')
+    for (let index = 0; index < paramsArr.length; index++) {
+        let arr=paramsArr[index].split('=')
+        obj[arr[0]]=arr[1]
+    }
+    return obj
+}
+export default {
+    props:{
+        compData:{}
+    },
+    computed: {
+        id(){
+            if(this.compData.content){
+                // console.log(this.compData.content);
+                let params = GetQueryString(this.compData.content)
+                // console.log(params);
+                return `iframe${params.code}`
+            }
+            return ''
+        }
+    },
+}
+</script>
+
+<style>
+
+</style>

+ 243 - 0
src/views/smartReport/components/StatisticAnalysis.vue

@@ -0,0 +1,243 @@
+<template>
+    <div class="statistic-analysis-wrap">
+        <div class="top-box">
+            <div class="left-card">
+                <span
+                    :class="['item',activeType===item.key?'active':'']" 
+                    @click="activeTypeChange(item.key)"
+                    v-for="item in typeOpts"
+                    :key="item.key"
+                >{{item.name}}</span>
+            </div>
+            <div class="right">
+                <el-input
+                    class="search-box"
+					placeholder="图表名称"
+					v-model="keyword"
+					size="medium"
+					prefix-icon="el-icon-search"
+                    @input="handleSearch"
+				/>
+            </div>
+        </div>
+        <div class="main-box">
+            <el-checkbox
+                class="onlyshowme-box"
+                v-model="isShowMe" 
+                @change="handleIsShowMeChange"
+            >只看我的</el-checkbox>
+            <div class="list-wrap" v-infinite-scroll="handleLoadMore" :infinite-scroll-immediate="false">
+                <draggable
+                    :list="list"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="chart-list-box"
+                    animation="300"
+                    :sort="false"
+                    tag="div"
+                >
+                    <div class="chart-item" :comp-data="getCompData(item)" v-for="item in list" :key="item.UniqueCode">
+                        <div class="title">{{item.ChartName}}</div>
+                        <div class="img" :style="'backgroundImage:url('+item.ChartImage+')'"></div>
+                    </div>
+                </draggable>
+                <tableNoData text="暂无图表" size="mini" v-if="list.length===0&&finished"/>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import chartRelevanceApi from "@/api/modules/chartRelevanceApi";
+import {
+  fittingEquationInterface,
+  statisticFeatureInterface,
+} from "@/api/modules/chartRelevanceApi";
+export default {
+    data() {
+        return {
+            typeOpts:[
+                {
+                    name:'相关性',
+                    key:'相关性'
+                },
+                {
+                    name:'拟合方程曲线',
+                    key:'拟合方程曲线'
+                },
+                {
+                    name:'统计特征',
+                    key:'统计特征'
+                }
+            ],
+            keyword:'',
+            isShowMe:false,
+            activeType:'相关性',
+
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false
+        }
+    },
+    created(){
+        this.getChartList()
+    },
+    methods: {
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        getCompData(item){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            const obj={
+                compId:3,
+                compType:'chart',
+                content:`${LINK_CHART_URL}?code=${item.UniqueCode}`
+            }
+            return JSON.stringify(obj)
+        },
+
+        activeTypeChange(e){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.keyword=''
+            this.isShowMe=false
+            this.activeType=e
+            this.getChartList()
+        },
+
+        handleIsShowMeChange(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        handleSearch(){
+            this.list=[]
+            this.page=1
+            this.finished=false
+            this.getChartList()
+        },
+
+        /* 搜索图表分页 */
+        async getChartList(word) {
+            let params = {
+                Keyword: this.keyword || "",
+                CurrentIndex: this.page,
+                PageSize: this.pageSize,
+                IsShowMe: this.isShowMe,
+            };
+            let res = null;
+            if (this.activeType === '相关性') {
+                res = await chartRelevanceApi.searchChart(params);
+            } else if (this.activeType === '拟合方程曲线') {
+                res = await fittingEquationInterface.searchChart(params);
+            } else if (this.activeType === '统计特征') {
+                res = await statisticFeatureInterface.searchChart(params);
+            }
+
+            if (res.Ret !== 200) return;
+            const arr = res.Data.List || [];
+            this.list =
+                this.page === 1
+                ? arr
+                : [...this.list, ...arr];
+            this.finished =  res.Data.Paging.IsEnd;
+        },
+
+        handleLoadMore(){
+            if(this.finished) return
+            this.page++
+            this.getChartList()
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.statistic-analysis-wrap{
+    .top-box{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .left-card{
+            border-radius: 4px;
+            border: 1px solid var(--unnamed, #DCDFE6);
+            background: var(--gary-gy-3-disabled, #EBEFF6);
+            display: flex;
+            align-items: center;
+            gap: 0 3px;
+            padding: 6px;
+            .item{
+                cursor: pointer;
+                display: block;
+                width: 110px;
+                height: 30px;
+                line-height: 30px;
+                text-align: center;
+                border-radius: 4px;
+                font-size: 18px;
+                color: #666;
+                &.active{
+                    background: #FFF;
+                    color: #002D78;
+                }
+            }
+        }
+        .right{
+            .search-box{
+                width: 330px;
+            }
+        }
+    }
+    .main-box{
+        margin-top: 30px;
+        height: calc(100vh - 180px);
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px;
+        display: flex;
+        flex-direction: column;
+        .list-wrap{
+            flex: 1;
+            overflow-y: auto;
+
+        }
+    }
+}
+.chart-list-box{
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+    .chart-item{
+                cursor: move;
+                padding: 0 10px;
+                width: 250px;
+                border-radius: 4px;
+                border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                background: var(--gary-gy-white, #FFF);
+                .title{
+                    padding: 10px 0;
+                    text-align: center;
+                    border-bottom: 1px solid #C8CDD9;
+                    overflow: hidden;
+                    white-space: nowrap;
+                    text-overflow: ellipsis;
+                }
+                .img{
+                    width: 100%;
+                    height: 197px;
+                    background-size: cover;
+                    background-position: center;
+                    background-repeat: no-repeat;
+                }
+            }
+}
+</style>

+ 20 - 0
src/views/smartReport/components/TextComp.vue

@@ -0,0 +1,20 @@
+<template>
+    <div 
+        class="report-comp-item text-comp" 
+        style="width:100%;height: 100%;overflow-y: auto;margin-bottom:10px"
+    >
+        <div class="rich-text-box" v-html="compData.content"></div>
+    </div>
+</template>
+
+<script>
+export default {
+    props:{
+        compData:{}
+    }
+}
+</script>
+
+<style>
+
+</style>

+ 112 - 0
src/views/smartReport/components/TextEdit.vue

@@ -0,0 +1,112 @@
+<template>
+    <div class="text-edit-wrap">
+        <div style="font-size:16px;margin-bottom:20px">文本编辑</div>
+        <froala
+			id="froala-editor"
+			ref="froalaEditor"
+			:tag="'textarea'"
+			:config="froalaConfig"
+			v-model="html"
+		/>
+    </div>
+</template>
+
+<script>
+import VueFroala from 'vue-froala-wysiwyg';
+export default {
+    name:'TextEdit',
+    props:{
+        content:''
+    },
+    data() {
+        const that = this;
+        return {
+            html:this.content||'',
+            editor: null,
+            lastEditRange: null,
+            froalaConfig:{
+                toolbarButtons: [
+                    "textColor",
+                    "bold",
+                    "italic",
+                    "underline",
+                    "strikeThrough",
+                    "subscript",
+                    "superscript",
+                    "fontFamily",
+                    "fontSize",
+                    "color",
+                    "inlineClass",
+                    "inlineStyle",
+                    "paragraphStyle",
+                    "lineHeight",
+                    "paragraphFormat",
+                    "align",
+                    "formatOL",
+                    "formatUL",
+                    "outdent",
+                    "indent",
+                    "quote",
+                    "specialCharacters",
+                    "insertHR",
+                    "selectAll",
+                    "clearFormatting",
+                    "html",
+                    "undo",
+                    "redo",
+                ],
+                height: 800,
+                fontSize: ["12", "14", "16", "18", "20", "24", "28", "32", "36", "40"],
+                fontSizeDefaultSelection: "16",
+                theme: "dark", //主题
+                placeholderText: "请输入内容",
+                language: "zh_cn", //国际化
+                imageDefaultWidth: false,
+                quickInsertEnabled: false,
+                toolbarVisibleWithoutSelection: true, //是否开启 不选中模式
+                toolbarSticky: false, //操作栏是否自动吸顶
+                saveInterval: 0,
+                events: {
+                    //this.editor 定义在vue data 中
+                    initialized: function () {
+                        // this.editor = editor;
+                        that.editor = this;
+                        // that.editor.html.set(that.value);
+                        // that.setHtml()
+                    },
+                    keyup: function (e, editor) {
+                        //添加事件,在每次按键按下时,都记录一下最后停留位置
+                        that.$nextTick(function () {
+                            that.lastEditRange = getSelection().getRangeAt(0);
+                        });
+                    },
+                    click: function (e, editor) {
+                        //添加事件,在每次鼠标点击时,都记录一下最后停留位置
+                        that.$nextTick(function () {
+                            that.lastEditRange = getSelection().getRangeAt(0);
+                        });
+                    },
+                    //内容改变事件
+                    contentChanged: function () {
+                        that.lastEditRange = getSelection().getRangeAt(0) || 0;
+                        
+                        // that.$emit('textChange', that.html)
+                        that.sendHtml()
+                    },
+                },
+            }
+        }
+    },
+    methods: {
+        sendHtml(){
+            const str=this.html.replace(/<p data-f-id=\"pbf\".*?<\/p>/g, "")
+            this.$emit('textChange', str)
+        }
+    },
+    components:{}
+}
+</script>
+
+<style>
+
+</style>

+ 1274 - 0
src/views/smartReport/editReport.vue

@@ -0,0 +1,1274 @@
+<template>
+    <div class="edit-smart-report-page">
+        <!-- 顶部操作栏 -->
+        <div class="top-action-wrap">
+            <div class="title">{{reportInfo&&reportInfo.Title}}</div>
+            <ul class="action-list">
+                <li class="action-item" @click="showReportBaseInfo=true">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>基础信息</span>
+                </li>
+                <li class="action-item" @click="handleRefreshAllChart">
+                    <img src="~@/assets/img/smartReport/icon02.png" alt="">
+                    <span>图表刷新 </span>
+                </li>
+                <li class="action-item" @click="handlePreviewReport">
+                    <img src="~@/assets/img/smartReport/icon03.png" alt="">
+                    <span>预览</span>
+                </li>
+                <li class="action-item" @click="handleSaveContent">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>存草稿</span>
+                </li>
+                <li class="action-item" @click="handlePublishOpt('dsfb')">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>定时发布</span>
+                </li>
+                <li class="action-item" @click="handlePublishOpt('fb')">
+                    <img src="~@/assets/img/smartReport/icon01.png" alt="">
+                    <span>发布</span>
+                </li>
+            </ul>
+        </div>
+        <div class="main-wrap">
+            <div class="report-action-wrap">
+                <ul class="top-type-list">
+                    <li class="item" v-for="item in topTypeList" :key="item.name" @click="handleShowRight(item)">
+                        <img class="icon" :src="item.icon" alt="">
+                        <span>{{item.name}}</span>
+                    </li>
+                </ul>
+                <!-- 公共组件 -->
+                <draggable
+                    :list="compList"
+                    :group="{ name: 'component', pull: 'clone', put: false }"
+                    class="report-comp-wrap"
+                    animation="300"
+                    :sort="false"
+                    tag="ul"
+                >
+                    <li class="comp-item" :comp-data="JSON.stringify(comp)" v-for="comp in compList" :key="comp.id">
+                        <img :src="comp.icon">
+                    </li>
+                </draggable>
+
+                <div class="report-content-box">
+                    <draggable
+                        :list="conList"
+                        :group="{ name: 'component', pull: true, put: true }"
+                        class="report-html-wrap"
+                        animation="300"
+                        tag="div"
+                        handle=".drag-btn_p"
+                        @add="handleParentAdd"
+                        @remove="handleParentRemove"
+                        :move="handleParentMove"
+                    >
+                        <div 
+                            :class="['report-drag-item-wrap',activeId===item.id?'blue-bg':'']" 
+                            v-for="item,index in conList" 
+                            :key="item.id"
+                            :comp-type="item.compType"
+                            @click="handleChoose(item,index)"
+                            :style="item.style"
+                        >
+                            <!-- 缩放的盒子 -->
+                            <div class="resize-drag-box" @mousedown.stop="handleResizeP($event,index)"></div>
+                            <div class="opt-btn-box" style="display: none;">
+                                <div class="drag-btn drag-btn_p"></div>
+                                <div class="del-btn" @click.stop="handleDelItem(index,-1)"></div>
+                            </div>
+                            <div 
+                                v-if="item.child&&!item.child.length"
+                                class="report-drag-item-wrap_content"
+                                :data-id="item.id"
+                            >
+                                <component :is="getComponentName(item)" :compData="item"/>
+                            </div>
+                            <draggable
+                                :list="item.child"
+                                :group="{ name: 'component', pull: true, put: item.child&&item.child.length<3?true:false }"
+                                animation="300"
+                                tag="div"
+                                class="report-drag-item-wrap_child-wrap"
+                                @add="handleChildAdd($event,item,index)"
+                                @remove="handleChildRemove($event,item.child)"
+                                handle=".drag-btn_c"
+                                style="display: flex;gap: 20px;align-items: flex-start;"
+                            >
+                                <div 
+                                    :class="['report-drag-item-wrap_child_content',activeId===child.id?'blue-bg':'']" 
+                                    v-for="child,cindex in item.child" 
+                                    :key="child.id"
+                                    :comp-type="child.compType"
+                                    :data-id="child.id"
+                                    @click.stop="handleChoose(child,index,cindex)"
+                                    style="flex:1"
+                                    :style="child.style"
+                                >
+                                    <div class="opt-btn-box2" style="display: none;">
+                                        <div class="drag-btn drag-btn_c"></div>
+                                        <div class="del-btn" @click.stop="handleDelItem(index,cindex)"></div>
+                                    </div>
+                                    <!-- 拖动按钮 -->
+                                    <div class="resize-drag-box_lb" @mousedown.stop="handleResizeC($event,index,cindex,'lb')"></div>
+                                    <div class="resize-drag-box_rb" @mousedown.stop="handleResizeC($event,index,cindex,'rb')"></div>
+                                    <component :is="getComponentName(child)" :compData="child"/>
+                                </div>
+                            </draggable>
+                        </div>
+                    </draggable>
+                </div>
+            </div>
+
+            <div class="right-action-wrap" v-show="showRight">
+                <div class="close-icon" @click="handleCloseRight">
+                    <img src="~@/assets/img/smartReport/icon14.png" alt="">
+                </div>
+                <div style="overflow-x:auto">
+                    <div style="min-width:600px">
+                <TextEdit 
+                    v-if="rightType==='text'"
+                    :key="activeId"
+                    :content="activeContent" 
+                    @textChange="handleTextChange" 
+                />
+                <ImgEdit 
+                    v-if="rightType==='img'"
+                    :key="activeId"
+                    :content="activeContent" 
+                    @imgChange="handleTextChange" 
+                />
+                <!-- 图库插入 -->
+                <ETAChart 
+                    v-if="rightType==='etaChart'"
+                    @handleImportMyChart="handleImportMyChart"
+                />
+                <!-- ETA表格 -->
+                <ETASheet v-if="rightType==='etaSheet'"/>
+                <!-- 统计分析 -->
+                <StatisticAnalysis v-if="rightType==='statisticAnalysis'"/>
+                <!-- 商品价格曲线 -->
+                <ETAPriceChart v-if="rightType==='etaPriceChart'"/>
+                <!-- 沙盘图 -->
+                <ETASandBox v-if="rightType==='etaSandBox'"/>
+                <!-- 语义分析 -->
+                <SemanticAnalysis v-if="rightType==='semanticAnalysis'"/>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- 报告基础信息 -->
+        <BaseInfo  v-model="showReportBaseInfo" :id="$route.query.id" @save="handleReportEdit" />
+
+        <!-- 定时发布弹窗 -->
+		<el-dialog 
+			v-dialogDrag 
+			:append-to-body="true" 
+			:visible.sync="showDSFB" 
+			width="500px" 
+			title="定时发布"
+		>
+			<div>
+				<div>
+					<span>发送时间</span>
+					<el-date-picker
+						v-model="taskTime"
+						type="datetime"
+						placeholder="选择日期时间"
+						value-format="yyyy-MM-dd HH:mm"
+						:picker-options="timePickerOpt"
+					/>
+				</div>
+				<p style="margin:15px 0">设置成功之后,研报将定时进行发送。</p>
+				<div style="text-align:right;margin:20px 0">
+					<el-button type="primary" plain @click="showDSFB=false">取消</el-button>
+					<el-button type="primary" @click="handleSetReportPrepublish">确定</el-button>
+				</div>
+			</div>
+		</el-dialog>
+
+    </div>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+import TextComp from './components/TextComp.vue'
+import ChartComp from './components/ChartComp.vue'
+import ImgComp from './components/ImgComp.vue'
+import SheetComp from './components/SheetComp.vue'
+import _ from 'lodash'
+import TextEdit from './components/TextEdit.vue'
+import ImgEdit from './components/ImgEdit.vue'
+import ETAChart from './components/ETAChart.vue'
+import ETASheet from './components/ETASheet.vue'
+import { getPublicSettingsApi } from '@/api/modules/oldApi';
+import { dataBaseInterface } from "@/api/api.js";
+import {apiSmartReport}  from '@/api/modules/smartReport'
+import BaseInfo from './components/BaseInfo.vue'
+import StatisticAnalysis from './components/StatisticAnalysis.vue'
+import ETAPriceChart from './components/ETAPriceChart.vue'
+import ETASandBox from './components/ETASandBox.vue'
+import SemanticAnalysis from './components/SemanticAnalysis.vue'
+import { getUrlParams } from '@/utils/common'
+export default {
+    name:"smartReportEdit",
+    components: {
+        draggable,
+        BaseInfo,
+        TextComp,
+        ChartComp,
+        ImgComp,
+        SheetComp,
+        TextEdit,
+        ImgEdit,
+        ETAChart,
+        ETASheet,
+        StatisticAnalysis,
+        ETAPriceChart,
+        ETASandBox,
+        SemanticAnalysis
+    },
+    watch:{
+        'taskTime'(){
+            this.taskTime=this.$moment(this.taskTime).format('YYYY-MM-DD HH:mm')+':00'
+			const date = this.$moment(this.taskTime).startOf('day').format('x');
+	        const nowDate = this.$moment().startOf('day').format('x');
+	        // 如果选择的是今天 则需要禁用已经过去的时间节点
+	        if (date <= nowDate) {
+	            // 默认选择的最新时间 是当前时间的两分钟后 (留出2分钟的富裕时间)
+	            this.timePickerOpt.selectableRange = (
+	                `${this.$moment().add(2,'m').format('HH:mm:ss')} - 23:59:59`
+	            );
+	        }else {
+				// 如果是以后的日期,则不需要禁用时间节点
+	            this.timePickerOpt.selectableRange = '00:00:00 - 23:59:59';
+	        }
+		},
+        conList:{
+            handler(n,o){
+                console.log('内容改变');
+                this.contentChange=true
+            },
+            deep:true
+        }
+    },
+    data() {
+        return {
+            reportInfo:null,
+            showReportBaseInfo:false,
+
+            topTypeList:[
+                {
+                    name:'图库',
+                    type:'etaChart',
+                    icon:require('@/assets/img/smartReport/icon04.png')
+                },
+                {
+                    name:'ETA表格',
+                    type:'etaSheet',
+                    icon:require('@/assets/img/smartReport/icon05.png')
+                },
+                {
+                    name:'统计分析',
+                    type:'statisticAnalysis',
+                    icon:require('@/assets/img/smartReport/icon06.png')
+                },
+                {
+                    name:'商品价格曲线',
+                    type:'etaPriceChart',
+                    icon:require('@/assets/img/smartReport/icon07.png')
+                },
+                {
+                    name:'沙盘图',
+                    type:'etaSandBox',
+                    icon:require('@/assets/img/smartReport/icon08.png')
+                },
+                {
+                    name:'语义分析',
+                    type:'semanticAnalysis',
+                    icon:require('@/assets/img/smartReport/icon09.png')
+                }
+            ],
+            compList:[
+                {   
+                    compId:1,
+                    compType:'text',
+                    icon:require('@/assets/img/smartReport/icon10.png')
+                },
+                {
+                    compId:2,
+                    compType:'img',
+                    icon:require('@/assets/img/smartReport/icon11.png')
+                },
+                // {
+                //     compId:3,
+                //     compType:'chart',
+                // },
+                // {
+                //     compId:4,
+                //     compType:'sheet',
+                // }
+                
+            ],
+            conList:[],
+            activeId:'',
+            activeContent:'',
+            activePindex:'',
+            activeCindex:'',
+            showRight:false,
+            rightType:'',
+
+            timer:null,//自动保存定时器
+
+            showDSFB:false,//显示定时发布弹窗
+			taskTime:'',//定时发布的时间
+			timePickerOpt:{
+				disabledDate(e){
+					return e.getTime()< new Date().getTime()-24 * 60 * 60 * 1000
+				},
+				selectableRange:'00:00:00 - 23:59:59',
+                format:'HH:mm'
+			},
+
+            contentChange:false,//内容是否发生变化
+
+            isDragResize:false,//是否正在拖动缩放
+        }
+    },
+    methods: {
+        // 大盒子的高度缩放
+        handleResizeP(e,index){
+            this.isDragResize=true
+            e.preventDefault()
+            const targetBox=e.target.parentNode
+            const targetBoxHeight=targetBox.offsetHeight
+            const startY=e.clientY
+            document.onmousemove=(mouseEl)=>{
+                mouseEl.preventDefault()
+                const h=mouseEl.clientY-startY+targetBoxHeight
+                targetBox.style.minHeight=`${h<50?50:h}px`
+            }
+            document.onmouseup=(el)=>{
+                console.log(targetBox.style.cssText);
+                this.$set(this.conList[index],'style',targetBox.style.cssText)
+                el.preventDefault()
+                document.onmousemove=null
+                setTimeout(() => {
+                    this.isDragResize=false
+                }, 50);
+                
+            }
+        },
+        // 内部元素的缩放
+        handleResizeC(e,index,cindex,type){
+            this.isDragResize=true
+            e.preventDefault()
+            const parentBox=e.target.parentNode.parentNode
+            const parentBoxWidth=parentBox.offsetWidth
+            console.log(e);
+            const targetBox=e.target.parentNode
+            const targetBoxHeight=targetBox.offsetHeight
+            const targetBoxWidth=targetBox.offsetWidth
+            const startY=e.clientY
+            const startX=e.clientX
+            const initW=targetBoxWidth //拖动前要拖动盒子所占的宽度
+
+            let computerW=0//存放其他兄弟节点要改变的宽度的值
+
+            document.onmousemove=(mouseEl)=>{
+                mouseEl.preventDefault()
+                const h=mouseEl.clientY-startY+targetBoxHeight
+                // 计算宽度
+                const w=type==='rb'?mouseEl.clientX-startX+targetBoxWidth:startX-mouseEl.clientX+targetBoxWidth
+                const resW= (w/parentBoxWidth)*100//计算出的百分比结果值
+                targetBox.style.width=resW+'%'
+                targetBox.style.flex='none'
+
+                // 处理兄弟盒子的宽度
+                const changeW=w-initW //宽度变化值
+                computerW=changeW
+                if(parentBox.childNodes.length===3){
+                    computerW=changeW/2
+                }
+                // console.log('改变的宽度',computerW);
+
+                targetBox.style.height=`${h<50?50:h}px`
+            }   
+            document.onmouseup=(el)=>{
+                if(document.onmousemove){
+                    parentBox.childNodes.forEach(item=>{
+                        if(item!==targetBox){
+                            const temw=item.offsetWidth-computerW
+                            item.style.width=((temw/parentBoxWidth)*100)+'%'
+                        }
+                    })
+                    // 存储修改的值
+                    parentBox.childNodes.forEach((item,idx)=>{
+                        this.$set(this.conList[index].child[idx],'style',item.style.cssText)
+                    })
+                }
+                el.preventDefault()
+                document.onmousemove=null
+                setTimeout(() => {
+                    this.isDragResize=false
+                    document.onmouseup=null
+                }, 50);
+            }
+        },
+
+        // 跳转预览
+        handlePreviewReport(){
+            const htmlStr=$('.report-html-wrap').html()
+            sessionStorage.setItem('smartReportContent', htmlStr);
+			let { href } = this.$router.resolve({ 
+                path: '/smartReportDetail',
+                query:{
+                    id:this.$route.query.id,
+                    type:'preview'
+                }
+            });
+			window.open(href, '_blank');
+        },
+
+        // 批量插入myETA数据
+        handleImportMyChart(list){
+            const LINK_CHART_URL = this.$setting.dynamicOutLinks.ChartViewUrl+'/chartshow';
+            // console.log(list);
+            let arr=[]
+            // 分成三个一组
+            for (let index = 0; index < list.length; index+=3) {
+                const temarr=list.slice(index,index+3)
+                let resArr={child:[]}
+                if(temarr.length===1){//落单了
+                    resArr={
+                        compId:3,
+                        compType:'chart',
+                        content:`${LINK_CHART_URL}?code=${temarr[0].UniqueCode}`,
+                        id:this.getCompId(`chart${temarr[0].UniqueCode}_`),
+                        child:[]
+                    }
+                }else{
+                    temarr.forEach(e => {
+                        resArr.child.push({
+                            compId:3,
+                            compType:'chart',
+                            content:`${LINK_CHART_URL}?code=${e.UniqueCode}`,
+                            id:this.getCompId(`chart${e.UniqueCode}_`),
+                            child:[]
+                        })
+                    });
+                }
+                arr.push(resArr)
+            }
+            // console.log(arr);
+            this.conList=[...this.conList,...arr]
+            
+        },
+
+        // 设置sheet iframe 样式
+        setSheetIframeStyle(e){
+            const { height, code } = e.data;
+            // console.log(e.data);
+            let iframeDom = document.getElementsByClassName(`iframe${code}`);
+            // console.log(iframeDom);
+            iframeDom.forEach((ele) => {
+                ele.height = `${height + 45}px`;
+            });
+        },
+
+        handleParentAdd(e){
+            console.log('container-onAdd操作------------------->');
+
+            const {item,newDraggableIndex}=e
+
+            const hasid=item.getAttribute('data-id')
+            console.log(hasid);
+
+            this.conList.forEach(item=>{
+                if(item.id==hasid&&item.style){
+                    const styleArr=item.style.split(';').filter(s=>s&&(s.indexOf('width')===-1&&s.indexOf('flex')===-1)).join(';')
+                    item.style=styleArr
+                }
+            })
+            if(hasid) return
+
+            // 要添加的元素数据
+            const compData=JSON.parse(item.getAttribute('comp-data'))
+            // console.log(compData);
+            // 非注册组件返回
+            // if(!comp){
+            //     this.conList.splice(newDraggableIndex,1)
+            //     return
+            // }
+            const tempCompData={
+                compId:compData.compId,
+                compType:compData.compType,
+                id:this.getCompId(compData.compType),
+                content:compData.content||'',
+                child:[]
+            }
+            // console.log(tempCompData);
+            this.conList.splice(newDraggableIndex,1,tempCompData)
+            // this.activeId=tempCompData.id
+        },
+
+        handleChildAdd(e,parent,parentIndex){
+            console.log('child-onAdd操作------------------->');
+            // console.log(parent);
+            const {item,newDraggableIndex}=e
+
+            const compData=JSON.parse(item.getAttribute('comp-data'))
+
+            console.log(compData,newDraggableIndex);
+
+            const index=parentIndex
+            // if(index>-1){
+                let obj=_.cloneDeep(this.conList[index]) 
+
+                console.log(obj);
+
+                if(obj.child&&obj.child.length===1&&obj.id){
+                    if(compData){
+                        obj={
+                            child:[
+                                {
+                                    compId:obj.compId,
+                                    compType:obj.compType,
+                                    id:obj.id,
+                                    content:obj.content,
+                                    child:[]
+                                },
+                                {
+                                    compId:compData.compId,
+                                    compType:compData.compType,
+                                    content:compData.content||'',
+                                    id:this.getCompId(compData.compType),
+                                    child:[]
+                                }
+                            ]
+                        }
+                    }else{//是内容区域拖动排序的
+                        const temItem=_.cloneDeep(obj.child[0])
+                        if(temItem.child.length>0){//如果拖动的盒子里面有子元素则不能进入
+                            obj={
+                                ...obj,
+                                child:[]
+                            }
+                            setTimeout(() => {
+                                this.conList.splice(index,0,temItem)
+                            }, 50);
+                        }else{
+                            obj={
+                                child:[
+                                    {
+                                        compId:obj.compId,
+                                        compType:obj.compType,
+                                        id:obj.id,
+                                        content:obj.content,
+                                        child:[]
+                                    },
+                                    {
+                                        ...temItem
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                    
+                }else{
+                    if(compData){//如果是从内容区域拖入的没有compData
+                        obj.child.splice(newDraggableIndex,1,{
+                            compId:compData.compId,
+                            compType:compData.compType,
+                            content:compData.content||'',
+                            id:this.getCompId(compData.compType),
+                            child:[]
+                        })
+                    }
+                    
+                }
+                console.log(obj);
+
+                this.conList.splice(index,1,obj)
+            // }
+        },
+
+        handleParentMove(e){
+            // console.log(e.draggedContext);
+            // console.log(e.relatedContext);
+            // // console.log(e.relatedContext.compType);
+            // console.log(e.draggedContext.element.child.length>0&&!e.relatedContext.compType);
+            // if(e.draggedContext.element.child.length>0&&(!e.relatedContext.compType||e.relatedContext.element.compType)) return false
+        },
+
+        // 移除事件 
+        handleChildRemove(e,arr){
+            console.log('child-remove操作------------------->');
+            // 如果都移除了则删除这个
+            // this.conList=this.conList.filter(_item=>!(_item.child&&_item.child.length===0&&!_item.id))
+
+            // 如果当前移出的这个child还有两个的话则重置他们的宽度
+            arr.forEach(item=>{
+                if(item.style){
+                    const styleArr=item.style.split(';').filter(s=>s&&(s.indexOf('width')===-1&&s.indexOf('flex')===-1)).join(';')
+                    item.style=styleArr
+                }
+            })
+
+
+            // 如果child只剩一个了则移出来
+            this.conList=this.conList.map(_item=>{
+                if(_item.child&&_item.child.length===1){
+                    const obj=_item.child[0]
+                    if(obj.style){
+                        const styleArr=obj.style.split(';').filter(s=>s&&(s.indexOf('width')===-1&&s.indexOf('flex')===-1)).join(';')
+                        obj.style=styleArr
+                    }
+                    return obj
+                }else{
+                    return _item
+                }
+            })
+        },
+
+        handleParentRemove(e){
+            console.log('container-remove操作------------------->');
+        },
+
+        // 点击删除某个
+        handleDelItem(pindex,cindex){
+            if(cindex===-1){
+                this.conList.splice(pindex,1)
+            }else{//删除子盒子
+                this.conList[pindex].child.splice(cindex,1)
+                if(this.conList[pindex].child.length===1){//只剩一个子盒子了则变成一个大盒子
+                    this.conList[pindex]=this.conList[pindex].child[0]
+                    this.conList[pindex].style='flex:1'
+                }
+            }
+        },
+
+        // 富文本编辑组件/图片组件数据变化
+        handleTextChange(e){
+            console.log(e);
+            this.activeContent=e
+            if(this.activeCindex>=0&&this.activeCindex!==''){
+                if(this.conList[this.activePindex].child[this.activeCindex].content){
+                    this.conList[this.activePindex].child[this.activeCindex].content=e
+                }else{
+                    this.$set(this.conList[this.activePindex].child[this.activeCindex],'content',e)
+                }
+            }else{
+                if(this.conList[this.activePindex].content){
+                    this.conList[this.activePindex].content=e
+                }else{
+                    this.$set(this.conList[this.activePindex],'content',e)
+                }
+            }
+        },
+
+        // 当前再编辑哪个
+        handleChoose(item,index,cindex){
+            //{item:数据,index:父序号,cindex:子序号}
+            if(!item.id||this.isDragResize||!['text','img'].includes(item.compType)) return
+            this.activeId=item.id
+            this.showRight=true
+            this.rightType=item.compType
+            this.activeContent=item.content||''
+            this.activePindex=index
+            this.activeCindex=cindex>=0?cindex:''
+        },
+
+        //点击顶部插入图或者图表等类型
+        handleShowRight(item){
+            this.rightType=item.type
+            this.activeId=''
+            this.activeContent=''
+            this.activePindex=''
+            this.activeCindex=''
+            this.showRight=true 
+        },
+
+        // 关闭右侧
+        handleCloseRight(){
+            this.activeId=''
+            this.activeContent=''
+            this.activePindex=''
+            this.activeCindex=''
+            this.showRight=false
+            this.rightType=''
+        },
+
+        getComponentName(item){
+            const temMap=new Map([
+                ['text',TextComp],
+                ['chart',ChartComp],
+                ['img',ImgComp],
+                ['sheet',SheetComp]
+            ])
+            return temMap.get(item.compType)
+        },
+        // 生产随机id
+        getCompId(type){
+            return type+new Date().getTime()
+        },
+
+        // 编辑保存报告
+        handleReportEdit(e){
+            const params={
+                SmartReportId:Number(this.$route.query.id)||0,
+                ...e,
+                Content:$('.report-html-wrap').html(),
+                ContentStruct:JSON.stringify(this.conList)
+            }
+            console.log(params);
+            apiSmartReport.reportEdit({...params}).then(res=>{
+                if(res.Ret===200){
+                    this.$message.success('保存成功')
+                    this.reportInfo.Title=params.Title
+                    this.showReportBaseInfo=false
+                }
+            })
+        },
+
+        /* 获取动态配置 外部动态链接 */
+        async getPublicSettings() {
+            const res =  await getPublicSettingsApi();
+            if(res.Ret !== 200) return
+            
+            this.$store.commit('SET_DYNAMIC_LINK',res.Data)
+        },
+
+        // 获取报告详情
+        getReportDetail(){
+            const id=this.$route.query.id||0
+            if(!id) return
+            apiSmartReport.reportDetail({
+                SmartReportId:Number(id)
+            }).then(res=>{
+                if(res.Ret===200){
+                    this.reportInfo=res.Data
+                    this.conList=res.Data.ContentStruct?JSON.parse(res.Data.ContentStruct):[]
+                    this.$nextTick(()=>{
+                        this.contentChange=false
+                    })
+                }
+            })
+        },
+
+        // 刷新所有图表
+        handleRefreshAllChart: _.debounce ( async function() {
+            let code_arr = [];
+            $('iframe').each((k,i) => {
+                try {
+                    let href = $(i).attr('src');
+                    code_arr.push(getUrlParams(href,'code'));
+                } catch (err) {
+                }
+            });
+
+            if(!code_arr.length) return this.$message.warning('请插入图表');
+
+            const loading = this.$loading({
+                lock: true,
+                text: '刷新中..',
+                spinner: 'el-icon-loading',
+                background: 'rgba(0, 0, 0, 0.02)'
+            });
+
+            const { Ret } = await dataBaseInterface.reportRefresh({
+                ChartInfoCode: code_arr
+            })
+        
+            loading.close();
+            
+            if(Ret === 200) {
+                $('iframe').each((k,i) => {
+                    $(i).attr('src',$(i).attr('src'))
+                });
+                this.$message.success('刷新成功');
+            }
+
+        },1000),
+        
+        // 自动/存草稿保存内容
+        handleSaveContent({isAutoSave}){
+            return new Promise((resolve,reject)=>{
+                const id=this.$route.query.id||0
+                if(!id) return
+                apiSmartReport.saveReportContent({
+                    SmartReportId:Number(id),
+                    Content:$('.report-html-wrap').html(),
+                    ContentStruct:JSON.stringify(this.conList),
+                    NoChange:this.contentChange?2:1,
+                }).then(res=>{
+                    if(res.Ret===200){
+                        resolve(true)
+                        if(!isAutoSave){
+                            this.$message.success('保存成功')
+                        }
+                    }
+                    if(res.Msg==='报告已发布, 不允许编辑'){
+                        this.$router.replace({ path: '/smartReportList' });
+                    }
+                })
+            })
+        },
+
+        // 点击定时发布/发布
+        async handlePublishOpt(type){
+            // 存一次草稿
+            const saveRes=await this.handleSaveContent({isAutoSave:true})
+            if(!saveRes) return
+            if(type==='dsfb'){
+                this.showDSFB=true
+                return
+            }
+            // 发布
+            if(this.reportInfo.MsgIsSend==1){//该报告已经推送过模板消息
+                this.reportPublish({sendMsg:false})
+            }else{
+                const isPost = this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_sendMsg)
+                this.$confirm(
+                    isPost?'发布后,是否推送模板消息?':'是否立即发布报告?', 
+                    '发布提示', 
+                    {
+                        confirmButtonText: isPost?'推送':'发布',
+                        cancelButtonText: isPost?'不推送':'取消',
+                        type: 'warning',
+                        distinguishCancelAndClose:true,
+                    
+                        beforeClose:(action, instance,done)=>{
+                            if(action==='close') {
+                                //右上角
+                                // this.isPublishloading = false;
+                            } else if(action==='cancel') {
+                                //cancelButton
+                                if(isPost){
+                                    this.reportPublish({sendMsg:false})
+                                }
+                            }else {
+                                //confirmButton
+                                this.reportPublish({sendMsg:isPost?true:false})
+                            }
+                            done()
+                        }
+                    }
+                )
+            }
+        },
+
+        // 定时发布报告
+        async handleSetReportPrepublish(){
+            if(!this.taskTime){
+				this.$message.warning('请选择定时发布的时间')
+				return
+			}
+			const now=this.$moment().format('YYYY-MM-DD HH:mm:ss')
+			// console.log(now);
+			// console.log(this.taskTime);
+			if(this.$moment(this.taskTime).isBefore(now,'second')){
+				this.$message.warning('定时发布不得早于当前时间')
+				return
+			}
+            // 如果该报告已经推送过模板消息
+            if(this.reportInfo.MsgIsSend==1){
+                apiSmartReport.prePublishReport({
+                    SmartReportId:Number(this.$route.query.id),
+                    PrePublishTime:this.taskTime,
+                    PreMsgSend:0
+                }).then(res=>{
+                    if(res.Ret===200){
+                        this.$message.success('定时发布成功!')
+                        this.$router.replace({ path: '/smartReportList' });
+                    }
+                })
+                return
+            }
+
+            const isPost = this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_sendMsg)
+
+            this.$confirm(isPost?'是否发布定时报告,并推送模板消息?':'是否发布定时报告?', '发布提示', {
+				confirmButtonText: isPost?'推送':'发布',
+				cancelButtonText: isPost?'不推送':'取消',
+				type: 'warning',
+				distinguishCancelAndClose:true,
+				beforeClose:(action, instance,done)=>{
+					console.log(action, instance);
+					if(action==='close'||action==='cancel') {
+						//右上角或者不推送
+						if(isPost){
+							apiSmartReport.prePublishReport({
+								SmartReportId:Number(this.$route.query.id),
+								PrePublishTime:this.taskTime,
+								PreMsgSend:0,
+							}).then(res=>{
+								if(res.Ret===200){
+									this.$message.success('定时发布成功!')
+									this.$router.replace({ path: '/smartReportList' });
+								}
+							})
+						}
+					} else {
+						//confirmButton
+						apiSmartReport.prePublishReport({
+							SmartReportId:Number(this.$route.query.id),
+							PrePublishTime:this.taskTime,
+							PreMsgSend:isPost?1:0,
+						}).then(res=>{
+							if(res.Ret===200){
+								this.$message.success('定时发布成功!')
+								this.$router.replace({ path: '/smartReportList' });
+							}
+						})
+					}
+					done()
+				}
+			})
+        },
+
+        // 发布报告
+        reportPublish({sendMsg}){
+            apiSmartReport.publishReport({
+                SmartReportId:Number(this.$route.query.id),
+                PublishState:2
+            }).then(res=>{
+                if(res.Ret===200){
+                    if(sendMsg){
+                        this.reportSendMsg()
+                    }
+                    this.$router.replace({ path: '/smartReportList' });
+                }
+            })
+        },
+
+        //报告消息推送
+        reportSendMsg(){
+            apiSmartReport.reportMsgSend({SmartReportId:Number(this.$route.query.id)}).then(res=>{})
+        }
+    },
+    created() {
+        this.getPublicSettings()
+        this.getReportDetail()
+    },
+    mounted () {
+        window.addEventListener('message',this.setSheetIframeStyle)
+        this.timer = setInterval(() => {
+			this.handleSaveContent({isAutoSave:true});
+		}, 6000);
+    },
+    destroyed() {
+		window.removeEventListener('message',this.setSheetIframeStyle)
+        if (this.timer) {
+			clearInterval(this.timer);
+		}
+	},
+}
+</script>
+<style lang='scss' scoped>
+div{
+    box-sizing: border-box;
+}
+// .sortable-ghost{
+//     height: 5px !important;
+//     background-color: #0052D9 !important;
+//     overflow: hidden !important;
+//     padding: 0 !important;
+//     min-height: 0 !important;
+//     border: none !important;
+// }
+.edit-smart-report-page{
+    background: var(--unnamed, #F2F6FA);
+    min-width: 100vw;
+    min-height: 100vh;
+    .top-action-wrap{
+        position: sticky;
+        top: 0px;
+        background-color: #fff;
+        box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.10);
+        height: 60px;
+        display: flex;
+        justify-content: space-between;
+        align-content: center;
+        padding: 0 24px;
+        .title{
+            line-height: 60px;
+            font-size: 16px;
+        }
+        .action-list{
+            display: flex;
+            align-items: center;
+            .action-item{
+                height: 36px;
+                display: flex;
+                align-items: center;
+                border-right: 1px solid #C8CDD9;
+                padding: 0 30px;
+                font-size: 16px;
+                cursor: pointer;
+                img{
+                    width: 16px;
+                    height: 16px;
+                    margin-right: 4px;
+                    position: relative;
+                    top: 2px;
+                }
+                &:last-child{
+                    border-right: none;
+                }
+            }
+        }
+    }
+    .main-wrap{
+        display: flex;
+        justify-content: center;
+    }
+}
+.report-action-wrap{
+    width: 800px;
+    flex-shrink: 0;
+    margin-left: 90px;
+    margin-right: 60px;
+    margin-top: 30px;
+    position: relative;
+    .report-comp-wrap{
+            position: absolute;
+            left: -62px;
+            top: 72px;
+            width: 40px;
+            border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+            background: var(--gary-gy-white, #FFF);
+            border-radius: 4px;
+            overflow: hidden;
+            .comp-item{
+                cursor: move;
+                height: 40px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                border-bottom: 1px solid var(--gary-gy-5-line, #C8CDD9);
+                &:last-child{
+                    border-bottom: none;
+                }
+                img{
+                    width: 16px;
+                }
+            }
+    }
+    .top-type-list{
+        margin-bottom: 20px;
+        display: flex;
+        align-items: center;
+        background-color: #fff;
+        height: 50px;
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        .item{
+            cursor: pointer;
+            flex: auto;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            border-right: 1px solid #C8CDD9;
+            font-size: 16px;
+            &:last-child{
+                border-right: none;
+            }
+            .icon{
+                width: 20px;
+                height: 20px;
+                margin-right: 4px;
+            }
+        }
+    }
+    .report-content-box{
+        border-radius: 4px;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        background: #FFF;
+        padding: 20px 20px 20px 64px;
+        height: calc(100vh - 180px);
+        position: relative;
+        overflow-y: auto;
+        
+    }
+}
+
+
+
+.main-wrap{
+    .report-html-wrap{
+        min-height: 100%;
+        .report-drag-item-wrap{
+            width: 100%;
+            padding: 20px;
+            min-height: 80px;
+            border: 1px dashed #0052D9;
+            position: relative;
+            margin-bottom: 10px;
+            &:hover{
+                border-style: solid;
+                .opt-btn-box{
+                    display: block !important;
+                }
+                .resize-drag-box{
+                    display: block;
+                }
+            }
+            .opt-btn-box{
+                position: absolute;
+                left: -36px;
+                padding-right: 8px;
+                top: 0;
+                .drag-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon12.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+                .del-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon13.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+            }
+            .resize-drag-box{
+                position: absolute;
+                right: -4px;
+                bottom: -4px;
+                width: 8px;
+                height: 8px;
+                display: none;
+                border: 1px solid #0052D9;
+                cursor: n-resize;
+            }
+        }
+        .report-drag-item-wrap_child-wrap{
+            min-height: 30px;
+        }
+        .report-drag-item-wrap_child_content{
+            min-height: 80px;
+            border: 1px dashed #0052D9;
+            flex: 1;
+            position: relative;
+            &:hover{
+                border-style: solid;
+                .opt-btn-box2{
+                    display: block !important;
+                }
+                .resize-drag-box_lt,
+                .resize-drag-box_lb,
+                .resize-drag-box_rt,
+                .resize-drag-box_rb{
+                    display: block;
+                }
+            }
+            .opt-btn-box2{
+                position: absolute;
+                right: -15px;
+                top: 0;
+                .drag-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon12.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+                .del-btn::after{
+                    content: '';
+                    display: block;
+                    width: 28px;
+                    height: 28px;
+                    background-image: url('~@/assets/img/smartReport/icon13.png');
+                    background-size: cover;
+                    cursor: pointer;
+                }
+            }
+            .resize-drag-box_lt,
+            .resize-drag-box_lb,
+            .resize-drag-box_rt,
+            .resize-drag-box_rb{
+                position: absolute;
+                width: 8px;
+                height: 8px;
+                display: none;
+                border: 1px solid #0052D9;
+            }
+            .resize-drag-box_lt{
+                left: -4px;
+                top: -4px;
+                cursor: nw-resize;
+            }
+            .resize-drag-box_lb{
+                left: -4px;
+                bottom: -4px;
+                cursor: ne-resize;
+            }
+            .resize-drag-box_rt{
+                right: -4px;
+                top: -4px;
+                cursor: ne-resize;
+            }
+            .resize-drag-box_rb{
+                right: -4px;
+                bottom: -4px;
+                cursor: nw-resize;
+            }
+        }
+        .blue-bg{
+            background: var(--brand-brand-1-light, #ECF2FE);
+        }
+    }
+}
+
+.main-wrap{
+    .right-action-wrap{
+        height: calc(100vh - 60px);
+        overflow: hidden;
+        flex: 1;
+        position: relative;
+        padding-top: 30px;
+        padding-left: 75px;
+        padding-right: 30px;
+        padding-bottom: 30px;
+        &::before{
+            display: block;
+            content: '';
+            width: 1px;
+            height: 100vh;
+            background-color: #C8CDD9;
+            position: absolute;
+            left: 15px;
+            top: 0;
+        }
+        .close-icon{
+            width: 20px;
+            height: 20px;
+            border-radius: 4px;
+            background: #FFF;
+            box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25);
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            cursor: pointer;
+            position: absolute;
+            left: 5px;
+            top: 22px;
+            img{
+                width: 16px;
+                height: 16px;
+            }
+        }
+    }
+}
+</style>

+ 239 - 0
src/views/smartReport/reportDetail.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="smart-report-detail">
+        <div class="main-box">
+            <div class="top-box">
+                <div class="title">{{reportInfo&&reportInfo.Title}}</div>
+                <div class="flex">
+                    <span>{{reportInfo&&reportInfo.Author}}</span>
+                    <span>{{reportInfo&&reportInfo.PublishTime}}</span>
+                </div>
+            </div>
+            <div class="html-wrap" v-html="content"></div>
+        </div>
+        <div class="right-opt-box" v-if="$route.query.type!=='preview'">
+            <div 
+                class="item copy" 
+                v-if="linkUrl" 
+                :data-clipboard-text='linkUrl' 
+                @click="copyHandle"
+                v-permission="permissionBtn.smartReportManageBtn.reportManage_reportView_copyWechat"
+            >
+                <img src="~@/assets/img/icons/cop.png" alt="" style="width:30px;height:30px;marginRight:10px;">
+                <span>复制链接</span>
+            </div>
+            <div 
+                class="item wechat-item" 
+                v-if="linkUrl"
+                v-permission="permissionBtn.smartReportManageBtn.reportManage_reportView_copyWechat"
+            >
+                <img src="~@/assets/img/icons/wechat.png" alt="" style="width:30px;height:30px;marginRight:10px;">
+                <span>微信分享</span>
+                <vue-qr 
+                    :text="linkUrl" 
+                    :margin="0" 
+                    colorDark="#333" 
+                    colorLight="#fff" 
+                    :dotScale="1" 
+                    :size="100" 
+                    class="qrcode"
+                ></vue-qr>
+            </div>
+            <div class="item" @click="handleGetReportImg" v-permission="permissionBtn.smartReportManageBtn.reportManage_reportView_exportImg">
+                <img src="~@/assets/img/smartReport/icon16.png" alt="" style="width:30px;height:30px;marginRight:10px;">
+                <span>导&nbsp;&nbsp;图</span>
+            </div>
+        </div>
+
+        <!-- 报告图片 -->
+        <div class="report-img-box" v-if="showReportImg&&reportImgUrl" @click="showReportImg=false">
+            <img :src="reportImgUrl" alt="" @click.stop="">
+        </div>
+    </div>
+</template>
+
+<script>
+import {apiSmartReport}  from '@/api/modules/smartReport'
+import vueQr from 'vue-qr'
+export default {
+    computed: {
+		linkUrl(){
+			let str=''
+			const baseUrl= localStorage.getItem('dynamicOutLinks') ? JSON.parse(localStorage.getItem('dynamicOutLinks')).ReportViewUrl : '';
+			if(this.$route.query.code){
+				str=`${baseUrl}/reportshare_smart_report?code=${this.$route.query.code}`
+			}
+			return str
+		}
+	},
+    data() {
+        return {
+            reportInfo:null,
+            content:'',
+            reportImgUrl:'',
+            showReportImg:false
+        }
+    },
+    created() {
+        this.getReportDetail()
+    },
+    methods: {
+        // 获取报告详情
+        getReportDetail(){
+            const id=this.$route.query.id||0
+            if(!id) return
+            apiSmartReport.reportDetail({
+                SmartReportId:Number(id)
+            }).then(res=>{
+                if(res.Ret===200){
+                    this.reportInfo=res.Data
+                    if(this.$route.query.type==='preview'){
+                       this.content=sessionStorage.getItem('smartReportContent')
+                    }else{
+                        this.content=res.Data.Content
+                    }
+                }
+            })
+        },
+
+        /* 复制链接 */
+		copyHandle() {
+				var clipboard = new this.Clipboard('.copy')
+				clipboard.on('success', e => {
+					this.$message.success('复制链接成功')
+					e.clearSelection() // 释放内存
+					clipboard.destroy()
+				})
+				// // 浏览器不支持
+				clipboard.on('error', e => {
+					this.$message.warning('浏览器暂不支持')
+					// 释放内存
+					clipboard.destroy()
+				})
+		},
+
+        handleGetReportImg(){
+            // if(this.reportImgUrl){
+            //     this.showReportImg=true
+            //     return
+            // }
+            apiSmartReport.getReportImg({
+                SmartReportId:Number(this.$route.query.id)
+            }).then(res=>{
+                if(res.Ret===200){
+                    // this.reportImgUrl=res.Data
+                    // this.showReportImg=true
+                    // 改为直接下载图片不是展示
+                    this.saveImg(res.Data)
+                }else{
+                    if(res.Ret!==403){
+                        this.$message.warning(res.Msg)
+                    }
+                }
+            })
+        },
+
+        saveImg(imgUrl){
+            let img=new Image()
+            img.setAttribute('crossOrigin', 'anonymous');
+            img.src=imgUrl
+            img.onload=()=>{
+                let canvas = document.createElement("canvas");
+                canvas.width = img.width;
+                canvas.height = img.height;
+                let context = canvas.getContext('2d');
+                context.drawImage(img, 0, 0, img.width, img.height);
+                let dataURL = canvas.toDataURL("image/png", 1);
+                const a=document.createElement('a')
+                a.setAttribute("download",this.reportInfo.Title)
+                a.style.display = "none"
+                a.href=dataURL
+                document.body.appendChild(a);
+                a.click()
+            }
+            img.onerror=(e)=>{
+                console.log(e);
+                this.$message.warning("自动下载图片失败,请手动保存")
+                if(imgUrl){
+                    this.reportImgUrl=imgUrl
+                    this.showReportImg=true
+                }
+            }
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+div{
+    box-sizing: border-box;
+}
+.smart-report-detail{
+    .right-opt-box{
+            position: fixed;
+            top: 100px;
+            left: calc(50% + 450px);
+            .item{
+                display: flex;
+                align-items: center;
+                cursor: pointer;
+                margin-bottom: 20px;
+                position: relative;
+                .qrcode{
+                    position: absolute;
+                    left: 110%;
+                    display: none !important;
+                    z-index: 9;
+                }
+            }
+            .wechat-item{
+                &:hover{
+                    .qrcode{
+                        display: inline-block !important;
+                    }
+                }
+            }
+    }
+    .main-box{
+        width: 800px;
+        margin: 0 auto;
+        border: 1px solid var(--gary-gy-5-line, #C8CDD9);
+        border-radius: 4px;
+        overflow: hidden;
+        position: relative;
+        .top-box{
+            width: 100%;
+            height: 160px;
+            background-image: url('~@/assets/img/smartReport/bg01.png');
+            background-size: cover;
+            background-repeat: no-repeat;
+            background-position: center;
+            padding: 40px;
+            color: #fff;
+            .title{
+                font-size: 20px;
+                margin-bottom: 20px;
+            }
+            .flex{
+                display: flex;
+                justify-content: space-between;
+            }
+        }
+        .html-wrap{
+            padding: 30px;
+        }
+    }
+    .report-img-box{
+        position: fixed;
+        left: 0;
+        top: 0;
+        bottom: 0;
+        right: 0;
+        overflow-y: auto;
+        background: rgba($color: #000000, $alpha: 0.6);
+        text-align: center;
+        img{
+            max-width: 800px;
+        }
+    }
+}
+</style>

+ 794 - 0
src/views/smartReport/reportList.vue

@@ -0,0 +1,794 @@
+<template>
+    <div class="smart-report-list">
+        <el-card class="box-card">
+            <div slot="header" class="header">
+                <el-form :inline="true" :model="searchform" @submit.native.prevent>
+                <el-form-item label="">
+                    <el-button
+                        v-permission="permissionBtn.smartReportManageBtn.reportManage_reportAdd"
+                        type="primary"
+                        size="medium"
+                        @click="showAddReport=true"
+                    >添加研报</el-button>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-select
+                    v-model="searchform.timeType"
+                    placeholder="选择时间类型"
+                    size="medium"
+                    style="width:110px"
+                    @change="search"
+                    >
+                    <el-option label="发布时间" value="publish_time"></el-option>
+                    <el-option label="更新时间" value="modify_time"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-date-picker
+                        @change="search"
+                        v-model="searchform.dateValue"
+                        type="daterange"
+                        unlink-panels
+                        value-format="yyyy-MM-dd"
+                        range-separator="至"
+                        start-placeholder="起始日期"
+                        end-placeholder="结束日期"
+                        size="medium"
+                        style="width: 280px"
+                    />
+                </el-form-item>
+                <el-form-item label="">
+                    <el-select
+                        @change="search"
+                        v-model="searchform.frequency"
+                        placeholder="更新频度筛选"
+                        size="medium"
+                        clearable
+                        style="width: 140px"
+                    >
+                        <el-option label="年度" value="年度"></el-option>
+                        <el-option label="半年度" value="半年度"></el-option>
+                        <el-option label="季度" value="季度"></el-option>
+                        <el-option label="月度" value="月度"></el-option>
+                        <el-option label="双周度" value="双周度"></el-option>
+                        <el-option label="周度" value="周度"></el-option>
+                        <el-option label="日度" value="日度"></el-option>
+                        <el-option label="不定时" value="不定时"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-cascader
+                        @change="search"
+                        :options="optionsArr"
+                        v-model="searchform.classifynameArr"
+                        clearable
+                        placeholder="类型筛选"
+                        size="medium"
+                    />
+                </el-form-item>
+                <el-form-item label="">
+                    <el-select
+                        v-model.number="searchform.publishState"
+                        placeholder="发布筛选"
+                        size="medium"
+                        clearable
+                        style="width: 140px"
+                        @change="search"
+                    >
+                        <el-option label="已发布" :value="2"></el-option>
+                        <el-option label="未发布" :value="1"></el-option>
+                    </el-select>
+                </el-form-item>
+                <!-- <el-form-item label="">
+                    <el-select
+                        v-permission="permissionBtn.smartReportManageBtn.reportManage_reportList_sendTime"
+                        @change="search"
+                        v-model.number="searchform.msgIsSend"
+                        placeholder="推送消息状态"
+                        size="medium"
+                        clearable
+                        style="width: 140px"
+                    >
+                        <el-option label="未推送消息" :value="1"></el-option>
+                        <el-option label="已推送消息" :value="2"></el-option>
+                    </el-select>
+                </el-form-item> -->
+                <el-form-item>
+                    <el-input
+                        @input="search"
+                        placeholder="标题 / 创建人 / 更新人"
+                        v-model="searchform.key_word"
+                        clearable
+                        size="medium"
+                    >
+                        <i class="el-icon-search" slot="prefix" @click="search"></i>
+                    </el-input>
+                </el-form-item>
+                </el-form>
+            </div>
+            <template>
+                <el-table
+                    ref="table"
+                    :key="tableKey"
+                    :data="tableData"
+                    v-loading="listLoading"
+                    @sort-change="sortChange"
+                    element-loading-text="数据加载中..."
+                    border
+                    style="width: 100%"
+                >
+                <el-table-column
+                    prop="Title"
+                    label="报告标题"
+                    align="center"
+                    min-width="140"
+                >
+                    <template slot-scope="scope">
+                    <div @click="lookreportdtl(scope.row)">
+                        <span style="cursor: pointer; color: #4099ef" >{{ scope.row.Title }}</span>
+                        <span
+                            style="cursor: pointer; color: #4099ef"
+                            v-if="scope.row.MsgSendTime"
+                        >({{ scope.row.MsgSendTime.substring(5, 7)}}{{ scope.row.MsgSendTime.substring(8, 10) }})</span>
+                        <span
+                        style="cursor: pointer; color: #4099ef"
+                        v-else-if="scope.row.PublishTime"
+                        >({{ scope.row.PublishTime.substring(5, 7)
+                        }}{{ scope.row.PublishTime.substring(8, 10) }})</span
+                        >
+                        <span
+                        style="cursor: pointer; color: #4099ef"
+                        v-else-if="scope.row.CreateTime"
+                        >({{ scope.row.CreateTime.substring(5, 7)
+                        }}{{ scope.row.CreateTime.substring(8, 10) }})</span
+                        >
+
+                    </div>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    prop="Abstract"
+                    label="摘要"
+                    align="center"
+                    min-width="140"
+                ></el-table-column>
+                <el-table-column label="报告类型" align="center" min-width="140">
+                    <template slot-scope="scope"
+                    >{{ scope.row.ClassifyNameFirst }}
+                    <span v-if="scope.row.ClassifyNameSecond"
+                        >/ {{ scope.row.ClassifyNameSecond }}</span
+                    ></template
+                    >
+                </el-table-column>
+                <el-table-column
+                    prop="AdminRealName"
+                    label="创建人"
+                    align="center"
+                    min-width="100"
+                ></el-table-column>
+                <el-table-column
+                    prop="LastModifyAdminName"
+                    label="更新人"
+                    align="center"
+                    min-width="100"
+                ></el-table-column>
+                <el-table-column label="发布状态" align="center">
+                    <template slot-scope="scope">
+                    <span v-if="scope.row.State == '2'" style="color: #46c371"
+                        >已发布</span
+                    >
+                    <span v-if="scope.row.State == '1'">未发布</span>
+                    </template>
+                </el-table-column>
+                <el-table-column
+                    prop="PublishTime"
+                    label="发布时间"
+                    min-width="124"
+                    align="center"
+                    :formatter="formatterColumn"
+                >
+                    <template slot-scope="scope">
+                    <span>{{scope.row.PrePublishTime?scope.row.PrePublishTime:scope.row.PublishTime|formatTime}}</span>
+                    <svg style="position: relative;top:2px" v-if="scope.row.PrePublishTime&&scope.row.State == '1'" width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M15.2002 8C15.2002 4.13401 12.0662 1 8.20019 1C4.3342 1 1.20019 4.13401 1.2002 8C1.2002 11.866 4.3342 15 8.2002 15C12.0662 15 15.2002 11.866 15.2002 8ZM5.2002 10L5.2002 9L8.17491 9L11.2002 4.5L11.9073 5.20711L8.58912 10L5.2002 10Z" fill="#0052D9"/>
+                    </svg>
+                    </template>
+                </el-table-column>
+                <!-- <el-table-column v-if="permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_reportList_sendTime)"
+                    prop="MsgSendTime"
+                    label="报告推送时间"
+                    min-width="124"
+                    align="center"
+                >
+                    <template slot-scope="scope">{{
+                    scope.row.MsgSendTime | formatTime
+                    }}</template>
+                </el-table-column> -->
+                <el-table-column
+                    prop="ContentModifyTime"
+                    label="更新时间"
+                    min-width="124"
+                    align="center"
+                    :formatter="formatterColumn"
+                ></el-table-column>
+                <el-table-column label="期数" align="center">
+                    <template slot-scope="scope">第{{ scope.row.Stage }}期</template>
+                </el-table-column>
+                <el-table-column label="频度" align="center">
+                    <template slot-scope="scope">{{ scope.row.Frequency }}</template>
+                </el-table-column>
+                <el-table-column
+                    :label="hasUV?'PV / UV':'PV'"
+                    align="center"
+                    :render-header="renderHeader"
+                    width="140"
+                >
+                    <template slot-scope="scope"
+                    >{{ hasUV? scope.row.Pv+'/'+scope.row.Uv:scope.row.Pv }}</template
+                    >
+                </el-table-column>
+                <el-table-column label="操作" align="center" min-width="130">
+                    <template slot-scope="scope">
+                    <div class="opt-btns">
+                        <template
+                        v-if="scope.row.State == 1"
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_publish"
+                            class="editsty"
+                            @click="handlePublishReportOpt(scope.row)"
+                            v-if="scope.row.CanEdit"
+                            >发布</span
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_reportEdit"
+                            class="editsty"
+                            @click="editreport(scope.row, 'edit')"
+                            v-if="scope.row.CanEdit"
+                            >编辑</span
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_reportEdit"
+                            class="editsty"
+                            @click="editreport(scope.row, 'editing')"
+                            v-else
+                            >{{ scope.row.Editor || "" }}编辑中...</span
+                        >
+                        <span
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_reportDel"
+                            class="deletesty"
+                            @click="handleDelReport(scope.row)"
+                            v-if="scope.row.CanEdit"
+                            >删除</span
+                        >
+                        </template>
+                        <template v-if="scope.row.State == 2">
+                        <span v-if="permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_cancelPublish)"
+                            @click="handleReportPublishCancel(scope.row)"
+                            style="color: red; cursor: pointer"
+                            >取消发布</span>
+                        <!-- <span
+                            v-if="scope.row.MsgIsSend == 0 && permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_sendMsg)"
+                            style="color: #4099ef; cursor: pointer"
+                            @click="handleSendMsg(scope.row)"
+                            >推送消息</span
+                        >
+                        <span v-else-if="scope.row.MsgIsSend != 0&&permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_sendMsg)"
+                            style="color: red">已推送消息</span> -->
+                        </template>
+                        <span
+                        style="color: #46c371; cursor: pointer;display:inline-block"
+                        v-if="permissionBtn.checkPermissionBtn(permissionBtn.smartReportManageBtn.reportManage_audioUpload)"
+                        @click="openupload(scope.row.SmartReportId)"
+                        >音频上传</span
+                        >
+                        <span 
+                            style="cursor: pointer; color: #4099ef;display:inline-block" 
+                            v-permission="permissionBtn.smartReportManageBtn.reportManage_audioDownload"
+                            v-if="scope.row.VideoUrl"
+                            @click="handleDownloadVoice(scope.row)"
+                        >
+                            音频下载
+                        </span>
+                        <!-- <a
+                        v-permission="permissionBtn.smartReportManageBtn.reportManage_audioDownload"
+                        :href="scope.row.VideoUrl"
+                        v-if="scope.row.VideoUrl"
+                        :download="scope.row.VideoName"
+                        style="cursor: pointer; color: #4099ef"
+                        >音频下载</a
+                        > -->
+                    </div>
+                    </template>
+                </el-table-column>
+                </el-table>
+            </template>
+            <el-col :span="24" class="toolbar">
+                <el-pagination
+                    v-if="ispage"
+                    layout="total,prev,pager,next,jumper"
+                    background
+                    @current-change="handleCurrentChange"
+                    :page-size="pageSize"
+                    @size-change="handleSizeChange"
+                    :total="total"
+                    style="float: right"
+                />
+            </el-col>
+        </el-card>
+        <!-- 上传音频弹框 -->
+        <el-dialog
+            title="上传音频"
+            :visible.sync="uploadDialog"
+            :modal-append-to-body="false"
+            :close-on-click-modal="false"
+            :center="true"
+            v-dialogDrag
+            custom-class="dialogclass"
+            width="510px"
+        >
+            <div slot="title" style="display: flex; align-items: center">
+                <img
+                :src="$icons.up"
+                style="color: #fff; width: 16px; height: 16px; margin-right: 5px"
+                />
+                <span style="font-size: 16px">上传音频</span>
+            </div>
+            <el-form
+                :model="uploadForm"
+                ref="uploadForm"
+                label-position="right"
+                label-width="0px"
+                @submit.native.prevent
+            >
+                <el-form-item label="">
+                <input
+                    type="file"
+                    name="file"
+                    @change="fileSelected()"
+                    id="file"
+                    class="true-file"
+                    style="display: none"
+                />
+                <el-input
+                    type="text"
+                    v-model="uploadForm.audioname"
+                    readonly
+                    placeholder="上传报告录音"
+                    size="medium"
+                    style="width: 350px"
+                ></el-input>
+                <el-button type="primary" size="medium" @click.native="clickinput"
+                    >选择文件</el-button
+                >
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer" style="text-align: center">
+                <el-button
+                type="primary"
+                plain
+                size="medium"
+                @click.native="uploadDialog = false"
+                >取 消</el-button
+                >
+                <el-button
+                type="primary"
+                size="medium"
+                :loading="uploadloading"
+                @click.native="uploadaudio"
+                >{{ uploadloading ? "上传中" : "上 传" }}</el-button
+                >
+            </div>
+        </el-dialog>
+
+        <!-- 新增报告基础信息 -->
+        <BaseInfo v-model="showAddReport" :id="0" />
+
+        <!-- 发布弹窗(改掉了不用这个弹窗了但是先留着吧) -->
+        <el-dialog
+            title="发布提示"
+            :visible.sync="showPublish"
+            :modal-append-to-body="false"
+            :close-on-click-modal="false"
+            :center="true"
+            v-dialogDrag
+            custom-class="dialogclass"
+            width="510px"
+            @close="handleClosePublish"
+        >
+            <div>
+                <div style="height: 100px; padding-top: 40px">
+                    <i
+                        class="el-icon-warning"
+                        style="font-size: 24px; color: #e6a23c; vertical-align: middle"
+                    ></i>
+                    {{isDSFB?'该报告已设置定时发布,是否立即发布报告?':'是否立即发布报告?'}}
+                </div>
+                <div style="margin-bottom: 20px; text-align: center">
+                    <el-button
+                        type="primary"
+                        plain
+                        style="width: 100px"
+                        @click="handleClosePublish"
+                        >取消</el-button
+                    >
+                    <el-button
+                        type="primary"
+                        style="width: 100px; margin: 0 20px"
+                        @click="handleConfirmPublishReport"
+                        >发布</el-button
+                    >
+                </div>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import { voiceupload } from "api/api.js";
+import {apiSmartReport}  from '@/api/modules/smartReport'
+import BaseInfo from './components/BaseInfo.vue'
+export default {
+    components:{BaseInfo},
+    computed:{
+        Role() {
+            let role = localStorage.getItem("Role") || "";
+            return role;
+        },
+        //是否有UV
+        hasUV(){
+            return this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_reportList_uv)
+        }
+    },
+    data() {
+        return {
+            hostapi: process.env.VUE_APP_API_ROOT + "/voice/download",
+            searchform: {
+                timeType:'publish_time',
+                dateValue: "",
+                frequency: "",
+                classifynameArr: "",
+                state: "",
+                key_word: "",
+                publish_sort: "desc",
+                msgIsSend: "",
+                publishState:'',
+            },
+            tableKey:0,
+            tableData:[],
+            PageIndex: 1,
+            total: 0,
+            pageSize: 15,
+            ispage: true,
+            listLoading: false,
+            optionsArr: [],//分类数据
+
+            uploadDialog: false,
+            uploadForm: {
+                id: "",
+                formdata: null,
+                audioname: "",
+            },
+            uploadloading: false,
+
+            showAddReport:false,
+
+            showPublish:false,
+            activeReportId:0,
+            isDSFB:false
+        }
+    },
+    methods:{
+        // 删除某个报告
+        handleDelReport(item){
+            //删除
+            this.$confirm("确认删除吗?", "提示", {
+                type: "warning",
+            }).then(() => {
+                apiSmartReport.delReport({ SmartReportId: item.SmartReportId }).then((res) => {
+                    if (res.Ret === 200) {
+                        this.$message.success(res.Msg);
+                        this.getReportList();
+                    }
+                });
+            }).catch(() => {});
+        },
+
+        //取消发布
+        handleReportPublishCancel(item){
+            apiSmartReport.publishReport({ SmartReportId: item.SmartReportId,PublishState:1 }).then((res) => {
+                if (res.Ret === 200) {
+                    this.$message.success(res.Msg);
+                    this.getReportList();
+                }
+            });
+        },
+
+        // 发布报告
+        handlePublishReportOpt(item){
+            this.activeReportId=item.SmartReportId
+            // this.isDSFB=item.PrePublishTime?true:false
+            // this.showPublish=true
+            this.$confirm(
+                item.PrePublishTime?'该报告已设置定时发布,是否立即发布报告?':'是否立即发布报告?', 
+                '发布提示', 
+                {
+                    confirmButtonText: '发布',
+                    cancelButtonText: '取消',
+                    type: 'warning',
+                    distinguishCancelAndClose:true,
+                    beforeClose:(action, instance,done)=>{
+                        if(action==='close') {
+                            //右上角
+                        } else if(action==='cancel') {
+                            //cancelButton
+                                
+                        }else {
+                            //confirmButton
+                            this.handleConfirmPublishReport()
+                        }
+                        done()
+                    }
+                }
+            )
+        },
+        handleConfirmPublishReport(){
+            apiSmartReport.publishReport({ SmartReportId: this.activeReportId,PublishState:2 }).then((res) => {
+                if (res.Ret === 200) {
+                    this.$message.success(res.Msg);
+                    this.getReportList();
+                    this.handleClosePublish()
+                }
+            });
+        },
+        handleClosePublish(){
+            this.activeReportId=0
+            this.isDSFB=false
+            this.showPublish=false
+        },
+
+        //编辑报告
+        async editreport(item, type){
+            //编辑前标记一下
+            const res = await apiSmartReport.markReport({
+                Status: 1,
+                SmartReportId: item.SmartReportId,
+            });
+            if (res.Ret === 200) {
+                if (res.Data.Status == 1) {
+                    this.$message.warning(res.Data.Msg || "该研报正在编辑,不可重复编辑");
+                    item.CanEdit = false;
+                    item.Editor = res.Data.Editor || "";
+                    return;
+                } else if (res.Data.Status == 0) {
+                    item.CanEdit = true;
+                    item.Editor = res.Data.Editor || "";
+                }
+            } else {
+                this.$message.error(res.ErrMsg || "未知错误,请稍后重试");
+                return;
+            }
+            let { href } = this.$router.resolve({
+                path: "/smartReportEdit",
+                query: { id: item.SmartReportId },
+            });
+            window.open(href, "_blank");
+        },
+
+        // 推送消息
+        // async handleSendMsg(item){
+        //     const res = await apiSmartReport.reportMsgSend({
+        //         SmartReportId: [item.Id],
+        //     });
+        //     if (res.Ret == 200) {
+        //         item.ThsMsgIsSend = 1;
+        //         this.$message.success("发送成功");
+        //     }
+        // },
+
+        // 跳转报告详情
+        lookreportdtl(item){
+            //如果没有预览权限,就不跳转
+            if(!this.permissionBtn.checkPermissionBtn(this.permissionBtn.smartReportManageBtn.reportManage_reportView)) return
+            
+            let { href } = this.$router.resolve({ 
+                path: '/smartReportDetail',
+                query:{
+                    id:item.SmartReportId,
+                    code:item.ReportCode
+                }
+            });
+            window.open(href, "_blank");
+        },
+
+        // 报告列表
+        async getReportList(){
+            let params = {
+                CurrentIndex: this.PageIndex,
+                PageSize: this.pageSize,
+                Frequency: this.searchform.frequency,
+                ClassifyIdFirst: this.searchform.classifynameArr[0]?this.searchform.classifynameArr[0]:'',
+                ClassifyIdSecond:this.searchform.classifynameArr[1]?this.searchform.classifynameArr[1]:'',
+                Keyword: this.searchform.key_word,
+                State:this.searchform.publishState,
+                TimeType:this.searchform.timeType,
+            };
+            if (this.searchform.dateValue) {
+                params.StartDate = this.searchform.dateValue[0];
+                params.EndDate = this.searchform.dateValue[1];
+            }
+            this.listLoading = true;
+            const res=await apiSmartReport.reportList(params)
+            if (res.Ret === 200) {
+                this.tableData = res.Data.List || [];
+                this.tableKey++
+                this.total = parseInt(res.Data.Paging.Totals);
+            }
+            this.listLoading = false;
+        },
+
+        //分页页码跳转
+        handleCurrentChange(current) {
+            this.PageIndex = current;
+            this.getReportList();
+        },
+
+        //搜索
+        search() {
+            this.ispage = false;
+            this.$nextTick(() => {
+                this.PageIndex = 1;
+                this.getReportList();
+                this.ispage = true;
+            });
+        },
+
+        //分页页码改变
+        handleSizeChange(val) {
+            this.pageSize = val;
+            this.getlist();
+        },
+
+        renderHeader(h, { column, $index }) {
+            //table表头自定义
+            return h("div", { attrs: { style: "padding:0;" } }, [
+                h("span", column.label),
+                h("el-tooltip", { props: { placement: "top" } }, [
+                h(
+                    "p",
+                    {
+                    slot: "content",
+                    attrs: { style: "display:block;padding:5px 0;width:420px;" },
+                    },
+                    "pv:报告被打开的次数,每次打开都计算一次(只统计有权限用户)"
+                ),
+                this.hasUV?h(
+                    "p",
+                    {
+                    slot: "content",
+                    attrs: { style: "display:block;padding:5px 0;width:420px;" },
+                    },
+                    "uv:访问报告的人数,每篇报告同一个人访问只计算一次(只统计有权限用户)"
+                ):h(''),
+                h(
+                    "el-button",
+                    {
+                    props: { icon: "el-icon-info" },
+                    attrs: { style: "border:none;background:none" },
+                    },
+                    ""
+                ),
+                ]),
+            ]);
+        },
+
+        //报告类型
+        getclassifylist() {
+            //获取分类列表
+            let params = { CurrentIndex: 0, PageSize: 1000, KeyWord: "",HideDayWeek:1,/*不显示晨报/周报*/ };
+            apiSmartReport.classifyList(params).then((res) => {
+                if (res.Ret == 200 && Array.isArray(res.Data.List)) {
+                    this.optionsArr = [];
+                    res.Data.List.forEach((item, index) => {
+                        let newitem = {
+                            label: item.ClassifyName,
+                            value: item.Id,
+                        };
+                        if (item.Child) {
+                            let childnode = [];
+                            item.Child.forEach((itemchild, i) => {
+                                childnode.push({
+                                    label: itemchild.ClassifyName,
+                                    value: itemchild.Id,
+                                });
+                            });
+                            newitem.children = childnode;
+                        }
+                        this.optionsArr.push(newitem);
+                    });
+                }
+            });
+        },
+
+        //唤醒上传弹窗
+        openupload(id) {
+            this.uploadForm = { id: id, formdata: null, audioname: "" };
+            this.uploadDialog = true;
+        },
+        //上传模拟点击
+        clickinput() {
+            $("#file").click();
+        },
+        //选择文件上传
+        fileSelected() {
+            const that = this;
+            if (document.getElementById("file").files[0]) {
+                let hostfile = document.getElementById("file").files[0];
+                let size = Math.floor(hostfile.size / 1024 / 1024);
+                if (size > 200) {
+                    that.$message.error("上传文件大小不能大于200M!");
+                    hostfile = {};
+                    return false;
+                }
+                if (
+                    hostfile.name.includes(".mp3") ||
+                    hostfile.name.includes(".wav") ||
+                    hostfile.name.includes(".wma")
+                ) {
+                let form = new FormData();
+                form.append("file", hostfile); //hostfile.name
+                form.append("SmartReportId", this.uploadForm.id);
+                    this.uploadForm.formdata = form;
+                    this.uploadForm.audioname = hostfile.name;
+                } else {
+                    that.$message.error("上传文件格式不正确!");
+                }
+            }
+        },
+        uploadaudio() {
+            if (this.uploadForm.formdata == null) {
+                this.$message.error("请选择文件");
+                return false;
+            }
+            this.uploadloading = true;
+            apiSmartReport.voiceupload(this.uploadForm.formdata).then((res) => {
+                if (res.Ret === 200) {
+                    this.$message.success("上传成功");
+                    this.getReportList();
+                }
+                this.uploadloading = false;
+                $("#file").val("");
+                this.uploadDialog = false;
+            });
+        },
+        // 下载音频
+        handleDownloadVoice(e){
+            const x = new window.XMLHttpRequest();
+            x.open('GET', e.VideoUrl, true);
+            x.responseType = 'blob';
+            x.onload = () => {
+                const url = window.URL.createObjectURL(x.response);
+                const a = document.createElement('a');
+                a.download = e.VideoName+'.mp3';
+                a.style.display = 'none'
+
+                let blob=new Blob([x.response])
+                a.href = URL.createObjectURL(blob);
+                a.target = '_blank'
+                document.body.append(a)
+                a.click();
+            };
+            x.send();
+        }
+    },
+
+    created() {
+        this.getclassifylist()
+        this.getReportList()
+    },
+}
+</script>
+
+<style>
+
+</style>