Browse Source

替换为新版本项目

jwyu 3 years ago
parent
commit
582d78d80d
57 changed files with 4015 additions and 0 deletions
  1. 6 0
      .env.development
  2. 6 0
      .env.product
  3. 6 0
      .env.test
  4. 8 0
      .gitignore
  5. 5 0
      README.md
  6. 14 0
      index.html
  7. 26 0
      package.json
  8. 21 0
      postcss.config.js
  9. 4 0
      src/App.vue
  10. 38 0
      src/api/cygx/api.js
  11. 80 0
      src/api/cygx/http.js
  12. 54 0
      src/api/hzyb/chart.js
  13. 787 0
      src/api/hzyb/crypto.js
  14. 92 0
      src/api/hzyb/http.js
  15. 14 0
      src/api/hzyb/report.js
  16. 10 0
      src/api/hzyb/user.js
  17. BIN
      src/assets/404.png
  18. BIN
      src/assets/cygx/apply_act.png
  19. BIN
      src/assets/cygx/apply_ico.png
  20. BIN
      src/assets/cygx/attention_act.png
  21. BIN
      src/assets/cygx/attention_ico.png
  22. BIN
      src/assets/cygx/collect_act.png
  23. BIN
      src/assets/cygx/collect_ico.png
  24. BIN
      src/assets/cygx/quiz_ico.png
  25. BIN
      src/assets/cygx/returntop.png
  26. BIN
      src/assets/hzyb/chart/before-next.png
  27. BIN
      src/assets/hzyb/chart/calendar.png
  28. BIN
      src/assets/hzyb/chart/refresh.png
  29. BIN
      src/assets/hzyb/chart/save.png
  30. BIN
      src/assets/hzyb/chart/search.png
  31. BIN
      src/assets/hzyb/daybanner.jpg
  32. BIN
      src/assets/hzyb/daytop.jpg
  33. BIN
      src/assets/hzyb/lianzi.png
  34. BIN
      src/assets/hzyb/monthbanner.jpg
  35. BIN
      src/assets/hzyb/moreother.png
  36. BIN
      src/assets/hzyb/mzsm.png
  37. BIN
      src/assets/hzyb/otherbanner.jpg
  38. BIN
      src/assets/hzyb/selected.png
  39. BIN
      src/assets/hzyb/shou.png
  40. BIN
      src/assets/hzyb/twoweekbanner.jpg
  41. BIN
      src/assets/hzyb/weekbanner.jpg
  42. BIN
      src/assets/hzyb/weektop.jpg
  43. 7 0
      src/main.js
  44. 7 0
      src/router/cygx/index.js
  45. 38 0
      src/router/hzyb/index.js
  46. 20 0
      src/router/index.js
  47. 17 0
      src/style/common.scss
  48. 21 0
      src/views/Error.vue
  49. 82 0
      src/views/cygx/dlg.vue
  50. 502 0
      src/views/cygx/raiReportDtl.vue
  51. 270 0
      src/views/hzyb/activity/reportDetail.vue
  52. 295 0
      src/views/hzyb/activity/reportWeekDetail.vue
  53. 1064 0
      src/views/hzyb/chart/Detail.vue
  54. 86 0
      src/views/hzyb/chart/Search.vue
  55. 167 0
      src/views/hzyb/chart/component/chartBox.vue
  56. 240 0
      src/views/hzyb/utils/highcahrts-zh_CN.js
  57. 28 0
      vite.config.js

+ 6 - 0
.env.development

@@ -0,0 +1,6 @@
+VITE_APP_BASE_URL="/xcx_h5"
+VITE_APP_OUTDIR="dist"
+
+VITE_APP_CYGX_BASEAPIURL="http://8.136.199.33:8500/api"
+
+VITE_APP_HZYB_BASEAPIURL="http://8.136.199.33:8612"

+ 6 - 0
.env.product

@@ -0,0 +1,6 @@
+VITE_APP_BASE_URL="/"
+VITE_APP_OUTDIR="raiwechat_link_h5"
+
+VITE_APP_CYGX_BASEAPIURL="https://cygx.hzinsights.com/api"
+
+VITE_APP_HZYB_BASEAPIURL="https://yanbao.hzinsights.com"

+ 6 - 0
.env.test

@@ -0,0 +1,6 @@
+VITE_APP_BASE_URL="/xcx_h5"
+VITE_APP_OUTDIR="dist"
+
+VITE_APP_CYGX_BASEAPIURL="http://8.136.199.33:8500/api"
+
+VITE_APP_HZYB_BASEAPIURL="http://8.136.199.33:8612"

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+raiwechat_link_h5
+*.local
+.vscode
+package-lock.json

+ 5 - 0
README.md

@@ -0,0 +1,5 @@
+# 小程序内部H5 项目
+注:此项目为弘则研究小程序中h5页面项目;可以同时多个项目;
+    每个项目请用项目命简称命名;如:hzyb(弘则研报小程序);
+    如需在本地存储信息,存储key请用项目简称开头如:hzyb_token;
+    路由请用项目简称开头如:/hzyb/report/detail

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
+    <title>弘则研究</title>
+    <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 26 - 0
package.json

@@ -0,0 +1,26 @@
+{
+  "name": "hongze_xcx_h5",
+  "version": "0.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build --mode product",
+    "build.test": "vite build --mode test",
+    "serve": "vite preview --port 3001"
+  },
+  "dependencies": {
+    "axios": "^0.24.0",
+    "highcharts": "^9.3.2",
+    "moment": "^2.29.1",
+    "normalize.css": "^8.0.1",
+    "vant": "^3.3.4",
+    "vue": "^3.2.16",
+    "vue-router": "^4.0.12"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^1.9.3",
+    "postcss-px-to-viewport": "^1.1.1",
+    "sass": "^1.44.0",
+    "vite": "^2.6.4",
+    "vite-plugin-style-import": "^1.4.0"
+  }
+}

+ 21 - 0
postcss.config.js

@@ -0,0 +1,21 @@
+module.exports = {
+  plugins: {
+    "postcss-px-to-viewport": {
+      unitToConvert: "px", // 需要转换的单位,默认为"px"
+      viewportWidth: 750, //  设计稿的视口宽度
+      unitPrecision: 5, // 单位转换后保留的精度
+      propList: ["*"], // 能转化为vw的属性列表
+      viewportUnit: "vw", //  希望使用的视口单位
+      // fontViewportUnit: "vw", // 字体使用的视口单位
+      selectorBlackList: [], // 需要忽略的CSS选择器
+      minPixelValue: 1, // 最小的转换数值,如果为1的话,只有大于1的值会被转换
+      mediaQuery: false, // 媒体查询里的单位是否需要转换单位
+      replace: true, // 是否直接更换属性值,而不添加备用属性
+      exclude: /node_modules/, // 忽略某些文件夹下的文件或特定文件
+      include: undefined, // 如果设置了include,那将只有匹配到的文件才会被转换,例如只转换 'src/mobile' 下的文件 (include: /\/src\/mobile\//)
+      landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
+      landscapeUnit: "vw", // 横屏时使用的单位
+      landscapeWidth: 568, // 横屏时使用的视口宽度
+    },
+  },
+};

+ 4 - 0
src/App.vue

@@ -0,0 +1,4 @@
+<template>
+  <router-view />
+</template>
+

+ 38 - 0
src/api/cygx/api.js

@@ -0,0 +1,38 @@
+import {get,post} from './http'
+
+/* 权益链接 */
+export const RaiApi = {
+    /* 获取详情 */
+    reportDtl: params => {
+      return get('/article/detail',params)
+    },
+    reportDtlTwo: params => {
+      return get('/tactics/detail',params)
+    },
+    /* 查看报告 */
+    lookReport: params => {
+      return get('/article/look/detail',params)
+    },
+    /* 收藏 ArticleId*/
+      collectRpt: params => {
+          return post('/article/collect',params,1)
+      },
+      /* 申请访谈 ArticleId*/
+      applyRpt: params => {
+          return post('/article/interview/apply',params,1)
+      },
+    /* 关注作者/取消关注作者 接口 DepartmentId*/
+      fllowDepartment: params => {
+          return post('/report/fllowDepartment',params,1)
+      },
+    /* 下载PDF打水印接口*/
+      articlePdfwatermark: params => {
+          return get('/article/pdfwatermark',params,'下载')
+      },
+    /* 页面复制监听*/
+    pageHistoryCopy: params => {
+          return post('/config/pageHistory',params,1)
+      },
+  
+  
+  }

+ 80 - 0
src/api/cygx/http.js

@@ -0,0 +1,80 @@
+"use strict";
+import axios from "axios";
+import {Toast} from 'vant'
+
+// 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_CYGX_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
+    let auth = localStorage.getItem("access_token") || "";
+    if (auth) {
+      config.headers.Authorization = auth;
+    }
+    // 设置loading
+    // if (LOADINGCOUNT === 0) {
+    //   LOADING = Toast.loading({
+    //     message: "loading...",
+    //     duration: 0,
+    //     forbidClick: true,
+    //   });
+    // }
+    // LOADINGCOUNT++;
+    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) {
+    // 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)
+};

+ 54 - 0
src/api/hzyb/chart.js

@@ -0,0 +1,54 @@
+//图库模块
+
+import {get,post} from './http'
+
+/**
+ * 图表详情
+ * @param ChartInfoId 图表ID
+ * @param DateType 时段筛选:1-00年至今; 2-10年至今; 3-15年至今; 4-21年至今; 5-指定区间; 6-指定年月至今; 7-18年至今; 8-19年至今; 9-20年至今
+ * @param startDate 自定义开始日期
+ * @param endDate 自定义结束日期
+ * @param SeasonStartDate 季节性图表-开始日期
+ * @param SeasonEndDate 季节性图表-结束日期
+ * @param EdbInfoId 指标ID,多个用英文逗号隔开
+ * @param Calendar 公历/农历
+ * @param ChartType 图表样式 1-曲线图 2-季节性图表
+ */
+export const apiChartInfo=params=>{
+    return get('/my_chart/getChartDetail',params)
+}
+
+/**
+ * 图表列表
+ * @param Keywords
+ * @param ClassifyId
+ * @param Page
+ * @param Limit
+ */
+export const apiChartList=params=>{
+    return get('/my_chart/getChartList',params)
+}
+
+/**
+ * 获取当前图表前一个\后一个 
+ * @param MyChartId 当前图表的MyChartId
+ * @param ChartInfoId 
+ */
+export const apiChartBeforeAndNext=params=>{
+    return get('/my_chart/getChartBeforeAndNext',params)
+}
+
+/**
+ * 保存图表
+ */
+export const apiChartSave=params=>{
+    return post('/my_chart/editChartInfo',params)
+}
+
+/**
+ * 刷新图表
+ * @param ChartInfoId
+ */
+export const apiChartRefresh=params=>{
+    return post('/my_chart/refreshChartInfo',params)
+}

+ 787 - 0
src/api/hzyb/crypto.js

