liuqiao 1 рік тому
батько
коміт
b56e6cf177

+ 14 - 0
.editorconfig

@@ -0,0 +1,14 @@
+#编辑配置 http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 12 - 0
.env.development

@@ -0,0 +1,12 @@
+# 开发环境
+# 使用VUE_CLI_BABEL_TRANSPILE_MODULES环境变量,控制是否启用babel-plugin-dynamic-import-node插件,将所有import()转换为require()。
+# 当有大量的页面时,这种配置可以显著提高热更新的速度。
+
+ENV = 'development'
+
+VUE_APP_BASE_API = '/api'
+VUE_APP_BASE_websokt = '/192.168.0.131'
+//VUE_APP_BASE_websokt = '/192.168.0.43'
+//VUE_APP_BASE_websokt = '/www.adicn.com'
+
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 7 - 0
.env.production

@@ -0,0 +1,7 @@
+# 正式环境
+
+ENV = 'production'
+
+VUE_APP_BASE_API = '/tadi'
+
+VUE_APP_BASE_websokt = '/www.adicn.com'

+ 8 - 0
.env.staging

@@ -0,0 +1,8 @@
+# 测试环境
+
+NODE_ENV = production
+
+ENV = 'staging'
+
+VUE_APP_BASE_API = '/stage-api'
+

+ 6 - 0
.eslintignore

@@ -0,0 +1,6 @@
+# 忽略eslint校验文件
+
+build/*.js
+src/assets
+public
+dist

+ 197 - 0
.eslintrc.js

@@ -0,0 +1,197 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint',
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true,
+  },
+  extends: ['plugin:vue/recommended', 'eslint:recommended'],
+
+  // 在此处添加自定义规则
+  rules: {
+    "vue/max-attributes-per-line": [2, {
+      "singleline": 10,
+      "multiline": {
+        "max": 1,
+        "allowFirstLine": false
+      }
+    }],
+    "vue/singleline-html-element-content-newline": "off",
+    "vue/multiline-html-element-content-newline": "off",
+    "vue/name-property-casing": ["error", "PascalCase"],
+    "vue/no-v-html": "off",
+    'accessor-pairs': 2,
+    'arrow-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'block-spacing': [2, 'always'],
+    'brace-style': [2, '1tbs', {
+      'allowSingleLine': true
+    }],
+    'camelcase': [0, {
+      'properties': 'always'
+    }],
+    'comma-dangle': [2, 'never'],
+    'comma-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'comma-style': [2, 'last'],
+    'constructor-super': 2,
+    'curly': [2, 'multi-line'],
+    'dot-location': [2, 'property'],
+    'eol-last': 2,
+    'eqeqeq': ["error", "always", { "null": "ignore" }],
+    'generator-star-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'handle-callback-err': [2, '^(err|error)$'],
+    'indent': [2, 2, {
+      'SwitchCase': 1
+    }],
+    'jsx-quotes': [2, 'prefer-single'],
+    'key-spacing': [2, {
+      'beforeColon': false,
+      'afterColon': true
+    }],
+    'keyword-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'new-cap': [2, {
+      'newIsCap': true,
+      'capIsNew': false
+    }],
+    'new-parens': 2,
+    'no-array-constructor': 2,
+    'no-caller': 2,
+    'no-console': 'off',
+    'no-class-assign': 2,
+    'no-cond-assign': 2,
+    'no-const-assign': 2,
+    'no-control-regex': 0,
+    'no-delete-var': 2,
+    'no-dupe-args': 2,
+    'no-dupe-class-members': 2,
+    'no-dupe-keys': 2,
+    'no-duplicate-case': 2,
+    'no-empty-character-class': 2,
+    'no-empty-pattern': 2,
+    'no-eval': 2,
+    'no-ex-assign': 2,
+    'no-extend-native': 2,
+    'no-extra-bind': 2,
+    'no-extra-boolean-cast': 2,
+    'no-extra-parens': [2, 'functions'],
+    'no-fallthrough': 2,
+    'no-floating-decimal': 2,
+    'no-func-assign': 2,
+    'no-implied-eval': 2,
+    'no-inner-declarations': [2, 'functions'],
+    'no-invalid-regexp': 2,
+    'no-irregular-whitespace': 2,
+    'no-iterator': 2,
+    'no-label-var': 2,
+    'no-labels': [2, {
+      'allowLoop': false,
+      'allowSwitch': false
+    }],
+    'no-lone-blocks': 2,
+    'no-mixed-spaces-and-tabs': 2,
+    'no-multi-spaces': 2,
+    'no-multi-str': 2,
+    'no-multiple-empty-lines': [2, {
+      'max': 1
+    }],
+    'no-native-reassign': 2,
+    'no-negated-in-lhs': 2,
+    'no-new-object': 2,
+    'no-new-require': 2,
+    'no-new-symbol': 2,
+    'no-new-wrappers': 2,
+    'no-obj-calls': 2,
+    'no-octal': 2,
+    'no-octal-escape': 2,
+    'no-path-concat': 2,
+    'no-proto': 2,
+    'no-redeclare': 2,
+    'no-regex-spaces': 2,
+    'no-return-assign': [2, 'except-parens'],
+    'no-self-assign': 2,
+    'no-self-compare': 2,
+    'no-sequences': 2,
+    'no-shadow-restricted-names': 2,
+    'no-spaced-func': 2,
+    'no-sparse-arrays': 2,
+    'no-this-before-super': 2,
+    'no-throw-literal': 2,
+    'no-trailing-spaces': 2,
+    'no-undef': 2,
+    'no-undef-init': 2,
+    'no-unexpected-multiline': 2,
+    'no-unmodified-loop-condition': 2,
+    'no-unneeded-ternary': [2, {
+      'defaultAssignment': false
+    }],
+    'no-unreachable': 2,
+    'no-unsafe-finally': 2,
+    'no-unused-vars': [2, {
+      'vars': 'all',
+      'args': 'none'
+    }],
+    'no-useless-call': 2,
+    'no-useless-computed-key': 2,
+    'no-useless-constructor': 2,
+    'no-useless-escape': 0,
+    'no-whitespace-before-property': 2,
+    'no-with': 2,
+    'one-var': [2, {
+      'initialized': 'never'
+    }],
+    'operator-linebreak': [2, 'after', {
+      'overrides': {
+        '?': 'before',
+        ':': 'before'
+      }
+    }],
+    'padded-blocks': [2, 'never'],
+    'quotes': [2, 'single', {
+      'avoidEscape': true,
+      'allowTemplateLiterals': true
+    }],
+    'semi': [2, 'never'],
+    'semi-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'space-before-blocks': [2, 'always'],
+    'space-before-function-paren': [2, 'never'],
+    'space-in-parens': [2, 'never'],
+    'space-infix-ops': 2,
+    'space-unary-ops': [2, {
+      'words': true,
+      'nonwords': false
+    }],
+    'spaced-comment': [2, 'always', {
+      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
+    }],
+    'template-curly-spacing': [2, 'never'],
+    'use-isnan': 2,
+    'valid-typeof': 2,
+    'wrap-iife': [2, 'any'],
+    'yield-star-spacing': [2, 'both'],
+    'yoda': [2, 'never'],
+    'prefer-const': 2,
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+    'object-curly-spacing': [2, 'always', {
+      objectsInObjects: false
+    }],
+    'array-bracket-spacing': [2, 'never']
+  }
+}

+ 4 - 0
.prrettierrc

@@ -0,0 +1,4 @@
+{
+  "singleQuote": true,// 使用单引号
+  "semi": false      // 行末不使用分号
+}

+ 5 - 0
.travis.yml

@@ -0,0 +1,5 @@
+language: node_js
+node_js: 10
+script: npm run test
+notifications:
+  email: false

+ 40 - 0
build/index.js

@@ -0,0 +1,40 @@
+const { run } = require("runjs");
+const chalk = require("chalk");
+const config = require("../vue.config.js");
+const rawArgv = process.argv.slice(2);
+const args = rawArgv.join(" ");
+
+if (process.env.npm_config_preview || rawArgv.includes("--preview")) {
+  const report = rawArgv.includes("--report");
+
+  run(`vue-cli-service build ${args}`);
+
+  const port = 8081;
+  const publicPath = config.publicPath;
+
+  var connect = require("connect");
+  var serveStatic = require("serve-static");
+  const app = connect();
+
+  app.use(
+    publicPath,
+    serveStatic("./dist", {
+      index: ["index.html", "/"]
+    })
+  );
+
+  app.listen(port, function() {
+    console.log(
+      chalk.green(`> Preview at  http://localhost:${port}${publicPath}`)
+    );
+    if (report) {
+      console.log(
+        chalk.green(
+          `> Report at  http://localhost:${port}${publicPath}report.html`
+        )
+      );
+    }
+  });
+} else {
+  run(`vue-cli-service build ${args}`);
+}

+ 24 - 0
jest.config.js

@@ -0,0 +1,24 @@
+module.exports = {
+  moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
+  transform: {
+    '^.+\\.vue$': 'vue-jest',
+    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
+      'jest-transform-stub',
+    '^.+\\.jsx?$': 'babel-jest'
+  },
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1'
+  },
+  snapshotSerializers: ['jest-serializer-vue'],
+  testMatch: [
+    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
+  ],
+  collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
+  coverageDirectory: '<rootDir>/tests/unit/coverage',
+  // 'collectCoverage': true,
+  'coverageReporters': [
+    'lcov',
+    'text-summary'
+  ],
+  testURL: 'http://localhost/'
+}

Різницю між файлами не показано, бо вона завелика
+ 136 - 138
package-lock.json


+ 14 - 5
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "disaster",
+  "name": "disaster1",
   "version": "0.1.0",
   "private": true,
   "scripts": {
@@ -9,10 +9,17 @@
   },
   "dependencies": {
     "@kitware/vtk.js": "^29.3.0",
+    "@vue/compiler-sfc": "^3.4.13",
+    "axios": "^1.6.3",
     "core-js": "^3.8.3",
-    "element-plus": "^2.4.4",
+    "element-plus": "^2.5.1",
+    "exports-loader": "^1.1.0",
+    "js-cookie": "2.2.0",
+    "sass": "^1.69.7",
+    "sass-loader": "^13.3.3",
     "save": "^2.9.0",
-    "vue": "^3.2.36"
+    "vue-router": "^4.1.1",
+    "vuex": "^4.0.2"
   },
   "devDependencies": {
     "@babel/core": "^7.12.16",
@@ -20,8 +27,10 @@
     "@vue/cli-plugin-babel": "~5.0.0",
     "@vue/cli-plugin-eslint": "~5.0.0",
     "@vue/cli-service": "~5.0.0",
-    "eslint": "^7.32.0",
-    "eslint-plugin-vue": "^8.0.3"
+    "eslint": "^5.15.3",
+    "eslint-plugin-vue": "^8.0.3",
+    "html-webpack-plugin": "^5.6.0",
+    "vue": "^3.2.13"
   },
   "eslintConfig": {
     "root": true,

BIN
public/favicon.ico


+ 8 - 7
src/App.vue

@@ -2,15 +2,16 @@
   <viewIndex/>
 </template>
 
-<script>
+<script setup>
+//import viewIndex from './view/myDemo.vue'
 import viewIndex from './view/myIndex.vue'
 // import HelloWorld from './components/HelloWorld.vue'
-export default {
-  name: 'App',
-  components: {
-    viewIndex
-  }
-}
+// export default {
+//   name: 'App',
+//   // components: {
+//   //   viewIndex
+//   // }
+// }
 </script>
 
 <style>

+ 23 - 0
src/components/myHome.vue

@@ -0,0 +1,23 @@
+<template>
+    <div > 
+          <div class="left_container">
+          <h2>我是跳转的路由</h2>
+         </div>
+       </div>
+     </template>
+   <script setup>
+   
+
+   </script>
+   <style scoped>
+   .left_container {
+     padding: 15px;
+     width: 300px;
+     position: relative;
+     top: 60px;
+     left: 169px;
+     z-index: 2018;
+     border-radius: 5px;
+     box-shadow: 0px 3px 10px rgba(255, 255, 255, 0.1);
+   }
+   </style>

+ 13 - 1
src/main.js

@@ -1,6 +1,18 @@
 import { createApp } from 'vue'
 import App from './App.vue'
+import router from './router/index.js'
+import Vuex from 'vuex' // 引入vuex
 import ElementUI from 'element-plus'
 import 'element-plus/theme-chalk/index.css' // 引入整个Element样式
+
 //import '"element-plus/dist/index.css'
-createApp(App).use(ElementUI).mount('#app')
+//创建一个路由.user(router)
+createApp(App)
+    .use(router)
+    .use(ElementUI)
+    .use(Vuex)
+    .mount('#app')
+// const app= createApp(App)
+// app.use(ElementUI)
+// app.user(router)
+// app.mount('#app')

+ 24 - 0
src/permission.js

@@ -0,0 +1,24 @@
+import router from './router' // vue官方的路由
+import store from './store' // vue状态管理
+import { Message, MessageBox } from 'element-plus' // 引入Message、MessageBox
+import NProgress from 'nprogress' // 进度条
+import 'nprogress/nprogress.css' // 进度条样式
+import getPageTitle from '@/utils/get-page-title' // 获取页面标题
+import { Caegw_LogUrl } from '@/settings' // 引入settings.js
+
+NProgress.configure({ showSpinner: false }) // NProgress配置
+
+const whiteList = []// 免登录的'白名单
+// 前置路由守卫
+router.beforeEach(async (to, from, next) => {
+  NProgress.start() // 开启进度条
+  document.title = getPageTitle(to.meta.title) // 设置页面标题
+
+  const token = store.getters.token // 确定用户是否已登录
+
+})
+
+// 后置路由守卫
+router.afterEach(() => {
+  NProgress.done() // 关闭进度条
+})

+ 30 - 0
src/router/index.js

@@ -0,0 +1,30 @@
+// //创建一个路由器
+// import {createRouter,createwebHashHistory,} from "vue-router"
+// import myDemo from "@/components/myDemo.vue"
+// const router=createRouter({
+//    // history:createWebHashHistory(),//路由器的工作模式地址栏加#号
+//    history: createwebHashHistory(),
+//     routes:[
+//         {
+//             path:'/myDemo',
+//             component:myDemo
+//         }
+//     ]
+// })
+
+// export default router
+import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
+const router = createRouter({
+    history: createWebHistory(),
+    routes: [
+        {
+            path: '/myHome',
+            name:'zhuye',
+            component: () => import('@/components/myHome.vue') // 这是路由的籁加载,也可以其他方式
+        },
+              
+   
+    ]
+})
+
+export default router;

+ 28 - 0
src/settings.js

@@ -0,0 +1,28 @@
+module.exports = {
+  /**
+   * @type {string}
+   * @description page title
+   * @desc 页面标题
+   */
+  title: '前沿动力',
+  /**
+   * @type {boolean} true | false
+   * @description Whether fix the header
+   * @desc 是否固定头部
+   */
+  fixedHeader: true,
+  /**
+  * @type {string}
+  * @description auth TokenKey
+  * @desc token
+  */
+  TokenKey: 'CAE_token',
+  LoginUrl:'logUrl'
+
+  /**
+   * 
+   */
+  // Caegw_LogUrl: 'cae/#/indexLayout/home?type=reLogin',
+
+  // Caegw_ProUrl: 'cae/#/userLayout/details',
+}

