Karsa 3 سال پیش
کامیت
d8f7a3cb2c

+ 1 - 0
.env.development

@@ -0,0 +1 @@
+VITE_BASEURL = '/v1'

+ 1 - 0
.env.production

@@ -0,0 +1 @@
+VITE_BASEURL = 'https://ficc.hzinsights.com/v1'

+ 1 - 0
.env.test

@@ -0,0 +1 @@
+VITE_BASEURL = 'http://8.136.199.33:8608/v1'

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+horz_chart

+ 11 - 0
README.md

@@ -0,0 +1,11 @@
+# Vue 3 + Typescript + Vite
+
+This template should help get you started developing with Vue 3 and Typescript in Vite.
+
+## Recommended IDE Setup
+
+- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
+
+## Type Support For `.vue` Imports in TS
+
+Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette.

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" href="/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>图表后台</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script src="/src/static/js/rem.js"></script>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 31 - 0
package.json

@@ -0,0 +1,31 @@
+{
+  "name": "horz_chart_front",
+  "version": "0.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc --noEmit && vite build --mode production",
+    "build.test": "vue-tsc --noEmit && vite build --mode test",
+    "serve": "vite preview"
+  },
+  "dependencies": {
+    "axios": "^0.21.1",
+    "element-plus": "^1.1.0-beta.9",
+    "highcharts": "^9.2.2",
+    "less": "^4.1.1",
+    "less-loader": "^8.0.0",
+    "lodash": "^4.17.21",
+    "moment": "^2.29.1",
+    "vue": "^3.2.6",
+    "vue-router": "^4.0.11",
+    "vuex": "^4.0.2"
+  },
+  "devDependencies": {
+    "@types/node": "^16.7.2",
+    "@vitejs/plugin-legacy": "^1.5.3",
+    "@vitejs/plugin-vue": "^1.6.0",
+    "@vue/compiler-sfc": "^3.2.6",
+    "typescript": "^4.3.2",
+    "vite": "^2.5.2",
+    "vue-tsc": "^0.2.2"
+  }
+}

BIN
public/favicon.ico


+ 9 - 0
src/App.vue

@@ -0,0 +1,9 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <router-view></router-view>
+</template>
+
+<style lang="less">
+</style>

BIN
src/assets/logo.png


+ 61 - 0
src/components/chart.vue

@@ -0,0 +1,61 @@
+<!--  -->
+<template>
+  <div id="container" class="chart"></div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch,onMounted,PropType } from 'vue';
+import Highcharts,{} from 'highcharts/highstock';
+import HighchartszhCN  from '@/utils/highcahrts-zh_CN';
+import { defaultOpts } from '@/utils/chartOptions';
+import { IChart } from '@/types';
+
+HighchartszhCN(Highcharts)
+
+export default defineComponent({
+  name: '',
+  props: {
+    chartId: {
+      type: Number,
+      required: true,
+    },
+    options: {
+      type: Object,
+      required: true,
+    },
+  },
+  setup(props) {
+    
+    const chart = ref({});
+
+    /* 设置options */
+    const init = (): void => {
+      const new_options: any = { ...defaultOpts, ...props.options };
+      // console.log(new_options)
+      chart.value = Highcharts.stockChart('container', new_options);
+    };
+
+    onMounted((): void => {
+      init();
+    })
+
+    watch(
+      () => props.options,
+      (newval) => {
+        console.log('reload')
+        init();
+      },
+      // {
+      //   immediate: true
+      // }
+    );
+
+    return {};
+  },
+});
+</script>
+<style scoped lang="less">
+.chart {
+  height: 100%;
+}
+</style>

+ 14 - 0
src/env.d.ts

@@ -0,0 +1,14 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import { DefineComponent } from 'vue'
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}
+
+interface ImportMetaEnv {
+	VITE_BASEURL: string;
+}
+
+declare module 'lodash'

+ 14 - 0
src/main.ts

@@ -0,0 +1,14 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import './reset.css'
+
+import router from './router';
+import store from './store';
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css';
+
+const app = createApp(App);
+app.use(ElementPlus)
+app.use(router);
+app.use(store);
+app.mount('#app');

+ 17 - 0
src/request/api.ts

@@ -0,0 +1,17 @@
+import { get,post } from './request';
+
+interface IChartParams {
+	UniqueCode: string | any;
+}
+
+/* 用户模块 */
+export const ChartApi = {
+	/**
+	 * 获取图表详情
+	 * @param params UniqueCode 
+	 * @returns 
+	 */
+	getChart: (params:IChartParams) => {
+		return get('/chart/detail',params);
+	}
+}