@@ -0,0 +1,787 @@
+const key = 'zDeESsxsXuionhqSLZYHWcDJ';
+
+class CryptoJS {
+	// 3DES加密,CBC/PKCS5Padding
+	static Des3Encrypt (input) {
+		let genKey = genkey(key, 0, 24);
+		return base64encode(des(genKey.key, input, 1, 1, key.substr(0, 8), 1));
+	}
+
+	// 3DES解密,CBC/PKCS5Padding
+	static Des3Decrypt (input) {
+		let genKey = genkey(key, 0, 24);
+		return des(genKey.key, base64decode(input), 0, 1, key.substr(0, 8), 1);
+	}
+
+	// md5
+	static Md5Encrypt (input) {
+		return md5(String(input));
+	}
+}
+
+function des (key, message, encrypt, mode, iv, padding) {
+	if (encrypt) // 如果是加密的话,首先转换编码
+	{ message = unescape(encodeURIComponent(message)); }
+	// declaring this locally speeds things up a bit
+	let spfunction1 = new Array(0x1010400, 0, 0x10000, 0x1010404, 0x1010004, 0x10404, 0x4, 0x10000, 0x400, 0x1010400, 0x1010404, 0x400, 0x1000404, 0x1010004, 0x1000000, 0x4, 0x404, 0x1000400, 0x1000400, 0x10400, 0x10400, 0x1010000, 0x1010000, 0x1000404, 0x10004, 0x1000004, 0x1000004, 0x10004, 0, 0x404, 0x10404, 0x1000000, 0x10000, 0x1010404, 0x4, 0x1010000, 0x1010400, 0x1000000, 0x1000000, 0x400, 0x1010004, 0x10000, 0x10400, 0x1000004, 0x400, 0x4, 0x1000404, 0x10404, 0x1010404, 0x10004, 0x1010000, 0x1000404, 0x1000004, 0x404, 0x10404, 0x1010400, 0x404, 0x1000400, 0x1000400, 0, 0x10004, 0x10400, 0, 0x1010004);
+	let spfunction2 = new Array(-0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0, -0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x80000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020, -0x7fff7fe0, 0, -0x80000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000, -0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000, -0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x80000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0, 0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x80000000, -0x7fefffe0, -0x7fef7fe0, 0x108000);
+	let spfunction3 = new Array(0x208, 0x8020200, 0, 0x8020008, 0x8000200, 0, 0x20208, 0x8000200, 0x20008, 0x8000008, 0x8000008, 0x20000, 0x8020208, 0x20008, 0x8020000, 0x208, 0x8000000, 0x8, 0x8020200, 0x200, 0x20200, 0x8020000, 0x8020008, 0x20208, 0x8000208, 0x20200, 0x20000, 0x8000208, 0x8, 0x8020208, 0x200, 0x8000000, 0x8020200, 0x8000000, 0x20008, 0x208, 0x20000, 0x8020200, 0x8000200, 0, 0x200, 0x20008, 0x8020208, 0x8000200, 0x8000008, 0x200, 0, 0x8020008, 0x8000208, 0x20000, 0x8000000, 0x8020208, 0x8, 0x20208, 0x20200, 0x8000008, 0x8020000, 0x8000208, 0x208, 0x8020000, 0x20208, 0x8, 0x8020008, 0x20200);
+	let spfunction4 = new Array(0x802001, 0x2081, 0x2081, 0x80, 0x802080, 0x800081, 0x800001, 0x2001, 0, 0x802000, 0x802000, 0x802081, 0x81, 0, 0x800080, 0x800001, 0x1, 0x2000, 0x800000, 0x802001, 0x80, 0x800000, 0x2001, 0x2080, 0x800081, 0x1, 0x2080, 0x800080, 0x2000, 0x802080, 0x802081, 0x81, 0x800080, 0x800001, 0x802000, 0x802081, 0x81, 0, 0, 0x802000, 0x2080, 0x800080, 0x800081, 0x1, 0x802001, 0x2081, 0x2081, 0x80, 0x802081, 0x81, 0x1, 0x2000, 0x800001, 0x2001, 0x802080, 0x800081, 0x2001, 0x2080, 0x800000, 0x802001, 0x80, 0x800000, 0x2000, 0x802080);
+	let spfunction5 = new Array(0x100, 0x2080100, 0x2080000, 0x42000100, 0x80000, 0x100, 0x40000000, 0x2080000, 0x40080100, 0x80000, 0x2000100, 0x40080100, 0x42000100, 0x42080000, 0x80100, 0x40000000, 0x2000000, 0x40080000, 0x40080000, 0, 0x40000100, 0x42080100, 0x42080100, 0x2000100, 0x42080000, 0x40000100, 0, 0x42000000, 0x2080100, 0x2000000, 0x42000000, 0x80100, 0x80000, 0x42000100, 0x100, 0x2000000, 0x40000000, 0x2080000, 0x42000100, 0x40080100, 0x2000100, 0x40000000, 0x42080000, 0x2080100, 0x40080100, 0x100, 0x2000000, 0x42080000, 0x42080100, 0x80100, 0x42000000, 0x42080100, 0x2080000, 0, 0x40080000, 0x42000000, 0x80100, 0x2000100, 0x40000100, 0x80000, 0, 0x40080000, 0x2080100, 0x40000100);
+	let spfunction6 = new Array(0x20000010, 0x20400000, 0x4000, 0x20404010, 0x20400000, 0x10, 0x20404010, 0x400000, 0x20004000, 0x404010, 0x400000, 0x20000010, 0x400010, 0x20004000, 0x20000000, 0x4010, 0, 0x400010, 0x20004010, 0x4000, 0x404000, 0x20004010, 0x10, 0x20400010, 0x20400010, 0, 0x404010, 0x20404000, 0x4010, 0x404000, 0x20404000, 0x20000000, 0x20004000, 0x10, 0x20400010, 0x404000, 0x20404010, 0x400000, 0x4010, 0x20000010, 0x400000, 0x20004000, 0x20000000, 0x4010, 0x20000010, 0x20404010, 0x404000, 0x20400000, 0x404010, 0x20404000, 0, 0x20400010, 0x10, 0x4000, 0x20400000, 0x404010, 0x4000, 0x400010, 0x20004010, 0, 0x20404000, 0x20000000, 0x400010, 0x20004010);
+	let spfunction7 = new Array(0x200000, 0x4200002, 0x4000802, 0, 0x800, 0x4000802, 0x200802, 0x4200800, 0x4200802, 0x200000, 0, 0x4000002, 0x2, 0x4000000, 0x4200002, 0x802, 0x4000800, 0x200802, 0x200002, 0x4000800, 0x4000002, 0x4200000, 0x4200800, 0x200002, 0x4200000, 0x800, 0x802, 0x4200802, 0x200800, 0x2, 0x4000000, 0x200800, 0x4000000, 0x200800, 0x200000, 0x4000802, 0x4000802, 0x4200002, 0x4200002, 0x2, 0x200002, 0x4000000, 0x4000800, 0x200000, 0x4200800, 0x802, 0x200802, 0x4200800, 0x802, 0x4000002, 0x4200802, 0x4200000, 0x200800, 0, 0x2, 0x4200802, 0, 0x200802, 0x4200000, 0x800, 0x4000002, 0x4000800, 0x800, 0x200002);
+	let spfunction8 = new Array(0x10001040, 0x1000, 0x40000, 0x10041040, 0x10000000, 0x10001040, 0x40, 0x10000000, 0x40040, 0x10040000, 0x10041040, 0x41000, 0x10041000, 0x41040, 0x1000, 0x40, 0x10040000, 0x10000040, 0x10001000, 0x1040, 0x41000, 0x40040, 0x10040040, 0x10041000, 0x1040, 0, 0, 0x10040040, 0x10000040, 0x10001000, 0x41040, 0x40000, 0x41040, 0x40000, 0x10041000, 0x1000, 0x40, 0x10040040, 0x1000, 0x41040, 0x10001000, 0x40, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x40000, 0x10001040, 0, 0x10041040, 0x40040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0, 0x10041040, 0x41000, 0x41000, 0x1040, 0x1040, 0x40040, 0x10000000, 0x10041000);
+
+	// create the 16 or 48 subkeys we will need
+	let keys = des_createKeys(key);
+
+	let m = 0, i, j, temp, temp2, right1, right2, left, right, looping;
+	let cbcleft, cbcleft2, cbcright, cbcright2;
+	let endloop, loopinc;
+	var len = message.length;
+	let chunk = 0;
+	// set up the loops for single and triple des
+	let iterations = keys.length === 32 ? 3 : 9; // single or triple des
+	if (iterations === 3) { looping = encrypt ? new Array(0, 32, 2) : new Array(30, -2, -2); } else { looping = encrypt ? new Array(0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array(94, 62, -2, 32, 64, 2, 30, -2, -2); }
+
+	// pad the message depending on the padding parameter
+	if (padding === 2) message += '        '; // pad the message with spaces
+	else if (padding === 1) {
+		if (encrypt) {
+			temp = 8 - (len % 8);
+			message += String.fromCharCode(temp, temp, temp, temp, temp, temp, temp, temp);
+			if (temp === 8) len += 8;
+		}
+	} // PKCS7 padding
+	else if (!padding) message += '\0\0\0\0\0\0\0\0'; // pad the message out with null bytes
+
+	// store the result here
+	let result = '';
+	let tempresult = '';
+
+	if (mode === 1) { // CBC mode
+		cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
+		cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
+		m = 0;
+	}
+
+	// loop through each 64 bit chunk of the message
+	while (m < len) {
+		left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
+		right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
+
+		// for Cipher Block Chaining mode, xor the message with the previous result
+		if (mode === 1) {
+			if (encrypt) {
+				left ^= cbcleft;
+				right ^= cbcright;
+			} else {
+				cbcleft2 = cbcleft;
+				cbcright2 = cbcright;
+				cbcleft = left;
+				cbcright = right;
+			}
+		}
+
+		// first each 64 but chunk of the message must be permuted according to IP
+		temp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+		right ^= temp;
+		left ^= (temp << 4);
+		temp = ((left >>> 16) ^ right) & 0x0000ffff;
+		right ^= temp;
+		left ^= (temp << 16);
+		temp = ((right >>> 2) ^ left) & 0x33333333;
+		left ^= temp;
+		right ^= (temp << 2);
+		temp = ((right >>> 8) ^ left) & 0x00ff00ff;
+		left ^= temp;
+		right ^= (temp << 8);
+		temp = ((left >>> 1) ^ right) & 0x55555555;
+		right ^= temp;
+		left ^= (temp << 1);
+
+		left = ((left << 1) | (left >>> 31));
+		right = ((right << 1) | (right >>> 31));
+
+		// do this either 1 or 3 times for each chunk of the message
+		for (j = 0; j < iterations; j += 3) {
+			endloop = looping[j + 1];
+			loopinc = looping[j + 2];
+			// now go through and perform the encryption or decryption
+			for (i = looping[j]; i !== endloop; i += loopinc) { // for efficiency
+				right1 = right ^ keys[i];
+				right2 = ((right >>> 4) | (right << 28)) ^ keys[i + 1];
+				// the result is attained by passing these bytes through the S selection functions
+				temp = left;
+				left = right;
+				right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f] |
+					spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f] |
+					spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f] |
+					spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]);
+			}
+			temp = left;
+			left = right;
+			right = temp; // unreverse left and right
+		} // for either 1 or 3 iterations
+
+		// move then each one bit to the right
+		left = ((left >>> 1) | (left << 31));
+		right = ((right >>> 1) | (right << 31));
+
+		// now perform IP-1, which is IP in the opposite direction
+		temp = ((left >>> 1) ^ right) & 0x55555555;
+		right ^= temp;
+		left ^= (temp << 1);
+		temp = ((right >>> 8) ^ left) & 0x00ff00ff;
+		left ^= temp;
+		right ^= (temp << 8);
+		temp = ((right >>> 2) ^ left) & 0x33333333;
+		left ^= temp;
+		right ^= (temp << 2);
+		temp = ((left >>> 16) ^ right) & 0x0000ffff;
+		right ^= temp;
+		left ^= (temp << 16);
+		temp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+		right ^= temp;
+		left ^= (temp << 4);
+
+		// for Cipher Block Chaining mode, xor the message with the previous result
+		if (mode === 1) {
+			if (encrypt) {
+				cbcleft = left;
+				cbcright = right;
+			} else {
+				left ^= cbcleft2;
+				right ^= cbcright2;
+			}
+		}
+		tempresult += String.fromCharCode((left >>> 24), ((left >>> 16) & 0xff), ((left >>> 8) & 0xff), (left & 0xff), (right >>> 24), ((right >>> 16) & 0xff), ((right >>> 8) & 0xff), (right & 0xff));
+
+		chunk += 8;
+		if (chunk === 512) {
+			result += tempresult;
+			tempresult = '';
+			chunk = 0;
+		}
+	} // for every 8 characters, or 64 bits in the message
+
+	// return the result as an array
+	result += tempresult;
+	result = result.replace(/\0*$/g, '');
+
+	if (!encrypt) { // 如果是解密的话,解密结束后对PKCS7 padding进行解码,并转换成utf-8编码
+		if (padding === 1) { // PKCS7 padding解码
+			var len = result.length, paddingChars = 0;
+			len && (paddingChars = result.charCodeAt(len - 1));
+			(paddingChars <= 8) && (result = result.substring(0, len - paddingChars));
+		}
+		// 转换成UTF-8编码
+		result = decodeURIComponent(escape(result));
+	}
+
+	return result;
+} // end of des
+
+// des_createKeys
+// this takes as input a 64 bit key (even though only 56 bits are used)
+// as an array of 2 integers, and returns 16 48 bit keys
+function des_createKeys (key) {
+	// declaring this locally speeds things up a bit
+	let pc2bytes0 = new Array(0, 0x4, 0x20000000, 0x20000004, 0x10000, 0x10004, 0x20010000, 0x20010004, 0x200, 0x204, 0x20000200, 0x20000204, 0x10200, 0x10204, 0x20010200, 0x20010204);
+	let pc2bytes1 = new Array(0, 0x1, 0x100000, 0x100001, 0x4000000, 0x4000001, 0x4100000, 0x4100001, 0x100, 0x101, 0x100100, 0x100101, 0x4000100, 0x4000101, 0x4100100, 0x4100101);
+	let pc2bytes2 = new Array(0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808, 0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808);
+	let pc2bytes3 = new Array(0, 0x200000, 0x8000000, 0x8200000, 0x2000, 0x202000, 0x8002000, 0x8202000, 0x20000, 0x220000, 0x8020000, 0x8220000, 0x22000, 0x222000, 0x8022000, 0x8222000);
+	let pc2bytes4 = new Array(0, 0x40000, 0x10, 0x40010, 0, 0x40000, 0x10, 0x40010, 0x1000, 0x41000, 0x1010, 0x41010, 0x1000, 0x41000, 0x1010, 0x41010);
+	let pc2bytes5 = new Array(0, 0x400, 0x20, 0x420, 0, 0x400, 0x20, 0x420, 0x2000000, 0x2000400, 0x2000020, 0x2000420, 0x2000000, 0x2000400, 0x2000020, 0x2000420);
+	let pc2bytes6 = new Array(0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002, 0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002);
+	let pc2bytes7 = new Array(0, 0x10000, 0x800, 0x10800, 0x20000000, 0x20010000, 0x20000800, 0x20010800, 0x20000, 0x30000, 0x20800, 0x30800, 0x20020000, 0x20030000, 0x20020800, 0x20030800);
+	let pc2bytes8 = new Array(0, 0x40000, 0, 0x40000, 0x2, 0x40002, 0x2, 0x40002, 0x2000000, 0x2040000, 0x2000000, 0x2040000, 0x2000002, 0x2040002, 0x2000002, 0x2040002);
+	let pc2bytes9 = new Array(0, 0x10000000, 0x8, 0x10000008, 0, 0x10000000, 0x8, 0x10000008, 0x400, 0x10000400, 0x408, 0x10000408, 0x400, 0x10000400, 0x408, 0x10000408);
+	let pc2bytes10 = new Array(0, 0x20, 0, 0x20, 0x100000, 0x100020, 0x100000, 0x100020, 0x2000, 0x2020, 0x2000, 0x2020, 0x102000, 0x102020, 0x102000, 0x102020);
+	let pc2bytes11 = new Array(0, 0x1000000, 0x200, 0x1000200, 0x200000, 0x1200000, 0x200200, 0x1200200, 0x4000000, 0x5000000, 0x4000200, 0x5000200, 0x4200000, 0x5200000, 0x4200200, 0x5200200);
+	let pc2bytes12 = new Array(0, 0x1000, 0x8000000, 0x8001000, 0x80000, 0x81000, 0x8080000, 0x8081000, 0x10, 0x1010, 0x8000010, 0x8001010, 0x80010, 0x81010, 0x8080010, 0x8081010);
+	let pc2bytes13 = new Array(0, 0x4, 0x100, 0x104, 0, 0x4, 0x100, 0x104, 0x1, 0x5, 0x101, 0x105, 0x1, 0x5, 0x101, 0x105);
+
+	// how many iterations (1 for des, 3 for triple des)
+	let iterations = key.length > 8 ? 3 : 1; // changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
+	// stores the return keys
+	let keys = new Array(32 * iterations);
+	// now define the left shifts which need to be done
+	let shifts = new Array(0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0);
+	// other variables
+	let lefttemp, righttemp, m = 0, n = 0, temp;
+
+	for (let j = 0; j < iterations; j++) { // either 1 or 3 iterations
+		let left = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
+		let right = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
+
+		temp = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+		right ^= temp;
+		left ^= (temp << 4);
+		temp = ((right >>> -16) ^ left) & 0x0000ffff;
+		left ^= temp;
+		right ^= (temp << -16);
+		temp = ((left >>> 2) ^ right) & 0x33333333;
+		right ^= temp;
+		left ^= (temp << 2);
+		temp = ((right >>> -16) ^ left) & 0x0000ffff;
+		left ^= temp;
+		right ^= (temp << -16);
+		temp = ((left >>> 1) ^ right) & 0x55555555;
+		right ^= temp;
+		left ^= (temp << 1);
+		temp = ((right >>> 8) ^ left) & 0x00ff00ff;
+		left ^= temp;
+		right ^= (temp << 8);
+		temp = ((left >>> 1) ^ right) & 0x55555555;
+		right ^= temp;
+		left ^= (temp << 1);
+
+		// the right side needs to be shifted and to get the last four bits of the left side
+		temp = (left << 8) | ((right >>> 20) & 0x000000f0);
+		// left needs to be put upside down
+		left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
+		right = temp;
+
+		// now go through and perform these shifts on the left and right keys
+		for (let i = 0; i < shifts.length; i++) {
+			// shift the keys either one or two bits to the left
+			if (shifts[i]) {
+				left = (left << 2) | (left >>> 26);
+				right = (right << 2) | (right >>> 26);
+			} else {
+				left = (left << 1) | (left >>> 27);
+				right = (right << 1) | (right >>> 27);
+			}
+			left &= -0xf;
+			right &= -0xf;
+
+			// now apply PC-2, in such a way that E is easier when encrypting or decrypting
+			// this conversion will look like PC-2 except only the last 6 bits of each byte are used
+			// rather than 48 consecutive bits and the order of lines will be according to
+			// how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
+			lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] |
+				pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf] |
+				pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] |
+				pc2bytes6[(left >>> 4) & 0xf];
+			righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] |
+				pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf] |
+				pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] |
+				pc2bytes13[(right >>> 4) & 0xf];
+			temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff;
+			keys[n++] = lefttemp ^ temp;
+			keys[n++] = righttemp ^ (temp << 16);
+		}
+	} // for each iterations
+	// return the keys we've created
+	return keys;
+} // end of des_createKeys
+function genkey (key, start, end) {
+	// 8 byte / 64 bit Key (DES) or 192 bit Key
+	return { key: pad(key.slice(start, end)), vector: 1 };
+}
+function pad (key) {
+	for (let i = key.length; i < 24; i++) {
+		key += '0';
+	}
+	return key;
+}
+
+let BASE64_MAPPING = [
+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+	'w', 'x', 'y', 'z', '0', '1', '2', '3',
+	'4', '5', '6', '7', '8', '9', '+', '/'
+];
+
+/**
+ *ascii convert to binary
+ */
+let _toBinary = function (ascii) {
+	let binary = new Array();
+	while (ascii > 0) {
+		let b = ascii % 2;
+		ascii = Math.floor(ascii / 2);
+		binary.push(b);
+	}
+	/*
+	 var len = binary.length;
+	 if(6-len > 0){
+	 for(var i = 6-len ; i > 0 ; --i){
+	 binary.push(0);
+	 }
+	 } */
+	binary.reverse();
+	return binary;
+};
+
+/**
+ *binary convert to decimal
+ */
+let _toDecimal = function (binary) {
+	let dec = 0;
+	let p = 0;
+	for (let i = binary.length - 1; i >= 0; --i) {
+		let b = binary[i];
+		if (b === 1) {
+			dec += Math.pow(2, p);
+		}
+		++p;
+	}
+	return dec;
+};
+
+/**
+ *unicode convert to utf-8
+ */
+let _toUTF8Binary = function (c, binaryArray) {
+	let mustLen = (8 - (c + 1)) + ((c - 1) * 6);
+	let fatLen = binaryArray.length;
+	let diff = mustLen - fatLen;
+	while (--diff >= 0) {
+		binaryArray.unshift(0);
+	}
+	let binary = [];
+	let _c = c;
+	while (--_c >= 0) {
+		binary.push(1);
+	}
+	binary.push(0);
+	let i = 0, len = 8 - (c + 1);
+	for (; i < len; ++i) {
+		binary.push(binaryArray[i]);
+	}
+
+	for (let j = 0; j < c - 1; ++j) {
+		binary.push(1);
+		binary.push(0);
+		let sum = 6;
+		while (--sum >= 0) {
+			binary.push(binaryArray[i++]);
+		}
+	}
+	return binary;
+};
+
+let BASE64 = {
+	/**
+	 *BASE64 Encode
+	 */
+	encoder: function (str) {
+		let base64_Index = [];
+		let binaryArray = [];
+		for (var i = 0, len = str.length; i < len; ++i) {
+			let unicode = str.charCodeAt(i);
+			let _tmpBinary = _toBinary(unicode);
+			if (unicode < 0x80) {
+				let _tmpdiff = 8 - _tmpBinary.length;
+				while (--_tmpdiff >= 0) {
+					_tmpBinary.unshift(0);
+				}
+				binaryArray = binaryArray.concat(_tmpBinary);
+			} else if (unicode >= 0x80 && unicode <= 0x7FF) {
+				binaryArray = binaryArray.concat(_toUTF8Binary(2, _tmpBinary));
+			} else if (unicode >= 0x800 && unicode <= 0xFFFF) { // UTF-8 3byte
+				binaryArray = binaryArray.concat(_toUTF8Binary(3, _tmpBinary));
+			} else if (unicode >= 0x10000 && unicode <= 0x1FFFFF) { // UTF-8 4byte
+				binaryArray = binaryArray.concat(_toUTF8Binary(4, _tmpBinary));
+			} else if (unicode >= 0x200000 && unicode <= 0x3FFFFFF) { // UTF-8 5byte
+				binaryArray = binaryArray.concat(_toUTF8Binary(5, _tmpBinary));
+			} else if (unicode >= 4000000 && unicode <= 0x7FFFFFFF) { // UTF-8 6byte
+				binaryArray = binaryArray.concat(_toUTF8Binary(6, _tmpBinary));
+			}
+		}
+
+		let extra_Zero_Count = 0;
+		for (var i = 0, len = binaryArray.length; i < len; i += 6) {
+			let diff = (i + 6) - len;
+			if (diff === 2) {
+				extra_Zero_Count = 2;
+			} else if (diff === 4) {
+				extra_Zero_Count = 4;
+			}
+			// if(extra_Zero_Count > 0){
+			//  len += extra_Zero_Count+1;
+			// }
+			let _tmpExtra_Zero_Count = extra_Zero_Count;
+			while (--_tmpExtra_Zero_Count >= 0) {
+				binaryArray.push(0);
+			}
+			base64_Index.push(_toDecimal(binaryArray.slice(i, i + 6)));
+		}
+
+		let base64 = '';
+		for (var i = 0, len = base64_Index.length; i < len; ++i) {
+			base64 += BASE64_MAPPING[base64_Index[i]];
+		}
+
+		for (var i = 0, len = extra_Zero_Count / 2; i < len; ++i) {
+			base64 += '=';
+		}
+		return base64;
+	},
+	/**
+	 *BASE64  Decode for UTF-8
+	 */
+	decoder: function (_base64Str) {
+		let _len = _base64Str.length;
+		let extra_Zero_Count = 0;
+		/**
+		 *计算在进行BASE64编码的时候,补了几个0
+		 */
+		if (_base64Str.charAt(_len - 1) === '=') {
+			// alert(_base64Str.charAt(_len-1));
+			// alert(_base64Str.charAt(_len-2));
+			if (_base64Str.charAt(_len - 2) === '=') { // 两个等号说明补了4个0
+				extra_Zero_Count = 4;
+				_base64Str = _base64Str.substring(0, _len - 2);
+			} else { // 一个等号说明补了2个0
+				extra_Zero_Count = 2;
+				_base64Str = _base64Str.substring(0, _len - 1);
+			}
+		}
+
+		let binaryArray = [];
+		for (var i = 0, len = _base64Str.length; i < len; ++i) {
+			let c = _base64Str.charAt(i);
+			for (let j = 0, size = BASE64_MAPPING.length; j < size; ++j) {
+				if (c === BASE64_MAPPING[j]) {
+					let _tmp = _toBinary(j);
+					/* 不足6位的补0 */
+					let _tmpLen = _tmp.length;
+					if (6 - _tmpLen > 0) {
+						for (let k = 6 - _tmpLen; k > 0; --k) {
+							_tmp.unshift(0);
+						}
+					}
+					binaryArray = binaryArray.concat(_tmp);
+					break;
+				}
+			}
+		}
+
+		if (extra_Zero_Count > 0) {
+			binaryArray = binaryArray.slice(0, binaryArray.length - extra_Zero_Count);
+		}
+
+		let unicode = [];
+		let unicodeBinary = [];
+		for (var i = 0, len = binaryArray.length; i < len;) {
+			if (binaryArray[i] === 0) {
+				unicode = unicode.concat(_toDecimal(binaryArray.slice(i, i + 8)));
+				i += 8;
+			} else {
+				let sum = 0;
+				while (i < len) {
+					if (binaryArray[i] === 1) {
+						++sum;
+					} else {
+						break;
+					}
+					++i;
+				}
+				unicodeBinary = unicodeBinary.concat(binaryArray.slice(i + 1, i + 8 - sum));
+				i += 8 - sum;
+				while (sum > 1) {
+					unicodeBinary = unicodeBinary.concat(binaryArray.slice(i + 2, i + 8));
+					i += 8;
+					--sum;
+				}
+				unicode = unicode.concat(_toDecimal(unicodeBinary));
+				unicodeBinary = [];
+			}
+		}
+		// ---------直接转换为结果
+		let strResult = '';
+		for (var i = 0, len = unicode.length; i < len; ++i) {
+			strResult += String.fromCharCode(unicode[i]);
+		}
+		return strResult;
+	}
+};
+
+let rotateLeft = function (lValue, iShiftBits) {
+	return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
+};
+
+let addUnsigned = function (lX, lY) {
+	let lX4, lY4, lX8, lY8, lResult;
+	lX8 = (lX & 0x80000000);
+	lY8 = (lY & 0x80000000);
+	lX4 = (lX & 0x40000000);
+	lY4 = (lY & 0x40000000);
+	lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
+	if (lX4 & lY4) return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
+	if (lX4 | lY4) {
+		if (lResult & 0x40000000) return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
+		else return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
+	} else {
+		return (lResult ^ lX8 ^ lY8);
+	}
+};
+
+let F = function (x, y, z) {
+	return (x & y) | ((~x) & z);
+};
+
+let G = function (x, y, z) {
+	return (x & z) | (y & (~z));
+};
+
+let H = function (x, y, z) {
+	return (x ^ y ^ z);
+};
+
+let I = function (x, y, z) {
+	return (y ^ (x | (~z)));
+};
+
+let FF = function (a, b, c, d, x, s, ac) {
+	a = addUnsigned(a, addUnsigned(addUnsigned(F(b, c, d), x), ac));
+	return addUnsigned(rotateLeft(a, s), b);
+};
+
+let GG = function (a, b, c, d, x, s, ac) {
+	a = addUnsigned(a, addUnsigned(addUnsigned(G(b, c, d), x), ac));
+	return addUnsigned(rotateLeft(a, s), b);
+};
+
+let HH = function (a, b, c, d, x, s, ac) {
+	a = addUnsigned(a, addUnsigned(addUnsigned(H(b, c, d), x), ac));
+	return addUnsigned(rotateLeft(a, s), b);
+};
+
+let II = function (a, b, c, d, x, s, ac) {
+	a = addUnsigned(a, addUnsigned(addUnsigned(I(b, c, d), x), ac));
+	return addUnsigned(rotateLeft(a, s), b);
+};
+
+let convertToWordArray = function (string) {
+	let lWordCount;
+	let lMessageLength = string.length;
+	let lNumberOfWordsTempOne = lMessageLength + 8;
+	let lNumberOfWordsTempTwo = (lNumberOfWordsTempOne - (lNumberOfWordsTempOne % 64)) / 64;
+	let lNumberOfWords = (lNumberOfWordsTempTwo + 1) * 16;
+	let lWordArray = Array(lNumberOfWords - 1);
+	let lBytePosition = 0;
+	let lByteCount = 0;
+	while (lByteCount < lMessageLength) {
+		lWordCount = (lByteCount - (lByteCount % 4)) / 4;
+		lBytePosition = (lByteCount % 4) * 8;
+		lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
+		lByteCount++;
+	}
+	lWordCount = (lByteCount - (lByteCount % 4)) / 4;
+	lBytePosition = (lByteCount % 4) * 8;
+	lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
+	lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
+	lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
+	return lWordArray;
+};
+
+let wordToHex = function (lValue) {
+	let WordToHexValue = '', WordToHexValueTemp = '', lByte, lCount;
+	for (lCount = 0; lCount <= 3; lCount++) {
+		lByte = (lValue >>> (lCount * 8)) & 255;
+		WordToHexValueTemp = '0' + lByte.toString(16);
+		WordToHexValue = WordToHexValue + WordToHexValueTemp.substr(WordToHexValueTemp.length - 2, 2);
+	}
+	return WordToHexValue;
+};
+
+let uTF8Encode = function (str) {
+	str = str.replace(/\x0d\x0a/g, '\x0a');
+	let output = '';
+	for (let n = 0; n < str.length; n++) {
+		let c = str.charCodeAt(n);
+		if (c < 128) {
+			output += String.fromCharCode(c);
+		} else if ((c > 127) && (c < 2048)) {
+			output += String.fromCharCode((c >> 6) | 192);
+			output += String.fromCharCode((c & 63) | 128);
+		} else {
+			output += String.fromCharCode((c >> 12) | 224);
+			output += String.fromCharCode(((c >> 6) & 63) | 128);
+			output += String.fromCharCode((c & 63) | 128);
+		}
+	}
+	return output;
+};
+
+var md5 = function (string) {
+	let x = Array();
+	let k, AA, BB, CC, DD, a, b, c, d;
+	let S11 = 7, S12 = 12, S13 = 17, S14 = 22;
+	let S21 = 5, S22 = 9, S23 = 14, S24 = 20;
+	let S31 = 4, S32 = 11, S33 = 16, S34 = 23;
+	let S41 = 6, S42 = 10, S43 = 15, S44 = 21;
+	string = uTF8Encode(string);
+	x = convertToWordArray(string);
+	a = 0x67452301;
+	b = 0xEFCDAB89;
+	c = 0x98BADCFE;
+	d = 0x10325476;
+	for (k = 0; k < x.length; k += 16) {
+		AA = a;
+		BB = b;
+		CC = c;
+		DD = d;
+		a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
+		d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
+		c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
+		b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
+		a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
+		d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
+		c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
+		b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
+		a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
+		d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
+		c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
+		b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
+		a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
+		d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
+		c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
+		b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
+		a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
+		d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
+		c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
+		b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
+		a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
+		d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
+		c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
+		b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
+		a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
+		d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
+		c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
+		b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
+		a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
+		d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
+		c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
+		b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
+		a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
+		d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
+		c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
+		b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
+		a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
+		d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
+		c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
+		b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
+		a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
+		d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
+		c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
+		b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
+		a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
+		d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
+		c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
+		b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
+		a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
+		d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
+		c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
+		b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
+		a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
+		d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
+		c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
+		b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
+		a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
+		d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
+		c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
+		b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
+		a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
+		d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
+		c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
+		b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
+		a = addUnsigned(a, AA);
+		b = addUnsigned(b, BB);
+		c = addUnsigned(c, CC);
+		d = addUnsigned(d, DD);
+	}
+	let tempValue = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
+	return tempValue.toLowerCase();
+};
+let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+let base64DecodeChars = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
+/**
+ * base64编码
+ * @param {Object} str
+ */
+function base64encode (str) {
+	let out, i, len;
+	let c1, c2, c3;
+	len = str.length;
+	i = 0;
+	out = '';
+	while (i < len) {
+		c1 = str.charCodeAt(i++) & 0xff;
+		if (i === len) {
+			out += base64EncodeChars.charAt(c1 >> 2);
+			out += base64EncodeChars.charAt((c1 & 0x3) << 4);
+			out += '==';
+			break;
+		}
+		c2 = str.charCodeAt(i++);
+		if (i === len) {
+			out += base64EncodeChars.charAt(c1 >> 2);
+			out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+			out += base64EncodeChars.charAt((c2 & 0xF) << 2);
+			out += '=';
+			break;
+		}
+		c3 = str.charCodeAt(i++);
+		out += base64EncodeChars.charAt(c1 >> 2);
+		out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+		out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
+		out += base64EncodeChars.charAt(c3 & 0x3F);
+	}
+	return out;
+}
+/**
+ * base64解码
+ * @param {Object} str
+ */
+function base64decode (str) {
+	let c1, c2, c3, c4;
+	let i, len, out;
+	len = str.length;
+	i = 0;
+	out = '';
+	while (i < len) {
+		/* c1 */
+		do {
+			c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+		}
+		while (i < len && c1 === -1);
+		if (c1 === -1) { break; }
+		/* c2 */
+		do {
+			c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+		}
+		while (i < len && c2 === -1);
+		if (c2 === -1) { break; }
+		out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
+		/* c3 */
+		do {
+			c3 = str.charCodeAt(i++) & 0xff;
+			if (c3 === 61) { return out; }
+			c3 = base64DecodeChars[c3];
+		}
+		while (i < len && c3 === -1);
+		if (c3 === -1) { break; }
+		out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
+		/* c4 */
+		do {
+			c4 = str.charCodeAt(i++) & 0xff;
+			if (c4 === 61) { return out; }
+			c4 = base64DecodeChars[c4];
+		}
+		while (i < len && c4 === -1);
+		if (c4 === -1) { break; }
+		out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
+	}
+	return out;
+}
+
+export default CryptoJS;