+ 11 - 0
src/store/getters.js

@@ -0,0 +1,11 @@
+// 根级别的getters(可以认为是store的计算属性)
+const getters = {
+  fixedHeader: state => state.config.fixedHeader,
+  token: state => state.user.token,
+  name: state => state.user.name,
+  userId: state => state.user.userId,
+  userType: state => state.user.userType,
+  realStatus: state => state.user.realStatus,
+  loginStatus: state => state.user.loginStatus
+}
+export default getters

+ 23 - 0
src/store/index.js

@@ -0,0 +1,23 @@
+// 组装模块并导出store的文件
+import Vue from 'vue' // 引入vue
+import Vuex from 'vuex' // 引入vuex
+import getters from './getters' // 根级别的 getters
+
+// Vue.use(Vuex)
+
+// 不再需要 `import xxx from './modules/xxx `,会自动从modules导入所有的vuex模块
+const modulesFiles = require.context('./modules', true, /\.js$/)
+
+const modules = modulesFiles.keys().reduce((modules, modulePath) => {
+  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') // 获取文件名,例如 './app.js' => 'app'
+  const value = modulesFiles(modulePath)
+  modules[moduleName] = value.default
+  return modules
+}, {})
+
+const store = new Vuex.Store({
+  modules,
+  getters
+})
+
+export default store // 导出store

+ 26 - 0
src/store/modules/app.js

@@ -0,0 +1,26 @@
+
+// Vuex状态树
+const state = {
+  device: 'desktop'
+}
+
+const mutations = {
+  // 切换设备
+  TOGGLE_DEVICE: (state, device) => {
+    state.device = device
+  }
+}
+
+const actions = {
+  // 切换设备
+  toggleDevice({ commit }, device) {
+    commit('TOGGLE_DEVICE', device)
+  }
+}
+
+export default {
+  namespaced: true, // 解决不同模块命名冲突的问题
+  state,
+  mutations,
+  actions
+}

+ 30 - 0
src/store/modules/config.js

@@ -0,0 +1,30 @@
+import { fixedHeader } from '@/settings' // 引入settings.js
+
+// Vuex状态树
+const state = {
+  fixedHeader: fixedHeader
+}
+
+// 同步更改设置 (更改Vuex的store中的状态的唯一方法是提交mutation)
+const mutations = {
+  CHANGE_SETTING: (state, { key, value }) => {
+    if (state.hasOwnProperty(key)) {
+      state[key] = value
+    }
+  }
+}
+
+// 异步更改设置 (action提交的是mutation,而不是直接变更状态)
+const actions = {
+  changeSetting({ commit }, data) {
+    commit('CHANGE_SETTING', data)
+  }
+}
+
+export default {
+  namespaced: true, // 解决不同模块命名冲突的问题
+  state,
+  mutations,
+  actions
+}
+

+ 33 - 0
src/store/modules/user.js

@@ -0,0 +1,33 @@
+// Vuex状态树
+const state = {
+  token: '', // 获取token
+  name: '',
+  userId: '',
+  userType: '',
+  realStatus: '',
+  loginStatus: false
+}
+
+// 同步更改设置 (更改Vuex的store中的状态的唯一方法是提交mutation)
+const mutations = {
+  CHANGE_STATE: (state, { key, value }) => {
+    if (state.hasOwnProperty(key)) {
+      state[key] = value
+    }
+  }
+}
+
+// 异步更改设置 (action提交的是mutation,而不是直接变更状态)
+const actions = {
+  changeState({ commit }, data) {
+    commit('CHANGE_STATE', data)
+  }
+}
+
+export default {
+  namespaced: true,// 解决不同模块命名冲突的问题
+  state,
+  mutations,
+  actions
+}
+

+ 243 - 0
src/utils/3des.js

