lrf402788946 4 年之前
父節點
當前提交
62c8d56eb3

+ 2 - 0
.env

@@ -0,0 +1,2 @@
+VUE_APP_AXIOS_BASE_URL = ''
+VUE_APP_ROUTER="platlive"

+ 33 - 0
.eslintrc.js

@@ -0,0 +1,33 @@
+// https://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  env: {
+    node: true,
+  },
+  extends: ['plugin:vue/essential', '@vue/prettier'],
+  plugins: ['vue'],
+  rules: {
+    'max-len': [
+      'warn',
+      {
+        code: 10000,
+      },
+    ],
+    'no-unused-vars': 'off',
+    'no-console': 'off',
+    'prettier/prettier': [
+      'warn',
+      {
+        singleQuote: true,
+        trailingComma: 'es5',
+        bracketSpacing: true,
+        jsxBracketSameLine: true,
+        printWidth: 160,
+      },
+    ],
+  },
+  parserOptions: {
+    parser: 'babel-eslint',
+  },
+};

+ 128 - 19
package-lock.json

@@ -2339,6 +2339,14 @@
       "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=",
       "dev": true
     },
+    "async-validator": {
+      "version": "1.8.5",
+      "resolved": "https://registry.nlark.com/async-validator/download/async-validator-1.8.5.tgz?cache=0&sync_timestamp=1619755974429&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fasync-validator%2Fdownload%2Fasync-validator-1.8.5.tgz",
+      "integrity": "sha1-3D4I7B/Q3dtn5ghC8CwM0c7G1/A=",
+      "requires": {
+        "babel-runtime": "6.x"
+      }
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
@@ -2378,6 +2386,14 @@
       "integrity": "sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk=",
       "dev": true
     },
+    "axios": {
+      "version": "0.21.1",
+      "resolved": "https://registry.npm.taobao.org/axios/download/axios-0.21.1.tgz",
+      "integrity": "sha1-IlY0gZYvTWvemnbVFu8OXTwJsrg=",
+      "requires": {
+        "follow-redirects": "^1.10.0"
+      }
+    },
     "babel-eslint": {
       "version": "10.1.0",
       "resolved": "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.1.0.tgz?cache=0&sync_timestamp=1611946213770&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-eslint%2Fdownload%2Fbabel-eslint-10.1.0.tgz",
@@ -2392,6 +2408,11 @@
         "resolve": "^1.12.0"
       }
     },
+    "babel-helper-vue-jsx-merge-props": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npm.taobao.org/babel-helper-vue-jsx-merge-props/download/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+      "integrity": "sha1-Iq69OzOQIyjlEyk6jkmSs4T58bY="
+    },
     "babel-loader": {
       "version": "8.2.2",
       "resolved": "https://registry.npm.taobao.org/babel-loader/download/babel-loader-8.2.2.tgz",
@@ -2443,6 +2464,27 @@
         "@babel/helper-define-polyfill-provider": "^0.2.0"
       }
     },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz",
+      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.nlark.com/core-js/download/core-js-2.6.12.tgz?cache=0&sync_timestamp=1620508118283&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcore-js%2Fdownload%2Fcore-js-2.6.12.tgz",
+          "integrity": "sha1-2TM9+nsGXjR8xWgiGdb2kIWcwuw="
+        },
+        "regenerator-runtime": {
+          "version": "0.11.1",
+          "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz",
+          "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
+        }
+      }
+    },
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.2.tgz?cache=0&sync_timestamp=1617714298273&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbalanced-match%2Fdownload%2Fbalanced-match-1.0.2.tgz",
@@ -2896,7 +2938,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npm.taobao.org/call-bind/download/call-bind-1.0.2.tgz?cache=0&sync_timestamp=1610403232833&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcall-bind%2Fdownload%2Fcall-bind-1.0.2.tgz",
       "integrity": "sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
         "get-intrinsic": "^1.0.2"
@@ -4097,8 +4138,7 @@
     "deepmerge": {
       "version": "1.5.2",
       "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz?cache=0&sync_timestamp=1606805746825&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-1.5.2.tgz",
-      "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M=",
-      "dev": true
+      "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M="
     },
     "default-gateway": {
       "version": "5.0.5",
@@ -4539,6 +4579,19 @@
       "integrity": "sha1-yNMYpOsnUJGQzzoIhw28vwbHTcs=",
       "dev": true
     },
+    "element-ui": {
+      "version": "2.15.1",
+      "resolved": "https://registry.npm.taobao.org/element-ui/download/element-ui-2.15.1.tgz?cache=0&sync_timestamp=1614082623756&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felement-ui%2Fdownload%2Felement-ui-2.15.1.tgz",
+      "integrity": "sha1-raAKpuMsAndKLndWPdhGaPgTzf8=",
+      "requires": {
+        "async-validator": "~1.8.1",
+        "babel-helper-vue-jsx-merge-props": "^2.0.0",
+        "deepmerge": "^1.2.0",
+        "normalize-wheel": "^1.0.1",
+        "resize-observer-polyfill": "^1.5.0",
+        "throttle-debounce": "^1.0.1"
+      }
+    },
     "elliptic": {
       "version": "6.5.4",
       "resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.4.tgz?cache=0&sync_timestamp=1612290836352&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felliptic%2Fdownload%2Felliptic-6.5.4.tgz",
@@ -5453,8 +5506,7 @@
     "follow-redirects": {
       "version": "1.14.1",
       "resolved": "https://registry.nlark.com/follow-redirects/download/follow-redirects-1.14.1.tgz?cache=0&sync_timestamp=1620555246888&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.14.1.tgz",
-      "integrity": "sha1-2RFN7Qoc/dM04WTmZirQK/2R/0M=",
-      "dev": true
+      "integrity": "sha1-2RFN7Qoc/dM04WTmZirQK/2R/0M="
     },
     "for-in": {
       "version": "1.0.2",
@@ -5549,8 +5601,7 @@
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz",
-      "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=",
-      "dev": true
+      "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
     },
     "functional-red-black-tree": {
       "version": "1.0.1",
@@ -5574,7 +5625,6 @@
       "version": "1.1.1",
       "resolved": "https://registry.npm.taobao.org/get-intrinsic/download/get-intrinsic-1.1.1.tgz?cache=0&sync_timestamp=1612364352840&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-intrinsic%2Fdownload%2Fget-intrinsic-1.1.1.tgz",
       "integrity": "sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1",
         "has": "^1.0.3",
@@ -5704,7 +5754,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz",
       "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=",
-      "dev": true,
       "requires": {
         "function-bind": "^1.1.1"
       }
@@ -5724,8 +5773,7 @@
     "has-symbols": {
       "version": "1.0.2",
       "resolved": "https://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.2.tgz?cache=0&sync_timestamp=1614443577352&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-symbols%2Fdownload%2Fhas-symbols-1.0.2.tgz",
-      "integrity": "sha1-Fl0wcMADCXUqEjakeTMeOsVvFCM=",
-      "dev": true
+      "integrity": "sha1-Fl0wcMADCXUqEjakeTMeOsVvFCM="
     },
     "has-value": {
       "version": "1.0.0",
@@ -7111,8 +7159,7 @@
     "lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.21.tgz",
-      "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=",
-      "dev": true
+      "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw="
     },
     "lodash.debounce": {
       "version": "4.0.8",
@@ -7479,6 +7526,11 @@
         "minimist": "^1.2.5"
       }
     },
+    "moment": {
+      "version": "2.29.1",
+      "resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz?cache=0&sync_timestamp=1601983423917&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.29.1.tgz",
+      "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M="
+    },
     "move-concurrently": {
       "version": "1.0.1",
       "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
@@ -7532,6 +7584,14 @@
         "thenify-all": "^1.0.0"
       }
     },
+    "naf-core": {
+      "version": "0.1.2",
+      "resolved": "https://registry.nlark.com/naf-core/download/naf-core-0.1.2.tgz",
+      "integrity": "sha1-0UetT3+BTsnSvYGPWCOVHgWAsJU=",
+      "requires": {
+        "lodash": "^4.17.11"
+      }
+    },
     "nan": {
       "version": "2.14.2",
       "resolved": "https://registry.npm.taobao.org/nan/download/nan-2.14.2.tgz",
@@ -7698,6 +7758,11 @@
       "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=",
       "dev": true
     },
+    "normalize-wheel": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npm.taobao.org/normalize-wheel/download/normalize-wheel-1.0.1.tgz",
+      "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU="
+    },
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz",
@@ -7774,8 +7839,7 @@
     "object-inspect": {
       "version": "1.10.3",
       "resolved": "https://registry.nlark.com/object-inspect/download/object-inspect-1.10.3.tgz",
-      "integrity": "sha1-wqp9LQn1DJk3VwT3oK3yTFeC02k=",
-      "dev": true
+      "integrity": "sha1-wqp9LQn1DJk3VwT3oK3yTFeC02k="
     },
     "object-is": {
       "version": "1.1.5",
@@ -9013,10 +9077,12 @@
       "dev": true
     },
     "qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz",
-      "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=",
-      "dev": true
+      "version": "6.10.1",
+      "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.10.1.tgz",
+      "integrity": "sha1-STFIL6jWR6Wqt5nFJx0hM7mB+2o=",
+      "requires": {
+        "side-channel": "^1.0.4"
+      }
     },
     "query-string": {
       "version": "4.3.4",
@@ -9293,6 +9359,14 @@
         "tough-cookie": "~2.5.0",
         "tunnel-agent": "^0.6.0",
         "uuid": "^3.3.2"
+      },
+      "dependencies": {
+        "qs": {
+          "version": "6.5.2",
+          "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz",
+          "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=",
+          "dev": true
+        }
       }
     },
     "require-directory": {
@@ -9313,6 +9387,11 @@
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
       "dev": true
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ="
+    },
     "resolve": {
       "version": "1.20.0",
       "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.20.0.tgz",
@@ -9681,6 +9760,16 @@
       "integrity": "sha1-Z6fQLHbJ2iT5nSCAj8re0ODgS+I=",
       "dev": true
     },
+    "side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npm.taobao.org/side-channel/download/side-channel-1.0.4.tgz",
+      "integrity": "sha1-785cj9wQTudRslxY1CkAEfpeos8=",
+      "requires": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      }
+    },
     "signal-exit": {
       "version": "3.0.3",
       "resolved": "https://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.3.tgz",
@@ -10503,6 +10592,11 @@
         "neo-async": "^2.6.0"
       }
     },
+    "throttle-debounce": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npm.taobao.org/throttle-debounce/download/throttle-debounce-1.1.0.tgz",
+      "integrity": "sha1-UYU9o3vmihVctugns1FKPEIuic0="
+    },
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npm.taobao.org/through/download/through-2.3.8.tgz",
@@ -11168,6 +11262,21 @@
         }
       }
     },
+    "vue-meta": {
+      "version": "2.4.0",
+      "resolved": "https://registry.nlark.com/vue-meta/download/vue-meta-2.4.0.tgz?cache=0&sync_timestamp=1621210847193&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvue-meta%2Fdownload%2Fvue-meta-2.4.0.tgz",
+      "integrity": "sha1-pBn7S0E1zpZdqzLsZB0ZicLuSEU=",
+      "requires": {
+        "deepmerge": "^4.2.2"
+      },
+      "dependencies": {
+        "deepmerge": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-4.2.2.tgz?cache=0&sync_timestamp=1606805746825&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-4.2.2.tgz",
+          "integrity": "sha1-RNLqNnm49NT/ujPwPYZfwee/SVU="
+        }
+      }
+    },
     "vue-router": {
       "version": "3.5.1",
       "resolved": "https://registry.nlark.com/vue-router/download/vue-router-3.5.1.tgz",

+ 7 - 0
package.json

@@ -8,8 +8,15 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
+    "axios": "^0.21.1",
     "core-js": "^3.6.5",
+    "element-ui": "^2.15.1",
+    "lodash": "^4.17.21",
+    "moment": "^2.29.1",
+    "naf-core": "^0.1.2",
+    "qs": "^6.10.1",
     "vue": "^2.6.11",
+    "vue-meta": "^2.4.0",
     "vue-router": "^3.2.0",
     "vuex": "^3.4.0"
   },

+ 354 - 0
public/css/codemirror.css

