bd 3 gadi atpakaļ
vecāks
revīzija
365cd98611

+ 3 - 1
.env.development

@@ -5,4 +5,6 @@ VITE_APP_CYGX_BASEAPIURL="http://8.136.199.33:8500/api"
 
 VITE_APP_HZYB_BASEAPIURL="http://8.136.199.33:8612"
 
-VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+
+VITE_APP_HTGJ_BASEAPIURL="http://8.136.199.33:8500/api"

+ 3 - 1
.env.product

@@ -5,4 +5,6 @@ VITE_APP_CYGX_BASEAPIURL="https://cygx.hzinsights.com/api"
 
 VITE_APP_HZYB_BASEAPIURL="https://yanbao.hzinsights.com"
 
-VITE_APP_HZSL_BASEAPIURL="https://openapi.hzinsights.com/api"
+VITE_APP_HZSL_BASEAPIURL="https://openapi.hzinsights.com/api"
+
+VITE_APP_HTGJ_BASEAPIURL="https://cygx.hzinsights.com/api"

+ 4 - 1
.env.test

@@ -5,4 +5,7 @@ VITE_APP_CYGX_BASEAPIURL="http://8.136.199.33:8500/api"
 
 VITE_APP_HZYB_BASEAPIURL="http://8.136.199.33:8612"
 
-VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+VITE_APP_HZSL_BASEAPIURL="http://8.136.199.33:8608/api"
+
+VITE_APP_HTGJ_BASEAPIURL="http://8.136.199.33:8500/api"
+

+ 2 - 1
package.json

@@ -15,7 +15,8 @@
     "normalize.css": "^8.0.1",
     "vant": "^3.3.4",
     "vue": "^3.2.16",
-    "vue-router": "^4.0.12"
+    "vue-router": "^4.0.12",
+    "vuex": "^4.0.2"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^1.9.3",

+ 37 - 0
src/api/htgj/api.js

@@ -0,0 +1,37 @@
+import { get, post } from "./http";
+export const report = {
+  /* 获取品种 */
+  Tab: (params) => {
+    return get("permission/allPublic", params);
+  },
+  /* 
+	列表
+	PageSize * CurrentIndex * ChartPermissionId *品类id,最新传0
+	*/
+  getList: (params) => {
+    return get("/home/listPublic", params);
+  },
+  /* 获取搜索推荐词 */
+  getKeys: (params) => {
+    return get("/config/detailPublic", params);
+  },
+  /* 搜索  KeyWord */
+  getResult: (params) => {
+    return get("/search/listPublic", params);
+  },
+};
+/* 权益链接 */
+export const RaiApi = {
+  /* 获取详情 */
+  reportDtl: (params) => {
+    return get("/article/detailPublic", params);
+  },
+  /* 页面复制监听*/
+  pageHistoryCopy: (params) => {
+    return post("/config/pageHistoryPublic", params, 1);
+  },
+  /*上传文章阅读时间接口*/
+  addStopTime: (params) => {
+    return post("/article/addStopTimePublic", params);
+  },
+};

+ 87 - 0
src/api/htgj/http.js

@@ -0,0 +1,87 @@
+"use strict";
+import axios from "axios";
+import { Toast } from "vant";
+import store from "@/store";
+// Full config:  https://github.com/axios/axios#request-config
+// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
+// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
+// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
+
+// 请求数
+let LOADINGCOUNT = 0;
+let LOADING;
+let config = {
+  baseURL: import.meta.env.VITE_APP_HTGJ_BASEAPIURL,
+  timeout: 60 * 1000, // Timeout
+  // withCredentials: true, // Check cross-site Access-Control
+};
+
+const _axios = axios.create(config);
+
+_axios.interceptors.request.use(
+  function (config) {
+    // Do something before request is sent
+    // 设置loading
+    if (LOADINGCOUNT === 0) {
+      LOADING = Toast.loading({
+        message: "loading...",
+        duration: 0,
+        forbidClick: true,
+      });
+    }
+    LOADINGCOUNT++;
+    let data = store.state.userData;
+
+    if (config.method == "get") {
+      config.params.UserName = data.name;
+      config.params.Sign = data.sign;
+    } else {
+      config.data.UserName = data.name;
+      config.data.Sign = data.sign;
+    }
+
+    return config;
+  },
+  function (error) {
+    // Do something with request error
+    return Promise.reject(error);
+  }
+);
+
+// Add a response interceptor
+_axios.interceptors.response.use(
+  function (response) {
+    // Do something with response data
+
+    //关闭loading
+    LOADINGCOUNT--;
+    if (LOADINGCOUNT === 0) {
+      LOADING.clear();
+    }
+
+    return response.data;
+  },
+  function (error) {
+    LOADING.clear();
+    // Do something with response error
+    return Promise.reject(error);
+  }
+);
+
+/**
+ * 导出get请求方法
+ * @url 请求地址
+ * @params get请求参数
+ */
+export const get = (url, params) => {
+  return _axios.get(url, { params });
+};
+
+/**
+ * 导出post请求方法
+ * @url 请求地址
+ * @params post请求参数
+ */
+export const post = (url, params) => {
+  return _axios.post(url, params);
+};