+ 92 - 0
src/api/hzyb/http.js

@@ -0,0 +1,92 @@
+"use strict";
+import axios from "axios";
+import { Toast } from "vant";
+import CryptoJS from './crypto'
+
+// 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;
+console.log(import.meta.env.VITE_APP_HZYB_BASEAPIURL);
+let config = {
+  baseURL: import.meta.env.VITE_APP_HZYB_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++;
+    
+    config.headers.Authorization = localStorage.getItem('hzyb-token')||''
+    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();
+    }
+
+    let data
+    if(import.meta.env.MODE==='product'){
+      const data=JSON.parse(CryptoJS.Des3Decrypt(response.data));//解密
+    }else{
+      data=response.data
+    }
+    if(data.code!==200){
+      setTimeout(() => {
+        Toast(data.msg)
+      }, 10);
+    }
+    return 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);
+};

+ 14 - 0
src/api/hzyb/report.js

@@ -0,0 +1,14 @@
+// 活动报告接口
+
+import {get,post} from './http'
+
+// 获取报告详情
+export const apiGetReportDetail=(params)=>{
+    return get('/report/research_report',params)
+}
+
+//获取周度报告详情
+export const apiGetWeekReportDetail=params=>{
+    return get('/report/research_report_chapter',params)
+}
+