+ 62 - 0
src/request/request.ts

@@ -0,0 +1,62 @@
+import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
+// import { useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+import { IUnknowObject } from '@/types';
+
+interface IResponsePramas extends IUnknowObject {
+  Ret: number;
+  Msg: string;
+}
+
+const baseURL = import.meta.env.VITE_BASEURL;
+
+
+const request: AxiosInstance = axios.create({
+  baseURL,
+  timeout:10000
+});
+
+// 请求拦截器
+request.interceptors.request.use(
+  (config) => {
+    let token: string = localStorage.getItem('token') ?? '';
+    if (token) config.headers.Authorization = token;
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  }
+);
+
+// 响应拦截器
+request.interceptors.response.use(
+  (response: AxiosResponse) => {
+    let code: number = response.data.Ret;
+    if (code === 403) {
+      ElMessage.error(response.data.Msg);
+      return;
+    }
+    if (code == 40001 || code == 408) {
+      // router.replace('/login')
+      ElMessage.error(response.data.Msg);
+      return;
+    }
+    return Promise.resolve(response.data);
+  },
+  (error) => {
+    ElMessage.error('网络异常');
+    return Promise.reject(error);
+  }
+);
+
+const get = (url: string, params?: any):Promise<IResponsePramas> =>
+  request.get(url, { params });
+const post = (
+  url: string,
+  params: any,
+  config?: AxiosRequestConfig
+):Promise<IResponsePramas> => request.post(url, params, config);
+
+export { get, post };
+
+export default request;

+ 116 - 0
src/reset.css

@@ -0,0 +1,116 @@
+* {
+	margin: 0;
+	padding: 0;
+	box-sizing: border-box;
+	-moz-box-sizing:border-box; 
+	-webkit-box-sizing:border-box;
+}
+
+html,
+body {
+	width: 100%;
+	height: 100%;
+	margin: 0 auto;
+	font-family: 'PingFangSC-Regular','微软雅黑','宋体'!important;
+	color: #333;
+	font-size: 14px;
+	background-color: #fff;
+	-webkit-overflow-scrolling: touch;
+	
+}
+/* 禁用iPhone中Safari的字号自动调整 */
+html {
+	overflow:hidden;
+}
+a {
+	text-decoration: none;
+	color: #000;
+}
+
+a:hover {
+	text-decoration: none;
+}
+span {
+	display: block;
+}
+p,
+img {
+	display: block;
+}
+
+ul,
+li {
+	list-style: none;
+}
+body,
+div,
+ul,
+li,
+ol,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+input,
+textarea,
+select,
+option,
+p,
+dl,
+dt,
+dd,
+a,
+img,
+button,
+form,
+table,
+th,
+tr,
+td,
+tbody,
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+	-webkit-tap-highlight-color: transparent;
+}
+::-webkit-input-placeholder { 
+	/* WebKit browsers */
+	color:#C1C1C1;
+}
+:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
+		color:#C1C1C1;
+}
+::-moz-placeholder { /* Mozilla Firefox 19+ */
+		color:#C1C1C1;
+}
+:-ms-input-placeholder { /* Internet Explorer 10+ */
+		color: #C1C1C1;
+}
+button {
+	border: 0 none;
+	outline: none;
+	text-align: center;
+	border-radius: 5px;
+	background-color: transparent;
+}
+input {
+	border: none;
+	border-style: none;
+	text-shadow: none;
+	outline-color: transparent;
+	box-shadow: none;
+}
+option {
+	min-height: 0 !important;
+}

+ 32 - 0
src/router/index.ts

@@ -0,0 +1,32 @@
+import { createRouter, RouteRecordRaw, createWebHistory } from 'vue-router';
+
+// 扩展路由配置 额外参数
+export type AppRouteRecordRaw = RouteRecordRaw & {
+	hidden?: boolean
+}
+
+export const routes: AppRouteRecordRaw[] = [
+  {
+    path: '/',
+    redirect:'/chartshow'
+    // component: () => import('@/views/home/index.vue'),
+    // name: '首页',
+    // hidden: false,
+  },
+  {
+    path: '/chartshow',
+    component: () => import('@/views/chartShow/index.vue'),
+    name: '图表详情',
+  },
+];
+
+const router = createRouter({
+  history: createWebHistory(), //路由模式
+  routes,
+});
+
+router.beforeEach((to,from,next) => {
+  next();
+})
+
+export default router;