@@ -0,0 +1,354 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+  color: black;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+  white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+  white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+  border-left: 1px solid black;
+  border-right: none;
+  width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+  width: auto;
+  border: 0;
+  background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+  z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+  width: auto;
+  border: 0;
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+  background-color: #7e7;
+}
+@-moz-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@-webkit-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+
+.cm-s-default .cm-colorblack {color:rgb(0,0,0);}
+.cm-s-default .cm-colorblue {color:rgb(18,95,216);}
+.cm-s-default .cm-colorgreen {color:rgb(25,158,0);}
+.cm-s-default .cm-colorcyan {color:rgb(0,255,198);}
+.cm-s-default .cm-colorred {color:rgb(238,21,21);}
+.cm-s-default .cm-colormagenta {color:rgb(255,0,222);}
+.cm-s-default .cm-colorbrown {color:rgb(149,94,15);}
+.cm-s-default .cm-colorligray {color:rgb(218,218,218);}
+.cm-s-default .cm-colorgray {color:rgb(133,133,133);}
+.cm-s-default .cm-colorliblue {color:rgb(94,204,255);}
+.cm-s-default .cm-colorligreen {color:rgb(183,255,190);}
+.cm-s-default .cm-colorlicyan {color:rgb(154,255,242);}
+.cm-s-default .cm-colorlired {color:rgb(255,172,172);}
+.cm-s-default .cm-colorlimagenta {color:rgb(255,145,241);}
+.cm-s-default .cm-colorwhite {color:rgb(255,255,255);}
+.cm-s-default .cm-coloryellow {color:rgb(255,198,0);}
+
+
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  position: relative;
+  overflow: hidden;
+  background: white;
+}
+
+.CodeMirror-scroll {
+  overflow: scroll !important; /* Things will break if this is overridden */
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+  border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actuall scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+  left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  margin-bottom: -30px;
+  /* Hack to make IE7 behave */
+  *zoom:1;
+  *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+  position: absolute;
+  z-index: 4;
+  background: none !important;
+  border: none !important;
+}
+.CodeMirror-gutter-background {
+  position: absolute;
+  top: 0; bottom: 0;
+  z-index: 4;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+.CodeMirror-gutter-wrapper {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
+}
+
+.CodeMirror-lines {
+  cursor: text;
+  min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+  -webkit-tap-highlight-color: transparent;
+}
+.CodeMirror-wrap pre {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  visibility: hidden;
+}
+
+.CodeMirror-cursor { position: absolute; }
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 3;
+}
+div.CodeMirror-dragcursors {
+  visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+  background: #ffa;
+  background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursors {
+    visibility: hidden;
+  }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
+.CodeMirror {border: 1px solid #ccc;}

+ 178 - 0
public/css/phone7.tools.css

@@ -0,0 +1,178 @@
+ [v-cloak] {
+    display: none;
+}
+ .shadow{
+	width:100%;
+    position: fixed;
+    top: 0;
+    left: 0;
+    background: #000;
+    opacity: 0;
+    z-index: 99;
+}
+.app {
+    width: 80%;
+}
+.app .shadow{
+	width:100%;
+    position: fixed;
+    top: 0;
+    left: 0;
+    background: #000;
+    opacity: 0;
+    z-index: 99;
+}
+.app .tools1{
+	width: 80%;
+    position: fixed;
+    top: 120px;
+    left: 180px;
+    z-index: 999;
+    background: #fff;
+    box-shadow: 0px 4px 17px 0px rgba(0, 0, 0, 0.55);
+}
+.title{
+	height: 60px;
+	width: 100%;
+	border-bottom: 1px solid #E5E5E5;
+	line-height: 60px;
+	text-align: center;
+	font-size: 32px;
+}
+.app .tools1 .top {
+    margin-bottom:2px;
+    width: 98%;
+}
+.app .tools1 .top .toolsButton {
+    height:32px;
+    padding:0 8px;
+    cursor:pointer;
+}
+.app .tools1 .top span {
+    padding-left:2px;
+}
+.app .tools1 .top .input {
+    height:28px;
+    padding-left:5px;
+}
+.app .tools1 .top .changeIndex {
+    height:32px;
+    width:90px;
+}
+
+.app .tools1 .table {
+    width:19.5%;
+    float:left;
+    height: ;
+    background: #fff;
+}
+
+.app .tools1 .table table {
+    width:100%;
+    border-left: 1px solid #ccc;
+    border-top: 1px solid #ccc;
+    margin-left:1px;
+    /*height: 700px;*/
+}
+
+.app .tools1 .table table tbody td,.app .tools1 .table table thead th {
+    border-right: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+    height:32px;
+    padding:5px;
+    width:95px;
+}
+ .app .tools1 .table table tbody td input{  
+	background:none;  
+	outline:none;  
+	border:none;
+}
+.app .tools1 .table table td input{
+    width:50px;
+}
+
+.app .tools1 .code1 {
+    float:left;
+    width:45%;
+    height: 698px;
+    border: 1px solid #E5E5E5;
+}
+.code1 p{
+	width: 10%;
+	height: 698px;
+	float: left;
+	text-align: right;
+	background: #f7f7f7;
+	padding-right: 3px;
+}
+.code1 p span{
+	display: block;
+	height: 25px;
+	line-height: 25px;
+}
+.tools_code1{
+	resize: none;
+	height: 690px;
+	width: 88%;
+}
+.app .tools1 .cache1 {
+    float:left;
+    width:18%;
+    border:1px solid #ccc;
+    height:257px;
+    margin-left:-1px;
+    overflow-y:auto;
+}
+.app .tools1 .cache1 li {
+    padding:5px;
+    height:18px;
+}
+.app .tools1 .cache1 li.active,.app .tools1 .cache1 li:hover{
+    background-color:#26B99A;
+    color:#fff;
+    cursor:pointer;
+}
+.app .tools1 .cache1 li span{
+    float:right;
+    margin-right:5px;
+}
+.menu{
+	width: 30%;
+	height: 698px;
+	float: left;
+	border: 1px solid #e5e5e5;
+}
+.menu-ul{
+	float: left;
+	width: 36%;
+	border-right: 1px solid #E5E5E5;
+	margin-top: 20px;
+	border-top: 1px solid #E5E5E5;
+	height: 660px;
+	padding: 10px;
+} 
+.menu-ul .lione{
+	font-size: 13px;
+    line-height: 30px;
+    color: #757d81;
+    cursor: pointer;
+}
+.ul_div{
+	width: 56%;
+	height: 500px;
+	float: left;
+}
+.ul_div p{
+    margin-top: 5px;
+    margin-left: 10px;
+    width: 100%;
+    line-height: 30px;
+    cursor: pointer;
+    color: #4c525e;
+}
+.activeColor{
+	color: #1295d9!important;
+}
+.ul_div p:hover {
+    background: #26B99A;
+}

File diff suppressed because it is too large
+ 1124 - 0
public/css/tools.css


+ 14 - 0
public/index.html

@@ -5,6 +5,20 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <link rel="stylesheet" href="css/tools.css" />
+    <link rel="stylesheet" href="umychart/umychart.resource/font/iconfont.css" />
+    <link rel="stylesheet" href="css/codemirror.css" />
+    <link rel="stylesheet" href="css/phone7.tools.css?V=3" />
+    <script src="umychart/umychart.network.js"></script>
+    <script type="text/javascript" src="umychart/umychart.js?v=1.2"></script>
+    <script type="text/javascript" src="umychart/umychart.complier.js"></script>
+    <script type="text/javascript" src="umychart/umychart.index.data.js"></script>
+    <script type="text/javascript" src="js/codemirror.js"></script>
+    <script type="text/javascript" src="js/javascript.js"></script>
+    <script type="text/javascript" src="js/phone7.tools.js?v=1.1"></script>
+    <script src="js/jquery.min.js"></script>
+
+
     <title><%= htmlWebpackPlugin.options.title %></title>
   </head>
   <body>

File diff suppressed because it is too large
+ 8835 - 0
public/js/codemirror.js


+ 780 - 0
public/js/javascript.js

@@ -0,0 +1,780 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+// TODO actually recognize syntax of TypeScript constructs
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var statementIndent = parserConfig.statementIndent;
+  var jsonldMode = parserConfig.jsonld;
+  var jsonMode = parserConfig.json || jsonldMode;
+  var isTS = parserConfig.typescript;
+  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
+
+  // Tokenizer
+
+  var keywords = function(){
+    function kw(type) {return {type: type, style: "keyword"};}
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+
+    var FUNCTION_NAME=kw('functionname'); //函数名
+    var DATA_NAME={type: "dataname", style:'variable-2'}; //行情数据变量
+    var LINE_WIDTH_NAME=kw('linewidth');
+    var DRAW_NAME=kw("drawname");
+
+    //颜色
+    var COLORBLACK={type: "colorname", style:'colorblack'};
+    var COLORBLUE={type: "colorname", style:'colorblue'};
+    var COLORGREEN={type: "colorname", style:'colorgreen'};
+    var COLORCYAN={type: "colorname", style:'colorcyan'};
+    var COLORRED={type: "colorname", style:'colorred'};
+    var COLORMAGENTA={type: "colorname", style:'colormagenta'};
+    var COLORBROWN={type: "colorname", style:'colorbrown'};
+    var COLORLIGRAY={type: "colorname", style:'colorligray'};
+    var COLORGRAY={type: "colorname", style:'colorgray'};
+    var COLORLIBLUE={type: "colorname", style:'colorliblue'};
+    var COLORLIGREEN={type: "colorname", style:'colorligreen'};
+    var COLORLICYAN={type: "colorname", style:'colorlicyan'};
+    var COLORLIRED={type: "colorname", style:'colorlired'};
+    var COLORLIMAGENTA={type: "colorname", style:'colorlimagenta'};
+    var COLORWHITE={type: "colorname", style:'colorwhite'};
+    var COLORYELLOW={type: "colorname", style:'coloryellow'};
+
+    /*
+    var jsKeywords = {
+      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+      "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C,
+      "var": kw("var"), "const": kw("var"), "let": kw("var"),
+      "function": kw("function"), "catch": kw("catch"),
+      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+      "in": operator, "typeof": operator, "instanceof": operator,
+      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
+      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
+    };
+    */
+
+   var jsKeywords = 
+   {
+      "MA":FUNCTION_NAME,"MAX":FUNCTION_NAME,"REF":FUNCTION_NAME,"EXPMEMA":FUNCTION_NAME,"IF":FUNCTION_NAME,'IFF':FUNCTION_NAME,
+      "ABS":FUNCTION_NAME,"SUM":FUNCTION_NAME,"HHV":FUNCTION_NAME,"LLV":FUNCTION_NAME,"EMA":FUNCTION_NAME,
+      "SUM":FUNCTION_NAME,'RANGE':FUNCTION_NAME,'EXIST':FUNCTION_NAME,'TFILTER':FUNCTION_NAME,'CODELIKE':FUNCTION_NAME,
+      'BARSLAST':FUNCTION_NAME,'SMA':FUNCTION_NAME,'BARSCOUNT':FUNCTION_NAME,'VOLSTICK':FUNCTION_NAME,'CROSS':FUNCTION_NAME,
+      'NOT':FUNCTION_NAME,'SLOPE':FUNCTION_NAME,'NAMELIKE':FUNCTION_NAME,'BARSSINCEN':FUNCTION_NAME,'BARSSINCE':FUNCTION_NAME,
+      'ATAN':FUNCTION_NAME,'ACOS':FUNCTION_NAME,'ASIN':FUNCTION_NAME,'COS':FUNCTION_NAME,'SIN':FUNCTION_NAME,'TAN':FUNCTION_NAME,
+      'LAST':FUNCTION_NAME,'LN':FUNCTION_NAME,'LOG':FUNCTION_NAME,'EXP':FUNCTION_NAME,'SQRT':FUNCTION_NAME,"REFDATE":FUNCTION_NAME,
+      'DEVSQ':FUNCTION_NAME,'MIN':FUNCTION_NAME,'ZIG':FUNCTION_NAME,'TROUGHBARS':FUNCTION_NAME,'PEAKBARS':FUNCTION_NAME,'EVERY':FUNCTION_NAME,
+      'COST':FUNCTION_NAME,'WINNER':FUNCTION_NAME,'COUNT':FUNCTION_NAME,'FORCAST':FUNCTION_NAME,'STDP':FUNCTION_NAME,'VAR':FUNCTION_NAME,
+      'VARP':FUNCTION_NAME,'NDAY':FUNCTION_NAME,'UPNDAY':FUNCTION_NAME,'DOWNNDAY':FUNCTION_NAME,'LONGCROSS':FUNCTION_NAME,'EXISTR':FUNCTION_NAME,
+      'RELATE':FUNCTION_NAME,'COVAR':FUNCTION_NAME,'HHVBARS':FUNCTION_NAME,'LLVBARS':FUNCTION_NAME,'BETA':FUNCTION_NAME,
+      'DRAWKLINE_IF':FUNCTION_NAME,'BACKSET':FUNCTION_NAME,'SARTURN':FUNCTION_NAME,'SAR':FUNCTION_NAME,'REVERSE':FUNCTION_NAME,'SUMBARS':FUNCTION_NAME,
+      'MEMA':FUNCTION_NAME,'WMA':FUNCTION_NAME,
+
+      //动态行情函数
+      "DYNAINFO":FUNCTION_NAME,
+
+      //财务数据函数
+      'FINANCE':FUNCTION_NAME,
+
+      //融资融券
+      'MARGIN':FUNCTION_NAME,
+
+      //新闻统计
+      'NEWS':FUNCTION_NAME,
+
+      //日期类
+      'DATE':DATA_NAME,'MONTH':DATA_NAME,'YEAR':DATA_NAME,'WEEK':DATA_NAME,
+
+      //绘图函数
+      'DRAWTEXT':FUNCTION_NAME,'STICKLINE':FUNCTION_NAME,'DRAWBAND':FUNCTION_NAME,'DRAWKLINE':FUNCTION_NAME,'DRAWCHANNEL':FUNCTION_NAME,
+      'PLOYLINE':FUNCTION_NAME,'POLYLINE':FUNCTION_NAME,'DRAWNUMBER':FUNCTION_NAME,'DRAWICON':FUNCTION_NAME,'SUPERDRAWTEXT':FUNCTION_NAME,
+
+      //个股行情数据
+      "CLOSE":DATA_NAME, "C":DATA_NAME,"VOL":DATA_NAME, "V":DATA_NAME, 
+      "OPEN":DATA_NAME, "O":DATA_NAME, "HIGH":DATA_NAME, "H":DATA_NAME, "LOW":DATA_NAME,"L":DATA_NAME,"AMOUNT":DATA_NAME,
+      "CURRBARSCOUNT":DATA_NAME,
+
+      //大盘数据
+      "INDEXA":DATA_NAME,"INDEXC":DATA_NAME,"INDEXH":DATA_NAME,"INDEXL":DATA_NAME,"INDEXO":DATA_NAME,"INDEXV":DATA_NAME,'INDEXADV':DATA_NAME,'INDEXDEC':DATA_NAME,
+      'UPCOUNT':FUNCTION_NAME,'DOWNCOUNT':FUNCTION_NAME,
+
+      //线段宽度
+      'LINETHICK1':LINE_WIDTH_NAME,'LINETHICK2':LINE_WIDTH_NAME,'LINETHICK3':LINE_WIDTH_NAME,'LINETHICK4':LINE_WIDTH_NAME,'LINETHICK5':LINE_WIDTH_NAME,
+      'LINETHICK6':LINE_WIDTH_NAME,'LINETHICK7':LINE_WIDTH_NAME,'LINETHICK8':LINE_WIDTH_NAME,'LINETHICK9':LINE_WIDTH_NAME,'LINETHICK10':LINE_WIDTH_NAME,
+
+      'COLORSTICK':DRAW_NAME,'CIRCLEDOT':DRAW_NAME,'POINTDOT':DRAW_NAME,'LINESTICK':DRAW_NAME,'STICK':DRAW_NAME,'NODRAW':DRAW_NAME,
+
+      //颜色
+      'COLORYELLOW':COLORYELLOW,'COLORBLACK':COLORBLACK,'COLORBLUE':COLORBLUE,'COLORGREEN':COLORGREEN,'COLORCYAN':COLORCYAN,'COLORRED':COLORRED,
+      'COLORMAGENTA':COLORMAGENTA,'COLORBROWN':COLORBROWN,'COLORLIGRAY':COLORLIGRAY,'COLORGRAY':COLORGRAY,'COLORLIBLUE':COLORLIBLUE,'COLORLIGREEN':COLORLIGREEN,
+      'COLORLICYAN':COLORLICYAN,'COLORLIRED':COLORLIRED,'COLORLIMAGENTA':COLORLIMAGENTA,'COLORWHITE':COLORWHITE
+   };
+
+    // Extend the 'normal' keywords with the TypeScript language extensions
+    if (isTS) {
+      var type = {type: "variable", style: "variable-3"};
+      var tsKeywords = {
+        // object-like things
+        "interface": kw("interface"),
+        "extends": kw("extends"),
+        "constructor": kw("constructor"),
+
+        // scope modifiers
+        "public": kw("public"),
+        "private": kw("private"),
+        "protected": kw("protected"),
+        "static": kw("static"),
+
+        // types
+        "string": type, "number": type, "bool": type, "any": type
+      };
+
+      for (var attr in tsKeywords) {
+        jsKeywords[attr] = tsKeywords[attr];
+      }
+    }
+
+    return jsKeywords;
+  }();
+
+  var isOperatorChar = /[+\-*&%=<>!?|~^]/;
+  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
+
+  function readRegexp(stream) {
+    var escaped = false, next, inSet = false;
+    while ((next = stream.next()) != null) {
+      if (!escaped) {
+        if (next == "/" && !inSet) return;
+        if (next == "[") inSet = true;
+        else if (inSet && next == "]") inSet = false;
+      }
+      escaped = !escaped && next == "\\";
+    }
+  }
+
+  // Used as scratch variables to communicate multiple values without
+  // consing up tons of objects.
+  var type, content;
+  function ret(tp, style, cont) {
+    type = tp; content = cont;
+    return style;
+  }
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
+      return ret("number", "number");
+    } else if (ch == "." && stream.match("..")) {
+      return ret("spread", "meta");
+    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+      return ret(ch);
+    } else if (ch == "=" && stream.eat(">")) {
+      return ret("=>", "operator");
+    } else if (ch == "0" && stream.eat(/x/i)) {
+      stream.eatWhile(/[\da-f]/i);
+      return ret("number", "number");
+    } else if (/\d/.test(ch)) {
+      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+      return ret("number", "number");
+    } else if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize = tokenComment;
+        return tokenComment(stream, state);
+      } else if (stream.eat("/")) {
+        stream.skipToEnd();
+        return ret("comment", "comment");
+      } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
+               state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
+        readRegexp(stream);
+        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
+        return ret("regexp", "string-2");
+      } else {
+        stream.eatWhile(isOperatorChar);
+        return ret("operator", "operator", stream.current());
+      }
+    } else if (ch == "`") {
+      state.tokenize = tokenQuasi;
+      return tokenQuasi(stream, state);
+    } else if (ch == "#") {
+      stream.skipToEnd();
+      return ret("error", "error");
+    } else if (isOperatorChar.test(ch)) {
+      stream.eatWhile(isOperatorChar);
+      return ret("operator", "operator", stream.current());
+    } else if (wordRE.test(ch)) {
+      stream.eatWhile(wordRE);
+      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+      return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
+                     ret("variable", "variable", word);
+    }
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, next;
+      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
+        state.tokenize = tokenBase;
+        return ret("jsonld-keyword", "meta");
+      }
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) break;
+        escaped = !escaped && next == "\\";
+      }
+      if (!escaped) state.tokenize = tokenBase;
+      return ret("string", "string");
+    };
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenQuasi(stream, state) {
+    var escaped = false, next;
+    while ((next = stream.next()) != null) {
+      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      escaped = !escaped && next == "\\";
+    }
+    return ret("quasi", "string-2", stream.current());
+  }
+
+  var brackets = "([{}])";
+  // This is a crude lookahead trick to try and notice that we're
+  // parsing the argument patterns for a fat-arrow function before we
+  // actually hit the arrow token. It only works if the arrow is on
+  // the same line as the arguments and there's no strange noise
+  // (comments) in between. Fallback is to only notice when we hit the
+  // arrow, and not declare the arguments as locals for the arrow
+  // body.
+  function findFatArrow(stream, state) {
+    if (state.fatArrowAt) state.fatArrowAt = null;
+    var arrow = stream.string.indexOf("=>", stream.start);
+    if (arrow < 0) return;
+
+    var depth = 0, sawSomething = false;
+    for (var pos = arrow - 1; pos >= 0; --pos) {
+      var ch = stream.string.charAt(pos);
+      var bracket = brackets.indexOf(ch);
+      if (bracket >= 0 && bracket < 3) {
+        if (!depth) { ++pos; break; }
+        if (--depth == 0) break;
+      } else if (bracket >= 3 && bracket < 6) {
+        ++depth;
+      } else if (wordRE.test(ch)) {
+        sawSomething = true;
+      } else if (/["'\/]/.test(ch)) {
+        return;
+      } else if (sawSomething && !depth) {
+        ++pos;
+        break;
+      }
+    }
+    if (sawSomething && !depth) state.fatArrowAt = pos;
+  }
+
+  // Parser
+
+  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
+
+  function JSLexical(indented, column, type, align, prev, info) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.prev = prev;
+    this.info = info;
+    if (align != null) this.align = align;
+  }
+
+  function inScope(state, varname) {
+    for (var v = state.localVars; v; v = v.next)
+      if (v.name == varname) return true;
+    for (var cx = state.context; cx; cx = cx.prev) {
+      for (var v = cx.vars; v; v = v.next)
+        if (v.name == varname) return true;
+    }
+  }
+
+  function parseJS(state, style, type, content, stream) {
+    var cc = state.cc;
+    // Communicate our context to the combinators.
+    // (Less wasteful than consing up a hundred closures on every call.)
+    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
+
+    if (!state.lexical.hasOwnProperty("align"))
+      state.lexical.align = true;
+
+    while(true) {
+      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+      if (combinator(type, content)) {
+        while(cc.length && cc[cc.length - 1].lex)
+          cc.pop()();
+        if (cx.marked) return cx.marked;
+        if (type == "variable" && inScope(state, content)) return "variable-2";
+        return style;
+      }
+    }
+  }
+
+  // Combinator utils
+
+  var cx = {state: null, column: null, marked: null, cc: null};
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+  function register(varname) {
+    function inList(list) {
+      for (var v = list; v; v = v.next)
+        if (v.name == varname) return true;
+      return false;
+    }
+    var state = cx.state;
+    if (state.context) {
+      cx.marked = "def";
+      if (inList(state.localVars)) return;
+      state.localVars = {name: varname, next: state.localVars};
+    } else {
+      if (inList(state.globalVars)) return;
+      if (parserConfig.globalVars)
+        state.globalVars = {name: varname, next: state.globalVars};
+    }
+  }
+
+  // Combinators
+
+  var defaultVars = {name: "this", next: {name: "arguments"}};
+  function pushcontext() {
+    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+    cx.state.localVars = defaultVars;
+  }
+  function popcontext() {
+    cx.state.localVars = cx.state.context.vars;
+    cx.state.context = cx.state.context.prev;
+  }
+  function pushlex(type, info) {
+    var result = function() {
+      var state = cx.state, indent = state.indented;
+      if (state.lexical.type == "stat") indent = state.lexical.indented;
+      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
+        indent = outer.indented;
+      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
+    };
+    result.lex = true;
+    return result;
+  }
+  function poplex() {
+    var state = cx.state;
+    if (state.lexical.prev) {
+      if (state.lexical.type == ")")
+        state.indented = state.lexical.indented;
+      state.lexical = state.lexical.prev;
+    }
+  }
+  poplex.lex = true;
+
+  function expect(wanted) {
+    function exp(type) {
+      if (type == wanted) return cont();
+      else if (wanted == ";") return pass();
+      else return cont(exp);
+    };
+    return exp;
+  }
+
+  function statement(type, value) {
+    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
+    if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+    if (type == "{") return cont(pushlex("}"), block, poplex);
+    if (type == ";") return cont();
+    if (type == "if") {
+      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+        cx.state.cc.pop()();
+      return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+    }
+    if (type == "function") return cont(functiondef);
+    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+    if (type == "variable") return cont(pushlex("stat"), maybelabel);
+    if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+                                      block, poplex, poplex);
+    if (type == "case") return cont(expression, expect(":"));
+    if (type == "default") return cont(expect(":"));
+    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+                                     statement, poplex, popcontext);
+    if (type == "class") return cont(pushlex("form"), className, poplex);
+    if (type == "export") return cont(pushlex("form"), afterExport, poplex);
+    if (type == "import") return cont(pushlex("form"), afterImport, poplex);
+    return pass(pushlex("stat"), expression, expect(";"), poplex);
+  }
+  function expression(type) {
+    return expressionInner(type, false);
+  }
+  function expressionNoComma(type) {
+    return expressionInner(type, true);
+  }
+  function expressionInner(type, noComma) {
+    if (cx.state.fatArrowAt == cx.stream.start) {
+      var body = noComma ? arrowBodyNoComma : arrowBody;
+      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
+      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+    }
+
+    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
+    if (type == "function") return cont(functiondef, maybeop);
+    if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+    if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
+    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+    if (type == "quasi") { return pass(quasi, maybeop); }
+    return cont();
+  }
+  function maybeexpression(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expression);
+  }
+  function maybeexpressionNoComma(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expressionNoComma);
+  }
+
+  function maybeoperatorComma(type, value) {
+    if (type == ",") return cont(expression);
+    return maybeoperatorNoComma(type, value, false);
+  }
+  function maybeoperatorNoComma(type, value, noComma) {
+    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
+    var expr = noComma == false ? expression : expressionNoComma;
+    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+    if (type == "operator") {
+      if (/\+\+|--/.test(value)) return cont(me);
+      if (value == "?") return cont(expression, expect(":"), expr);
+      return cont(expr);
+    }
+    if (type == "quasi") { return pass(quasi, me); }
+    if (type == ";") return;
+    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
+    if (type == ".") return cont(property, me);
+    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+  }
+  function quasi(type, value) {
+    if (type != "quasi") return pass();
+    if (value.slice(value.length - 2) != "${") return cont(quasi);
+    return cont(expression, continueQuasi);
+  }
+  function continueQuasi(type) {
+    if (type == "}") {
+      cx.marked = "string-2";
+      cx.state.tokenize = tokenQuasi;
+      return cont(quasi);
+    }
+  }
+  function arrowBody(type) {
+    findFatArrow(cx.stream, cx.state);
+    return pass(type == "{" ? statement : expression);
+  }
+  function arrowBodyNoComma(type) {
+    findFatArrow(cx.stream, cx.state);
+    return pass(type == "{" ? statement : expressionNoComma);
+  }
+  function maybelabel(type) {
+    if (type == ":") return cont(poplex, statement);
+    return pass(maybeoperatorComma, expect(";"), poplex);
+  }
+  function property(type) {
+    if (type == "variable") {cx.marked = "property"; return cont();}
+  }
+  function objprop(type, value) {
+    if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property";
+      if (value == "get" || value == "set") return cont(getterSetter);
+      return cont(afterprop);
+    } else if (type == "number" || type == "string") {
+      cx.marked = jsonldMode ? "property" : (cx.style + " property");
+      return cont(afterprop);
+    } else if (type == "jsonld-keyword") {
+      return cont(afterprop);
+    } else if (type == "[") {
+      return cont(expression, expect("]"), afterprop);
+    }
+  }
+  function getterSetter(type) {
+    if (type != "variable") return pass(afterprop);
+    cx.marked = "property";
+    return cont(functiondef);
+  }
+  function afterprop(type) {
+    if (type == ":") return cont(expressionNoComma);
+    if (type == "(") return pass(functiondef);
+  }
+  function commasep(what, end) {
+    function proceed(type) {
+      if (type == ",") {
+        var lex = cx.state.lexical;
+        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
+        return cont(what, proceed);
+      }
+      if (type == end) return cont();
+      return cont(expect(end));
+    }
+    return function(type) {
+      if (type == end) return cont();
+      return pass(what, proceed);
+    };
+  }
+  function contCommasep(what, end, info) {
+    for (var i = 3; i < arguments.length; i++)
+      cx.cc.push(arguments[i]);
+    return cont(pushlex(end, info), commasep(what, end), poplex);
+  }
+  function block(type) {
+    if (type == "}") return cont();
+    return pass(statement, block);
+  }
+  function maybetype(type) {
+    if (isTS && type == ":") return cont(typedef);
+  }
+  function maybedefault(_, value) {
+    if (value == "=") return cont(expressionNoComma);
+  }
+  function typedef(type) {
+    if (type == "variable") {cx.marked = "variable-3"; return cont();}
+  }
+  function vardef() {
+    return pass(pattern, maybetype, maybeAssign, vardefCont);
+  }
+  function pattern(type, value) {
+    if (type == "variable") { register(value); return cont(); }
+    if (type == "[") return contCommasep(pattern, "]");
+    if (type == "{") return contCommasep(proppattern, "}");
+  }
+  function proppattern(type, value) {
+    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
+      register(value);
+      return cont(maybeAssign);
+    }
+    if (type == "variable") cx.marked = "property";
+    return cont(expect(":"), pattern, maybeAssign);
+  }
+  function maybeAssign(_type, value) {
+    if (value == "=") return cont(expressionNoComma);
+  }
+  function vardefCont(type) {
+    if (type == ",") return cont(vardef);
+  }
+  function maybeelse(type, value) {
+    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
+  }
+  function forspec(type) {
+    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
+  }
+  function forspec1(type) {
+    if (type == "var") return cont(vardef, expect(";"), forspec2);
+    if (type == ";") return cont(forspec2);
+    if (type == "variable") return cont(formaybeinof);
+    return pass(expression, expect(";"), forspec2);
+  }
+  function formaybeinof(_type, value) {
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
+    return cont(maybeoperatorComma, forspec2);
+  }
+  function forspec2(type, value) {
+    if (type == ";") return cont(forspec3);
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
+    return pass(expression, expect(";"), forspec3);
+  }
+  function forspec3(type) {
+    if (type != ")") cont(expression);
+  }
+  function functiondef(type, value) {
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+    if (type == "variable") {register(value); return cont(functiondef);}
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
+  }
+  function funarg(type) {
+    if (type == "spread") return cont(funarg);
+    return pass(pattern, maybetype, maybedefault);
+  }
+  function className(type, value) {
+    if (type == "variable") {register(value); return cont(classNameAfter);}
+  }
+  function classNameAfter(type, value) {
+    if (value == "extends") return cont(expression, classNameAfter);
+    if (type == "{") return cont(pushlex("}"), classBody, poplex);
+  }
+  function classBody(type, value) {
+    if (type == "variable" || cx.style == "keyword") {
+      if (value == "static") {
+        cx.marked = "keyword";
+        return cont(classBody);
+      }
+      cx.marked = "property";
+      if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
+      return cont(functiondef, classBody);
+    }
+    if (value == "*") {
+      cx.marked = "keyword";
+      return cont(classBody);
+    }
+    if (type == ";") return cont(classBody);
+    if (type == "}") return cont();
+  }
+  function classGetterSetter(type) {
+    if (type != "variable") return pass();
+    cx.marked = "property";
+    return cont();
+  }
+  function afterExport(_type, value) {
+    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
+    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
+    return pass(statement);
+  }
+  function afterImport(type) {
+    if (type == "string") return cont();
+    return pass(importSpec, maybeFrom);
+  }
+  function importSpec(type, value) {
+    if (type == "{") return contCommasep(importSpec, "}");
+    if (type == "variable") register(value);
+    if (value == "*") cx.marked = "keyword";
+    return cont(maybeAs);
+  }
+  function maybeAs(_type, value) {
+    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
+  }
+  function maybeFrom(_type, value) {
+    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+  }
+  function arrayLiteral(type) {
+    if (type == "]") return cont();
+    return pass(expressionNoComma, maybeArrayComprehension);
+  }
+  function maybeArrayComprehension(type) {
+    if (type == "for") return pass(comprehension, expect("]"));
+    if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
+    return pass(commasep(expressionNoComma, "]"));
+  }
+  function comprehension(type) {
+    if (type == "for") return cont(forspec, comprehension);
+    if (type == "if") return cont(expression, comprehension);
+  }
+
+  function isContinuedStatement(state, textAfter) {
+    return state.lastType == "operator" || state.lastType == "," ||
+      isOperatorChar.test(textAfter.charAt(0)) ||
+      /[,.]/.test(textAfter.charAt(0));
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      var state = {
+        tokenize: tokenBase,
+        lastType: "sof",
+        cc: [],
+        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+        localVars: parserConfig.localVars,
+        context: parserConfig.localVars && {vars: parserConfig.localVars},
+        indented: 0
+      };
+      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
+        state.globalVars = parserConfig.globalVars;
+      return state;
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (!state.lexical.hasOwnProperty("align"))
+          state.lexical.align = false;
+        state.indented = stream.indentation();
+        findFatArrow(stream, state);
+      }
+      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      if (type == "comment") return style;
+      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
+      return parseJS(state, style, type, content, stream);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize == tokenComment) return CodeMirror.Pass;
+      if (state.tokenize != tokenBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+      // Kludge to prevent 'maybelse' from blocking lexical scope pops
+      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
+        var c = state.cc[i];
+        if (c == poplex) lexical = lexical.prev;
+        else if (c != maybeelse) break;
+      }
+      if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
+        lexical = lexical.prev;
+      var type = lexical.type, closing = firstChar == type;
+
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
+      else if (type == "form" && firstChar == "{") return lexical.indented;
+      else if (type == "form") return lexical.indented + indentUnit;
+      else if (type == "stat")
+        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
+      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
+        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+      else return lexical.indented + (closing ? 0 : indentUnit);
+    },
+
+    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
+    blockCommentStart: jsonMode ? null : "/*",
+    blockCommentEnd: jsonMode ? null : "*/",
+    lineComment: jsonMode ? null : "//",
+    fold: "brace",
+    closeBrackets: "()[]{}''\"\"``",
+
+    helperType: jsonMode ? "json" : "javascript",
+    jsonldMode: jsonldMode,
+    jsonMode: jsonMode
+  };
+});
+
+CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("text/ecmascript", "javascript");
+CodeMirror.defineMIME("application/javascript", "javascript");
+CodeMirror.defineMIME("application/x-javascript", "javascript");
+CodeMirror.defineMIME("application/ecmascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
+
+});