+ 10 - 0
src/api/hzyb/user.js

@@ -0,0 +1,10 @@
+//用户模块
+
+import {get,post} from './http'
+
+/**
+ * 获取个人信息
+ */
+export const apiUserInfo=(params)=>{
+	return get('/user/info',params)
+}

BIN
src/assets/404.png


BIN
src/assets/cygx/apply_act.png


BIN
src/assets/cygx/apply_ico.png


BIN
src/assets/cygx/attention_act.png


BIN
src/assets/cygx/attention_ico.png


BIN
src/assets/cygx/collect_act.png


BIN
src/assets/cygx/collect_ico.png


BIN
src/assets/cygx/quiz_ico.png


BIN
src/assets/cygx/returntop.png


BIN
src/assets/hzyb/chart/before-next.png


BIN
src/assets/hzyb/chart/calendar.png


BIN
src/assets/hzyb/chart/refresh.png


BIN
src/assets/hzyb/chart/save.png


BIN
src/assets/hzyb/chart/search.png


BIN
src/assets/hzyb/daybanner.jpg


BIN
src/assets/hzyb/daytop.jpg


BIN
src/assets/hzyb/lianzi.png


BIN
src/assets/hzyb/monthbanner.jpg


BIN
src/assets/hzyb/moreother.png


BIN
src/assets/hzyb/mzsm.png


BIN
src/assets/hzyb/otherbanner.jpg


BIN
src/assets/hzyb/selected.png


BIN
src/assets/hzyb/shou.png


BIN
src/assets/hzyb/twoweekbanner.jpg


BIN
src/assets/hzyb/weekbanner.jpg


BIN
src/assets/hzyb/weektop.jpg


+ 7 - 0
src/main.js

@@ -0,0 +1,7 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import router from './router'
+import 'normalize.css'
+import './style/common.scss'
+
+createApp(App).use(router).mount('#app')

+ 7 - 0
src/router/cygx/index.js

@@ -0,0 +1,7 @@
+export const cygxRoutes=[
+    {
+        path:'/raiReportDtl',
+        name:"raiReportDtl",
+        component: () => import("@/views/cygx/raiReportDtl.vue"),
+    },  
+]

+ 38 - 0
src/router/hzyb/index.js

@@ -0,0 +1,38 @@
+export const hzybRoutes=[
+    {
+        path:'/ficcReportDetail',
+        name:"hzybReportDetail",
+        component: () => import("@/views/hzyb/activity/reportDetail.vue"),
+    },  
+    // 研报活动模块
+    {
+        path:"/hzyb/activity",
+        name:"hzybActivity",
+        component: () => import("@/App.vue"),
+        children: [
+            {
+                path:"report/week/detail",
+                name:"hzybReportWeekDetail",
+                component: () => import("@/views/hzyb/activity/reportWeekDetail.vue"),
+            }
+        ]
+    },
+    // 图库模块
+    {
+        path:"/hzyb/chart",
+        name:"hzybChart",
+        component: () => import("@/App.vue"),
+        children: [
+            {
+                path:"detail",
+                name:"hzybChartDetail",
+                component: () => import("@/views/hzyb/chart/Detail.vue"),
+            },
+            {
+                path:"search",
+                name:'hzybChartSearch',
+                component: () => import("@/views/hzyb/chart/Search.vue")
+            }
+        ]
+    },
+]

+ 20 - 0
src/router/index.js

@@ -0,0 +1,20 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import {hzybRoutes} from './hzyb/index'// 弘则研报小程序路由
+import {cygxRoutes} from './cygx/index'// 查研观向小程序路由
+const routes=[
+    ...hzybRoutes,
+    ...cygxRoutes,
+    //404
+    {
+        path: "/:pathMatch(.*)",
+        name:"404",
+        component: () => import("@/views/Error.vue"),
+    },
+]
+
+const router = createRouter({
+   history: createWebHistory(import.meta.env.VITE_APP_BASE_URL),
+   routes
+})
+
+export default router

+ 17 - 0
src/style/common.scss

@@ -0,0 +1,17 @@
+#app{
+    font-size: 28px;
+}
+div{
+    box-sizing: border-box;
+}
+body{
+    line-height: 1.6;
+    font-family: -apple-system-font, "Helvetica Neue", sans-serif;
+}
+p,span {
+    margin: 0;
+    padding: 0;
+}
+a {
+    text-decoration: none;
+}

+ 21 - 0
src/views/Error.vue

@@ -0,0 +1,21 @@
+<template>
+    <div class="no-found-page">
+        <img src="@/assets/404.png" alt="">
+        <p>页面飞走了</p>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.no-found-page{
+    img{
+        width: 80%;
+        display: block;
+        margin: 150px auto 20px auto;
+    }
+    p{
+        text-align: center;
+        font-size: 32px;
+        color: #666;
+    }
+}
+</style>

+ 82 - 0
src/views/cygx/dlg.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="container-dlg" v-if="showTips">
+    <div class="text">
+      <h1>免责声明</h1>
+      <p v-if="reportInfo.IsResearch">
+        本文为用户投稿,用户在平台中发表的所有资料、言论等仅代表个人观点,与本平台立场无关,不对您构成任何投资建议。弘则研究对文中陈述、观点判断保持中立,不对所包含内容及数据的真实性、准确性、可靠性或完整性提供任何明示或暗示的保证。请读者仅作参考,并请自行承担全部责任。
+      </p>
+      <template v-else>
+        <p>1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。</p>
+        <p>
+          2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。
+        </p>
+        <p>
+          3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。
+        </p>
+        <p>4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。</p>
+      </template>
+      <p class="hide-tips" @click="isHide">知道了</p>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { reactive, onMounted, toRefs, ref } from "vue";
+const props = defineProps({
+  showTips: {
+    type: Boolean,
+    required: true,
+    default: false,
+  },
+  reportInfo: {
+    type: Object,
+  },
+});
+
+const emit = defineEmits(["hideDlg"]);
+
+const isHide = () => {
+  emit("hideDlg");
+};
+</script>
+
+<style lang="scss" scoped>
+.container-dlg {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 99;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.6);
+  padding: 0px 60px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  .text {
+    width: 100%;
+    background-color: #fff;
+    border-radius: 20px;
+    h1 {
+      padding-top: 30px;
+      text-align: center;
+      font: 32px "PingFang-SC-Regular";
+      color: #333;
+      text-align: center;
+      margin-bottom: 17px;
+    }
+    p {
+      padding: 0 30px;
+      font-size: 26px;
+    }
+    .hide-tips {
+      margin-top: 20px;
+      font-size: 28px;
+      line-height: 80px;
+      color: #2680eb;
+      text-align: center;
+      border-top: 1px solid #eaeaea;
+    }
+  }
+}
+</style>

+ 502 - 0
src/views/cygx/raiReportDtl.vue

@@ -0,0 +1,502 @@
+<template>
+  <div class="container-cygx" :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>搜索您想要的{{ reportInfo.IsBelongSummary ? "纪要" : "产业资源包" }}</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="report-link" v-if="fileLink">
+        报告全文:
+        <span style="color: #0808e5" @click="downloadFile">(PDF格式报告下载.pdf)</span>
+      </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>
+    <!-- 底部悬浮固定 -->
+    <div class="fixed_cont" v-if="from_type == 'mpwechat'">
+      <div class="handle-item" @click="applyHandle" v-if="reportInfo.IsSummary == 1 && !reportInfo.IsResearch">
+        <img src="@/assets/cygx/apply_act.png" class="img_ico" v-if="reportInfo.IsInterviewApply" />
+        <img src="@/assets/cygx/apply_ico.png" class="img_ico" v-else />
+        <div>{{ reportInfo.IsInterviewApply ? "已申请访谈" : "申请访谈" }}</div>
+      </div>
+      <div class="handle-item" @click="quizBtn" v-if="reportInfo.IsResearch">
+        <img src="@/assets/cygx/quiz_ico.png" class="img_ico" />
+        <div>提问</div>
+      </div>
+      <div class="handle-item" @click="attentionBtn" v-if="reportInfo.IsResearch">
+        <img src="@/assets/cygx/attention_act.png" class="img_ico" v-if="reportInfo.IsFollow" />
+        <img src="@/assets/cygx/attention_ico.png" class="img_ico" v-else />
+        <div>{{ `${reportInfo.FollowNum}人关注` }}</div>
+      </div>
+
+      <div class="handle-item" @click="collectHandle">
+        <img src="@/assets/cygx/collect_act.png" class="img_ico" v-if="reportInfo.IsCollect" />
+        <img src="@/assets/cygx/collect_ico.png" class="img_ico" v-else />
+        <div v-if="reportInfo.IsResearch">
+          {{ `${reportInfo.CollectionNum}  人收藏` }}
+        </div>
+        <div v-else>{{ reportInfo.IsCollect ? "已收藏" : "收藏" }}</div>
+      </div>
+    </div>
+    <dlg :showTips="showTips" :reportInfo="reportInfo" @hideDlg="showTips = false" />
+  </div>
+</template>
+
+<script setup>
+import { reactive, onMounted, toRefs, ref } from "vue";
+import { useRouter, useRoute } from "vue-router";
+import { RaiApi } from "@/api/cygx/api.js";
+import { Icon, Dialog, Toast } from "vant";
+import dlg from "./dlg.vue";
+const router = useRouter();
+const route = useRoute();
+const state = reactive({
+  reportInfo: {},
+});
+const rerportId = ref(null);
+const from_type = 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 downloadFile = async () => {
+  Toast.loading({
+    message: "下载中...",
+    duration: 0,
+    forbidClick: true,
+  });
+  const res = await RaiApi.articlePdfwatermark({
+    ArticleId: Number(rerportId.value),
+  });
+  if (res.Ret === 200) {
+    Toast.clear();
+    wx.miniProgram.navigateTo({
+      url: "/pageMy/downloadFile/downloadFile?url=" + res.Data.FileLink,
+    });
+  }
+};
+/* 访谈申请 */
+const applyHandle = () => {
+  !state.reportInfo.IsInterviewApply &&
+    Dialog.confirm({
+      title: "",
+      message: "专家访谈申请会提交给您的对口销售,销售会线下与您取得联系,确定申请吗?",
+      confirmButtonColor: "#fff",
+      cancelButtonColor: "#fff",
+      theme: "round-button",
+    }).then(() => {
+      interviewApi();
+    });
+  // 取消申请访谈 区分状态 '待邀请','待访谈','已完成','已取消'
+  if (state.reportInfo.IsInterviewApply) {
+    Dialog.confirm({
+      title: "",
+      message:
+        state.reportInfo.InterviewApplyStatus == "待访谈"
+          ? "当前无法取消访谈,若有疑问,请联系对口销售" + state.reportInfo.SellerMobile
+          : state.reportInfo.InterviewApplyStatus == "待邀请"
+          ? "您要取消此次访谈申请吗?"
+          : "该访谈已完成",
+      confirmButtonColor: "#fff",
+      cancelButtonColor: "#fff",
+      theme: "round-button",
+    }).then(() => {
+      state.reportInfo.InterviewApplyStatus == "待邀请" ? interviewApi() : state.reportInfo.InterviewApplyStatus == "待访谈" ? (window.location.href = "tel://" + state.reportInfo.SellerMobile) : "";
+    });
+  }
+};
+const scrolltop = () => {
+  document.body.scrollTop = document.documentElement.scrollTop = 0;
+};
+//关注作者事件
+const attentionBtn = () => {
+  RaiApi.fllowDepartment({
+    DepartmentId: state.reportInfo.DepartmentId,
+  }).then((res) => {
+    if (res.Ret === 200) {
+      state.reportInfo.IsFollow = !state.reportInfo.IsFollow;
+      if (res.Data.Status == 1) {
+        state.reportInfo.FollowNum += 1;
+        if (res.Data.GoFollow) {
+          Dialog.confirm({
+            title: "作者关注成功",
+            message: "想要及时获取该作者新发报告时的消息推送,请关注【弘则研究】公众号",
+            confirmButtonColor: "#fff",
+            confirmButtonText: "去关注",
+            cancelButtonColor: "#fff",
+            theme: "round-button",
+          })
+            .then(() => {
+              wx.miniProgram.navigateTo({
+                url: "/activityPages/accountsOfficial/accountsOfficial",
+              });
+            })
+            .catch(() => {
+              // on cancel
+            });
+        } else {
+          Dialog.alert({
+            title: "",
+            message: "作者关注成功,该作者发布新的报告时,【弘则研究】公众号会为您推送微信消息提醒",
+            confirmButtonColor: "#3385FF",
+            confirmButtonText: "知道了",
+          });
+        }
+      } else {
+        state.reportInfo.FollowNum -= 1;
+        Toast("已取消关注");
+      }
+    }
+  });
+};
+/* 收藏 */
+const collectHandle = () => {
+  RaiApi.collectRpt({
+    ArticleId: Number(rerportId.value),
+  }).then((res) => {
+    if (res.Ret === 200) {
+      state.reportInfo.IsCollect = !state.reportInfo.IsCollect;
+      if (state.reportInfo.IsCollection == 1) {
+        state.reportInfo.CollectionNum -= 1;
+        state.reportInfo.IsCollection = 0;
+      } else {
+        state.reportInfo.CollectionNum += 1;
+        state.reportInfo.IsCollection = 1;
+      }
+      Toast(res.Msg);
+    }
+  });
+};
+/* 复制 */
+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 = () => {
+  if (state.reportInfo.IsBelongSummary) {
+    wx.miniProgram.navigateTo({ url: "/pageMy/search/search" });
+  } else {
+    wx.miniProgram.navigateTo({
+      url: "/reportPages/reportSearch/reportSearch",
+    });
+  }
+};
+//点击到提问页面
+const quizBtn = () => {
+  wx.miniProgram.navigateTo({
+    url: "/activityPages/generationAsk/generationAsk?id=" + state.reportInfo.ArticleId + "&type=文章",
+  });
+};
+/* 获取报告详情 */
+const getReport = (id, token, type) => {
+  if (type == "mpwechat") {
+    RaiApi.reportDtl({
+      ArticleId: id,
+      Authorization: token,
+    }).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(res.Data.Mobile);
+          }
+          $(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 });
+            }
+          });
+        }
+      }
+    });
+  } else {
+    RaiApi.lookReport({
+      ArticleIdMd5: 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;
+          $(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 });
+            }
+          });
+        }
+      }
+    });
+  }
+};
+onMounted(() => {
+  if (route.query.id) {
+    rerportId.value = route.query.id;
+    from_type.value = route.query.fromType;
+    let access_token = route.query.token || "";
+    localStorage.setItem("access_token", access_token);
+    getReport(rerportId.value, access_token, from_type.value);
+    if (from_type.value == "mpwechat") {
+      document.addEventListener("copy", (e) => {
+        copyMonitor();
+      });
+    }
+  }
+});
+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;
+      }
+    }
+  }
+}
+.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>

+ 270 - 0
src/views/hzyb/activity/reportDetail.vue

