Browse Source

Merge branch 'master' into v1.7.1_EDB

jwyu 1 year ago
parent
commit
14ea0956b6
38 changed files with 1842 additions and 127 deletions
  1. 1 1
      package.json
  2. 9 0
      src/api/report.js
  3. 8 0
      src/api/reportEn.js
  4. 14 0
      src/api/semanticAnalysis.js
  5. 79 1
      src/api/user.js
  6. BIN
      src/assets/imgs/login/account-icon.png
  7. BIN
      src/assets/imgs/login/email-icon.png
  8. BIN
      src/assets/imgs/login/phone-icon.png
  9. BIN
      src/assets/imgs/report/icon_time.png
  10. 2 0
      src/hooks/edb/useCopyEdbData.js
  11. 2 1
      src/hooks/useFroalaEditor.js
  12. 169 41
      src/views/Login.vue
  13. 1 1
      src/views/dataEDB/Detail.vue
  14. 2 2
      src/views/dataEDB/Index.vue
  15. 19 8
      src/views/dataEDB/RelationEDB.vue
  16. 1 1
      src/views/dataEDB/components/EDBChartDetail.vue
  17. 8 9
      src/views/dataEDB/components/EDBClassify.vue
  18. 2 1
      src/views/dataEDB/components/EDBDataDetail.vue
  19. 5 4
      src/views/dataEDB/components/SetEDBNewData.vue
  20. 104 0
      src/views/login/EmailModel.vue
  21. 389 0
      src/views/login/ForgetPassModel.vue
  22. 146 0
      src/views/login/MobileModel.vue
  23. 45 0
      src/views/login/OrdinaryModel.vue
  24. 60 0
      src/views/login/hooks/useLogin.js
  25. 80 0
      src/views/login/style/common.scss
  26. 10 5
      src/views/myETA/Index.vue
  27. 61 2
      src/views/report/AddReport.vue
  28. 83 13
      src/views/report/EditReport.vue
  29. 103 9
      src/views/report/List.vue
  30. 1 1
      src/views/report/Search.vue
  31. 1 1
      src/views/report/components/ReportPublishPop.vue
  32. 77 0
      src/views/report/components/ReportPublishTimeSet.vue
  33. 9 2
      src/views/report/components/reportInsert/Index.vue
  34. 181 0
      src/views/report/components/reportInsert/SemanticsImg.vue
  35. 41 3
      src/views/reportEn/AddReport.vue
  36. 119 16
      src/views/reportEn/List.vue
  37. 1 1
      src/views/reportEn/Search.vue
  38. 9 4
      src/views/tabbar/Home.vue

+ 1 - 1
package.json

@@ -28,7 +28,7 @@
     "pinia": "^2.0.36",
     "pptxgenjs": "^3.12.0",
     "v3-color-picker-teleport": "^1.0.9",
-    "vant": "^4.1.2",
+    "vant": "^4.6.4",
     "vconsole": "^3.15.0",
     "vue": "^3.2.47",
     "vue-router": "^4.1.6",

+ 9 - 0
src/api/report.js

@@ -202,5 +202,14 @@ export default {
      */
     chapterDayReportTicketList(params){
         return get('/report/getDayReportTickerList',params)
+    },
+    /**
+     * 定时发布报告
+     * @param ReportId
+     * @param PrePublishTime
+     * @param PreMsgSend 定时发布成功后是否立即推送模版消息:0否,1是
+     */
+    reportPublishTimeSet(params){
+        return post('/report/pre_publish',params)
     }
 }

+ 8 - 0
src/api/reportEn.js

@@ -138,5 +138,13 @@ export default {
      */
     strategyReportEdit(params){
         return post('/english_report/edit_policy',params)
+    },
+    /**
+     * 定时发布报告
+     * @param ReportId
+     * @param PrePublishTime
+     */
+    reportPublishTimeSet(params){
+        return post('/english_report/pre_publish',params)
     }
 }

+ 14 - 0
src/api/semanticAnalysis.js

@@ -0,0 +1,14 @@
+// 语义分析模块
+import { get,post } from "./index";
+
+export default{
+    /**
+     * 获取文档对比图
+     * @param Keyword
+     * @param PageSize
+     * @param CurrentIndex
+     */
+    compareImgList(params){
+        return get('/semantic_analysis/compare/search',params)
+    }
+}

+ 79 - 1
src/api/user.js

@@ -1,7 +1,7 @@
 import {get,post} from './index'
 
 /**
- * 登录
+ * 登录 (ETA1.4后弃用)
  * @param Username
  * @param Password
  * @param IsRemember
@@ -16,3 +16,81 @@ export function apiLogin(params){
 export function apiMenuList(params){
     return get('/system/menu/list',params)
 }
+
+export const _apiLogin = {
+    /**
+     * 获取图形验证码,返回base64格式图片
+     * @returns 
+     */
+    apiGetPicCode:(params)=>{
+        return get('/user_login/get_captcha',params)
+    },
+    /**
+     * 获取短信/邮箱验证码
+     * @param {Number} VerifyType 验证方式: 1-手机号; 2-邮箱
+     * @param {String} CaptchaId 图形验证码ID
+     * @param {String} CaptchaCode 图形验证码的值
+     * @param {String} Mobile 手机号 验证方式为1时必填
+     * @param {String} Email 邮箱 验证方式为2时必填
+     * @param {String} TelAreaCode 手机区号
+     * @param {Number} Source 来源:1-登录;3-忘记密码
+     * @returns 
+     */
+    apiGetVerifyCode:(params)=>{
+        return post('/user_login/verify_code',params)
+    },
+    /**
+     * ETA1.4后,用户登录
+     * @param {Number} LoginType 登录方式: 1-账号; 2-手机号; 3-邮箱
+     * @param {String} Username 用户名
+     * @param {String} Password 密码
+     * @param {String} Mobile 手机号
+     * @param {String} Email 邮箱
+     * @param {String} VerifyCode 手机号/邮箱的验证码
+     * @returns 
+     */
+    apiUserLogin:(params)=>{
+        return post('/user_login/login',params)
+    },
+    /**
+     * 忘记密码账号校验,获取账号信息
+     * @param {String} CaptchaId 图形验证码ID
+     * @param {String} CaptchaCode 图形验证码
+     * @param {String} UserName 账号
+     * @returns 
+    */
+    apiAccountCheck:(params)=>{
+        return post('/user_login/forget/account_get',params)
+    },
+    /**
+     * 忘记密码-手机/邮箱的验证码校验
+     * @param {Number} FindType 密码找回方式:1-手机号;2-邮箱
+     * @param {String} VerifyCode 验证码
+     * @param {String} UserName 用户名
+     * @param {String} Mobile 找回方式为手机号时必填
+     * @param {String} Email 找回方式为邮箱时必填
+     * @returns 
+     */
+    apiCheckCodeVerify:(params)=>{
+        return post('/user_login/forget/code_verify',params)
+    },
+    /**
+     * 忘记密码-重置密码
+     * @param {String} UserName 
+     * @param {String} Password 
+     * @param {String} RePassword 
+     * @returns 
+     */
+    resetPass:(params)=>{
+        return post('/user_login/forget/reset_pass',params)
+    },
+    /**
+     * 获取手机号区号
+     * @returns 
+     */
+    getPhoneAreaCode:(params)=>{
+        return get('/user_login/area_code/list',params)
+    },
+}
+
+

BIN
src/assets/imgs/login/account-icon.png


BIN
src/assets/imgs/login/email-icon.png


BIN
src/assets/imgs/login/phone-icon.png


BIN
src/assets/imgs/report/icon_time.png


+ 2 - 0
src/hooks/edb/useCopyEdbData.js