+ 13 - 0
src/static/js/rem.js

@@ -0,0 +1,13 @@
+! function () {
+	var timer,
+		rem = function rem() {
+			var w = window.innerWidth >= 1200 ? 1200 : window.innerWidth;
+			document.querySelector('html').style.fontSize = parseInt(w / 1200 * 100) + 'px';
+		};
+
+	window.addEventListener('resize', function () {
+		clearTimeout(timer);
+		timer = setTimeout(rem, 10);
+	});
+	rem();
+}();

+ 21 - 0
src/store/index.ts

@@ -0,0 +1,21 @@
+import { InjectionKey } from 'vue'
+import { createStore, Store } from 'vuex';
+
+interface IState {
+  count: number;
+}
+
+export const key:InjectionKey<Store<IState>> = Symbol();
+
+const store = createStore({
+  state: {
+    count: 0,
+  },
+  mutations: {
+    add(state) {
+      state.count++;
+    },
+  },
+});
+
+export default store;

+ 85 - 0
src/types.d.ts

@@ -0,0 +1,85 @@
+import { Options } from 'highcharts';
+
+export interface IUnknowObject {
+  [key: string]: any;
+}
+
+export interface IChart extends Options {}
+
+//曲线图的数据
+export interface IDataItemProps extends IUnknowObject {
+  DataTime: string;
+  DataTimestamp: number;
+  Value: number;
+}
+
+export interface IParams extends IUnknowObject {
+  DataTimestamp: number ;
+  Value: number;
+}
+
+//季节性数据
+export interface ISeasonDataItemProps extends IUnknowObject {
+  Year: number;
+  DataList: IParams[]
+}
+
+//农历
+export interface ILunarItem extends IUnknowObject {
+  Items: IParams[];
+  Year: number;
+}
+
+//农历数据结构
+export interface ILunarParams extends IUnknowObject {
+  List: ILunarItem[]
+}
+
+//图表的包含的指标数据列
+export interface IDataProps extends IUnknowObject {
+  ChartColor: string;
+  ChartEdbMappingId: number
+  ChartInfoId: number
+  ChartStyle: string;
+  ChartWidth: number
+  DataList: Array<ISeasonDataItemProps | IParams>;
+  EdbCode: string;
+  EdbInfoId: number;
+  EdbInfoType: number;
+  EdbName: string;
+  EndDate: string;
+  Frequency: string;
+  IsAxis: number;
+  IsOrder: boolean;
+  LeadUnit: string;
+  LeadValue: number;
+  MaxData: number;
+  MinData: number;
+  ModifyTime: string;
+  Source: number;
+  SourceName: string;
+  StartDate: string;
+  Unit: string;
+}
+
+//图表详情
+export interface IChartinfo {
+  Calendar: string;
+  ChartClassifyId: number
+  ChartImage: string;
+  ChartInfoId: number
+  ChartName: string;
+  ChartType: number
+  CreateTime: string;
+  DateType: number
+  EdbInfoIds: string;
+  EndDate: string;
+  IsSetName: number
+  ModifyTime: string;
+  SeasonEndDate: string;
+  SeasonStartDate: string;
+  StartDate: string;
+  SysUserId: number;
+  SysUserRealName: string;
+  UniqueCode: string;
+}

+ 127 - 0
src/utils/chartOptions.ts