@@ -0,0 +1,270 @@
+<script setup>
+import { computed, ref,onMounted } from "vue";
+import { apiGetReportDetail } from "@/api/hzyb/report.js";
+import { useRoute, useRouter } from "vue-router";
+const route = useRoute();
+const router=useRouter()
+document.title = "报告详情";
+
+let info = ref(null);
+let topBg=ref(null)
+let noAuth = ref(false);
+const getDetail = async () => {
+    const res = await apiGetReportDetail({
+        Authorization: route.query.token,
+        research_report_id: route.query.research_report_id,
+    });
+    if (res.code === 200) {
+        info.value = res.data;
+        let temTopBg
+        switch (res.data.research_report_info.type) {
+            case 'day':
+                temTopBg=await import('@/assets/hzyb/daybanner.jpg')
+                break;
+            case 'week':
+                temTopBg=await import('@/assets/hzyb/weekbanner.jpg')
+                break;
+            case 'two_week':
+                temTopBg=await import('@/assets/hzyb/twoweekbanner.jpg')
+                break;
+            case 'month':
+                temTopBg=await import('@/assets/hzyb/monthbanner.jpg')
+                break;
+            case 'other':
+                temTopBg=await import('@/assets/hzyb/otherbanner.jpg')
+                break;
+            default:
+                temTopBg=await import('@/assets/hzyb/daybanner.jpg')
+                break;
+        }
+        topBg.value=temTopBg.default
+
+        noAuth.value = false;
+    } else if (res.code == 400) {
+        noAuth.value = true;
+    }
+};
+getDetail();
+
+const subval=(val,l,r)=>{//时间格式化
+    return val.substring(l,r);
+}
+
+const getWeek=(dateString)=>{//获取周几
+    let date;
+	if( dateString == null || typeof dateString == "undefined" ){
+		date = new Date();
+	}else{
+		let dateArray = dateString.split("-");
+		date = new Date(dateArray[0], parseInt(dateArray[1] - 1), dateArray[2]);
+	}
+	return "周" + "日一二三四五六".charAt(date.getDay());
+}
+
+onMounted(()=>{
+    $(document).on('click', '.content-wrap img',function(event) {
+	    let imgArray = [];
+	    let curImageSrc = $(this).attr('src');
+		let oParent = $(this).parent();
+		if (curImageSrc && !oParent.attr('href')) {
+            $('.content-wrap img').each(function(index, el) {
+                let itemSrc = $(this).attr('src');
+                imgArray.push(itemSrc);
+            });
+            wx.previewImage({current:curImageSrc,urls:imgArray});
+	    }
+    })
+})
+
+
+// 周度报告列表跳转详情
+const goDetail=(e)=>{
+    router.push({
+        path:"/hzyb/activity/report/week/detail",
+        query:{
+            token:route.query.token,
+            report_type_id:e.ResearchReportTypeId,
+            type:info.value.research_report_info.type
+        }
+    })
+}
+</script>
+
+<template>
+    <div class="report-detail" v-if="info">
+        <div class="top-wrap" :style="{backgroundImage:'url(' + topBg + ')'}">
+            <h1 class="title">{{info.research_report_info.reportVariety}}</h1>
+            <h3 class="sub-title">{{info.research_report_info.researchReportName.substring(info.research_report_info.researchReportName.indexOf('】')+1)}}</h3>
+            <div class="time-box">
+                <div class="num">{{subval(info.research_report_info.researchReportDate,8,10)}}</div>
+                <div class="right">
+                    <div>{{getWeek(info.research_report_info.researchReportDate)}}</div>
+                    <div>{{subval(info.research_report_info.researchReportDate,0,7)}}</div>
+                </div>
+            </div>
+            <div class="num-box">第{{info.research_report_info.periods}}期</div>
+        </div>
+        <div class="content-wrap list-content" v-if="['day','week'].includes(info.research_report_info.type)">
+            <div class="flex list-item" v-for="item in info.research_report_type_list" :key="item.ResearchReportTypeId" @click="goDetail(item)">
+                <div class="left-img">
+                    <img :src="item.BannerUrl" alt="">
+                </div>
+                <div class="right-con">
+                    <p style="color:#2E88EB">{{item.ReportChapterTypeName}}</p>
+                    <p class="van-multi-ellipsis--l2">{{item.ResearchReportTypeTitle}}</p>
+                </div>
+            </div>
+        </div>
+        <div class="content-wrap" v-else>
+            <div v-for="item in info.ResearchReportTypeContentList" :key="item.sort">
+              <h2 class="content-title">{{item.content_type?item.content_type:'核心观点'}}</h2>
+              <div v-html="item.content" class="content-text"></div>
+          </div>
+        </div>
+        <div class="footer-wrap">
+            <img class="img" src="@/assets/hzyb/mzsm.png" alt="" />
+            <div class="content">
+                <p>1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。</p>
+                <p>2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。</p>
+                <p>3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。</p>
+                <p>4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。</p>
+            </div>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.flex{
+    display: flex;
+}
+.top-wrap {
+    width: 100%;
+    height: 412px;
+    background-size: cover;
+    background-repeat: no-repeat;
+    color: #fff;
+    padding-top: 60px;
+    position: relative;
+    h1,
+    h3 {
+        margin: 0;
+        padding: 0;
+    }
+    .title,
+    .sub-title {
+        text-align: center;
+        width: 80%;
+        margin-left: auto;
+        margin-right: auto;
+    }
+    .title{
+        font-size: 52px;
+        font-family: '方正行楷';
+        letter-spacing: 1px;
+    }
+    .sub-title {
+        font-size: 37px;
+        margin-top: 80px;
+    }
+    .time-box{
+        position: absolute;
+        left: 30px;
+        bottom: 24px;
+        display: flex;
+        color: #fff;
+        align-items: center;
+        .num{
+            font-size: 40px;
+            font-weight: bold;
+            padding-right: 10px;
+            margin-right: 10px;
+            border-right: 1px solid #fff;
+        }
+        .right{
+            font-size: 25px;
+        }
+    }
+    .num-box{
+        position: absolute;
+        right: 30px;
+        bottom: 36px;
+        color: #fff;
+        font-size: 27px;
+    }
+}
+.footer-wrap {
+    background-color: rgb(236, 235, 235);
+    padding: 40px 32px;
+    .img {
+        display: block;
+        margin: auto;
+        height: 32px;
+        margin-bottom: 20px;
+    }
+    p {
+        margin-top: 0;
+        margin-bottom: 30px;
+    }
+    .content {
+        background-color: #fff;
+        padding: 30px;
+        border-radius: 8px;
+        line-height: 1.7;
+        color: rgb(102, 102, 102);
+    }
+}
+.content-wrap {
+    background-color: #fff;
+    margin-top: -20px;
+    border-top-left-radius: 20px;
+    border-top-right-radius: 20px;
+    min-height: 400px;
+    position: relative;
+    z-index: 2;
+    padding: 40px 30px;
+    line-height: 1.7;
+    :deep(img){
+        width: 100%;
+        display: block;
+        margin: 20px 0;
+    }
+    .content-title{
+        font-size: 38px;
+        font-family: '思源黑体';
+        margin: 40px 0 15px 0;
+    }
+    .content-text{
+        font-size: 38px;
+    }
+    :deep(p){
+        margin: 0;
+        padding: 0;
+    }
+}
+.list-content{
+    padding: 40px 0;
+    .list-item{
+        padding: 30px 30px;
+        border-bottom: 1px solid #f3f3f3;
+        align-items: center;
+        .left-img{
+            flex-shrink: 0;
+            width: 100px;
+            height: 100px;
+            border-radius: 8px;
+            border: 1px solid #f3f3f3;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            img{
+                width: 60px;
+                height: 60px;
+            }
+        }
+        .right-con{
+            margin-left: 20px;
+            font-size: 26px;
+        }
+    }
+}
+</style>

+ 295 - 0
src/views/hzyb/activity/reportWeekDetail.vue

@@ -0,0 +1,295 @@
+<script setup>
+import { computed, ref, onMounted,watch } from "vue";
+import { apiGetReportDetail, apiGetWeekReportDetail } from "@/api/hzyb/report.js";
+import { useRoute, useRouter,onBeforeRouteUpdate  } from "vue-router";
+const route = useRoute();
+const router=useRouter()
+document.title = "报告详情";
+
+let info = ref(null);
+let topBg = ref(null)
+let noAuth = ref(false);
+const getDetail = async () => {
+    const res = await apiGetWeekReportDetail({
+        Authorization: route.query.token,
+        research_report_type_id: route.query.report_type_id,
+    });
+    console.log(res);
+    if (res.code === 200) {
+        info.value = res.data;
+        let temTopBg
+        if (route.query.type === 'week') {
+            temTopBg = await import('@/assets/hzyb/weektop.jpg')
+        } else {
+            temTopBg = await import('@/assets/hzyb/daytop.jpg')
+        }
+        topBg.value = temTopBg.default
+        getList(res.data.research_report_type_info.research_report_id)
+        noAuth.value = false;
+    } else if (res.code == 400) {
+        noAuth.value = true;
+    }
+};
+
+let list = ref([])
+let selectId = ref(null)
+const getList = async (id) => {
+    const res = await apiGetReportDetail({
+        Authorization: route.query.token,
+        research_report_id: id,
+    });
+    if (res.code === 200) {
+        list.value = res.data.research_report_type_list || []
+        selectId.value = route.query.report_type_id
+    }
+}
+getDetail();
+
+onMounted(() => {
+    $(document).on('click', '.content-wrap img', function (event) {
+        let imgArray = [];
+        let curImageSrc = $(this).attr('src');
+        let oParent = $(this).parent();
+        if (curImageSrc && !oParent.attr('href')) {
+            $('.content-wrap img').each(function (index, el) {
+                let itemSrc = $(this).attr('src');
+                imgArray.push(itemSrc);
+            });
+            wx.previewImage({ current: curImageSrc, urls: imgArray });
+        }
+    })
+})
+
+const showTips = () => {
+    //免责声明显示
+    $("#tipsAlert").animate({ top: 0 });
+}
+const hideTips = () => {
+    //免责声明收起
+    $("#tipsAlert").animate({ top: "-120rem" });
+}
+
+const selectTag=(id)=>{
+    router.push({
+        query:{
+            report_type_id:id,
+            token:route.query.token,
+            type:route.query.type
+        }
+    })
+    document.body.scrollTop = document.documentElement.scrollTop = 0;
+
+}
+
+onBeforeRouteUpdate(to => {
+    getDetail()
+});
+</script>
+
+<template>
+    <div class="report-detail" v-if="info">
+        <div class="top-wrap">
+            <img :src="topBg" alt="" />
+            <div class="box" @click="showTips"></div>
+        </div>
+        <div class="content-wrap">
+            <div
+                v-for="item in info.research_report_type_content_list"
+                :key="item.sort"
+            >
+                <h2 class="content-title">
+                    {{ item.content_type ? item.content_type : "核心观点" }}
+                </h2>
+                <div v-html="item.content" class="content-text"></div>
+            </div>
+        </div>
+        <div class="footer-wrap">
+            <img
+                class="more-img"
+                src="../../../assets/hzyb/moreother.png"
+                alt=""
+            />
+            <div class="list">
+                <div
+                    v-for="(item, index) in list"
+                    :key="index"
+                    @click="selectTag(item.ResearchReportTypeId)"
+                >
+                    <img
+                        v-show="item.ResearchReportTypeId == selectId"
+                        src="../../../assets/hzyb/selected.png"
+                        class="selectimg"
+                    />
+                    <img :src="item.BannerUrl" class="imgBg" />
+                    <p>{{ item.ReportChapterTypeName }}</p>
+                </div>
+            </div>
+        </div>
+    </div>
+    <!-- 弹窗 -->
+    <div class="tipsAlert" id="tipsAlert">
+        <img class="top-img" src="../../../assets/hzyb/lianzi.png" />
+        <div class="con">
+            <h1>免责声明</h1>
+            <p>
+                1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。
+            </p>
+            <p>
+                2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。
+            </p>
+            <p>
+                3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。
+            </p>
+            <p>
+                4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。
+            </p>
+            <img
+                class="bot-img"
+                src="../../../assets/hzyb/shou.png"
+                @click="hideTips"
+            />
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.flex {
+    display: flex;
+}
+.top-wrap {
+    width: 100%;
+    height: 150px;
+    background-color: rgb(220, 222, 223);
+    color: #fff;
+    padding-top: 40px;
+    position: relative;
+    padding: 30px 30px 0 30px;
+    box-sizing: border-box;
+    img {
+        width: 100%;
+        border-top-right-radius: 8px;
+        border-top-left-radius: 8px;
+    }
+    .box {
+        position: absolute;
+        width: 200px;
+        height: 60px;
+        top: 40px;
+        right: 20px;
+    }
+}
+.footer-wrap {
+    background-color: rgb(236, 235, 235);
+    padding: 40px 0;
+    .more-img {
+        width: 262px;
+        display: block;
+        margin-left: auto;
+        margin-right: auto;
+    }
+    .list {
+        width: 100%;
+        padding: 0 30px;
+        box-sizing: border-box;
+        font: 0.56rem "黑体";
+        color: #6184bc;
+        overflow: auto;
+        font-size: 28px;
+        div {
+            float: left;
+            width: 150px;
+            height: 150px;
+            padding: 20px 0;
+            box-sizing: border-box;
+            position: relative;
+            overflow: hidden;
+            background: #fff;
+            border: 1px solid #eaeaea;
+            text-align: center;
+            border-radius: 20px;
+            margin: 11px;
+        }
+        .imgBg {
+            width: 56px;
+            height: 56px;
+            margin: 5px auto 3px;
+        }
+        .selectimg {
+            width: 62px;
+            height: 62px;
+            position: absolute;
+            top: 0;
+            left: 0;
+        }
+    }
+}
+.content-wrap {
+    background-color: #fff;
+    margin-top: -20px;
+    border-top-left-radius: 20px;
+    border-top-right-radius: 20px;
+    min-height: 400px;
+    position: relative;
+    z-index: 2;
+    padding: 40px 30px;
+    line-height: 1.7;
+    :deep(img) {
+        width: 100%;
+        display: block;
+        margin: 20px 0;
+    }
+    .content-title {
+        font-size: 38px;
+        font-family: "思源黑体";
+        margin: 0px 0 15px 0;
+    }
+    .content-text {
+        font-size: 38px;
+    }
+    :deep(p) {
+        margin: 0;
+        padding: 0;
+    }
+}
+
+#tipsAlert {
+    width: 100%;
+    height: 100%;
+    padding: 13% 0.8rem;
+    box-sizing: border-box;
+    position: fixed;
+    top: -120rem;
+    left: 0;
+    background: rgba(0, 0, 0, 0.6);
+    z-index: 200;
+    .top-img {
+        width: 28px;
+        position: absolute;
+        left: 50%;
+        z-index: 10;
+        transform: translateX(-50%);
+        top: -26px;
+    }
+    .con {
+        width: 100%;
+        height: 100%;
+        background-color: #fff;
+        border-radius: 8px;
+        padding: 40px 32px;
+        position: relative;
+        h1 {
+            font-size: 32px;
+            text-align: center;
+        }
+        font-size: 28px;
+        line-height: 1.7;
+    }
+    .bot-img {
+        width: 178px;
+        bottom: 0;
+        position: absolute;
+        left: 50%;
+        transform: translateX(-50%);
+    }
+}
+</style>

+ 1064 - 0
src/views/hzyb/chart/Detail.vue