File diff suppressed because it is too large
+ 4 - 0
public/js/jquery.min.js


+ 310 - 0
public/js/phone7.tools.js

@@ -0,0 +1,310 @@
+var jsEditor;
+var Root = {};
+
+var Tools = {
+    init: function (defalutData) {
+        var _this = this;
+
+        _this.bindEvent();
+        _this.bindModular();
+//      _this.setDefault(defalutData);
+    },
+    setDefault: function (defalutData) {
+        $('#tools_code').val(defalutData.script);
+        jsEditor = CodeMirror.fromTextArea($('#tools_code').get(0), {
+            mode: "javascript",
+            gutter: true,
+            lineNumbers: true
+        })
+        jsEditor.setSize('99.8%', 698);
+        jsEditor.gutter = true;
+
+        Root.top.indexName = defalutData.indexName;
+        Root.top.symbol = defalutData.symbol;
+
+        for (let i in defalutData.args) {
+            Root.table.list[i].name.value = defalutData.args[i].Name;
+            Root.table.list[i].min.value = 1;
+            Root.table.list[i].max.value = 100;
+            Root.table.list[i].value.value = defalutData.args[i].Value;
+        }
+
+        setTimeout(function () {
+            var $tradeDate = $("#txt_tradeDate");
+            if ($tradeDate.length > 0) {
+                $tradeDate.datepicker({
+                    language: 'zh-CN',
+                    autoclose: true,
+                    todayHighlight: true,
+                    format: "yyyy-mm-dd"
+                }).on('changeDate', function (el) {
+                    if (el.date != undefined) {
+                        jsChart.ChangeTradeDate(parseInt(el.date.Format("yyyyMMdd")));
+                    }
+                });
+
+                $tradeDate.datepicker("setDate", Common.formatDate(defalutData.tradeDate));
+            }
+        }, 200);
+    },
+    bindEvent: function () {
+        
+    },
+    bindModular: function () {
+        var _this = this;
+
+        Root.top = _this.getTop();
+        Root.table = _this.getTable();
+//      Root.cache = _this.getCache();
+    },
+    getTop: function () {
+        return new Vue({
+            el: '#tools_top',
+            data: {
+                indexName: "",
+                symbol: "",
+                changeIndex: 1,
+                isDebug: JS_EXECUTE_DEBUG_LOG,
+                scriptType:1
+            },
+            methods: {
+                execute: function () {
+                    var _this = this;
+
+                    if (_this.scriptType == 2) {
+                        jsChart.ChangePyScriptIndex(parseInt(_this.changeIndex), { Name: _this.indexName, Script: jsEditor.doc.getValue(), Args: Root.table.getArgs(), "Modify": false, "Change": false });
+                    } else {
+                        jsChart.ChangeScriptIndex(parseInt(_this.changeIndex), { Name: _this.indexName, Script: jsEditor.doc.getValue(), Args: Root.table.getArgs(), "Modify": false, "Change": false });
+                    }
+                },
+                change: function () {
+                    jsChart.ChangeSymbol(this.symbol);
+                },
+                changeDebug: function () {
+                    var _this = this;
+                    _this.isDebug = !_this.isDebug;
+
+                    JS_EXECUTE_DEBUG_LOG = _this.isDebug;
+                },
+                save: function () {
+                    var _this = this;
+
+                    var cacheValue = Root.cache.getValue();
+
+                    var hasIndex = -1;
+
+                    for (var i = 0; i < cacheValue.length; i++) {
+                        if (cacheValue[i].name == _this.indexName) {
+                            hasIndex = i;
+                            break;
+                        }
+                    }
+
+                    var data = {
+                        indexName: Root.top.indexName,
+                        code: jsEditor.doc.getValue(),
+                        args: Root.table.getValue()
+                    };
+
+                    if (hasIndex > -1 && confirm("【" + _this.indexName + "】已存在,是否覆盖?")) {
+                        cacheValue[hasIndex].value = data;
+                    } else {
+                        cacheValue.push({
+                            name: _this.indexName,
+                            value: data
+                        });
+                    }
+
+                    Root.cache.setValue(cacheValue);
+                }
+            },
+            watch: {
+                scriptType: function (newValue, oldValue) {
+                    if (newValue == 1)
+                        codemirror_is_caseSensitive = false
+                    else
+                        codemirror_is_caseSensitive = true;
+                },
+                
+            }
+        });
+    },
+    getTable: function () {
+        return new Vue({
+            el: '#tools_table',
+            data: {
+                list:[]
+            },
+            methods: {
+                initList: function () {
+                    var _this = this;
+                    var delfaultLength = 5;
+
+                    for (var i = 0; i < delfaultLength; i++) {
+                        _this.list.push({
+                            name: { value: "", isEdit: false },
+                            min: { value: "", isEdit: false },
+                            max: { value: "", isEdit: false },
+                            value: { value: "", isEdit: false },
+                        });
+                    }
+                },
+                openEdit: function (index, type) {
+                    var _this = this;
+
+                    _this.list[index][type].isEdit = true;
+                },
+                closeEdit: function (index, type) {
+                    var _this = this;
+
+                    if (_this.list[index][type].value == "") {
+                        _this.list[index][type].isEdit = true;
+                    }
+                    else {
+                        _this.list[index][type].isEdit = false;
+                    }
+                },
+                getArgs: function () {
+                    var _this =this;
+
+                    var args = [];
+
+                    for (var i = 0; i < _this.list.length; i++) {
+                        var name = _this.list[i].name.value,
+                            value = _this.list[i].value.value;
+
+                        if (!name || !value)
+                            continue;
+
+                        value = parseInt(value);
+
+                        args.push({
+                            Name: name,
+                            Value: value
+                        });
+                    }
+
+                    return args;
+                },
+                getValue: function () {
+                    return this.list;
+                },
+                setValue: function (list) {
+                    for (var i = 0; i < list.length; i++) {
+                        list[i].name.isEdit = false;
+                        list[i].min.isEdit = false;
+                        list[i].max.isEdit = false;
+                        list[i].value.isEdit = false;
+                    }
+
+                    this.list = list;
+                },
+                clear: function () {
+                    var _this = this;
+
+                    for (var i = 0; i < _this.list.length; i++) {
+                        if (!_this.list[i].name.value && !_this.list[i].value.value)
+                            continue;
+
+                        _this.list[i].name.value = "";
+                        _this.list[i].name.isEdit = true;
+
+                        _this.list[i].value.value = "";
+                        _this.list[i].value.isEdit = true;
+                    }
+                }
+            },
+            created: function () {
+                this.initList();
+            }
+        });
+    },
+//  getCache: function () {
+//      return new Vue({
+//          el: '#tools_cache',
+//          data: {
+//              list: [],
+//
+//              activeIndex: -1,
+//
+//              cacheKye: "i_s_c"
+//          },
+//          methods: {
+//              getList: function () {
+//                  var _this = this;
+//
+//                  _this.list = _this.getValue();
+//              },
+//              getValue: function () {
+//                  var _this = this;
+//
+//                  var cacheValue = localStorage[_this.cacheKye] || [];
+//                  cacheValue = typeof cacheValue == "string" ? JSON.parse(cacheValue) : cacheValue;
+//
+//                  return cacheValue;
+//              },
+//              setValue: function (val) {
+//                  var _this = this;
+//
+//                  localStorage[_this.cacheKye] = JSON.stringify(val);
+//
+//                  _this.getList();
+//              },
+//              select: function (index) {
+//                  var _this = this;
+//
+//                  _this.activeIndex = index;
+//
+//                  var data = _this.getValue()[index].value;
+//
+//                  Root.top.indexName = data.indexName;
+//                  jsEditor.setValue(data.code);
+//                  Root.table.setValue(data.args);
+//              },
+//              _delete: function (index) {
+//                  var _this = this;
+//
+//                  var list = _this.getValue();
+//
+//                  if (confirm("是否删除【" + list[index].name + "】")) {
+//                      list.splice(index, 1);
+//
+//                      _this.setValue(list);
+//                  }
+//              }
+//          },
+//          created: function () {
+//          	$(".shadow").css("height",document.documentElement.clientHeight+'px' )
+//              this.getList();
+//          }
+//      });
+//  }
+}
+
+var Common = {
+    formatDate: function (date) {
+        if (!date) return "";
+
+        date = date + "";
+        if (date.length != 8)
+            return date;
+
+        return new Date(date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6, 8));
+    }
+}
+
+Date.prototype.Format = function (fmt) { //author: meizz 
+    var o = {
+        "M+": this.getMonth() + 1, //月份 
+        "d+": this.getDate(), //日 
+        "h+": this.getHours(), //小时 
+        "m+": this.getMinutes(), //分 
+        "s+": this.getSeconds(), //秒 
+        "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
+        "S": this.getMilliseconds() //毫秒 
+    };
+    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+    for (var k in o)
+        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+    return fmt;
+}