@@ -0,0 +1,243 @@
+/**
+ * DES 加密算法 
+ * 
+ * 该函数接受一个 8 字节字符串作为普通 DES 算法的密钥(也就是 64 位,但是算法只使用 56 位),或者接受一个 24 字节字符串作为 3DES 
+ * 算法的密钥;第二个参数是要加密或解密的信息字符串;第三个布尔值参数用来说明信息是加密还是解密;接下来的可选参数 mode 如果是 0 表示 ECB 
+ * 模式,1 表示 CBC 模式,默认是 ECB 模式;最后一个可选项是一个 8 字节的输入向量字符串(在 ECB 模式下不使用)。返回的密文是字符串。 
+ * 
+ * 参数: <br> 
+ * key: 8字节字符串作为普通 DES 算法的密钥,或 24 字节字符串作为 3DES <br> 
+ * message: 加密或解密的信息字符串<br> 
+ * encrypt: 布尔值参数用来说明信息是加密还是解密<br> 
+ * mode: 1:CBC模式,0:ECB模式(默认)<br> 
+ * iv:<br> 
+ * padding: 可选项, 8字节的输入向量字符串(在 ECB 模式下不使用) 
+ */
+//des http://www.cnblogs.com/bullub/archive/2013/05/02/3054798.html
+//this takes the key, the message, and whether to encrypt or decrypt
+import { base } from '@/utils/Base64'
+
+function des(key, message, encrypt, mode, iv, padding) {
+  if (encrypt) //如果是加密的话,首先转换编码
+    message = unescape(encodeURIComponent(message));
+  //declaring this locally speeds things up a bit
+  var spfunction1 = new Array(0x1010400, 0, 0x10000, 0x1010404, 0x1010004, 0x10404, 0x4, 0x10000, 0x400, 0x1010400, 0x1010404, 0x400, 0x1000404, 0x1010004, 0x1000000, 0x4, 0x404, 0x1000400, 0x1000400, 0x10400, 0x10400, 0x1010000, 0x1010000, 0x1000404, 0x10004, 0x1000004, 0x1000004, 0x10004, 0, 0x404, 0x10404, 0x1000000, 0x10000, 0x1010404, 0x4, 0x1010000, 0x1010400, 0x1000000, 0x1000000, 0x400, 0x1010004, 0x10000, 0x10400, 0x1000004, 0x400, 0x4, 0x1000404, 0x10404, 0x1010404, 0x10004, 0x1010000, 0x1000404, 0x1000004, 0x404, 0x10404, 0x1010400, 0x404, 0x1000400, 0x1000400, 0, 0x10004, 0x10400, 0, 0x1010004);
+  var spfunction2 = new Array(-0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0, -0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x80000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020, -0x7fff7fe0, 0, -0x80000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000, -0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000, -0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x80000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0, 0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x80000000, -0x7fefffe0, -0x7fef7fe0, 0x108000);
+  var spfunction3 = new Array(0x208, 0x8020200, 0, 0x8020008, 0x8000200, 0, 0x20208, 0x8000200, 0x20008, 0x8000008, 0x8000008, 0x20000, 0x8020208, 0x20008, 0x8020000, 0x208, 0x8000000, 0x8, 0x8020200, 0x200, 0x20200, 0x8020000, 0x8020008, 0x20208, 0x8000208, 0x20200, 0x20000, 0x8000208, 0x8, 0x8020208, 0x200, 0x8000000, 0x8020200, 0x8000000, 0x20008, 0x208, 0x20000, 0x8020200, 0x8000200, 0, 0x200, 0x20008, 0x8020208, 0x8000200, 0x8000008, 0x200, 0, 0x8020008, 0x8000208, 0x20000, 0x8000000, 0x8020208, 0x8, 0x20208, 0x20200, 0x8000008, 0x8020000, 0x8000208, 0x208, 0x8020000, 0x20208, 0x8, 0x8020008, 0x20200);
+  var spfunction4 = new Array(0x802001, 0x2081, 0x2081, 0x80, 0x802080, 0x800081, 0x800001, 0x2001, 0, 0x802000, 0x802000, 0x802081, 0x81, 0, 0x800080, 0x800001, 0x1, 0x2000, 0x800000, 0x802001, 0x80, 0x800000, 0x2001, 0x2080, 0x800081, 0x1, 0x2080, 0x800080, 0x2000, 0x802080, 0x802081, 0x81, 0x800080, 0x800001, 0x802000, 0x802081, 0x81, 0, 0, 0x802000, 0x2080, 0x800080, 0x800081, 0x1, 0x802001, 0x2081, 0x2081, 0x80, 0x802081, 0x81, 0x1, 0x2000, 0x800001, 0x2001, 0x802080, 0x800081, 0x2001, 0x2080, 0x800000, 0x802001, 0x80, 0x800000, 0x2000, 0x802080);
+  var spfunction5 = new Array(0x100, 0x2080100, 0x2080000, 0x42000100, 0x80000, 0x100, 0x40000000, 0x2080000, 0x40080100, 0x80000, 0x2000100, 0x40080100, 0x42000100, 0x42080000, 0x80100, 0x40000000, 0x2000000, 0x40080000, 0x40080000, 0, 0x40000100, 0x42080100, 0x42080100, 0x2000100, 0x42080000, 0x40000100, 0, 0x42000000, 0x2080100, 0x2000000, 0x42000000, 0x80100, 0x80000, 0x42000100, 0x100, 0x2000000, 0x40000000, 0x2080000, 0x42000100, 0x40080100, 0x2000100, 0x40000000, 0x42080000, 0x2080100, 0x40080100, 0x100, 0x2000000, 0x42080000, 0x42080100, 0x80100, 0x42000000, 0x42080100, 0x2080000, 0, 0x40080000, 0x42000000, 0x80100, 0x2000100, 0x40000100, 0x80000, 0, 0x40080000, 0x2080100, 0x40000100);
+  var spfunction6 = new Array(0x20000010, 0x20400000, 0x4000, 0x20404010, 0x20400000, 0x10, 0x20404010, 0x400000, 0x20004000, 0x404010, 0x400000, 0x20000010, 0x400010, 0x20004000, 0x20000000, 0x4010, 0, 0x400010, 0x20004010, 0x4000, 0x404000, 0x20004010, 0x10, 0x20400010, 0x20400010, 0, 0x404010, 0x20404000, 0x4010, 0x404000, 0x20404000, 0x20000000, 0x20004000, 0x10, 0x20400010, 0x404000, 0x20404010, 0x400000, 0x4010, 0x20000010, 0x400000, 0x20004000, 0x20000000, 0x4010, 0x20000010, 0x20404010, 0x404000, 0x20400000, 0x404010, 0x20404000, 0, 0x20400010, 0x10, 0x4000, 0x20400000, 0x404010, 0x4000, 0x400010, 0x20004010, 0, 0x20404000, 0x20000000, 0x400010, 0x20004010);
+  var spfunction7 = new Array(0x200000, 0x4200002, 0x4000802, 0, 0x800, 0x4000802, 0x200802, 0x4200800, 0x4200802, 0x200000, 0, 0x4000002, 0x2, 0x4000000, 0x4200002, 0x802, 0x4000800, 0x200802, 0x200002, 0x4000800, 0x4000002, 0x4200000, 0x4200800, 0x200002, 0x4200000, 0x800, 0x802, 0x4200802, 0x200800, 0x2, 0x4000000, 0x200800, 0x4000000, 0x200800, 0x200000, 0x4000802, 0x4000802, 0x4200002, 0x4200002, 0x2, 0x200002, 0x4000000, 0x4000800, 0x200000, 0x4200800, 0x802, 0x200802, 0x4200800, 0x802, 0x4000002, 0x4200802, 0x4200000, 0x200800, 0, 0x2, 0x4200802, 0, 0x200802, 0x4200000, 0x800, 0x4000002, 0x4000800, 0x800, 0x200002);
+  var spfunction8 = new Array(0x10001040, 0x1000, 0x40000, 0x10041040, 0x10000000, 0x10001040, 0x40, 0x10000000, 0x40040, 0x10040000, 0x10041040, 0x41000, 0x10041000, 0x41040, 0x1000, 0x40, 0x10040000, 0x10000040, 0x10001000, 0x1040, 0x41000, 0x40040, 0x10040040, 0x10041000, 0x1040, 0, 0, 0x10040040, 0x10000040, 0x10001000, 0x41040, 0x40000, 0x41040, 0x40000, 0x10041000, 0x1000, 0x40, 0x10040040, 0x1000, 0x41040, 0x10001000, 0x40, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x40000, 0x10001040, 0, 0x10041040, 0x40040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0, 0x10041040, 0x41000, 0x41000, 0x1040, 0x1040, 0x40040, 0x10000000, 0x10041000);
+
+  //create the 16 or 48 subkeys we will need
+  var keys = des_createKeys(key);
+  var m = 0, i, j, temp, temp2, right1, right2, left, right, looping;
+  var cbcleft, cbcleft2, cbcright, cbcright2;
+  var endloop, loopinc;
+  var len = message.length;
+  var chunk = 0;
+  //set up the loops for single and triple des
+  var iterations = keys.length == 32 ? 3 : 9; //single or triple des
+  if (iterations == 3) { looping = encrypt ? new Array(0, 32, 2) : new Array(30, -2, -2); }
+  else { looping = encrypt ? new Array(0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array(94, 62, -2, 32, 64, 2, 30, -2, -2); }
+
+  //pad the message depending on the padding parameter
+  if (padding == 2) message += "        "; //pad the message with spaces
+  else if (padding == 1) {
+    if (encrypt) {
+      temp = 8 - (len % 8);
+      message += String.fromCharCode(temp, temp, temp, temp, temp, temp, temp, temp);
+      if (temp === 8) len += 8;
+    }
+  } //PKCS7 padding
+  else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes
+
+  //store the result here
+  var result = "";
+  var tempresult = "";
+
+  if (mode == 1) { //CBC mode
+    cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
+    cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
+    m = 0;
+  }
+
+  //loop through each 64 bit chunk of the message
+  while (m < len) {
+    left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
+    right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
+
+    //for Cipher Block Chaining mode, xor the message with the previous result
+    if (mode == 1) { if (encrypt) { left ^= cbcleft; right ^= cbcright; } else { cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right; } }
+
+    //first each 64 but chunk of the message must be permuted according to IP
+    temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
+    temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
+    temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
+    temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
+    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
+
+    left = ((left << 1) | (left >>> 31));
+    right = ((right << 1) | (right >>> 31));
+
+    //do this either 1 or 3 times for each chunk of the message
+    for (j = 0; j < iterations; j += 3) {
+      endloop = looping[j + 1];
+      loopinc = looping[j + 2];
+      //now go through and perform the encryption or decryption
+      for (i = looping[j]; i != endloop; i += loopinc) { //for efficiency
+        right1 = right ^ keys[i];
+        right2 = ((right >>> 4) | (right << 28)) ^ keys[i + 1];
+        //the result is attained by passing these bytes through the S selection functions
+        temp = left;
+        left = right;
+        right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f]
+          | spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f]
+          | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f]
+          | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]);
+      }
+      temp = left; left = right; right = temp; //unreverse left and right
+    } //for either 1 or 3 iterations
+
+    //move then each one bit to the right
+    left = ((left >>> 1) | (left << 31));
+    right = ((right >>> 1) | (right << 31));
+
+    //now perform IP-1, which is IP in the opposite direction
+    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
+    temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
+    temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
+    temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
+    temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
+
+    //for Cipher Block Chaining mode, xor the message with the previous result
+    if (mode == 1) { if (encrypt) { cbcleft = left; cbcright = right; } else { left ^= cbcleft2; right ^= cbcright2; } }
+    tempresult += String.fromCharCode((left >>> 24), ((left >>> 16) & 0xff), ((left >>> 8) & 0xff), (left & 0xff), (right >>> 24), ((right >>> 16) & 0xff), ((right >>> 8) & 0xff), (right & 0xff));
+
+    chunk += 8;
+    if (chunk == 512) { result += tempresult; tempresult = ""; chunk = 0; }
+  } //for every 8 characters, or 64 bits in the message
+
+  //return the result as an array
+  result += tempresult;
+  //result = result.replace(/\0*$/g, "");
+
+  if (!encrypt) { //如果是解密的话,解密结束后对PKCS7 padding进行解码,并转换成utf-8编码
+    if (padding === 1) { //PKCS7 padding解码
+      var len = result.length, paddingChars = 0;
+      len && (paddingChars = result.charCodeAt(len - 1));
+      (paddingChars <= 8) && (result = result.substring(0, len - paddingChars));
+    }
+    //转换成UTF-8编码
+    result = decodeURIComponent(escape(result));
+  }
+
+  return result;
+} //end of des
+
+
+
+//des_createKeys
+//this takes as input a 64 bit key (even though only 56 bits are used)
+//as an array of 2 integers, and returns 16 48 bit keys
+function des_createKeys(key) {
+  //declaring this locally speeds things up a bit
+  var pc2bytes0 = new Array(0, 0x4, 0x20000000, 0x20000004, 0x10000, 0x10004, 0x20010000, 0x20010004, 0x200, 0x204, 0x20000200, 0x20000204, 0x10200, 0x10204, 0x20010200, 0x20010204);
+  var pc2bytes1 = new Array(0, 0x1, 0x100000, 0x100001, 0x4000000, 0x4000001, 0x4100000, 0x4100001, 0x100, 0x101, 0x100100, 0x100101, 0x4000100, 0x4000101, 0x4100100, 0x4100101);
+  var pc2bytes2 = new Array(0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808, 0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808);
+  var pc2bytes3 = new Array(0, 0x200000, 0x8000000, 0x8200000, 0x2000, 0x202000, 0x8002000, 0x8202000, 0x20000, 0x220000, 0x8020000, 0x8220000, 0x22000, 0x222000, 0x8022000, 0x8222000);
+  var pc2bytes4 = new Array(0, 0x40000, 0x10, 0x40010, 0, 0x40000, 0x10, 0x40010, 0x1000, 0x41000, 0x1010, 0x41010, 0x1000, 0x41000, 0x1010, 0x41010);
+  var pc2bytes5 = new Array(0, 0x400, 0x20, 0x420, 0, 0x400, 0x20, 0x420, 0x2000000, 0x2000400, 0x2000020, 0x2000420, 0x2000000, 0x2000400, 0x2000020, 0x2000420);
+  var pc2bytes6 = new Array(0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002, 0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002);
+  var pc2bytes7 = new Array(0, 0x10000, 0x800, 0x10800, 0x20000000, 0x20010000, 0x20000800, 0x20010800, 0x20000, 0x30000, 0x20800, 0x30800, 0x20020000, 0x20030000, 0x20020800, 0x20030800);
+  var pc2bytes8 = new Array(0, 0x40000, 0, 0x40000, 0x2, 0x40002, 0x2, 0x40002, 0x2000000, 0x2040000, 0x2000000, 0x2040000, 0x2000002, 0x2040002, 0x2000002, 0x2040002);
+  var pc2bytes9 = new Array(0, 0x10000000, 0x8, 0x10000008, 0, 0x10000000, 0x8, 0x10000008, 0x400, 0x10000400, 0x408, 0x10000408, 0x400, 0x10000400, 0x408, 0x10000408);
+  var pc2bytes10 = new Array(0, 0x20, 0, 0x20, 0x100000, 0x100020, 0x100000, 0x100020, 0x2000, 0x2020, 0x2000, 0x2020, 0x102000, 0x102020, 0x102000, 0x102020);
+  var pc2bytes11 = new Array(0, 0x1000000, 0x200, 0x1000200, 0x200000, 0x1200000, 0x200200, 0x1200200, 0x4000000, 0x5000000, 0x4000200, 0x5000200, 0x4200000, 0x5200000, 0x4200200, 0x5200200);
+  var pc2bytes12 = new Array(0, 0x1000, 0x8000000, 0x8001000, 0x80000, 0x81000, 0x8080000, 0x8081000, 0x10, 0x1010, 0x8000010, 0x8001010, 0x80010, 0x81010, 0x8080010, 0x8081010);
+  var pc2bytes13 = new Array(0, 0x4, 0x100, 0x104, 0, 0x4, 0x100, 0x104, 0x1, 0x5, 0x101, 0x105, 0x1, 0x5, 0x101, 0x105);
+
+  //how many iterations (1 for des, 3 for triple des)
+  var iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
+  //stores the return keys
+  var keys = new Array(32 * iterations);
+  //now define the left shifts which need to be done
+  var shifts = new Array(0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0);
+  //other variables
+  var lefttemp, righttemp, m = 0, n = 0, temp;
+
+  for (var j = 0; j < iterations; j++) { //either 1 or 3 iterations
+    var left = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
+    var right = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
+
+    temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
+    temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
+    temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2);
+    temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
+    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
+    temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
+    temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
+
+    //the right side needs to be shifted and to get the last four bits of the left side
+    temp = (left << 8) | ((right >>> 20) & 0x000000f0);
+    //left needs to be put upside down
+    left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
+    right = temp;
+
+    //now go through and perform these shifts on the left and right keys
+    for (var i = 0; i < shifts.length; i++) {
+      //shift the keys either one or two bits to the left
+      if (shifts[i]) { left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26); }
+      else { left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27); }
+      left &= -0xf; right &= -0xf;
+
+      //now apply PC-2, in such a way that E is easier when encrypting or decrypting
+      //this conversion will look like PC-2 except only the last 6 bits of each byte are used
+      //rather than 48 consecutive bits and the order of lines will be according to
+      //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
+      lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf]
+        | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf]
+        | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf]
+        | pc2bytes6[(left >>> 4) & 0xf];
+      righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf]
+        | pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf]
+        | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf]
+        | pc2bytes13[(right >>> 4) & 0xf];
+      temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff;
+      keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16);
+    }
+  } //for each iterations
+  //return the keys we've created
+  return keys;
+} //end of des_createKeys
+function genkey(key, start, end) {
+  //8 byte / 64 bit Key (DES) or 192 bit Key
+  return { key: pad(key.slice(start, end)), vector: 1 };
+}
+function pad(key) {
+  for (var i = key.length; i < 24; i++) {
+    key += "0";
+  }
+  return key;
+}
+
+var des3iv = '/ECB/PKCS5Padding';
+var DES3 = {
+  //3DES加密,CBC/PKCS5Padding
+  encrypt: function (key, input) {
+    var genKey = genkey(key, 0, 24);
+    var b = base;
+    return b.encode(des(genKey.key, input, 1, 0, des3iv, 1));
+  },
+  //3DES解密,CBC/PKCS5Padding
+  decrypt: function (key, input) {
+    var genKey = genkey(key, 0, 24);
+    var b = base;
+    return des(genKey.key, b.decode(input), 0, 0, des3iv, 1);
+  }
+};
+
+export {
+  DES3
+}