@@ -0,0 +1,127 @@
+import { IChart } from '@/types';
+
+export const defaultOpts = {
+  //图表配置
+  chart: {
+    spacing: [5, 8, 2, 8],
+  },
+  time: {
+    useUTC: false,
+  },
+  title: {
+    text: '',
+  },
+  exporting: {
+    enabled: false,
+  },
+  //默认颜色配置
+  colors: [
+    '#00f',
+    '#f00',
+    '#999',
+    '#000',
+    '#7cb5ec',
+    '#90ed7d',
+    '#f7a35c',
+    '#8085e9',
+    '#f15c80',
+    '#e4d354',
+    '#2b908f',
+    '#f45b5b',
+    '#91e8e1',
+  ],
+  //版权信息
+  credits: { enabled: false },
+  //数据列通用配置
+  plotOptions: {
+    series: {
+      turboThreshold: 0, //不限制数据点个数
+    },
+  },
+  //范围选择器
+  rangeSelector: {
+    enabled: false,
+    selected: 2,
+  },
+  //悬浮提示框
+  tooltip: {
+    split: false,
+    shared: true,
+    dateTimeLabelFormats: {
+      // 时间格式化字符
+      day: '%Y/%m/%d',
+      week: '%m/%d',
+      month: '%Y/%m',
+      year: '%Y/%m',
+    },
+    xDateFormat: '%Y/%m/%d',
+    valueDecimals: 2,
+  },
+  //图例
+  legend: {
+    enabled: true,
+    verticalAlign: "top",
+    margin: 3,
+    // layout: 'vertical'
+  },
+  //滚动条
+  scrollbar: {
+    enabled: false,
+  },
+  //导航器
+  navigator: {
+    enabled: false,
+  },
+  xAxis: {
+    tickPosition: 'inside',
+    // startOnTick: true,
+    // tickInterval: 24 * 3600 * 1000,
+    // tickPixelInterval:120,
+    lineColor: '#bfbfbf',
+    tickColor: '#bfbfbf',
+    type: 'datetime',
+    dateTimeLabelFormats: {
+      day: '%y/%m',
+      week: '%y/%m',
+      month: '%y/%m',
+      year: '%y/%m',
+    },
+  },
+  //默认y轴配置 用于季节图
+  yAxis: {
+    lineWidth: 1,
+    lineColor: '#bfbfbf',
+    tickColor: '#bfbfbf',
+    offset: 0,
+    opposite: false,
+    reversed: false,
+    visible: true,
+    gridLineWidth: 0,
+    tickWidth: 1,
+    tickPosition: 'inside',
+    endOnTick: false,
+    startOnTick: false,
+  }
+};
+
+/* 季节性图配置 */
+export const seasonOptions = {
+  //默认颜色配置
+  colors: [
+    '#4B0082',
+    '#7FFFAA',
+    '#FF4500',
+    '#808000',
+    '#EEE8AA',
+    '#849EC1',
+    '#8A4294',
+    '#578B5A',
+    '#FDA8C7',
+    '#53B3FF',
+    '#999999',
+    '#000000',
+    '#FFDF0C',
+    '#FF0000',
+    '#0033FF',
+  ],
+};

+ 239 - 0
src/utils/highcahrts-zh_CN.ts