File diff suppressed because it is too large
+ 20550 - 0
public/umychart/umychart.complier.js


File diff suppressed because it is too large
+ 3997 - 0
public/umychart/umychart.index.data.js


File diff suppressed because it is too large
+ 49214 - 0
public/umychart/umychart.js


+ 44 - 0
public/umychart/umychart.network.js

@@ -0,0 +1,44 @@
+/*
+   Copyright (c) 2018 jones
+ 
+    http://www.apache.org/licenses/LICENSE-2.0
+
+   开源项目 https://github.com/jones2000/HQChart
+ 
+   jones_2000@163.com
+
+   数据请求分装 可以根据不同的平台 替换网络请求模块
+*/
+
+function JSNetwork()
+{
+
+}
+
+/*
+JSNetwork.HttpReqeust=function(obj) //对请求进行封装
+{
+    $.ajax(
+        { 
+            url: obj.url, data: obj.data,
+            type:obj.type, dataType: obj.dataType,async:obj.async, 
+            success: obj.success,
+            error: obj.error,
+        }
+    );
+}
+*/
+
+
+JSNetwork.HttpRequest=function(obj) //对请求进行封装
+{
+    $.ajax(
+        { 
+            url: obj.url, data: obj.data,
+            type:obj.type, dataType: obj.dataType,async:obj.async, 
+            success: obj.success,
+            error: obj.error,
+        }
+    );
+}
+