+ 112 - 0
src/utils/Base64.js

@@ -0,0 +1,112 @@
+/**   
+*  Base64 encode / decode        
+*/
+
+function Base64() {
+
+  // private property    
+  var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+  // public method for encoding    
+  this.encode = function (input) {
+    var output = "";
+    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+    var i = 0;
+    //input = _utf8_encode(input);    
+    while (i < input.length) {
+      chr1 = input.charCodeAt(i++);
+      chr2 = input.charCodeAt(i++);
+      chr3 = input.charCodeAt(i++);
+      enc1 = chr1 >> 2;
+      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+      enc4 = chr3 & 63;
+      if (isNaN(chr2)) {
+        enc3 = enc4 = 64;
+      } else if (isNaN(chr3)) {
+        enc4 = 64;
+      }
+      output = output +
+        _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
+        _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
+    }
+    return output;
+  };
+
+  // public method for decoding    
+  this.decode = function (input) {
+    var output = "";
+    var chr1, chr2, chr3;
+    var enc1, enc2, enc3, enc4;
+    var i = 0;
+    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+    while (i < input.length) {
+      enc1 = _keyStr.indexOf(input.charAt(i++));
+      enc2 = _keyStr.indexOf(input.charAt(i++));
+      enc3 = _keyStr.indexOf(input.charAt(i++));
+      enc4 = _keyStr.indexOf(input.charAt(i++));
+      chr1 = (enc1 << 2) | (enc2 >> 4);
+      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+      chr3 = ((enc3 & 3) << 6) | enc4;
+      output = output + String.fromCharCode(chr1);
+      if (enc3 != 64) {
+        output = output + String.fromCharCode(chr2);
+      }
+      if (enc4 != 64) {
+        output = output + String.fromCharCode(chr3);
+      }
+    }
+    //output = _utf8_decode(output);    
+    return output;
+  };
+
+  // private method for UTF-8 encoding    
+  var _utf8_encode = function (string) {
+    string = string.replace(/\r\n/g, "\n");
+    var utftext = "";
+    for (var n = 0; n < string.length; n++) {
+      var c = string.charCodeAt(n);
+      if (c < 128) {
+        utftext += String.fromCharCode(c);
+      } else if ((c > 127) && (c < 2048)) {
+        utftext += String.fromCharCode((c >> 6) | 192);
+        utftext += String.fromCharCode((c & 63) | 128);
+      } else {
+        utftext += String.fromCharCode((c >> 12) | 224);
+        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+        utftext += String.fromCharCode((c & 63) | 128);
+      }
+
+    }
+    return utftext;
+  }
+
+  // private method for UTF-8 decoding    
+  var _utf8_decode = function (utftext) {
+    var string = "";
+    var i = 0;
+    var c = c1 = c2 = 0;
+    while (i < utftext.length) {
+      c = utftext.charCodeAt(i);
+      if (c < 128) {
+        string += String.fromCharCode(c);
+        i++;
+      } else if ((c > 191) && (c < 224)) {
+        c2 = utftext.charCodeAt(i + 1);
+        string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+        i += 2;
+      } else {
+        c2 = utftext.charCodeAt(i + 1);
+        c3 = utftext.charCodeAt(i + 2);
+        string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+        i += 3;
+      }
+    }
+    return string;
+  }
+}
+
+const base = new Base64();
+export {
+  base
+}

+ 11 - 0
src/utils/get-page-title.js

@@ -0,0 +1,11 @@
+import settings from '@/settings' // 引入settings.js
+
+const title = settings.title
+
+// 获取标题
+export default function getPageTitle(pageTitle) {
+  if (pageTitle) {
+    return `${title} - ${pageTitle}`
+  }
+  return `${title}`
+}

+ 104 - 0
src/utils/index.js