@@ -0,0 +1,1064 @@
+<script setup>
+import chartBox from './component/chartBox.vue'
+import { Popup, Toast,Picker } from 'vant';
+import {ref,onMounted, reactive, watch} from 'vue'
+import {useRoute, useRouter,onBeforeRouteUpdate} from 'vue-router'
+import moment, { min } from 'moment'
+import Highcharts from 'highcharts/highstock';
+import {apiChartInfo,apiChartList,apiChartSave,apiChartBeforeAndNext,apiChartRefresh} from '@/api/hzyb/chart.js'
+const router=useRouter()
+const route=useRoute()
+
+localStorage.setItem('hzyb-token',route.query.token)
+
+
+// 获取用户信息
+import {apiUserInfo} from '@/api/hzyb/user'
+let canSave=ref(false)//是否有保存功能
+const getUserInfo=async ()=>{
+    const res=await apiUserInfo({Authorization:route.query.token})
+    if(res.code===200){
+        if(res.data.company_name==='弘则研究'){
+            canSave.value=true
+        }
+    }
+}
+getUserInfo()
+
+
+let showDate=ref(false)
+let startDate=ref('')
+let endDate=ref('')
+let columns=ref([])
+// type 1:年-月 2:年
+const makeTimeData=(type)=>{
+    let curYear=new Date().getFullYear()
+    let yearArr=[]
+    let monthArr=[]
+    for (let i = 2010; i <= curYear; i++) {
+        yearArr.push(i)
+    }
+    for (let i = 1; i < 13; i++) {
+        monthArr.push(i<10?'0'+i:i)
+    }
+    if(type==1){
+        return [{values:yearArr},{values:monthArr},{values:yearArr},{values:monthArr}]
+    }else{
+        return [{values:yearArr},{values:yearArr}]
+    }
+}
+const handleShowDate=()=>{
+    if(columns.value.length===0){
+        if(resData.value.ChartInfo.ChartType===1){//曲线图
+            columns.value=makeTimeData(1)
+        }else if(resData.value.ChartInfo.ChartType===2){//季节性图表
+            columns.value=makeTimeData(2)
+        }
+    }
+    showDate.value=true
+}
+// 确定选择时间
+const handleConfirmDate=(e)=>{
+    let start='',end=''
+    if(resData.value.ChartInfo.ChartType===1){
+        start=e[0]+'-'+e[1]
+        end=e[2]+'-'+e[3]
+    }else if(resData.value.ChartInfo.ChartType===2){
+        start=e[0]
+        end=e[1]
+    }
+    if(new Date(end)>new Date(start)){
+        startDate.value=start
+        endDate.value=end
+        dataType.value=5
+        getChartInfo()
+        showDate.value=false
+    }else{
+        Toast('结束时间不能小于开始时间')
+    }
+}
+
+// 选择年份时间段
+let dateTypeList=ref([
+    {
+		name: '15年至今',
+		value: 3,
+	},
+	// {
+	// 	name: '18年至今',
+	// 	value: 7,
+	// },
+	{
+		name: '19年至今',
+		value: 8,
+	},
+	{
+		name: '20年至今',
+		value: 9,
+	},
+	{
+		name: '21年至今',
+		value: 4,
+	},
+    {
+		name: '全部',
+		value: 0,
+	},
+])
+let dateType=ref(3)
+const dateTypeClick=(item)=>{
+    startDate.value=''
+    endDate.value=''
+    dateType.value=item.value
+    getChartInfo()
+} 
+
+let calendarType=ref('公历')//季节图 公历/农历
+// 公历/农历切换
+const calendarTypeChange=(val)=>{
+    calendarType.value=val
+    getChartInfo()
+}
+
+
+// 获取详情
+let ChartInfoId=route.query.ChartInfoId
+let chartData=ref({
+    series:[],
+    xAxis:[],
+    yAxis:[],
+
+})// 图表配置数据
+let resData=ref(null)//接口详情数据
+const getChartInfo=async ()=>{
+    const res=await apiChartInfo({
+        ChartInfoId:ChartInfoId,
+        DateType:dateType.value,
+        startDate:startDate.value&&resData.value.ChartInfo.ChartType===1?startDate.value:'',
+        endDate:endDate.value&&resData.value.ChartInfo.ChartType===1?endDate.value:'',
+        SeasonStartDate:startDate.value&&resData.value.ChartInfo.ChartType===2?startDate.value:'',
+        SeasonEndDate:endDate.value&&resData.value.ChartInfo.ChartType===2?endDate.value:'',
+        Calendar:calendarType.value,
+        Authorization:route.query.token
+    })
+    if(res.code===200){
+        resData.value=res.data
+        document.title=res.data.ChartInfo.ChartName
+        // 设置highchart配置 ChartType: 1曲线图 2季节图:季节图中公历和农历数据结构不同
+        if(res.data.ChartInfo.ChartType===1){
+            setSplineOpt(res.data.EdbInfoList)
+        }else{
+            setSeasonOpt(res.data.EdbInfoList[0])
+        }
+    }
+}
+getChartInfo()
+
+// 路由改变 解决从搜索页返回数据不刷新问题
+onBeforeRouteUpdate((nval)=>{
+    console.log('路由改变',nval);
+    ChartInfoId=nval.query.ChartInfoId
+    // router.go(0)
+    getChartInfo()
+})
+
+// 上下线设置
+let showLimit=ref(false)
+let hasLeftAxis=ref(false)//是否有左轴
+let hasRightAxis=ref(false)//是否有右轴
+let axisLimitData=reactive({//左右轴极值
+    leftMin:0,
+    leftMax:0,
+    rightMin:0,
+    rightMax:0
+})
+
+
+// 设置曲线图配置
+const setSplineOpt=(data)=>{
+    let series=[]
+    let xAxis={
+        tickPosition: 'inside',
+		lineColor: '#bfbfbf',
+    	tickColor: '#bfbfbf',
+		tickLength:5,
+		type: 'datetime',
+		ordinal: false,
+		dateTimeLabelFormats: {
+			day: '%y/%m',
+			week: '%y/%m',
+			month: '%y/%m',
+			year: '%y/%m',
+		},
+        xDateFormat:'%Y-%m-%d'
+    }
+    let yAxis=[]
+
+    let temYLeftArr=[]
+    let temYRightArr=[]
+
+    let minAndMaxTimeTemArr=[]//存放所有指标的最大最小时间
+
+    data.forEach((item,index)=>{
+        let dynamic_title = item.EdbName;
+        let dynamic_arr = data.filter(
+          (item) => dynamic_title === item.EdbName
+        );
+        // 拼接配置  IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
+        let dynamic_tag =item.IsAxis && item.IsOrder && item.EdbInfoType
+            ? '(逆序)'
+            : !item.IsAxis && item.IsOrder && item.EdbInfoType
+            ? '(右轴,逆序)'
+            : !item.IsAxis && !item.IsOrder && item.EdbInfoType
+            ? '(右轴)'
+            : !item.IsAxis && !item.IsOrder && !item.EdbInfoType
+            ? `(右轴,领先${item.LeadValue}${item.LeadUnit})`
+            : !item.IsAxis && item.IsOrder && !item.EdbInfoType
+            ? `(右轴,逆序,领先${item.LeadValue}${item.LeadUnit})`
+            : item.IsAxis && item.IsOrder && !item.EdbInfoType
+            ? `(逆序,领先${item.LeadValue}${item.LeadUnit})`
+            : item.IsAxis && !item.IsOrder && !item.EdbInfoType
+            ? `(领先${item.LeadValue}${item.LeadUnit})`
+            : '';
+        let seriesItemObj={
+            data:[],
+            dataGrouping:{
+                enabled:false
+            },
+            type:item.ChartStyle,
+            yAxis:index,
+            name:dynamic_arr.length > 1
+              ? `${item.EdbName}(${item.SourceName})${dynamic_tag}`
+              : `${item.EdbName}${dynamic_tag}`,//拼接标题 判断相同指标名称拼接来源
+            color: item.ChartColor,
+            lineWidth: Number(item.ChartWidth),
+            visible:true,
+            LatestDate:item.LatestDate,
+            LatestValue:item.LatestValue
+        }
+        item.DataList = item.DataList || [];
+        for (let i of item.DataList) {
+          seriesItemObj.data.push([i.DataTimestamp, i.Value]);
+        }
+        series.push(seriesItemObj)
+        
+
+        // 设置y轴
+        if(item.IsAxis){
+            temYLeftArr.push(item)
+        }else{
+            temYRightArr.push(item)
+        }
+
+        let yItem={
+            IsAxis:item.IsAxis,
+            labels: {
+                formatter: function (ctx) {
+                    return ctx.value;
+                },
+                align: 'center',
+            },
+            opposite: item.IsAxis === 0,
+            reversed: item.IsOrder,
+            min: item.MinData,
+            max: item.MaxData,
+            tickWidth: 1,
+            tickLength: 5,
+            lineWidth: 1,
+            lineColor: '#bfbfbf',
+            tickColor: '#bfbfbf',
+            offset: 0,
+            visible: true,
+            gridLineWidth: 0,
+            tickPosition: 'inside',
+            endOnTick: false,
+            startOnTick: false,
+            showLastLabel: true,
+            tickPixelInterval: 50,
+            chartEdbInfo:item//指标数据用于在保存时读取指标数据
+        }
+        yAxis.push(yItem)
+
+        minAndMaxTimeTemArr.push(item.DataList[0].DataTimestamp)
+        minAndMaxTimeTemArr.push(item.DataList[item.DataList.length-1].DataTimestamp)
+    })
+
+    // 设置值
+    chartData.value.series=series
+    
+    
+    // 设置x轴
+    // 找出所有指标的最大和最小时间 分成6段
+    let minTime=Math.min.apply(null,minAndMaxTimeTemArr)
+    let maxTime=Math.max.apply(null,minAndMaxTimeTemArr)
+
+
+    const bool_time = xTimeDiffer(minTime,maxTime)
+    if(bool_time){
+        xAxis={
+            ...xAxis,
+            labels: {
+              formatter: (ctx)=> {
+                return Highcharts.dateFormat('%m/%d', ctx.value)
+              }
+            }
+        }
+    }
+    // console.log(((maxTime-minTime)/6)/(24*3600*1000),':天');
+    // let maxYear=new Date(maxTime).getFullYear()
+    // let minYear=new Date(minTime).getFullYear()
+
+    xAxis={
+        ...xAxis,
+        tickInterval:((maxTime-minTime)/6)/(24*3600*1000)>30?(maxTime-minTime)/6:24*3600*1000*30,
+    }
+    
+    chartData.value.xAxis=[xAxis]
+    
+
+    yAxis.forEach(item=>{
+        if(item.IsAxis){//左轴
+            item.min=getAxisMaxOrMin(temYLeftArr,'min')
+            item.max=getAxisMaxOrMin(temYLeftArr,'max')
+            hasLeftAxis.value=true
+            axisLimitData.leftMin=getAxisMaxOrMin(temYLeftArr,'min')
+            axisLimitData.leftMax=getAxisMaxOrMin(temYLeftArr,'max')
+        }else{
+            item.min=getAxisMaxOrMin(temYRightArr,'min')
+            item.max=getAxisMaxOrMin(temYRightArr,'max')
+            hasRightAxis.value=true
+            axisLimitData.rightMin=getAxisMaxOrMin(temYRightArr,'min')
+            axisLimitData.rightMax=getAxisMaxOrMin(temYRightArr,'max')
+        }
+    })
+    chartData.value.yAxis=yAxis
+
+}
+
+//设置季节图配置
+const setSeasonOpt=(data)=>{
+    hasLeftAxis.value=true
+
+    const colorsArr=['#4B0082','#7FFFAA','#FF4500','#808000','#EEE8AA','#849EC1','#8A4294','#578B5A','#FDA8C7','#53B3FF','#999999','#000000','#FFDF0C','#FF0000','#0033FF']
+    let series=[],yAxis=[]
+    //农历默认选中一年数据并隐藏按钮  公历显示全部数据
+    let rangeSelector={}
+
+    // 公历
+    if(calendarType.value==='公历'){
+        data.DataList.forEach((item,index)=>{
+            let seriesItem={
+                data:[],
+                dataGrouping:{
+                    enabled:false
+                },
+                type:data.ChartStyle,
+                yAxis:index,
+                name:item.Year,
+                color:colorsArr.slice(-data.DataList.length)[index],                
+                visible:true
+            }
+            item.DataList=item.DataList||[]
+            for(let i of item.DataList){
+                seriesItem.data.push([i.DataTimestamp, i.Value])
+            }
+            series.push(seriesItem)
+
+            let yAxisItem={
+                IsAxis:data.IsAxis,
+                labels: {
+                    // formatter: function (ctx) {
+                    //     return ctx.value;
+                    // },
+                    align: 'center',
+                },
+                max: Number(data.MaxData),
+                min: Number(data.MinData),
+                lineWidth: 1,
+                lineColor: '#bfbfbf',
+                tickColor: '#bfbfbf',
+                offset: 0,
+                opposite: false,
+                reversed: false,
+                visible: true,
+                gridLineWidth: 0,
+                tickWidth: 1,
+                tickLength:5,
+                tickPosition: 'inside',
+                endOnTick: false,
+                startOnTick: false,
+                showLastLabel: true, //显示最后刻度值
+                tickPixelInterval: 50,
+                // chartEdbInfo:item//指标数据
+            }
+            yAxis.push(yAxisItem)
+        })
+
+        rangeSelector={ enabled: false}
+    }
+
+    // 农历
+    if(calendarType.value==='农历'){
+        let filterArr=data.DataList.List.slice(1,data.DataList.List.length)||[]
+        // console.log('aaa',filterArr);
+        filterArr.forEach((item,index)=>{
+            let seriesItem={
+                data:[],
+                dataGrouping:{
+                    enabled:false
+                },
+                type:data.ChartStyle,
+                yAxis:index,
+                name:item.Year,
+                color:colorsArr.slice(-filterArr.length)[index],                
+                visible:true
+            }
+            let temarr=item.Items||[]
+            for(let i of temarr){
+                seriesItem.data.push([i.DataTimestamp, i.Value])
+            }
+            series.push(seriesItem)
+
+            let yAxisItem={
+                IsAxis:data.IsAxis,
+                labels: {
+                    formatter: function (ctx) {
+                        return ctx.value;
+                    },
+                    align: 'center',
+                },
+                max: Number(data.MaxData),
+                min: Number(data.MinData),
+                lineWidth: 1,
+                lineColor: '#bfbfbf',
+                tickColor: '#bfbfbf',
+                offset: 0,
+                opposite: false,
+                reversed: false,
+                visible: true,
+                gridLineWidth: 0,
+                tickWidth: 1,
+                tickLength:5,
+                tickPosition: 'inside',
+                endOnTick: false,
+                startOnTick: false,
+                showLastLabel: true, //显示最后刻度值
+                tickPixelInterval: 50
+            }
+            yAxis.push(yAxisItem)
+        })
+
+        rangeSelector={ 
+            enabled: true,
+            selected: 0,
+            inputStyle: {
+                display: 'none',
+            },
+            labelStyle: {
+                display: 'none',
+            },
+            buttonTheme: {
+                style: {
+                    display: 'none',
+                },
+            },
+            buttons: [
+                {
+                  type: 'month',
+                  count: 12,
+                  text: '12月',
+                },
+                {
+                  type: 'month',
+                  count: 15,
+                  text: '15月',
+                },
+                {
+                  type: 'all',
+                  text: '全部',
+                }
+            ]
+        }
+    }
+
+    chartData.value.chart={ spacing: [5, 8, 2, 8]}
+    chartData.value.series=series
+    chartData.value.yAxis=yAxis
+    chartData.value.rangeSelector=rangeSelector
+    // 设置坐标轴极值
+    hasLeftAxis.value=true
+    axisLimitData.leftMin=Number(data.MinData)
+    axisLimitData.leftMax=Number(data.MaxData)
+
+    // 季节图x轴显示月/日
+    let xAxis={
+        tickPosition: 'inside',
+		lineColor: '#bfbfbf',
+    	tickColor: '#bfbfbf',
+		tickLength:5,
+		type: 'datetime',
+		ordinal: false,
+		dateTimeLabelFormats: {
+			day: '%y/%m',
+			week: '%y/%m',
+			month: '%y/%m',
+			year: '%y/%m',
+		},
+        labels: {
+            formatter: (ctx)=> {
+                return Highcharts.dateFormat('%m/%d', ctx.value)
+            }
+        }
+    }
+
+    xAxis={
+        ...xAxis,
+        tickInterval:24*3600*1000*60,//季节图
+    }
+    
+
+    chartData.value.xAxis=[xAxis]
+
+    // 季节图提示框显示 月/日
+    chartData.value.tooltip={
+        split: false,
+        shared: true,
+        dateTimeLabelFormats: {
+          // 时间格式化字符
+          day: '%m/%d',
+          week: '%m/%d',
+          month: '%m/%d',
+          year: '%m/%d',
+        },
+        xDateFormat: '%m/%d',
+    }
+
+}
+
+// 查询范围为1年内 x轴显示为月/日 否则默认年/月
+const xTimeDiffer=(minTime,maxTime)=>{
+    //年限差
+    let year_differ=moment(maxTime).diff(moment(minTime),'years',true)
+    console.log('年限差',year_differ)
+    if (year_differ<=1) {
+        console.log('true');
+        return true;
+    } else {
+        console.log('false');
+        return false;
+    }
+}
+
+// 找出最大最小值 arr 数据, type:min max
+const getAxisMaxOrMin=(arr,type)=>{
+    let minArr=[],maxArr=[],resNum=0
+
+    arr.forEach(item=>{
+        minArr.push(item.MinData)
+        maxArr.push(item.MaxData)
+    })
+
+    if(type==='min'){
+        resNum=Math.min.apply(null,minArr)
+    }else{
+        resNum=Math.max.apply(null,maxArr)
+    }
+
+    return resNum
+}
+
+// 监听极值变化
+watch(
+    ()=>axisLimitData,
+    (nval)=>{
+        // 只有当修改极值弹窗弹起时才去修改
+        if(!showLimit.value) return
+        console.log('极值改变');
+        chartData.value.yAxis.forEach(item=>{
+            if(item.IsAxis){//左轴
+                item.min=nval.leftMin
+                item.max=nval.leftMax
+            }else{
+                item.min=nval.rightMin
+                item.max=nval.rightMax
+            }
+        })
+    },
+    {
+        deep:true
+    }
+)
+
+// 前去搜索
+const handleGoSearch=()=>{
+    router.push({
+        path:'/hzyb/chart/search',
+        query:{
+            token:route.query.token
+        }
+    })
+}
+
+// 获取当前图表
+let searchVal=route.query.searchVal
+let searchListData=ref([])//搜索的数据
+const getSearchListData=async ()=>{
+    const res=await apiChartList({Keywords:searchVal,Page:1,Limit:10000,Authorization:route.query.token})
+    if(res.code===200){
+        searchListData.value=res.data
+    }
+}
+
+let chartBeforeAndNextData=ref(null)
+
+// 翻页
+const pageChange=async (type)=>{
+    // 搜索情况
+    if(searchVal){
+        if(type==='before'){
+            let index=searchListData.value.findIndex(item=>item.ChartInfoId==resData.value.ChartInfo.ChartInfoId)
+            if(!searchListData.value[index-1]){
+                Toast('当前已经是图库第一张图')
+            }else{
+                router.replace({
+                    query:{
+                        ...route.query,
+                        ChartInfoId:searchListData.value[index-1].ChartInfoId,
+                    }
+                })
+            }
+        }
+        if(type==='next'){
+            let index=searchListData.value.findIndex(item=>item.ChartInfoId==resData.value.ChartInfo.ChartInfoId)
+            if(!searchListData.value[index+1]){
+                Toast('当前已经是图库最后一张图')
+            }else{
+                router.replace({
+                    query:{
+                        ...route.query,
+                        ChartInfoId:searchListData.value[index+1].ChartInfoId,
+                    }
+                })
+            }
+        }
+
+        return
+    }
+
+    // 非搜索情况可切换分类
+    const res=await apiChartBeforeAndNext({
+        MyChartId:route.query.MyChartId,
+        MyChartClassifyId:route.query.MyChartClassifyId,
+        Authorization:route.query.token
+    })
+    if(res.code===200){
+        chartBeforeAndNextData.value=res.data
+        if(type==='before'){
+            if(chartBeforeAndNextData.value.PrevChart.ChartInfoId===0){
+                Toast('当前已经是图库第一张图')
+            }else{
+                if(chartBeforeAndNextData.value.PrevChart.Switch){
+                    let temStr=chartBeforeAndNextData.value.PrevChart.MyChartClassifyName
+                    setTimeout(() => {
+                        Toast({
+                            message:`您正在浏览${temStr}中的图表`,
+                            duration: 3000,
+                        })
+                    }, 500);
+                }
+                router.replace({
+                    query:{
+                        ...route.query,
+                        ChartInfoId:chartBeforeAndNextData.value.PrevChart.ChartInfoId,
+                        MyChartId:chartBeforeAndNextData.value.PrevChart.MyChartId,
+                        MyChartClassifyId:chartBeforeAndNextData.value.PrevChart.MyChartClassifyId,
+                        token:route.query.token
+                    }
+                })
+            }
+        }else if(type==='next'){
+            if(chartBeforeAndNextData.value.NextChart.ChartInfoId===0){
+                Toast('当前已经是图库最后一张图')
+            }else{
+                if(chartBeforeAndNextData.value.NextChart.Switch){
+                    let temStr=chartBeforeAndNextData.value.NextChart.MyChartClassifyName
+                    setTimeout(() => {
+                        Toast({
+                            message:`您正在浏览${temStr}中的图表`,
+                            duration: 3000,
+                        })
+                    }, 500);
+                }
+
+                router.replace({
+                    query:{
+                        ...route.query,
+                        ChartInfoId:chartBeforeAndNextData.value.NextChart.ChartInfoId,
+                        MyChartId:chartBeforeAndNextData.value.NextChart.MyChartId,
+                        MyChartClassifyId:chartBeforeAndNextData.value.NextChart.MyChartClassifyId,
+                        token:route.query.token
+                    }
+                })
+                
+            }
+        }
+    }
+    
+}
+
+onMounted(()=>{
+    if(searchVal){
+        getSearchListData()
+    }
+})
+
+// 保存
+const handleSaveChart=async ()=>{
+    let arr=chartData.value.yAxis.map(item=>{
+        return {
+            ChartColor: item.chartEdbInfo.ChartColor,
+            ChartStyle: item.chartEdbInfo.ChartStyle,
+            ChartWidth: Number(item.chartEdbInfo.ChartWidth),
+            EdbInfoId: item.chartEdbInfo.EdbInfoId,
+            EdbInfoType: item.chartEdbInfo.EdbInfoType,
+            IsAxis: item.chartEdbInfo.IsAxis,
+            IsOrder: item.chartEdbInfo.IsOrder,
+            LeadUnit: item.chartEdbInfo.EdbInfoType ? '' : item.chartEdbInfo.LeadUnit,
+            LeadValue: item.chartEdbInfo.EdbInfoType ? 0 : Number(item.chartEdbInfo.LeadValue),
+            MaxData: Number(item.max),
+            MinData: Number(item.min),
+        }
+    })
+    let params={}
+    if(resData.value.ChartInfo.ChartType===1){
+        params={
+            ChartInfoId: resData.value.ChartInfo.ChartInfoId || 0,
+            ChartEdbInfoList: arr,
+            DateType: dateType.value,
+            StartDate:startDate.value,
+            EndDate: endDate.value,
+        }
+    }else{
+        params={
+            ChartInfoId: resData.value.ChartInfo.ChartInfoId || 0,
+            ChartEdbInfoList: arr,
+            Calendar: calendarType.value,
+            SeasonStartDate:startDate.value,
+            SeasonEndDate: endDate.value,
+        }
+    }
+    const res=await apiChartSave(params)
+    if(res.code===200){
+        Toast.success('保存成功')
+    }
+
+}
+
+// 刷新图表
+const handleRefreshChart=async ()=>{
+    const res=await apiChartRefresh({ChartInfoId:Number(ChartInfoId)})
+    if(res.code===200){
+        setTimeout(() => {
+            Toast.success('刷新成功')
+        }, 10);
+        getChartInfo()
+    }
+}
+</script>
+
+<template>
+    <div class="chart-detail">
+        <div class="top-box">
+            <div class="flex calendar-box" style="float:left" @click="handleShowDate">
+                <img src="../../../assets/hzyb/chart/calendar.png" alt="">
+                <span class="date">{{startDate||'开始日期'}}</span>
+                <span style="margin:0 5px">至</span>
+                <span class="date">{{endDate||'结束日期'}}</span>
+            </div>
+            <img class="icon" src="../../../assets/hzyb/chart/search.png" alt="" @click="handleGoSearch">
+            <img class="icon" src="../../../assets/hzyb/chart/save.png" alt="" @click="handleSaveChart" v-if="canSave">
+            <img class="icon" src="../../../assets/hzyb/chart/refresh.png" alt="" @click="handleRefreshChart">
+        </div>
+
+        <chartBox :options='chartData' v-if="resData"></chartBox>
+        
+        <div class="flex source-box">
+            <span>来源:弘则研究</span>
+            <div class="season-change-box" v-if="resData&&resData.ChartInfo.ChartType===2">
+                <span :class="calendarType==='农历'&&'active'" @click="calendarTypeChange('农历')">农历</span>
+                <span :class="calendarType==='公历'&&'active'" @click="calendarTypeChange('公历')">公历</span>
+            </div>
+            <span style="color:#E3B377" @click="showLimit=true">上下限设置</span>
+        </div>
+
+        <!-- 日期类型 -->
+        <div class="date-type-box">
+            <div 
+                :class="['item',item.value==dateType?'active':'']" 
+                v-for="item in dateTypeList" 
+                :key="item.value"
+                @click="dateTypeClick(item)"
+            >{{item.name}}</div>
+        </div>
+
+        <!-- 最新值 -->
+        <div class="latest-value-wrap" v-if="resData">
+            <p style="margin-bottom:10px">最新数值</p>
+            <ul class="flex list" v-if="resData.ChartInfo.ChartType===1">
+                <li v-for="item in chartData.series" :key="item.name">
+                    <p style="color:#333">{{moment(item.LatestDate).format('YYYY-MM-DD')}}</p>
+                    <p :style="{color:item.color}">{{item.name}}</p>
+                    <p style="color:#1F243A">{{item.LatestValue}}</p>
+                </li>
+            </ul>
+            <ul class="list" v-else>
+                <li style="width:100%;border-right:none">
+                    <p style="color:#333">{{moment(resData.EdbInfoList[0].LatestDate).format('YYYY-MM-DD')}}</p>
+                    <p :style="{color:resData.EdbInfoList[0].ChartColor}">{{resData.ChartInfo.ChartName}}</p>
+                    <p style="color:#1F243A">{{resData.EdbInfoList[0].LatestValue}}</p>
+                </li>
+            </ul>   
+        </div>
+
+        <!-- 上一张下一张图切换 -->
+        <div class="change-page-wrap">
+            <div class="top" @click="pageChange('before')"></div>
+            <div class="bot" @click="pageChange('next')"></div>
+        </div>
+
+        <!-- 日期选择 -->
+        <Popup
+            v-model:show="showDate"
+            position="bottom"
+            round
+        >   
+            <Picker 
+                title="" 
+                :columns="columns" 
+                @cancel="showDate=false" 
+                @confirm="handleConfirmDate" 
+            />
+        </Popup>
+
+        <!-- 上下限设置 -->
+        <Popup
+            v-model:show="showLimit"
+            position="bottom"
+            :overlay-style="{background:'rgba(0,0,0,0)'}"
+        >
+            <div class="set-limit-box">
+                <div class="flex limit-top">
+                    <span style="color:#A7A7A7" @click="showLimit=false">取消</span>
+                    <span>上下限设置</span>
+                    <span style="color:#E3B377" @click="showLimit=false">确定</span>
+                </div>
+                <div class="con">
+                    <p style="margin:20px 0" class="top">
+                        <span>上限</span>
+                        <span>下限</span>
+                    </p>
+                    <p style="margin-bottom:25px" v-if="hasLeftAxis">
+                        <span style="margin-right:35px">左轴</span>
+                        <input type="number" v-model="axisLimitData.leftMin">
+                        <span style="margin:0 10px">至</span>
+                        <input type="number" v-model="axisLimitData.leftMax">
+                    </p>
+                    <p v-if="hasRightAxis">
+                        <span style="margin-right:35px">右轴</span>
+                        <input type="number" v-model="axisLimitData.rightMin">
+                        <span style="margin:0 10px">至</span>
+                        <input type="number" v-model="axisLimitData.rightMax">
+                    </p>
+                </div>
+            </div>
+        </Popup>
+    </div>
+    
+    
+
+</template>
+
+<style lang="scss" scoped>
+.chart-detail{
+    .flex{
+        display: flex;
+    }
+    .top-box{
+        padding: 40px 34px;
+        height: 40px;
+        .calendar-box{
+            align-items: center;
+            img{
+                width: 40px;
+                height: 40px;
+                margin-right: 18px;
+            }
+            .date{
+                width: 128px;
+                height: 40px;
+                background: #F6F6F6;
+                border: 1px solid #E5E5E5;
+                border-radius: 4px;
+                text-align: center;
+                line-height: 44px;
+                font-size: 24px;
+                color: #1F243A;
+            }
+        }
+        .icon{
+            float: right;
+            width: 40px;
+            height: 40px;
+            margin-left: 50px;
+        }
+    }
+    .select-date-box-head{
+        padding: 40px 34px;
+        justify-content: space-between;
+    }
+    .select-date-box{
+        // height:50vh;
+        .left,.right{
+            flex: 1;
+        }
+    }
+
+    
+
+    .source-box{
+        padding: 0 34px;
+        justify-content: space-between;
+        align-items: center;
+        font-size: 28px;
+        margin-bottom: 10px;
+        width: 100vw;
+        margin-top: 50px;
+        .season-change-box{
+            height: 50px;
+            margin-bottom: 10px;
+            padding-right: 34px;
+            span{
+                display: inline-block;
+                width: 100px;
+                line-height: 50px;
+                text-align: center;
+                border: 1px solid #ededed;
+                float: right;
+                border-radius: 4px;
+                &:first-child{
+                    border-left: none;
+                }
+                &:last-child{
+                    border-right: none;
+                }
+            }
+
+            .active{
+                color: #fff;
+                background-color: #E3B377;
+            }
+        }
+    }
+
+    .date-type-box{
+        padding: 0 34px;
+        margin-top: 38px;
+        display: flex;
+        overflow-x: auto;
+        border-bottom: 1px solid #F6F6F6;
+        .item{
+            // flex: 1;
+            width: 20vw;
+            padding: 0 8px;
+            text-align: center;
+            font-size: 24px;
+            padding-bottom: 14px;
+            color: #1F243A;
+        }
+        .active{
+            color: #E3B377;
+            border-bottom: 6px solid #E3B377;
+        }
+    }
+
+    .van-tabs{
+        border-bottom: 1px solid #F6F6F6;
+    }
+
+    .latest-value-wrap{
+        margin-top: 60px;
+        padding: 0 34px;
+        margin-bottom: 30px;
+        .list{
+            flex-wrap: wrap;
+            box-sizing: border-box;
+            li{
+                padding: 30px 0 10px 0;
+                width: 50%;
+                box-sizing: border-box;
+                border-top: 1px solid #F6F6F6;
+                text-align: center;
+                font-size:24px;
+                &:nth-child(odd){
+                    border-right: 1px solid #F6F6F6;
+                }
+                p:nth-child(3){
+                    font-size: 28px;
+                }
+            }
+        }
+    }
+
+    .set-limit-box{
+        min-height: 450px;
+        padding-top: 5px;
+        .limit-top{
+            padding: 30px 34px;
+            background: #FFFFFF;
+            box-shadow: 0px 0 12px rgba(206, 206, 206, 0.3);
+            justify-content: space-between;
+            font-size: 32px;
+        }
+        .con{
+            font-size: 28px;
+            padding-left: 34px;
+            .top{
+                span:first-child{
+                    margin-left: 196px;
+                }
+                span:last-child{
+                    margin-left: 188px;
+                }
+            }
+            input{
+                width: 181px;
+                height: 50px;
+                font-size: 24px;
+                background: #F6F6F6;
+                border: 1px solid #E5E5E5;
+                border-radius: 4px;
+                box-sizing: border-box;
+                padding: 0 10px;
+            }
+        }
+    }
+
+
+    .change-page-wrap{
+        position: fixed;
+        right: 0;
+        bottom: 50px;
+        width: 100px;
+        height: 231px;
+        background-image: url('../../../assets/hzyb/chart/before-next.png');
+        background-size: cover;
+        z-index: 10;
+        .top,.bot{
+            height: 50%;
+        }
+
+    }
+
+
+}
+</style>