@@ -0,0 +1,239 @@
+/**
+ * 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:any) => {
+  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: [
+        '星期天',
+        '星期一',
+        '星期二',
+        '星期三',
+        '星期四',
+        '星期五',
+        '星期六',
+      ],
+      // 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);
+};

+ 78 - 0
src/views/chartShow/index.less

@@ -0,0 +1,78 @@
+
+	.chart-show {
+		::v-deep(.el-loading-spinner) {
+			color: #333;
+			.el-icon-loading {
+				color: #333;
+				font-size: 26px;
+			}
+			.el-loading-text {
+				color: #666;
+			}
+		}
+		::v-deep(.highcharts-range-selector-group) {
+			display: none;
+		}
+		max-width: 1200px;
+		max-height: 100vh;
+		overflow: hidden;
+		position: relative;
+		margin: 0 auto;
+		border: 1px solid rgba(0, 0, 0, 0.125);
+		background: #fff;
+		border-radius: 5px;
+		.chart-header {
+			background-color: rgba(0, 0, 0, 0.03);
+			border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+			cursor: pointer;
+			min-height: 30px;
+			line-height: 30px;
+		}
+		.chart-wrapper {
+			flex: 1;
+		}
+		.bootom-source {
+			font-size: 12px;
+			padding-left: 50px;
+			padding-bottom: 5px;
+		}
+		.notfound {
+			padding: 2rem;
+			font-size: 0.25rem;
+			color: #777;
+			text-align: center;
+			.el-icon-warning {
+				margin-right: 10px;
+			}
+		}
+	}
+	@media (min-width: 1201px) {
+		.chart-header {
+			font-size: 24px;
+			padding: 2px 20px;
+		}
+		.chart-show {
+			top: 5vh;
+		}
+		.chart-wrapper {
+			// height: 700px;
+			padding: 0 10px 10px;
+			height: calc(100vh - 80px);
+			max-height: 700px;
+		}
+	}
+	@media (max-width: 1200px) {
+		.chart-header {
+			font-size: 16px;
+			padding: 0.04rem 0.2rem;
+		}
+		.chart-show {
+			width: 100%;
+		}
+		.chart-wrapper {
+			width: 100vw;
+			height: calc(100vh - 60px);
+			max-height: calc(100vw - 60px);
+			padding: 0 0.05rem 0.1rem;
+		}
+	}

+ 379 - 0
src/views/chartShow/index.vue

@@ -0,0 +1,379 @@
+<!--  -->
+<template>
+  <div class="chart-show">
+    <header class="chart-header" @click="openNew">
+      {{ chartInfo.ChartName }}
+    </header>
+    <template v-if="haveData">
+      <div
+        class="chart-wrapper"
+        v-loading="loading"
+        element-loading-spinner="el-icon-loading"
+        element-loading-text="加载中..."
+      >
+        <chart :options="options" :chartId="1" />
+      </div>
+    </template>
+    <div class="chart-wrapper notfound" v-else>
+      <i class="el-icon-warning"></i>哎吆,你的图飞了,赶快去找管理员救命吧~
+    </div>
+    <!-- <div class="bootom-source">source: <strong><em> 弘则研究</em></strong></div> -->
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs, onMounted, ref } from 'vue';
+import { useRoute } from 'vue-router';
+import chart from '@/components/chart.vue';
+import { IState } from './typing';
+import { ChartApi } from '@/request/api';
+import _ from 'lodash';
+import { IDataProps, ILunarItem, IParams, ISeasonDataItemProps } from '@/types';
+import Highcharts from 'Highcharts';
+import { defaultOpts, seasonOptions } from '@/utils/chartOptions';
+import moment from 'moment';
+
+export default defineComponent({
+  components: {
+    chart,
+  },
+  setup() {
+    const route = useRoute();
+
+    const loading = ref(false);
+
+    const haveData = ref(true);
+
+    // const code = ref('b9ce50bc8a64fd6ff88d361ed44d2de7');//公历
+    // const code = ref('3df87f3b906c074780a643dcd46dcc22'); //农历
+    // const code = ref('5292157e53beaa7a1146e8090cae308d'); //曲线
+    const code = ref(route.query.code);
+
+    const state = reactive<IState>({
+      options: {},
+      chartInfo: {},
+      dataList: [],
+    });
+
+    onMounted((): void => {
+      getChartInfo();
+    });
+
+    // 打开新窗口
+    const openNew = (): void => {
+			window.open(window.location.href,'_blank');
+    }
+
+    /* 获取图表数据信息 */
+    const getChartInfo = async () => {
+      loading.value = true;
+      try {
+        const { Data } = await ChartApi.getChart({
+          UniqueCode: code.value || '',
+        });
+        loading.value = false;
+        state.chartInfo = Data.ChartInfo;
+        state.dataList = Data.EdbInfoList;
+        document.title = Data.ChartInfo.ChartName;
+        haveData.value = true;
+        setOptions();
+      }catch (e) {
+        loading.value = false;
+        haveData.value = false;
+      }
+
+    };
+
+    // 曲线图x轴显示计算年限差 >1年 显示年/月 <=1 显示月/日
+    const xTimeDiffer = () => {
+      const end_date = state.chartInfo.DateType === 5
+        ? state.chartInfo.EndDate
+        : state.chartInfo.DateType === 6
+        ? new Date()
+        : '';
+      //年限差
+      const year_differ = moment(end_date).diff(
+        moment(state.chartInfo.StartDate),
+        'years',
+        true
+      );
+      // console.log(year_differ)
+      if (
+      state.chartInfo.DateType === 4 ||
+        ([5, 6].includes(state.chartInfo.DateType) && year_differ <= 1)
+      ) {
+        return true
+      } else {
+        return false
+      }
+    }
+
+    /* 设置options 曲线图 季节图*/
+    const setOptions = () => {
+      // ChartType: 1曲线图 2季节图  季节图中公历和农历数据结构不同
+      if (state.chartInfo.ChartType === 1) {
+
+				//拼接标题 数据列
+				let data = [] as any[],ydata = [] as any[];
+        const chartData = _.cloneDeep(state.dataList);
+        
+        chartData.forEach((item:IDataProps ,index:number) => {
+
+          //轴位置值相同的下标
+          let sameSideIndex = chartData.findIndex(
+						(i:IDataProps) => i.IsAxis === item.IsAxis
+					);
+          //y轴
+          let yItem = {
+            labels: {
+							formatter: function (ctx: any) {
+                let val = ctx.value < 1000 ? ctx.value :  ctx.value / 1000 + 'k';
+								return sameSideIndex !== index ? '' : val;
+							},
+							align: 'center',
+						},
+            opposite: item.IsAxis === 0,
+            reversed: item.IsOrder,
+            min: Number(chartData[sameSideIndex].MinData),
+						max: Number(chartData[sameSideIndex].MaxData),
+            tickWidth: sameSideIndex !== index ? 0 : 1,
+            lineWidth: 1,
+						lineColor: '#bfbfbf',
+						tickColor: '#bfbfbf',
+            offset: 0,
+						visible: true,
+						gridLineWidth: 0,
+            tickPosition: 'inside',
+						endOnTick: false,
+						startOnTick: false,
+          }
+
+          // //拼接标题 判断相同指标名称拼接来源
+					let dynamic_title = item.EdbName;
+					let dynamic_arr = chartData.filter(
+						(item: IDataProps) => dynamic_title === item.EdbName
+					);
+
+          // 拼接配置  IsAxis左轴1 右轴0 IsOrder正序false 逆序true EdbInfoType是否是领先指标
+					let dynamic_tag: string = 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 obj = {
+						data: [] as any[],
+						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),
+					};
+          item.DataList = item.DataList || []
+          for (let i of item.DataList) {
+						obj.data.push([i.DataTimestamp, i.Value]);
+					}
+
+          data.push(obj);
+					ydata.push(yItem);
+        })
+
+        // 范围为1年内 x轴显示为月/日 否则默认年/月
+        let xAxis:any = {};
+        const bool_time:boolean = xTimeDiffer();
+        xAxis = bool_time ? {
+          ...defaultOpts.xAxis,
+          labels: {
+            formatter: function (ctx: any) {
+              return Highcharts.dateFormat('%m/%d', ctx.value);
+            },
+          }
+        } : {
+          ...defaultOpts.xAxis,
+          labels: {},
+        }
+
+        state.options = {
+					series: data,
+					yAxis: ydata,
+          xAxis
+				};
+
+      } else {
+        /* 季节性图的图表配置 */
+        const chartData = state.dataList[0];
+        let seasonYdata = [],
+          seasonData = [],
+          chart = {};
+
+        /* 公历数据处理 处理数据列 y轴 */
+        if (state.chartInfo.Calendar === '公历')
+          for (let j of chartData.DataList) {
+            let serie_item = {
+              data: [] as any[],
+              type: chartData.ChartStyle,
+              yAxis: 0,
+              name: j.Year,
+            };
+            const data_array = _.cloneDeep(j.DataList);
+            data_array &&
+              data_array.forEach((item: IParams) => {
+                serie_item.data.push([item.DataTimestamp, item.Value]);
+              });
+            const index = chartData.DataList.findIndex(
+              (item: ISeasonDataItemProps) => item.Year === j.Year
+            );
+            const s_yItem = {
+              labels: {
+                formatter: function (ctx: any) {
+                  let val = ctx.value < 1000 ? ctx.value :  ctx.value / 1000 + 'k';
+                  return index !== 0 ? '' : val;
+                },
+                align: 'center',
+                x: -5,
+              },
+              max: Number(chartData.MaxData),
+              min: Number(chartData.MinData),
+              ...defaultOpts.yAxis,
+            };
+            seasonData.push(serie_item);
+            seasonYdata.push(s_yItem);
+          }
+
+        /* 农历数据处理  */
+        let filterArr =
+          state.chartInfo.Calendar === '农历'
+            ? chartData.DataList.List.filter(
+                (item: ILunarItem, index: number) => index > 0
+              )
+            : [];
+        if (state.chartInfo.Calendar === '农历')
+          for (let j of filterArr) {
+            let serie_item = {
+              data: [] as any[],
+              type: chartData.ChartStyle,
+              yAxis: 0,
+              name: j.Year,
+            };
+            const data_array = _.cloneDeep(j.Items);
+            data_array &&
+              data_array.forEach((item: IParams) => {
+                serie_item.data.push([item.DataTimestamp, item.Value]);
+              });
+            const index = filterArr.findIndex(
+              (item: ILunarItem) => item.Year === j.Year
+            );
+
+            const s_yItem = {
+              labels: {
+                formatter: function (ctx: any) {
+                  let val = ctx.value < 1000 ? ctx.value : ctx.value / 1000 + 'k';
+                  return index !== 0 ? '' : val;
+                },
+                align: 'center',
+                x: -5,
+              },
+              max: Number(chartData.MaxData),
+              min: Number(chartData.MinData),
+              ...defaultOpts.yAxis,
+            };
+            seasonData.push(serie_item);
+            seasonYdata.push(s_yItem);
+          }
+
+        // 季节图x轴显示月/日
+        const xAxis = {
+          ...defaultOpts.xAxis,
+          labels: {
+            formatter: function (ctx: any) {
+              return Highcharts.dateFormat('%m/%d', ctx.value);
+            },
+          },
+        };
+
+        // 季节图提示框显示 月/日
+        defaultOpts.tooltip = {
+          split: false,
+          shared: true,
+          dateTimeLabelFormats: {
+            // 时间格式化字符
+            day: '%m/%d',
+            week: '%m/%d',
+            month: '%m/%d',
+            year: '%m/%d',
+          },
+          xDateFormat: '%m/%d',
+          valueDecimals: 2,
+        };
+
+        //农历默认选中一年数据并隐藏按钮  公历显示全部数据
+        let rangeSelector = state.chartInfo.Calendar === '农历'
+          ? {
+              enabled: true,
+              selected: 0,
+              buttons: [
+                {
+                  type: 'month',
+                  count: 12,
+                  text: '12月',
+                },
+                {
+                  type: 'month',
+                  count: 15,
+                  text: '15月',
+                },
+                {
+                  type: 'all',
+                  text: '全部',
+                },
+              ],
+            }
+          : {
+              enabled: false,
+            };
+
+        //农历图调整顶部缩进
+        if (state.chartInfo.Calendar === '农历') chart = {
+          spacingTop: -24,
+        }
+
+        state.options = {
+          colors:
+            state.chartInfo.Calendar === '公历'
+              ? seasonOptions.colors.slice(-chartData.DataList.length)
+              : seasonOptions.colors.slice(-filterArr.length),
+          series: seasonData,
+          yAxis: seasonYdata,
+          xAxis,
+          rangeSelector,
+          chart
+        };
+      }
+    };
+
+    return {
+      ...toRefs(state),
+      loading,
+      haveData,
+      openNew
+    };
+  },
+});
+</script>
+<style scoped lang="less">
+@import './index.less';
+</style>