@@ -0,0 +1,104 @@
+
+/**
+ * Parse the time to string
+ * @param {(Object|string|number)} time
+ * @param {string} cFormat
+ * @returns {string | null}
+ */
+export function parseTime(time, cFormat) {
+  if (arguments.length === 0) {
+    return null
+  }
+  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+      time = parseInt(time)
+    }
+    if ((typeof time === 'number') && (time.toString().length === 10)) {
+      time = time * 1000
+    }
+    date = new Date(time)
+  }
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  }
+  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
+    const value = formatObj[key]
+    // Note: getDay() returns 0 on Sunday
+    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+    return value.toString().padStart(2, '0')
+  })
+  return time_str
+}
+
+/**
+ * @param {number} time
+ * @param {string} option
+ * @returns {string}
+ */
+export function formatTime(time, option) {
+  if (('' + time).length === 10) {
+    time = parseInt(time) * 1000
+  } else {
+    time = +time
+  }
+  const d = new Date(time)
+  const now = Date.now()
+
+  const diff = (now - d) / 1000
+
+  if (diff < 30) {
+    return '刚刚'
+  } else if (diff < 3600) {
+    // less 1 hour
+    return Math.ceil(diff / 60) + '分钟前'
+  } else if (diff < 3600 * 24) {
+    return Math.ceil(diff / 3600) + '小时前'
+  } else if (diff < 3600 * 24 * 2) {
+    return '1天前'
+  }
+  if (option) {
+    return parseTime(time, option)
+  } else {
+    return (
+      d.getMonth() +
+      1 +
+      '月' +
+      d.getDate() +
+      '日' +
+      d.getHours() +
+      '时' +
+      d.getMinutes() +
+      '分'
+    )
+  }
+}
+
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+export function param2Obj(url) {
+  const search = url.split('?')[1]
+  if (!search) {
+    return {}
+  }
+  return JSON.parse(
+    '{"' +
+    decodeURIComponent(search)
+      .replace(/"/g, '\\"')
+      .replace(/&/g, '","')
+      .replace(/=/g, '":"')
+      .replace(/\+/g, ' ') +
+    '"}'
+  )
+}

+ 123 - 0
src/utils/jquery.base64.js

@@ -0,0 +1,123 @@
+/*!
+ * jquery.base64.js 0.0.3 - https://github.com/yckart/jquery.base64.js
+ * Makes Base64 en & -decoding simpler as it is.
+ *
+ * Based upon: https://gist.github.com/Yaffle/1284012
+ *
+ * Copyright (c) 2012 Yannick Albert (http://yckart.com)
+ * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php).
+ * 2013/02/10
+ **/
+;(function($) {
+
+    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
+        a256 = '',
+        r64 = [256],
+        r256 = [256],
+        i = 0;
+
+    var UTF8 = {
+
+        /**
+         * Encode multi-byte Unicode string into utf-8 multiple single-byte characters
+         * (BMP / basic multilingual plane only)
+         *
+         * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars
+         *
+         * @param {String} strUni Unicode string to be encoded as UTF-8
+         * @returns {String} encoded string
+         */
+        encode: function(strUni) {
+            // use regular expressions & String.replace callback function for better efficiency
+            // than procedural approaches
+            var strUtf = strUni.replace(/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz
+            function(c) {
+                var cc = c.charCodeAt(0);
+                return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
+            })
+            .replace(/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
+            function(c) {
+                var cc = c.charCodeAt(0);
+                return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f);
+            });
+            return strUtf;
+        },
+
+        /**
+         * Decode utf-8 encoded string back into multi-byte Unicode characters
+         *
+         * @param {String} strUtf UTF-8 string to be decoded back to Unicode
+         * @returns {String} decoded string
+         */
+        decode: function(strUtf) {
+            // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!
+            var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars
+            function(c) { // (note parentheses for precence)
+                var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);
+                return String.fromCharCode(cc);
+            })
+            .replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars
+            function(c) { // (note parentheses for precence)
+                var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;
+                return String.fromCharCode(cc);
+            });
+            return strUni;
+        }
+    };
+
+    while(i < 256) {
+        var c = String.fromCharCode(i);
+        a256 += c;
+        r256[i] = i;
+        r64[i] = b64.indexOf(c);
+        ++i;
+    }
+
+    function code(s, discard, alpha, beta, w1, w2) {
+        s = String(s);
+        var buffer = 0,
+            i = 0,
+            length = s.length,
+            result = '',
+            bitsInBuffer = 0;
+
+        while(i < length) {
+            var c = s.charCodeAt(i);
+            c = c < 256 ? alpha[c] : -1;
+
+            buffer = (buffer << w1) + c;
+            bitsInBuffer += w1;
+
+            while(bitsInBuffer >= w2) {
+                bitsInBuffer -= w2;
+                var tmp = buffer >> bitsInBuffer;
+                result += beta.charAt(tmp);
+                buffer ^= tmp << bitsInBuffer;
+            }
+            ++i;
+        }
+        if(!discard && bitsInBuffer > 0) result += beta.charAt(buffer << (w2 - bitsInBuffer));
+        return result;
+    }
+
+    var Plugin = $.base64 = function(dir, input, encode) {
+            return input ? Plugin[dir](input, encode) : dir ? null : this;
+        };
+
+    Plugin.btoa = Plugin.encode = function(plain, utf8encode) {
+        plain = Plugin.raw === false || Plugin.utf8encode || utf8encode ? UTF8.encode(plain) : plain;
+        plain = code(plain, false, r256, b64, 8, 6);
+        return plain + '===='.slice((plain.length % 4) || 4);
+    };
+
+    Plugin.atob = Plugin.decode = function(coded, utf8decode) {
+        coded = coded.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+        coded = String(coded).split('=');
+        var i = coded.length;
+        do {--i;
+            coded[i] = code(coded[i], true, r64, a256, 6, 8);
+        } while (i > 0);
+        coded = coded.join('');
+        return Plugin.raw === false || Plugin.utf8decode || utf8decode ? UTF8.decode(coded) : coded;
+    };
+}(jQuery));

+ 46 - 0
src/utils/message.js

@@ -0,0 +1,46 @@
+/*
+element-ui中的message在实际使用过程中很多时候会遇到,相同的message会在短时间内同时出现在页面上,这种情况下是没有必要的。比如一个错误信息提示被放在定时器中进行:定时调用某个接口,然后这个接口会返回一个message的话,在定时器时间设置很短的情况下,就会出现短时间内弹出很多相同的message。我们可以通过重写element-ui的message来解决这个问题。
+*/
+import { Message } from 'element-plus'
+
+const showMessage = Symbol('showMessage')
+/**
+ * 封装element-ui message方法,只有在页面没有message或者没有相同message的情况下才弹出该message
+ */
+class ElMessage {
+  success(options, single = true) {
+    this[showMessage]('success', options, single)
+  }
+  warning(options, single = true) {
+    this[showMessage]('warning', options, single)
+  }
+  info(options, single = true) {
+    this[showMessage]('info', options, single)
+  }
+  error(options, single = true) {
+    this[showMessage]('error', options, single)
+  }
+  /* eslint-disable */
+  [showMessage](type, options, single) {
+    if (single) {
+      let canShow = true;//设置值控制显示当前message
+      let messageArr = document.getElementsByClassName('el-message');//获取页面所有已经存在的message
+      //遍历获取到的message DOM集合,如果页面已存在的message中有显示文本和当前message相同的情况,canShow设置为false
+      for (let i = 0; i < messageArr.length; i++) {
+        if (options == messageArr[i].getElementsByClassName('el-message__content')[0].innerHTML) {
+          canShow = false;
+        }
+      }
+      //如果页面不存在message或者canShow==true,则正常执行该message
+      if (messageArr.length == 0 || canShow) {
+        Message[type](options)
+      }
+    } else {
+      Message[type](options)
+    }
+  }
+}
+
+export {
+  ElMessage
+}

+ 237 - 0
src/utils/request.js