File diff suppressed because it is too large
+ 661 - 0
public/umychart/umychart.resource/font/iconfont.css


+ 8 - 22
src/App.vue

@@ -1,32 +1,18 @@
 <template>
   <div id="app">
-    <div id="nav">
-      <router-link to="/">Home</router-link> |
-      <router-link to="/about">About</router-link>
-    </div>
     <router-view />
   </div>
 </template>
 
 <style lang="less">
-#app {
-  font-family: Avenir, Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
+.no_space {
+  margin: 0;
+  padding: 0;
 }
-
-#nav {
-  padding: 30px;
-
-  a {
-    font-weight: bold;
-    color: #2c3e50;
-
-    &.router-link-exact-active {
-      color: #42b983;
-    }
-  }
+body:extend(.no_space) {
+  // self
+}
+p:extend(.no_space) {
+  // self
 }
 </style>

+ 4 - 0
src/assets/less/var.less

@@ -0,0 +1,4 @@
+@s_main_BgC: #222629;
+@s_sec_BgC: #18191d;
+@s_thr_BgC: #000;
+@s_menu_color:#fff;

+ 0 - 130
src/components/HelloWorld.vue

@@ -1,130 +0,0 @@
-<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-    <p>
-      For a guide and recipes on how to configure / customize this project,<br />
-      check out the
-      <a href="https://cli.vuejs.org" target="_blank" rel="noopener"
-        >vue-cli documentation</a
-      >.
-    </p>
-    <h3>Installed CLI Plugins</h3>
-    <ul>
-      <li>
-        <a
-          href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
-          target="_blank"
-          rel="noopener"
-          >babel</a
-        >
-      </li>
-      <li>
-        <a
-          href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router"
-          target="_blank"
-          rel="noopener"
-          >router</a
-        >
-      </li>
-      <li>
-        <a
-          href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex"
-          target="_blank"
-          rel="noopener"
-          >vuex</a
-        >
-      </li>
-      <li>
-        <a
-          href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
-          target="_blank"
-          rel="noopener"
-          >eslint</a
-        >
-      </li>
-    </ul>
-    <h3>Essential Links</h3>
-    <ul>
-      <li>
-        <a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
-      </li>
-      <li>
-        <a href="https://forum.vuejs.org" target="_blank" rel="noopener"
-          >Forum</a
-        >
-      </li>
-      <li>
-        <a href="https://chat.vuejs.org" target="_blank" rel="noopener"
-          >Community Chat</a
-        >
-      </li>
-      <li>
-        <a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
-          >Twitter</a
-        >
-      </li>
-      <li>
-        <a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
-      </li>
-    </ul>
-    <h3>Ecosystem</h3>
-    <ul>
-      <li>
-        <a href="https://router.vuejs.org" target="_blank" rel="noopener"
-          >vue-router</a
-        >
-      </li>
-      <li>
-        <a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
-      </li>
-      <li>
-        <a
-          href="https://github.com/vuejs/vue-devtools#vue-devtools"
-          target="_blank"
-          rel="noopener"
-          >vue-devtools</a
-        >
-      </li>
-      <li>
-        <a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
-          >vue-loader</a
-        >
-      </li>
-      <li>
-        <a
-          href="https://github.com/vuejs/awesome-vue"
-          target="_blank"
-          rel="noopener"
-          >awesome-vue</a
-        >
-      </li>
-    </ul>
-  </div>
-</template>
-
-<script>
-export default {
-  name: "HelloWorld",
-  props: {
-    msg: String,
-  },
-};
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped lang="less">
-h3 {
-  margin: 40px 0 0;
-}
-ul {
-  list-style-type: none;
-  padding: 0;
-}
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-a {
-  color: #42b983;
-}
-</style>

+ 12 - 6
src/main.js

@@ -1,12 +1,18 @@
-import Vue from "vue";
-import App from "./App.vue";
-import router from "./router";
-import store from "./store";
-
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+import '@/plugins/element.js';
+import '@/plugins/axios';
+import '@/plugins/check-res';
+import '@/plugins/meta';
 Vue.config.productionTip = false;
 
 new Vue({
   router,
   store,
   render: (h) => h(App),
-}).$mount("#app");
+}).$mount('#app');
+window.vm = new Vue({
+  router,
+});

+ 19 - 0
src/plugins/axios.js

@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import AxiosWrapper from '@/util/axios-wrapper';
+
+const Plugin = {
+  install(vue, options) {
+    // 3. 注入组件
+    vue.mixin({
+      created() {
+        if (this.$store && !this.$store.$axios) {
+          this.$store.$axios = this.$axios;
+        }
+      },
+    });
+    // 4. 添加实例方法
+    vue.prototype.$axios = new AxiosWrapper(options);
+  },
+};
+
+Vue.use(Plugin, { baseUrl: process.env.VUE_APP_AXIOS_BASE_URL });

+ 39 - 0
src/plugins/check-res.js

@@ -0,0 +1,39 @@
+/* eslint-disable no-underscore-dangle */
+/* eslint-disable no-param-reassign */
+/* eslint-disable no-unused-vars */
+/* eslint-disable no-shadow */
+import Vue from 'vue';
+import _ from 'lodash';
+import { Message } from 'element-ui';
+
+const vm = new Vue({});
+const Plugin = {
+  install(Vue, options) {
+    // 4. 添加实例方法
+    Vue.prototype.$checkRes = (res, okText, errText) => {
+      let _okText = okText;
+      let _errText = errText;
+      if (!_.isFunction(okText) && _.isObject(okText) && okText != null) {
+        ({ okText: _okText, errText: _errText } = okText);
+      }
+      const { errcode = 0, errmsg } = res || {};
+      if (errcode === 0) {
+        if (_.isFunction(_okText)) {
+          return _okText();
+        }
+        if (_okText) {
+          Message.success(_okText);
+        }
+        return true;
+      }
+      if (_.isFunction(_errText)) {
+        return _errText();
+      }
+      Message.error(_errText || errmsg);
+      // Message({ message: _errText || errmsg, duration: 60000 });
+      return false;
+    };
+  },
+};
+
+Vue.use(Plugin);

+ 5 - 0
src/plugins/element.js

@@ -0,0 +1,5 @@
+import Vue from 'vue';
+import Element from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+
+Vue.use(Element);

+ 4 - 0
src/plugins/meta.js

@@ -0,0 +1,4 @@
+import Vue from 'vue';
+import Meta from 'vue-meta';
+
+Vue.use(Meta);

+ 15 - 15
src/router/index.js

@@ -1,28 +1,28 @@
-import Vue from "vue";
-import VueRouter from "vue-router";
-import Home from "../views/Home.vue";
-
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+const originalPush = VueRouter.prototype.push;
+VueRouter.prototype.push = function push(location) {
+  return originalPush.call(this, location).catch((err) => err);
+};
 Vue.use(VueRouter);
 
 const routes = [
   {
-    path: "/",
-    name: "Home",
-    component: Home,
+    path: '/',
+    name: 'index',
+    meta: { title: '球赛数据' },
+    component: () => import(/* webpackChunkName: "about" */ '../views/index/index.vue'),
   },
   {
-    path: "/about",
-    name: "About",
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () =>
-      import(/* webpackChunkName: "about" */ "../views/About.vue"),
+    path: '/kline',
+    name: 'kline',
+    meta: { title: '球赛K线图' },
+    component: () => import(/* webpackChunkName: "about" */ '../views/kline/index.vue'),
   },
 ];
 
 const router = new VueRouter({
-  mode: "history",
+  mode: 'history',
   base: process.env.BASE_URL,
   routes,
 });

+ 40 - 0
src/store/common.js