@@ -20,6 +20,8 @@ export function useCopyEdbData(){
             copyText(str,undefined,(error,event)=>{
                 if(error){
                     showToast('复制失败')
+                    console.log('复制数据失败',error);
+                    throw new Error(error)
                 }else{
                     showToast('复制成功')
                 }

+ 2 - 1
src/hooks/useFroalaEditor.js

@@ -42,7 +42,8 @@ export function useInitFroalaEditor() {
 		videoUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url
 		fileUploadURL: import.meta.env.VITE_APP_API_URL + '/report/uploadImg', //上传url 更多上传介绍 请访问https://www.froala.com/wysiwyg-editor/docs/options
 		imageDefaultWidth: false,
-		quickInsertButtons: ['image', 'ul', 'ol'], //快速插入项
+		quickInsertEnabled: false,
+		// quickInsertButtons: ['image', 'ul', 'ol'], //快速插入项
 		toolbarVisibleWithoutSelection: true, //是否开启 不选中模式
 		toolbarSticky: false, //操作栏是否自动吸顶
 		saveInterval: 0,

+ 169 - 41
src/views/Login.vue

@@ -1,16 +1,26 @@
 <script setup>
 import {ref} from 'vue'
 import { showToast } from 'vant';
-import {apiLogin} from '@/api/user'
+import {apiLogin,_apiLogin} from '@/api/user'
 import md5 from 'js-md5'
 import {Base64} from 'js-base64'
 import { useRouter } from 'vue-router';
+import MobileModel from './login/MobileModel.vue';
+import OrdinaryModel from './login/OrdinaryModel.vue';
+import EmailModel from './login/EmailModel.vue';
+import ForgetPassModel from './login/ForgetPassModel.vue';
 
 const router=useRouter()
 
-
-let username=ref(localStorage.getItem('account')?Base64.decode(localStorage.getItem('account')):'')
-let password=ref(localStorage.getItem('checkPass')?Base64.decode(localStorage.getItem('checkPass')):'')
+//登陆模式 ordinaryModel账号密码 mobileModel手机号 emailModel邮箱
+let activeModel = ref('ordinaryModel')
+let autoAccount = ref('')
+function changeModel(model){
+    if(model==='forgetPassModel'){
+        autoAccount.value = loginForm.value.getValues().username
+    }
+    activeModel.value = model
+}
 
 const onSubmit = (values) => {
     const params={
@@ -22,7 +32,7 @@ const onSubmit = (values) => {
         if(res.Ret===200){
             localStorage.setItem('token',res.Data.Authorization)
             localStorage.setItem('account',Base64.encode(values.username))
-			localStorage.setItem('checkPass',Base64.encode(values.password))
+            localStorage.setItem('checkPass',Base64.encode(values.password))
             const userInfo={
                 AdminName:res.Data.AdminName,
                 Authority:res.Data.Authority,
@@ -39,40 +49,143 @@ const onSubmit = (values) => {
         }
     })
 };
+
+
+let loginForm = ref(null)
+let mobileModel = ref(null)
+let emailModel = ref(null)
+const onSubmit2 = ()=>{
+    //根据activeModel验证值
+    const values = loginForm.value.getValues()
+    if(activeModel.value==='ordinaryModel'){
+        loginForm.value.validate(['username','password']).then(()=>{
+            userLogin({
+                LoginType:1,
+                Username:values.username,
+                Password:md5(values.password),
+            },values)
+        }).catch((error)=>{
+            console.log('error',error)
+        })
+    }
+
+    if(activeModel.value==='mobileModel'){
+        loginForm.value.validate(['mobile','mobileCode']).then(()=>{
+            userLogin({
+                LoginType:2,
+                Mobile:values.mobile,
+                VerifyCode:values.mobileCode
+            })
+        }).catch((error)=>{
+            console.log('error',error)
+        })
+    }
+    
+    if(activeModel.value==='emailModel'){
+        loginForm.value.validate(['email','emailCode']).then(()=>{
+            userLogin({
+                LoginType:3,
+                Email:values.email,
+                VerifyCode:values.emailCode
+            })
+        }).catch((error)=>{
+            console.log('error',error)
+        })
+    }
+}
+function userLogin(params,values){
+    _apiLogin.apiUserLogin(params).then(res=>{
+        //账号异常,提醒用户用其他方式登陆
+        if(res.Ret===4011){
+            showToast('您的账号异常,请使用其他方式登陆')
+        }
+        //账号或密码错误,弹窗提示就行,因为PC端有特殊提示要求所以单独设置了状态码
+        if(res.Ret===4012){
+            showToast('账号或密码错误,请检查')
+            return
+        }
+        if(res.Ret!==200){
+            activeModel.value!=='ordinaryModel'&&[activeModel].value.getCodePic()
+        }
+
+        localStorage.setItem('token',res.Data.Authorization)
+        activeModel.value==='ordinaryModel'&&localStorage.setItem('account',Base64.encode(values.username))
+        activeModel.value==='ordinaryModel'&&localStorage.setItem('checkPass',Base64.encode(values.password))
+        const userInfo={
+            AdminName:res.Data.AdminName,
+            Authority:res.Data.Authority,
+            ProductName:res.Data.ProductName,
+            RealName:res.Data.RealName,
+            RoleName:res.Data.RoleName,
+            RoleTypeCode:res.Data.RoleTypeCode,
+            SysRoleTypeCode:res.Data.SysRoleTypeCode,
+            AdminId:res.Data.AdminId,
+            DepartmentName:res.Data.DepartmentName
+        }
+        localStorage.setItem('userInfo',Base64.encode(JSON.stringify(userInfo)))
+        router.replace('/')
+    })
+}
 </script>
 
 <template>
     <div class="login-page">
-        <van-form class="form-box" @submit="onSubmit">
+        <van-form class="form-box" ref="loginForm" v-if="activeModel!=='forgetPassModel'">
             <div class="logo-wrap">
                 <img class="logo" src="@/assets/imgs/logo_icon.png" alt="">
                 <div class="title">Bind on account,</div>
                 <div class="sub-title">sign in to continue</div>
             </div>
-            <van-field
-                class="form-input-box"
-                v-model="username"
-                name="username"
-                placeholder="请输入管理后台账号"
-                :rules="[{ required: true, message: '请填写账号' }]"
-            />
-            <van-field
-                class="form-input-box"
-                v-model="password"
-                type="password"
-                name="password"
-                placeholder="请输入密码"
-                :rules="[{ required: true, message: '请填写密码' }]"
-            />
+            <div class="ordinaryModel" v-show="activeModel==='ordinaryModel'">
+                <ordinary-model :activeModel="activeModel" @change-model="changeModel('forgetPassModel')"/>
+            </div>
+            <div class="mobileModel" v-show="activeModel==='mobileModel'">
+                <mobile-model ref="mobileModel"
+                    :activeModel="activeModel"/>
+            </div>
+            <div class="emailModel" v-show="activeModel==='emailModel'">
+                <email-model ref="emailModel"
+                    :activeModel="activeModel"/>
+            </div>
             <div class="btn-box">
                 <van-button 
                     round 
                     block 
                     type="primary" 
-                    native-type="submit"
+                    @click="onSubmit2"
                 >登录</van-button>
             </div>
+            <!-- 选择其他登陆方法 -->
+            <div class="model-icon-box">
+                <div class="model-item" 
+                    @click="changeModel('ordinaryModel')"
+                    v-show="activeModel!=='ordinaryModel'">
+                    <div class="model-item-icon">
+                        <img src="@/assets/imgs/login/account-icon.png"/>
+                    </div>
+                </div>
+                <div class="model-item" 
+                    @click="changeModel('mobileModel')"
+                    v-show="activeModel!=='mobileModel'">
+                    <div class="model-item-icon">
+                        <img src="@/assets/imgs/login/phone-icon.png"/>
+                    </div>
+                    
+                </div>
+                <div class="model-item" 
+                    @click="changeModel('emailModel')"
+                    v-show="activeModel!=='emailModel'">
+                    <div class="model-item-icon">
+                        <img src="@/assets/imgs/login/email-icon.png"/>
+                    </div>
+                </div>
+            </div>
         </van-form>
+        <div class="form-box" v-else>
+            <forget-pass-model ref="forgetPassModel"
+                :autoAccount="autoAccount"
+                @change-model="changeModel('ordinaryModel')"/>
+        </div>
         <img class="pad-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/login_img.png" alt="">
         <div class="mobile-bot-text">Long time&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;More profit</div>
     </div>
@@ -100,16 +213,24 @@ const onSubmit = (values) => {
     
 }
 
-.form-input-box{
-    padding-left: 0;
-    padding-right: 0;
-    border-bottom: 1px solid $border-color;
-    margin-bottom: 40px;
-    &::after{
-        display: none;
+.model-icon-box{
+    margin-top: 100px;
+    display: flex;
+    width: 100%;
+    justify-content: center;
+    gap:120px;
+    .model-item-icon{
+        width:80px;
+        height:80px;
+        border-radius: 50%;
+        box-shadow: 0px 2px 12px 0px #0000001A;
+        box-sizing: border-box;
+        padding:16px;
+        img{
+            width:100%;
+        }
     }
 }
-
 .mobile-bot-text{
     margin-top: 200px;
     text-align: center;
@@ -148,17 +269,6 @@ const onSubmit = (values) => {
             }
             
         }
-        .form-input-box{
-            padding-left: 0;
-            padding-right: 0;
-            border: 1px solid $border-color;
-            border-radius: 4px;
-            padding: 12px 20px;
-            margin-bottom: 40px;
-            &::after{
-                display: none;
-            }
-        }
 
         .pad-img{
             display: block;
@@ -173,5 +283,23 @@ const onSubmit = (values) => {
     .mobile-bot-text{
         display: none;
     }
+    .model-icon-box{
+        margin-top: 50px;
+        display: flex;
+        width: 100%;
+        justify-content: center;
+        gap:60px;
+        .model-item-icon{
+            width:40px;
+            height:40px;
+            border-radius: 50%;
+            box-shadow: 0px 2px 12px 0px #0000001A;
+            box-sizing: border-box;
+            padding:6px;
+            img{
+                width:100%;
+            }
+        }
+    }
 }
 </style>

+ 1 - 1
src/views/dataEDB/Detail.vue

@@ -21,7 +21,7 @@ const {copyData} =useCopyEdbData()
 const route=useRoute()
 const router=useRouter()
 
-const activeType=ref('chart')
+const activeType=ref(route.query.showType||'chart')
 
 const chartDetailIns=ref(null)//图表组件实例
 const dataDetailIns=ref(null)//数据详情组件实例

+ 2 - 2
src/views/dataEDB/Index.vue

@@ -257,7 +257,7 @@ async function goSearch(){
                             :src="item.ChartImage"
                         />
                         <div>
-                            <span class="time">{{item.StartDate}}</span>
+                            <span class="time">{{item.CreateTime.substring(0,10)}}</span>
                             <svg 
                                 class="opt-icon" 
                                 xmlns="http://www.w3.org/2000/svg" 
@@ -521,7 +521,7 @@ async function goSearch(){
             gap: 15px;
             justify-content: flex-start;
             .edb-item{
-                width: 313px;
+                width: 308px;
                 border-radius: 8px;
                 padding: 10px;
                 .name{

+ 19 - 8
src/views/dataEDB/RelationEDB.vue

@@ -50,6 +50,18 @@ function onLoad(){
 function handleCopyEDBData(e){
     copyData({EdbInfoId:e.EdbInfoId})
 }
+
+// 查看数据
+function goDetail(e){
+    router.push({
+        path:'/dataEDB/detail',
+        query:{
+            edbInfoId:e.EdbInfoId,
+            showType:'data'
+        }
+    })
+}
+
 </script>
 
 <template>
@@ -74,12 +86,12 @@ function handleCopyEDBData(e){
                             <li class="info-item">最新日期:{{item.LatestDate}}</li>
                             <li class="info-item">单位:{{item.Unit}}</li>
                             <li class="info-item">最新值:{{item.LatestValue}}</li>
-                            <li class="info-item" style="width:100%">最近更新:{{item.ModifyTime}}</li>
-                            <li class="info-item" style="width:100%">数据来源:{{item.SourceName}}</li>
+                            <li class="info-item">最近更新:{{item.ModifyTime}}</li>
+                            <li class="info-item">数据来源:{{item.SourceName}}</li>
                         </ul>
                         <div style="text-align:right">
                             <van-button color="#F2F3FF" size="small" style="color:#0052D9;margin-right:10px" @click="handleCopyEDBData(item)">复制数据</van-button>
-                            <van-button color="#0052D9" size="small">查看数据</van-button>
+                            <van-button color="#0052D9" size="small" @click="goDetail(item)">查看数据</van-button>
                         </div>
                     </li>
                 </ul>
@@ -107,13 +119,10 @@ function handleCopyEDBData(e){
             font-size: 32px;
         }
         .info-list{
-            display: flex;
-            flex-wrap: wrap;
-            gap: 10px 0;
             color: $font-grey;
             margin-bottom: 10px;
             .info-item{
-                width: 50%;
+                margin-bottom: 10px;
             }
         }
     }
@@ -134,8 +143,10 @@ function handleCopyEDBData(e){
                 font-size: 16px;
             }
             .info-list{
-                gap: 5px 0;
                 margin-bottom: 5px;
+                .info-item{
+                    margin-bottom: 5px;
+                }
             }
         }
     }

+ 1 - 1
src/views/dataEDB/components/EDBChartDetail.vue

@@ -511,7 +511,7 @@ const selectEndDate=ref('')
 function handleConfrimSelectDate(e){
     selectStartDate.value=e[0].selectedValues.join('-')
     selectEndDate.value=e[1].selectedValues.join('-')
-    selectYear.value=''
+    selectYear.value=5
     if(chartType.value==='季节性图'){
         getEDBSeasonChartData()
     }else{

+ 8 - 9
src/views/dataEDB/components/EDBClassify.vue

@@ -96,7 +96,7 @@ watch(
 )
 function handleAddLevel1(){
     classifyState.title='添加一级目录'
-    classifyState.placeholderText='请输入一级分类名称'
+    classifyState.placeholderText='请输入一级目录名称'
     classifyState.show=true
 }
 async function handleConfirmClassifyState(){
@@ -194,7 +194,7 @@ function handleShowClassifyOpt(level,level1,level2,level3){
 function handleSelectOpt(e){
     if(e.type==='add'){
         classifyState.title=e.name
-        classifyState.placeholderText='请输入分类名称'
+        classifyState.placeholderText='请输入目录名称'
         classifyState.type='add'
         classifyState.level1Text=classifyOptState.level1Data?.ClassifyName
         classifyState.level2Text=classifyOptState.level2Data?.ClassifyName
@@ -210,7 +210,7 @@ function handleSelectOpt(e){
     }
     if(e.type==='edit'){
         classifyState.title='重命名'
-        classifyState.placeholderText='请输入分类名称'
+        classifyState.placeholderText='请输入目录名称'
         classifyState.type='edit'
         classifyState.level=classifyOptState.level
         if(classifyOptState.level===1){
@@ -295,11 +295,11 @@ function handleSelectMoveClassifyChange({value,selectedOptions,tabIndex}){
 }
 function handleConfirmMove(){
     if(!classifyMoveState.selectValue){
-        showToast('请选择要移动至的分类')
+        showToast('请选择要移动至的目录')
         return
     }
     if(classifyMoveState.moveLevel===3&&classifyMoveState.tabIndex!=1){
-        showToast('请选择要移动至的二级分类')
+        showToast('请选择要移动至的二级目录')
         return
     }
     let NextClassifyId=0
@@ -361,7 +361,7 @@ defineExpose({classifyList,langTypeChange})
                         <template #title>
                             <div class="van-ellipsis" @click="handleSelectClassify(level1)">
                                 <van-icon :name="openClassify.includes(level1.ClassifyId)?'arrow-down':'arrow'" style="margin-right:4px;cursor: pointer;"/>
-                                <span>{{getClassifyItemName(level1)}}</span>
+                                <span :style="{color:selectClassifyId===level1.ClassifyId?'#0052D9':''}">{{getClassifyItemName(level1)}}</span>
                             </div>
                         </template>
                         <template #value>
@@ -378,7 +378,7 @@ defineExpose({classifyList,langTypeChange})
                                 <template #title>
                                     <div class="van-ellipsis" @click="handleSelectClassify(level1,level2)">
                                         <van-icon :name="openClassify.includes(level2.ClassifyId)?'arrow-down':'arrow'" style="margin-right:4px;cursor: pointer;"/>
-                                        <span>{{getClassifyItemName(level2)}}</span>
+                                        <span :style="{color:selectClassifyId===level2.ClassifyId?'#0052D9':''}">{{getClassifyItemName(level2)}}</span>
                                     </div>
                                 </template>
                                 <template #value>
@@ -391,12 +391,11 @@ defineExpose({classifyList,langTypeChange})
                                         :title="getClassifyItemName(level3)" 
                                         :name="level3.ClassifyId"
                                         :is-link="false"
-                                        disabled
                                         :border="false"
                                     >
                                         <template #title>
                                             <div class="van-ellipsis" @click="handleSelectClassify(level1,level2,level3)">
-                                                <span>{{getClassifyItemName(level3)}}</span>
+                                                <span :style="{color:selectClassifyId===level3.ClassifyId?'#0052D9':''}">{{getClassifyItemName(level3)}}</span>
                                             </div>
                                         </template>
                                         <template #value>

+ 2 - 1
src/views/dataEDB/components/EDBDataDetail.vue

@@ -30,6 +30,7 @@ const listState=reactive({
     finished:false,
     loading:false,
     LatestDate:'',
+    insertDate:'',//插入的日期
     classifyVal:''
 })
 async function getEDBDataList(){
@@ -113,6 +114,7 @@ function refreshList(){
         </van-collapse>
         <ul class="data-list-box">
             <div class="label">数据信息</div>
+            <img v-if="listState.list.length==0&&listState.finished" class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
             <van-list
                 v-model:loading="listState.loading"
                 :finished="listState.finished"
@@ -125,7 +127,6 @@ function refreshList(){
                     <span class="val">{{item.Value}}</span>
                 </li>
             </van-list>
-            
         </ul>
     </div>
 </template>

+ 5 - 4
src/views/dataEDB/components/SetEDBNewData.vue

@@ -162,15 +162,16 @@ async function handleSave(){
             display: flex;
             align-items: center;
             padding: 12px 32px;
-            max-width: 280px;
+            max-width: 350px;
             border-radius: 12px;
             svg{
                 width: 32px;
                 height: 32px;
+                flex-shrink: 0;
             }
             input{
                 background-color: transparent;
-                width: 180px;
+                max-width: 220px;
             }
         }
         .val-input{
@@ -200,14 +201,14 @@ async function handleSave(){
             margin-top: 20px;
             .select-date-box{
                 padding: 6px 16px;
-                max-width: 140px;
+                max-width: 180px;
                 border-radius: 6px;
                 svg{
                     width: 20px;
                     height: 20px;
                 }
                 input{
-                    width: 90px;
+                    width: 110px;
                 }
             }
             .val-input{

+ 104 - 0
src/views/login/EmailModel.vue

@@ -0,0 +1,104 @@
+<script setup>
+import {ref,computed,watch} from 'vue'
+import {_apiLogin} from '@/api/user'
+import {useLogin} from './hooks/useLogin'
+const props = defineProps({
+    activeModel:{//当前选择的登陆模式
+        type:String,
+        default:'ordinaryModel'
+    }
+})
+const { picSrc,picId,getPicCode,
+        timer,codeStr,codeCountDown,countDown,getCode
+      } = useLogin()
+watch(()=>props.activeModel,()=>{
+    if(props.activeModel==='emailModel'){
+        if(!timer){
+            getPicCode()
+            codeStr.value = '获取验证码'
+        }
+    }
+})
+
+//邮箱
+let email = ref('')
+//邮箱的图形验证码
+let emailPicCode = ref('')
+//邮件验证码
+let emailCode = ref('')
+const isEmailCountDown = computed(()=>{
+    return codeCountDown.value<60&&codeCountDown.value>0
+})
+
+const emailPattern = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/
+function checkForm(){
+    if(isEmailCountDown.value) return 
+    //是否是重新获取验证码
+    if(codeStr.value==='重新获取'){
+        //清空图形验证码输入框,引导用户重新输入
+        getPicCode()
+        emailPicCode.value = ''
+    }
+    //检查邮箱和图形验证码是否正确
+    if(!emailPicCode.value.length){
+        showToast('请输入图形验证码')
+        return
+    }
+    if(!email.value.length){
+        showToast('请输入邮箱')
+        return 
+    }
+    if(!emailPattern.test(email.value)){
+        showToast('请输入正确的邮箱')
+        return 
+    }
+
+    getCode({
+        email:email.value,
+        picCode:emailPicCode.value
+    },'email',1)
+}
+</script>
+
+<template>
+    <van-field
+        class="form-input-box form-mobile-box"
+        v-model="email"
+        name="email"
+        placeholder="请输入邮箱"
+        :rules="[
+            { required: true, message: '请输入邮箱' },
+            {validator:(val)=>emailPattern.test(val),message:'请输入正确的邮箱'}
+        ]"
+    />
+    <van-field
+        class="form-input-box form-mobile-box"
+        v-model="emailPicCode"
+        name="emailPicCode"
+        placeholder="请输入图形验证码"
+        :rules="[{ required: true, message: '请输入图形验证码' }]"
+    >
+        <template #button>
+            <div class="pic-filed" @click="getPicCode">
+                <img :src="picSrc" />
+            </div>
+        </template>
+    </van-field>
+    <van-field
+        class="form-input-box form-mobile-box"
+        v-model="emailCode"
+        name="emailCode"
+        placeholder="请输入邮件验证码"
+        :rules="[{ required: true, message: '请输入邮件验证码' }]"
+    >
+        <template #button>
+            <span class="btn-text" :class="{'disabled':isEmailCountDown}"
+                @click="checkForm"
+            >{{codeStr}}</span>
+        </template>
+    </van-field>
+</template>
+
+<style scoped lang="scss">
+@import "./style/common.scss";
+</style>

+ 389 - 0
src/views/login/ForgetPassModel.vue

@@ -0,0 +1,389 @@
+<script setup>
+import {ref,onMounted} from 'vue'
+import { showToast } from 'vant'
+import {_apiLogin} from '@/api/user'
+import {useLogin} from './hooks/useLogin'
+import {Base64} from 'js-base64'
+const props = defineProps({
+    autoAccount:{//自动填入账号,如果有
+        type:String,
+        default:''
+    }
+})
+
+const { picSrc,picId,getPicCode} = useLogin()
+
+const stepTitle = [
+    '忘记密码',
+    '安全验证',
+    '请输入验证码',
+    '设置密码'
+]
+let currentStep = ref(0)
+let account = ref('')
+let accountCode = ref('') //账号的图形验证码
+
+let userMobile = ref('')
+let areaCode = ref('')
+let userEmail = ref('')
+let choosedWay = ref('')
+
+let captInput = ref('')
+let showKeyboard = ref(false)
+function getUserInfo(values){
+    _apiLogin.apiAccountCheck({
+        CaptchaId:picId.value,
+        CaptchaCode:accountCode.value,
+        UserName:account.value
+    }).then(res=>{
+        if(res.Ret!==200){
+            //刷新图形验证码
+            getPicCode()
+            accountCode.value = ''
+            return 
+        }
+        if(res.Data){
+            const {Mobile,Email,TelAreaCode} = res.Data
+            userMobile.value = Mobile
+            userEmail.value = Email
+            areaCode.value = TelAreaCode
+            goNextStep()
+        }
+    })
+}
+
+//获取手机/邮箱验证码
+async function getVerifyCode(){
+    if(!choosedWay.value){
+        showToast('请选择验证方式')
+        return
+    }
+    const res = await _apiLogin.apiGetVerifyCode({
+            VerifyType:choosedWay.value==='mobile'?1:2,
+            CaptchaId:picId.value,
+            CaptchaCode:accountCode.value,
+            Mobile:choosedWay.value==='mobile'?userMobile.value:'',
+            Email:choosedWay.value==='email'?userEmail.value:'',
+            TelAreaCode:choosedWay.value==='mobile'?areaCode.value+'':'',
+            Source:3
+    })
+    if(res.Ret!==200) return 
+    showToast('验证码已发送')
+    goNextStep()
+}
+//检查输入的验证码
+function checkInput(){
+    if(captInput.value.length!==6){
+        showToast('请输入完整的验证码')
+        return
+    }
+    _apiLogin.apiCheckCodeVerify({
+        FindType:choosedWay.value==='mobile'?1:2,
+        VerifyCode:captInput.value,
+        UserName:account.value,
+        Mobile:choosedWay.value==='mobile'?userMobile.value:'',
+        Email:choosedWay.value==='email'?userEmail.value:''
+    }).then(res=>{
+        if(res.Ret!==200) return 
+        goNextStep()
+    })
+}
+
+//重置密码
+let checkPassStr = ref('确定')
+let resetPassTimer = 0
+let countDownNum = ref(5)
+let password = ref('')
+let checkPassword = ref('')
+
+//验证密码的正则 产品定的规则是:8位及以上,包含数字、大写字母、小写字母、特殊字符中的三个类型
+const patternPassWord = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/
+function checkPwd(pwd){
+    let num = 0
+    const patternArr = [
+        /^(?=.*[0-9])/,
+        /^(?=.*[a-z])/,
+        /^(?=.*[A-Z])/,
+        /^(?=.*[@#$%^&+=.])/,
+    ]
+    patternArr.forEach(pattern=>{
+        if(pattern.test(pwd)){
+            num++
+        }
+    })
+    if(pwd.length<8){
+        num = 0
+    }
+    return num>=3
+}
+
+function resetPass(values){
+    if(checkPassStr.value.includes('去登陆')){
+        emit('changeModel')
+        return
+    }
+    _apiLogin.resetPass({
+        UserName:account.value,
+        Password:Base64.encode(values.password),
+        RePassword:Base64.encode(values.checkPassword)
+    }).then(res=>{
+        if(res.Ret!==200) return 
+        showToast('重置密码成功,请登陆')
+        resetPassCountDown()
+    })
+}
+function resetPassCountDown(){
+    countDownNum.value--
+    checkPassStr.value = `去登陆(${countDownNum.value}s)`
+    if(countDownNum.value<=0){
+        clearInterval(resetPassTimer)
+        emit('changeModel')
+        return
+    }
+}
+function goNextStep(){
+    currentStep.value++
+}
+
+const emit = defineEmits(['changeModel'])
+function changeModel(){
+    if(currentStep.value > 0){
+        currentStep.value--
+        if(currentStep.value===0){
+            getPicCode()
+            accountCode.value = ''
+        }
+    }else{
+        emit('changeModel')
+    }
+}
+onMounted(()=>{
+    account.value = props.autoAccount
+    getPicCode()
+})
+</script>
+
+<template>
+    <div class="forget-pass-model-wrap">
+        <div class="header-nav" @click="changeModel">
+            <span><van-icon name="arrow-left" /></span>
+            <span>{{stepTitle[currentStep]}}</span>
+        </div>
+        <div class="step-container" v-show="currentStep===0">
+            <van-form @submit="getUserInfo">
+                <van-field
+                    class="form-input-box form-mobile-box"
+                    v-model="account"
+                    name="account"
+                    placeholder="请输入账号"
+                    :rules="[{ required: true, message: '请输入账号' },]"
+                />
+                <van-field
+                    class="form-input-box form-mobile-box"
+                    v-model="accountCode"
+                    name="accountCode"
+                    placeholder="请输入图形验证码"
+                    :rules="[{ required: true, message: '请输入图形验证码' }]"
+                >
+                    <template #button>
+                        <div class="pic-filed" @click="getPicCode">
+                            <img :src="picSrc" />
+                        </div>
+                    </template>
+                </van-field>
+                <div class="btn-box">
+                    <van-button 
+                        round 
+                        block 
+                        type="primary" 
+                        native-type="submit"
+                    >下一步</van-button>
+                </div>
+            </van-form>
+        </div>
+        <div class="step-container" v-show="currentStep>0">
+            <div class="container-inner" v-show="currentStep===1">
+                <div class="verification-item" 
+                    :class="{'active':choosedWay==='mobile','disabled':!userMobile.length}" @click="choosedWay = 'mobile'">
+                    <div class="icon">
+                        <img src="@/assets/imgs/login/phone-icon.png" />
+                    </div>
+                    <div class="text">{{userMobile||'暂未绑定'}}</div>
+                </div>
+                <div class="verification-item"
+                    :class="{'active':choosedWay==='email','disabled':!userEmail.length}" @click="choosedWay = 'email'">
+                    <div class="icon">
+                        <img src="@/assets/imgs/login/email-icon.png" />
+                    </div>
+                    <div class="text">{{userEmail||'暂未绑定'}}</div>
+                </div>
+                <van-button round block type="primary"
+                    @click="getVerifyCode"
+                    >获取验证码</van-button>
+            </div>
+            <div class="container-inner" v-show="currentStep===2">
+                <van-password-input
+                    :value="captInput"
+                    :length="6"
+                    :gutter="10"
+                    :mask="false"
+                    :focused="showKeyboard"
+                    @focus="showKeyboard = true"
+                    />
+                <van-number-keyboard
+                    v-model="captInput"
+                    :show="showKeyboard"
+                    @blur="showKeyboard = false"
+                    />
+                <van-button round block type="primary"
+                    @click="checkInput"
+                    >下一步</van-button>
+            </div>
+            <div class="container-inner" v-show="currentStep===3">
+                <van-form @submit="resetPass">
+                    <van-field
+                        class="form-input-box form-mobile-box"
+                        v-model="password"
+                        name="password"
+                        type="password"
+                        placeholder="请输入密码"
+                        :rules="[
+                            { required: true, message: '请输入密码' },
+                            { validator:(val)=>{
+                                if(!checkPwd(val)){
+                                    return `密码要求8位及以上,包含数字、大写字母、小写字母、特殊字符中的三个类型`
+                                }else{
+                                    return true
+                                }
+                             }
+                            }]"
+                    />
+                    <van-field
+                        class="form-input-box form-mobile-box"
+                        v-model="checkPassword"
+                        name="checkPassword"
+                        type="password"
+                        placeholder="请输入确认密码"
+                        :rules="[
+                            { required: true, message: '请输入确认密码' },
+                            { validator:(val)=>{
+                                if(val!==password){
+                                    return `两次密码输入不一致,请检查`
+                                }else{
+                                    return true
+                                }
+                             }
+                            }]"
+                    />
+                    <van-button round block type="primary"
+                        native-type="submit"
+                        >{{checkPassStr}}</van-button>
+                </van-form>
+            </div>
+        </div>
+    </div>
+</template>
+
+<style scoped lang="scss">
+@import "./style/common.scss";
+.forget-pass-model-wrap{
+    .header-nav{
+        margin-top:160px;
+        font-size: 48px;
+    }
+    .step-container{
+        margin-top: 70px;
+        .container-inner{
+            .verification-item{
+                display: flex;
+                align-items: center;
+                width:100%;
+                box-sizing: border-box;
+                padding:20px 30px;
+                margin-bottom: 60px;
+                border: 1px solid #DCDFE6;
+                border-radius: 16px;
+                background-color: #fff;
+                &.active{
+                    border: 1px solid #0052D9;
+                    background-color: #F2F3FF;
+                }
+                &.disabled{
+                    user-select: none;
+                    pointer-events: none;
+                }
+                .icon{
+                    width:80px;
+                    height: 80px;
+                    padding:16px;
+                    border-radius: 50%;
+                    box-shadow: 0px 2px 12px 0px #0000001A;
+                    margin-right:20px;
+                    img{
+                        width: 100%;
+                    }
+                }
+            }
+            .van-password-input{
+                margin-bottom: 60px;
+                :deep(.van-password-input__security){
+                    li{
+                        border: 1px solid #DCDFE6;
+                        border-radius: 8px;
+                    }
+                }
+            }
+        }
+    }
+    @media screen and (min-width:$media-width) {
+        .header-nav{
+            margin-top:0;
+            font-size: 36px;
+        }
+        .step-container{
+            margin-top: 70px;
+            .container-inner{
+                .verification-item{
+                    display: flex;
+                    align-items: center;
+                    width:100%;
+                    box-sizing: border-box;
+                    padding:10px 15px;
+                    margin-bottom: 30px;
+                    border: 1px solid #DCDFE6;
+                    border-radius: 8px;
+                    background-color: #fff;
+                    &.active{
+                        border: 1px solid #0052D9;
+                        background-color: #F2F3FF;
+                    }
+                    &.disabled{
+                        user-select: none;
+                        pointer-events: none;
+                    }
+                    .icon{
+                        width:40px;
+                        height: 40px;
+                        padding:8px;
+                        border-radius: 50%;
+                        box-shadow: 0px 2px 12px 0px #0000001A;
+                        margin-right:20px;
+                        img{
+                            width: 100%;
+                        }
+                    }
+                }
+                .van-password-input{
+                    margin-bottom: 60px;
+                    :deep(.van-password-input__security){
+                        li{
+                            border: 1px solid #DCDFE6;
+                            border-radius: 8px;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+</style>

+ 146 - 0
src/views/login/MobileModel.vue

@@ -0,0 +1,146 @@
+<script setup>
+import {ref,computed,watch} from 'vue'
+import { showToast } from 'vant';
+import { _apiLogin } from '@/api/user'
+import { useLogin } from './hooks/useLogin'
+import { isMobile } from 'vant/lib/utils';
+
+
+const props = defineProps({
+    activeModel:{//当前选择的登陆模式
+        type:String,
+        default:'ordinaryModel'
+    }
+})
+
+const { picSrc,picId,getPicCode,
+        timer,codeStr,codeCountDown,countDown,getCode
+    } = useLogin()
+
+watch(()=>props.activeModel,()=>{
+    if(props.activeModel==='mobileModel'){
+        if(!timer){
+            getPicCode()
+            codeStr.value = '获取验证码'
+        }
+    }
+})
+
+//手机号
+let mobile = ref('')
+//图形验证码
+let mobilePicCode = ref('')
+//短信验证码
+let mobileCode = ref('')
+
+//短信验证码倒计时相关
+const isMobileCountDown = computed(()=>{
+    return codeCountDown.value<60&&codeCountDown.value>0
+})
+
+//区号相关
+let mobileTelAreaCode=ref('86')
+let showMobilePicker = ref(false)
+let TelAreaCodeList = ref([])
+//获取区号
+function getTelAreaCode(){
+    _apiLogin.getPhoneAreaCode().then(res=>{
+        if(res.Ret!==200) return 
+        TelAreaCodeList.value = res.Data||[]
+    })
+}
+getTelAreaCode()
+
+//校验表单
+function checkForm(){
+    if(isMobileCountDown.value) return 
+    //是否是重新获取验证码
+    if(codeStr.value==='重新获取'){
+        //清空图形验证码输入框,引导用户重新输入
+        getPicCode()
+        mobilePicCode.value = ''
+    }
+    //检查手机号和图形验证码是否正确
+    if(!mobilePicCode.value.length){
+        showToast('请输入图形验证码')
+        return
+    }
+    if(!mobile.value.length){
+        showToast('请输入手机号')
+        return 
+    }
+    if(!isMobile(mobile.value)){
+        showToast('请输入正确的手机号')
+        return 
+    }
+    getCode({ mobile:mobile.value,
+              areaCode:mobileTelAreaCode.value,
+              picCode:mobilePicCode.value
+            },'mobile',1)
+
+}
+
+defineExpose({
+    getPicCode
+})
+</script>
+
+<template>
+    <van-field
+        class="form-input-box form-mobile-box tel"
+        v-model="mobile"
+        type="tel"
+        name="mobile"
+        placeholder="请输入手机号"
+        :rules="[
+            { required: true, message: '请输入手机号' },
+            { validator:(val)=>isMobile(val),message:'请输入正确的手机号'}
+        ]"
+    >
+        <!-- 选择区号的选择框-->
+        <template #button>
+            <div class="area-filed" @click="showMobilePicker = true" >
+                <span>+{{mobileTelAreaCode}}</span>
+                <van-icon name="arrow-down" />
+            </div>
+        </template>
+    </van-field>
+    <van-popup v-model:show="showMobilePicker" position="bottom">
+        <van-picker
+            :columns-field-names="{ text: 'Name',value: 'Value',}"
+            :columns="TelAreaCodeList"
+            @confirm="({selectedValues})=>{mobileTelAreaCode= selectedValues[0];showMobilePicker = false}"
+            @cancel="showMobilePicker = false"
+        />
+    </van-popup>
+    <van-field
+        class="form-input-box form-mobile-box"
+        v-model="mobilePicCode"
+        name="picCode"
+        placeholder="请输入图形验证码"
+        :rules="[{ required: true, message: '请输入图形验证码' }]"
+    >
+        <template #button>
+            <div class="pic-filed" @click="getPicCode">
+                <img :src="picSrc" />
+            </div>
+        </template>
+    </van-field>
+    <van-field
+        class="form-input-box form-mobile-box"
+        v-model="mobileCode"
+        name="mobileCode"
+        placeholder="请输入短信验证码"
+        :rules="[{ required: true, message: '请输入短信验证码' }]"
+    >
+        <template #button>
+            <span class="btn-text" :class="{'disabled':isMobileCountDown}"
+                @click="checkForm"
+            >{{codeStr}}</span>
+        </template>
+    </van-field>
+</template>
+
+<style scoped lang="scss">
+@import "./style/common.scss";
+</style>

+ 45 - 0
src/views/login/OrdinaryModel.vue

@@ -0,0 +1,45 @@
+<script setup>
+import {Base64} from 'js-base64'
+import {ref} from 'vue'
+const props = defineProps({
+    activeModel:{//当前选择的登陆模式
+        type:String,
+        default:'ordinaryModel'
+    }
+})
+
+//账号密码
+let username=ref(localStorage.getItem('account')?Base64.decode(localStorage.getItem('account')):'')
+let password=ref(localStorage.getItem('checkPass')?Base64.decode(localStorage.getItem('checkPass')):'')
+
+const emit = defineEmits(['changeModel'])
+function changeModel(){
+    emit('changeModel')
+}
+</script>
+
+<template>
+    <van-field
+        class="form-input-box"
+        v-model="username"
+        name="username"
+        placeholder="请输入管理后台账号"
+        :rules="[{ required: true, message: '请填写账号' }]"
+    />
+    <van-field
+        class="form-input-box"
+        v-model="password"
+        type="password"
+        name="password"
+        placeholder="请输入密码"
+        :rules="[{ required: true, message: '请填写密码' }]"
+    >
+        <template #button>
+            <span class="btn-text" style="color:#0052D9;" @click="changeModel">忘记密码</span>
+        </template>
+    </van-field>
+</template>
+
+<style scoped lang="scss">
+@import "./style/common.scss";
+</style>

+ 60 - 0
src/views/login/hooks/useLogin.js

@@ -0,0 +1,60 @@
+import {ref} from 'vue'
+import { showToast } from 'vant';
+import {_apiLogin} from '@/api/user'
+
+export function useLogin(){
+    //图形验证码src
+    let picSrc = ref('')
+    //图形验证码id
+    let picId = ref('')
+
+    //获取图形验证码
+    async function getPicCode(){
+        const res = await _apiLogin.apiGetPicCode()
+        if(res.Ret!==200) return
+        picId.value = res.Data.Id
+        picSrc.value = res.Data.Base64Blob
+    }
+
+    async function getCode({mobile,email,picCode,areaCode},model,source){
+        const res = await _apiLogin.apiGetVerifyCode({
+            VerifyType:model==='mobile'?1:2,
+            CaptchaId:picId.value,
+            CaptchaCode:picCode,
+            Mobile:mobile||'',
+            Email:email||'',
+            TelAreaCode:areaCode+''||'',
+            Source:source
+        })
+        if(res.Ret!==200) return 
+        showToast('验证码已发送')
+        //60秒倒计时
+        countDown()
+        timer = setInterval(()=>{
+            countDown()
+        },1000)
+    }
+    let timer = 0
+    let codeStr = ref('获取验证码')
+    let codeCountDown = ref(60)
+    function countDown(){
+        codeCountDown.value--
+        codeStr.value = `重新获取(${codeCountDown.value})秒`
+        if(codeCountDown.value<=0){
+            clearInterval(timer)
+            codeStr.value = '重新获取'
+        }
+    }
+
+
+
+    return {
+        picId,
+        picSrc,
+        getPicCode,
+
+        timer,codeStr,codeCountDown,
+        countDown,
+        getCode,
+    }
+}

+ 80 - 0
src/views/login/style/common.scss

@@ -0,0 +1,80 @@
+.form-input-box{
+    padding-left: 0;
+    padding-right: 0;
+    border-bottom: 1px solid $border-color;
+    margin-bottom: 40px;
+    &::after{
+        display: none;
+    }
+}
+.form-mobile-box{
+    &.tel{
+        :deep(.van-field__body){
+            flex-direction: row-reverse;
+        }
+        
+    }
+    .area-filed{
+        width:110px;
+        text-align: left;
+    }
+    .pic-filed{
+        width:180px;
+        height: 60px;
+        box-sizing: border-box;
+        padding: 0 15px;
+        background-color:#D9D9D9;
+        img{
+            width:100%;
+            height: 100%;
+        }
+    }
+    .btn-text{
+        color: #0052D9;
+        &.disabled{
+            color:#999999;
+        }
+    }
+}
+@media screen and (min-width:$media-width) {
+    .form-input-box{
+        padding-left: 0;
+        padding-right: 0;
+        border: 1px solid $border-color;
+        border-radius: 4px;
+        padding: 12px 20px;
+        margin-bottom: 40px;
+        &::after{
+            display: none;
+        }
+    }
+    .form-mobile-box{
+        &.tel{
+            :deep(.van-field__body){
+                flex-direction: row-reverse;
+            }
+            
+        }
+        .area-filed{
+            width:110px;
+            text-align: left;
+        }
+        .pic-filed{
+            width:80px;
+            height: 24px;
+            box-sizing: border-box;
+            padding: 0 15px;
+            background-color:#D9D9D9;
+            img{
+                width:100%;
+                height: 100%;
+            }
+        }
+        .btn-text{
+            color: #0052D9;
+            &.disabled{
+                color:#999999;
+            }
+        }
+    }
+}

+ 10 - 5
src/views/myETA/Index.vue

@@ -227,18 +227,23 @@ async function goSearch(){
                 @end="onClassifySortEnd"
                 :disabled="classifyState.classifyTypeAct!=1"
                 tag="ul"
+                handle=".icon-drag-box"
             >
                 <template #item="{element}">
                     <li 
-                        class="item"
+                        class="select-text-disabled item"
                         @click="handleSwitchClassify(element)"
                     >
                         <div :class="['van-ellipsis name',element.MyChartClassifyId===listState.cid&&'active-name']">{{element.MyChartClassifyName}}</div>
                         <div class="icon-drag-box" v-if="classifyState.classifyTypeAct==1">
-                            <img src="@/assets/imgs/icon_drag.png" alt="">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
+                                <path d="M40.4087 23.8334L33.7785 18.3083V22.7283H24.9384V13.8882H29.3585L23.8334 7.25806L18.3083 13.8882H22.7283V22.7283H13.8882V18.3083L7.25806 23.8334L13.8882 29.3585V24.9384H22.7283V33.7785H18.3083L23.8334 40.4087L29.3585 33.7785H24.9384V24.9384H33.7785V29.3585L40.4087 23.8334Z" fill="#666666"/>
+                            </svg>
                         </div>
                         <div class="icon-menu-box" @click.stop="handleShowOpt(element)">
-                            <img src="@/assets/imgs/myETA/icon_opt.png" alt="">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
+                                <path d="M24 33C25.65 33 27 34.35 27 36C27 37.65 25.65 39 24 39C22.35 39 21 37.65 21 36C21 34.35 22.35 33 24 33ZM21 24C21 25.65 22.35 27 24 27C25.65 27 27 25.65 27 24C27 22.35 25.65 21 24 21C22.35 21 21 22.35 21 24ZM21 12C21 13.65 22.35 15 24 15C25.65 15 27 13.65 27 12C27 10.35 25.65 9 24 9C22.35 9 21 10.35 21 12Z" fill="#666666"/>
+                            </svg>
                         </div>
                     </li>
                 </template>
@@ -419,7 +424,7 @@ async function goSearch(){
             .icon-drag-box{
                 width: 48px;
                 height: 48px;
-                img{
+                svg{
                     width: 100%;
                     height: 100%;
                 }
@@ -428,7 +433,7 @@ async function goSearch(){
                 margin-left: 20px;
                 width: 48px;
                 height: 48px;
-                img{
+                svg{
                     width: 100%;
                     height: 100%;
                 }

+ 61 - 2
src/views/report/AddReport.vue

@@ -3,10 +3,11 @@ import {ref,onMounted,onUnmounted} from 'vue'
 import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
 import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
 import ReportInsertContent from './components/reportInsert/Index.vue'
+import ReportPublishTimeSet from './components/ReportPublishTimeSet.vue'
 import apiReport from '@/api/report'
 import apiChart from '@/api/chart'
 import moment from 'moment'
-import { showToast } from 'vant'
+import { showToast,showDialog  } from 'vant'
 import { useRouter } from 'vue-router'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
 const cachedViewsStore=useCachedViewsStore()
@@ -18,6 +19,8 @@ const {lastFocusPosition,initFroalaEditor}=useInitFroalaEditor()
 
 let reportContentEditorIns=null//报告内容编辑器实例
 
+let reportId=0//报告id
+
 onMounted(() => {
     const el=document.getElementById('editor')
     reportContentEditorIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
@@ -205,6 +208,14 @@ async function handleReportOpt(type){
             showPublishPop.value=true
         }
     }
+    if(type==='dsfb'){
+        //定时发布
+        const res=await apiReport.reportAdd(params)
+        if(res.Ret===200){
+            reportId=res.Data.ReportId
+            showDSFBTime.value=true
+        }
+    }
 }
 
 // 点击发布提示弹窗中的操作按钮
@@ -244,6 +255,47 @@ async function reportPublish(id){
     }
 }
 
+// 定时发布报告选择时间
+const showDSFBTime=ref(false)
+function onConfirmDSFBTime(time){
+    console.log(time);
+    showDialog({
+        title: '提示',
+        message:'是否发布定时报告,并推送模板消息?',
+        confirmButtonText:'推送',
+        cancelButtonText:'不推送',
+        showCancelButton:true
+    }).then(()=>{
+        //推送
+        apiReport.reportPublishTimeSet({
+            ReportId:reportId,
+            PrePublishTime:time,
+            PreMsgSend:1
+        }).then(res=>{
+            if(res.Ret===200){
+                showToast('定时发布成功!')
+                setTimeout(() => {
+                    router.back()
+                }, 1000);
+            }
+        })
+    }).catch(()=>{
+        //不推送
+        apiReport.reportPublishTimeSet({
+            ReportId:reportId,
+            PrePublishTime:time,
+            PreMsgSend:0
+        }).then(res=>{
+            if(res.Ret===200){
+                showToast('定时发布成功!')
+                setTimeout(() => {
+                    router.back()
+                }, 1000);
+            }
+        })
+    })
+}
+
 </script>
 
 <template>
@@ -267,6 +319,10 @@ async function reportPublish(id){
                     <img src="@/assets/imgs/report/icon_save2.png" alt="">
                     <span>保存</span>
                 </div>
+                <div class="item" @click="handleReportOpt('dsfb')">
+                    <img src="@/assets/imgs/report/icon_time.png" alt="">
+                    <span>定时发布</span>
+                </div>
                 <div class="item" @click="handleReportOpt('fb')">
                     <img src="@/assets/imgs/report/icon_publish3.png" alt="">
                     <span>发布</span>
@@ -304,7 +360,7 @@ async function reportPublish(id){
     >
         <div class="publish-report-pop-box">
             <div class="title">发布提示</div>
-            <p class="tips">是否发布报告,且推送模板消息和客户群?</p>
+            <p class="tips">是否立即发布报告,并推送模板消息?</p>
             <div class="btns">
                 <div class="btn blue" @click="handleConfirmPublish(2)">发布&推送</div>
                 <div class="btn" @click="handleConfirmPublish(1)">仅发布</div>
@@ -312,6 +368,9 @@ async function reportPublish(id){
             </div>
         </div>
     </van-popup>
+
+    <!-- 定时发布选择时间 -->
+    <ReportPublishTimeSet v-model="showDSFBTime" @confirm="onConfirmDSFBTime" />
 </template>
 <style lang="scss" scoped>
 .publish-report-pop-box{

+ 83 - 13
src/views/report/EditReport.vue

@@ -3,10 +3,11 @@ import {ref,onMounted,onUnmounted} from 'vue'
 import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
 import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
 import ReportInsertContent from './components/reportInsert/Index.vue'
+import ReportPublishTimeSet from './components/ReportPublishTimeSet.vue'
 import apiReport from '@/api/report'
 import apiChart from '@/api/chart'
 import moment from 'moment'
-import { showToast } from 'vant'
+import { showToast,showDialog } from 'vant'
 import { useRoute, useRouter } from 'vue-router'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
 const cachedViewsStore=useCachedViewsStore()
@@ -263,17 +264,22 @@ async function handleReportOpt(type){
     }
     if(type==='fb'){
         // 发布
-        const hasTel=reportBaseInfoData.classifyName[1].HasTeleconference
-        //有电话会的不提示推送客群
-        if(hasTel==1){
-            const res=await apiReport.reportEdit(params)
-            if(res.Ret===200){
-                reportPublish(res.Data.ReportId)
-            }
-        }else{
-            // 显示发布提示弹窗,提示推送客群
-            showPublishPop.value=true
-        }
+        // const hasTel=reportBaseInfoData.classifyName[1].HasTeleconference
+        // //有电话会的不提示推送客群
+        // if(hasTel==1){
+        //     const res=await apiReport.reportEdit(params)
+        //     if(res.Ret===200){
+        //         reportPublish(res.Data.ReportId)
+        //     }
+        // }else{
+        //     // 显示发布提示弹窗,提示推送客群
+        //     showPublishPop.value=true
+        // }
+        // 没有客群了发布都有二次提示弹窗
+        showPublishPop.value=true
+    }
+    if(type==='dsfb'){
+        showDSFBTime.value=true
     }
 }
 
@@ -316,6 +322,63 @@ async function reportPublish(id){
     }
 }
 
+// 定时发布报告选择时间
+const showDSFBTime=ref(false)
+function onConfirmDSFBTime(time){
+    console.log(time);
+    if(reportData.value.MsgIsSend===1){//已经推送过了
+        apiReport.reportPublishTimeSet({
+            ReportId:reportData.value.Id,
+            PrePublishTime:time,
+            PreMsgSend:0
+        }).then(res=>{
+            if(res.Ret===200){
+                showToast('定时发布成功!')
+                setTimeout(() => {
+                    router.back()
+                }, 1000);
+            }
+        })
+        return
+    }
+    showDialog({
+        title: '提示',
+        message:'是否发布定时报告,并推送模板消息?',
+        confirmButtonText:'推送',
+        cancelButtonText:'不推送',
+        showCancelButton:true
+    }).then(()=>{
+        //推送
+        apiReport.reportPublishTimeSet({
+            ReportId:reportData.value.Id,
+            PrePublishTime:time,
+            PreMsgSend:1
+        }).then(res=>{
+            if(res.Ret===200){
+                showToast('定时发布成功!')
+                setTimeout(() => {
+                    router.back()
+                }, 1000);
+            }
+        })
+    }).catch(()=>{
+        //不推送
+        apiReport.reportPublishTimeSet({
+            ReportId:reportData.value.Id,
+            PrePublishTime:time,
+            PreMsgSend:0
+        }).then(res=>{
+            if(res.Ret===200){
+                showToast('定时发布成功!')
+                setTimeout(() => {
+                    router.back()
+                }, 1000);
+            }
+        })
+    })
+}
+
+
 </script>
 
 <template>
@@ -339,6 +402,10 @@ async function reportPublish(id){
                     <img src="@/assets/imgs/report/icon_save2.png" alt="">
                     <span>保存</span>
                 </div>
+                <div class="item" @click="handleReportOpt('dsfb')">
+                    <img src="@/assets/imgs/report/icon_time.png" alt="">
+                    <span>定时发布</span>
+                </div>
                 <div class="item" @click="handleReportOpt('fb')">
                     <img src="@/assets/imgs/report/icon_publish3.png" alt="">
                     <span>发布</span>
@@ -376,7 +443,7 @@ async function reportPublish(id){
     >
         <div class="publish-report-pop-box">
             <div class="title">发布提示</div>
-            <p class="tips">是否发布报告,且推送模板消息和客户群?</p>
+            <p class="tips">{{reportData.PrePublishTime?'该报告已设置定时发布,是否立即发布报告并推送模板消息?':'是否立即发布报告,并推送模板消息?'}}</p>
             <div class="btns">
                 <div :class="['btn blue',reportData.MsgIsSend===1?'disabled':'']" @click="handleConfirmPublish(2)">发布&推送</div>
                 <div class="btn" @click="handleConfirmPublish(1)">仅发布</div>
@@ -384,6 +451,9 @@ async function reportPublish(id){
             </div>
         </div>
     </van-popup>
+
+    <!-- 定时发布选择时间 -->
+    <ReportPublishTimeSet v-model="showDSFBTime" :prePublishTime="reportData?.PrePublishTime" @confirm="onConfirmDSFBTime" />
 </template>
 <style lang="scss" scoped>
 .publish-report-pop-box{

+ 103 - 9
src/views/report/List.vue

@@ -22,7 +22,8 @@ const showCleanFilterBox=computed(()=>{
         listState.ClassifyNameFirst||
         listState.EndDate||
         listState.Frequency||
-        listState.MsgIsSend
+        listState.MsgIsSend||
+        listState.publishStatus
     ) return true
 })
 const isClickClose=ref(false)//是否点击过关闭一键清空模块
@@ -33,6 +34,7 @@ function handleCleanFilter(){
     listState.Frequency=''
     listState.StartDate=''
     listState.EndDate=''
+    listState.publishStatus=''
     refreshList()
 }
 
@@ -41,6 +43,7 @@ const showClassify=ref(false)
 
 
 const listState = reactive({
+    publishStatus:'',
     MsgIsSend:'',
     Frequency:'',
     ClassifyNameFirst:'',
@@ -62,7 +65,9 @@ async function getList(){
         ClassifyNameFirst:listState.ClassifyNameFirst,
         ClassifyNameSecond:listState.ClassifyNameSecond,
         Frequency:listState.Frequency,
-        MsgIsSend:listState.MsgIsSend
+        MsgIsSend:listState.MsgIsSend,
+        TimeType:dateType.value===1?'publish_time':'modify_time',
+        State:listState.publishStatus
     })
     if(res.Ret===200){
         listState.loading=false
@@ -178,9 +183,11 @@ function handldReportMsgSend(item){
 const calendarMinDate=new Date(2010,0,1)
 const showCalendar=ref(false)
 const calendarIns=ref(null)
+const dateType=ref(1)//1发布时间 2更新时间
 function handleCalendarChange(e){
     listState.StartDate=moment(e[0]).format('YYYY-MM-DD')
     listState.EndDate=moment(e[1]).format('YYYY-MM-DD')
+    
     refreshList()
     showCalendar.value=false
 }
@@ -227,16 +234,28 @@ async function onLongPressItem(e){
 // 更多操作
 const frequencyDropMenuIns=ref(null)
 const statusDropMenuIns=ref(null)
+const publishStatusDropMenuIns=ref(null)
 const showMoreFilter=ref(false)
 const temFrequencyVal=ref('')
 const temMsgIsSendVal=ref('')
+const temPublishStatusVal=ref('')
 const reportStatusOpt=[
     {
-        label:'已推送',
+        label:'已推送消息',
+        value:2
+    },
+    {
+        label:'未推送消息',
+        value:1
+    }
+]
+const publishStatusOpt=[
+    {
+        label:'已发布',
         value:2
     },
     {
-        label:'未推送',
+        label:'未发布',
         value:1
     }
 ]
@@ -254,6 +273,13 @@ function handleSelectFrequency(item){
         temFrequencyVal.value=item.value
     }
 }
+function handleSelectReportPublishStatus(item){
+    if(temPublishStatusVal.value==item.value){
+        temPublishStatusVal.value=''
+    }else{
+        temPublishStatusVal.value=item.value
+    }
+}
 function handleShowFilter(){
     temFrequencyVal.value=listState.Frequency
     temMsgIsSendVal.value=listState.MsgIsSend
@@ -274,6 +300,11 @@ function handleConfirmStatus(){
     refreshList()
     showMoreFilter.value=false
 }
+function handleConfirmPublishStatus(){
+    listState.publishStatus=temPublishStatusVal.value
+    refreshList()
+    showMoreFilter.value=false
+}
 
 async function goSearch(){
     // 删除报告搜索页的缓存
@@ -360,7 +391,7 @@ async function handleReportEdit(e){
                     style="flex:1"
                     shape="round" 
                     readonly 
-                    placeholder="请输入报告标题或作者"
+                    placeholder="请输入报告标题或创建人"
                     @click-input="goSearch"
                 />
                 <div :class="['menu-icon',showClassify||listState.ClassifyNameSecond?'active':'']" @click="showClassify=true">
@@ -382,7 +413,7 @@ async function handleReportEdit(e){
                         <path d="M12.7528 34.0668C12.7528 34.3535 12.8093 34.6375 12.919 34.9024C13.0288 35.1674 13.1896 35.4081 13.3924 35.6109C13.5952 35.8136 13.8359 35.9745 14.1008 36.0842C14.3658 36.194 14.6497 36.2505 14.9365 36.2505C15.2233 36.2505 15.5072 36.194 15.7722 36.0842C16.0371 35.9745 16.2778 35.8136 16.4806 35.6109C16.6834 35.4081 16.8442 35.1674 16.954 34.9024C17.0637 34.6375 17.1202 34.3535 17.1202 34.0668C17.1202 33.78 17.0637 33.496 16.954 33.2311C16.8442 32.9662 16.6834 32.7254 16.4806 32.5226C16.2778 32.3199 16.0371 32.159 15.7722 32.0493C15.5072 31.9395 15.2233 31.8831 14.9365 31.8831C14.6497 31.8831 14.3658 31.9395 14.1008 32.0493C13.8359 32.159 13.5952 32.3199 13.3924 32.5226C13.1896 32.7254 13.0288 32.9662 12.919 33.2311C12.8093 33.496 12.7528 33.78 12.7528 34.0668Z" fill="currentColor" stroke="currentColor" stroke-width="0.5"/>
                     </svg>
                 </div>
-                <div :class="['menu-icon',showMoreFilter||listState.Frequency||listState.MsgIsSend?'active':'']" @click="handleShowFilter">
+                <div :class="['menu-icon',showMoreFilter||listState.Frequency||listState.MsgIsSend||listState.publishStatus?'active':'']" @click="handleShowFilter">
                     <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
                         <path d="M30.283 23.977L40.824 9.5685C41.3719 8.81954 41.6002 7.88381 41.459 6.96667C41.3177 6.04952 40.8184 5.22589 40.0705 4.6765C39.4715 4.23699 38.7479 4 38.005 4H9.995C8.065 4 6.5 5.567 6.5 7.5C6.5 8.244 6.7365 8.9685 7.1755 9.5685L17.717 23.977V42.5C17.717 43.3285 18.3875 44 19.215 44C19.6125 43.9996 19.9935 43.8414 20.2743 43.5601C20.5552 43.2788 20.7128 42.8975 20.7125 42.5V23.486C20.7125 23.1675 20.611 22.857 20.423 22.5995L9.592 7.7955C9.53761 7.72096 9.50489 7.63283 9.49745 7.54085C9.49001 7.44887 9.50814 7.35663 9.54984 7.27431C9.59155 7.192 9.65519 7.12281 9.73375 7.0744C9.81231 7.02599 9.90272 7.00024 9.995 7H38.005C38.0973 7.00015 38.1878 7.02586 38.2664 7.07427C38.345 7.12268 38.4086 7.1919 38.4503 7.27426C38.492 7.35663 38.51 7.44892 38.5025 7.54092C38.4949 7.63292 38.4621 7.72104 38.4075 7.7955L27.5765 22.5995C27.3884 22.8568 27.287 23.1673 27.287 23.486V38.271C27.287 39.0995 27.9575 39.771 28.785 39.771C28.9819 39.7709 29.1768 39.732 29.3586 39.6565C29.5404 39.5811 29.7056 39.4705 29.8447 39.3312C29.9838 39.192 30.0941 39.0266 30.1693 38.8447C30.2445 38.6628 30.2831 38.4678 30.283 38.271V23.977Z" fill="currentColor"/>
                     </svg>
@@ -407,7 +438,7 @@ async function handleReportEdit(e){
                         </div>
                     </div>
                 </van-dropdown-item>
-                <van-dropdown-item title="报告状态" ref="statusDropMenuIns">
+                <van-dropdown-item title="推送状态" ref="statusDropMenuIns">
                     <div class="report-status-box">
                         <ul>
                             <li 
@@ -424,6 +455,23 @@ async function handleReportEdit(e){
                         </div>
                     </div>
                 </van-dropdown-item>
+                <van-dropdown-item title="发布状态" ref="publishStatusDropMenuIns">
+                    <div class="report-status-box">
+                        <ul>
+                            <li 
+                                :class="['status-item',temPublishStatusVal===item.value?'active':'']" 
+                                v-for="item in publishStatusOpt" 
+                                :key="item.value"
+                                @click="handleSelectReportPublishStatus(item)"
+                            >{{item.label}}</li>
+                        </ul>
+                        
+                        <div class="bot-btn-box">
+                            <div class="btn cancel-btn" @click="showMoreFilter=false">取消</div>
+                            <div class="btn confirm-btn" @click="handleConfirmPublishStatus">确定</div>
+                        </div>
+                    </div>
+                </van-dropdown-item>
             </van-dropdown-menu>
             </template>
         </div>
@@ -445,12 +493,17 @@ async function handleReportEdit(e){
                     <h2 class="van-ellipsis title">
                         <span :class="['tag',item.ChapterType]">{{['周报','晨报'].includes(item.ClassifyNameFirst)?item.ClassifyNameFirst:'研报'}}</span>
                         {{item.Title}}
+                        <span v-if="item.CreateTime">({{item.CreateTime.substring(5,7)}}{{item.CreateTime.substring(8,10)}})</span>
                     </h2>
                     <p class="van-multi-ellipsis--l2 des">{{item.Abstract}}</p>
                     <div class="bot-info">
                         <div class="time">
-                            <span style="margin-right:10px">{{moment(item.ModifyTime).format('YYYY-MM-DD')}}</span>
-                            <span>{{item.AdminRealName}}</span>
+                            <span style="margin-right:2px">{{moment(item.ModifyTime).format('YYYY-MM-DD')}}</span>
+                            <svg v-if="item.PrePublishTime&&item.State==1" style="width:14px;height:14px;position: relative;top:2px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none">
+                                <path d="M13.0357 6.28571V14.7501L17.8576 19.7857L19.2213 18.422L14.9643 13.9512V6.28571H13.0357Z" fill="#0052D9"/>
+                                <path d="M27.5 14C27.5 21.4558 21.4558 27.5 14 27.5C6.54416 27.5 0.5 21.4558 0.5 14C0.5 6.54416 6.54416 0.5 14 0.5C21.4558 0.5 27.5 6.54416 27.5 14ZM25.5714 14C25.5714 7.60928 20.3907 2.42857 14 2.42857C7.60928 2.42857 2.42857 7.60928 2.42857 14C2.42857 20.3907 7.60928 25.5714 14 25.5714C20.3907 25.5714 25.5714 20.3907 25.5714 14Z" fill="#0052D9"/>
+                            </svg>
+                            <span style="margin-left:10px">{{item.AdminRealName}}</span>
                         </div>
                         <div class="read-count">
                             <span>PV:{{item.Pv}}</span>
@@ -546,12 +599,17 @@ async function handleReportEdit(e){
             :min-date="calendarMinDate"
             @confirm="handleCalendarChange" 
             :style="{ height: '500px' }"
+            class="calendar-box"
         >
             <template #title>
                 <div style="position: relative;">
                     <span style="color:#666;position: absolute;left:16px" @click="handleResetCalendar">重置</span>
                     <span>日期选择</span>
                 </div>
+                <div class="time-type-box">
+                    <span @click="dateType=1" :class="['item',dateType===1?'active':'']">发布时间</span>
+                    <span @click="dateType=2" :class="['item',dateType===2?'active':'']">更新时间</span>
+                </div>
             </template>
         </van-calendar>
     </van-popup>
@@ -777,6 +835,42 @@ async function handleReportEdit(e){
     }
 }
 
+.calendar-box{
+    :deep(.van-calendar__header-title){
+        height: auto;
+        min-height: var(--van-calendar-header-title-height);
+    }
+    :deep(.van-calendar__header){
+        box-shadow: none;
+    }
+    .time-type-box{
+        font-weight: normal;
+        text-align: left;
+        padding-left: 32px;
+        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.08);
+        .item{
+            display: inline-block;
+            margin-right: 40px;
+            color: $font-grey;
+            position: relative;
+            &.active{
+                color: #333;
+                &::after{
+                    content: '';
+                    width: 30PX;
+                    height: 4PX;
+                    background-color: $theme-color;
+                    position: absolute;
+                    bottom: 0;
+                    left: 50%;
+                    border-radius: 2px;
+                    transform: translateX(-50%);
+                }
+            }
+        }
+    }
+}
+
 @media screen and (min-width:$media-width){
     .add-report-btn{
         svg{

+ 1 - 1
src/views/report/Search.vue

@@ -67,7 +67,7 @@ function goDetail(item){
         <div class="search-box">
             <van-search 
                 shape="round"
-                placeholder="请输入报告标题或作者"
+                placeholder="请输入报告标题或创建人"
                 v-model="keyword"
                 @search="handleSearch"
             />

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

@@ -59,7 +59,7 @@ function handleReportMessageSend(publish){
 <template>
     <div class="publish-report-pop-box">
         <div class="title">发布提示</div>
-        <p class="tips">是否发布报告,且推送模板消息和客户群?</p>
+        <p class="tips">{{reportData.PrePublishTime?'该报告已设置定时发布,是否立即发布报告并推送模板消息?':'是否立即发布报告,并推送模板消息?'}}</p>
         <div class="btns">
             <div :class="['btn blue',reportData.MsgIsSend===1?'disabled':'']" @click="handleConfirmPublish(2)">发布&推送</div>
             <div class="btn" @click="handleConfirmPublish(1)">仅发布</div>

+ 77 - 0
src/views/report/components/ReportPublishTimeSet.vue

@@ -0,0 +1,77 @@
+<script setup>
+import moment from 'moment'
+import {ref, watch} from 'vue'
+import { showToast } from 'vant'
+const props=defineProps({
+    modelValue:{
+        type:Boolean,
+        default:false
+    },
+    prePublishTime:''
+})
+const emits=defineEmits(['update:modelValue','confirm'])
+
+const minDSFBDate=new Date()
+const minDSFBTime=ref(moment().add(2,'m').format('HH:mm:ss'))
+const dsfbDate=ref([])//定时发布选中的日期
+const dsfbTime=ref([])//定时发布选中的时间
+watch(
+    ()=>props.modelValue,
+    (n)=>{
+        if(n&&props.prePublishTime){
+            dsfbDate.value=props.prePublishTime.split(' ')[0].split('-')
+            dsfbTime.value=props.prePublishTime.split(' ')[1].split(':').slice(0,2)
+        }
+    }
+)
+
+watch(
+    ()=>dsfbDate.value,
+    ()=>{
+        if(dsfbDate.value.join('-')==moment().format('YYYY-MM-DD')){//选中的今天
+            minDSFBTime.value=moment().add(2,'m').format('HH:mm:ss')
+        }else{
+            minDSFBTime.value='00:00:00'
+        }
+    }
+)
+function handleClose(){
+    emits('update:modelValue',false)
+}
+function onConfirmDSFBTime(){
+    const time=`${dsfbDate.value.join('-')} ${dsfbTime.value.join(':')}:00`
+    const now=moment().format('YYYY-MM-DD HH:mm:ss')
+    if(moment(time).isBefore(now,'second')){
+        showToast('定时发布不得早于当前时间')
+        return
+    }
+    emits('confirm',time)
+}
+
+</script>
+
+<template>
+    <van-popup
+        v-model:show="props.modelValue"
+        position="bottom"
+        round
+    >
+        <van-picker-group
+            title="设置发送时间"
+            :tabs="['选择日期', '选择时间']"
+            next-step-text="下一步"
+            @confirm="onConfirmDSFBTime"
+            @cancel="handleClose"
+        >
+            <van-date-picker
+                v-model="dsfbDate"
+                :min-date="minDSFBDate"
+            />
+            <van-time-picker
+                v-model="dsfbTime"
+                :columns-type="['hour', 'minute']"
+                :min-time="minDSFBTime"
+            />
+        </van-picker-group>
+    </van-popup>
+</template>

+ 9 - 2
src/views/report/components/reportInsert/Index.vue

@@ -4,10 +4,11 @@ import ETAChart from './ETAChart.vue'
 import MyETAChart from './MyETAChart.vue'
 import SandTableImg from './SandTableImg.vue'
 import SheetTableChart from './SheetTableChart.vue'
+import SemanticsImg from './SemanticsImg.vue'
 
 const emits=defineEmits(['insert'])
 
-const typeOpt=['图表插入','批量插入','表格插入','沙盘插入']
+const typeOpt=['图表插入','批量插入','表格插入','沙盘插入','语义分析插入']
 const activeType=ref('图表插入')
 
 let list=ref([])
@@ -21,7 +22,7 @@ function handleConfirmInsert(){
         emits('insert',{list:list.value,type:'iframe',chartType:'chart'})
     }else if(activeType.value==='表格插入'){
         emits('insert',{list:list.value,type:'iframe',chartType:'sheet'})
-    }else if(activeType.value==='沙盘插入'){
+    }else if(['沙盘插入','语义分析插入'].includes(activeType.value)){
         emits('insert',{list:list.value,type:'img',chartType:''})
     }
     
@@ -44,6 +45,7 @@ function handleConfirmInsert(){
             <MyETAChart @update="handleSelectChart" v-if="activeType==='批量插入'"/>
             <SheetTableChart @update="handleSelectChart" v-if="activeType==='表格插入'"/>
             <SandTableImg @update="handleSelectChart" v-if="activeType==='沙盘插入'"/>
+            <SemanticsImg @update="handleSelectChart" v-if="activeType==='语义分析插入'"/>
         </div>
         <div class="bot-btn">
             <van-button type="primary" block @click="handleConfirmInsert" :disabled="list.length===0">插入</van-button>
@@ -61,9 +63,14 @@ function handleConfirmInsert(){
         background-color: $page-bg-grey;
         display: flex;
         border-bottom: 1px solid $border-color;
+        overflow-x: auto;
+        &::-webkit-scrollbar{
+            display: none;
+        }
         .item{
             padding: 24px 0;
             flex: 1;
+            min-width: 96PX;
             text-align: center;
             border-top-right-radius: var(--van-popup-round-radius);
             border-top-left-radius: var(--van-popup-round-radius);

+ 181 - 0
src/views/report/components/reportInsert/SemanticsImg.vue

@@ -0,0 +1,181 @@
+<script setup>
+import {reactive, ref,watch} from 'vue'
+import apiSemanticAnalysis from '@/api/semanticAnalysis'
+import { vInfiniteScroll } from '@vueuse/components'
+
+const emits=defineEmits(['update'])
+
+const searchVal=ref('')
+const listState=reactive({
+    page:1,
+    pageSize:20,
+    list:[],
+    finished:false,
+    loading:false
+})
+async function getSandBoxList(){
+    const params={
+        Keyword:searchVal.value,
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize
+    }
+    listState.loading=true
+    let res=await apiSemanticAnalysis.compareImgList(params)
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.List||[]
+        listState.list=[...listState.list,...arr]
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getSandBoxList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading) return
+    listState.page++
+    getSandBoxList()
+}
+function handleRefreshList(){
+    listState.list=[]
+    listState.page=1
+    listState.finished=false
+    getSandBoxList()
+}
+
+let selectChartList=ref([])
+function handleSelect(item){
+    const index=selectChartList.value.indexOf(item.SaCompareId)
+    if(index!==-1){
+        selectChartList.value.splice(index,1)
+    }else{
+        selectChartList.value.push(item.SaCompareId)
+    }
+    
+}
+
+watch(
+    ()=>selectChartList.value,
+    ()=>{
+        const arr=listState.list.filter(item=>selectChartList.value.includes(item.SaCompareId)).map(e=>e.ResultImg)
+        emits('update',arr)
+    },
+    {
+        immediate:true,
+        deep:true
+    }
+)
+</script>
+
+
+<template>
+    <div class="sandTableImg-insert-wrap">
+        <van-search v-model="searchVal" shape="round" placeholder="请输入搜索关键词" @search="handleRefreshList" @clear="handleRefreshList" />
+        <div class="content-box">
+            <div v-if="listState.list.length==0&&listState.finished">
+                <img class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
+                <p style="text-align:center;color:#999999;font-size:12px">暂无图表</p>
+            </div>
+            
+            <ul class="chart-list" v-infinite-scroll="[onLoadMore, { 'distance' : 10 }]">
+                <li 
+                    :class="['chart-item',selectChartList.includes(item.SaCompareId)&&'active']" 
+                    v-for="item in listState.list" 
+                    :key="item.SaCompareId"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title">{{item.Title}}</div>
+                    <img :src="item.ResultImg" alt="">
+
+                    <svg v-if="selectChartList.includes(item.SaCompareId)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M14.5 28C22.232 28 28.5 21.732 28.5 14C28.5 6.26801 22.232 0 14.5 0C6.76801 0 0.5 6.26801 0.5 14C0.5 21.732 6.76801 28 14.5 28ZM7.5 14.413L8.913 13L12.5 16.586L20.085 9L21.5 10.415L12.5 19.414L7.5 14.413Z" fill="#0052D9"/>
+                    </svg>
+
+                </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.sandTableImg-insert-wrap{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .content-box{
+        overflow: hidden;
+        flex: 1;
+        padding: var(--van-padding-sm);
+        min-height: 300PX;
+    }
+    .chart-list{
+        height: 100%;
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        .chart-item{
+            width: 48%;
+            background: #FFFFFF;
+            border: 3px solid $border-color;
+            box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.03);
+            border-radius: 4px;
+            margin-bottom: 30px;
+            overflow: hidden;
+            padding: 14px;
+            box-sizing: border-box;
+            position: relative;
+            .title{
+                font-size: 28px;
+                min-height: 60px;
+            }
+            img{
+                width: 100%;
+                height: 220px;
+                object-fit: contain;
+            }
+        }
+        .active{
+            border-color: $theme-color;
+            svg{
+                width: 28px;
+                height: 28px;
+                position: absolute;
+                right: 22px;
+                bottom: 22px;
+            }
+        }
+    }
+}
+@media screen and (min-width:$media-width){
+    .sandTableImg-insert-wrap{
+        .chart-list{
+            height: auto;
+            max-height: 100%;
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+                img{
+                    height: 150px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 41 - 3
src/views/reportEn/AddReport.vue

@@ -1,11 +1,12 @@
 <script setup name="ReportEnAdd">
-import {ref,onMounted,onUnmounted, nextTick} from 'vue'
+import {ref,onMounted,onUnmounted, nextTick, watch} from 'vue'
 import EditReportBaseInfo from './components/EditReportBaseInfo.vue'
 import ReportInsertContent from '../report/components/reportInsert/Index.vue'
+import ReportPublishTimeSet from '@/views/report/components/ReportPublishTimeSet.vue'
 import apiReportEn from '@/api/reportEn'
 import apiChart from '@/api/chart'
 import moment from 'moment'
-import { showToast } from 'vant'
+import { showToast,showDialog } from 'vant'
 import { useRoute, useRouter } from 'vue-router'
 import {useInitFroalaEditor} from '@/hooks/useFroalaEditor'
 import {useCachedViewsStore} from '@/store/modules/cachedViews'
@@ -21,6 +22,8 @@ let overviewContentIns=null//overview内容编辑器实例
 
 let autoSaveTimer=null//自动保存定时器
 
+let reportId=route.query.id||0//报告id
+
 onMounted(async () => {
     const el=document.getElementById('editor')
     reportContentIns=initFroalaEditor('#editor',{height:el.offsetHeight-150})
@@ -271,6 +274,7 @@ async function handleReportOpt(e){
     ?await apiReportEn.reportEdit({ReportId:Number(route.query.id),...params})
     :await apiReportEn.reportAdd(params)
     if(res.Ret!==200) return
+    reportId=res.Data.ReportId
     cachedViewsStore.removeCaches('ReportEnList')
     if(e==='cg'){
         showToast('保存成功')
@@ -284,7 +288,17 @@ async function handleReportOpt(e){
         }, 1000);
     }
     if(e==='fb'){
-        reportPublish(res.Data.ReportId)
+        showDialog({
+            title: '提示',
+            message: reportData.value?.PrePublishTime?'该报告已设置定时发布,是否修改为立即发布?':`是否确定立即发布报告?`,
+            showCancelButton:true
+        }).then(()=>{
+            reportPublish(res.Data.ReportId)
+        })
+        
+    }
+    if(e==='dsfb'){
+        showDSFBTime.value=true
     }
     
 }
@@ -301,6 +315,23 @@ function reportPublish(id){
     })
 }
 
+
+// 定时发布报告选择时间
+const showDSFBTime=ref(false)
+function onConfirmDSFBTime(time){
+    console.log(time);
+    apiReportEn.reportPublishTimeSet({
+        ReportId:reportId,
+        PrePublishTime:time
+    }).then(res=>{
+        if(res.Ret===200){
+            showToast('定时发布成功')
+            setTimeout(() => {
+                router.back()
+            }, 1500);
+        }
+    })
+}
 </script>
 
 <template>
@@ -325,6 +356,10 @@ function reportPublish(id){
                     <img src="@/assets/imgs/report/icon_save2.png" alt="">
                     <span>保存</span>
                 </div>
+                <div class="item" @click="handleReportOpt('dsfb')">
+                    <img src="@/assets/imgs/report/icon_time.png" alt="">
+                    <span>定时发布</span>
+                </div>
                 <div class="item" @click="handleReportOpt('fb')">
                     <img src="@/assets/imgs/report/icon_publish3.png" alt="">
                     <span>发布</span>
@@ -371,6 +406,9 @@ function reportPublish(id){
         <report-insert-content v-if="showReportInsertPop" @insert="handleInsert"/>
     </van-popup>
 
+    <!-- 定时发布选择时间 -->
+    <ReportPublishTimeSet v-model="showDSFBTime" :prePublishTime="reportData?.PrePublishTime" @confirm="onConfirmDSFBTime" />
+
 </template>
 
 <style lang="scss" scoped>

+ 119 - 16
src/views/reportEn/List.vue

@@ -24,7 +24,8 @@ const showCleanFilterBox=computed(()=>{
         listState.ClassifyNameFirst||
         listState.EndDate||
         listState.Frequency||
-        listState.MsgIsSend
+        listState.MsgIsSend||
+        listState.publishStatus
     ) return true
 })
 const isClickClose=ref(false)//是否点击过关闭一键清空模块
@@ -35,6 +36,7 @@ function handleCleanFilter(){
     listState.Frequency=''
     listState.StartDate=''
     listState.EndDate=''
+    listState.publishStatus=''
     refreshList()
 }
 
@@ -43,6 +45,7 @@ const showClassify=ref(false)
 
 
 const listState = reactive({
+    publishStatus:'',
     MsgIsSend:'',
     Frequency:'',
     ClassifyNameFirst:'',
@@ -64,7 +67,9 @@ async function getList(){
         ClassifyNameFirst:listState.ClassifyNameFirst,
         ClassifyNameSecond:listState.ClassifyNameSecond,
         Frequency:listState.Frequency,
-        EmailState:listState.MsgIsSend
+        EmailState:listState.MsgIsSend,
+        TimeType:dateType.value===1?'publish_time':'modify_time',
+        State:listState.publishStatus
     })
     if(res.Ret===200){
         listState.loading=false
@@ -111,15 +116,23 @@ function handleReportDel(item){
 
 // 发布报告
 async function handleReportPublish(item){
-    apiReportEn.reportPublish({
-        ReportIds:item.Id.toString()
-    }).then(res=>{
-        if(res.Ret===200){
-            showToast('发布成功')
-            showReportItemOpt.value=false
-            refreshList()
-        }
+    console.log(item);
+    showDialog({
+        title: '提示',
+        message: item.PrePublishTime?'该报告已设置定时发布,是否修改为立即发布?':`是否确定立即发布报告?`,
+        showCancelButton:true
+    }).then(()=>{
+        apiReportEn.reportPublish({
+            ReportIds:item.Id.toString()
+        }).then(res=>{
+            if(res.Ret===200){
+                showToast('发布成功')
+                showReportItemOpt.value=false
+                refreshList()
+            }
+        })
     })
+    
 }
 
 // 取消发布
@@ -142,6 +155,7 @@ function handleReportPublishCancle(item){
 const calendarMinDate=new Date(2010,0,1)
 const showCalendar=ref(false)
 const calendarIns=ref(null)
+const dateType=ref(1)//1发布时间 2更新时间
 function handleCalendarChange(e){
     listState.StartDate=moment(e[0]).format('YYYY-MM-DD')
     listState.EndDate=moment(e[1]).format('YYYY-MM-DD')
@@ -190,9 +204,11 @@ function onLongPressItem(e){
 // 更多操作
 const frequencyDropMenuIns=ref(null)
 const statusDropMenuIns=ref(null)
+const publishStatusDropMenuIns=ref(null)
 const showMoreFilter=ref(false)
 const temFrequencyVal=ref('')
 const temMsgIsSendVal=ref('')
+const temPublishStatusVal=ref('')
 const frequencyOpt=[
     {
         label:'年度',
@@ -237,6 +253,16 @@ const reportStatusOpt=[
         value:1
     }
 ]
+const publishStatusOpt=[
+    {
+        label:'已发布',
+        value:2
+    },
+    {
+        label:'未发布',
+        value:1
+    }
+]
 function handleSelectReportStatus(item){
     if(temMsgIsSendVal.value==item.value){
         temMsgIsSendVal.value=''
@@ -251,6 +277,13 @@ function handleSelectFrequency(item){
         temFrequencyVal.value=item.value
     }
 }
+function handleSelectReportPublishStatus(item){
+    if(temPublishStatusVal.value==item.value){
+        temPublishStatusVal.value=''
+    }else{
+        temPublishStatusVal.value=item.value
+    }
+}
 function handleShowFilter(){
     temFrequencyVal.value=listState.Frequency
     temMsgIsSendVal.value=listState.MsgIsSend
@@ -271,6 +304,11 @@ function handleConfirmStatus(){
     refreshList()
     showMoreFilter.value=false
 }
+function handleConfirmPublishStatus(){
+    listState.publishStatus=temPublishStatusVal.value
+    refreshList()
+    showMoreFilter.value=false
+}
 
 function goSearch(){
     // 删除报告搜索页的缓存
@@ -362,7 +400,7 @@ function handleGoEmailLog(e){
                     style="flex:1"
                     shape="round" 
                     readonly 
-                    placeholder="请输入报告标题或作者"
+                    placeholder="请输入报告标题或创建人"
                     @click="goSearch"
                 />
                 <div :class="['menu-icon',showClassify||listState.ClassifyNameSecond?'active':'']" @click="showClassify=true">
@@ -384,7 +422,7 @@ function handleGoEmailLog(e){
                         <path d="M12.7528 34.0668C12.7528 34.3535 12.8093 34.6375 12.919 34.9024C13.0288 35.1674 13.1896 35.4081 13.3924 35.6109C13.5952 35.8136 13.8359 35.9745 14.1008 36.0842C14.3658 36.194 14.6497 36.2505 14.9365 36.2505C15.2233 36.2505 15.5072 36.194 15.7722 36.0842C16.0371 35.9745 16.2778 35.8136 16.4806 35.6109C16.6834 35.4081 16.8442 35.1674 16.954 34.9024C17.0637 34.6375 17.1202 34.3535 17.1202 34.0668C17.1202 33.78 17.0637 33.496 16.954 33.2311C16.8442 32.9662 16.6834 32.7254 16.4806 32.5226C16.2778 32.3199 16.0371 32.159 15.7722 32.0493C15.5072 31.9395 15.2233 31.8831 14.9365 31.8831C14.6497 31.8831 14.3658 31.9395 14.1008 32.0493C13.8359 32.159 13.5952 32.3199 13.3924 32.5226C13.1896 32.7254 13.0288 32.9662 12.919 33.2311C12.8093 33.496 12.7528 33.78 12.7528 34.0668Z" fill="currentColor" stroke="currentColor" stroke-width="0.5"/>
                     </svg>
                 </div>
-                <div :class="['menu-icon',showMoreFilter||listState.Frequency||listState.MsgIsSend?'active':'']" @click="handleShowFilter">
+                <div :class="['menu-icon',showMoreFilter||listState.Frequency||listState.MsgIsSend||listState.publishStatus?'active':'']" @click="handleShowFilter">
                     <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
                         <path d="M30.283 23.977L40.824 9.5685C41.3719 8.81954 41.6002 7.88381 41.459 6.96667C41.3177 6.04952 40.8184 5.22589 40.0705 4.6765C39.4715 4.23699 38.7479 4 38.005 4H9.995C8.065 4 6.5 5.567 6.5 7.5C6.5 8.244 6.7365 8.9685 7.1755 9.5685L17.717 23.977V42.5C17.717 43.3285 18.3875 44 19.215 44C19.6125 43.9996 19.9935 43.8414 20.2743 43.5601C20.5552 43.2788 20.7128 42.8975 20.7125 42.5V23.486C20.7125 23.1675 20.611 22.857 20.423 22.5995L9.592 7.7955C9.53761 7.72096 9.50489 7.63283 9.49745 7.54085C9.49001 7.44887 9.50814 7.35663 9.54984 7.27431C9.59155 7.192 9.65519 7.12281 9.73375 7.0744C9.81231 7.02599 9.90272 7.00024 9.995 7H38.005C38.0973 7.00015 38.1878 7.02586 38.2664 7.07427C38.345 7.12268 38.4086 7.1919 38.4503 7.27426C38.492 7.35663 38.51 7.44892 38.5025 7.54092C38.4949 7.63292 38.4621 7.72104 38.4075 7.7955L27.5765 22.5995C27.3884 22.8568 27.287 23.1673 27.287 23.486V38.271C27.287 39.0995 27.9575 39.771 28.785 39.771C28.9819 39.7709 29.1768 39.732 29.3586 39.6565C29.5404 39.5811 29.7056 39.4705 29.8447 39.3312C29.9838 39.192 30.0941 39.0266 30.1693 38.8447C30.2445 38.6628 30.2831 38.4678 30.283 38.271V23.977Z" fill="currentColor"/>
                     </svg>
@@ -409,7 +447,7 @@ function handleGoEmailLog(e){
                         </div>
                     </div>
                 </van-dropdown-item>
-                <van-dropdown-item title="报告状态" ref="statusDropMenuIns">
+                <van-dropdown-item title="群发状态" ref="statusDropMenuIns">
                     <div class="report-status-box">
                         <ul>
                             <li 
@@ -426,6 +464,23 @@ function handleGoEmailLog(e){
                         </div>
                     </div>
                 </van-dropdown-item>
+                <van-dropdown-item title="发布状态" ref="publishStatusDropMenuIns">
+                    <div class="report-status-box">
+                        <ul>
+                            <li 
+                                :class="['status-item',temPublishStatusVal===item.value?'active':'']" 
+                                v-for="item in publishStatusOpt" 
+                                :key="item.value"
+                                @click="handleSelectReportPublishStatus(item)"
+                            >{{item.label}}</li>
+                        </ul>
+                        
+                        <div class="bot-btn-box">
+                            <div class="btn cancel-btn" @click="showMoreFilter=false">取消</div>
+                            <div class="btn confirm-btn" @click="handleConfirmPublishStatus">确定</div>
+                        </div>
+                    </div>
+                </van-dropdown-item>
             </van-dropdown-menu>
             </template>
         </div>
@@ -444,12 +499,19 @@ function handleGoEmailLog(e){
                     @click="goDetail(item)"
                     v-longpress="{ handler: onLongPressItem, args: item, duration: 1000 }"
                 >
-                    <h2 :class="['van-ellipsis title',item.Title.startsWith('【')?'inline-title':'']">{{item.Title}}</h2>
+                    <h2 :class="['van-ellipsis title',item.Title.startsWith('【')?'inline-title':'']">
+                        <span>{{item.Title}}</span>
+                        <span v-if="item.CreateTime">({{item.CreateTime.substring(5,7)}}{{item.CreateTime.substring(8,10)}})</span>
+                    </h2>
                     <p class="van-multi-ellipsis--l2 des">{{item.Abstract}}</p>
                     <div class="bot-info">
                         <div style="flex:1">
-                            <span style="margin-right:10px">{{moment(item.ModifyTime).format('YYYY-MM-DD')}}</span>
-                            <span>{{item.AdminRealName}}</span>
+                            <span style="margin-right:2px">{{moment(item.ModifyTime).format('YYYY-MM-DD')}}</span>
+                            <svg v-if="item.PrePublishTime&&item.State==1" style="width:14px;height:14px;position: relative;top:2px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" fill="none">
+                                <path d="M13.0357 6.28571V14.7501L17.8576 19.7857L19.2213 18.422L14.9643 13.9512V6.28571H13.0357Z" fill="#0052D9"/>
+                                <path d="M27.5 14C27.5 21.4558 21.4558 27.5 14 27.5C6.54416 27.5 0.5 21.4558 0.5 14C0.5 6.54416 6.54416 0.5 14 0.5C21.4558 0.5 27.5 6.54416 27.5 14ZM25.5714 14C25.5714 7.60928 20.3907 2.42857 14 2.42857C7.60928 2.42857 2.42857 7.60928 2.42857 14C2.42857 20.3907 7.60928 25.5714 14 25.5714C20.3907 25.5714 25.5714 20.3907 25.5714 14Z" fill="#0052D9"/>
+                            </svg>
+                            <span style="margin-left:10px">{{item.AdminRealName}}</span>
                         </div>
                         <div class="read-count">
                             <span>PV:{{item.Pv}}</span>
@@ -518,12 +580,17 @@ function handleGoEmailLog(e){
             :min-date="calendarMinDate"
             @confirm="handleCalendarChange" 
             :style="{ height: '500px' }"
+            class="calendar-box"
         >
             <template #title>
                 <div style="position: relative;">
                     <span style="color:#666;position: absolute;left:16px" @click="handleResetCalendar">重置</span>
                     <span>日期选择</span>
                 </div>
+                <div class="time-type-box">
+                    <span @click="dateType=1" :class="['item',dateType===1?'active':'']">发布时间</span>
+                    <span @click="dateType=2" :class="['item',dateType===2?'active':'']">更新时间</span>
+                </div>
             </template>
         </van-calendar>
     </van-popup>
@@ -783,6 +850,42 @@ function handleGoEmailLog(e){
     }
 }
 
+.calendar-box{
+    :deep(.van-calendar__header-title){
+        height: auto;
+        min-height: var(--van-calendar-header-title-height);
+    }
+    :deep(.van-calendar__header){
+        box-shadow: none;
+    }
+    .time-type-box{
+        font-weight: normal;
+        text-align: left;
+        padding-left: 32px;
+        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.08);
+        .item{
+            display: inline-block;
+            margin-right: 40px;
+            color: $font-grey;
+            position: relative;
+            &.active{
+                color: #333;
+                &::after{
+                    content: '';
+                    width: 30PX;
+                    height: 4PX;
+                    background-color: $theme-color;
+                    position: absolute;
+                    bottom: 0;
+                    left: 50%;
+                    border-radius: 2px;
+                    transform: translateX(-50%);
+                }
+            }
+        }
+    }
+}
+
 @media screen and (min-width:$media-width){
     .add-report-btn{
         svg{

+ 1 - 1
src/views/reportEn/Search.vue

@@ -64,7 +64,7 @@ function goDetail(item){
         <div class="search-box">
             <van-search 
                 shape="round"
-                placeholder="请输入报告标题或作者"
+                placeholder="请输入报告标题或创建人"
                 v-model="keyword"
                 @search="handleSearch"
             />

+ 9 - 4
src/views/tabbar/Home.vue

@@ -38,15 +38,15 @@ const topImg=computed(()=>{
     let url=''
     if(width.value>650){//大屏
         if(language_version.value==='zh'){
-            url='https://hzstatic.hzinsights.com/static/ETA_mobile/tabbar_home_topimg_zh_big.png'
-        }else{
             url='https://hzstatic.hzinsights.com/static/ETA_mobile/tabbar_home_topimg_en_big.png'
+        }else{
+            url='https://hzstatic.hzinsights.com/static/ETA_mobile/tabbar_home_topimg_zh_big.png'
         }
     }else{
         if(language_version.value==='zh'){
-            url='https://hzstatic.hzinsights.com/static/ETA_mobile/tabbar_home_topimg_zh.png'
-        }else{
             url='https://hzstatic.hzinsights.com/static/ETA_mobile/tabbar_home_topimg_en.png'
+        }else{
+            url='https://hzstatic.hzinsights.com/static/ETA_mobile/tabbar_home_topimg_zh.png'
         }
     }
     return url
@@ -235,6 +235,8 @@ getMenuList()
                     <div class="des">{{item.des}}</div>
                 </div>
             </div>
+            <div class="item-box" style="height:0;min-height:0;padding:0"></div>
+            <div class="item-box" style="height:0;min-height:0;padding:0"></div>
         </div>
         
     </div>
@@ -243,6 +245,7 @@ getMenuList()
 <style lang="scss" scoped>
 .home-page{
     padding: 30px 34px;
+    padding-bottom: 140px;
     .top-img{
         display: block;
         width: 100%;
@@ -298,12 +301,14 @@ getMenuList()
         padding: 30px 34px;
         max-width: 1180px;
         margin: 0 auto;
+        padding-bottom: 30px;
         .top-img{
             width: 100%;
             margin-bottom: 20px;
         }
         .list{
             gap: 20px;
+            justify-content: center;
             .item-box{
                 width: 260px;
                 min-height: 161px;