@@ -0,0 +1,237 @@
+import axios from 'axios';
+import store from '@/store'
+import { MessageBox } from 'element-plus'
+import { getToken } from '@/utils/token'
+import { DES3 } from '@/utils/3des'
+import router from '@/router/index'
+import { ElMessage } from '../utils/message.js'
+import { Caegw_ProUrl,Caegw_LogUrl } from '@/settings' ;// 引入settings.js
+let Message = new ElMessage()
+
+const requestList = []; // 请求列表
+const CancelToken = axios.CancelToken;
+let sources = {};
+let successCode = '000000000' | '0000000';//成功编码
+
+//axios配置
+//全局的 axios 默认值:指定将被用在各个请求的配置默认值
+axios.defaults.timeout = 20000;//设置超时时间
+axios.defaults.baseURL = process.env.VUE_APP_BASE_API;// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
+//axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;//根据项目需要配置
+//axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';//设置请求头的类型
+axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';//设置请求头的类型
+// 添加请求拦截器
+axios.interceptors.request.use(config => {
+  const request = JSON.stringify(config.url) + (!!config.data ? JSON.stringify(config.data) : JSON.stringify(config.params))
+  config.cancelToken = new CancelToken((cancel) => {
+    sources[request] = cancel
+  })
+  //判断请求是否已存在请求列表,避免重复请求,将当前请求添加进请求列表数组;
+  if (requestList.includes(request)) {
+    // console.log(request)
+    // sources[request]('取消重复请求')
+    // requestList.splice(requestList.findIndex(item => item === request), 1)
+    requestList.push(request)
+  } else {
+    requestList.push(request)
+  }
+
+  //因本项目部分请求需要重复获取所以不用去重
+  // requestList.push(request)
+
+
+  // 登录控制中根据本地是否存在token判断用户的登录情况,
+  // 但即使token存在,也有可能token是过期的,所以在每次的请求头中携带token,
+  // 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码,
+  // 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
+
+  //本项目不用请求头判断而是在报文体中发送token
+  // let token = store.getters.token || getToken();
+  // token && (config.headers.Authorization = token);
+  return config;
+}, error => Promise.reject(error));
+
+
+// 添加响应拦截器
+axios.interceptors.response.use(res => {
+  const request = JSON.stringify(res.config.url) + (!!res.config.data ? JSON.stringify(res.config.data) : JSON.stringify(res.config.params))
+  requestList.splice(requestList.findIndex(item => item === request), 1)
+  if (res.status === 200) {
+    let { data } = res
+    if (data.returnCode == successCode) {
+      return Promise.resolve(data)
+    } else {
+      if (data.returnMsg === '系统没有登录或会话超时!') {
+        store.dispatch('user/changeState', {
+          key: 'token',
+          value: ''
+        })
+        store.dispatch('user/changeState', {
+          key: 'name',
+          value: ''
+        })
+        store.dispatch('user/changeState', {
+          key: 'userId',
+          value: ''
+        })
+        store.dispatch('user/changeState', {
+          key: 'loginStatus',
+          value: false
+        })
+        MessageBox.confirm('系统没有登录或会话超时, 是否登录?', '提示', {
+          confirmButtonText: '登录',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+          .then(() => {
+            MessageBox.close()
+            // window.location.hash = "/login/index";
+            // window.location.href = "#/login/index";
+            // router.replace(`/login/index`)
+             // this.$router.replace('/login/index')
+             let logUrl = window.location.protocol+"//"+window.location.host+"/"+Caegw_LogUrl;
+             // console.log(logUrl);
+             window.location.href=logUrl
+          })
+          .catch(() => {
+            Message.info('已取消!')
+            MessageBox.close()
+          })
+      }
+      return Promise.reject(data)
+    }
+  } else {
+    return Promise.reject(res)
+  }
+}, err => {
+  const { res } = err;
+  if (res) {
+    // 请求已发出,但是不在2xx的范围
+    requestList.length = 0;
+    errorHandle(res.status, res.data.message);
+    return Promise.reject(res);
+  } else {
+    console.log(err)
+    if (err.message == '取消上传') {
+      return Promise.reject('cancel')
+    }
+    // 处理断网的情况
+    // eg:请求超时或断网时,更新network状态,可在app.vue中控制着一个全局的断网提示组件的显示隐藏
+    if (err.message == '取消重复请求') {
+      // console.log(err)
+    } else {
+      let error = err.message.indexOf('timeout') != -1 ? { returnMsg: '请求超时' } : { returnMsg: '网络通讯异常' };
+      requestList.splice(requestList.findIndex(item => item === request), 1)
+      return Promise.reject(error)
+    }
+  }
+});
+
+//完善请求url
+function getUrl(channelNo = 'service') {
+  let url = ''
+  if (channelNo == 'service') {
+    url = '/TransServlet'
+  } else if (channelNo == 'manager') {
+    url = '/managersvr/TransServlet'
+  }
+  return url
+}
+
+//配置上行报文公共包头
+function getParams(params, channelNo) {
+ // params['clientToken'] = "11112";
+  params['clientToken'] = "e47b87eec69545559d1e81e56626da68";
+  let userId ='5f06c8bc77234f969d13e160b54c27e3';
+  // let userId = store.getters.userId;
+  // if (userId) {
+  //   params['userId'] = userId;
+  // }
+  params['channelNo'] = channelNo;
+  return params;
+}
+
+// 请求
+const request = (params, channelNo = 'service', method = 'post') => {
+  let url = getUrl(channelNo);//完善请求url
+  params = getParams(params, channelNo);//配置上行报文公共包头
+  return new Promise((resolve, reject) => {
+    if (method == 'post') {
+      axios.post(url, params).then(res => {
+        resolve(res)
+      }).catch(err => {
+        // Message.error(err.returnMsg)
+        reject(err)
+      })
+    } else if (method == 'get') {
+      axios.get(url, { params }).then(res => {
+        resolve(res)
+      }).catch(err => {
+        // Message.error(err.returnMsg)
+        reject(err)
+      })
+    }
+  })
+}
+
+// 文件上传
+const uploadFile = (params, channelNo = 'service', callback1) => {
+  let config = {
+    timeout: 60000,
+    headers: { "Content-Type": "multipart/form-data" },
+    onUploadProgress: progressEvent => {
+      //属性lengthComputable主要表明总共需要完成的工作量和已经完成的工作是否可以被测量,如果lengthComputable为false,就获取不到progress.total和progress.loaded
+      if (progressEvent.lengthComputable) {
+        let loaded = progressEvent.loaded
+        let total = progressEvent.total
+        // let progress = Math.floor((loaded / total) * 100) > 1 ? Math.floor((loaded / total) * 100) : 1;
+        // console.log('上传进度 ' + progress + '%');
+        // callback1(progress)
+        
+      }
+    }
+  }
+  let url = getUrl(channelNo);//完善请求url
+  params.append("clientToken", store.getters.token || getToken());
+  params.append("userId", store.getters.userId);
+  params.append("channelNo", channelNo);//配置上行报文公共包头
+  console.log(params);
+  return new Promise((resolve, reject) => {
+    axios.post(url, params, config).then(res => {
+      resolve(res)
+    }).catch(err => {
+      if (err != 'cancel') {
+        Message.error(err.returnMsg)
+      }
+      reject(err)
+    })
+  })
+}
+
+// 获取图片验证码
+const getCode = (channelNo = 'service') => {
+  let rand = Math.random();
+  let token = store.getters.token || getToken();
+  return process.env.VUE_APP_BASE_API + getUrl(channelNo) + '?transCode=HM0000&randomKey=' + rand + '&clientToken=' + token;
+}
+
+// 获取图片Url
+const getImage = (id, channelNo = 'server') => {
+  let token = store.getters.token || getToken();
+  return process.env.VUE_APP_BASE_API + getUrl(channelNo) + "?transCode=B00022&clientToken=" + token + "&id=" + id;
+}
+
+//加密密码
+const enPassword = (password) => {
+  let token = store.getters.token || getToken();
+  return DES3.encrypt(token, password);
+}
+
+export {
+  request,
+  uploadFile,
+  getCode,
+  getImage,
+  enPassword,
+  sources
+}

+ 34 - 0
src/utils/token.js

@@ -0,0 +1,34 @@
+import Cookies from 'js-cookie' // 引入js-cookie插件
+import { TokenKey } from '@/settings' // 引入settings.js
+
+/**
+ * 前端uuid
+*/
+function uuid() {
+  let s = [];
+  let hexDigits = "0123456789abcdef";
+  for (let i = 0; i < 36; i++) {
+    s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
+  }
+  s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
+  s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
+  s[8] = s[13] = s[18] = s[23] = "";
+  var uuid = s.join("");
+  return uuid;
+}
+
+// 获取token
+export function getToken() {
+  if (Cookies.get(TokenKey) == null || Cookies.get(TokenKey) == '' || Cookies.get(TokenKey) == 'undefined') {
+    setToken(uuid());
+  }
+  return Cookies.get(TokenKey);
+}
+// 设置token
+export function setToken(token) {
+  return Cookies.set(TokenKey, token)
+}
+// 移除token
+export function removeToken() {
+  return Cookies.remove(TokenKey)
+}

+ 31 - 0
src/utils/validate.js

@@ -0,0 +1,31 @@
+/**
+ * @param {string} path
+ * @returns {Boolean}
+ */
+export function isExternal(path) {
+  return /^(https?:|mailto:|tel:)/.test(path)
+}
+
+/**
+ * @param {string} email
+ * @returns {Boolean}
+ */
+export function validEmail(email) {
+  return /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(email)
+}
+
+/**
+ * @param {string} code
+ * @returns {Boolean}
+ */
+export function validCode(code) {
+  return /^[0-9]{6}$/.test(code)
+}
+
+/**
+ * @param {string} pwd
+ * @returns {Boolean}
+ */
+export function validPwd(pwd) {
+  return /^[a-zA-Z]\w{5,7}$/.test(pwd)
+}

+ 459 - 0
src/view/evolutionView.vue

@@ -0,0 +1,459 @@
+<template>
+    <div > 
+          <div class="left_container " v-show="isshow">
+         
+           <h2 ref="title">{{props.title1}}</h2>
+           <!-- <div
+           class="img"
+           v-for="(item, index) in listArray"
+           :key="index"
+         >
+          <span>{{ item.name }}</span>
+         </div> -->
+         <el-tabs
+       v-model="activeName"
+       type="card"
+       :tab-position="tabPosition"
+       class="demo-tabs yanshib"
+       @tab-click="handleClick"
+     >
+     <!-- 火灾 -->
+       <el-tab-pane label="火灾" name="111">
+         <div class="demo-input-suffix firsttitle">
+           <el-form-item label="火灾源" label-width="formLabelWidth5">
+         <el-input  v-model="input2"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           :prefix-icon="Search"   />
+       </el-form-item>
+       <div class="btn2">
+       <el-button type="success">搜索</el-button></div>
+       <!-- <div class="btn2">
+       <el-button type="success">导入</el-button></div> -->
+       <div class="btn2">
+       <el-button type="success">开始演化</el-button></div>
+     </div>
+   <!-- table表 -->
+   <el-table :data="tableData" style="width: 100%">
+       <el-table-column fixed prop="date" label="时间序列名" width="100" />
+       <el-table-column prop="name" label="节点名称" width="80" />
+       <el-table-column prop="state" label="值" width="90" />
+       <el-table-column label="操作" width="220">
+     
+         <template #default="scope">
+           <el-button size="small" @click="handleEdit(scope.$index, scope.row)"
+             >添加</el-button
+           >
+           <el-button
+             size="small"
+             type="success"
+             @click="handleDelete(scope.$index, scope.row)"
+             >Delete</el-button
+           >
+           <el-button
+             size="small"
+             type="danger"
+             @click="handleDelete(scope.$index, scope.row)"
+             >删除</el-button
+           >
+         </template>
+       </el-table-column>
+     </el-table>
+     <div class="demo-pagination-block">
+    <!-- <div class="demonstration">All combined</div> -->
+    <!-- <el-pagination
+      v-model:current-page="currentPage4"
+      v-model:page-size="pageSize4"
+      :page-sizes="[4, 8, 16, 50]"
+      :small="small"
+      :disabled="disabled"
+      :background="background"
+      layout="total, sizes, prev, pager, next, jumper"
+      :total="400"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+    /> -->
+    <el-pagination
+    v-model:current-page="currentPage4"
+      v-model:page-size="pageSize4"
+      :page-sizes="[100, 200, 300, 400]"
+    small
+    background
+    layout="prev, pager, next, jumpe,"      
+    :total="total"
+    class="mt-4"
+    @size-change="handleSizeChange"
+    @current-change="handleCurrentChange"
+  />
+  </div>
+     <div class="input">
+     <el-form-item label="时间序列名" :label-width="formLabelWidth5">
+         <el-input  v-model="input2"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           />
+       </el-form-item>
+     </div>
+       <div class="demo-input-suffix firsttitle">
+         
+           <el-form-item label="节点名称" :label-width="formLabelWidth5">
+         <el-input  v-model="input3"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           :prefix-icon="Search"   />
+       </el-form-item>
+       <div class="btn2">
+       <el-button type="primary"  @click="dialogVisiblenode = true;selenum=1">选择节点</el-button></div>
+       </div>
+       <div class="input " style="margin-top: -2px;">
+         <el-form-item  label="值"  :label-width="formLabelWidth5" >
+         <el-input v-model="des1" :autosize="{ minRows: 5, maxRows: 10}" type="textarea" />
+       </el-form-item>
+     </div>
+   
+   
+       </el-tab-pane>
+         <!-- 选择节点弹出框 -->
+       <el-dialog
+       v-model="dialogVisiblenode"
+       title="Tips"
+       width="50%"
+     >
+     <div class="demo-input-suffix firsttitle">
+           <el-form-item label="节点选择" :label-width="formLabelWidth5">
+         <el-input  v-model="input2"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           :prefix-icon="Search"   />
+       </el-form-item>
+       <div class="btn2">
+       <el-button type="success">导入</el-button></div>
+     
+     </div>
+     <el-table
+       ref="multipleTableRef"
+       :data="tableData2"
+       style="width: 100%"
+       @selection-change="handleSelectionChange"
+     >
+       <el-table-column type="selection" width="55" />
+       <!-- <el-table-column label="Date" width="120">
+       <template #default="scope">{{ scope.row.date }}</template>
+       </el-table-column> -->
+       <el-table-column type="index" width="50" />
+       <el-table-column property="name" label="名称" width="120" />
+       <el-table-column property="chuk" label="位置坐标"/>
+       <el-table-column property="chuk" label="出口"/>
+       <el-table-column property="w1" label="初始水位(升)" width="120"/>
+       <el-table-column property="w2" label="初始温度(K)" width="120"/>
+     </el-table>
+       <template #footer>
+         <span class="dialog-footer">
+           <el-button @click="dialogVisiblenode = false">取消</el-button>
+           <el-button type="primary" @click="queding();dialogVisiblenode = false">
+             确认
+           </el-button>
+         </span>
+       </template>
+     </el-dialog>
+       <!--水灾  -->
+       <el-tab-pane label="水灾" name="222" >
+         <div class="demo-input-suffix firsttitle">
+           <el-form-item label="突水源" label-width="formLabelWidth5">
+         <el-input  v-model="input4"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           :prefix-icon="Search"   />
+       </el-form-item>
+       <div class="btn2">
+       <el-button type="success">导入</el-button></div>
+       <div class="btn2">
+       <el-button type="success">开始演化</el-button></div>
+     </div>
+   <!-- table表 -->
+   <el-table :data="tableData" style="width: 100%">
+       <el-table-column fixed prop="date" label="时间序列名" width="100" />
+       <el-table-column prop="name" label="节点名称" width="80" />
+       <el-table-column prop="state" label="值" width="90" />
+       <el-table-column label="操作" width="220">
+     
+         <template #default="scope">
+           <el-button size="small" @click="handleEdit(scope.$index, scope.row)"
+             >添加</el-button
+           >
+           <el-button
+             size="small"
+             type="success"
+             @click="handleDelete(scope.$index, scope.row)"
+             >Delete</el-button
+           >
+           <el-button
+             size="small"
+             type="danger"
+             @click="handleDelete(scope.$index, scope.row)"
+             >删除</el-button
+           >
+         </template>
+       </el-table-column>
+     </el-table>
+     <div class="input">
+     <el-form-item label="时间序列名" :label-width="formLabelWidth5">
+         <el-input  v-model="input2"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           />
+       </el-form-item>
+     </div>
+       <div class="demo-input-suffix firsttitle">
+         
+           <el-form-item label="节点名称" :label-width="formLabelWidth5">
+         <el-input  v-model="input4"
+           class="w-50 m-2"
+           placeholder="节点名称"
+           :prefix-icon="Search"   />
+       </el-form-item>
+       <div class="btn2">
+       <el-button type="primary"  @click="dialogVisiblenode = true;selenum=2">选择节点</el-button></div>
+       </div>
+       <div class="input " style="margin-top: -2px;">
+         <el-form-item label="值" :label-width="formLabelWidth5" >
+         <el-input v-model="des2"   :autosize="{ minRows: 5, maxRows: 10}" type="textarea" />
+       </el-form-item>
+     </div>
+   
+       </el-tab-pane>
+       <el-tab-pane label="瓦斯爆炸" name="333">还在开发中.....</el-tab-pane>
+     </el-tabs>
+     <!--  底部 -->
+     <div class="dialog-footer">
+       <el-button type="primary" @click="isshow = false">
+             确认
+           </el-button>
+               <span class="btn" @click="isshow = false">关 闭</span>
+             </div>
+         </div>
+      
+       </div>
+     </template>
+     <!-- //defineProps,,defineExpose -->
+   <script setup>
+   import { ref, onMounted, reactive,defineProps,defineExpose } from "vue";
+   import { Calendar, Search } from '@element-plus/icons-vue'
+   import { request, uploadFile } from "@/utils/request";
+   const props= defineProps(['title1']);
+   let isshow=ref(false);
+   let sum=ref('1')
+   let title=ref()
+   let selectstr=ref('')
+   let input3=ref("")
+   let input4=ref("")
+   let selenum=ref(1)
+   const dialogVisiblenode = ref(false)
+   
+   const tabPosition = ref('left')
+    let title1=ref("1")
+   let dialogVisible=ref(true);
+   const formLabelWidth5=ref(77)
+   const multipleTableRef = ref()
+   const multipleSelection = ref([])
+   let listArray = ref([
+     { id: 0, name: "灾情演化" },
+     { id: 1, name: "演化过程" },
+     { id: 2, name: "灾情历史" },
+     { id: 3,  name: "日志" },
+   ]);
+   let input2=ref("")
+   let des1=ref("  0   373 0.1 0.1    \n 10  373 0.1 0.1\n 20  373 0.1 0.1")
+   let des2=ref("  0 100\n 10 100 \n 20 100")
+   const tableData = [
+     {
+       date: '2022-05-03',
+       name: 'Tom',
+       state: 'California',
+       city: 'Los Angeles',
+       address: 'No. 189, Grove St, Los Angeles',
+       zip: 'CA 90036',
+       tag: 'Home',
+     },
+     {
+       date: '2023-05-02',
+       name: 'Tom',
+       state: 'California',
+       city: 'Los Angeles',
+       address: 'No. 189, Grove St, Los Angeles',
+       zip: 'CA 90036',
+       tag: 'Office',
+     },
+     {
+       date: '2016-05-04',
+       name: 'Tom',
+       state: 'California',
+       city: 'Los Angeles',
+       address: 'No. 189, Grove St, Los Angeles',
+       zip: 'CA 90036',
+       tag: 'Home',
+     },
+     {
+       date: '2016-05-01',
+       name: 'Tom',
+       state: 'California',
+       city: 'Los Angeles',
+       address: 'No. 189, Grove St, Los Angeles',
+       zip: 'CA 90036',
+       tag: 'Office',
+     },
+   ]
+   const tableData2= [
+     {
+       date: '2022-05-03',
+       name: 'Tom0',
+       address: 'No. 189, Grove St, Los Angeles',
+       chuk: '是',
+       w1:"30",
+       w2:'10'
+     },
+     {
+       date: '2023-05-02',
+       name: 'Tom1',
+       address: 'No. 189, Grove St, Los Angeles',
+       chuk: '否',
+       w1:"20",
+       w2:'10'
+     },
+    
+   ]
+const activeName = ref('111');
+
+const currentPage4 = ref(4)
+const pageSize4 = ref(1)
+const small = ref(false)
+const background = ref(false)
+const disabled = ref(false)
+let total=ref(100)
+let paginationConfig=reactive({
+        hideSinglePage: false,
+        page: 1,
+        size: 15,
+        sizeList: [2, 15, 30, 50],
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+      })
+         // 分页查询
+
+ function handleSizeChange(val) {
+  console.log("11111"+val)
+}
+function handleCurrentChange(val){ 
+  console.log("22222"+val)
+}
+   function handleClick(tab, event){
+     activeName.value=tab.props.name;
+    // console.log( activeName.value);
+   }
+  //火灾接口
+  function getdata(){
+    console.log(1111);
+ 
+    const params = {
+        transCode: 'D00001',
+        count:currentPage4.value,
+        page:pageSize4.value,
+        searchtag:'',
+        userId:"5f06c8bc77234f969d13e160b54c27e3"
+        }
+        request(params)
+          .then((res) => { 
+            console.log(res)
+          })
+          .catch((err) => {
+          })
+  }
+   function handleClicktable() {
+     console.log('click')
+   }
+   function handleSelectionChange(val){
+     multipleSelection.value = val
+     console.log(  multipleSelection.value );
+   }
+   function handleEdit(index,row){
+     console.log(index, row)
+   }
+   function handleDelete(index,row) {
+     console.log(index, row)
+   }
+   function queding(){
+     selectfun();
+     if(selenum.value==1){
+       input3.value=selectstr.value
+   
+     }else{
+       input4.value=selectstr.value
+     }
+   
+   
+   }
+   function selectfun(){
+     selectstr.value='';
+     if(multipleSelection.value.length!=0){
+       //selectstr.value='';
+     for(let i=0;i<=multipleSelection.value.length-1;i++){
+        selectstr.value+=multipleSelection.value[i].name;
+        selectstr.value+=','
+      console.log(multipleSelection.value[i].name)
+     }
+     }else{
+   
+       selectstr.value='';
+      
+     }
+   }
+   onMounted(()=>{
+    getdata();
+   })
+   defineExpose({isshow})
+   </script>
+   <style scoped>
+   .demo-pagination-block + .demo-pagination-block {
+  margin-top: 10px;
+}
+.demo-pagination-block .demonstration {
+  margin-bottom: 16px;
+}
+   .left_container {
+     padding: 15px;
+     width: 600px;
+     position: relative;
+     top: 49px;
+     left: 89px;
+     z-index: 2018;
+     border-radius: 5px;
+     box-shadow: 0px 3px 10px rgba(255, 255, 255, 0.1);
+     background-color: #fff;
+     font-size: 12px;
+   }
+   .demo-input-suffix{
+     /* padding: 10px; */
+   }
+   
+   .firsttitle{
+     display: flex;
+   }
+   .btn2{
+     padding: 0 20px;
+   }
+   .input{
+     width: 400px;
+     margin-top: 10px;
+   }
+   
+   </style>
+   <style>
+   .yanshib .el-tabs__content{
+     border: 1px solid #e4e7ed !important;
+     padding: 5px 20px;
+     min-height: 200px;
+   }
+   .el-tabs--left .el-tabs__header.is-left{
+     margin-right: 0 !important;
+   }
+   </style>

+ 60 - 0
src/view/myDemo.vue

@@ -0,0 +1,60 @@
+<template>
+    <div > 
+          <div class="left_container">
+          <h2 @click="projectlist()"> 我是路由</h2>
+        <!-- <RouterLink to="/myDemo" >点击</RouterLink> -->
+        <RouterLink :to="{path:'/myHome'}">跳转到目标页</RouterLink>
+         </div>
+         <!-- 展示去 -->
+         <div class="mian-content">
+          <RouterView></RouterView>
+         </div>
+       </div>
+     </template>
+   <script setup>
+   import { ref, onMounted,defineProps, reactive, } from "vue";
+    import {RouterView,RouterLink } from "vue-router"
+     import { request, uploadFile } from "@/utils/request";
+
+    onMounted(()=>{
+     //  projectlist()
+
+})
+    function projectlist(){//列表加载
+  
+        //  setTimeout(() => {
+       const params = {
+          transCode: 'C00001',
+        page: 1,
+        count: 5,
+        searchtag:"",
+        userId:"5f06c8bc77234f969d13e160b54c27e3"
+        }
+        request(params)
+          .then((res) => { 
+            console.log(res)
+          })
+          .catch((err) => {
+          })
+          // }, 1500)
+    }
+   </script>
+   <style scoped>
+   .left_container {
+     padding: 15px;
+     width: 300px;
+     position: relative;
+     top: 60px;
+     left: 169px;
+     z-index: 2018;
+     border-radius: 5px;
+     box-shadow: 0px 3px 10px rgba(255, 255, 255, 0.1);
+   }
+   .mian-content{
+    width: 500px;
+    height: 600px;
+    border: 1px solid;
+    margin-left: 300px;
+    margin-top: 100px;
+   }
+   </style>

+ 21 - 5
src/view/myIndex.vue

@@ -75,6 +75,10 @@
       </div>
     </div>
     <!-- 内容 -->
+        <!-- 内容 -->
+        <div>
+   <evolutionView ref="evolution" :title1="title1" />
+  </div>
   </div>
 </template>
 
@@ -99,8 +103,12 @@ import vtkLookupTable from "@kitware/vtk.js/Common/Core/LookupTable";
 import vtkDataArray from "@kitware/vtk.js/Common/Core/DataArray.js";
 import vtkColorTransferFunction from "@kitware/vtk.js/Rendering/Core/ColorTransferFunction";
 import { I } from "@kitware/vtk.js/macros2.js";
+import { Representation } from '@kitware/vtk.js/Rendering/Core/Property/Constants';
+import evolutionView  from "./evolutionView.vue"
+
 // const props = {};
 let time = 3 * 60;
+let evolution=ref();
 let listArray = reactive([
   { id: 0, img: p1, name: "灾情演化" },
   { id: 1, img: p2, name: "演化过程" },
@@ -151,6 +159,10 @@ let options = reactive([
 function add(id) {
   if (id == 2) {
     dialogVisible.value = true;
+    evolution.value.isshow=false;
+  }else if(id==0){
+    evolution.value.isshow=true;
+    dialogVisible.value = false;
   }
 }
 // 响应式状态
@@ -186,6 +198,10 @@ const mapper = vtkMapper.newInstance();
 const actor = vtkActor.newInstance();
 const reader = vtkUnstructuredDataReader.newInstance();
 const scalarBarActor = vtkScalarBarActor.newInstance();
+// actor.getProperty().setRepresentation(Representation.WIREFRAME);//线
+// actor.getProperty().setRepresentation(Representation.POINTS);//点
+actor.getProperty().setRepresentation(Representation.SURFACE);//面
+
 // 用来修改状态、触发更新的函数
 function inOpacity() {
   opacity.value = opacity.value + 0.1;
@@ -238,11 +254,11 @@ function play(time) {
       setTimeout(resolve, timeout);
     });
   let timer = async (timeout) => {
-    while (count.value < endtime.value&&isstop.value) {
+    while (count.value < endtime.value && isstop.value) {
       // if (isstop.value == true) {
-        await sleep(time);
-        changeScalar();
-        count.value++;
+      await sleep(time);
+      changeScalar();
+      count.value++;
       // }
     }
   };
@@ -349,7 +365,7 @@ onMounted(() => {
     renderer.addActor(actor);
 
     let lut = mapper.getLookupTable();
-    
+
     renderer.addActor(scalarBarActor);
     scalarBarActor.setScalarsToColors(lut);
     // console.log("lut:", lut.getRange());

+ 5 - 0
tests/unit/.eslintrc.js

@@ -0,0 +1,5 @@
+module.exports = {
+  env: {
+    jest: true
+  }
+}

+ 22 - 0
tests/unit/components/SvgIcon.spec.js

@@ -0,0 +1,22 @@
+import { shallowMount } from '@vue/test-utils'
+import SvgIcon from '@/components/SvgIcon/index.vue'
+describe('SvgIcon.vue', () => {
+  it('iconClass', () => {
+    const wrapper = shallowMount(SvgIcon, {
+      propsData: {
+        iconClass: 'test'
+      }
+    })
+    expect(wrapper.find('use').attributes().href).toBe('#icon-test')
+  })
+  it('className', () => {
+    const wrapper = shallowMount(SvgIcon, {
+      propsData: {
+        iconClass: 'test'
+      }
+    })
+    expect(wrapper.classes().length).toBe(1)
+    wrapper.setProps({ className: 'test' })
+    expect(wrapper.classes().includes('test')).toBe(true)
+  })
+})

+ 32 - 0
tests/unit/utils/formatTime.spec.js

@@ -0,0 +1,32 @@
+import {
+    formatTime
+} from '@/utils/index.js'
+
+describe('Utils:formatTime', () => {
+    const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
+    const retrofit = 5 * 1000
+
+    it('ten digits timestamp', () => {
+        expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分')
+    })
+    it('test now', () => {
+        expect(formatTime(+new Date() - 1)).toBe('刚刚')
+    })
+    it('less two minute', () => {
+        expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前')
+    })
+    it('less two hour', () => {
+        expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前')
+    })
+    it('less one day', () => {
+        expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前')
+    })
+    it('more than one day', () => {
+        expect(formatTime(d)).toBe('7月13日17时54分')
+    })
+    it('format', () => {
+        expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
+        expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
+        expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
+    })
+})

+ 28 - 0
tests/unit/utils/parseTime.spec.js

@@ -0,0 +1,28 @@
+import { parseTime } from '@/utils/index.js'
+
+describe('Utils:parseTime', () => {
+  const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01"
+  it('timestamp', () => {
+    expect(parseTime(d)).toBe('2018-07-13 17:54:01')
+  })
+  it('ten digits timestamp', () => {
+    expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01')
+  })
+  it('new Date', () => {
+    expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01')
+  })
+  it('format', () => {
+    expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54')
+    expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13')
+    expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54')
+  })
+  it('get the day of the week', () => {
+    expect(parseTime(d, '{a}')).toBe('五') // 星期五
+  })
+  it('get the day of the week', () => {
+    expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日
+  })
+  it('empty argument', () => {
+    expect(parseTime()).toBeNull()
+  })
+})

+ 39 - 0
tests/unit/utils/validate.spec.js

@@ -0,0 +1,39 @@
+import { validUsername, isExternal, validEmail, validCode, validPwd } from '@/utils/validate.js'
+
+describe('Utils:validate', () => {
+  it('validUsername', () => {
+    expect(validUsername('admin')).toBe(true)
+    expect(validUsername('editor')).toBe(true)
+    expect(validUsername('xxxx')).toBe(false)
+  })
+  it('isExternal', () => {
+    expect(isExternal('https://www.xichain.com.cn')).toBe(true)
+    expect(isExternal('http://www.xichain.com.cn')).toBe(true)
+    expect(isExternal('www.xichain.com.cn')).toBe(false)
+    expect(isExternal('/index')).toBe(false)
+    expect(isExternal('./index')).toBe(false)
+    expect(isExternal('index')).toBe(false)
+  })
+  it('validEmail', () => {
+    expect(validEmail('2345637@qq.com')).toBe(true)
+    expect(validEmail('237542356')).toBe(false)
+    expect(validEmail('world')).toBe(false)
+  })
+  it('validCode', () => {
+    expect(validCode('123456')).toBe(true)
+    expect(validCode('12345')).toBe(false)
+    expect(validCode('1234567')).toBe(false)
+    expect(validCode('com321')).toBe(false)
+    expect(validCode('hellos')).toBe(false)
+  })
+  it('validPwd', () => {
+    expect(validPwd('hellos')).toBe(true)
+    expect(validPwd('z12345')).toBe(true)
+    expect(validPwd('z12345678912345678')).toBe(true)
+    expect(validPwd('z_12345')).toBe(true)
+    expect(validPwd('z34ewr6_1234')).toBe(true)
+    expect(validPwd('z1234')).toBe(false)
+    expect(validPwd('237com')).toBe(false)
+    expect(validPwd('237542356')).toBe(false)
+  })
+})

+ 153 - 1
vue.config.js

@@ -1,9 +1,161 @@
 const { defineConfig } = require('@vue/cli-service')
+const path = require('path') // Node.js的path模块提供了一些用于处理文件路径的小工具
+const settings = require('./src/settings.js')
+const name = settings.title // 页面标题
+const port = process.env.port || process.env.npm_config_port || 8080 // dev端口号
+function resolve(dir) {
+  return path.join(__dirname, dir) // 连接路径,会正确使用当前系统的路径分隔符,Unix系统是"/",Windows系统是"\"
+}
+const webpack = require("webpack");
 module.exports = defineConfig({
  // If you want to transpile all dependencies:
  transpileDependencies: true,
 
  // If you selectively transpile dependencies:
  transpileDependencies: ["@kitware/vtk.js"],
-  // lintOnSave: false, //关闭语法检查
+  lintOnSave: false, //关闭语法检查
+  publicPath: '',
+  outputDir: 'dist',//输出目录
+  assetsDir: 'static',//编译文件存放目录
+ // lintOnSave: process.env.NODE_ENV === 'development',//dev环境保存时eslint校验
+  productionSourceMap: false,//prod环境不启用sourceMap
+ // hot:"only",
+  devServer: {
+    port: port,
+    allowedHosts: 'all',
+    historyApiFallback: true,
+    proxy: {
+      '/api': {
+        // target: 'http://localhost:8081/', // 后端接口地址
+        target: 'http://192.168.131:8187/TransServlet',
+        //target: 'http://192.168.0.131:8087/TransServlet',
+        // target: 'https://www.gzchain.org.cn/managersvc/', //后端接口地址
+        secure: false, //接受使用https
+        changeOrigin: true, //允许跨域
+        ws: false, //使用websocket
+        pathRewrite: { // 路径重写
+          '^/api': ''
+        }
+      },
+      
+      '/file': {
+        // target: 'http://192.168.0.15:8081/', // 后端接口地址
+        target: 'http://192.168.0.43:2201/',
+        secure: false, //接受使用https
+        changeOrigin: true, //允许跨域
+        ws: false, //使用websocket
+        pathRewrite: { // 路径重写
+          '^/file': ''
+        }
+      }, '/websokct':{
+        target: 'http://192.168.0.131:8081/',
+       // target: 'http://192.168.0.43:8081/',
+        // target: 'https://www.gzchain.org.cn/managersvc/', //后端接口地址
+        secure: false, //接受使用https
+      }
+    },
+    open: false,//不自动打开浏览器
+    // overlay: {
+    //   warnings: false,
+    //   errors: true
+    // },
+  },
+  configureWebpack: {
+    // 创建别名(在webpack的name字段中提供别名,以便在index.html中插入正确的引用路径)
+    name: name,
+    resolve: {
+      alias: {
+        '@': resolve('src')
+      }
+    }
+  },
+  chainWebpack(config) {
+    config.plugins.delete('preload') // TODO: need test
+    config.plugins.delete('prefetch') // TODO: need test
+
+    // 设置 svg-sprite-loader 插件生效文件夹(src/icons)
+    config.module
+      .rule('svg')
+      .exclude.add(resolve('src/icons'))
+      .end()
+    config.module
+      .rule('icons')
+      .test(/\.svg$/)
+      .include.add(resolve('src/icons'))
+      .end()
+      .use('svg-sprite-loader')
+      .loader('svg-sprite-loader')
+      .options({
+        symbolId: 'icon-[name]'
+      })
+      .end()
+
+    // 设置 preserveWhitespace
+    config.module
+      .rule('vue')
+      .use('vue-loader')
+      .loader('vue-loader')
+      .tap(options => {
+        // options.compilerOptions.preserveWhitespace = true
+        return options
+      })
+      .end()
+
+    // config.module
+    //   .rule('THREE')
+    //   .use('imports-loader?THREE=three')
+    //   .loader('imports-loader')
+    //   .end()
+
+    // config.module
+    //   .rule('OrbitControls”')
+    //   .use('exports-loader?THREE.OrbitControls')
+    //   .loader('exports-loader')
+    //   .end()
+
+    //  webpack的SourceMaps 7种打包编译模式详见 https://webpack.js.org/configuration/devtool/#development
+    config
+      .when(process.env.NODE_ENV === 'development',
+        config => config.devtool('cheap-source-map') // 生成一个没有列信息(column-mappings)的SourceMaps文件,不包含loader的sourcemap(譬如babel的sourcemap)
+      )
+
+    config
+      .when(process.env.NODE_ENV !== 'development',
+        config => {
+          config
+            .plugin('ScriptExtHtmlWebpackPlugin')
+            .after('html')
+            .use('script-ext-html-webpack-plugin', [{
+              // `runtime` 必须与runtimeChunk名称相同,默认值为`runtime`
+              inline: /runtime\..*\.js$/
+            }])
+            .end()
+          config
+            .optimization.splitChunks({
+              chunks: 'all',
+              cacheGroups: {
+                libs: {
+                  name: 'chunk-libs',
+                  test: /[\\/]node_modules[\\/]/,
+                  priority: 10,
+                  chunks: 'initial' // 仅打包最初的第三方依赖
+                },
+                elementUI: {
+                  name: 'chunk-elementUI', // 将elementUI拆分为一个包
+                  priority: 20, // weight必须大于libs和app,否则将打包成libs或app
+                  test: /[\\/]node_modules[\\/]_?element-plus(.*)/ // 为了适应cnpm
+                },
+                commons: {
+                  name: 'chunk-commons',
+                  test: resolve('src/components'), // 可以自定义规则
+                  minChunks: 3, //  最小公共数
+                  priority: 5,
+                  reuseExistingChunk: true
+                }
+              }
+            })
+          config.optimization.runtimeChunk('single')
+        }
+      )
+  }
 })

Деякі файли не було показано, через те що забагато файлів було змінено