@@ -0,0 +1,40 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  commonIndexTree: `/hqserver/demo/CommonIndexTree`,
+  commonAdd: `/hqserver/demo/NewUserIndex`,
+  commonUpdate: `/hqserver/demo/UpdateUserIndex`,
+  commonDelete: `/hqserver/demo/DeleteUserIndex`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  // 指标树
+  async commonTree({ commit }) {
+    const res = await this.$axios.$post(`${api.commonIndexTree}`);
+    return res;
+  },
+
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.commonAdd}`, payload);
+    return res;
+  },
+  async update({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.commonUpdate}`, payload);
+    return res;
+  },
+  async delete({ commit }, payload) {
+    // payload:{iid:xxx}
+    const res = await this.$axios.$post(`${api.commonDelete}`, payload);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 47 - 0
src/store/gameList.js

@@ -0,0 +1,47 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  userMainMenu: `/hqserver/demo/GetUserMainMenu`,
+  mainMenu: `/hqserver/demo/GetMainMenu`,
+  gameList: `/hqserver/demo/GetGameList`,
+  historyGameList: `/hqserver/demo/GetGameListHistory`,
+  kline: (num) => `/hqserver/demo/KLine${num}`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  // 列表页-表头用户自定义显示
+  async userMainMenu({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.userMainMenu}`, payload);
+    return res;
+  },
+  // 列表页-表头列表
+  async mainMenu({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.mainMenu}`, payload);
+    return res;
+  },
+  // 列表页球赛数据
+  async gameList({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.gameList}`, payload);
+    return res;
+  },
+  // 列表页球赛历史数据
+  async historyGameList({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.historyGameList}`, payload);
+    return res;
+  },
+  // K线图
+  async kline({ commit }, { num, ...data }) {
+    const res = await this.$axios.$post(`${api.kline(num)}`, data);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 5 - 3
src/store/index.js

@@ -1,5 +1,7 @@
-import Vue from "vue";
-import Vuex from "vuex";
+import Vue from 'vue';
+import Vuex from 'vuex';
+import gameList from './gameList';
+import common from './common';
 
 Vue.use(Vuex);
 
@@ -7,5 +9,5 @@ export default new Vuex.Store({
   state: {},
   mutations: {},
   actions: {},
-  modules: {},
+  modules: { gameList, common },
 });

+ 116 - 0
src/util/axios-wrapper.js

@@ -0,0 +1,116 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import _ from 'lodash';
+import qs from 'qs';
+import Axios from 'axios';
+import { Util, Error } from 'naf-core';
+// import { Indicator } from 'mint-ui';
+
+const { trimData, isNullOrUndefined } = Util;
+const { ErrorCode } = Error;
+
+let currentRequests = 0;
+export default class AxiosWrapper {
+  constructor({ baseUrl = '', unwrap = true } = {}) {
+    this.baseUrl = baseUrl;
+    this.unwrap = unwrap;
+  }
+
+  // 替换uri中的参数变量
+  static merge(uri, query = {}) {
+    if (!uri.includes(':')) {
+      return uri;
+    }
+    const keys = [];
+    const regexp = /\/:([a-z0-9_]+)/gi;
+    let res;
+    // eslint-disable-next-line no-cond-assign
+    while ((res = regexp.exec(uri)) != null) {
+      keys.push(res[1]);
+    }
+    keys.forEach((key) => {
+      if (!isNullOrUndefined(query[key])) {
+        uri = uri.replace(`:${key}`, query[key]);
+      }
+    });
+    return uri;
+  }
+
+  $get(uri, query, options) {
+    return this.$request(uri, null, query, options);
+  }
+
+  $post(uri, data = {}, query, options) {
+    return this.$request(uri, data, query, options);
+  }
+  $delete(uri, data = {}, router, query, options = {}) {
+    options = { ...options, method: 'delete' };
+    return this.$request(uri, data, query, options, router);
+  }
+  async $request(uri, data, query, options) {
+    // TODO: 合并query和options
+    if (_.isObject(query) && _.isObject(options)) {
+      options = { ...options, params: query, method: 'get' };
+    } else if (_.isObject(query) && !query.params) {
+      options = { params: query };
+    } else if (_.isObject(query) && query.params) {
+      options = query;
+    }
+    if (!options) options = {};
+    if (options.params) options.params = trimData(options.params);
+    const url = AxiosWrapper.merge(uri, options.params);
+    currentRequests += 1;
+    // Indicator.open({
+    //   spinnerType: 'fading-circle',
+    // });
+    try {
+      const axios = Axios.create({
+        baseURL: this.baseUrl,
+      });
+      axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded;';
+      data = qs.stringify(data);
+      let res = await axios.request({
+        method: isNullOrUndefined(data) ? 'get' : 'post',
+        url,
+        data,
+        responseType: 'json',
+        ...options,
+      });
+      res = res.data;
+      const { errcode, errmsg, details } = res;
+      if (errcode) {
+        console.warn(`[${uri}] fail: ${errcode}-${errmsg} ${details}`);
+        return res;
+      }
+      // unwrap data
+      if (this.unwrap) {
+        res = _.omit(res, ['errmsg', 'details']);
+        const keys = Object.keys(res);
+        if (keys.length === 1 && keys.includes('data')) {
+          res = res.data;
+        }
+      }
+      return res;
+    } catch (err) {
+      let errmsg = '接口请求失败,请稍后重试';
+      if (err.response) {
+        const { status } = err.response;
+        if (status === 401) errmsg = '用户认证失败,请重新登录';
+        if (status === 403) errmsg = '当前用户不允许执行该操作';
+      }
+      console.error(
+        `[AxiosWrapper] 接口请求失败: ${err.config && err.config.url} - 
+        ${err.message}`
+      );
+      return { errcode: ErrorCode.SERVICE_FAULT, errmsg, details: err.message };
+    } finally {
+      /* eslint-disable */
+      currentRequests -= 1;
+      if (currentRequests <= 0) {
+        currentRequests = 0;
+        // Indicator.close();
+      }
+    }
+  }
+}

+ 0 - 5
src/views/About.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="about">
-    <h1>This is an about page</h1>
-  </div>
-</template>

+ 0 - 18
src/views/Home.vue

@@ -1,18 +0,0 @@
-<template>
-  <div class="home">
-    <img alt="Vue logo" src="../assets/logo.png" />
-    <HelloWorld msg="Welcome to Your Vue.js App" />
-  </div>
-</template>
-
-<script>
-// @ is an alias to /src
-import HelloWorld from "@/components/HelloWorld.vue";
-
-export default {
-  name: "Home",
-  components: {
-    HelloWorld,
-  },
-};
-</script>

+ 281 - 0
src/views/index/index.vue

@@ -0,0 +1,281 @@
+<template>
+  <div id="index">
+    <el-row type="flex">
+      <el-col :span="1" class="left_menu">
+        <leftMenu />
+      </el-col>
+      <el-col :span="2" class="left_sec">
+        <leftSec />
+      </el-col>
+      <el-col :span="21" class="main_view" ref="mainView">
+        <el-col :span="24">
+          <top @getHistory="getHistoryGameList" @getToday="getGameList" />
+        </el-col>
+        <el-col :span="24">
+          <mainTable :data="allList" :columns="localList" :tableHeight="tableHeight" @rightClick="rightClick" />
+        </el-col>
+      </el-col>
+    </el-row>
+    <el-dialog title="显示菜单项" :visible.sync="dialog" center :destory-on-close="true" width="800px" custom-class="hdialog">
+      <el-transfer
+        style="margin-left: 10%"
+        v-model="localSelect"
+        :data="allMenu"
+        :props="{ key: 'model', label: 'label' }"
+        :titles="['全部菜单', '自定义菜单']"
+      >
+      </el-transfer>
+      <el-row type="flex" justify="center" style="margin-top: 20px">
+        <el-col :span="4">
+          <el-button size="medium" type="info" @click="dialog = false">返回</el-button>
+        </el-col>
+        <el-col :span="4">
+          <el-button size="medium" type="primary" @click="saveLocalMenu">保存</el-button>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+const moment = require('moment');
+const _ = require('lodash');
+import mainTable from './parts/main-table.vue';
+import top from './parts/top.vue';
+import leftMenu from './parts/left-menu.vue';
+import leftSec from './parts/left-sec.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: gameList } = createNamespacedHelpers('gameList');
+export default {
+  name: 'index',
+  props: {},
+  components: { leftMenu, leftSec, top, mainTable },
+  data: function () {
+    return {
+      dialog: false,
+      list: [
+        {
+          name: '数据',
+          show: false,
+        },
+        {
+          name: '模型',
+          show: false,
+        },
+        {
+          name: '页面盒子',
+          show: false,
+        },
+      ],
+      allList: [],
+      allMenu: [],
+      localList: [],
+      localSelect: [],
+      fieldsNeedDeal: [
+        { key: '量化分析编号', model: 'dataWeekteamId' },
+        { key: '赛事编号', model: 'WeekteamId' },
+        { key: '开赛时间', model: 'start_time' },
+      ],
+      fieldsIndex: ['量化分析编号', '赛事编号', '赛事', '开赛时间', '主队', '客队', '比分', '让球', '胜', '平', '负', '让胜', '让平', '让负'],
+      tableHeight: 500,
+    };
+  },
+  created() {
+    this.show();
+  },
+  methods: {
+    ...gameList(['userMainMenu', 'mainMenu', 'gameList', 'historyGameList']),
+    show() {
+      this.getMainMenu();
+      this.getUserMainMenu();
+      let history = sessionStorage.getItem('index-history');
+      if (!history) this.getGameList();
+      else this.restoreHistory();
+    },
+    saveLocalMenu() {
+      let dup = _.cloneDeep(this.localSelect);
+      dup = dup.map((i) => {
+        const r = this.allMenu.find((f) => f.model === i);
+        if (r) return r;
+      });
+      dup = _.compact(dup);
+      dup = _.orderBy(dup, ['index'], ['asc']);
+      this.$set(this, `localList`, dup);
+      localStorage.setItem('localList', JSON.stringify(dup));
+      this.dialog = false;
+    },
+    // 获取列表表头
+    async getUserMainMenu() {
+      const res = await this.userMainMenu();
+      if (this.$checkRes(res)) {
+        let arr = [];
+        for (const key in res) {
+          const r = this.fieldsNeedDeal.find((f) => f.key === key);
+          const index = this.fieldsIndex.findIndex((f) => f === key);
+          const obj = {};
+          obj.label = key;
+          obj.index = index;
+          if (!r) obj.model = res[key];
+          else obj.model = r.model;
+          arr.push(obj);
+        }
+        arr = _.orderBy(arr, ['index'], ['asc']);
+        localStorage.setItem('localList', JSON.stringify(arr));
+        this.$set(this, `localList`, arr);
+      }
+    },
+    // 获取列表数据
+    async getMainMenu() {
+      const res = await this.mainMenu();
+      if (this.$checkRes(res)) {
+        let arr = [];
+        for (const key in res) {
+          const r = this.fieldsNeedDeal.find((f) => f.key === key);
+          const index = this.fieldsIndex.findIndex((f) => f === key);
+          const obj = {};
+          obj.label = key;
+          obj.index = index;
+          if (!r) obj.model = res[key];
+          else obj.model = r.model;
+          arr.push(obj);
+        }
+        arr = _.orderBy(arr, ['index'], ['asc']);
+        this.$set(this, `allMenu`, arr);
+      }
+    },
+    async getGameList() {
+      const res = await this.gameList();
+      if (this.$checkRes(res)) {
+        const { body } = res;
+        const { data1 } = body;
+        const arr = [];
+        for (const i of data1) {
+          const { matches } = i;
+          for (let match of matches) arr.push(this.setData(match));
+        }
+        this.$set(this, `allList`, arr);
+        this.setHistory({ method: 'getGameList' });
+      }
+    },
+    async getHistoryGameList(data) {
+      const res = await this.historyGameList(data);
+      if (this.$checkRes(res)) {
+        const { body } = res;
+        const { data1 } = body;
+        const arr = [];
+        for (const i of data1) {
+          const { matches } = i;
+          for (let match of matches) arr.push(this.setData(match));
+        }
+        this.$set(this, `allList`, arr);
+        this.hform = {};
+        this.hdialog = false;
+        this.setHistory({
+          method: 'getHistoryGameList',
+          body: data,
+        });
+      }
+    },
+    rightClick() {
+      this.dialog = true;
+      const selects = this.localList.map((i) => i.model);
+      this.$set(this, `localSelect`, selects);
+    },
+    /**
+     * 用字符串的日期换取对应的数字
+     * @param {String} str 日期字符串
+     * @return String
+     */
+    getDayNum(str) {
+      const obj = {
+        周日: '7',
+        周六: '6',
+        周五: '5',
+        周四: '4',
+        周三: '3',
+        周二: '2',
+        周一: '1',
+      };
+      return obj[str];
+    },
+    /*
+     * 设置显示所需字段
+     * @param {Object} match 每场比赛的相关数据
+     * return match 只是往里面加几个属性而已
+     */
+    setData(match) {
+      const { dayOfWeekStr, teamId, date, sellEndTime } = match;
+      const week = this.getDayNum(dayOfWeekStr);
+      match['WeekteamId'] = `${week}${teamId}`;
+      match['dataWeekteamId'] = `${date}${week}${teamId}`;
+      match['start_time'] = moment(sellEndTime).format('YYYY-MM-DD');
+      return match;
+    },
+    // 存放历史记录
+    /*
+     * @param {String} method 函数
+     * @param {Object} body 函数的body部分
+     * @param {Object} query 函数的query部分
+     */
+    setHistory(data) {
+      const { method, body, query } = data;
+      sessionStorage.setItem('index-history', JSON.stringify(data));
+    },
+    //还原历史
+    restoreHistory() {
+      let history = sessionStorage.getItem('index-history');
+      if (!history) this.getGameList();
+      history = JSON.parse(history);
+      const { method, body, query } = history;
+      // query:目前还用不到
+      this[method](body);
+    },
+    // 计算表格高度
+    computedHeight() {
+      const height = this.$refs.mainView.$el.clientHeight - 60;
+      this.$set(this, 'tableHeight', height);
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.computedHeight();
+    });
+    const that = this;
+    window.onresize = () => {
+      return (() => {
+        that.computedHeight();
+      })();
+    };
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.left_menu {
+  height: 100vh;
+  background: #222629;
+}
+.left_sec {
+  background: #18191d;
+  height: 100vh;
+  padding: 30px;
+}
+
+.main_view {
+  background: #000;
+  color: @s_menu_color;
+  height: 100vh;
+  width: 100%;
+  overflow-x: auto;
+  overflow-y: hidden;
+}
+</style>

+ 48 - 0
src/views/index/parts/left-menu.vue

@@ -0,0 +1,48 @@
+<template>
+  <div id="left-menu">
+    <p v-for="(item, index) in data" :key="`left-menu-${index}`">{{ item.name }}</p>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'left-menu',
+  props: {
+    data: {
+      type: Array,
+      default: () => [
+        {
+          name: '数据',
+          show: false,
+        },
+        {
+          name: '模型',
+          show: false,
+        },
+        {
+          name: '页面盒子',
+          show: false,
+        },
+      ],
+    },
+  },
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+p {
+  font-size: 24px;
+  color: #ffffff;
+  text-align: left;
+  font-size: 20px;
+  writing-mode: vertical-lr;
+  margin: 40px 20px;
+}
+</style>

+ 71 - 0
src/views/index/parts/left-sec.vue

@@ -0,0 +1,71 @@
+<template>
+  <div id="left-sec">
+    <p class="basket_title">我的篮子</p>
+    <el-tree :data="tree" :props="treeProp" icon-class="el-icon-plus" @node-click="handleNodeClick"></el-tree>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'left-sec',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      tree: [
+        {
+          label: '股票',
+          children: [
+            {
+              label: '股票 1',
+            },
+            {
+              label: '股票 2',
+            },
+          ],
+        },
+      ],
+      treeProp: {
+        children: 'children',
+        label: 'label',
+      },
+    };
+  },
+  created() {},
+  methods: {
+    // 树形图点击节点
+    handleNodeClick(node) {
+      console.log(node);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.basket_title {
+  font-size: 16px;
+  padding-top: 40px;
+  color: #fff;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+/deep/.el-tree {
+  background: #18191d;
+  color: #fff;
+}
+
+/deep/.el-tree-node__label {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+/deep/.el-tree-node__content:hover {
+  background-color: #18191d;
+}
+
+/deep/.el-tree-node:focus > .el-tree-node__content {
+  background-color: #18191d;
+}
+</style>

+ 64 - 0
src/views/index/parts/main-table.vue

@@ -0,0 +1,64 @@
+<template>
+  <div id="main-table">
+    <el-table
+      :data="data"
+      :height="tableHeight"
+      id="trId"
+      style="background: #000"
+      :row-style="setRowStyle"
+      :header-cell-style="setHeaderStyle"
+      @header-contextmenu="headerRightClick"
+      @row-click="rowClick"
+      ref="table"
+    >
+      <el-table-column v-for="(i, index) in columns" :key="`col-${index}`" align="center" :prop="i.model" :label="i.label"></el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'main-table',
+  props: {
+    data: { type: Array, default: () => [] },
+    columns: { type: Array, default: () => [] },
+    tableHeight: { type: Number, default: 500 },
+  },
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {
+    // 表格行样式
+    setRowStyle({ row, rowIndex }) {
+      let style = { background: '#000', color: '#c7bf3e' };
+      return style;
+    },
+    // 表格头行样式
+    setHeaderStyle() {
+      let style = { background: '#000', color: '#1f7a7c' };
+      return style;
+    },
+    //表头右键事件
+    headerRightClick(column, event) {
+      event.preventDefault();
+      this.$emit('rightClick');
+    },
+    // 点击行事件
+    rowClick(row) {
+      // let urls = 'https://www.ilikegou.cn/umychart/phone7.html?mid=' + row.id;
+      // //					let urls = "http://127.0.0.1:8020/iponekline/phone7.html?mid="+item.id
+      // window.location.href = urls;
+      this.$router.push({ path: '/kline', query: { mid: row.id } });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+table {
+  text-align: center;
+  overflow-x: auto;
+}
+</style>

+ 86 - 0
src/views/index/parts/top.vue

@@ -0,0 +1,86 @@
+<template>
+  <div id="top">
+    <el-col :span="12" class="one_line">
+      <span>自选一</span>
+      <span>自选二</span>
+      <span>主力</span>
+    </el-col>
+    <el-col :span="12" class="one_line" style="text-align: right">
+      <span @click="toGetToday()" class="can_point">查询当天</span>
+      <span @click="dialog = true" class="can_point">历史查询</span>
+    </el-col>
+
+    <el-dialog title="历史查询" :visible.sync="dialog" center :destory-on-close="true" width="500px">
+      <el-form label-position="left" label-width="100px">
+        <el-form-item label="历史日期" :required="true" :rules="[{ required: true, message: '请选择一个日期', trigger: 'blur' }]">
+          <el-date-picker v-model="form.date" type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd" placeholder="选择日期" :picker-options="pickerOptions">
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" :disabled="form.date ? false : true" @click="toRefresh('history')">查询</el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+const moment = require('moment');
+const _ = require('lodash');
+export default {
+  name: 'top',
+  props: {},
+  components: {},
+  data: function () {
+    let _self = this;
+    return {
+      dialog: false,
+      form: {},
+      pickerOptions: {
+        disabledDate: (date) => _self.checkDate(date),
+      },
+    };
+  },
+  created() {},
+  methods: {
+    toRefresh(type) {
+      if (type) {
+        let dup = _.cloneDeep(this.form);
+        this.$emit('getHistory', dup);
+        this.dialog = false;
+      } else {
+        this.$emit('getToday');
+      }
+    },
+    /**
+     * 检验时间是否在一个月范围内(后续还有vip更久的情况)
+     * @param {Date} date 某天的new Date()形式
+     * @return Boolean
+     */
+    checkDate(date) {
+      const start = moment().format('YYYY-MM-DD');
+      // TODO:end可能会变(非会员限制在一个月内)
+      const end = moment().subtract(1, 'months').format('YYYY-MM-DD');
+      return !moment(date).isBetween(end, start, null, '[]');
+    },
+  },
+  computed: {},
+};
+</script>
+
+<style lang="less" scoped>
+.one_line {
+  white-space: nowrap;
+  background: @s_sec_BgC;
+  span {
+    padding: 10px 5px;
+    height: 40px;
+    display: inline-block;
+    min-width: 60px;
+    color: #fff;
+  }
+}
+.can_point {
+  cursor: pointer;
+}
+</style>

+ 337 - 0
src/views/kline/index.vue

@@ -0,0 +1,337 @@
+<template>
+  <div id="kLineIndex">
+    <div class="left-menu">
+      <leftMenu />
+    </div>
+    <!--//****************************Franklin 创建主队K线*********************************-->
+    <div id="klineHome" style="width: 900px; height: 400px; float: left; position: relative"></div>
+    <!--//****************************Franklin 创建客队K线*********************************-->
+    <div id="klineAway" style="width: 900px; height: 400px; float: left; position: relative"></div>
+    <!--//****************************Franklin 创建主队VS客队K线*********************************-->
+    <div id="klineCalculation" style="width: 900px; height: 400px; float: left; position: relative"></div>
+  </div>
+</template>
+
+<script>
+import leftMenu from './parts/left-menu.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: gameList } = createNamespacedHelpers('gameList');
+export default {
+  name: 'kLineIndex',
+  props: {},
+  components: { leftMenu },
+  data: function () {
+    return {
+      jsChart1: undefined,
+      jsChart2: undefined,
+      jsChart3: undefined,
+    };
+  },
+  created() {},
+  mounted() {
+    this.init();
+    $(window).resize(this.resizeCanvas);
+  },
+  methods: {
+    ...gameList(['kline']),
+    init() {
+      let jsChart1, jsChart2, jsChart3;
+      let symbol = this.$route.query.name || '600000.sh';
+      let aryIndex = [];
+      let index = this.$route.query.index || 'MA';
+      aryIndex.push({
+        Index: index,
+        Modify: true,
+        Change: true,
+        Lock: {
+          IsLocked: false,
+          Callback: this.unlockIndex,
+          BG: 'rgba(200,0,40,0.5)',
+          TextColor: 'rgb(255,255,255)',
+          Text: '上锁了',
+          Font: '12px 微软雅黑',
+          Count: 10, //锁主右边几条数据
+        },
+      });
+      let jsIndexData = new JSIndexScript(); // 创建子窗口
+      let testIndex = jsIndexData.Get('MACD');
+      //指标3
+      let scriptIndex = {
+        Name: testIndex.Name,
+        Script: testIndex.Script,
+        Args: testIndex.Args,
+        Modify: false,
+        Change: true,
+      };
+      aryIndex.push(scriptIndex);
+      // 创建股票K线图   主队K线图
+      jsChart1 = JSChart.Init(document.getElementById('klineHome'));
+      var option1 = {
+        Type: '历史K线图',
+        Windows: aryIndex, //窗口指标
+        Symbol: symbol,
+        IsAutoUpate: true, //是自动更新数据
+        //工具条
+        Tool: [{ Name: '周期' }, { Name: '复权' }],
+        IsShowRightMenu: true, //右键菜单
+        //IsShowCorssCursorInfo: false,    //是否显示十字光标的刻度信息
+        ScriptError: this.ComplierError,
+        NetworkFilter: this.NetworkFilter,
+        KLine: {
+          DragMode: 1, //拖拽模式 0 禁止拖拽 1 数据拖拽 2 区间选择
+          Right: 1, //复权 0 不复权 1 前复权 2 后复权
+          Period: 0, //周期 0 日线 1 周线 2 月线 3 年线
+          MaxReqeustDataCount: 1000, //数据个数
+          PageSize: 50, //一屏显示多少数据
+          MaxRequestMinuteDayCount: 10,
+          // IndexTreeApiUrl: "https://opensourcecache.zealink.com/cache/hqh5/index/commonindextree.json",        //指标树下载地址
+          // IndexTreeApiUrl: "http://127.0.0.1:8080/demo/CommonIndexTree",        //指标树下载地址
+          IndexTreeApiUrl: 'https://www.ilikegou.cn/hqserver/demo/CommonIndexTree', //指标树下载地址
+          // IndexTreeApiUrl: "./json/commonIndexTree.json",        //指标树下载地址
+
+          //Info:["业绩预告","公告","互动易","调研","大宗交易"],       //信息地雷
+          //Info:["业绩预告","公告"],          //信息地雷
+          KLineDoubleClick: true, //双击分钟走势图
+          IsShowTooltip: true, //是否显示K线提示信息
+        },
+        //标题设置
+        KLineTitle: {
+          IsShowName: true, //不显示股票名称
+          IsShowSettingInfo: true, //不显示周期/复权
+        },
+        //画图工具
+        DrawPicture: {
+          StorageKey: 'draw-key', //给一个唯一的ID, 防止冲突
+        },
+        //边框
+        Border: {
+          Left: 60, //左边间距
+          Right: 60, //右边间距
+        },
+        //子框架设置
+        Frame: [],
+        //****************************Franklin 创建第二条K线*********************************
+        Team: 1, //1主队,2客队,3计算
+      };
+      console.warn(option1);
+      jsChart1.SetOption(option1);
+      //****************************Franklin 创建第二条K线*********************************
+      //客队K线图
+      jsChart2 = JSChart.Init(document.getElementById('klineAway'));
+      var option2 = {
+        Type: '历史K线图',
+        Windows: aryIndex, //窗口指标
+        Symbol: symbol,
+        IsAutoUpate: true, //是自动更新数据
+        //工具条
+        Tool: [{ Name: '周期' }, { Name: '复权' }],
+        IsShowRightMenu: true, //右键菜单
+        ScriptError: this.ComplierError,
+        NetworkFilter: this.NetworkFilter,
+        KLine: {
+          DragMode: 1, //拖拽模式 0 禁止拖拽 1 数据拖拽 2 区间选择
+          Right: 1, //复权 0 不复权 1 前复权 2 后复权
+          Period: 0, //周期 0 日线 1 周线 2 月线 3 年线
+          MaxReqeustDataCount: 1000, //数据个数
+          PageSize: 50, //一屏显示多少数据
+          MaxRequestMinuteDayCount: 10,
+          // IndexTreeApiUrl: "https://opensourcecache.zealink.com/cache/hqh5/index/commonindextree.json",        //指标树下载地址
+          // IndexTreeApiUrl: "http://127.0.0.1:8080/demo/CommonIndexTree",        //指标树下载地址
+          IndexTreeApiUrl: 'https://www.ilikegou.cn/hqserver/demo/CommonIndexTree', //指标树下载地址
+          //Info:["业绩预告","公告","互动易","调研","大宗交易"],       //信息地雷
+          //Info:["业绩预告","公告"],          //信息地雷
+          KLineDoubleClick: true, //双击分钟走势图
+          IsShowTooltip: true, //是否显示K线提示信息
+        },
+        //标题设置
+        KLineTitle: {
+          IsShowName: true, //不显示股票名称
+          IsShowSettingInfo: true, //不显示周期/复权
+        },
+        //画图工具
+        DrawPicture: {
+          StorageKey: 'draw-key', //给一个唯一的ID, 防止冲突
+        },
+        //边框
+        Border: {
+          Left: 60, //左边间距
+          Right: 60, //右边间距
+        },
+        //子框架设置
+        Frame: [],
+        Team: 2, //1主队,2客队,3计算
+      };
+      jsChart2.SetOption(option2);
+      //****************************Franklin 创建第三条K线*********************************
+      //主客队数据计算K线图
+      jsChart3 = JSChart.Init(document.getElementById('klineCalculation'));
+      var arrIndexCalculation = new Array();
+      arrIndexCalculation.push(aryIndex[0]); //计算K线没有指标图
+      let testIndex1 = jsIndexData.Get('MACD12');
+      let scriptIndextemp = {
+        Name: testIndex1.Name,
+        Script: testIndex1.Script,
+        Args: testIndex1.Args,
+        Modify: true,
+        Change: true,
+      };
+      arrIndexCalculation.push(scriptIndextemp);
+      var option3 = {
+        Type: '历史K线图',
+        Windows: arrIndexCalculation, //窗口指标
+        Symbol: symbol,
+        IsAutoUpate: true, //是自动更新数据
+        //工具条
+        Tool: [{ Name: '周期' }, { Name: '复权' }],
+        IsShowRightMenu: true, //右键菜单
+        ScriptError: this.ComplierError,
+        NetworkFilter: this.NetworkFilter,
+        KLine: {
+          DragMode: 1, //拖拽模式 0 禁止拖拽 1 数据拖拽 2 区间选择
+          Right: 1, //复权 0 不复权 1 前复权 2 后复权
+          Period: 0, //周期 0 日线 1 周线 2 月线 3 年线
+          MaxReqeustDataCount: 1000, //数据个数
+          PageSize: 50, //一屏显示多少数据
+          MaxRequestMinuteDayCount: 10,
+          IndexTreeApiUrl: 'https://www.ilikegou.cn/hqserver/demo/CommonIndexTree', //指标树下载地址
+          KLineDoubleClick: true, //双击分钟走势图
+          IsShowTooltip: true, //是否显示K线提示信息
+        },
+        //标题设置
+        KLineTitle: {
+          IsShowName: true, //不显示股票名称
+          IsShowSettingInfo: true, //不显示周期/复权
+        },
+        //画图工具
+        DrawPicture: {
+          StorageKey: 'draw-key', //给一个唯一的ID, 防止冲突
+        },
+        //边框
+        Border: {
+          Left: 60, //左边间距
+          Right: 60, //右边间距
+        },
+        //子框架设置
+        Frame: [],
+        Team: 3, //1主队,2客队,3计算
+      };
+      jsChart3.SetOption(option3);
+      this.resizeCanvas();
+      this.$set(this, `jsChart1`, jsChart1);
+      this.$set(this, `jsChart2`, jsChart2);
+      this.$set(this, `jsChart3`, jsChart3);
+    },
+    ComplierError(error) {
+      alert(error);
+      console.log('[ComplierError]', error);
+    },
+    //获取k线数据
+    async NetworkFilter(data, callback) {
+      console.log('[NetworkFilter] data', data);
+      console.log('franklin html');
+      var mid = this.$route.query.mid; //****************************Franklin K线请求参数*********************************
+      data.PreventDefault = true;
+      switch (data.Name) {
+        case 'KLineChartContainer::ReqeustHistoryMinuteData': //1分钟全量数据下载
+          break;
+        case 'KLineChartContainer::RequestMinuteRealtimeData': //1分钟增量数据更新
+          break;
+        case 'KLineChartContainer::RequestHistoryData': //日线全量数据下载
+          console.log('franklin html 376', data);
+          const reqData = {
+            num: data.Team,
+            data: mid,
+            field: ['name', 'symbol', 'yclose', 'open', 'price', 'high', 'low', 'vol'],
+            symbol: data.Request.Data.symbol,
+            start: -1,
+            count: data.Request.Data.count,
+          };
+          const res = await this.kline(reqData);
+          if (this.$checkRes(res)) {
+            callback(res);
+          }
+          break;
+        case 'KLineChartContainer::RequestRealtimeData': //日线增量数据更新
+          break;
+        case 'APIScriptIndex::ExecuteScript':
+          console.log('[NetworkFilter] index name ', data.Request.Data.indexname);
+          break;
+      }
+    },
+
+    resizeCanvas() {
+      // var height= $(window).height()-300; //300高度给指标编辑器
+      // 计算k线框体大小
+      var height = $(window).height() / 3; //300高度给指标编辑器
+      var width = $(window).width() - 80;
+      //****************************Franklin 创建第一条K线*********************************
+      var divKlineHome = document.getElementById('klineHome');
+      divKlineHome.style.width = width + 'px';
+      divKlineHome.style.height = height + 'px';
+      divKlineHome.JSChart.OnSize();
+      //****************************Franklin 创建第二条K线*********************************
+      var divKlineAway = document.getElementById('klineAway');
+      divKlineAway.style.width = width + 'px';
+      divKlineAway.style.height = height + 'px';
+      divKlineAway.JSChart.OnSize();
+      //****************************Franklin 创建第三条K线*********************************
+      var divKlineCalculation = document.getElementById('klineCalculation');
+      divKlineCalculation.style.width = width + 'px';
+      divKlineCalculation.style.height = height + 'px';
+      divKlineCalculation.JSChart.OnSize();
+    },
+
+    unlockIndex(info) {
+      console.log(info);
+      var lockData = { IndexName: info.Data.IndexName, IsLocked: false };
+      info.HQChart.LockIndex(lockData);
+    },
+
+    GetUserBuy(obj) {
+      console.log('[GetUserBuy] obj ', obj);
+      var result = [];
+      for (var i in obj.KData.Data) {
+        result[i] = 0;
+        var item = obj.KData.Data[i];
+        if (USER_BUY.has(item.Date)) result[i] = USER_BUY.get(item.Date).Count;
+      }
+
+      return result;
+    },
+    DownloadUserBuy(obj) {
+      // TODO:待修改
+      console.log('[DownloadUserBuy] obj ', obj);
+
+      setTimeout(() => {
+        const USER_BUY = new Map([
+          [20190705, { Count: 1 }],
+          [20190714, { Count: 3 }],
+          [20190806, { Count: 1 }],
+          [20190826, { Count: 1 }],
+          [20190902, { Count: 2 }],
+        ]);
+        obj.Success();
+      }, 500);
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.left-menu {
+  background: #18191d;
+  color: #fff;
+  width: 60px;
+  float: left;
+  height: 100vh;
+}
+</style>

+ 290 - 0
src/views/kline/parts/left-menu.vue

@@ -0,0 +1,290 @@
+<template>
+  <div id="left-menu">
+    <el-link class="word" :underline="false" @click="dialog = true"> 编程</el-link>
+    <el-dialog title="编程" width="80%" center :visible.sync="dialog" :close-on-click-modal="false" :destory-on-close="false">
+      <el-row class="rows" :gutter="20">
+        <el-col :span="24">
+          指标名称:
+          <el-input v-model="form.Name" size="small" placeholder="请输入指标名称" class="inner_element_lr" style="width: 200px" />
+          <el-select v-model="form.IsMainIndex" placeholder="请选择指标显示的位置" size="small" class="inner_element_lr" style="width: 200px">
+            <el-option v-for="(i, index) in position" :key="`pos-${index}`" :label="i.label" :value="i.value"></el-option>
+          </el-select>
+          <el-button type="primary" size="small">执行</el-button>
+          <el-button size="small" v-if="form.Script && form.Script.length > 0" @click="cleanScript">清空脚本</el-button>
+          <el-button type="success" size="small" @click="toSaveCommon">保存</el-button>
+          <el-link type="primary" @click="toHelp" class="inner_element_lr">函数帮助</el-link>
+          <el-select v-model="form.type" placeholder="请选择脚本类型" size="small" class="inner_element_lr" style="width: 200px">
+            <el-option v-for="(i, index) in typeList" :key="`type-${index}`" :label="i.label" :value="i.value"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="8">
+          <el-table :data="form.Args" border>
+            <el-table-column align="center" v-for="(i, index) in pararmsColumn" :key="`ArgsCol-${index}`" :label="i.label">
+              <template v-slot="{ row }">
+                <el-input v-model="row[i.model]" size="small"></el-input>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-col>
+        <el-col :span="8">
+          <el-input
+            v-model="form.Script"
+            type="textarea"
+            :autosize="{ minRows: 30, maxRows: 30 }"
+            placeholder="请在此处输入脚本或选择右侧已有指标进行脚本编辑"
+          ></el-input>
+        </el-col>
+        <el-col :span="8" style="border: 1px #c0c4cc solid">
+          <el-tabs tab-position="left">
+            <el-tab-pane v-for="(i, index) in commonTrees" :key="`comTree-tabs-${index}`" :label="i.node">
+              <el-menu default-active="2" style="max-height: 635px; overflow-x: auto">
+                <el-menu-item v-for="(o, oIndex) in i.list" :key="`common-${index}-${oIndex}`" :index="o.ID" @click="selectItem(o)">{{ o.Name }}</el-menu-item>
+              </el-menu>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: common } = createNamespacedHelpers('common');
+export default {
+  name: 'left-menu',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      dialog: false,
+      position: [
+        { label: '副图', value: '0' },
+        { label: '主图', value: '1' },
+      ],
+      typeList: [
+        { label: '通达信脚本', value: '1' },
+        { label: 'py脚本', value: '2' },
+      ],
+      pararmsColumn: [
+        { label: '参数名', model: 'label' },
+        { label: '最小值', model: 'min' },
+        { label: '最大值', model: 'max' },
+        { label: '缺省值', model: 'def' },
+      ],
+      commonTrees: [],
+      form: {
+        Script: 'WR1:100*(HHV(HIGH,N)-CLOSE)/(HHV(HIGH,N)-LLV(LOW,N));\nWR2:100*(HHV(HIGH,N1)-CLOSE)/(HHV(HIGH,N1)-LLV(LOW,N1));',
+        Args: [],
+      },
+    };
+  },
+  created() {
+    this.getCommonTree();
+  },
+  methods: {
+    ...common(['commonTree', 'create', 'update', 'delete']),
+    selectItem(item) {
+      console.warn(item);
+      let dup = _.cloneDeep(item);
+      dup.Args = dup.Args.map((i) => {
+        const obj = {};
+        obj.label = i.Name;
+        obj.def = i.Value;
+        return obj;
+      });
+      dup.IsMainIndex = dup.IsMainIndex ? '1' : '0';
+      this.$set(this, 'form', dup);
+    },
+    toSaveCommon() {
+      let dup = _.cloneDeep(this.form);
+      const checkDataRes = this.checkData(dup);
+      if (!checkDataRes) return;
+      // 验证都合格后,处理参数
+      const obj = {};
+      const Args = _.get(dup, 'Args', []);
+      for (let i = 0; i < Args.length; i++) {
+        const { label, min, max, def } = Args[i];
+        if (!label || !def) continue;
+        obj[`ArgsName${i + 1}`] = label;
+        obj[`ArgsValue${i + 1}`] = def;
+        // TODO:min和max没处理
+      }
+      dup = { ...dup, ...obj };
+      // TODO:缺少的属性,先踢出去,问完之后决定
+      dup = _.omit(dup, ['Args', 'type']);
+      // 如果Name重复了,那就需要提示,然后走最后的提交函数,接接口
+      this.checkNameRepeat(dup);
+    },
+    /**
+     * 最后与接口对接的函数
+     */
+    async saveCommon(data) {
+      let res;
+      if (data.ID || data.iid) {
+        res = await this.update({ data: JSON.stringify(data) });
+      } else {
+        res = await this.create({ data: JSON.stringify(data) });
+      }
+      if (this.$checkRes(res, '保存成功', '保存失败')) {
+        this.getCommonTree();
+        // TODO:接口返回处理完,要干什么
+        this.resetForm();
+      }
+    },
+    /**
+     * 校验参数
+     * @param {Object} object 参数
+     * @return {Boolean} true:合格/false:不合格
+     */
+    checkData(object) {
+      let result = true;
+      const vailList = [
+        { model: 'Name', required: true, message: '请填写指标名称' },
+        { model: 'IsMainIndex', required: true, message: '请选择指标显示的位置' },
+        { model: 'Script', required: true, message: '请输入指标的脚本' },
+        { model: 'type', required: false, message: '请选择脚本类型' },
+      ];
+      for (const vail of vailList) {
+        const { model, required, message } = vail;
+        if (!required) continue;
+        const val = object[model];
+        if (!val) {
+          this.$message.error(message);
+          result = false;
+          break;
+        }
+      }
+      return result;
+    },
+    /**
+     * 检查名字有没有重复,如果有重复,需要返回是覆盖,还是重更名
+     * @return {Boolean} true:覆盖/false:没有重复/不覆盖,反正就不处理了
+     */
+    async checkNameRepeat(object) {
+      // TODO 需要检测是修改还是新增,修改的话:在点击时会进行赋值,需要询问
+      let obj = this.getSameOne(object);
+      if (!obj) {
+        this.saveCommon(object);
+        return;
+      }
+      this.$confirm('该指标已存在,是否覆盖原指标设置?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          const needs = _.pick(obj, ['ID', 'iid']);
+          object = { ...object, ...needs };
+          this.saveCommon(object);
+        })
+        .catch(() => {
+          this.$message.warning('清修改指标名称');
+        });
+    },
+    /**
+     * 获取名字重复的Object
+     * @param {Object} Object 当前数据
+     * @return  Object=>与之名称重复的;undefined:没有重复的
+     */
+    getSameOne(object) {
+      let dup = _.cloneDeep(this.commonTrees);
+      let arr = _.flattenDeep(dup.map((i) => i.list));
+      let obj = arr.find((f) => f.Name === object.Name);
+      return obj;
+    },
+    /**
+     * 重置编程表单
+     */
+    resetForm() {
+      this.$set(this, 'form', { Args: [] });
+    },
+    /**
+     * 脚本帮助
+     */
+    toHelp() {
+      const url = 'http://opensourcecdn.zealink.com/cache/webcache/hqfunctionhelp/index.html#/10';
+      window.open(url);
+    },
+    /**
+     * 请求指标数
+     */
+    async getCommonTree() {
+      const res = await this.commonTree();
+      if (this.$checkRes(res)) {
+        this.$set(this, `commonTrees`, res.list);
+      }
+    },
+    /**
+     * 快速清除脚本的按钮,不要就扔了,自己加的,也不是啥难的东西
+     */
+    cleanScript() {
+      let dup = _.cloneDeep(this.form);
+      delete dup.Script;
+      this.$set(this, 'form', dup);
+    },
+  },
+  watch: {
+    'form.Args': {
+      // 目的:监控参数列表的最后一条数据是否是空,若不是空,则加上一个行,是空的不加数据
+      // 总数组长度(总长度:l) <= 0 => 加一个 :保持数组最小长度为1;对应页面映射:参数列表最少有一条可以填写
+      // 若 l >= 1 => 判断最后一条数据(object)的值(lvl:last values length)是否存在(getObjectLength获取)
+      //    若 lvl > 0 => 自动向尾行添加一行空数据;
+      //    若 lvl<=0 => 接下来就要做减去尾行的操作 ↓↓↓;
+      //        取出倒数第二条数据(lodash的_.nth(Array,Number)=>取出Array数组中索引Number的数据,Number为负数时,倒着取;看不懂就查lodash文档即可,再看不懂...┓( ´∀` )┏,火候还差一把,自己烧下吧)
+      //        然后继续查有没有值的操作=> secvl(last but one=>太长了,直接sec得了 values length)
+      //        若 secvl <= 0;说明倒数第二项也没有任何值,可以删掉最后一个;反之不能删,否则进入无限循环
+      // 最后说下 getObjectLength(object) 函数,这个函数主要使用 Object类 中获取所有 值 形成一个数组;
+      // 只要输入过,即使没有值,也会用 '' 站位,所以在过滤(filter)条件为 存在 且 不为 引号空值;
+      // 可能用的更多的Object.keys()(至少这东西我用的多),但是因为上述问题(↑),key一定会存在,所以不适用,要是非得用,也得判断value,是吧?
+      handler(val) {
+        const l = val.length;
+        const getObjectLength = (object) => Object.values(object).filter((f) => f && f !== '').length;
+        if (l <= 0) this.form.Args.push({});
+        else {
+          const last = _.last(val);
+          let lvl = getObjectLength(last); //列表最后一项的有多少填写内容的数
+          if (lvl > 0) this.form.Args.push({});
+          else if (l > 1) {
+            const sec = _.nth(val, -2);
+            const secvl = getObjectLength(sec); //列表倒数第二项的值有多少填写内容的数
+            if (secvl <= 0) this.form.Args.pop();
+          }
+        }
+      },
+      immediate: true,
+      deep: true,
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.word {
+  font-size: 24px;
+  color: #fff !important;
+  padding-top: 30px;
+  padding-left: 5px;
+}
+.rows {
+  .el-col {
+    padding: 5px;
+  }
+}
+.inner_element_lr {
+  margin: 0 10px;
+}
+.inner_element_ud {
+  margin: 10px 0;
+}
+</style>

+ 43 - 0
vue.config.js

@@ -0,0 +1,43 @@
+const path = require('path');
+module.exports = {
+  publicPath: `/`,
+  // 打包文件
+  outputDir: 'platlive',
+  productionSourceMap: false,
+  configureWebpack: (config) => {
+    Object.assign(config, {
+      // 开发生产共同配置
+      resolve: {
+        alias: {
+          '@': path.resolve(__dirname, './src'),
+          '@c': path.resolve(__dirname, './src/components'),
+          '@a': path.resolve(__dirname, './src/assets'),
+        },
+      },
+    });
+  },
+  css: {
+    loaderOptions: {
+      less: {
+        globalVars: {
+          hack: `true; @import '~@/assets/less/var.less';`,
+        },
+      },
+    },
+  },
+  devServer: {
+    port: '8001',
+    //api地址前缀
+    proxy: {
+      '/hqserver': {
+        target: 'https://www.ilikegou.cn', //http://192.168.1.19:9101
+        changeOrigin: true,
+        ws: false,
+        // pathRewrite: {
+        //   // 路径重写
+        //   '/api': '', // 这个意思就是以api开头的,定向到哪里, 如果你的后边还有路径的话, 会自动拼接上
+        // },
+      },
+    },
+  },
+};