+ 8 - 0
src/views/chartShow/typing.ts

@@ -0,0 +1,8 @@
+import { IChartinfo, IDataProps } from "@/types";
+
+export interface IState {
+	options: any;
+	chartInfo: any | IChartinfo;
+	// dataList: IDataProps[];
+	dataList: any[];
+}

+ 21 - 0
src/views/home/index.vue

@@ -0,0 +1,21 @@
+<!--  -->
+<template>
+  <div></div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, toRefs, onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { IState } from './typing';
+export default defineComponent({
+  name: '',
+  setup() {
+    const state = reactive<IState>({});
+    onMounted((): void => {});
+    return {
+      ...toRefs(state),
+    };
+  },
+});
+</script>
+<style scoped></style>

+ 3 - 0
src/views/home/typing.ts

@@ -0,0 +1,3 @@
+export interface IState {
+	
+}

+ 21 - 0
tsconfig.json

@@ -0,0 +1,21 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "useDefineForClassFields": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "strict": true,
+    "jsx": "preserve",
+    "sourceMap": true,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "lib": ["esnext", "dom"],
+    "skipLibCheck": true,
+    "types": ["vite/client"],
+    "baseUrl": "./src",
+    "paths": {
+      "@/*": ["./*"],
+    }  
+  },
+  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
+}

+ 37 - 0
vite.config.ts

@@ -0,0 +1,37 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import legacy from '@vitejs/plugin-legacy'
+import path from 'path'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+		vue(),
+		legacy({
+			targets: ['> 1%, last 1 version, ie >= 11'],
+      additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
+			polyfills: ['es.promise.finally', 'es/map', 'es/set'],
+      modernPolyfills: ['es.promise.finally']
+    })
+	],
+  server: {
+		port: 3000,
+    cors: true,
+		host: '0.0.0.0',
+		proxy: {
+			'/v1': {
+				target: 'http://8.136.199.33:8608',
+				changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/v1/, '/v1')
+			}
+		},
+	},
+  build: {
+    outDir: 'horz_chart',
+  },
+  resolve: {
+		alias: {
+			'@': path.resolve(__dirname, './src')
+		},
+	},
+})