+ 86 - 0
src/views/hzyb/chart/Search.vue

@@ -0,0 +1,86 @@
+<script setup>
+import {ref} from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { Search } from 'vant'
+
+const route=useRoute()
+const router=useRouter()
+
+document.title='图表搜索'
+
+import {apiChartList} from '@/api/hzyb/chart.js'
+let searchVal=ref('')
+let list=ref([])
+let finished=ref(false)
+const onSearch=async ()=>{
+    finished.value=false
+    const res=await apiChartList({Keywords:searchVal.value,Page:1,Limit:10000,Authorization:route.query.token})
+    finished.value=true
+    if(res.code===200){
+        list.value=res.data||[]
+    }
+}
+
+const handleSelect=(item)=>{
+    router.replace({
+        path:'/hzyb/chart/detail',
+        query:{
+            ChartInfoId:item.ChartInfoId,
+            token:route.query.token,
+            searchVal:searchVal.value
+        }
+    })
+}
+</script>
+
+
+<template>
+    <div class="chart-search">
+        <div class="top-search">
+            <Search 
+                v-model="searchVal" 
+                shape="round" 
+                placeholder="请输入图表关键词"
+                @search="onSearch"
+            />
+        </div>
+        <div class="empty-box" v-if="list.length==0&&finished">
+            <img src="https://hzstatic.hzinsights.com/static/icon/hzyb/chart_empty.png" alt="">
+            <p>暂时找不到对应图,试试别的搜索词吧~</p>
+        </div>
+        <ul class="list" v-else>
+            <li 
+                class="item" 
+                v-for="item in list" 
+                :key="item.ChartInfoId"
+                @click="handleSelect(item)"
+            >{{item.ChartName}}</li>
+        </ul>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.chart-search{
+    .list{
+        margin-top: 50px;
+        .item{
+            padding: 18px 34px 21px 34px;
+            border-bottom: 1px solid #E5E5E5;
+            font-size: 28px;
+            color: #1F243A;
+        }
+    }
+    .empty-box{
+        margin-top: 200px;
+        text-align: center;
+        img{
+            width: 346px;
+        }
+        p{
+
+            color: #999999;
+            font-size: 32px;
+        }
+    }
+}
+</style>