BIN
src/assets/cygx/noauth.png


+ 24 - 0
src/router/htgj/index.js

@@ -0,0 +1,24 @@
+export const htgjRoutes=[
+    {
+        path:'/htgj',
+        name:"htgj",
+        component: () => import("@/App.vue"),
+        children:[
+            {
+                path:"index",
+                name:"htgjIndex",
+                component: () => import("@/views/htgj/index.vue"),
+            },
+            {
+                path:"detail",
+                name:"htgjDetail",
+                component: () => import("@/views/htgj/report.vue"),
+            },
+            {
+                path:"search",
+                name:"htgjSearch",
+                component: () => import("@/views/htgj/search.vue"),
+            }
+        ]
+    }
+]

+ 16 - 0
src/router/index.js

@@ -2,10 +2,13 @@ import { createRouter, createWebHistory } from 'vue-router'
 import {hzybRoutes} from './hzyb/index'// 弘则研报小程序路由
 import {cygxRoutes} from './cygx/index'// 查研观向小程序路由
 import {hzslRoutes} from './hzsl/index'// 弘则思路小程序路由
+import {htgjRoutes} from './htgj/index'// 海通国际 APP 路由
+import store from "@/store";
 const routes=[
     ...hzybRoutes,
     ...cygxRoutes,
     ...hzslRoutes,
+    ...htgjRoutes,
     //404
     {
         path: "/:pathMatch(.*)",
@@ -19,4 +22,17 @@ const router = createRouter({
    routes
 })
 
+router.beforeEach((to, from, next) => {
+        if(to.fullPath.includes('htgj')) {
+             to.query.userName  = '哔哩哔哩老电视-0001'
+             to.query.sign  = 77777
+             let data = {
+                name: to.query.userName,
+                sign: to.query.sign 
+             }
+            store.commit('getUserData', data)
+        }
+	next();
+})
+
 export default router

+ 18 - 0
src/store/index.js

@@ -0,0 +1,18 @@
+import { createStore, Store } from "vuex";
+
+const store = createStore({
+  state: {
+    userData: {
+      name: "",
+      sign: null,
+    },
+  },
+  mutations: {
+    getUserData(state, payload) {
+      state.userData = payload;
+    },
+  },
+  actions: {},
+});
+
+export default store;

+ 118 - 0
src/views/htgj/index.scss

@@ -0,0 +1,118 @@
+.top-box {
+  padding: 20px 14px 10px;
+  position: sticky;
+  left: 0;
+  top: 0;
+  z-index: 9;
+  background: #fff;
+  .seach {
+    padding: 0 20px;
+  }
+  .seach-top {
+    width: 100%;
+    height: 70px;
+    background: rgba(246, 246, 246, 0.39);
+    border: 1px solid #e5e5e5;
+    border-radius: 35px;
+    display: flex;
+    align-items: center;
+    padding-left: 34px;
+    font-size: 24px;
+    color: #8d8d8d;
+    box-sizing: border-box;
+    .van-icon-search {
+      font-size: 28px;
+      margin-right: 10px;
+    }
+  }
+}
+
+.data-cont {
+  display: flex;
+  margin-top: 15px;
+  padding: 0 10px;
+  .report-ul {
+    width: 50%;
+    &:first-child {
+      margin-right: 10px;
+    }
+    .report-item {
+      padding: 20px 20px 24px 20px;
+      margin-bottom: 20px;
+      border-radius: 8px;
+      box-shadow: 0 3px 6px rgba($color: #000000, $alpha: 0.16);
+      background: #fff;
+      .item-content-img {
+        display: flex;
+        align-items: center;
+        img {
+          width: 100%;
+          height: 232px;
+          vertical-align: middle;
+        }
+      }
+      .item-content {
+        height: 277px;
+        font-size: 24px;
+        line-height: 40px;
+        color: #7f7f7f;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        display: -webkit-box;
+        -webkit-line-clamp: 7;
+        -webkit-box-orient: vertical;
+        img {
+          width: 100% !important;
+        }
+      }
+      .line {
+        margin: 18px 0;
+        content: "";
+        width: 100%;
+        height: 1px;
+        padding: 0 32px;
+        box-sizing: border-box;
+        background-color: #e5e5e5;
+        -webkit-transform: scale(1, 0.5);
+        transform: scale(1, 0.5);
+        -webkit-transform-origin: center bottom;
+        transform-origin: center bottom;
+      }
+      .item-title {
+        font-size: 28px;
+        color: #4a4a4a;
+        margin-bottom: 10px;
+      }
+      .item-abstract {
+        font-size: 26px;
+        color: #6a6a6a;
+        margin-bottom: 10px;
+        .report_ico {
+          width: 32px;
+          height: 26px;
+          margin-right: 20px;
+          display: inline-block;
+        }
+      }
+      .item-createtime {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        color: #acacac;
+        font-size: 24px;
+        .item-examine {
+          display: flex;
+          align-items: center;
+          img {
+            width: 30px;
+            height: 24px;
+            margin: 0 10px 0 15px;
+          }
+        }
+      }
+    }
+  }
+}
+.van-tabs__line {
+    background: linear-gradient(268deg, #2ddbff 0%, #1599ff 49%, #005eff 100%);
+  }

+ 180 - 0
src/views/htgj/index.vue

@@ -0,0 +1,180 @@
+<template>
+  <div class="container-htgj-index">
+    <div class="top-box">
+      <div class="seach" @click="goSearch">
+        <div class="seach-top">
+          <Icon name="search" color="#8D8D8D" />
+          <span>搜索您想看的纪要</span>
+        </div>
+      </div>
+      <div>
+        <Tabs v-model:active="tabActive" title-active-color="#2C83FF" title-inactive-color="#707070" @click-tab="onClickTabs">
+          <Tab v-for="item in tabBars" :key="item.ChartPermissionId" :name="item.ChartPermissionId" :title="item.PermissionName"></Tab>
+          <template #nav-right v-if="tabBars.length">
+            <img class="tabs-img" src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/limit_icon.png" alt="" />
+          </template>
+        </Tabs>
+      </div>
+    </div>
+    <div>
+      <PullRefresh v-model="refreshing" @refresh="onRefresh">
+        <List v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
+          <div class="data-cont">
+            <div class="report-ul">
+              <template v-for="(report, index) in dataList" :key="index">
+                <div class="report-item" v-if="index % 2 === 0" @click="goDetail(report)">
+                  <div class="item-content-img" v-if="report.BodyHtml">
+                    <img :src="report.BodyHtml" mode="" />
+                  </div>
+                  <div class="item-content" v-else>{{ report.Body }}</div>
+                  <div class="line"></div>
+                  <text class="item-title">{{ report.Title }}</text>
+                  <div class="item-abstract text_twoLine" v-if="report.ExpertBackground">
+                    <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/fenxi_ico.png" class="report_ico" />
+                    {{ report.ExpertBackground }}
+                  </div>
+                  <div class="item-createtime">
+                    <text>{{ report.PublishDate }}</text>
+                    <div class="item-examine" v-if="report.IsResearch">
+                      <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/examine_icon.png" />
+                      <text>{{ report.Pv }}</text>
+                    </div>
+                  </div>
+                </div>
+              </template>
+            </div>
+            <div class="report-ul">
+              <template v-for="(report, index) in dataList" :key="index">
+                <div class="report-item" v-if="index % 2 != 0" @click="goDetail(report)">
+                  <div class="item-content-img" v-if="report.BodyHtml">
+                    <img :src="report.BodyHtml" />
+                  </div>
+                  <div class="item-content" v-else>{{ report.Body }}</div>
+                  <div class="line"></div>
+                  <text class="item-title">{{ report.Title }}</text>
+                  <div class="item-abstract text_twoLine" v-if="report.ExpertBackground">
+                    <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/fenxi_ico.png" class="report_ico" />
+                    {{ report.ExpertBackground }}
+                  </div>
+                  <div class="item-createtime">
+                    <text>{{ report.PublishDate }}</text>
+                    <div class="item-examine" v-if="report.IsResearch">
+                      <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/examine_icon.png" />
+                      <text>{{ report.Pv }}</text>
+                    </div>
+                  </div>
+                </div>
+              </template>
+            </div>
+          </div>
+        </List>
+      </PullRefresh>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { reactive, onMounted, toRefs, ref } from "vue";
+import { Icon, Tab, Tabs, List, PullRefresh } from "vant";
+import { report } from "@/api/htgj/api.js";
+import { useRoute, useRouter } from "vue-router";
+const router = useRouter();
+const tabState = reactive({
+  tabActive: 0,
+  tabBars: [],
+});
+/* 获取tab分类 */
+const getTabs = async () => {
+  const res = await report.Tab({});
+  if (res.Ret === 200) {
+    let arr = res.Data.List;
+    arr.unshift({
+      ChartPermissionId: 0,
+      PermissionName: "最新",
+    });
+    tabState.tabBars = arr;
+  }
+};
+/* tab点击 事件 */
+const onClickTabs = (e) => {
+  listState.finished = false;
+  listState.pageNum = 1;
+  listState.dataList = [];
+  onLoad();
+};
+
+const listState = reactive({
+  pageSize: 30,
+  pageNum: 1,
+  loading: false,
+  finished: false,
+  refreshing: false, //下拉组件
+  dataList: [],
+});
+
+/* 获取列表 */
+const getReportList = async () => {
+  const res = await report.getList({
+    PageSize: listState.pageSize,
+    CurrentIndex: listState.pageNum,
+    ChartPermissionId: tabState.tabActive,
+  });
+  if (res.Ret === 200) {
+    listState.loading = false;
+    listState.refreshing = false;
+    if (res.Data.Paging.IsEnd) {
+      listState.finished = true;
+    }
+    if (res.Data.List) {
+      listState.dataList = listState.dataList.concat(res.Data.List);
+    }
+  }
+};
+
+/* 下拉刷新 */
+const onRefresh = async () => {
+  listState.finished = false;
+  listState.pageNum = 1;
+  listState.dataList = [];
+  onLoad();
+};
+
+//触底加载
+const onLoad = () => {
+  getReportList();
+  listState.pageNum++;
+};
+
+/* 去往详情 */
+const goDetail = (item) => {
+  router.push({
+    path: "detail",
+    query: {
+      id: item.ArticleId,
+    },
+  });
+};
+
+/* 去往搜索 */
+const goSearch = () => {
+  router.push("search");
+};
+
+getTabs();
+
+const { tabActive, tabBars } = toRefs(tabState);
+const { dataList, refreshing, loading, finished } = toRefs(listState);
+</script>
+
+<style lang="scss">
+.container-htgj-index {
+  background: #f7f7f7;
+  @import "./index.scss";
+  .tabs-img {
+    padding-top: 16px;
+    margin-left: -15px;
+    width: 46px;
+    height: 26px;
+  }
+}
+</style>

+ 367 - 0
src/views/htgj/report.vue

@@ -0,0 +1,367 @@
+<template>
+  <div class="container-cygx" v-if="haveData" :class="reportInfo.IsResearch ? 'no-cv' : ''">
+    <canvas id="tutorial" ref="tutorial"></canvas>
+    <div class="search">
+      <div class="search-box" @click="btnSearch">
+        <Icon name="search" color="#8D8D8D" />
+        <span>搜索您想要的产业资源包</span>
+      </div>
+    </div>
+    <div class="z-index-content">
+      <div class="content-top">
+        <div class="report-title">{{ reportInfo.Title }}</div>
+        <div class="report-text">
+          <div class="report_desc">
+            <span class="author">{{ reportInfo.Department }}</span>
+            <span>{{ reportInfo.PublishDate }}</span>
+          </div>
+          <div class="seller-list" v-if="!reportInfo.IsResearch">
+            <span>联系人:</span>
+            <span v-for="(item, index) in reportInfo.SellerList" :key="index"> {{ item.SellerName }}({{ item.SellerMobile }})&nbsp;&nbsp; </span>
+          </div>
+          <div class="seller-list" v-else>
+            <span>作者:{{ reportInfo.SellerAndMobile }} </span>
+          </div>
+          <div>注:请务必阅读<span class="tip" @click="showTips = true"> &nbsp;免责声明</span></div>
+          <div class="container-abstract">&nbsp;&nbsp;摘要:&nbsp;{{ reportInfo.Abstract }}</div>
+        </div>
+      </div>
+      <div class="detail-report">
+        <div id="report-content" v-html="reportInfo.Body"></div>
+      </div>
+    </div>
+    <div class="btn-returntop">
+      <img src="~@/assets/cygx/returntop.png" @click="scrolltop" style="width: 40px" />
+    </div>
+    <dlg :showTips="showTips" :reportInfo="reportInfo" @hideDlg="showTips = false" />
+  </div>
+  <div v-else class="nodata">
+    <img src="@/assets/cygx/noauth.png" mode="" class="nodata_ico" />
+    <p>您的试用权限已到期</p>
+    <p>若想继续试用,请联系销售</p>
+  </div>
+</template>
+
+<script setup>
+import { reactive, onMounted, toRefs, ref ,onUnmounted } from "vue";
+import { useRouter, useRoute } from "vue-router";
+import { RaiApi } from "@/api/htgj/api.js";
+import { Icon, Dialog, Toast } from "vant";
+import dlg from "../cygx/dlg.vue";
+import store from "@/store";
+
+const router = useRouter();
+const route = useRoute();
+const state = reactive({
+  reportInfo: {},
+});
+const rerportId = ref(null);
+const haveData = ref(true);
+const fileLink = ref(false);
+const showTips = ref(false);
+const tutorial = ref(null);
+/* 访谈接口 */
+const interviewApi = () => {
+  RaiApi.applyRpt({
+    ArticleId: Number(rerportId.value),
+  }).then((res) => {
+    if (res.Ret === 200) {
+      state.reportInfo.IsInterviewApply = !state.reportInfo.IsInterviewApply;
+      state.reportInfo.InterviewApplyStatus = state.reportInfo.IsInterviewApply ? "待邀请" : "";
+      Toast(res.Msg);
+    }
+  });
+};
+
+const scrolltop = () => {
+  document.body.scrollTop = document.documentElement.scrollTop = 0;
+};
+
+/* 复制 */
+const copyMonitor = () => {
+  RaiApi.pageHistoryCopy({
+    DetailId: rerportId.value + "",
+    PageType: "ArticleCopy",
+  }).then((res) => {});
+};
+
+/* 打水印 */
+const waterMark = (text) => {
+  let canvas = document.createElement("canvas");
+  let ctx = canvas.getContext("2d");
+  ctx.font = "20px Arial";
+  ctx.rotate((-45 * Math.PI) / 200);
+  ctx.fillText(text, 30, 160);
+  ctx.fillText(text, -40, 80);
+
+  // 将canvas的内容转换为base64编码
+  let data = canvas.toDataURL("image/png");
+
+  // 将容器的的背景图片设置为生成的base64图片,并平铺
+  tutorial.value.style.background = "url(" + data + ") repeat";
+};
+
+//点击回到搜索页面
+const btnSearch = () => {
+ router.push('search')
+};
+
+const timeState = reactive({
+  setIntervalTiem: null,
+  readTiem: 0,
+});
+
+/* 获取报告详情 */
+const getReport = (id) => {
+  RaiApi.reportDtl({
+    ArticleId: id,
+  }).then((res) => {
+    if (res.Ret === 200) {
+      haveData.value = res.Data.HasPermission === 1 ? true : false;
+      if (res.Data.HasPermission === 1) {
+        //有访问权限
+        state.reportInfo = res.Data.Detail;
+        fileLink.value = res.Data.Detail.FileLink;
+        if (state.reportInfo.IsResearch || state.reportInfo.IsBelongSummary) {
+          waterMark(store.state.userData.name);
+        }
+        $(document).on("click", "#report-content img", function (event) {
+          let imgArray = [];
+          let src_tag = $(this).attr("src");
+          let parent_tag = $(this).parent();
+          if (src_tag && !parent_tag.attr("href")) {
+            $("#report-content img").each(function (index, el) {
+              let itemSrc = $(this).attr("src");
+              imgArray.push(itemSrc);
+            });
+            wx.previewImage({ current: src_tag, urls: imgArray });
+          }
+        });
+        timeState.readTiem = 0;
+        timeState.setIntervalTiem = setInterval(() => {
+          timeState.readTiem++;
+        }, 1000);
+      }
+    }
+  });
+};
+onMounted(() => {
+  if (route.query.id) {
+    rerportId.value = route.query.id;
+    getReport(rerportId.value);
+    document.addEventListener("copy", (e) => {
+      copyMonitor();
+    });
+  }
+});
+onUnmounted(() => {
+  clearInterval(timeState.setIntervalTiem);
+  if (!rerportId.value || !haveData.value) return;
+  RaiApi.addStopTime({
+    ArticleId: Number(rerportId.value),
+    StopTime: timeState.readTiem,
+    OutType: 1,
+    Source: "PC",
+  }).then((res) => {});
+});
+const { reportInfo } = toRefs(state);
+</script>
+
+<style lang="scss">
+.container-cygx {
+  padding: 54px 34px 34px 34px;
+  position: relative;
+  z-index: 5;
+  .z-index-content {
+    position: relative;
+    z-index: 5;
+  }
+  .search {
+    width: 100%;
+    padding: 20px 20px;
+    line-height: 70px;
+    position: fixed;
+    top: 0;
+    left: 0;
+    background: #fff;
+    z-index: 9;
+    .search-box {
+      display: flex;
+      align-items: center;
+      padding-left: 20px;
+      width: 100%;
+      height: 70px;
+      border-radius: 35px;
+      background: #f6f6f6;
+      color: #8d8d8d;
+      font-size: 24px;
+    }
+  }
+  .content-top {
+    .report-title {
+      font-size: 34px;
+      font-weight: bold;
+      color: #4a4a4a;
+      margin: 60px 0 20px;
+    }
+    .report-text {
+      color: #999999;
+      font-size: 28px;
+      .seller-list {
+        margin: 20px 0;
+      }
+      .report_desc {
+        display: flex;
+        justify-content: space-between;
+      }
+      .tip {
+        color: #3385ff;
+      }
+      .container-abstract {
+        margin: 40px 0 20px;
+        padding-bottom: 40px;
+        border-bottom: 2px dashed #999;
+        position: relative;
+        line-height: 44px;
+        color: #333;
+        &::before {
+          content: "";
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 8px;
+          height: 44px;
+          background: #2a65f5;
+        }
+      }
+    }
+  }
+  .report-link {
+    font-size: 28px;
+    line-height: 80px;
+  }
+  .detail-report {
+    padding-bottom: 130px;
+    p,
+    span {
+      font-size: 28px !important;
+    }
+    img {
+      width: 100% !important;
+    }
+    a {
+      color: #333;
+    }
+    table {
+      border-collapse: collapse;
+      width: 100% !important;
+      margin-left: 0 !important;
+    }
+    tr td,
+    th {
+      border: 1px solid #333;
+    }
+  }
+  pre {
+    width: 100%;
+    overflow-y: auto;
+    overflow-x: hidden;
+    outline: none;
+    border: 0;
+    white-space: pre-wrap;
+    word-break: normal;
+  }
+  .btn-returntop {
+    position: fixed;
+    right: 40px;
+    bottom: 290px;
+    width: 88px;
+    height: 88px;
+    z-index: 10;
+  }
+  .fixed_cont {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    display: flex;
+    justify-content: space-around;
+    border-top: 1px solid #ddd;
+    padding-bottom: calc(5px + constant(safe-area-inset-bottom));
+    padding-bottom: calc(5px + env(safe-area-inset-bottom));
+    background-color: #fff;
+    z-index: 9;
+    box-sizing: border-box;
+    .handle-item {
+      padding-top: 14px;
+      text-align: center;
+      line-height: 33px;
+      font-size: 20px;
+      color: #888888;
+
+      img {
+        width: 44px;
+        height: 44px;
+        padding: 0;
+        margin: 0;
+      }
+      div {
+        padding: 0;
+        margin: 0;
+      }
+    }
+  }
+}
+.nodata {
+  width: 100%;
+  margin: 30% auto;
+  text-align: center;
+  img {
+    width: 50%;
+    margin-bottom: 30px;
+  }
+  p {
+    margin-top: 20px;
+  }
+}
+.no-cv {
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+#tutorial {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  opacity: 0.1;
+  // transform: translateX(130px) rotate(-38deg);
+}
+.van-dialog--round-button .van-dialog__footer {
+  border-top: 1px solid rgba(0, 0, 0, 0.1) !important;
+  padding: 0 !important;
+  .van-button--warning {
+    color: #333 !important;
+    border-right: 1px solid rgba(0, 0, 0, 0.1) !important;
+  }
+  .van-button--danger {
+    color: #3385ff !important;
+  }
+  .van-button--warning,
+  .van-button--danger {
+    height: 80px !important;
+    border-radius: 0 !important;
+  }
+  .van-goods-action-button--first {
+    border-right: 1px solid rgba(0, 0, 0, 0.1) !important;
+  }
+}
+.van-icon-search {
+  font-size: 28px;
+  margin-right: 10px;
+}
+</style>

+ 287 - 0
src/views/htgj/search.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="container-htgj-search" :class="isResult ? 'bg-content' : ''">
+    <div :class="isResult ? 'top-box' : ''">
+      <Search v-model="searchTxt" shape="round" clearable placeholder="请输入关键词" @clear="clearIpt" @search="searchHandle">
+        <template #right-icon>
+          <div class="search-p" @click="searchHandle"><span></span>搜索</div>
+        </template>
+      </Search>
+      <template v-if="isResult">
+        <Tabs v-model:active="orderColumn" title-active-color="#2C83FF" title-inactive-color="#707070" @click-tab="onClickTabs" line-width="85">
+          <Tab v-for="item in tabBars" :key="item.ChartPermissionId" :name="item.ChartPermissionId" :title="item.PermissionName"></Tab>
+        </Tabs>
+      </template>
+    </div>
+    <div class="search-cont">
+      <template v-if="!isResult">
+        <div class="search-cont-top" v-if="historySearchList.length">
+          <div class="cont-tit">
+            <p>搜索历史</p>
+            <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/empty_ico.png" class="empty_ico" @click="clearHistory" />
+          </div>
+          <div class="targetList">
+            <div class="target-item" v-for="(item, index) in historySearchList" :key="index" @click="chooseTarget(item)">{{ item }}</div>
+          </div>
+        </div>
+        <div class="search-cont-top">
+          <div class="cont-tit">
+            <p>弘则推荐</p>
+          </div>
+          <div class="targetList">
+            <div class="target-item" v-for="(item, index) in keywordList" :key="index" @click="chooseTarget(item)">{{ item }}</div>
+          </div>
+        </div>
+      </template>
+      <template v-else>
+        <PullRefresh v-model="refreshing" @refresh="onRefresh" v-if="haveResult">
+          <div class="data-cont">
+            <div class="report-ul">
+              <template v-for="(report, index) in resultList" :key="index">
+                <div class="report-item" v-if="index % 2 === 0" @click="goDetail(report)">
+                  <div class="item-content-img" v-if="report.BodyHtml">
+                    <img :src="report.BodyHtml" mode="" />
+                  </div>
+                  <div class="item-content" v-else v-html="report.Body"></div>
+                  <div class="line"></div>
+                  <text class="item-title" v-html="report.Title"></text>
+                  <div class="item-abstract text_twoLine" v-if="report.ExpertBackground">
+                    <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/fenxi_ico.png" class="report_ico" />
+                    {{ report.ExpertBackground }}
+                  </div>
+                  <div class="item-createtime">
+                    <text>{{ report.PublishDate }}</text>
+                    <div class="item-examine" v-if="report.IsResearch">
+                      <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/examine_icon.png" />
+                      <text>{{ report.Pv }}</text>
+                    </div>
+                  </div>
+                </div>
+              </template>
+            </div>
+            <div class="report-ul">
+              <template v-for="(report, index) in resultList" :key="index">
+                <div class="report-item" v-if="index % 2 != 0" @click="goDetail(report)">
+                  <div class="item-content-img" v-if="report.BodyHtml">
+                    <img :src="report.BodyHtml" />
+                  </div>
+                  <div class="item-content" v-else v-html="report.Body"></div>
+                  <div class="line"></div>
+                  <text class="item-title" v-html="report.Title"></text>
+                  <div class="item-abstract text_twoLine" v-if="report.ExpertBackground">
+                    <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/fenxi_ico.png" class="report_ico" />
+                    {{ report.ExpertBackground }}
+                  </div>
+                  <div class="item-createtime">
+                    <text>{{ report.PublishDate }}</text>
+                    <div class="item-examine" v-if="report.IsResearch">
+                      <img src="https://hzchart.oss-cn-shanghai.aliyuncs.com/cygx/czbk/examine_icon.png" />
+                      <text>{{ report.Pv }}</text>
+                    </div>
+                  </div>
+                </div>
+              </template>
+            </div>
+          </div>
+          <div class="finished-text" v-if="resultList.length">没有更多了</div>
+        </PullRefresh>
+        <div class="nodata" v-else>
+          <img src="@/assets/cygx/noauth.png" mode="" class="nodata_ico" />
+          <p>未找到您想搜索的内容</p>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { reactive, onMounted, toRefs, ref, watch } from "vue";
+import { Search, Toast, Tab, Tabs, PullRefresh } from "vant";
+import { report } from "@/api/htgj/api.js";
+import { useRoute, useRouter } from "vue-router";
+const router = useRouter();
+const searchState = reactive({
+  searchTxt: "", //搜索关键字
+  isResult: false, //显示搜索结果
+  haveResult: true, //是否有搜索数据
+  // 历史搜索列表
+  historySearchList: [],
+  // 关键字列表
+  keywordList: [],
+  targetList: [], //所有指标列表
+});
+/* 获取关键词 */
+const getKeyWord = async () => {
+  const res = await report.getKeys({});
+  if (res.Ret === 200) {
+    searchState.keywordList = res.Data.Item.ConfigValue ? res.Data.Item.ConfigValue.split(",") : [];
+  }
+};
+// 选择历史搜索 和推荐
+const chooseTarget = (item) => {
+  searchState.searchTxt = item;
+  getDataList();
+};
+
+const searchHandle = () => {
+  if (searchState.searchTxt) {
+    //添加搜索记录
+    if (!searchState.historySearchList.includes(searchState.searchTxt)) {
+      searchState.historySearchList.unshift(searchState.searchTxt);
+      localStorage.setItem("historySearchList", JSON.stringify(searchState.historySearchList));
+      dataState.resultList = [];
+      getDataList();
+    }
+  } else {
+    Toast("请输入关键字");
+  }
+};
+/* 表单清空 */
+const clearIpt = () => {
+  dataState.resultList = [];
+  dataState.orderColumn = "Matching";
+};
+/* 历史搜索清空 */
+const clearHistory = () => {
+  searchState.historySearchList = [];
+  localStorage.removeItem("historySearchList");
+};
+
+getKeyWord();
+const dataState = reactive({
+  resultList: [],
+  orderColumn: "Matching",
+  refreshing: false,
+  tabBars: [
+    {
+      PermissionName: "匹配度排序",
+      ChartPermissionId: "Matching",
+    },
+    {
+      PermissionName: "综合排序",
+      ChartPermissionId: "Comprehensive",
+    },
+    {
+      PermissionName: "发布时间排序",
+      ChartPermissionId: "PublishDate",
+    },
+  ],
+});
+
+// 查找数据
+const getDataList = async () => {
+  searchState.isResult = true;
+  const res = await report.getResult({
+    KeyWord: searchState.searchTxt,
+    OrderColumn: dataState.orderColumn,
+  });
+  if (res.Ret === 200) {
+    dataState.refreshing = false;
+    dataState.resultList = res.Data.List || [];
+    searchState.haveResult = dataState.resultList.length ? true : false;
+  }
+};
+
+/* 下拉刷新 */
+const onRefresh = async () => {
+  dataState.resultList = [];
+  getDataList();
+};
+
+/* tab点击 事件 */
+const onClickTabs = () => {
+  dataState.resultList = [];
+  getDataList();
+};
+
+/* 去往详情 */
+const goDetail = (item) => {
+  router.push({
+    path: "detail",
+    query: {
+      id: item.ArticleId,
+    },
+  });
+};
+
+watch(
+  () => searchState.searchTxt,
+  (newValue) => {
+    if (newValue.length <= 0) {
+      searchState.isResult = false;
+    }
+  }
+);
+onMounted(() => {
+  // 获取历史搜索记录
+  if (localStorage.getItem("historySearchList")) {
+    let historyList = JSON.parse(localStorage.getItem("historySearchList"));
+    searchState.historySearchList = historyList;
+  }
+});
+const { searchTxt, isResult, haveResult, historySearchList, keywordList, targetList } = toRefs(searchState);
+const { resultList, tabBars, orderColumn, refreshing } = toRefs(dataState);
+</script>
+
+<style lang="scss">
+.container-htgj-search {
+  @import "./index.scss";
+  .search-p {
+    display: flex;
+    align-items: center;
+    color: #3385ff;
+    span {
+      width: 0px;
+      height: 17px;
+      border: 1px solid #e0e0e0;
+      opacity: 1;
+      margin-right: 10px;
+    }
+  }
+  .search-cont-top {
+    padding: 0 34px 0;
+    margin-bottom: 10px;
+    padding-top: 20px;
+    &:last-child {
+      margin-bottom: 0;
+    }
+    .cont-tit {
+      color: #666;
+      font-size: 28px;
+      margin-bottom: 30px;
+      display: flex;
+      justify-content: space-between;
+      .empty_ico {
+        width: 32px;
+        height: 33px;
+      }
+    }
+    .targetList {
+      display: flex;
+      flex-wrap: wrap;
+      // justify-content: space-between;
+      .target-item {
+        padding: 4px 18px;
+        color: #4a4a4a;
+        font-size: 26px;
+        // border: 1px solid #3385ff;
+        background-color: #f7f7f7;
+        margin-bottom: 30px;
+        margin-right: 30px;
+        border-radius: 20px;
+      }
+    }
+  }
+  .finished-text {
+    width: 100%;
+    text-align: center;
+    padding: 30px;
+    color: #666;
+  }
+  .top-box {
+    padding: 0 0 10px !important;
+  }
+}
+.bg-content {
+  background: #f7f7f7 !important;
+}
+</style>