+ 167 - 0
src/views/hzyb/chart/component/chartBox.vue

@@ -0,0 +1,167 @@
+<script setup>
+// 图默认配置
+const chartDefaultOpts={
+    //图表配置
+	chart: {},
+	title: {
+		enabled: false
+	},
+	exporting: {
+		enabled: false,
+	},
+	//默认颜色配置
+	colors:['#00f','#f00','#999','#000','#7cb5ec', '#90ed7d', '#f7a35c', '#8085e9', 
+	'#f15c80', '#e4d354', '#2b908f', '#f45b5b', '#91e8e1'],
+	//版权信息
+	credits: {enabled:false},
+	//数据列通用配置
+	plotOptions: {
+		series: {
+			turboThreshold: 0, //不限制数据点个数 
+			boostThreshold:0,
+			animation: {
+				duration: 1000
+			}
+		}
+	},
+	//范围选择器
+	rangeSelector: {
+		enabled: false,
+		selected: 2,
+	},
+	//悬浮提示框
+	tooltip: {
+		split: false,
+		shared: true,
+		dateTimeLabelFormats: {
+			// 时间格式化字符
+			day: '%Y/%m/%d',
+			week: "%Y/%m",
+			month: '%Y/%m',
+			year: '%Y/%m',
+		},
+		xDateFormat:'%Y/%m/%d',
+		// valueDecimals: 4,
+	},
+	//图例
+	legend: {
+		enabled: false,
+		verticalAlign: 'top',
+		margin:3,
+		// layout: 'vertical'
+	},
+	//滚动条
+	scrollbar: {
+		enabled: false,
+	},
+	//导航器
+	navigator: {
+		enabled: false,
+	},
+	//范围选择器
+	rangeSelector: {
+		enabled: false,
+	},
+}
+
+const props = defineProps({
+  options: Object
+})
+import {ref,onMounted,watch,toRefs } from 'vue'
+import Highcharts from 'highcharts/highstock';
+import HighchartszhCN  from '../../utils/highcahrts-zh_CN.js'
+HighchartszhCN(Highcharts)
+let chartIns=ref(null)// 图表实例
+onMounted(() => {
+	let obj={...chartDefaultOpts,...props.options}
+	console.log(obj);
+    chartIns.value=Highcharts.stockChart("chart-box",obj)
+})
+
+// 点击顶部label
+const clickChartTopLabel=index=>{
+    const series = chartIns.value.series[index];
+    if (series.visible) {
+		series.hide();
+        props.options.series[index].visible=false
+	} else {
+		series.show();
+        props.options.series[index].visible=true
+	}
+}
+
+watch(
+	()=>props.options,
+	(nval)=>{
+		console.log('重绘');
+		let obj={}
+		obj={...chartDefaultOpts,...props.options}
+		console.log(obj);
+		// update: function (options, redraw, oneToOne, animation)
+		// chartIns.value.update(obj,true)
+		chartIns.value=Highcharts.stockChart("chart-box",obj)
+	},
+	{
+		deep:true
+	}
+)
+</script>
+
+<template>
+    <div class="chart-wrap">
+        <div class="chart-top-labels">
+            <div 
+                class="item" 
+                v-for="(item,index) in props.options.series" 
+                :key="item.name"
+                @click="clickChartTopLabel(index)"
+            >
+                <span class="color" :style="{background:item.visible?item.color:'#ccc'}"></span>
+                <span :style="{color:item.visible?'':'#ccc'}">{{item.name}}</span>
+            </div>
+        </div>
+
+        <div class="chart-box" id="chart-box"></div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.chart-wrap{
+    margin-top: 43px;
+    width: 100%;
+    .chart-top-labels{
+        padding: 0 60px;
+        width: 100%;
+        display: flex;
+        flex-wrap: wrap;
+        max-height: 200px;
+        overflow-y: auto;
+        &::-webkit-scrollbar{
+            display: none;
+        }
+        .item{
+            display: flex;
+            align-items: center;
+            font-size: 26px;
+            line-height: 1;
+            margin-right: 50px;
+            margin-bottom: 20px;
+            .color{
+                display: inline-block;
+                width: 43px;
+                height: 6px;
+                flex-shrink: 0;
+            }
+        }
+    }
+    .chart-box {
+		padding-top: 10px;
+        margin-top: 30px;
+		// width: 100%;
+        height: 736px;
+        margin-left: 34px;
+        margin-right: 34px;
+    }
+}
+
+</style>

+ 240 - 0
src/views/hzyb/utils/highcahrts-zh_CN.js

@@ -0,0 +1,240 @@
+/**
+ * Highcharts-zh_CN ES6 版本 plugins v1.2.1 (2020-07-29)
+ *
+ * (c) 2020 Jianshu Technology Co.,LTD (https://jianshukeji.com)
+ *
+ * Author : john@jianshukeji.com, Blue Monkey
+ *
+ * License: Creative Commons Attribution (CC)
+ */
+
+export default (H) => {
+  let protocol = window.location.protocol;
+
+  if (!/^http(s)?:&/.test(protocol)) {
+    protocol = 'http:';
+  }
+
+  let defaultOptionsZhCn = {
+    lang: {
+      // Highcharts
+      contextButtonTitle: '图表导出菜单',
+      decimalPoint: '.',
+      downloadJPEG: '下载 JPEG 图片',
+      downloadPDF: '下载 PDF 文件',
+      downloadPNG: '下载 PNG 文件',
+      downloadSVG: '下载 SVG 文件',
+      downloadXLS: '下载 XLS 文件',
+      drillUpText: '◁ 返回 {series.name}',
+      exitFullscreen: '退出全屏',
+      exportData: {
+        categoryDatetimeHeader: '时间',
+        categoryHeader: '类别',
+      },
+      openInCloud: '在 Highcharts Cloud 中打开',
+      invalidDate: '无效的时间',
+      loading: '加载中...',
+      months: [
+        '一月',
+        '二月',
+        '三月',
+        '四月',
+        '五月',
+        '六月',
+        '七月',
+        '八月',
+        '九月',
+        '十月',
+        '十一月',
+        '十二月',
+      ],
+      navigation: {
+        popup: {
+          addButton: '新增',
+          arrowLine: '直线',
+          arrowRay: '射线',
+          arrowSegment: '线段',
+          background: '背景',
+          backgroundColor: '背景颜色',
+          backgroundColors: '背景颜色',
+          borderColor: '边框颜色',
+          borderRadius: '圆角',
+          borderWidth: '边框大小',
+          circle: '圆',
+          color: '颜色',
+          connector: '连接',
+          crooked3: 'Crooked 3 line',
+          crooked5: 'Crooked 5 line',
+          crosshairX: '竖直准星线',
+          crosshairY: '水平准星线',
+          editButton: '编辑',
+          elliott3: 'Elliott 3 line',
+          elliott5: 'Elliott 5 line',
+          fibonacci: '斐波纳契',
+          fill: '填充颜色',
+          flags: '标志',
+          fontSize: '字体大小',
+          format: '文本',
+          height: '高度',
+          horizontalLine: '水平线',
+          infinityLine: '无限线',
+          innerBackground: '内背景',
+          label: '文字标签',
+          labelOptions: '文字标签配置',
+          labels: '文字标签',
+          line: '线',
+          lines: '线条',
+          measure: 'Measure',
+          measureX: 'Measure X',
+          measureXY: 'Measure XY',
+          measureY: 'Measure Y',
+          name: '名字',
+          outerBackground: '外背景',
+          padding: '内间距',
+          parallelChannel: '并行通道',
+          pitchfork: '杈子',
+          ray: '射线',
+          rectangle: '矩形',
+          removeButton: '删除',
+          saveButton: '保存',
+          segment: '段落',
+          series: '数据列',
+          shapeOptions: '图形配置',
+          shapes: '图形',
+          simpleShapes: '简单图形',
+          stroke: '线条颜色',
+          strokeWidth: '线条粗细',
+          style: '样式',
+          title: '标题',
+          tunnel: '通道',
+          typeOptions: '详情',
+          verticalArrow: '竖直箭头',
+          verticalCounter: '竖直计数器',
+          verticalLabel: '竖直标签',
+          verticalLine: '竖直线',
+          volume: '成交量',
+        },
+      },
+      noData: '暂无数据',
+      numericSymbols: null,
+      printChart: '打印图表',
+      resetZoom: '重置缩放比例',
+      resetZoomTitle: '重置为原始大小',
+      shortMonths: [
+        '一月',
+        '二月',
+        '三月',
+        '四月',
+        '五月',
+        '六月',
+        '七月',
+        '八月',
+        '九月',
+        '十月',
+        '十一月',
+        '十二月',
+      ],
+      thousandsSep: ',',
+      viewData: '查看数据表格',
+      viewFullscreen: '全屏查看',
+      weekdays: [
+        '星期天',
+        '星期一',
+        '星期二',
+        '星期三',
+        '星期四',
+        '星期五',
+        '星期六',
+      ],
+      viewData: '查看数据表格',
+      // Highstock
+      rangeSelectorFrom: '开始时间',
+      rangeSelectorTo: '结束时间',
+      rangeSelectorZoom: '范围',
+
+      // Highmaps
+      zoomIn: '缩小',
+      zoomOut: '放大',
+    },
+
+    global: {
+      // 不使用 UTC时间
+      useUTC: false,
+      timezoneOffset: -8 * 60,
+      canvasToolsURL:
+        protocol + '//cdn.hcharts.cn/highcharts/modules/canvas-tools.js',
+      VMLRadialGradientURL:
+        protocol + +'//cdn.hcharts.cn/highcharts/gfx/vml-radial-gradient.png',
+    },
+
+    exporting: {
+      url: protocol + '//export.highcharts.com.cn',
+    },
+
+    credits: {
+      text: 'Highcharts.com.cn',
+      href: 'https://www.highcharts.com.cn',
+    },
+
+    /**
+     * Highstock
+     */
+
+    rangeSelector: {
+      inputDateFormat: '%Y-%m-%d',
+      buttons: [
+        {
+          type: 'month',
+          count: 1,
+          text: '月',
+        },
+        {
+          type: 'month',
+          count: 3,
+          text: '季度',
+        },
+        {
+          type: 'month',
+          count: 6,
+          text: '半年',
+        },
+        {
+          type: 'ytd',
+          text: 'YTD',
+        },
+        {
+          type: 'year',
+          count: 1,
+          text: '年',
+        },
+        {
+          type: 'all',
+          text: '所有',
+        },
+      ],
+    },
+
+    plotOptions: {
+      series: {
+        dataGrouping: {
+          dateTimeLabelFormats: {
+            /* millisecond: [
+              '%Y-%m-%d %H:%M:%S.%L',
+              '%Y-%m-%d %H:%M:%S.%L',
+              ' ~ %H:%M:%S.%L',
+            ],
+            second: ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S', ' ~ %H:%M:%S'],
+            minute: ['%Y-%m-%d %H:%M', '%Y-%m-%d %H:%M', ' ~ %H:%M'],
+            hour: ['%Y-%m-%d %H:%M', '%Y-%m-%d %H:%M', ' ~ %H:%M'], */
+            day: ['%Y/%m/%d', '%Y/%m/%d', ' ~ %Y/%m/%d'],
+            week: ['%Y/%m/%d', '%Y/%m/%d', ' ~ %Y/%m/%d'],
+            month: ['%Y/%m', '%Y/%m', ' ~ %Y/%m'],
+            year: ['%Y', '%Y', ' ~ %Y'],
+          },
+        },
+      },
+    },
+  };
+
+  H.setOptions(defaultOptionsZhCn);
+};

+ 28 - 0
vite.config.js

@@ -0,0 +1,28 @@
+import { defineConfig,loadEnv } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import styleImport from 'vite-plugin-style-import';
+import path from "path";
+// https://vitejs.dev/config/
+export default ({mode})=>defineConfig({
+  base:loadEnv(mode, process.cwd()).VITE_APP_BASE_URL,
+  plugins: [
+    vue(),
+    styleImport({
+      libs: [
+        {
+          libraryName: 'vant',
+          esModule: true,
+          resolveStyle: (name) => `vant/es/${name}/style/index`,
+        },
+      ],
+    }),
+  ],
+  resolve: {
+    alias: {
+      "@": path.resolve(__dirname, "./src"),
+    },
+  },
+  build:{
+    outDir:loadEnv(mode, process.cwd()).VITE_APP_OUTDIR
+  }
+})